From 58a6e85062e7b8c78d385a266682068a1f82f129 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jo=CC=88rg=20Prante?= Date: Tue, 3 Nov 2020 11:07:42 +0100 Subject: [PATCH] integration of subprojects barcode, chart, io-vector, layout-pdfbox, png --- .gitignore | 6 +- CREDITS.txt | 5 - barcode/NOTICE.txt | 7 + barcode/build.gradle | 7 + barcode/src/main/java/module-info.java | 6 + .../xbib/graphics/barcode/AustraliaPost.java | 363 +++ .../org/xbib/graphics/barcode/AztecCode.java | 1989 ++++++++++++ .../org/xbib/graphics/barcode/AztecRune.java | 168 + .../xbib/graphics/barcode/ChannelCode.java | 155 + .../org/xbib/graphics/barcode/Codabar.java | 86 + .../org/xbib/graphics/barcode/CodablockF.java | 852 +++++ .../org/xbib/graphics/barcode/Code11.java | 208 ++ .../org/xbib/graphics/barcode/Code128.java | 843 +++++ .../org/xbib/graphics/barcode/Code16k.java | 769 +++++ .../org/xbib/graphics/barcode/Code2Of5.java | 493 +++ .../org/xbib/graphics/barcode/Code32.java | 103 + .../org/xbib/graphics/barcode/Code3Of9.java | 155 + .../graphics/barcode/Code3Of9Extended.java | 79 + .../org/xbib/graphics/barcode/Code49.java | 1345 ++++++++ .../org/xbib/graphics/barcode/Code93.java | 192 ++ .../org/xbib/graphics/barcode/CodeOne.java | 1946 +++++++++++ .../org/xbib/graphics/barcode/Composite.java | 2846 +++++++++++++++++ .../org/xbib/graphics/barcode/DataBar14.java | 698 ++++ .../graphics/barcode/DataBarExpanded.java | 1652 ++++++++++ .../xbib/graphics/barcode/DataBarLimited.java | 476 +++ .../org/xbib/graphics/barcode/DataMatrix.java | 1636 ++++++++++ .../java/org/xbib/graphics/barcode/Ean.java | 366 +++ .../org/xbib/graphics/barcode/GridMatrix.java | 2015 ++++++++++++ .../barcode/HumanReadableLocation.java | 22 + .../org/xbib/graphics/barcode/JapanPost.java | 138 + .../org/xbib/graphics/barcode/KixCode.java | 95 + .../org/xbib/graphics/barcode/KoreaPost.java | 61 + .../org/xbib/graphics/barcode/Logmars.java | 97 + .../org/xbib/graphics/barcode/MaxiCode.java | 890 ++++++ .../xbib/graphics/barcode/MicroQrCode.java | 1593 +++++++++ .../org/xbib/graphics/barcode/MsiPlessey.java | 188 ++ .../java/org/xbib/graphics/barcode/Nve18.java | 72 + .../org/xbib/graphics/barcode/Pdf417.java | 1704 ++++++++++ .../org/xbib/graphics/barcode/Pharmacode.java | 66 + .../graphics/barcode/Pharmacode2Track.java | 106 + .../graphics/barcode/Pharmazentralnummer.java | 69 + .../org/xbib/graphics/barcode/Postnet.java | 185 ++ .../org/xbib/graphics/barcode/QrCode.java | 2008 ++++++++++++ .../graphics/barcode/RoyalMail4State.java | 112 + .../org/xbib/graphics/barcode/Symbol.java | 1058 ++++++ .../org/xbib/graphics/barcode/Telepen.java | 177 + .../java/org/xbib/graphics/barcode/Upc.java | 445 +++ .../xbib/graphics/barcode/UspsOneCode.java | 451 +++ .../xbib/graphics/barcode/UspsPackage.java | 114 + .../barcode/render/GraphicsRenderer.java | 150 + .../org/xbib/graphics/barcode/util/AddOn.java | 112 + .../xbib/graphics/barcode/util/Hexagon.java | 23 + .../graphics/barcode/util/ReedSolomon.java | 86 + .../xbib/graphics/barcode/util/TextBox.java | 40 + .../xbib/graphics/barcode/MaxiCodeTest.java | 19 + .../barcode/ParameterizedExtension.java | 253 ++ .../org/xbib/graphics/barcode/SymbolTest.java | 608 ++++ .../graphics/barcode/output/Code39Test.java | 59 + .../barcode/output/EPSRendererTest.java | 121 + .../barcode/output/PDFRendererTest.java | 121 + .../barcode/output/SvgRendererTest.java | 121 + .../graphics/barcode/codabar/basic.codewords | 18 + .../xbib/graphics/barcode/codabar/basic.png | Bin 0 -> 41561 bytes .../graphics/barcode/codabar/basic.properties | 1 + .../codabar/module-width-ratio-2.5.codewords | 18 + .../codabar/module-width-ratio-2.5.png | Bin 0 -> 47218 bytes .../codabar/module-width-ratio-2.5.properties | 2 + .../barcode/codabar/no-start-char.error | 1 + .../barcode/codabar/no-start-char.properties | 1 + .../barcode/codabar/no-stop-char.error | 1 + .../barcode/codabar/no-stop-char.properties | 1 + .../code11/basic-one-check-digit.codewords | 14 + .../barcode/code11/basic-one-check-digit.png | Bin 0 -> 25691 bytes .../code11/basic-one-check-digit.properties | 2 + .../graphics/barcode/code11/basic.codewords | 15 + .../xbib/graphics/barcode/code11/basic.png | Bin 0 -> 27064 bytes .../graphics/barcode/code11/basic.properties | 4 + .../human-readable-location-none.codewords | 15 + .../code11/human-readable-location-none.png | Bin 0 -> 19260 bytes .../human-readable-location-none.properties | 2 + .../human-readable-location-top.codewords | 15 + .../code11/human-readable-location-top.png | Bin 0 -> 27606 bytes .../human-readable-location-top.properties | 2 + .../barcode/code11/invalid-content.error | 1 + .../barcode/code11/invalid-content.properties | 1 + .../code11/module-width-ratio-2.5.codewords | 15 + .../barcode/code11/module-width-ratio-2.5.png | Bin 0 -> 29246 bytes .../code11/module-width-ratio-2.5.properties | 2 + .../code11/module-width-ratio-3.0.codewords | 15 + .../barcode/code11/module-width-ratio-3.0.png | Bin 0 -> 30414 bytes .../code11/module-width-ratio-3.0.properties | 2 + .../barcode/code11/start-delimiter.codewords | 15 + .../barcode/code11/start-delimiter.png | Bin 0 -> 27064 bytes .../barcode/code11/start-delimiter.properties | 2 + .../code11/start-stop-delimiter.codewords | 15 + .../barcode/code11/start-stop-delimiter.png | Bin 0 -> 28315 bytes .../code11/start-stop-delimiter.properties | 3 + .../barcode/code11/stop-delimiter.codewords | 15 + .../barcode/code11/stop-delimiter.png | Bin 0 -> 27184 bytes .../barcode/code11/stop-delimiter.properties | 2 + .../barcode/code11/three-check-digits.error | 1 + .../code11/three-check-digits.properties | 1 + .../barcode/code11/zero-check-digits.error | 1 + .../code11/zero-check-digits.properties | 1 + .../graphics/barcode/code2of5/basic.codewords | 1 + .../xbib/graphics/barcode/code2of5/basic.png | Bin 0 -> 31262 bytes .../barcode/code2of5/basic.properties | 1 + .../human-readable-location-none.codewords | 1 + .../code2of5/human-readable-location-none.png | Bin 0 -> 23160 bytes .../human-readable-location-none.properties | 2 + .../human-readable-location-top.codewords | 1 + .../code2of5/human-readable-location-top.png | Bin 0 -> 31522 bytes .../human-readable-location-top.properties | 2 + .../code2of5/module-width-ratio-2.5.codewords | 1 + .../code2of5/module-width-ratio-2.5.png | Bin 0 -> 31094 bytes .../module-width-ratio-2.5.properties | 2 + .../graphics/barcode/code3of9/basic.codewords | 12 + .../xbib/graphics/barcode/code3of9/basic.png | Bin 0 -> 33955 bytes .../barcode/code3of9/basic.properties | 1 + .../code3of9/module-width-ratio-3.codewords | 12 + .../barcode/code3of9/module-width-ratio-3.png | Bin 0 -> 38681 bytes .../code3of9/module-width-ratio-3.properties | 2 + .../code93/hide-check-digits.codewords | 15 + .../barcode/code93/hide-check-digits.png | Bin 0 -> 30281 bytes .../code93/hide-check-digits.properties | 2 + .../code93/letters-lowercase-1.codewords | 23 + .../barcode/code93/letters-lowercase-1.png | Bin 0 -> 50718 bytes .../code93/letters-lowercase-1.properties | 1 + .../code93/letters-lowercase-2.codewords | 23 + .../barcode/code93/letters-lowercase-2.png | Bin 0 -> 50474 bytes .../code93/letters-lowercase-2.properties | 1 + .../code93/letters-lowercase-3.codewords | 21 + .../barcode/code93/letters-lowercase-3.png | Bin 0 -> 47521 bytes .../code93/letters-lowercase-3.properties | 1 + .../code93/letters-uppercase-1.codewords | 14 + .../barcode/code93/letters-uppercase-1.png | Bin 0 -> 30679 bytes .../code93/letters-uppercase-1.properties | 1 + .../code93/letters-uppercase-2.codewords | 14 + .../barcode/code93/letters-uppercase-2.png | Bin 0 -> 32098 bytes .../code93/letters-uppercase-2.properties | 1 + .../code93/letters-uppercase-3.codewords | 13 + .../barcode/code93/letters-uppercase-3.png | Bin 0 -> 30660 bytes .../code93/letters-uppercase-3.properties | 1 + .../numbers-with-custom-bar-height.codewords | 15 + .../code93/numbers-with-custom-bar-height.png | Bin 0 -> 65838 bytes .../numbers-with-custom-bar-height.properties | 2 + ...numbers-with-custom-module-width.codewords | 15 + .../numbers-with-custom-module-width.png | Bin 0 -> 76279 bytes ...umbers-with-custom-module-width.properties | 2 + .../graphics/barcode/code93/numbers.codewords | 15 + .../xbib/graphics/barcode/code93/numbers.png | Bin 0 -> 31691 bytes .../barcode/code93/numbers.properties | 1 + .../special-characters-no-ctrl.codewords | 12 + .../code93/special-characters-no-ctrl.png | Bin 0 -> 25055 bytes .../special-characters-no-ctrl.properties | 1 + .../special-characters-with-ctrl-1.codewords | 23 + .../code93/special-characters-with-ctrl-1.png | Bin 0 -> 42643 bytes .../special-characters-with-ctrl-1.properties | 1 + .../special-characters-with-ctrl-2.codewords | 21 + .../code93/special-characters-with-ctrl-2.png | Bin 0 -> 36766 bytes .../special-characters-with-ctrl-2.properties | 1 + .../special-characters-with-ctrl-3.codewords | 19 + .../code93/special-characters-with-ctrl-3.png | Bin 0 -> 38864 bytes .../special-characters-with-ctrl-3.properties | 1 + .../code93/start-stop-delimiter.codewords | 15 + .../barcode/code93/start-stop-delimiter.png | Bin 0 -> 32073 bytes .../code93/start-stop-delimiter.properties | 2 + .../barcode/ean/ean-13-basic.codewords | 1 + .../graphics/barcode/ean/ean-13-basic.png | Bin 0 -> 42843 bytes .../barcode/ean/ean-13-basic.properties | 2 + .../barcode/ean/ean-8-basic.codewords | 1 + .../xbib/graphics/barcode/ean/ean-8-basic.png | Bin 0 -> 35321 bytes .../barcode/ean/ean-8-basic.properties | 2 + .../barcode/fonts/OkapiDejaVuSans.ttf | Bin 0 -> 739756 bytes .../barcode/japanpost/basic.codewords | 1 + .../xbib/graphics/barcode/japanpost/basic.png | Bin 0 -> 2952 bytes .../barcode/japanpost/basic.properties | 1 + .../barcode/japanpost/bug-50.codewords | 1 + .../graphics/barcode/japanpost/bug-50.png | Bin 0 -> 2916 bytes .../barcode/japanpost/bug-50.properties | 1 + .../barcode/logmars/letters1.codewords | 1 + .../graphics/barcode/logmars/letters1.png | Bin 0 -> 37786 bytes .../barcode/logmars/letters1.properties | 1 + .../barcode/logmars/letters2.codewords | 1 + .../graphics/barcode/logmars/letters2.png | Bin 0 -> 40583 bytes .../barcode/logmars/letters2.properties | 1 + .../barcode/logmars/letters3.codewords | 1 + .../graphics/barcode/logmars/letters3.png | Bin 0 -> 29551 bytes .../barcode/logmars/letters3.properties | 1 + .../logmars/module-width-ratio-2.codewords | 1 + .../barcode/logmars/module-width-ratio-2.png | Bin 0 -> 35381 bytes .../logmars/module-width-ratio-2.properties | 2 + .../barcode/logmars/numbers.codewords | 1 + .../xbib/graphics/barcode/logmars/numbers.png | Bin 0 -> 40694 bytes .../barcode/logmars/numbers.properties | 1 + .../barcode/logmars/special.codewords | 1 + .../xbib/graphics/barcode/logmars/special.png | Bin 0 -> 27318 bytes .../barcode/logmars/special.properties | 1 + .../graphics/barcode/maxicode/mode-0.error | 1 + .../barcode/maxicode/mode-0.properties | 1 + .../graphics/barcode/maxicode/mode-1.error | 1 + .../barcode/maxicode/mode-1.properties | 1 + .../maxicode/mode-2-invalid-primary.error | 1 + .../mode-2-invalid-primary.properties | 3 + .../maxicode/mode-2-max-data.codewords | 144 + .../barcode/maxicode/mode-2-max-data.png | Bin 0 -> 33472 bytes .../maxicode/mode-2-max-data.properties | 3 + .../maxicode/mode-2-standard.codewords | 144 + .../barcode/maxicode/mode-2-standard.png | Bin 0 -> 35250 bytes .../maxicode/mode-2-standard.properties | 3 + .../mode-2-structured-append-1-of-8.codewords | 144 + .../mode-2-structured-append-1-of-8.png | Bin 0 -> 33954 bytes ...mode-2-structured-append-1-of-8.properties | 5 + .../maxicode/mode-3-standard.codewords | 144 + .../barcode/maxicode/mode-3-standard.png | Bin 0 -> 35039 bytes .../maxicode/mode-3-standard.properties | 3 + ...-basic-structured-append-total-1.codewords | 144 + ...mode-4-basic-structured-append-total-1.png | Bin 0 -> 31119 bytes ...basic-structured-append-total-1.properties | 4 + .../barcode/maxicode/mode-4-basic.codewords | 144 + .../barcode/maxicode/mode-4-basic.png | Bin 0 -> 31119 bytes .../barcode/maxicode/mode-4-basic.properties | 2 + .../mode-4-code-set-a-all-symbols.codewords | 144 + .../mode-4-code-set-a-all-symbols.png | Bin 0 -> 33738 bytes .../mode-4-code-set-a-all-symbols.properties | 2 + .../mode-4-code-set-b-all-symbols.codewords | 144 + .../mode-4-code-set-b-all-symbols.png | Bin 0 -> 33314 bytes .../mode-4-code-set-b-all-symbols.properties | 2 + .../mode-4-code-sets-a-and-b.codewords | 144 + .../maxicode/mode-4-code-sets-a-and-b.png | Bin 0 -> 31556 bytes .../mode-4-code-sets-a-and-b.properties | 2 + .../maxicode/mode-4-code-sets-c-d-e.codewords | 144 + .../maxicode/mode-4-code-sets-c-d-e.png | Bin 0 -> 35100 bytes .../mode-4-code-sets-c-d-e.properties | 2 + .../maxicode/mode-4-max-data.codewords | 144 + .../barcode/maxicode/mode-4-max-data.png | Bin 0 -> 31509 bytes .../maxicode/mode-4-max-data.properties | 2 + .../maxicode/mode-4-numeric-shift.codewords | 144 + .../barcode/maxicode/mode-4-numeric-shift.png | Bin 0 -> 31472 bytes .../maxicode/mode-4-numeric-shift.properties | 2 + .../mode-4-structured-append-0-of-5.error | 1 + ...mode-4-structured-append-0-of-5.properties | 4 + .../mode-4-structured-append-1-of-3.codewords | 144 + .../mode-4-structured-append-1-of-3.png | Bin 0 -> 30962 bytes ...mode-4-structured-append-1-of-3.properties | 3 + .../mode-4-structured-append-2-of-3.codewords | 144 + .../mode-4-structured-append-2-of-3.png | Bin 0 -> 31413 bytes ...mode-4-structured-append-2-of-3.properties | 4 + .../mode-4-structured-append-3-of-3.codewords | 144 + .../mode-4-structured-append-3-of-3.png | Bin 0 -> 31221 bytes ...mode-4-structured-append-3-of-3.properties | 4 + .../mode-4-structured-append-5-of-9.error | 1 + ...mode-4-structured-append-5-of-9.properties | 4 + .../maxicode/mode-4-too-much-data.error | 1 + .../maxicode/mode-4-too-much-data.properties | 2 + .../barcode/maxicode/mode-5-basic.codewords | 144 + .../barcode/maxicode/mode-5-basic.png | Bin 0 -> 32099 bytes .../barcode/maxicode/mode-5-basic.properties | 2 + .../maxicode/mode-5-max-data.codewords | 144 + .../barcode/maxicode/mode-5-max-data.png | Bin 0 -> 32958 bytes .../maxicode/mode-5-max-data.properties | 2 + .../maxicode/mode-5-too-much-data.error | 1 + .../maxicode/mode-5-too-much-data.properties | 2 + .../maxicode/mode-6-max-data.codewords | 144 + .../barcode/maxicode/mode-6-max-data.png | Bin 0 -> 33072 bytes .../maxicode/mode-6-max-data.properties | 2 + .../maxicode/mode-6-too-much-data.error | 1 + .../maxicode/mode-6-too-much-data.properties | 2 + .../graphics/barcode/maxicode/mode-7.error | 1 + .../barcode/maxicode/mode-7.properties | 1 + .../graphics/barcode/output/code93-basic.eps | 71 + .../graphics/barcode/output/code93-basic.svg | 53 + .../graphics/barcode/output/code93-colors.eps | 71 + .../graphics/barcode/output/code93-colors.svg | 53 + .../barcode/output/code93-custom-font.eps | 71 + .../barcode/output/code93-custom-font.svg | 53 + .../barcode/output/code93-magnification-2.eps | 71 + .../barcode/output/code93-magnification-2.svg | 53 + .../barcode/output/code93-margin-size-20.eps | 71 + .../barcode/output/code93-margin-size-20.svg | 53 + .../barcode/output/maxicode-basic.eps | 393 +++ .../barcode/output/maxicode-basic.svg | 384 +++ .../pdf417/micro-basic-2-columns.codewords | 11 + .../barcode/pdf417/micro-basic-2-columns.png | Bin 0 -> 10109 bytes .../pdf417/micro-basic-2-columns.properties | 3 + .../micro-basic-ecc-level-7-ignored.codewords | 6 + .../micro-basic-ecc-level-7-ignored.png | Bin 0 -> 10491 bytes ...micro-basic-ecc-level-7-ignored.properties | 3 + .../barcode/pdf417/micro-basic.codewords | 6 + .../graphics/barcode/pdf417/micro-basic.png | Bin 0 -> 10491 bytes .../barcode/pdf417/micro-basic.properties | 2 + .../pdf417/micro-data-variety.codewords | 17 + .../barcode/pdf417/micro-data-variety.png | Bin 0 -> 16405 bytes .../pdf417/micro-data-variety.properties | 2 + .../micro-structured-append-1-of-3.codewords | 15 + .../pdf417/micro-structured-append-1-of-3.png | Bin 0 -> 20690 bytes .../micro-structured-append-1-of-3.properties | 6 + .../micro-structured-append-2-of-3.codewords | 15 + .../pdf417/micro-structured-append-2-of-3.png | Bin 0 -> 20723 bytes .../micro-structured-append-2-of-3.properties | 6 + .../micro-structured-append-3-of-3.codewords | 15 + .../pdf417/micro-structured-append-3-of-3.png | Bin 0 -> 20470 bytes .../micro-structured-append-3-of-3.properties | 6 + .../barcode/pdf417/micro-utf-8.codewords | 26 + .../graphics/barcode/pdf417/micro-utf-8.png | Bin 0 -> 35457 bytes .../barcode/pdf417/micro-utf-8.properties | 2 + .../barcode/pdf417/micro-variant-01.codewords | 11 + .../barcode/pdf417/micro-variant-01.png | Bin 0 -> 7209 bytes .../pdf417/micro-variant-01.properties | 16 + .../barcode/pdf417/micro-variant-02.codewords | 14 + .../barcode/pdf417/micro-variant-02.png | Bin 0 -> 9045 bytes .../pdf417/micro-variant-02.properties | 3 + .../barcode/pdf417/micro-variant-03.codewords | 17 + .../barcode/pdf417/micro-variant-03.png | Bin 0 -> 10610 bytes .../pdf417/micro-variant-03.properties | 3 + .../barcode/pdf417/micro-variant-04.codewords | 20 + .../barcode/pdf417/micro-variant-04.png | Bin 0 -> 12930 bytes .../pdf417/micro-variant-04.properties | 3 + .../barcode/pdf417/micro-variant-05.codewords | 24 + .../barcode/pdf417/micro-variant-05.png | Bin 0 -> 15652 bytes .../pdf417/micro-variant-05.properties | 3 + .../barcode/pdf417/micro-variant-06.codewords | 28 + .../barcode/pdf417/micro-variant-06.png | Bin 0 -> 18187 bytes .../pdf417/micro-variant-06.properties | 3 + .../barcode/pdf417/micro-variant-07.codewords | 8 + .../barcode/pdf417/micro-variant-07.png | Bin 0 -> 7655 bytes .../pdf417/micro-variant-07.properties | 16 + .../barcode/pdf417/micro-variant-08.codewords | 11 + .../barcode/pdf417/micro-variant-08.png | Bin 0 -> 10436 bytes .../pdf417/micro-variant-08.properties | 3 + .../barcode/pdf417/micro-variant-09.codewords | 14 + .../barcode/pdf417/micro-variant-09.png | Bin 0 -> 12501 bytes .../pdf417/micro-variant-09.properties | 3 + .../barcode/pdf417/micro-variant-10.codewords | 17 + .../barcode/pdf417/micro-variant-10.png | Bin 0 -> 15778 bytes .../pdf417/micro-variant-10.properties | 3 + .../barcode/pdf417/micro-variant-11.codewords | 20 + .../barcode/pdf417/micro-variant-11.png | Bin 0 -> 18636 bytes .../pdf417/micro-variant-11.properties | 3 + .../barcode/pdf417/micro-variant-12.codewords | 23 + .../barcode/pdf417/micro-variant-12.png | Bin 0 -> 21175 bytes .../pdf417/micro-variant-12.properties | 3 + .../barcode/pdf417/micro-variant-13.codewords | 26 + .../barcode/pdf417/micro-variant-13.png | Bin 0 -> 24053 bytes .../pdf417/micro-variant-13.properties | 3 + .../barcode/pdf417/micro-variant-14.codewords | 6 + .../barcode/pdf417/micro-variant-14.png | Bin 0 -> 8555 bytes .../pdf417/micro-variant-14.properties | 3 + .../barcode/pdf417/micro-variant-15.codewords | 8 + .../barcode/pdf417/micro-variant-15.png | Bin 0 -> 10891 bytes .../pdf417/micro-variant-15.properties | 3 + .../barcode/pdf417/micro-variant-16.codewords | 10 + .../barcode/pdf417/micro-variant-16.png | Bin 0 -> 13896 bytes .../pdf417/micro-variant-16.properties | 3 + .../barcode/pdf417/micro-variant-17.codewords | 12 + .../barcode/pdf417/micro-variant-17.png | Bin 0 -> 16943 bytes .../pdf417/micro-variant-17.properties | 3 + .../barcode/pdf417/micro-variant-18.codewords | 15 + .../barcode/pdf417/micro-variant-18.png | Bin 0 -> 21120 bytes .../pdf417/micro-variant-18.properties | 3 + .../barcode/pdf417/micro-variant-19.codewords | 20 + .../barcode/pdf417/micro-variant-19.png | Bin 0 -> 27818 bytes .../pdf417/micro-variant-19.properties | 3 + .../barcode/pdf417/micro-variant-20.codewords | 26 + .../barcode/pdf417/micro-variant-20.png | Bin 0 -> 36108 bytes .../pdf417/micro-variant-20.properties | 3 + .../barcode/pdf417/micro-variant-21.codewords | 32 + .../barcode/pdf417/micro-variant-21.png | Bin 0 -> 43759 bytes .../pdf417/micro-variant-21.properties | 3 + .../barcode/pdf417/micro-variant-22.codewords | 38 + .../barcode/pdf417/micro-variant-22.png | Bin 0 -> 52887 bytes .../pdf417/micro-variant-22.properties | 3 + .../barcode/pdf417/micro-variant-23.codewords | 44 + .../barcode/pdf417/micro-variant-23.png | Bin 0 -> 60274 bytes .../pdf417/micro-variant-23.properties | 3 + .../barcode/pdf417/micro-variant-24.codewords | 4 + .../barcode/pdf417/micro-variant-24.png | Bin 0 -> 6483 bytes .../pdf417/micro-variant-24.properties | 3 + .../barcode/pdf417/micro-variant-25.codewords | 6 + .../barcode/pdf417/micro-variant-25.png | Bin 0 -> 10233 bytes .../pdf417/micro-variant-25.properties | 3 + .../barcode/pdf417/micro-variant-26.codewords | 8 + .../barcode/pdf417/micro-variant-26.png | Bin 0 -> 13123 bytes .../pdf417/micro-variant-26.properties | 3 + .../barcode/pdf417/micro-variant-27.codewords | 10 + .../barcode/pdf417/micro-variant-27.png | Bin 0 -> 16531 bytes .../pdf417/micro-variant-27.properties | 3 + .../barcode/pdf417/micro-variant-28.codewords | 12 + .../barcode/pdf417/micro-variant-28.png | Bin 0 -> 20407 bytes .../pdf417/micro-variant-28.properties | 3 + .../barcode/pdf417/micro-variant-29.codewords | 15 + .../barcode/pdf417/micro-variant-29.png | Bin 0 -> 25051 bytes .../pdf417/micro-variant-29.properties | 3 + .../barcode/pdf417/micro-variant-30.codewords | 20 + .../barcode/pdf417/micro-variant-30.png | Bin 0 -> 33125 bytes .../pdf417/micro-variant-30.properties | 3 + .../barcode/pdf417/micro-variant-31.codewords | 26 + .../barcode/pdf417/micro-variant-31.png | Bin 0 -> 42947 bytes .../pdf417/micro-variant-31.properties | 3 + .../barcode/pdf417/micro-variant-32.codewords | 32 + .../barcode/pdf417/micro-variant-32.png | Bin 0 -> 52862 bytes .../pdf417/micro-variant-32.properties | 3 + .../barcode/pdf417/micro-variant-33.codewords | 38 + .../barcode/pdf417/micro-variant-33.png | Bin 0 -> 63380 bytes .../pdf417/micro-variant-33.properties | 3 + .../barcode/pdf417/micro-variant-34.codewords | 44 + .../barcode/pdf417/micro-variant-34.png | Bin 0 -> 73094 bytes .../pdf417/micro-variant-34.properties | 3 + .../pdf417/normal-basic-10-rows.codewords | 10 + .../barcode/pdf417/normal-basic-10-rows.png | Bin 0 -> 16553 bytes .../pdf417/normal-basic-10-rows.properties | 3 + .../normal-basic-30-columns-90-rows.error | 1 + ...normal-basic-30-columns-90-rows.properties | 4 + .../pdf417/normal-basic-31-columns.error | 1 + .../pdf417/normal-basic-31-columns.properties | 3 + .../pdf417/normal-basic-5-columns.codewords | 4 + .../barcode/pdf417/normal-basic-5-columns.png | Bin 0 -> 10324 bytes .../pdf417/normal-basic-5-columns.properties | 3 + .../normal-basic-6-rows-6-columns.codewords | 6 + .../pdf417/normal-basic-6-rows-6-columns.png | Bin 0 -> 16606 bytes .../normal-basic-6-rows-6-columns.properties | 4 + .../pdf417/normal-basic-ecc-level-0.codewords | 7 + .../pdf417/normal-basic-ecc-level-0.png | Bin 0 -> 11111 bytes .../normal-basic-ecc-level-0.properties | 3 + .../pdf417/normal-basic-ecc-level-1.codewords | 8 + .../pdf417/normal-basic-ecc-level-1.png | Bin 0 -> 12961 bytes .../normal-basic-ecc-level-1.properties | 3 + .../pdf417/normal-basic-ecc-level-2.codewords | 7 + .../pdf417/normal-basic-ecc-level-2.png | Bin 0 -> 13222 bytes .../normal-basic-ecc-level-2.properties | 3 + .../pdf417/normal-basic-ecc-level-3.codewords | 10 + .../pdf417/normal-basic-ecc-level-3.png | Bin 0 -> 18533 bytes .../normal-basic-ecc-level-3.properties | 3 + .../pdf417/normal-basic-ecc-level-4.codewords | 11 + .../pdf417/normal-basic-ecc-level-4.png | Bin 0 -> 24387 bytes .../normal-basic-ecc-level-4.properties | 3 + .../pdf417/normal-basic-ecc-level-5.codewords | 16 + .../pdf417/normal-basic-ecc-level-5.png | Bin 0 -> 39378 bytes .../normal-basic-ecc-level-5.properties | 3 + .../pdf417/normal-basic-ecc-level-6.codewords | 20 + .../pdf417/normal-basic-ecc-level-6.png | Bin 0 -> 61741 bytes .../normal-basic-ecc-level-6.properties | 3 + .../pdf417/normal-basic-ecc-level-7.codewords | 30 + .../pdf417/normal-basic-ecc-level-7.png | Bin 0 -> 108325 bytes .../normal-basic-ecc-level-7.properties | 3 + .../pdf417/normal-basic-ecc-level-8.codewords | 41 + .../pdf417/normal-basic-ecc-level-8.png | Bin 0 -> 195438 bytes .../normal-basic-ecc-level-8.properties | 3 + .../pdf417/normal-basic-ecc-level-9.error | 1 + .../normal-basic-ecc-level-9.properties | 3 + .../normal-basic-row-height-10.codewords | 7 + .../pdf417/normal-basic-row-height-10.png | Bin 0 -> 43738 bytes .../normal-basic-row-height-10.properties | 3 + .../barcode/pdf417/normal-basic.codewords | 7 + .../graphics/barcode/pdf417/normal-basic.png | Bin 0 -> 13222 bytes .../barcode/pdf417/normal-basic.properties | 2 + .../pdf417/normal-data-variety.codewords | 11 + .../barcode/pdf417/normal-data-variety.png | Bin 0 -> 21195 bytes .../pdf417/normal-data-variety.properties | 2 + .../normal-structured-append-1-of-3.codewords | 5 + .../normal-structured-append-1-of-3.png | Bin 0 -> 10922 bytes ...normal-structured-append-1-of-3.properties | 8 + .../normal-structured-append-2-of-3.codewords | 5 + .../normal-structured-append-2-of-3.png | Bin 0 -> 10944 bytes ...normal-structured-append-2-of-3.properties | 8 + .../normal-structured-append-3-of-3.codewords | 5 + .../normal-structured-append-3-of-3.png | Bin 0 -> 10645 bytes ...normal-structured-append-3-of-3.properties | 8 + .../normal-too-few-columns-and-rows.error | 1 + ...normal-too-few-columns-and-rows.properties | 4 + .../pdf417/normal-tracking-sample-2.codewords | 28 + .../pdf417/normal-tracking-sample-2.png | Bin 0 -> 107524 bytes .../normal-tracking-sample-2.properties | 2 + .../pdf417/normal-tracking-sample.codewords | 27 + .../barcode/pdf417/normal-tracking-sample.png | Bin 0 -> 86530 bytes .../pdf417/normal-tracking-sample.properties | 2 + .../barcode/pdf417/normal-utf-8.codewords | 15 + .../graphics/barcode/pdf417/normal-utf-8.png | Bin 0 -> 32982 bytes .../barcode/pdf417/normal-utf-8.properties | 2 + .../truncated-basic-ecc-level-0.codewords | 7 + .../pdf417/truncated-basic-ecc-level-0.png | Bin 0 -> 7357 bytes .../truncated-basic-ecc-level-0.properties | 3 + .../truncated-basic-ecc-level-1.codewords | 8 + .../pdf417/truncated-basic-ecc-level-1.png | Bin 0 -> 8662 bytes .../truncated-basic-ecc-level-1.properties | 3 + .../truncated-basic-ecc-level-2.codewords | 7 + .../pdf417/truncated-basic-ecc-level-2.png | Bin 0 -> 9543 bytes .../truncated-basic-ecc-level-2.properties | 3 + .../truncated-basic-ecc-level-3.codewords | 10 + .../pdf417/truncated-basic-ecc-level-3.png | Bin 0 -> 13191 bytes .../truncated-basic-ecc-level-3.properties | 3 + .../truncated-basic-ecc-level-4.codewords | 11 + .../pdf417/truncated-basic-ecc-level-4.png | Bin 0 -> 18509 bytes .../truncated-basic-ecc-level-4.properties | 3 + .../truncated-basic-ecc-level-5.codewords | 16 + .../pdf417/truncated-basic-ecc-level-5.png | Bin 0 -> 30832 bytes .../truncated-basic-ecc-level-5.properties | 3 + .../truncated-basic-ecc-level-6.codewords | 20 + .../pdf417/truncated-basic-ecc-level-6.png | Bin 0 -> 50878 bytes .../truncated-basic-ecc-level-6.properties | 3 + .../truncated-basic-ecc-level-7.codewords | 30 + .../pdf417/truncated-basic-ecc-level-7.png | Bin 0 -> 92193 bytes .../truncated-basic-ecc-level-7.properties | 3 + .../truncated-basic-ecc-level-8.codewords | 41 + .../pdf417/truncated-basic-ecc-level-8.png | Bin 0 -> 172929 bytes .../truncated-basic-ecc-level-8.properties | 3 + .../pdf417/truncated-basic-ecc-level-9.error | 1 + .../truncated-basic-ecc-level-9.properties | 3 + .../barcode/pdf417/truncated-basic.codewords | 7 + .../barcode/pdf417/truncated-basic.png | Bin 0 -> 9543 bytes .../barcode/pdf417/truncated-basic.properties | 2 + .../pdf417/truncated-data-variety.codewords | 11 + .../barcode/pdf417/truncated-data-variety.png | Bin 0 -> 15406 bytes .../pdf417/truncated-data-variety.properties | 2 + .../barcode/postnet/planet-basic.codewords | 1 + .../graphics/barcode/postnet/planet-basic.png | Bin 0 -> 4447 bytes .../barcode/postnet/planet-basic.properties | 2 + .../barcode/postnet/postnet-basic.codewords | 1 + .../barcode/postnet/postnet-basic.png | Bin 0 -> 3293 bytes .../barcode/postnet/postnet-basic.properties | 2 + .../postnet/postnet-module-width-2.codewords | 1 + .../postnet/postnet-module-width-2.png | Bin 0 -> 19309 bytes .../postnet/postnet-module-width-2.properties | 4 + .../postnet/postnet-readable-above.codewords | 1 + .../postnet/postnet-readable-above.png | Bin 0 -> 9554 bytes .../postnet/postnet-readable-above.properties | 3 + .../postnet/postnet-readable-below.codewords | 1 + .../postnet/postnet-readable-below.png | Bin 0 -> 9563 bytes .../postnet/postnet-readable-below.properties | 3 + .../barcode/qrcode/version-01.codewords | 21 + .../graphics/barcode/qrcode/version-01.png | Bin 0 -> 2457 bytes .../barcode/qrcode/version-01.properties | 2 + .../barcode/qrcode/version-02.codewords | 25 + .../graphics/barcode/qrcode/version-02.png | Bin 0 -> 3468 bytes .../barcode/qrcode/version-02.properties | 2 + .../barcode/qrcode/version-03.codewords | 29 + .../graphics/barcode/qrcode/version-03.png | Bin 0 -> 4515 bytes .../barcode/qrcode/version-03.properties | 2 + .../barcode/qrcode/version-04.codewords | 33 + .../graphics/barcode/qrcode/version-04.png | Bin 0 -> 5942 bytes .../barcode/qrcode/version-04.properties | 2 + .../barcode/qrcode/version-05.codewords | 37 + .../graphics/barcode/qrcode/version-05.png | Bin 0 -> 7518 bytes .../barcode/qrcode/version-05.properties | 2 + .../barcode/qrcode/version-06.codewords | 41 + .../graphics/barcode/qrcode/version-06.png | Bin 0 -> 8995 bytes .../barcode/qrcode/version-06.properties | 2 + .../barcode/qrcode/version-07.codewords | 45 + .../graphics/barcode/qrcode/version-07.png | Bin 0 -> 10765 bytes .../barcode/qrcode/version-07.properties | 2 + .../barcode/qrcode/version-08.codewords | 49 + .../graphics/barcode/qrcode/version-08.png | Bin 0 -> 13037 bytes .../barcode/qrcode/version-08.properties | 2 + .../barcode/qrcode/version-09.codewords | 53 + .../graphics/barcode/qrcode/version-09.png | Bin 0 -> 14844 bytes .../barcode/qrcode/version-09.properties | 2 + .../barcode/qrcode/version-10.codewords | 57 + .../graphics/barcode/qrcode/version-10.png | Bin 0 -> 17013 bytes .../barcode/qrcode/version-10.properties | 2 + .../barcode/upc/upc-a-basic.codewords | 1 + .../xbib/graphics/barcode/upc/upc-a-basic.png | Bin 0 -> 43876 bytes .../barcode/upc/upc-a-basic.properties | 2 + .../graphics/barcode/upc/upc-a-font.codewords | 1 + .../xbib/graphics/barcode/upc/upc-a-font.png | Bin 0 -> 38490 bytes .../barcode/upc/upc-a-font.properties | 3 + .../barcode/upc/upc-e-basic.codewords | 1 + .../xbib/graphics/barcode/upc/upc-e-basic.png | Bin 0 -> 32366 bytes .../barcode/upc/upc-e-basic.properties | 2 + .../usps-one-code-length-20.codewords | 1 + .../uspsonecode/usps-one-code-length-20.png | Bin 0 -> 4234 bytes .../usps-one-code-length-20.properties | 1 + .../usps-one-code-length-25.codewords | 1 + .../uspsonecode/usps-one-code-length-25.png | Bin 0 -> 4039 bytes .../usps-one-code-length-25.properties | 1 + .../usps-one-code-length-29.codewords | 1 + .../uspsonecode/usps-one-code-length-29.png | Bin 0 -> 4413 bytes .../usps-one-code-length-29.properties | 1 + .../usps-one-code-length-31.codewords | 1 + .../uspsonecode/usps-one-code-length-31.png | Bin 0 -> 3883 bytes .../usps-one-code-length-31.properties | 1 + build.gradle | 66 +- chart/NOTICE.txt | 6 + chart/src/main/java/module-info.java | 17 + .../java/org/xbib/graphics/chart/Chart.java | 391 +++ .../org/xbib/graphics/chart/ChartBuilder.java | 66 + .../xbib/graphics/chart/ChartComponent.java | 14 + .../org/xbib/graphics/chart/ChartTitle.java | 87 + .../org/xbib/graphics/chart/Histogram.java | 96 + .../org/xbib/graphics/chart/QuickChart.java | 59 + .../org/xbib/graphics/chart/axis/Axis.java | 293 ++ .../xbib/graphics/chart/axis/AxisPair.java | 363 +++ .../xbib/graphics/chart/axis/AxisTick.java | 62 + .../chart/axis/AxisTickCalculator.java | 87 + .../axis/AxisTickCalculatorCategory.java | 51 + .../chart/axis/AxisTickCalculatorInstant.java | 161 + .../axis/AxisTickCalculatorLogarithmic.java | 62 + .../chart/axis/AxisTickCalculatorNumber.java | 89 + .../axis/AxisTickCalculatorOverride.java | 79 + .../graphics/chart/axis/AxisTickLabels.java | 168 + .../graphics/chart/axis/AxisTickMarks.java | 101 + .../xbib/graphics/chart/axis/AxisTitle.java | 108 + .../xbib/graphics/chart/axis/DataType.java | 5 + .../xbib/graphics/chart/axis/Direction.java | 5 + .../graphics/chart/axis/TextAlignment.java | 5 + .../graphics/chart/axis/YAxisPosition.java | 5 + .../graphics/chart/bubble/BubbleChart.java | 204 ++ .../chart/bubble/BubbleChartBuilder.java | 27 + .../graphics/chart/bubble/BubbleLegend.java | 68 + .../graphics/chart/bubble/BubbleSeries.java | 33 + .../chart/bubble/BubbleSeriesRenderStyle.java | 19 + .../graphics/chart/bubble/BubbleStyler.java | 44 + .../chart/category/CategoryChart.java | 706 ++++ .../chart/category/CategoryChartBuilder.java | 40 + .../chart/category/CategorySeries.java | 36 + .../category/CategorySeriesRenderStyle.java | 30 + .../chart/category/CategoryStyler.java | 96 + .../chart/formatter/DateFormatter.java | 37 + .../chart/formatter/NumberFormatter.java | 102 + .../chart/formatter/NumberLogFormatter.java | 54 + .../chart/formatter/StringFormatter.java | 21 + .../xbib/graphics/chart/io/BitmapFormat.java | 5 + .../xbib/graphics/chart/io/CSVExporter.java | 91 + .../xbib/graphics/chart/io/CSVImporter.java | 109 + .../chart/io/VectorGraphicsFormat.java | 5 + .../graphics/chart/io/vector/Document.java | 13 + .../chart/io/vector/EPSGraphics2D.java | 42 + .../chart/io/vector/GraphicsState.java | 275 ++ .../chart/io/vector/PDFGraphics2D.java | 38 + .../chart/io/vector/ProcessingPipeline.java | 53 + .../graphics/chart/io/vector/Processor.java | 9 + .../chart/io/vector/SVGGraphics2D.java | 36 + .../chart/io/vector/SizedDocument.java | 19 + .../chart/io/vector/VectorGraphics2D.java | 894 ++++++ .../graphics/chart/io/vector/VectorHints.java | 83 + .../chart/io/vector/eps/EPSDocument.java | 476 +++ .../chart/io/vector/eps/EPSProcessor.java | 23 + .../vector/intermediate/CommandHandler.java | 8 + .../commands/AffineTransformCommand.java | 10 + .../vector/intermediate/commands/Command.java | 31 + .../intermediate/commands/CreateCommand.java | 10 + .../intermediate/commands/DisposeCommand.java | 10 + .../commands/DrawImageCommand.java | 58 + .../commands/DrawShapeCommand.java | 12 + .../commands/DrawStringCommand.java | 30 + .../commands/FillShapeCommand.java | 12 + .../vector/intermediate/commands/Group.java | 16 + .../intermediate/commands/RotateCommand.java | 38 + .../intermediate/commands/ScaleCommand.java | 31 + .../commands/SetBackgroundCommand.java | 10 + .../intermediate/commands/SetClipCommand.java | 10 + .../commands/SetColorCommand.java | 10 + .../commands/SetCompositeCommand.java | 10 + .../intermediate/commands/SetFontCommand.java | 10 + .../intermediate/commands/SetHintCommand.java | 24 + .../commands/SetPaintCommand.java | 10 + .../commands/SetStrokeCommand.java | 10 + .../commands/SetTransformCommand.java | 10 + .../commands/SetXORModeCommand.java | 10 + .../intermediate/commands/ShearCommand.java | 31 + .../intermediate/commands/StateCommand.java | 8 + .../commands/TransformCommand.java | 17 + .../commands/TranslateCommand.java | 31 + .../AbsoluteToRelativeTransformsFilter.java | 63 + .../FillPaintedShapeAsImageFilter.java | 70 + .../vector/intermediate/filters/Filter.java | 48 + .../intermediate/filters/GroupingFilter.java | 47 + .../intermediate/filters/OptimizeFilter.java | 57 + .../filters/StateChangeGroupingFilter.java | 18 + .../chart/io/vector/pdf/GeneratedPayload.java | 30 + .../chart/io/vector/pdf/PDFDocument.java | 647 ++++ .../chart/io/vector/pdf/PDFObject.java | 22 + .../chart/io/vector/pdf/PDFProcessor.java | 25 + .../graphics/chart/io/vector/pdf/Payload.java | 50 + .../chart/io/vector/pdf/Resources.java | 99 + .../chart/io/vector/pdf/SizePayload.java | 28 + .../chart/io/vector/svg/SVGDocument.java | 552 ++++ .../chart/io/vector/svg/SVGProcessor.java | 21 + .../io/vector/util/ASCII85EncodeStream.java | 102 + .../chart/io/vector/util/AlphaToMaskOp.java | 101 + .../io/vector/util/Base64EncodeStream.java | 83 + .../chart/io/vector/util/DataUtils.java | 240 ++ .../io/vector/util/FlateEncodeStream.java | 11 + .../io/vector/util/FormattingWriter.java | 66 + .../chart/io/vector/util/GraphicsUtils.java | 411 +++ .../chart/io/vector/util/ImageDataStream.java | 131 + .../io/vector/util/LineWrapOutputStream.java | 37 + .../chart/io/vector/util/PageSize.java | 48 + .../graphics/chart/legend/AbstractLegend.java | 234 ++ .../xbib/graphics/chart/legend/Legend.java | 10 + .../graphics/chart/legend/LegendLayout.java | 5 + .../graphics/chart/legend/LegendPosition.java | 5 + .../chart/legend/LegendRenderType.java | 5 + .../chart/legend/LegendRenderable.java | 6 + .../graphics/chart/legend/MarkerLegend.java | 117 + .../xbib/graphics/chart/ohlc/OHLCChart.java | 328 ++ .../graphics/chart/ohlc/OHLCChartBuilder.java | 35 + .../xbib/graphics/chart/ohlc/OHLCLegend.java | 72 + .../xbib/graphics/chart/ohlc/OHLCSeries.java | 130 + .../chart/ohlc/OHLCSeriesRenderStyle.java | 21 + .../xbib/graphics/chart/ohlc/OHLCStyler.java | 33 + .../org/xbib/graphics/chart/pie/PieChart.java | 354 ++ .../graphics/chart/pie/PieChartBuilder.java | 15 + .../xbib/graphics/chart/pie/PieLegend.java | 59 + .../xbib/graphics/chart/pie/PieSeries.java | 41 + .../chart/pie/PieSeriesRenderStyle.java | 5 + .../xbib/graphics/chart/pie/PieStyler.java | 198 ++ .../graphics/chart/plot/AxesChartPlot.java | 43 + .../graphics/chart/plot/CircularPlot.java | 48 + .../xbib/graphics/chart/plot/ContentPlot.java | 46 + .../org/xbib/graphics/chart/plot/Plot.java | 22 + .../xbib/graphics/chart/plot/SurfacePlot.java | 13 + .../chart/plot/SurfacePlotAxesChart.java | 104 + .../chart/series/AxesChartSeries.java | 104 + .../chart/series/AxesChartSeriesCategory.java | 102 + .../AxesChartSeriesNumericalNoErrorBars.java | 147 + .../graphics/chart/series/MarkerSeries.java | 37 + .../chart/series/NoMarkersSeries.java | 31 + .../xbib/graphics/chart/series/Series.java | 73 + .../graphics/chart/style/AxesChartStyler.java | 677 ++++ .../style/SeriesColorMarkerLineStyle.java | 31 + .../SeriesColorMarkerLineStyleCycler.java | 45 + .../org/xbib/graphics/chart/style/Styler.java | 518 +++ .../graphics/chart/theme/DefaultTheme.java | 313 ++ .../graphics/chart/theme/GGPlot2Theme.java | 283 ++ .../graphics/chart/theme/MatlabTheme.java | 284 ++ .../org/xbib/graphics/chart/theme/Theme.java | 431 +++ .../org/xbib/graphics/chart/xy/XYChart.java | 364 +++ .../graphics/chart/xy/XYChartBuilder.java | 37 + .../org/xbib/graphics/chart/xy/XYSeries.java | 38 + .../chart/xy/XYSeriesRenderStyle.java | 28 + .../org/xbib/graphics/chart/xy/XYStyler.java | 49 + .../xbib/graphics/chart/HistogramTest.java | 28 + .../org/xbib/graphics/chart/MatlabTest.java | 67 + .../graphics/chart/demo/AreaChartTest.java | 146 + .../graphics/chart/demo/BarChartTest.java | 134 + .../graphics/chart/demo/BubbleChartTest.java | 34 + .../graphics/chart/demo/PieChartTest.java | 41 + .../graphics/chart/demo/QuickChartTest.java | 23 + .../graphics/chart/demo/ScatterChartTest.java | 107 + .../graphics/chart/demo/StickChartTest.java | 34 + .../chart/io/vector/GraphicsStateTest.java | 63 + .../graphics/chart/io/vector/TestUtils.java | 267 ++ .../chart/io/vector/TestUtilsTest.java | 191 ++ .../chart/io/vector/VectorGraphics2DTest.java | 63 + .../chart/io/vector/eps/EPSProcessorTest.java | 70 + ...bsoluteToRelativeTransformsFilterTest.java | 91 + .../FillPaintedShapeAsImageFilterTest.java | 54 + .../intermediate/filters/FilterTest.java | 98 + .../filters/GroupingFilterTest.java | 56 + .../chart/io/vector/pdf/PDFProcessorTest.java | 114 + .../chart/io/vector/svg/SVGProcessorTest.java | 70 + .../vector/util/ASCII85EncodeStreamTest.java | 53 + .../vector/util/Base64EncodeStreamTest.java | 63 + .../chart/io/vector/util/DataUtilsTest.java | 28 + .../io/vector/util/GraphicsUtilsTest.java | 142 + .../chart/io/vector/visual/AbstractTest.java | 170 + .../chart/io/vector/visual/CharacterTest.java | 40 + .../chart/io/vector/visual/ClippingTest.java | 33 + .../chart/io/vector/visual/ColorTest.java | 28 + .../chart/io/vector/visual/EmptyFileTest.java | 10 + .../chart/io/vector/visual/FontTest.java | 47 + .../chart/io/vector/visual/ImageTest.java | 29 + .../chart/io/vector/visual/PaintTest.java | 37 + .../chart/io/vector/visual/ShapesTest.java | 127 + .../chart/io/vector/visual/StrokeTest.java | 90 + .../io/vector/visual/SwingExportTest.java | 24 + .../chart/io/vector/visual/TestBrowser.java | 263 ++ .../chart/io/vector/visual/TransformTest.java | 59 + .../graphics/chart/swing/SwingWrapper.java | 282 ++ gradle.properties | 8 +- gradle/compile/java.gradle | 44 + gradle/documentation/asciidoc.gradle | 55 + gradle/ext.gradle | 8 - gradle/ide/idea.gradle | 13 + gradle/publish.gradle | 70 - gradle/publishing/publication.gradle | 66 + gradle/publishing/sonatype.gradle | 11 + gradle/test/junit5.gradle | 28 + gradle/wrapper/gradle-wrapper.jar | Bin 0 -> 59203 bytes gradle/wrapper/gradle-wrapper.properties | 5 + gradlew | 185 ++ gradlew.bat | 89 + io-vector/NOTICE.txt | 5 + .../build/docs/javadoc/allclasses-index.html | 372 +++ .../build/docs/javadoc/allpackages-index.html | 120 + .../build/docs/javadoc/constant-values.html | 111 + .../build/docs/javadoc/deprecated-list.html | 80 + io-vector/build/docs/javadoc/element-list | 8 + io-vector/build/docs/javadoc/help-doc.html | 185 ++ io-vector/build/docs/javadoc/index-all.html | 1081 +++++++ io-vector/build/docs/javadoc/index.html | 25 + .../docs/javadoc/jquery-ui.overrides.css | 34 + .../build/docs/javadoc/member-search-index.js | 1 + .../build/docs/javadoc/module-search-index.js | 1 + .../module-summary.html | 138 + .../org/xbib/graphics/io/vector/Command.html | 252 ++ .../graphics/io/vector/GraphicsState.html | 656 ++++ .../org/xbib/graphics/io/vector/PageSize.html | 406 +++ .../xbib/graphics/io/vector/Processor.html | 185 ++ .../graphics/io/vector/ProcessorResult.html | 215 ++ .../graphics/io/vector/VectorGraphics2D.html | 1664 ++++++++++ .../io/vector/VectorGraphicsFormat.html | 291 ++ .../commands/AffineTransformCommand.html | 188 ++ .../io/vector/commands/CreateCommand.html | 184 ++ .../io/vector/commands/DisposeCommand.html | 184 ++ .../io/vector/commands/DrawImageCommand.html | 305 ++ .../io/vector/commands/DrawShapeCommand.html | 182 ++ .../io/vector/commands/DrawStringCommand.html | 253 ++ .../io/vector/commands/FillShapeCommand.html | 182 ++ .../graphics/io/vector/commands/Group.html | 223 ++ .../io/vector/commands/RotateCommand.html | 268 ++ .../io/vector/commands/ScaleCommand.html | 255 ++ .../vector/commands/SetBackgroundCommand.html | 184 ++ .../io/vector/commands/SetClipCommand.html | 184 ++ .../io/vector/commands/SetColorCommand.html | 184 ++ .../vector/commands/SetCompositeCommand.html | 184 ++ .../io/vector/commands/SetFontCommand.html | 184 ++ .../io/vector/commands/SetHintCommand.html | 242 ++ .../io/vector/commands/SetPaintCommand.html | 184 ++ .../io/vector/commands/SetStrokeCommand.html | 184 ++ .../vector/commands/SetTransformCommand.html | 184 ++ .../io/vector/commands/SetXORModeCommand.html | 184 ++ .../io/vector/commands/ShearCommand.html | 255 ++ .../io/vector/commands/StateCommand.html | 186 ++ .../io/vector/commands/TransformCommand.html | 227 ++ .../io/vector/commands/TranslateCommand.html | 255 ++ .../io/vector/commands/package-summary.html | 195 ++ .../io/vector/commands/package-tree.html | 126 + .../graphics/io/vector/eps/EPSGraphics2D.html | 217 ++ .../graphics/io/vector/eps/EPSProcessor.html | 232 ++ .../io/vector/eps/EPSProcessorResult.html | 263 ++ .../io/vector/eps/package-summary.html | 114 + .../graphics/io/vector/eps/package-tree.html | 107 + .../AbsoluteToRelativeTransformsFilter.html | 254 ++ .../FillPaintedShapeAsImageFilter.html | 254 ++ .../graphics/io/vector/filters/Filter.html | 293 ++ .../io/vector/filters/GroupingFilter.html | 286 ++ .../io/vector/filters/OptimizeFilter.html | 271 ++ .../filters/StateChangeGroupingFilter.html | 242 ++ .../io/vector/filters/package-summary.html | 123 + .../io/vector/filters/package-tree.html | 104 + .../graphics/io/vector/package-summary.html | 159 + .../xbib/graphics/io/vector/package-tree.html | 125 + .../io/vector/pdf/GeneratedPayload.html | 277 ++ .../graphics/io/vector/pdf/PDFGraphics2D.html | 217 ++ .../graphics/io/vector/pdf/PDFObject.html | 259 ++ .../graphics/io/vector/pdf/PDFProcessor.html | 242 ++ .../io/vector/pdf/PDFProcessorResult.html | 298 ++ .../xbib/graphics/io/vector/pdf/Payload.html | 309 ++ .../graphics/io/vector/pdf/Resources.html | 253 ++ .../graphics/io/vector/pdf/SizePayload.html | 248 ++ .../io/vector/pdf/package-summary.html | 134 + .../graphics/io/vector/pdf/package-tree.html | 125 + .../graphics/io/vector/svg/SVGGraphics2D.html | 217 ++ .../graphics/io/vector/svg/SVGProcessor.html | 229 ++ .../io/vector/svg/SVGProcessorResult.html | 275 ++ .../io/vector/svg/package-summary.html | 114 + .../graphics/io/vector/svg/package-tree.html | 107 + .../io/vector/util/ASCII85EncodeStream.html | 284 ++ .../io/vector/util/AlphaToMaskOp.html | 314 ++ .../io/vector/util/Base64EncodeStream.html | 270 ++ .../graphics/io/vector/util/DataUtils.html | 497 +++ .../io/vector/util/FlateEncodeStream.html | 208 ++ .../io/vector/util/FormattingWriter.html | 363 +++ .../io/vector/util/GraphicsUtils.html | 375 +++ .../util/ImageDataStream.Interleaving.html | 305 ++ .../io/vector/util/ImageDataStream.html | 283 ++ .../io/vector/util/LineWrapOutputStream.html | 301 ++ .../io/vector/util/VectorHints.Key.html | 259 ++ .../io/vector/util/VectorHints.Value.html | 263 ++ .../graphics/io/vector/util/VectorHints.html | 314 ++ .../io/vector/util/package-summary.html | 171 + .../graphics/io/vector/util/package-tree.html | 138 + .../build/docs/javadoc/overview-tree.html | 235 ++ .../docs/javadoc/package-search-index.js | 1 + .../build/docs/javadoc/resources/glass.png | Bin 0 -> 499 bytes io-vector/build/docs/javadoc/resources/x.png | Bin 0 -> 394 bytes .../images/ui-bg_glass_55_fbf9ee_1x400.png | Bin 0 -> 335 bytes .../images/ui-bg_glass_65_dadada_1x400.png | Bin 0 -> 262 bytes .../images/ui-bg_glass_75_dadada_1x400.png | Bin 0 -> 262 bytes .../images/ui-bg_glass_75_e6e6e6_1x400.png | Bin 0 -> 262 bytes .../images/ui-bg_glass_95_fef1ec_1x400.png | Bin 0 -> 332 bytes .../ui-bg_highlight-soft_75_cccccc_1x100.png | Bin 0 -> 280 bytes .../images/ui-icons_222222_256x240.png | Bin 0 -> 6922 bytes .../images/ui-icons_2e83ff_256x240.png | Bin 0 -> 4549 bytes .../images/ui-icons_454545_256x240.png | Bin 0 -> 6992 bytes .../images/ui-icons_888888_256x240.png | Bin 0 -> 6999 bytes .../images/ui-icons_cd0a0a_256x240.png | Bin 0 -> 4549 bytes .../javadoc/script-dir/jquery-3.5.1.min.js | 2 + .../docs/javadoc/script-dir/jquery-ui.min.css | 7 + .../docs/javadoc/script-dir/jquery-ui.min.js | 6 + .../script-dir/jquery-ui.structure.min.css | 5 + io-vector/build/docs/javadoc/script.js | 103 + io-vector/build/docs/javadoc/search.js | 378 +++ io-vector/build/docs/javadoc/stylesheet.css | 792 +++++ .../build/docs/javadoc/tag-search-index.js | 1 + .../build/docs/javadoc/type-search-index.js | 1 + .../build/libs/io-vector-3.0.0-javadoc.jar | Bin 0 -> 261 bytes .../build/libs/io-vector-3.0.0-sources.jar | Bin 0 -> 60307 bytes io-vector/build/libs/io-vector-3.0.0.jar | Bin 0 -> 109577 bytes ...bsoluteToRelativeTransformsFilterTest.html | 106 + ...ers.FillPaintedShapeAsImageFilterTest.html | 101 + ...g.xbib.graphics.io.filters.FilterTest.html | 106 + ...raphics.io.filters.GroupingFilterTest.html | 96 + ....graphics.io.vector.GraphicsStateTest.html | 106 + ...xbib.graphics.io.vector.TestUtilsTest.html | 131 + ...aphics.io.vector.VectorGraphics2DTest.html | 106 + ...aphics.io.vector.eps.EPSProcessorTest.html | 96 + ...aphics.io.vector.pdf.PDFProcessorTest.html | 96 + ...aphics.io.vector.svg.SVGProcessorTest.html | 106 + ...o.vector.util.ASCII85EncodeStreamTest.html | 106 + ...io.vector.util.Base64EncodeStreamTest.html | 106 + ...graphics.io.vector.util.DataUtilsTest.html | 106 + ...hics.io.vector.util.GraphicsUtilsTest.html | 111 + .../reports/tests/test/css/base-style.css | 179 ++ .../build/reports/tests/test/css/style.css | 84 + io-vector/build/reports/tests/test/index.html | 313 ++ .../build/reports/tests/test/js/report.js | 194 ++ .../org.xbib.graphics.io.filters.html | 133 + .../org.xbib.graphics.io.vector.eps.html | 103 + .../packages/org.xbib.graphics.io.vector.html | 123 + .../org.xbib.graphics.io.vector.pdf.html | 103 + .../org.xbib.graphics.io.vector.svg.html | 103 + .../org.xbib.graphics.io.vector.util.html | 133 + ...AbsoluteToRelativeTransformsFilterTest.xml | 9 + ...ters.FillPaintedShapeAsImageFilterTest.xml | 8 + ...rg.xbib.graphics.io.filters.FilterTest.xml | 9 + ...graphics.io.filters.GroupingFilterTest.xml | 7 + ...b.graphics.io.vector.GraphicsStateTest.xml | 9 + ....xbib.graphics.io.vector.TestUtilsTest.xml | 14 + ...raphics.io.vector.VectorGraphics2DTest.xml | 9 + ...raphics.io.vector.eps.EPSProcessorTest.xml | 7 + ...raphics.io.vector.pdf.PDFProcessorTest.xml | 7 + ...raphics.io.vector.svg.SVGProcessorTest.xml | 9 + ...io.vector.util.ASCII85EncodeStreamTest.xml | 9 + ....io.vector.util.Base64EncodeStreamTest.xml | 9 + ....graphics.io.vector.util.DataUtilsTest.xml | 9 + ...phics.io.vector.util.GraphicsUtilsTest.xml | 10 + .../build/test-results/test/binary/output.bin | 0 .../test-results/test/binary/output.bin.idx | Bin 0 -> 1 bytes .../test-results/test/binary/results.bin | Bin 0 -> 3972 bytes .../compileJava/source-classes-mapping.txt | 128 + .../source-classes-mapping.txt | 70 + io-vector/build/tmp/jar/MANIFEST.MF | 3 + io-vector/build/tmp/javadoc/javadoc.options | 69 + io-vector/build/tmp/javadocJar/MANIFEST.MF | 2 + io-vector/build/tmp/sourcesJar/MANIFEST.MF | 2 + io-vector/src/main/java/module-info.java | 8 + .../org/xbib/graphics/io/vector/Command.java | 29 + .../graphics/io/vector/GraphicsState.java | 274 ++ .../org/xbib/graphics/io/vector/PageSize.java | 75 + .../xbib/graphics/io/vector/Processor.java | 8 + .../graphics/io/vector/ProcessorResult.java | 13 + .../graphics/io/vector/VectorGraphics2D.java | 896 ++++++ .../io/vector/VectorGraphicsFormat.java | 5 + .../commands/AffineTransformCommand.java | 10 + .../io/vector/commands/CreateCommand.java | 10 + .../io/vector/commands/DisposeCommand.java | 10 + .../io/vector/commands/DrawImageCommand.java | 59 + .../io/vector/commands/DrawShapeCommand.java | 13 + .../io/vector/commands/DrawStringCommand.java | 31 + .../io/vector/commands/FillShapeCommand.java | 13 + .../graphics/io/vector/commands/Group.java | 17 + .../io/vector/commands/RotateCommand.java | 38 + .../io/vector/commands/ScaleCommand.java | 31 + .../vector/commands/SetBackgroundCommand.java | 10 + .../io/vector/commands/SetClipCommand.java | 10 + .../io/vector/commands/SetColorCommand.java | 10 + .../vector/commands/SetCompositeCommand.java | 10 + .../io/vector/commands/SetFontCommand.java | 10 + .../io/vector/commands/SetHintCommand.java | 24 + .../io/vector/commands/SetPaintCommand.java | 10 + .../io/vector/commands/SetStrokeCommand.java | 10 + .../vector/commands/SetTransformCommand.java | 10 + .../io/vector/commands/SetXORModeCommand.java | 10 + .../io/vector/commands/ShearCommand.java | 31 + .../io/vector/commands/StateCommand.java | 10 + .../io/vector/commands/TransformCommand.java | 17 + .../io/vector/commands/TranslateCommand.java | 31 + .../graphics/io/vector/eps/EPSGraphics2D.java | 34 + .../graphics/io/vector/eps/EPSProcessor.java | 22 + .../io/vector/eps/EPSProcessorResult.java | 470 +++ .../AbsoluteToRelativeTransformsFilter.java | 63 + .../FillPaintedShapeAsImageFilter.java | 69 + .../graphics/io/vector/filters/Filter.java | 47 + .../io/vector/filters/GroupingFilter.java | 46 + .../io/vector/filters/OptimizeFilter.java | 56 + .../filters/StateChangeGroupingFilter.java | 17 + .../io/vector/pdf/GeneratedPayload.java | 26 + .../graphics/io/vector/pdf/PDFGraphics2D.java | 30 + .../graphics/io/vector/pdf/PDFObject.java | 22 + .../graphics/io/vector/pdf/PDFProcessor.java | 37 + .../io/vector/pdf/PDFProcessorResult.java | 585 ++++ .../xbib/graphics/io/vector/pdf/Payload.java | 54 + .../graphics/io/vector/pdf/Resources.java | 98 + .../graphics/io/vector/pdf/SizePayload.java | 25 + .../graphics/io/vector/svg/SVGGraphics2D.java | 27 + .../graphics/io/vector/svg/SVGProcessor.java | 21 + .../io/vector/svg/SVGProcessorResult.java | 547 ++++ .../io/vector/util/ASCII85EncodeStream.java | 102 + .../io/vector/util/AlphaToMaskOp.java | 101 + .../io/vector/util/Base64EncodeStream.java | 83 + .../graphics/io/vector/util/DataUtils.java | 240 ++ .../io/vector/util/FlateEncodeStream.java | 11 + .../io/vector/util/FormattingWriter.java | 66 + .../io/vector/util/GraphicsUtils.java | 411 +++ .../io/vector/util/ImageDataStream.java | 131 + .../io/vector/util/LineWrapOutputStream.java | 40 + .../graphics/io/vector/util/VectorHints.java | 83 + ...bsoluteToRelativeTransformsFilterTest.java | 90 + .../FillPaintedShapeAsImageFilterTest.java | 53 + .../xbib/graphics/io/filters/FilterTest.java | 99 + .../io/filters/GroupingFilterTest.java | 56 + .../graphics/io/vector/GraphicsStateTest.java | 55 + .../xbib/graphics/io/vector/TestUtils.java | 265 ++ .../graphics/io/vector/TestUtilsTest.java | 189 ++ .../io/vector/VectorGraphics2DTest.java | 56 + .../io/vector/eps/EPSProcessorTest.java | 71 + .../io/vector/pdf/PDFProcessorTest.java | 109 + .../io/vector/svg/SVGProcessorTest.java | 68 + .../vector/util/ASCII85EncodeStreamTest.java | 53 + .../vector/util/Base64EncodeStreamTest.java | 63 + .../io/vector/util/DataUtilsTest.java | 28 + .../io/vector/util/GraphicsUtilsTest.java | 142 + .../xbib/graphics/io/visual/AbstractTest.java | 86 + .../graphics/io/visual/CharacterTest.java | 44 + .../xbib/graphics/io/visual/ClippingTest.java | 37 + .../xbib/graphics/io/visual/ColorTest.java | 32 + .../graphics/io/visual/EmptyFileTest.java | 14 + .../org/xbib/graphics/io/visual/FontTest.java | 50 + .../xbib/graphics/io/visual/ImageTest.java | 30 + .../xbib/graphics/io/visual/PaintTest.java | 40 + .../xbib/graphics/io/visual/ShapesTest.java | 131 + .../xbib/graphics/io/visual/StrokeTest.java | 94 + .../graphics/io/visual/SwingExportTest.java | 27 + .../xbib/graphics/io/visual/TestBrowser.java | 253 ++ .../graphics/io/visual/TransformTest.java | 63 + layout-pdfbox/NOTICE.txt | 7 + layout-pdfbox/build.gradle | 3 + .../pdfbox/elements/ControlElement.java | 25 + .../layout/pdfbox/elements/Cutter.java | 63 + .../layout/pdfbox/elements/Dimension.java | 57 + .../layout/pdfbox/elements/Dividable.java | 51 + .../layout/pdfbox/elements/Document.java | 364 +++ .../layout/pdfbox/elements/Drawable.java | 57 + .../layout/pdfbox/elements/Element.java | 8 + .../layout/pdfbox/elements/Frame.java | 680 ++++ .../pdfbox/elements/HorizontalRuler.java | 96 + .../layout/pdfbox/elements/ImageElement.java | 137 + .../layout/pdfbox/elements/Orientation.java | 7 + .../layout/pdfbox/elements/PageFormat.java | 369 +++ .../layout/pdfbox/elements/Paragraph.java | 85 + .../pdfbox/elements/PositionControl.java | 103 + .../layout/pdfbox/elements/Rectangle.java | 58 + .../pdfbox/elements/VerticalSpacer.java | 61 + .../pdfbox/elements/render/ColumnLayout.java | 93 + .../elements/render/ColumnLayoutHint.java | 86 + .../layout/pdfbox/elements/render/Layout.java | 11 + .../pdfbox/elements/render/LayoutHint.java | 14 + .../pdfbox/elements/render/RenderContext.java | 462 +++ .../elements/render/RenderListener.java | 26 + .../pdfbox/elements/render/Renderer.java | 25 + .../elements/render/VerticalLayout.java | 294 ++ .../elements/render/VerticalLayoutHint.java | 180 ++ .../layout/pdfbox/shape/AbstractShape.java | 59 + .../graphics/layout/pdfbox/shape/Ellipse.java | 26 + .../graphics/layout/pdfbox/shape/Rect.java | 20 + .../layout/pdfbox/shape/RoundRect.java | 126 + .../graphics/layout/pdfbox/shape/Shape.java | 71 + .../graphics/layout/pdfbox/shape/Stroke.java | 228 ++ .../layout/pdfbox/text/Alignment.java | 9 + .../graphics/layout/pdfbox/text/Area.java | 21 + .../graphics/layout/pdfbox/text/BaseFont.java | 52 + .../layout/pdfbox/text/Constants.java | 28 + .../layout/pdfbox/text/ControlCharacter.java | 83 + .../layout/pdfbox/text/ControlCharacters.java | 284 ++ .../layout/pdfbox/text/ControlFragment.java | 71 + .../layout/pdfbox/text/DrawContext.java | 26 + .../layout/pdfbox/text/DrawListener.java | 18 + .../layout/pdfbox/text/DrawableText.java | 24 + .../layout/pdfbox/text/FontDescriptor.java | 81 + .../graphics/layout/pdfbox/text/Indent.java | 176 + .../layout/pdfbox/text/IndentCharacters.java | 341 ++ .../graphics/layout/pdfbox/text/NewLine.java | 37 + .../graphics/layout/pdfbox/text/Position.java | 80 + .../pdfbox/text/ReplacedWhitespace.java | 29 + .../layout/pdfbox/text/SpaceUnit.java | 34 + .../layout/pdfbox/text/StyledText.java | 217 ++ .../graphics/layout/pdfbox/text/TextFlow.java | 287 ++ .../layout/pdfbox/text/TextFlowUtil.java | 354 ++ .../layout/pdfbox/text/TextFragment.java | 25 + .../graphics/layout/pdfbox/text/TextLine.java | 274 ++ .../layout/pdfbox/text/TextSequence.java | 9 + .../layout/pdfbox/text/TextSequenceUtil.java | 582 ++++ .../layout/pdfbox/text/WidthRespecting.java | 21 + .../layout/pdfbox/text/WrappingNewLine.java | 36 + .../pdfbox/text/annotations/Annotated.java | 18 + .../text/annotations/AnnotatedStyledText.java | 108 + .../pdfbox/text/annotations/Annotation.java | 8 + .../annotations/AnnotationCharacters.java | 324 ++ .../annotations/AnnotationDrawListener.java | 103 + .../text/annotations/AnnotationProcessor.java | 51 + .../AnnotationProcessorFactory.java | 45 + .../pdfbox/text/annotations/Annotations.java | 114 + .../HyperlinkAnnotationProcessor.java | 196 ++ .../UnderlineAnnotationProcessor.java | 97 + .../pdfbox/util/CompatibilityHelper.java | 326 ++ .../layout/pdfbox/util/Enumerator.java | 17 + .../layout/pdfbox/util/EnumeratorFactory.java | 65 + .../layout/pdfbox/util/Enumerators.java | 241 ++ .../graphics/layout/pdfbox/util/Pair.java | 66 + .../layout/pdfbox/util/WordBreaker.java | 28 + .../pdfbox/util/WordBreakerFactory.java | 66 + .../layout/pdfbox/util/WordBreakers.java | 148 + .../xbib/graphics/layout/pdfbox/Aligned.java | 47 + .../xbib/graphics/layout/pdfbox/Columns.java | 87 + .../layout/pdfbox/CustomAnnotation.java | 226 ++ .../layout/pdfbox/CustomRenderer.java | 136 + .../xbib/graphics/layout/pdfbox/Frames.java | 96 + .../xbib/graphics/layout/pdfbox/HelloDoc.java | 25 + .../graphics/layout/pdfbox/Indentation.java | 158 + .../graphics/layout/pdfbox/Landscape.java | 111 + .../xbib/graphics/layout/pdfbox/Letter.java | 87 + .../graphics/layout/pdfbox/LineSpacing.java | 54 + .../xbib/graphics/layout/pdfbox/Links.java | 73 + .../xbib/graphics/layout/pdfbox/Listener.java | 83 + .../graphics/layout/pdfbox/LowLevelText.java | 120 + .../xbib/graphics/layout/pdfbox/Margin.java | 58 + .../xbib/graphics/layout/pdfbox/Markup.java | 81 + .../graphics/layout/pdfbox/MultiplePages.java | 76 + .../xbib/graphics/layout/pdfbox/Rotation.java | 116 + .../xbib/graphics/layout/pdfbox/aligned.pdf | Bin 0 -> 1251 bytes .../org/xbib/graphics/layout/pdfbox/arrow.png | Bin 0 -> 13133 bytes .../xbib/graphics/layout/pdfbox/columns.pdf | Bin 0 -> 11018 bytes .../layout/pdfbox/customannotation.pdf | 116 + .../graphics/layout/pdfbox/customrenderer.pdf | Bin 0 -> 10178 bytes .../xbib/graphics/layout/pdfbox/frames.pdf | Bin 0 -> 4013 bytes .../xbib/graphics/layout/pdfbox/hellodoc.pdf | 76 + .../graphics/layout/pdfbox/indentation.pdf | Bin 0 -> 2668 bytes .../xbib/graphics/layout/pdfbox/landscape.pdf | Bin 0 -> 10456 bytes .../xbib/graphics/layout/pdfbox/letter.pdf | Bin 0 -> 9032 bytes .../graphics/layout/pdfbox/linespacing.pdf | Bin 0 -> 1739 bytes .../org/xbib/graphics/layout/pdfbox/links.pdf | Bin 0 -> 8186 bytes .../xbib/graphics/layout/pdfbox/listener.pdf | Bin 0 -> 8985 bytes .../graphics/layout/pdfbox/lowleveltext.pdf | Bin 0 -> 3702 bytes .../xbib/graphics/layout/pdfbox/margin.pdf | 79 + .../xbib/graphics/layout/pdfbox/markup.pdf | Bin 0 -> 2920 bytes .../graphics/layout/pdfbox/multiplepages.pdf | Bin 0 -> 10075 bytes .../xbib/graphics/layout/pdfbox/rotation.pdf | Bin 0 -> 9812 bytes png/NOTICE.txt | 15 + png/build.gradle | 0 .../services/javax.imageio.spi.ImageWriterSpi | 1 + .../compileJava/source-classes-mapping.txt | 234 ++ .../source-classes-mapping.txt | 19 + .../plugins/png/AbstractScanlineProvider.java | 105 + .../imageio/plugins/png/PNGWriter.java | 104 + .../plugins/png/RasterByteABGRProvider.java | 56 + .../png/RasterByteGrayAlphaProvider.java | 51 + .../RasterByteRepackSingleBandProvider.java | 65 + .../png/RasterByteSingleBandProvider.java | 35 + ...erByteSingleBandSkippingBytesProvider.java | 54 + .../plugins/png/RasterIntABGRProvider.java | 60 + .../plugins/png/RasterShortABGRProvider.java | 90 + .../png/RasterShortGrayAlphaProvider.java | 49 + .../png/RasterShortSingleBandProvider.java | 40 + .../imageio/plugins/png/ScanlineCursor.java | 52 + .../imageio/plugins/png/ScanlineProvider.java | 41 + .../plugins/png/ScanlineProviderFactory.java | 151 + .../png/pngj/BufferedStreamFeeder.java | 221 ++ .../imageio/plugins/png/pngj/ChunkReader.java | 258 ++ .../plugins/png/pngj/ChunkSeqBuffering.java | 35 + .../plugins/png/pngj/ChunkSeqReader.java | 473 +++ .../plugins/png/pngj/ChunkSeqReaderPng.java | 341 ++ .../plugins/png/pngj/ChunkSeqSkipping.java | 76 + .../plugins/png/pngj/DeflatedChunkReader.java | 88 + .../plugins/png/pngj/DeflatedChunksSet.java | 452 +++ .../plugins/png/pngj/Deinterlacer.java | 208 ++ .../plugins/png/pngj/ErrorBehaviour.java | 14 + .../imageio/plugins/png/pngj/FilterType.java | 119 + .../plugins/png/pngj/IBytesConsumer.java | 31 + .../plugins/png/pngj/IChunkFactory.java | 21 + .../imageio/plugins/png/pngj/IImageLine.java | 45 + .../plugins/png/pngj/IImageLineArray.java | 24 + .../plugins/png/pngj/IImageLineFactory.java | 8 + .../plugins/png/pngj/IImageLineSet.java | 59 + .../png/pngj/IImageLineSetFactory.java | 25 + .../plugins/png/pngj/IPngWriterFactory.java | 7 + .../plugins/png/pngj/IdatChunkWriter.java | 136 + .../imageio/plugins/png/pngj/IdatSet.java | 256 ++ .../imageio/plugins/png/pngj/ImageInfo.java | 273 ++ .../plugins/png/pngj/ImageLineByte.java | 190 ++ .../plugins/png/pngj/ImageLineHelper.java | 515 +++ .../plugins/png/pngj/ImageLineInt.java | 199 ++ .../plugins/png/pngj/ImageLineSetDefault.java | 167 + .../plugins/png/pngj/PngHelperInternal.java | 248 ++ .../plugins/png/pngj/PngHelperInternal2.java | 36 + .../imageio/plugins/png/pngj/PngReader.java | 674 ++++ .../plugins/png/pngj/PngReaderApng.java | 219 ++ .../plugins/png/pngj/PngReaderByte.java | 32 + .../plugins/png/pngj/PngReaderFilter.java | 104 + .../plugins/png/pngj/PngReaderInt.java | 38 + .../imageio/plugins/png/pngj/PngWriter.java | 468 +++ .../imageio/plugins/png/pngj/PngWriterHc.java | 36 + .../plugins/png/pngj/PngjBadCrcException.java | 20 + .../plugins/png/pngj/PngjBadSignature.java | 20 + .../plugins/png/pngj/PngjException.java | 20 + .../png/pngj/PngjExceptionInternal.java | 23 + .../plugins/png/pngj/PngjInputException.java | 20 + .../plugins/png/pngj/PngjOutputException.java | 20 + .../plugins/png/pngj/PngjPrematureEnding.java | 10 + .../png/pngj/PngjUnsupportedException.java | 21 + .../imageio/plugins/png/pngj/RowInfo.java | 54 + .../png/pngj/chunks/ChunkCopyBehaviour.java | 110 + .../plugins/png/pngj/chunks/ChunkFactory.java | 134 + .../plugins/png/pngj/chunks/ChunkHelper.java | 287 ++ .../png/pngj/chunks/ChunkLoadBehaviour.java | 25 + .../png/pngj/chunks/ChunkPredicate.java | 14 + .../plugins/png/pngj/chunks/ChunkRaw.java | 191 ++ .../plugins/png/pngj/chunks/ChunksList.java | 169 + .../png/pngj/chunks/ChunksListForWrite.java | 198 ++ .../pngj/chunks/PngBadCharsetException.java | 20 + .../plugins/png/pngj/chunks/PngChunk.java | 227 ++ .../plugins/png/pngj/chunks/PngChunkACTL.java | 57 + .../plugins/png/pngj/chunks/PngChunkBKGD.java | 116 + .../plugins/png/pngj/chunks/PngChunkCHRM.java | 76 + .../plugins/png/pngj/chunks/PngChunkFCTL.java | 158 + .../plugins/png/pngj/chunks/PngChunkFDAT.java | 72 + .../plugins/png/pngj/chunks/PngChunkGAMA.java | 52 + .../plugins/png/pngj/chunks/PngChunkHIST.java | 60 + .../plugins/png/pngj/chunks/PngChunkICCP.java | 77 + .../plugins/png/pngj/chunks/PngChunkIDAT.java | 35 + .../plugins/png/pngj/chunks/PngChunkIEND.java | 35 + .../plugins/png/pngj/chunks/PngChunkIHDR.java | 195 ++ .../plugins/png/pngj/chunks/PngChunkITXT.java | 115 + .../png/pngj/chunks/PngChunkMultiple.java | 28 + .../plugins/png/pngj/chunks/PngChunkOFFS.java | 84 + .../plugins/png/pngj/chunks/PngChunkPHYS.java | 112 + .../plugins/png/pngj/chunks/PngChunkPLTE.java | 99 + .../plugins/png/pngj/chunks/PngChunkSBIT.java | 125 + .../plugins/png/pngj/chunks/PngChunkSPLT.java | 132 + .../plugins/png/pngj/chunks/PngChunkSRGB.java | 56 + .../plugins/png/pngj/chunks/PngChunkSTER.java | 55 + .../png/pngj/chunks/PngChunkSingle.java | 44 + .../plugins/png/pngj/chunks/PngChunkTEXT.java | 47 + .../plugins/png/pngj/chunks/PngChunkTIME.java | 84 + .../plugins/png/pngj/chunks/PngChunkTRNS.java | 157 + .../png/pngj/chunks/PngChunkTextVar.java | 59 + .../png/pngj/chunks/PngChunkUNKNOWN.java | 40 + .../plugins/png/pngj/chunks/PngChunkZTXT.java | 64 + .../plugins/png/pngj/chunks/PngMetadata.java | 237 ++ .../png/pngj/pixels/CompressorStream.java | 171 + .../pngj/pixels/CompressorStreamDeflater.java | 113 + .../png/pngj/pixels/CompressorStreamLz4.java | 99 + .../png/pngj/pixels/DeflaterEstimatorHjg.java | 260 ++ .../png/pngj/pixels/DeflaterEstimatorLz4.java | 278 ++ .../png/pngj/pixels/FiltersPerformance.java | 224 ++ .../plugins/png/pngj/pixels/PixelsWriter.java | 289 ++ .../png/pngj/pixels/PixelsWriterDefault.java | 185 ++ .../png/pngj/pixels/PixelsWriterMultiple.java | 254 ++ .../services/javax.imageio.spi.ImageWriterSpi | 1 + .../plugins/png/BufferedImageChildTest.java | 62 + .../plugins/png/BufferedImageTypesTest.java | 67 + .../png/CustomByteIndexImageTypesTest.java | 88 + .../png/CustomUShortImageTypesTest.java | 57 + .../imageio/plugins/png/ImageAssert.java | 84 + .../imageio/plugins/png/PNGWriterTest.java | 76 + .../plugins/png/PngSuiteImagesTest.java | 84 + .../plugins/png/SampleImagePainter.java | 82 + settings.gradle | 5 + .../plugins/png/PNGImageWriterBackport.java | 1274 -------- .../png/PNGImageWriterSpiBackport.java | 145 - .../services/javax.imageio.spi.ImageWriterSpi | 1 - .../png/PNGImageWriterBackportTest.java | 85 - .../imageio/plugins/png/placeholder-text.gif | Bin 7153 -> 0 bytes 1274 files changed, 115564 insertions(+), 1636 deletions(-) delete mode 100644 CREDITS.txt create mode 100644 barcode/NOTICE.txt create mode 100644 barcode/build.gradle create mode 100644 barcode/src/main/java/module-info.java create mode 100755 barcode/src/main/java/org/xbib/graphics/barcode/AustraliaPost.java create mode 100755 barcode/src/main/java/org/xbib/graphics/barcode/AztecCode.java create mode 100755 barcode/src/main/java/org/xbib/graphics/barcode/AztecRune.java create mode 100755 barcode/src/main/java/org/xbib/graphics/barcode/ChannelCode.java create mode 100755 barcode/src/main/java/org/xbib/graphics/barcode/Codabar.java create mode 100755 barcode/src/main/java/org/xbib/graphics/barcode/CodablockF.java create mode 100755 barcode/src/main/java/org/xbib/graphics/barcode/Code11.java create mode 100755 barcode/src/main/java/org/xbib/graphics/barcode/Code128.java create mode 100755 barcode/src/main/java/org/xbib/graphics/barcode/Code16k.java create mode 100755 barcode/src/main/java/org/xbib/graphics/barcode/Code2Of5.java create mode 100755 barcode/src/main/java/org/xbib/graphics/barcode/Code32.java create mode 100755 barcode/src/main/java/org/xbib/graphics/barcode/Code3Of9.java create mode 100755 barcode/src/main/java/org/xbib/graphics/barcode/Code3Of9Extended.java create mode 100755 barcode/src/main/java/org/xbib/graphics/barcode/Code49.java create mode 100755 barcode/src/main/java/org/xbib/graphics/barcode/Code93.java create mode 100755 barcode/src/main/java/org/xbib/graphics/barcode/CodeOne.java create mode 100755 barcode/src/main/java/org/xbib/graphics/barcode/Composite.java create mode 100755 barcode/src/main/java/org/xbib/graphics/barcode/DataBar14.java create mode 100755 barcode/src/main/java/org/xbib/graphics/barcode/DataBarExpanded.java create mode 100755 barcode/src/main/java/org/xbib/graphics/barcode/DataBarLimited.java create mode 100755 barcode/src/main/java/org/xbib/graphics/barcode/DataMatrix.java create mode 100755 barcode/src/main/java/org/xbib/graphics/barcode/Ean.java create mode 100755 barcode/src/main/java/org/xbib/graphics/barcode/GridMatrix.java create mode 100755 barcode/src/main/java/org/xbib/graphics/barcode/HumanReadableLocation.java create mode 100755 barcode/src/main/java/org/xbib/graphics/barcode/JapanPost.java create mode 100755 barcode/src/main/java/org/xbib/graphics/barcode/KixCode.java create mode 100755 barcode/src/main/java/org/xbib/graphics/barcode/KoreaPost.java create mode 100755 barcode/src/main/java/org/xbib/graphics/barcode/Logmars.java create mode 100755 barcode/src/main/java/org/xbib/graphics/barcode/MaxiCode.java create mode 100755 barcode/src/main/java/org/xbib/graphics/barcode/MicroQrCode.java create mode 100755 barcode/src/main/java/org/xbib/graphics/barcode/MsiPlessey.java create mode 100755 barcode/src/main/java/org/xbib/graphics/barcode/Nve18.java create mode 100755 barcode/src/main/java/org/xbib/graphics/barcode/Pdf417.java create mode 100755 barcode/src/main/java/org/xbib/graphics/barcode/Pharmacode.java create mode 100755 barcode/src/main/java/org/xbib/graphics/barcode/Pharmacode2Track.java create mode 100755 barcode/src/main/java/org/xbib/graphics/barcode/Pharmazentralnummer.java create mode 100755 barcode/src/main/java/org/xbib/graphics/barcode/Postnet.java create mode 100755 barcode/src/main/java/org/xbib/graphics/barcode/QrCode.java create mode 100755 barcode/src/main/java/org/xbib/graphics/barcode/RoyalMail4State.java create mode 100755 barcode/src/main/java/org/xbib/graphics/barcode/Symbol.java create mode 100755 barcode/src/main/java/org/xbib/graphics/barcode/Telepen.java create mode 100755 barcode/src/main/java/org/xbib/graphics/barcode/Upc.java create mode 100755 barcode/src/main/java/org/xbib/graphics/barcode/UspsOneCode.java create mode 100755 barcode/src/main/java/org/xbib/graphics/barcode/UspsPackage.java create mode 100755 barcode/src/main/java/org/xbib/graphics/barcode/render/GraphicsRenderer.java create mode 100755 barcode/src/main/java/org/xbib/graphics/barcode/util/AddOn.java create mode 100755 barcode/src/main/java/org/xbib/graphics/barcode/util/Hexagon.java create mode 100755 barcode/src/main/java/org/xbib/graphics/barcode/util/ReedSolomon.java create mode 100755 barcode/src/main/java/org/xbib/graphics/barcode/util/TextBox.java create mode 100755 barcode/src/test/java/org/xbib/graphics/barcode/MaxiCodeTest.java create mode 100644 barcode/src/test/java/org/xbib/graphics/barcode/ParameterizedExtension.java create mode 100755 barcode/src/test/java/org/xbib/graphics/barcode/SymbolTest.java create mode 100644 barcode/src/test/java/org/xbib/graphics/barcode/output/Code39Test.java create mode 100755 barcode/src/test/java/org/xbib/graphics/barcode/output/EPSRendererTest.java create mode 100755 barcode/src/test/java/org/xbib/graphics/barcode/output/PDFRendererTest.java create mode 100755 barcode/src/test/java/org/xbib/graphics/barcode/output/SvgRendererTest.java create mode 100755 barcode/src/test/resources/org/xbib/graphics/barcode/codabar/basic.codewords create mode 100644 barcode/src/test/resources/org/xbib/graphics/barcode/codabar/basic.png create mode 100755 barcode/src/test/resources/org/xbib/graphics/barcode/codabar/basic.properties create mode 100755 barcode/src/test/resources/org/xbib/graphics/barcode/codabar/module-width-ratio-2.5.codewords create mode 100644 barcode/src/test/resources/org/xbib/graphics/barcode/codabar/module-width-ratio-2.5.png create mode 100755 barcode/src/test/resources/org/xbib/graphics/barcode/codabar/module-width-ratio-2.5.properties create mode 100755 barcode/src/test/resources/org/xbib/graphics/barcode/codabar/no-start-char.error create mode 100755 barcode/src/test/resources/org/xbib/graphics/barcode/codabar/no-start-char.properties create mode 100755 barcode/src/test/resources/org/xbib/graphics/barcode/codabar/no-stop-char.error create mode 100755 barcode/src/test/resources/org/xbib/graphics/barcode/codabar/no-stop-char.properties create mode 100755 barcode/src/test/resources/org/xbib/graphics/barcode/code11/basic-one-check-digit.codewords create mode 100644 barcode/src/test/resources/org/xbib/graphics/barcode/code11/basic-one-check-digit.png create mode 100755 barcode/src/test/resources/org/xbib/graphics/barcode/code11/basic-one-check-digit.properties create mode 100755 barcode/src/test/resources/org/xbib/graphics/barcode/code11/basic.codewords create mode 100644 barcode/src/test/resources/org/xbib/graphics/barcode/code11/basic.png create mode 100755 barcode/src/test/resources/org/xbib/graphics/barcode/code11/basic.properties create mode 100755 barcode/src/test/resources/org/xbib/graphics/barcode/code11/human-readable-location-none.codewords create mode 100644 barcode/src/test/resources/org/xbib/graphics/barcode/code11/human-readable-location-none.png create mode 100755 barcode/src/test/resources/org/xbib/graphics/barcode/code11/human-readable-location-none.properties create mode 100755 barcode/src/test/resources/org/xbib/graphics/barcode/code11/human-readable-location-top.codewords create mode 100644 barcode/src/test/resources/org/xbib/graphics/barcode/code11/human-readable-location-top.png create mode 100755 barcode/src/test/resources/org/xbib/graphics/barcode/code11/human-readable-location-top.properties create mode 100755 barcode/src/test/resources/org/xbib/graphics/barcode/code11/invalid-content.error create mode 100755 barcode/src/test/resources/org/xbib/graphics/barcode/code11/invalid-content.properties create mode 100755 barcode/src/test/resources/org/xbib/graphics/barcode/code11/module-width-ratio-2.5.codewords create mode 100644 barcode/src/test/resources/org/xbib/graphics/barcode/code11/module-width-ratio-2.5.png create mode 100755 barcode/src/test/resources/org/xbib/graphics/barcode/code11/module-width-ratio-2.5.properties create mode 100755 barcode/src/test/resources/org/xbib/graphics/barcode/code11/module-width-ratio-3.0.codewords create mode 100644 barcode/src/test/resources/org/xbib/graphics/barcode/code11/module-width-ratio-3.0.png create mode 100755 barcode/src/test/resources/org/xbib/graphics/barcode/code11/module-width-ratio-3.0.properties create mode 100755 barcode/src/test/resources/org/xbib/graphics/barcode/code11/start-delimiter.codewords create mode 100644 barcode/src/test/resources/org/xbib/graphics/barcode/code11/start-delimiter.png create mode 100755 barcode/src/test/resources/org/xbib/graphics/barcode/code11/start-delimiter.properties create mode 100755 barcode/src/test/resources/org/xbib/graphics/barcode/code11/start-stop-delimiter.codewords create mode 100644 barcode/src/test/resources/org/xbib/graphics/barcode/code11/start-stop-delimiter.png create mode 100755 barcode/src/test/resources/org/xbib/graphics/barcode/code11/start-stop-delimiter.properties create mode 100755 barcode/src/test/resources/org/xbib/graphics/barcode/code11/stop-delimiter.codewords create mode 100644 barcode/src/test/resources/org/xbib/graphics/barcode/code11/stop-delimiter.png create mode 100755 barcode/src/test/resources/org/xbib/graphics/barcode/code11/stop-delimiter.properties create mode 100755 barcode/src/test/resources/org/xbib/graphics/barcode/code11/three-check-digits.error create mode 100755 barcode/src/test/resources/org/xbib/graphics/barcode/code11/three-check-digits.properties create mode 100755 barcode/src/test/resources/org/xbib/graphics/barcode/code11/zero-check-digits.error create mode 100755 barcode/src/test/resources/org/xbib/graphics/barcode/code11/zero-check-digits.properties create mode 100755 barcode/src/test/resources/org/xbib/graphics/barcode/code2of5/basic.codewords create mode 100644 barcode/src/test/resources/org/xbib/graphics/barcode/code2of5/basic.png create mode 100755 barcode/src/test/resources/org/xbib/graphics/barcode/code2of5/basic.properties create mode 100755 barcode/src/test/resources/org/xbib/graphics/barcode/code2of5/human-readable-location-none.codewords create mode 100644 barcode/src/test/resources/org/xbib/graphics/barcode/code2of5/human-readable-location-none.png create mode 100755 barcode/src/test/resources/org/xbib/graphics/barcode/code2of5/human-readable-location-none.properties create mode 100755 barcode/src/test/resources/org/xbib/graphics/barcode/code2of5/human-readable-location-top.codewords create mode 100644 barcode/src/test/resources/org/xbib/graphics/barcode/code2of5/human-readable-location-top.png create mode 100755 barcode/src/test/resources/org/xbib/graphics/barcode/code2of5/human-readable-location-top.properties create mode 100755 barcode/src/test/resources/org/xbib/graphics/barcode/code2of5/module-width-ratio-2.5.codewords create mode 100644 barcode/src/test/resources/org/xbib/graphics/barcode/code2of5/module-width-ratio-2.5.png create mode 100755 barcode/src/test/resources/org/xbib/graphics/barcode/code2of5/module-width-ratio-2.5.properties create mode 100755 barcode/src/test/resources/org/xbib/graphics/barcode/code3of9/basic.codewords create mode 100644 barcode/src/test/resources/org/xbib/graphics/barcode/code3of9/basic.png create mode 100755 barcode/src/test/resources/org/xbib/graphics/barcode/code3of9/basic.properties create mode 100755 barcode/src/test/resources/org/xbib/graphics/barcode/code3of9/module-width-ratio-3.codewords create mode 100644 barcode/src/test/resources/org/xbib/graphics/barcode/code3of9/module-width-ratio-3.png create mode 100755 barcode/src/test/resources/org/xbib/graphics/barcode/code3of9/module-width-ratio-3.properties create mode 100755 barcode/src/test/resources/org/xbib/graphics/barcode/code93/hide-check-digits.codewords create mode 100644 barcode/src/test/resources/org/xbib/graphics/barcode/code93/hide-check-digits.png create mode 100755 barcode/src/test/resources/org/xbib/graphics/barcode/code93/hide-check-digits.properties create mode 100755 barcode/src/test/resources/org/xbib/graphics/barcode/code93/letters-lowercase-1.codewords create mode 100644 barcode/src/test/resources/org/xbib/graphics/barcode/code93/letters-lowercase-1.png create mode 100755 barcode/src/test/resources/org/xbib/graphics/barcode/code93/letters-lowercase-1.properties create mode 100755 barcode/src/test/resources/org/xbib/graphics/barcode/code93/letters-lowercase-2.codewords create mode 100644 barcode/src/test/resources/org/xbib/graphics/barcode/code93/letters-lowercase-2.png create mode 100755 barcode/src/test/resources/org/xbib/graphics/barcode/code93/letters-lowercase-2.properties create mode 100755 barcode/src/test/resources/org/xbib/graphics/barcode/code93/letters-lowercase-3.codewords create mode 100644 barcode/src/test/resources/org/xbib/graphics/barcode/code93/letters-lowercase-3.png create mode 100755 barcode/src/test/resources/org/xbib/graphics/barcode/code93/letters-lowercase-3.properties create mode 100755 barcode/src/test/resources/org/xbib/graphics/barcode/code93/letters-uppercase-1.codewords create mode 100644 barcode/src/test/resources/org/xbib/graphics/barcode/code93/letters-uppercase-1.png create mode 100755 barcode/src/test/resources/org/xbib/graphics/barcode/code93/letters-uppercase-1.properties create mode 100755 barcode/src/test/resources/org/xbib/graphics/barcode/code93/letters-uppercase-2.codewords create mode 100644 barcode/src/test/resources/org/xbib/graphics/barcode/code93/letters-uppercase-2.png create mode 100755 barcode/src/test/resources/org/xbib/graphics/barcode/code93/letters-uppercase-2.properties create mode 100755 barcode/src/test/resources/org/xbib/graphics/barcode/code93/letters-uppercase-3.codewords create mode 100644 barcode/src/test/resources/org/xbib/graphics/barcode/code93/letters-uppercase-3.png create mode 100755 barcode/src/test/resources/org/xbib/graphics/barcode/code93/letters-uppercase-3.properties create mode 100755 barcode/src/test/resources/org/xbib/graphics/barcode/code93/numbers-with-custom-bar-height.codewords create mode 100644 barcode/src/test/resources/org/xbib/graphics/barcode/code93/numbers-with-custom-bar-height.png create mode 100755 barcode/src/test/resources/org/xbib/graphics/barcode/code93/numbers-with-custom-bar-height.properties create mode 100755 barcode/src/test/resources/org/xbib/graphics/barcode/code93/numbers-with-custom-module-width.codewords create mode 100644 barcode/src/test/resources/org/xbib/graphics/barcode/code93/numbers-with-custom-module-width.png create mode 100755 barcode/src/test/resources/org/xbib/graphics/barcode/code93/numbers-with-custom-module-width.properties create mode 100755 barcode/src/test/resources/org/xbib/graphics/barcode/code93/numbers.codewords create mode 100644 barcode/src/test/resources/org/xbib/graphics/barcode/code93/numbers.png create mode 100755 barcode/src/test/resources/org/xbib/graphics/barcode/code93/numbers.properties create mode 100755 barcode/src/test/resources/org/xbib/graphics/barcode/code93/special-characters-no-ctrl.codewords create mode 100644 barcode/src/test/resources/org/xbib/graphics/barcode/code93/special-characters-no-ctrl.png create mode 100755 barcode/src/test/resources/org/xbib/graphics/barcode/code93/special-characters-no-ctrl.properties create mode 100755 barcode/src/test/resources/org/xbib/graphics/barcode/code93/special-characters-with-ctrl-1.codewords create mode 100644 barcode/src/test/resources/org/xbib/graphics/barcode/code93/special-characters-with-ctrl-1.png create mode 100755 barcode/src/test/resources/org/xbib/graphics/barcode/code93/special-characters-with-ctrl-1.properties create mode 100755 barcode/src/test/resources/org/xbib/graphics/barcode/code93/special-characters-with-ctrl-2.codewords create mode 100644 barcode/src/test/resources/org/xbib/graphics/barcode/code93/special-characters-with-ctrl-2.png create mode 100755 barcode/src/test/resources/org/xbib/graphics/barcode/code93/special-characters-with-ctrl-2.properties create mode 100755 barcode/src/test/resources/org/xbib/graphics/barcode/code93/special-characters-with-ctrl-3.codewords create mode 100644 barcode/src/test/resources/org/xbib/graphics/barcode/code93/special-characters-with-ctrl-3.png create mode 100755 barcode/src/test/resources/org/xbib/graphics/barcode/code93/special-characters-with-ctrl-3.properties create mode 100755 barcode/src/test/resources/org/xbib/graphics/barcode/code93/start-stop-delimiter.codewords create mode 100644 barcode/src/test/resources/org/xbib/graphics/barcode/code93/start-stop-delimiter.png create mode 100755 barcode/src/test/resources/org/xbib/graphics/barcode/code93/start-stop-delimiter.properties create mode 100755 barcode/src/test/resources/org/xbib/graphics/barcode/ean/ean-13-basic.codewords create mode 100644 barcode/src/test/resources/org/xbib/graphics/barcode/ean/ean-13-basic.png create mode 100755 barcode/src/test/resources/org/xbib/graphics/barcode/ean/ean-13-basic.properties create mode 100755 barcode/src/test/resources/org/xbib/graphics/barcode/ean/ean-8-basic.codewords create mode 100644 barcode/src/test/resources/org/xbib/graphics/barcode/ean/ean-8-basic.png create mode 100755 barcode/src/test/resources/org/xbib/graphics/barcode/ean/ean-8-basic.properties create mode 100755 barcode/src/test/resources/org/xbib/graphics/barcode/fonts/OkapiDejaVuSans.ttf create mode 100755 barcode/src/test/resources/org/xbib/graphics/barcode/japanpost/basic.codewords create mode 100644 barcode/src/test/resources/org/xbib/graphics/barcode/japanpost/basic.png create mode 100755 barcode/src/test/resources/org/xbib/graphics/barcode/japanpost/basic.properties create mode 100755 barcode/src/test/resources/org/xbib/graphics/barcode/japanpost/bug-50.codewords create mode 100644 barcode/src/test/resources/org/xbib/graphics/barcode/japanpost/bug-50.png create mode 100755 barcode/src/test/resources/org/xbib/graphics/barcode/japanpost/bug-50.properties create mode 100755 barcode/src/test/resources/org/xbib/graphics/barcode/logmars/letters1.codewords create mode 100644 barcode/src/test/resources/org/xbib/graphics/barcode/logmars/letters1.png create mode 100755 barcode/src/test/resources/org/xbib/graphics/barcode/logmars/letters1.properties create mode 100755 barcode/src/test/resources/org/xbib/graphics/barcode/logmars/letters2.codewords create mode 100644 barcode/src/test/resources/org/xbib/graphics/barcode/logmars/letters2.png create mode 100755 barcode/src/test/resources/org/xbib/graphics/barcode/logmars/letters2.properties create mode 100755 barcode/src/test/resources/org/xbib/graphics/barcode/logmars/letters3.codewords create mode 100644 barcode/src/test/resources/org/xbib/graphics/barcode/logmars/letters3.png create mode 100755 barcode/src/test/resources/org/xbib/graphics/barcode/logmars/letters3.properties create mode 100755 barcode/src/test/resources/org/xbib/graphics/barcode/logmars/module-width-ratio-2.codewords create mode 100644 barcode/src/test/resources/org/xbib/graphics/barcode/logmars/module-width-ratio-2.png create mode 100755 barcode/src/test/resources/org/xbib/graphics/barcode/logmars/module-width-ratio-2.properties create mode 100755 barcode/src/test/resources/org/xbib/graphics/barcode/logmars/numbers.codewords create mode 100644 barcode/src/test/resources/org/xbib/graphics/barcode/logmars/numbers.png create mode 100755 barcode/src/test/resources/org/xbib/graphics/barcode/logmars/numbers.properties create mode 100755 barcode/src/test/resources/org/xbib/graphics/barcode/logmars/special.codewords create mode 100644 barcode/src/test/resources/org/xbib/graphics/barcode/logmars/special.png create mode 100755 barcode/src/test/resources/org/xbib/graphics/barcode/logmars/special.properties create mode 100755 barcode/src/test/resources/org/xbib/graphics/barcode/maxicode/mode-0.error create mode 100755 barcode/src/test/resources/org/xbib/graphics/barcode/maxicode/mode-0.properties create mode 100755 barcode/src/test/resources/org/xbib/graphics/barcode/maxicode/mode-1.error create mode 100755 barcode/src/test/resources/org/xbib/graphics/barcode/maxicode/mode-1.properties create mode 100755 barcode/src/test/resources/org/xbib/graphics/barcode/maxicode/mode-2-invalid-primary.error create mode 100755 barcode/src/test/resources/org/xbib/graphics/barcode/maxicode/mode-2-invalid-primary.properties create mode 100755 barcode/src/test/resources/org/xbib/graphics/barcode/maxicode/mode-2-max-data.codewords create mode 100644 barcode/src/test/resources/org/xbib/graphics/barcode/maxicode/mode-2-max-data.png create mode 100755 barcode/src/test/resources/org/xbib/graphics/barcode/maxicode/mode-2-max-data.properties create mode 100755 barcode/src/test/resources/org/xbib/graphics/barcode/maxicode/mode-2-standard.codewords create mode 100644 barcode/src/test/resources/org/xbib/graphics/barcode/maxicode/mode-2-standard.png create mode 100755 barcode/src/test/resources/org/xbib/graphics/barcode/maxicode/mode-2-standard.properties create mode 100755 barcode/src/test/resources/org/xbib/graphics/barcode/maxicode/mode-2-structured-append-1-of-8.codewords create mode 100644 barcode/src/test/resources/org/xbib/graphics/barcode/maxicode/mode-2-structured-append-1-of-8.png create mode 100755 barcode/src/test/resources/org/xbib/graphics/barcode/maxicode/mode-2-structured-append-1-of-8.properties create mode 100755 barcode/src/test/resources/org/xbib/graphics/barcode/maxicode/mode-3-standard.codewords create mode 100644 barcode/src/test/resources/org/xbib/graphics/barcode/maxicode/mode-3-standard.png create mode 100755 barcode/src/test/resources/org/xbib/graphics/barcode/maxicode/mode-3-standard.properties create mode 100755 barcode/src/test/resources/org/xbib/graphics/barcode/maxicode/mode-4-basic-structured-append-total-1.codewords create mode 100644 barcode/src/test/resources/org/xbib/graphics/barcode/maxicode/mode-4-basic-structured-append-total-1.png create mode 100755 barcode/src/test/resources/org/xbib/graphics/barcode/maxicode/mode-4-basic-structured-append-total-1.properties create mode 100755 barcode/src/test/resources/org/xbib/graphics/barcode/maxicode/mode-4-basic.codewords create mode 100644 barcode/src/test/resources/org/xbib/graphics/barcode/maxicode/mode-4-basic.png create mode 100755 barcode/src/test/resources/org/xbib/graphics/barcode/maxicode/mode-4-basic.properties create mode 100755 barcode/src/test/resources/org/xbib/graphics/barcode/maxicode/mode-4-code-set-a-all-symbols.codewords create mode 100644 barcode/src/test/resources/org/xbib/graphics/barcode/maxicode/mode-4-code-set-a-all-symbols.png create mode 100755 barcode/src/test/resources/org/xbib/graphics/barcode/maxicode/mode-4-code-set-a-all-symbols.properties create mode 100755 barcode/src/test/resources/org/xbib/graphics/barcode/maxicode/mode-4-code-set-b-all-symbols.codewords create mode 100644 barcode/src/test/resources/org/xbib/graphics/barcode/maxicode/mode-4-code-set-b-all-symbols.png create mode 100755 barcode/src/test/resources/org/xbib/graphics/barcode/maxicode/mode-4-code-set-b-all-symbols.properties create mode 100755 barcode/src/test/resources/org/xbib/graphics/barcode/maxicode/mode-4-code-sets-a-and-b.codewords create mode 100644 barcode/src/test/resources/org/xbib/graphics/barcode/maxicode/mode-4-code-sets-a-and-b.png create mode 100755 barcode/src/test/resources/org/xbib/graphics/barcode/maxicode/mode-4-code-sets-a-and-b.properties create mode 100755 barcode/src/test/resources/org/xbib/graphics/barcode/maxicode/mode-4-code-sets-c-d-e.codewords create mode 100644 barcode/src/test/resources/org/xbib/graphics/barcode/maxicode/mode-4-code-sets-c-d-e.png create mode 100755 barcode/src/test/resources/org/xbib/graphics/barcode/maxicode/mode-4-code-sets-c-d-e.properties create mode 100755 barcode/src/test/resources/org/xbib/graphics/barcode/maxicode/mode-4-max-data.codewords create mode 100644 barcode/src/test/resources/org/xbib/graphics/barcode/maxicode/mode-4-max-data.png create mode 100755 barcode/src/test/resources/org/xbib/graphics/barcode/maxicode/mode-4-max-data.properties create mode 100755 barcode/src/test/resources/org/xbib/graphics/barcode/maxicode/mode-4-numeric-shift.codewords create mode 100644 barcode/src/test/resources/org/xbib/graphics/barcode/maxicode/mode-4-numeric-shift.png create mode 100755 barcode/src/test/resources/org/xbib/graphics/barcode/maxicode/mode-4-numeric-shift.properties create mode 100755 barcode/src/test/resources/org/xbib/graphics/barcode/maxicode/mode-4-structured-append-0-of-5.error create mode 100755 barcode/src/test/resources/org/xbib/graphics/barcode/maxicode/mode-4-structured-append-0-of-5.properties create mode 100755 barcode/src/test/resources/org/xbib/graphics/barcode/maxicode/mode-4-structured-append-1-of-3.codewords create mode 100644 barcode/src/test/resources/org/xbib/graphics/barcode/maxicode/mode-4-structured-append-1-of-3.png create mode 100755 barcode/src/test/resources/org/xbib/graphics/barcode/maxicode/mode-4-structured-append-1-of-3.properties create mode 100755 barcode/src/test/resources/org/xbib/graphics/barcode/maxicode/mode-4-structured-append-2-of-3.codewords create mode 100644 barcode/src/test/resources/org/xbib/graphics/barcode/maxicode/mode-4-structured-append-2-of-3.png create mode 100755 barcode/src/test/resources/org/xbib/graphics/barcode/maxicode/mode-4-structured-append-2-of-3.properties create mode 100755 barcode/src/test/resources/org/xbib/graphics/barcode/maxicode/mode-4-structured-append-3-of-3.codewords create mode 100644 barcode/src/test/resources/org/xbib/graphics/barcode/maxicode/mode-4-structured-append-3-of-3.png create mode 100755 barcode/src/test/resources/org/xbib/graphics/barcode/maxicode/mode-4-structured-append-3-of-3.properties create mode 100755 barcode/src/test/resources/org/xbib/graphics/barcode/maxicode/mode-4-structured-append-5-of-9.error create mode 100755 barcode/src/test/resources/org/xbib/graphics/barcode/maxicode/mode-4-structured-append-5-of-9.properties create mode 100755 barcode/src/test/resources/org/xbib/graphics/barcode/maxicode/mode-4-too-much-data.error create mode 100755 barcode/src/test/resources/org/xbib/graphics/barcode/maxicode/mode-4-too-much-data.properties create mode 100755 barcode/src/test/resources/org/xbib/graphics/barcode/maxicode/mode-5-basic.codewords create mode 100644 barcode/src/test/resources/org/xbib/graphics/barcode/maxicode/mode-5-basic.png create mode 100755 barcode/src/test/resources/org/xbib/graphics/barcode/maxicode/mode-5-basic.properties create mode 100755 barcode/src/test/resources/org/xbib/graphics/barcode/maxicode/mode-5-max-data.codewords create mode 100644 barcode/src/test/resources/org/xbib/graphics/barcode/maxicode/mode-5-max-data.png create mode 100755 barcode/src/test/resources/org/xbib/graphics/barcode/maxicode/mode-5-max-data.properties create mode 100755 barcode/src/test/resources/org/xbib/graphics/barcode/maxicode/mode-5-too-much-data.error create mode 100755 barcode/src/test/resources/org/xbib/graphics/barcode/maxicode/mode-5-too-much-data.properties create mode 100755 barcode/src/test/resources/org/xbib/graphics/barcode/maxicode/mode-6-max-data.codewords create mode 100644 barcode/src/test/resources/org/xbib/graphics/barcode/maxicode/mode-6-max-data.png create mode 100755 barcode/src/test/resources/org/xbib/graphics/barcode/maxicode/mode-6-max-data.properties create mode 100755 barcode/src/test/resources/org/xbib/graphics/barcode/maxicode/mode-6-too-much-data.error create mode 100755 barcode/src/test/resources/org/xbib/graphics/barcode/maxicode/mode-6-too-much-data.properties create mode 100755 barcode/src/test/resources/org/xbib/graphics/barcode/maxicode/mode-7.error create mode 100755 barcode/src/test/resources/org/xbib/graphics/barcode/maxicode/mode-7.properties create mode 100755 barcode/src/test/resources/org/xbib/graphics/barcode/output/code93-basic.eps create mode 100755 barcode/src/test/resources/org/xbib/graphics/barcode/output/code93-basic.svg create mode 100755 barcode/src/test/resources/org/xbib/graphics/barcode/output/code93-colors.eps create mode 100755 barcode/src/test/resources/org/xbib/graphics/barcode/output/code93-colors.svg create mode 100755 barcode/src/test/resources/org/xbib/graphics/barcode/output/code93-custom-font.eps create mode 100755 barcode/src/test/resources/org/xbib/graphics/barcode/output/code93-custom-font.svg create mode 100755 barcode/src/test/resources/org/xbib/graphics/barcode/output/code93-magnification-2.eps create mode 100755 barcode/src/test/resources/org/xbib/graphics/barcode/output/code93-magnification-2.svg create mode 100755 barcode/src/test/resources/org/xbib/graphics/barcode/output/code93-margin-size-20.eps create mode 100755 barcode/src/test/resources/org/xbib/graphics/barcode/output/code93-margin-size-20.svg create mode 100755 barcode/src/test/resources/org/xbib/graphics/barcode/output/maxicode-basic.eps create mode 100755 barcode/src/test/resources/org/xbib/graphics/barcode/output/maxicode-basic.svg create mode 100755 barcode/src/test/resources/org/xbib/graphics/barcode/pdf417/micro-basic-2-columns.codewords create mode 100644 barcode/src/test/resources/org/xbib/graphics/barcode/pdf417/micro-basic-2-columns.png create mode 100755 barcode/src/test/resources/org/xbib/graphics/barcode/pdf417/micro-basic-2-columns.properties create mode 100755 barcode/src/test/resources/org/xbib/graphics/barcode/pdf417/micro-basic-ecc-level-7-ignored.codewords create mode 100644 barcode/src/test/resources/org/xbib/graphics/barcode/pdf417/micro-basic-ecc-level-7-ignored.png create mode 100755 barcode/src/test/resources/org/xbib/graphics/barcode/pdf417/micro-basic-ecc-level-7-ignored.properties create mode 100755 barcode/src/test/resources/org/xbib/graphics/barcode/pdf417/micro-basic.codewords create mode 100644 barcode/src/test/resources/org/xbib/graphics/barcode/pdf417/micro-basic.png create mode 100755 barcode/src/test/resources/org/xbib/graphics/barcode/pdf417/micro-basic.properties create mode 100755 barcode/src/test/resources/org/xbib/graphics/barcode/pdf417/micro-data-variety.codewords create mode 100644 barcode/src/test/resources/org/xbib/graphics/barcode/pdf417/micro-data-variety.png create mode 100755 barcode/src/test/resources/org/xbib/graphics/barcode/pdf417/micro-data-variety.properties create mode 100755 barcode/src/test/resources/org/xbib/graphics/barcode/pdf417/micro-structured-append-1-of-3.codewords create mode 100644 barcode/src/test/resources/org/xbib/graphics/barcode/pdf417/micro-structured-append-1-of-3.png create mode 100755 barcode/src/test/resources/org/xbib/graphics/barcode/pdf417/micro-structured-append-1-of-3.properties create mode 100755 barcode/src/test/resources/org/xbib/graphics/barcode/pdf417/micro-structured-append-2-of-3.codewords create mode 100644 barcode/src/test/resources/org/xbib/graphics/barcode/pdf417/micro-structured-append-2-of-3.png create mode 100755 barcode/src/test/resources/org/xbib/graphics/barcode/pdf417/micro-structured-append-2-of-3.properties create mode 100755 barcode/src/test/resources/org/xbib/graphics/barcode/pdf417/micro-structured-append-3-of-3.codewords create mode 100644 barcode/src/test/resources/org/xbib/graphics/barcode/pdf417/micro-structured-append-3-of-3.png create mode 100755 barcode/src/test/resources/org/xbib/graphics/barcode/pdf417/micro-structured-append-3-of-3.properties create mode 100755 barcode/src/test/resources/org/xbib/graphics/barcode/pdf417/micro-utf-8.codewords create mode 100644 barcode/src/test/resources/org/xbib/graphics/barcode/pdf417/micro-utf-8.png create mode 100755 barcode/src/test/resources/org/xbib/graphics/barcode/pdf417/micro-utf-8.properties create mode 100755 barcode/src/test/resources/org/xbib/graphics/barcode/pdf417/micro-variant-01.codewords create mode 100644 barcode/src/test/resources/org/xbib/graphics/barcode/pdf417/micro-variant-01.png create mode 100755 barcode/src/test/resources/org/xbib/graphics/barcode/pdf417/micro-variant-01.properties create mode 100755 barcode/src/test/resources/org/xbib/graphics/barcode/pdf417/micro-variant-02.codewords create mode 100644 barcode/src/test/resources/org/xbib/graphics/barcode/pdf417/micro-variant-02.png create mode 100755 barcode/src/test/resources/org/xbib/graphics/barcode/pdf417/micro-variant-02.properties create mode 100755 barcode/src/test/resources/org/xbib/graphics/barcode/pdf417/micro-variant-03.codewords create mode 100644 barcode/src/test/resources/org/xbib/graphics/barcode/pdf417/micro-variant-03.png create mode 100755 barcode/src/test/resources/org/xbib/graphics/barcode/pdf417/micro-variant-03.properties create mode 100755 barcode/src/test/resources/org/xbib/graphics/barcode/pdf417/micro-variant-04.codewords create mode 100644 barcode/src/test/resources/org/xbib/graphics/barcode/pdf417/micro-variant-04.png create mode 100755 barcode/src/test/resources/org/xbib/graphics/barcode/pdf417/micro-variant-04.properties create mode 100755 barcode/src/test/resources/org/xbib/graphics/barcode/pdf417/micro-variant-05.codewords create mode 100644 barcode/src/test/resources/org/xbib/graphics/barcode/pdf417/micro-variant-05.png create mode 100755 barcode/src/test/resources/org/xbib/graphics/barcode/pdf417/micro-variant-05.properties create mode 100755 barcode/src/test/resources/org/xbib/graphics/barcode/pdf417/micro-variant-06.codewords create mode 100644 barcode/src/test/resources/org/xbib/graphics/barcode/pdf417/micro-variant-06.png create mode 100755 barcode/src/test/resources/org/xbib/graphics/barcode/pdf417/micro-variant-06.properties create mode 100755 barcode/src/test/resources/org/xbib/graphics/barcode/pdf417/micro-variant-07.codewords create mode 100644 barcode/src/test/resources/org/xbib/graphics/barcode/pdf417/micro-variant-07.png create mode 100755 barcode/src/test/resources/org/xbib/graphics/barcode/pdf417/micro-variant-07.properties create mode 100755 barcode/src/test/resources/org/xbib/graphics/barcode/pdf417/micro-variant-08.codewords create mode 100644 barcode/src/test/resources/org/xbib/graphics/barcode/pdf417/micro-variant-08.png create mode 100755 barcode/src/test/resources/org/xbib/graphics/barcode/pdf417/micro-variant-08.properties create mode 100755 barcode/src/test/resources/org/xbib/graphics/barcode/pdf417/micro-variant-09.codewords create mode 100644 barcode/src/test/resources/org/xbib/graphics/barcode/pdf417/micro-variant-09.png create mode 100755 barcode/src/test/resources/org/xbib/graphics/barcode/pdf417/micro-variant-09.properties create mode 100755 barcode/src/test/resources/org/xbib/graphics/barcode/pdf417/micro-variant-10.codewords create mode 100644 barcode/src/test/resources/org/xbib/graphics/barcode/pdf417/micro-variant-10.png create mode 100755 barcode/src/test/resources/org/xbib/graphics/barcode/pdf417/micro-variant-10.properties create mode 100755 barcode/src/test/resources/org/xbib/graphics/barcode/pdf417/micro-variant-11.codewords create mode 100644 barcode/src/test/resources/org/xbib/graphics/barcode/pdf417/micro-variant-11.png create mode 100755 barcode/src/test/resources/org/xbib/graphics/barcode/pdf417/micro-variant-11.properties create mode 100755 barcode/src/test/resources/org/xbib/graphics/barcode/pdf417/micro-variant-12.codewords create mode 100644 barcode/src/test/resources/org/xbib/graphics/barcode/pdf417/micro-variant-12.png create mode 100755 barcode/src/test/resources/org/xbib/graphics/barcode/pdf417/micro-variant-12.properties create mode 100755 barcode/src/test/resources/org/xbib/graphics/barcode/pdf417/micro-variant-13.codewords create mode 100644 barcode/src/test/resources/org/xbib/graphics/barcode/pdf417/micro-variant-13.png create mode 100755 barcode/src/test/resources/org/xbib/graphics/barcode/pdf417/micro-variant-13.properties create mode 100755 barcode/src/test/resources/org/xbib/graphics/barcode/pdf417/micro-variant-14.codewords create mode 100644 barcode/src/test/resources/org/xbib/graphics/barcode/pdf417/micro-variant-14.png create mode 100755 barcode/src/test/resources/org/xbib/graphics/barcode/pdf417/micro-variant-14.properties create mode 100755 barcode/src/test/resources/org/xbib/graphics/barcode/pdf417/micro-variant-15.codewords create mode 100644 barcode/src/test/resources/org/xbib/graphics/barcode/pdf417/micro-variant-15.png create mode 100755 barcode/src/test/resources/org/xbib/graphics/barcode/pdf417/micro-variant-15.properties create mode 100755 barcode/src/test/resources/org/xbib/graphics/barcode/pdf417/micro-variant-16.codewords create mode 100644 barcode/src/test/resources/org/xbib/graphics/barcode/pdf417/micro-variant-16.png create mode 100755 barcode/src/test/resources/org/xbib/graphics/barcode/pdf417/micro-variant-16.properties create mode 100755 barcode/src/test/resources/org/xbib/graphics/barcode/pdf417/micro-variant-17.codewords create mode 100644 barcode/src/test/resources/org/xbib/graphics/barcode/pdf417/micro-variant-17.png create mode 100755 barcode/src/test/resources/org/xbib/graphics/barcode/pdf417/micro-variant-17.properties create mode 100755 barcode/src/test/resources/org/xbib/graphics/barcode/pdf417/micro-variant-18.codewords create mode 100644 barcode/src/test/resources/org/xbib/graphics/barcode/pdf417/micro-variant-18.png create mode 100755 barcode/src/test/resources/org/xbib/graphics/barcode/pdf417/micro-variant-18.properties create mode 100755 barcode/src/test/resources/org/xbib/graphics/barcode/pdf417/micro-variant-19.codewords create mode 100644 barcode/src/test/resources/org/xbib/graphics/barcode/pdf417/micro-variant-19.png create mode 100755 barcode/src/test/resources/org/xbib/graphics/barcode/pdf417/micro-variant-19.properties create mode 100755 barcode/src/test/resources/org/xbib/graphics/barcode/pdf417/micro-variant-20.codewords create mode 100644 barcode/src/test/resources/org/xbib/graphics/barcode/pdf417/micro-variant-20.png create mode 100755 barcode/src/test/resources/org/xbib/graphics/barcode/pdf417/micro-variant-20.properties create mode 100755 barcode/src/test/resources/org/xbib/graphics/barcode/pdf417/micro-variant-21.codewords create mode 100644 barcode/src/test/resources/org/xbib/graphics/barcode/pdf417/micro-variant-21.png create mode 100755 barcode/src/test/resources/org/xbib/graphics/barcode/pdf417/micro-variant-21.properties create mode 100755 barcode/src/test/resources/org/xbib/graphics/barcode/pdf417/micro-variant-22.codewords create mode 100644 barcode/src/test/resources/org/xbib/graphics/barcode/pdf417/micro-variant-22.png create mode 100755 barcode/src/test/resources/org/xbib/graphics/barcode/pdf417/micro-variant-22.properties create mode 100755 barcode/src/test/resources/org/xbib/graphics/barcode/pdf417/micro-variant-23.codewords create mode 100644 barcode/src/test/resources/org/xbib/graphics/barcode/pdf417/micro-variant-23.png create mode 100755 barcode/src/test/resources/org/xbib/graphics/barcode/pdf417/micro-variant-23.properties create mode 100755 barcode/src/test/resources/org/xbib/graphics/barcode/pdf417/micro-variant-24.codewords create mode 100644 barcode/src/test/resources/org/xbib/graphics/barcode/pdf417/micro-variant-24.png create mode 100755 barcode/src/test/resources/org/xbib/graphics/barcode/pdf417/micro-variant-24.properties create mode 100755 barcode/src/test/resources/org/xbib/graphics/barcode/pdf417/micro-variant-25.codewords create mode 100644 barcode/src/test/resources/org/xbib/graphics/barcode/pdf417/micro-variant-25.png create mode 100755 barcode/src/test/resources/org/xbib/graphics/barcode/pdf417/micro-variant-25.properties create mode 100755 barcode/src/test/resources/org/xbib/graphics/barcode/pdf417/micro-variant-26.codewords create mode 100644 barcode/src/test/resources/org/xbib/graphics/barcode/pdf417/micro-variant-26.png create mode 100755 barcode/src/test/resources/org/xbib/graphics/barcode/pdf417/micro-variant-26.properties create mode 100755 barcode/src/test/resources/org/xbib/graphics/barcode/pdf417/micro-variant-27.codewords create mode 100644 barcode/src/test/resources/org/xbib/graphics/barcode/pdf417/micro-variant-27.png create mode 100755 barcode/src/test/resources/org/xbib/graphics/barcode/pdf417/micro-variant-27.properties create mode 100755 barcode/src/test/resources/org/xbib/graphics/barcode/pdf417/micro-variant-28.codewords create mode 100644 barcode/src/test/resources/org/xbib/graphics/barcode/pdf417/micro-variant-28.png create mode 100755 barcode/src/test/resources/org/xbib/graphics/barcode/pdf417/micro-variant-28.properties create mode 100755 barcode/src/test/resources/org/xbib/graphics/barcode/pdf417/micro-variant-29.codewords create mode 100644 barcode/src/test/resources/org/xbib/graphics/barcode/pdf417/micro-variant-29.png create mode 100755 barcode/src/test/resources/org/xbib/graphics/barcode/pdf417/micro-variant-29.properties create mode 100755 barcode/src/test/resources/org/xbib/graphics/barcode/pdf417/micro-variant-30.codewords create mode 100644 barcode/src/test/resources/org/xbib/graphics/barcode/pdf417/micro-variant-30.png create mode 100755 barcode/src/test/resources/org/xbib/graphics/barcode/pdf417/micro-variant-30.properties create mode 100755 barcode/src/test/resources/org/xbib/graphics/barcode/pdf417/micro-variant-31.codewords create mode 100644 barcode/src/test/resources/org/xbib/graphics/barcode/pdf417/micro-variant-31.png create mode 100755 barcode/src/test/resources/org/xbib/graphics/barcode/pdf417/micro-variant-31.properties create mode 100755 barcode/src/test/resources/org/xbib/graphics/barcode/pdf417/micro-variant-32.codewords create mode 100644 barcode/src/test/resources/org/xbib/graphics/barcode/pdf417/micro-variant-32.png create mode 100755 barcode/src/test/resources/org/xbib/graphics/barcode/pdf417/micro-variant-32.properties create mode 100755 barcode/src/test/resources/org/xbib/graphics/barcode/pdf417/micro-variant-33.codewords create mode 100644 barcode/src/test/resources/org/xbib/graphics/barcode/pdf417/micro-variant-33.png create mode 100755 barcode/src/test/resources/org/xbib/graphics/barcode/pdf417/micro-variant-33.properties create mode 100755 barcode/src/test/resources/org/xbib/graphics/barcode/pdf417/micro-variant-34.codewords create mode 100644 barcode/src/test/resources/org/xbib/graphics/barcode/pdf417/micro-variant-34.png create mode 100755 barcode/src/test/resources/org/xbib/graphics/barcode/pdf417/micro-variant-34.properties create mode 100755 barcode/src/test/resources/org/xbib/graphics/barcode/pdf417/normal-basic-10-rows.codewords create mode 100644 barcode/src/test/resources/org/xbib/graphics/barcode/pdf417/normal-basic-10-rows.png create mode 100755 barcode/src/test/resources/org/xbib/graphics/barcode/pdf417/normal-basic-10-rows.properties create mode 100755 barcode/src/test/resources/org/xbib/graphics/barcode/pdf417/normal-basic-30-columns-90-rows.error create mode 100755 barcode/src/test/resources/org/xbib/graphics/barcode/pdf417/normal-basic-30-columns-90-rows.properties create mode 100755 barcode/src/test/resources/org/xbib/graphics/barcode/pdf417/normal-basic-31-columns.error create mode 100755 barcode/src/test/resources/org/xbib/graphics/barcode/pdf417/normal-basic-31-columns.properties create mode 100755 barcode/src/test/resources/org/xbib/graphics/barcode/pdf417/normal-basic-5-columns.codewords create mode 100644 barcode/src/test/resources/org/xbib/graphics/barcode/pdf417/normal-basic-5-columns.png create mode 100755 barcode/src/test/resources/org/xbib/graphics/barcode/pdf417/normal-basic-5-columns.properties create mode 100755 barcode/src/test/resources/org/xbib/graphics/barcode/pdf417/normal-basic-6-rows-6-columns.codewords create mode 100644 barcode/src/test/resources/org/xbib/graphics/barcode/pdf417/normal-basic-6-rows-6-columns.png create mode 100755 barcode/src/test/resources/org/xbib/graphics/barcode/pdf417/normal-basic-6-rows-6-columns.properties create mode 100755 barcode/src/test/resources/org/xbib/graphics/barcode/pdf417/normal-basic-ecc-level-0.codewords create mode 100644 barcode/src/test/resources/org/xbib/graphics/barcode/pdf417/normal-basic-ecc-level-0.png create mode 100755 barcode/src/test/resources/org/xbib/graphics/barcode/pdf417/normal-basic-ecc-level-0.properties create mode 100755 barcode/src/test/resources/org/xbib/graphics/barcode/pdf417/normal-basic-ecc-level-1.codewords create mode 100644 barcode/src/test/resources/org/xbib/graphics/barcode/pdf417/normal-basic-ecc-level-1.png create mode 100755 barcode/src/test/resources/org/xbib/graphics/barcode/pdf417/normal-basic-ecc-level-1.properties create mode 100755 barcode/src/test/resources/org/xbib/graphics/barcode/pdf417/normal-basic-ecc-level-2.codewords create mode 100644 barcode/src/test/resources/org/xbib/graphics/barcode/pdf417/normal-basic-ecc-level-2.png create mode 100755 barcode/src/test/resources/org/xbib/graphics/barcode/pdf417/normal-basic-ecc-level-2.properties create mode 100755 barcode/src/test/resources/org/xbib/graphics/barcode/pdf417/normal-basic-ecc-level-3.codewords create mode 100644 barcode/src/test/resources/org/xbib/graphics/barcode/pdf417/normal-basic-ecc-level-3.png create mode 100755 barcode/src/test/resources/org/xbib/graphics/barcode/pdf417/normal-basic-ecc-level-3.properties create mode 100755 barcode/src/test/resources/org/xbib/graphics/barcode/pdf417/normal-basic-ecc-level-4.codewords create mode 100644 barcode/src/test/resources/org/xbib/graphics/barcode/pdf417/normal-basic-ecc-level-4.png create mode 100755 barcode/src/test/resources/org/xbib/graphics/barcode/pdf417/normal-basic-ecc-level-4.properties create mode 100755 barcode/src/test/resources/org/xbib/graphics/barcode/pdf417/normal-basic-ecc-level-5.codewords create mode 100644 barcode/src/test/resources/org/xbib/graphics/barcode/pdf417/normal-basic-ecc-level-5.png create mode 100755 barcode/src/test/resources/org/xbib/graphics/barcode/pdf417/normal-basic-ecc-level-5.properties create mode 100755 barcode/src/test/resources/org/xbib/graphics/barcode/pdf417/normal-basic-ecc-level-6.codewords create mode 100644 barcode/src/test/resources/org/xbib/graphics/barcode/pdf417/normal-basic-ecc-level-6.png create mode 100755 barcode/src/test/resources/org/xbib/graphics/barcode/pdf417/normal-basic-ecc-level-6.properties create mode 100755 barcode/src/test/resources/org/xbib/graphics/barcode/pdf417/normal-basic-ecc-level-7.codewords create mode 100644 barcode/src/test/resources/org/xbib/graphics/barcode/pdf417/normal-basic-ecc-level-7.png create mode 100755 barcode/src/test/resources/org/xbib/graphics/barcode/pdf417/normal-basic-ecc-level-7.properties create mode 100755 barcode/src/test/resources/org/xbib/graphics/barcode/pdf417/normal-basic-ecc-level-8.codewords create mode 100644 barcode/src/test/resources/org/xbib/graphics/barcode/pdf417/normal-basic-ecc-level-8.png create mode 100755 barcode/src/test/resources/org/xbib/graphics/barcode/pdf417/normal-basic-ecc-level-8.properties create mode 100755 barcode/src/test/resources/org/xbib/graphics/barcode/pdf417/normal-basic-ecc-level-9.error create mode 100755 barcode/src/test/resources/org/xbib/graphics/barcode/pdf417/normal-basic-ecc-level-9.properties create mode 100755 barcode/src/test/resources/org/xbib/graphics/barcode/pdf417/normal-basic-row-height-10.codewords create mode 100644 barcode/src/test/resources/org/xbib/graphics/barcode/pdf417/normal-basic-row-height-10.png create mode 100755 barcode/src/test/resources/org/xbib/graphics/barcode/pdf417/normal-basic-row-height-10.properties create mode 100755 barcode/src/test/resources/org/xbib/graphics/barcode/pdf417/normal-basic.codewords create mode 100644 barcode/src/test/resources/org/xbib/graphics/barcode/pdf417/normal-basic.png create mode 100755 barcode/src/test/resources/org/xbib/graphics/barcode/pdf417/normal-basic.properties create mode 100755 barcode/src/test/resources/org/xbib/graphics/barcode/pdf417/normal-data-variety.codewords create mode 100644 barcode/src/test/resources/org/xbib/graphics/barcode/pdf417/normal-data-variety.png create mode 100755 barcode/src/test/resources/org/xbib/graphics/barcode/pdf417/normal-data-variety.properties create mode 100755 barcode/src/test/resources/org/xbib/graphics/barcode/pdf417/normal-structured-append-1-of-3.codewords create mode 100644 barcode/src/test/resources/org/xbib/graphics/barcode/pdf417/normal-structured-append-1-of-3.png create mode 100755 barcode/src/test/resources/org/xbib/graphics/barcode/pdf417/normal-structured-append-1-of-3.properties create mode 100755 barcode/src/test/resources/org/xbib/graphics/barcode/pdf417/normal-structured-append-2-of-3.codewords create mode 100644 barcode/src/test/resources/org/xbib/graphics/barcode/pdf417/normal-structured-append-2-of-3.png create mode 100755 barcode/src/test/resources/org/xbib/graphics/barcode/pdf417/normal-structured-append-2-of-3.properties create mode 100755 barcode/src/test/resources/org/xbib/graphics/barcode/pdf417/normal-structured-append-3-of-3.codewords create mode 100644 barcode/src/test/resources/org/xbib/graphics/barcode/pdf417/normal-structured-append-3-of-3.png create mode 100755 barcode/src/test/resources/org/xbib/graphics/barcode/pdf417/normal-structured-append-3-of-3.properties create mode 100755 barcode/src/test/resources/org/xbib/graphics/barcode/pdf417/normal-too-few-columns-and-rows.error create mode 100755 barcode/src/test/resources/org/xbib/graphics/barcode/pdf417/normal-too-few-columns-and-rows.properties create mode 100755 barcode/src/test/resources/org/xbib/graphics/barcode/pdf417/normal-tracking-sample-2.codewords create mode 100644 barcode/src/test/resources/org/xbib/graphics/barcode/pdf417/normal-tracking-sample-2.png create mode 100755 barcode/src/test/resources/org/xbib/graphics/barcode/pdf417/normal-tracking-sample-2.properties create mode 100755 barcode/src/test/resources/org/xbib/graphics/barcode/pdf417/normal-tracking-sample.codewords create mode 100644 barcode/src/test/resources/org/xbib/graphics/barcode/pdf417/normal-tracking-sample.png create mode 100755 barcode/src/test/resources/org/xbib/graphics/barcode/pdf417/normal-tracking-sample.properties create mode 100755 barcode/src/test/resources/org/xbib/graphics/barcode/pdf417/normal-utf-8.codewords create mode 100644 barcode/src/test/resources/org/xbib/graphics/barcode/pdf417/normal-utf-8.png create mode 100755 barcode/src/test/resources/org/xbib/graphics/barcode/pdf417/normal-utf-8.properties create mode 100755 barcode/src/test/resources/org/xbib/graphics/barcode/pdf417/truncated-basic-ecc-level-0.codewords create mode 100644 barcode/src/test/resources/org/xbib/graphics/barcode/pdf417/truncated-basic-ecc-level-0.png create mode 100755 barcode/src/test/resources/org/xbib/graphics/barcode/pdf417/truncated-basic-ecc-level-0.properties create mode 100755 barcode/src/test/resources/org/xbib/graphics/barcode/pdf417/truncated-basic-ecc-level-1.codewords create mode 100644 barcode/src/test/resources/org/xbib/graphics/barcode/pdf417/truncated-basic-ecc-level-1.png create mode 100755 barcode/src/test/resources/org/xbib/graphics/barcode/pdf417/truncated-basic-ecc-level-1.properties create mode 100755 barcode/src/test/resources/org/xbib/graphics/barcode/pdf417/truncated-basic-ecc-level-2.codewords create mode 100644 barcode/src/test/resources/org/xbib/graphics/barcode/pdf417/truncated-basic-ecc-level-2.png create mode 100755 barcode/src/test/resources/org/xbib/graphics/barcode/pdf417/truncated-basic-ecc-level-2.properties create mode 100755 barcode/src/test/resources/org/xbib/graphics/barcode/pdf417/truncated-basic-ecc-level-3.codewords create mode 100644 barcode/src/test/resources/org/xbib/graphics/barcode/pdf417/truncated-basic-ecc-level-3.png create mode 100755 barcode/src/test/resources/org/xbib/graphics/barcode/pdf417/truncated-basic-ecc-level-3.properties create mode 100755 barcode/src/test/resources/org/xbib/graphics/barcode/pdf417/truncated-basic-ecc-level-4.codewords create mode 100644 barcode/src/test/resources/org/xbib/graphics/barcode/pdf417/truncated-basic-ecc-level-4.png create mode 100755 barcode/src/test/resources/org/xbib/graphics/barcode/pdf417/truncated-basic-ecc-level-4.properties create mode 100755 barcode/src/test/resources/org/xbib/graphics/barcode/pdf417/truncated-basic-ecc-level-5.codewords create mode 100644 barcode/src/test/resources/org/xbib/graphics/barcode/pdf417/truncated-basic-ecc-level-5.png create mode 100755 barcode/src/test/resources/org/xbib/graphics/barcode/pdf417/truncated-basic-ecc-level-5.properties create mode 100755 barcode/src/test/resources/org/xbib/graphics/barcode/pdf417/truncated-basic-ecc-level-6.codewords create mode 100644 barcode/src/test/resources/org/xbib/graphics/barcode/pdf417/truncated-basic-ecc-level-6.png create mode 100755 barcode/src/test/resources/org/xbib/graphics/barcode/pdf417/truncated-basic-ecc-level-6.properties create mode 100755 barcode/src/test/resources/org/xbib/graphics/barcode/pdf417/truncated-basic-ecc-level-7.codewords create mode 100644 barcode/src/test/resources/org/xbib/graphics/barcode/pdf417/truncated-basic-ecc-level-7.png create mode 100755 barcode/src/test/resources/org/xbib/graphics/barcode/pdf417/truncated-basic-ecc-level-7.properties create mode 100755 barcode/src/test/resources/org/xbib/graphics/barcode/pdf417/truncated-basic-ecc-level-8.codewords create mode 100644 barcode/src/test/resources/org/xbib/graphics/barcode/pdf417/truncated-basic-ecc-level-8.png create mode 100755 barcode/src/test/resources/org/xbib/graphics/barcode/pdf417/truncated-basic-ecc-level-8.properties create mode 100755 barcode/src/test/resources/org/xbib/graphics/barcode/pdf417/truncated-basic-ecc-level-9.error create mode 100755 barcode/src/test/resources/org/xbib/graphics/barcode/pdf417/truncated-basic-ecc-level-9.properties create mode 100755 barcode/src/test/resources/org/xbib/graphics/barcode/pdf417/truncated-basic.codewords create mode 100644 barcode/src/test/resources/org/xbib/graphics/barcode/pdf417/truncated-basic.png create mode 100755 barcode/src/test/resources/org/xbib/graphics/barcode/pdf417/truncated-basic.properties create mode 100755 barcode/src/test/resources/org/xbib/graphics/barcode/pdf417/truncated-data-variety.codewords create mode 100644 barcode/src/test/resources/org/xbib/graphics/barcode/pdf417/truncated-data-variety.png create mode 100755 barcode/src/test/resources/org/xbib/graphics/barcode/pdf417/truncated-data-variety.properties create mode 100755 barcode/src/test/resources/org/xbib/graphics/barcode/postnet/planet-basic.codewords create mode 100644 barcode/src/test/resources/org/xbib/graphics/barcode/postnet/planet-basic.png create mode 100755 barcode/src/test/resources/org/xbib/graphics/barcode/postnet/planet-basic.properties create mode 100755 barcode/src/test/resources/org/xbib/graphics/barcode/postnet/postnet-basic.codewords create mode 100644 barcode/src/test/resources/org/xbib/graphics/barcode/postnet/postnet-basic.png create mode 100755 barcode/src/test/resources/org/xbib/graphics/barcode/postnet/postnet-basic.properties create mode 100755 barcode/src/test/resources/org/xbib/graphics/barcode/postnet/postnet-module-width-2.codewords create mode 100644 barcode/src/test/resources/org/xbib/graphics/barcode/postnet/postnet-module-width-2.png create mode 100755 barcode/src/test/resources/org/xbib/graphics/barcode/postnet/postnet-module-width-2.properties create mode 100755 barcode/src/test/resources/org/xbib/graphics/barcode/postnet/postnet-readable-above.codewords create mode 100644 barcode/src/test/resources/org/xbib/graphics/barcode/postnet/postnet-readable-above.png create mode 100755 barcode/src/test/resources/org/xbib/graphics/barcode/postnet/postnet-readable-above.properties create mode 100755 barcode/src/test/resources/org/xbib/graphics/barcode/postnet/postnet-readable-below.codewords create mode 100644 barcode/src/test/resources/org/xbib/graphics/barcode/postnet/postnet-readable-below.png create mode 100755 barcode/src/test/resources/org/xbib/graphics/barcode/postnet/postnet-readable-below.properties create mode 100755 barcode/src/test/resources/org/xbib/graphics/barcode/qrcode/version-01.codewords create mode 100644 barcode/src/test/resources/org/xbib/graphics/barcode/qrcode/version-01.png create mode 100755 barcode/src/test/resources/org/xbib/graphics/barcode/qrcode/version-01.properties create mode 100755 barcode/src/test/resources/org/xbib/graphics/barcode/qrcode/version-02.codewords create mode 100644 barcode/src/test/resources/org/xbib/graphics/barcode/qrcode/version-02.png create mode 100755 barcode/src/test/resources/org/xbib/graphics/barcode/qrcode/version-02.properties create mode 100755 barcode/src/test/resources/org/xbib/graphics/barcode/qrcode/version-03.codewords create mode 100644 barcode/src/test/resources/org/xbib/graphics/barcode/qrcode/version-03.png create mode 100755 barcode/src/test/resources/org/xbib/graphics/barcode/qrcode/version-03.properties create mode 100755 barcode/src/test/resources/org/xbib/graphics/barcode/qrcode/version-04.codewords create mode 100644 barcode/src/test/resources/org/xbib/graphics/barcode/qrcode/version-04.png create mode 100755 barcode/src/test/resources/org/xbib/graphics/barcode/qrcode/version-04.properties create mode 100755 barcode/src/test/resources/org/xbib/graphics/barcode/qrcode/version-05.codewords create mode 100644 barcode/src/test/resources/org/xbib/graphics/barcode/qrcode/version-05.png create mode 100755 barcode/src/test/resources/org/xbib/graphics/barcode/qrcode/version-05.properties create mode 100755 barcode/src/test/resources/org/xbib/graphics/barcode/qrcode/version-06.codewords create mode 100644 barcode/src/test/resources/org/xbib/graphics/barcode/qrcode/version-06.png create mode 100755 barcode/src/test/resources/org/xbib/graphics/barcode/qrcode/version-06.properties create mode 100755 barcode/src/test/resources/org/xbib/graphics/barcode/qrcode/version-07.codewords create mode 100644 barcode/src/test/resources/org/xbib/graphics/barcode/qrcode/version-07.png create mode 100755 barcode/src/test/resources/org/xbib/graphics/barcode/qrcode/version-07.properties create mode 100755 barcode/src/test/resources/org/xbib/graphics/barcode/qrcode/version-08.codewords create mode 100644 barcode/src/test/resources/org/xbib/graphics/barcode/qrcode/version-08.png create mode 100755 barcode/src/test/resources/org/xbib/graphics/barcode/qrcode/version-08.properties create mode 100755 barcode/src/test/resources/org/xbib/graphics/barcode/qrcode/version-09.codewords create mode 100644 barcode/src/test/resources/org/xbib/graphics/barcode/qrcode/version-09.png create mode 100755 barcode/src/test/resources/org/xbib/graphics/barcode/qrcode/version-09.properties create mode 100755 barcode/src/test/resources/org/xbib/graphics/barcode/qrcode/version-10.codewords create mode 100644 barcode/src/test/resources/org/xbib/graphics/barcode/qrcode/version-10.png create mode 100755 barcode/src/test/resources/org/xbib/graphics/barcode/qrcode/version-10.properties create mode 100755 barcode/src/test/resources/org/xbib/graphics/barcode/upc/upc-a-basic.codewords create mode 100644 barcode/src/test/resources/org/xbib/graphics/barcode/upc/upc-a-basic.png create mode 100755 barcode/src/test/resources/org/xbib/graphics/barcode/upc/upc-a-basic.properties create mode 100755 barcode/src/test/resources/org/xbib/graphics/barcode/upc/upc-a-font.codewords create mode 100644 barcode/src/test/resources/org/xbib/graphics/barcode/upc/upc-a-font.png create mode 100755 barcode/src/test/resources/org/xbib/graphics/barcode/upc/upc-a-font.properties create mode 100755 barcode/src/test/resources/org/xbib/graphics/barcode/upc/upc-e-basic.codewords create mode 100644 barcode/src/test/resources/org/xbib/graphics/barcode/upc/upc-e-basic.png create mode 100755 barcode/src/test/resources/org/xbib/graphics/barcode/upc/upc-e-basic.properties create mode 100755 barcode/src/test/resources/org/xbib/graphics/barcode/uspsonecode/usps-one-code-length-20.codewords create mode 100644 barcode/src/test/resources/org/xbib/graphics/barcode/uspsonecode/usps-one-code-length-20.png create mode 100755 barcode/src/test/resources/org/xbib/graphics/barcode/uspsonecode/usps-one-code-length-20.properties create mode 100755 barcode/src/test/resources/org/xbib/graphics/barcode/uspsonecode/usps-one-code-length-25.codewords create mode 100644 barcode/src/test/resources/org/xbib/graphics/barcode/uspsonecode/usps-one-code-length-25.png create mode 100755 barcode/src/test/resources/org/xbib/graphics/barcode/uspsonecode/usps-one-code-length-25.properties create mode 100755 barcode/src/test/resources/org/xbib/graphics/barcode/uspsonecode/usps-one-code-length-29.codewords create mode 100644 barcode/src/test/resources/org/xbib/graphics/barcode/uspsonecode/usps-one-code-length-29.png create mode 100755 barcode/src/test/resources/org/xbib/graphics/barcode/uspsonecode/usps-one-code-length-29.properties create mode 100755 barcode/src/test/resources/org/xbib/graphics/barcode/uspsonecode/usps-one-code-length-31.codewords create mode 100644 barcode/src/test/resources/org/xbib/graphics/barcode/uspsonecode/usps-one-code-length-31.png create mode 100755 barcode/src/test/resources/org/xbib/graphics/barcode/uspsonecode/usps-one-code-length-31.properties create mode 100644 chart/NOTICE.txt create mode 100644 chart/src/main/java/module-info.java create mode 100644 chart/src/main/java/org/xbib/graphics/chart/Chart.java create mode 100644 chart/src/main/java/org/xbib/graphics/chart/ChartBuilder.java create mode 100644 chart/src/main/java/org/xbib/graphics/chart/ChartComponent.java create mode 100644 chart/src/main/java/org/xbib/graphics/chart/ChartTitle.java create mode 100644 chart/src/main/java/org/xbib/graphics/chart/Histogram.java create mode 100644 chart/src/main/java/org/xbib/graphics/chart/QuickChart.java create mode 100644 chart/src/main/java/org/xbib/graphics/chart/axis/Axis.java create mode 100644 chart/src/main/java/org/xbib/graphics/chart/axis/AxisPair.java create mode 100644 chart/src/main/java/org/xbib/graphics/chart/axis/AxisTick.java create mode 100644 chart/src/main/java/org/xbib/graphics/chart/axis/AxisTickCalculator.java create mode 100644 chart/src/main/java/org/xbib/graphics/chart/axis/AxisTickCalculatorCategory.java create mode 100644 chart/src/main/java/org/xbib/graphics/chart/axis/AxisTickCalculatorInstant.java create mode 100644 chart/src/main/java/org/xbib/graphics/chart/axis/AxisTickCalculatorLogarithmic.java create mode 100644 chart/src/main/java/org/xbib/graphics/chart/axis/AxisTickCalculatorNumber.java create mode 100644 chart/src/main/java/org/xbib/graphics/chart/axis/AxisTickCalculatorOverride.java create mode 100644 chart/src/main/java/org/xbib/graphics/chart/axis/AxisTickLabels.java create mode 100644 chart/src/main/java/org/xbib/graphics/chart/axis/AxisTickMarks.java create mode 100644 chart/src/main/java/org/xbib/graphics/chart/axis/AxisTitle.java create mode 100644 chart/src/main/java/org/xbib/graphics/chart/axis/DataType.java create mode 100644 chart/src/main/java/org/xbib/graphics/chart/axis/Direction.java create mode 100644 chart/src/main/java/org/xbib/graphics/chart/axis/TextAlignment.java create mode 100644 chart/src/main/java/org/xbib/graphics/chart/axis/YAxisPosition.java create mode 100644 chart/src/main/java/org/xbib/graphics/chart/bubble/BubbleChart.java create mode 100644 chart/src/main/java/org/xbib/graphics/chart/bubble/BubbleChartBuilder.java create mode 100644 chart/src/main/java/org/xbib/graphics/chart/bubble/BubbleLegend.java create mode 100644 chart/src/main/java/org/xbib/graphics/chart/bubble/BubbleSeries.java create mode 100644 chart/src/main/java/org/xbib/graphics/chart/bubble/BubbleSeriesRenderStyle.java create mode 100644 chart/src/main/java/org/xbib/graphics/chart/bubble/BubbleStyler.java create mode 100644 chart/src/main/java/org/xbib/graphics/chart/category/CategoryChart.java create mode 100644 chart/src/main/java/org/xbib/graphics/chart/category/CategoryChartBuilder.java create mode 100644 chart/src/main/java/org/xbib/graphics/chart/category/CategorySeries.java create mode 100644 chart/src/main/java/org/xbib/graphics/chart/category/CategorySeriesRenderStyle.java create mode 100644 chart/src/main/java/org/xbib/graphics/chart/category/CategoryStyler.java create mode 100644 chart/src/main/java/org/xbib/graphics/chart/formatter/DateFormatter.java create mode 100644 chart/src/main/java/org/xbib/graphics/chart/formatter/NumberFormatter.java create mode 100644 chart/src/main/java/org/xbib/graphics/chart/formatter/NumberLogFormatter.java create mode 100644 chart/src/main/java/org/xbib/graphics/chart/formatter/StringFormatter.java create mode 100644 chart/src/main/java/org/xbib/graphics/chart/io/BitmapFormat.java create mode 100644 chart/src/main/java/org/xbib/graphics/chart/io/CSVExporter.java create mode 100644 chart/src/main/java/org/xbib/graphics/chart/io/CSVImporter.java create mode 100644 chart/src/main/java/org/xbib/graphics/chart/io/VectorGraphicsFormat.java create mode 100644 chart/src/main/java/org/xbib/graphics/chart/io/vector/Document.java create mode 100644 chart/src/main/java/org/xbib/graphics/chart/io/vector/EPSGraphics2D.java create mode 100644 chart/src/main/java/org/xbib/graphics/chart/io/vector/GraphicsState.java create mode 100644 chart/src/main/java/org/xbib/graphics/chart/io/vector/PDFGraphics2D.java create mode 100644 chart/src/main/java/org/xbib/graphics/chart/io/vector/ProcessingPipeline.java create mode 100644 chart/src/main/java/org/xbib/graphics/chart/io/vector/Processor.java create mode 100644 chart/src/main/java/org/xbib/graphics/chart/io/vector/SVGGraphics2D.java create mode 100644 chart/src/main/java/org/xbib/graphics/chart/io/vector/SizedDocument.java create mode 100644 chart/src/main/java/org/xbib/graphics/chart/io/vector/VectorGraphics2D.java create mode 100644 chart/src/main/java/org/xbib/graphics/chart/io/vector/VectorHints.java create mode 100644 chart/src/main/java/org/xbib/graphics/chart/io/vector/eps/EPSDocument.java create mode 100644 chart/src/main/java/org/xbib/graphics/chart/io/vector/eps/EPSProcessor.java create mode 100644 chart/src/main/java/org/xbib/graphics/chart/io/vector/intermediate/CommandHandler.java create mode 100644 chart/src/main/java/org/xbib/graphics/chart/io/vector/intermediate/commands/AffineTransformCommand.java create mode 100644 chart/src/main/java/org/xbib/graphics/chart/io/vector/intermediate/commands/Command.java create mode 100644 chart/src/main/java/org/xbib/graphics/chart/io/vector/intermediate/commands/CreateCommand.java create mode 100644 chart/src/main/java/org/xbib/graphics/chart/io/vector/intermediate/commands/DisposeCommand.java create mode 100644 chart/src/main/java/org/xbib/graphics/chart/io/vector/intermediate/commands/DrawImageCommand.java create mode 100644 chart/src/main/java/org/xbib/graphics/chart/io/vector/intermediate/commands/DrawShapeCommand.java create mode 100644 chart/src/main/java/org/xbib/graphics/chart/io/vector/intermediate/commands/DrawStringCommand.java create mode 100644 chart/src/main/java/org/xbib/graphics/chart/io/vector/intermediate/commands/FillShapeCommand.java create mode 100644 chart/src/main/java/org/xbib/graphics/chart/io/vector/intermediate/commands/Group.java create mode 100644 chart/src/main/java/org/xbib/graphics/chart/io/vector/intermediate/commands/RotateCommand.java create mode 100644 chart/src/main/java/org/xbib/graphics/chart/io/vector/intermediate/commands/ScaleCommand.java create mode 100644 chart/src/main/java/org/xbib/graphics/chart/io/vector/intermediate/commands/SetBackgroundCommand.java create mode 100644 chart/src/main/java/org/xbib/graphics/chart/io/vector/intermediate/commands/SetClipCommand.java create mode 100644 chart/src/main/java/org/xbib/graphics/chart/io/vector/intermediate/commands/SetColorCommand.java create mode 100644 chart/src/main/java/org/xbib/graphics/chart/io/vector/intermediate/commands/SetCompositeCommand.java create mode 100644 chart/src/main/java/org/xbib/graphics/chart/io/vector/intermediate/commands/SetFontCommand.java create mode 100644 chart/src/main/java/org/xbib/graphics/chart/io/vector/intermediate/commands/SetHintCommand.java create mode 100644 chart/src/main/java/org/xbib/graphics/chart/io/vector/intermediate/commands/SetPaintCommand.java create mode 100644 chart/src/main/java/org/xbib/graphics/chart/io/vector/intermediate/commands/SetStrokeCommand.java create mode 100644 chart/src/main/java/org/xbib/graphics/chart/io/vector/intermediate/commands/SetTransformCommand.java create mode 100644 chart/src/main/java/org/xbib/graphics/chart/io/vector/intermediate/commands/SetXORModeCommand.java create mode 100644 chart/src/main/java/org/xbib/graphics/chart/io/vector/intermediate/commands/ShearCommand.java create mode 100644 chart/src/main/java/org/xbib/graphics/chart/io/vector/intermediate/commands/StateCommand.java create mode 100644 chart/src/main/java/org/xbib/graphics/chart/io/vector/intermediate/commands/TransformCommand.java create mode 100644 chart/src/main/java/org/xbib/graphics/chart/io/vector/intermediate/commands/TranslateCommand.java create mode 100644 chart/src/main/java/org/xbib/graphics/chart/io/vector/intermediate/filters/AbsoluteToRelativeTransformsFilter.java create mode 100644 chart/src/main/java/org/xbib/graphics/chart/io/vector/intermediate/filters/FillPaintedShapeAsImageFilter.java create mode 100644 chart/src/main/java/org/xbib/graphics/chart/io/vector/intermediate/filters/Filter.java create mode 100644 chart/src/main/java/org/xbib/graphics/chart/io/vector/intermediate/filters/GroupingFilter.java create mode 100644 chart/src/main/java/org/xbib/graphics/chart/io/vector/intermediate/filters/OptimizeFilter.java create mode 100644 chart/src/main/java/org/xbib/graphics/chart/io/vector/intermediate/filters/StateChangeGroupingFilter.java create mode 100644 chart/src/main/java/org/xbib/graphics/chart/io/vector/pdf/GeneratedPayload.java create mode 100644 chart/src/main/java/org/xbib/graphics/chart/io/vector/pdf/PDFDocument.java create mode 100644 chart/src/main/java/org/xbib/graphics/chart/io/vector/pdf/PDFObject.java create mode 100644 chart/src/main/java/org/xbib/graphics/chart/io/vector/pdf/PDFProcessor.java create mode 100644 chart/src/main/java/org/xbib/graphics/chart/io/vector/pdf/Payload.java create mode 100644 chart/src/main/java/org/xbib/graphics/chart/io/vector/pdf/Resources.java create mode 100644 chart/src/main/java/org/xbib/graphics/chart/io/vector/pdf/SizePayload.java create mode 100644 chart/src/main/java/org/xbib/graphics/chart/io/vector/svg/SVGDocument.java create mode 100644 chart/src/main/java/org/xbib/graphics/chart/io/vector/svg/SVGProcessor.java create mode 100644 chart/src/main/java/org/xbib/graphics/chart/io/vector/util/ASCII85EncodeStream.java create mode 100644 chart/src/main/java/org/xbib/graphics/chart/io/vector/util/AlphaToMaskOp.java create mode 100644 chart/src/main/java/org/xbib/graphics/chart/io/vector/util/Base64EncodeStream.java create mode 100644 chart/src/main/java/org/xbib/graphics/chart/io/vector/util/DataUtils.java create mode 100644 chart/src/main/java/org/xbib/graphics/chart/io/vector/util/FlateEncodeStream.java create mode 100644 chart/src/main/java/org/xbib/graphics/chart/io/vector/util/FormattingWriter.java create mode 100644 chart/src/main/java/org/xbib/graphics/chart/io/vector/util/GraphicsUtils.java create mode 100644 chart/src/main/java/org/xbib/graphics/chart/io/vector/util/ImageDataStream.java create mode 100644 chart/src/main/java/org/xbib/graphics/chart/io/vector/util/LineWrapOutputStream.java create mode 100644 chart/src/main/java/org/xbib/graphics/chart/io/vector/util/PageSize.java create mode 100644 chart/src/main/java/org/xbib/graphics/chart/legend/AbstractLegend.java create mode 100644 chart/src/main/java/org/xbib/graphics/chart/legend/Legend.java create mode 100644 chart/src/main/java/org/xbib/graphics/chart/legend/LegendLayout.java create mode 100644 chart/src/main/java/org/xbib/graphics/chart/legend/LegendPosition.java create mode 100644 chart/src/main/java/org/xbib/graphics/chart/legend/LegendRenderType.java create mode 100644 chart/src/main/java/org/xbib/graphics/chart/legend/LegendRenderable.java create mode 100644 chart/src/main/java/org/xbib/graphics/chart/legend/MarkerLegend.java create mode 100644 chart/src/main/java/org/xbib/graphics/chart/ohlc/OHLCChart.java create mode 100644 chart/src/main/java/org/xbib/graphics/chart/ohlc/OHLCChartBuilder.java create mode 100644 chart/src/main/java/org/xbib/graphics/chart/ohlc/OHLCLegend.java create mode 100644 chart/src/main/java/org/xbib/graphics/chart/ohlc/OHLCSeries.java create mode 100644 chart/src/main/java/org/xbib/graphics/chart/ohlc/OHLCSeriesRenderStyle.java create mode 100644 chart/src/main/java/org/xbib/graphics/chart/ohlc/OHLCStyler.java create mode 100644 chart/src/main/java/org/xbib/graphics/chart/pie/PieChart.java create mode 100644 chart/src/main/java/org/xbib/graphics/chart/pie/PieChartBuilder.java create mode 100644 chart/src/main/java/org/xbib/graphics/chart/pie/PieLegend.java create mode 100644 chart/src/main/java/org/xbib/graphics/chart/pie/PieSeries.java create mode 100644 chart/src/main/java/org/xbib/graphics/chart/pie/PieSeriesRenderStyle.java create mode 100644 chart/src/main/java/org/xbib/graphics/chart/pie/PieStyler.java create mode 100644 chart/src/main/java/org/xbib/graphics/chart/plot/AxesChartPlot.java create mode 100644 chart/src/main/java/org/xbib/graphics/chart/plot/CircularPlot.java create mode 100644 chart/src/main/java/org/xbib/graphics/chart/plot/ContentPlot.java create mode 100644 chart/src/main/java/org/xbib/graphics/chart/plot/Plot.java create mode 100644 chart/src/main/java/org/xbib/graphics/chart/plot/SurfacePlot.java create mode 100644 chart/src/main/java/org/xbib/graphics/chart/plot/SurfacePlotAxesChart.java create mode 100644 chart/src/main/java/org/xbib/graphics/chart/series/AxesChartSeries.java create mode 100644 chart/src/main/java/org/xbib/graphics/chart/series/AxesChartSeriesCategory.java create mode 100644 chart/src/main/java/org/xbib/graphics/chart/series/AxesChartSeriesNumericalNoErrorBars.java create mode 100644 chart/src/main/java/org/xbib/graphics/chart/series/MarkerSeries.java create mode 100644 chart/src/main/java/org/xbib/graphics/chart/series/NoMarkersSeries.java create mode 100644 chart/src/main/java/org/xbib/graphics/chart/series/Series.java create mode 100644 chart/src/main/java/org/xbib/graphics/chart/style/AxesChartStyler.java create mode 100644 chart/src/main/java/org/xbib/graphics/chart/style/SeriesColorMarkerLineStyle.java create mode 100644 chart/src/main/java/org/xbib/graphics/chart/style/SeriesColorMarkerLineStyleCycler.java create mode 100644 chart/src/main/java/org/xbib/graphics/chart/style/Styler.java create mode 100644 chart/src/main/java/org/xbib/graphics/chart/theme/DefaultTheme.java create mode 100644 chart/src/main/java/org/xbib/graphics/chart/theme/GGPlot2Theme.java create mode 100644 chart/src/main/java/org/xbib/graphics/chart/theme/MatlabTheme.java create mode 100644 chart/src/main/java/org/xbib/graphics/chart/theme/Theme.java create mode 100644 chart/src/main/java/org/xbib/graphics/chart/xy/XYChart.java create mode 100644 chart/src/main/java/org/xbib/graphics/chart/xy/XYChartBuilder.java create mode 100644 chart/src/main/java/org/xbib/graphics/chart/xy/XYSeries.java create mode 100644 chart/src/main/java/org/xbib/graphics/chart/xy/XYSeriesRenderStyle.java create mode 100644 chart/src/main/java/org/xbib/graphics/chart/xy/XYStyler.java create mode 100755 chart/src/test/java/org/xbib/graphics/chart/HistogramTest.java create mode 100644 chart/src/test/java/org/xbib/graphics/chart/MatlabTest.java create mode 100644 chart/src/test/java/org/xbib/graphics/chart/demo/AreaChartTest.java create mode 100644 chart/src/test/java/org/xbib/graphics/chart/demo/BarChartTest.java create mode 100644 chart/src/test/java/org/xbib/graphics/chart/demo/BubbleChartTest.java create mode 100644 chart/src/test/java/org/xbib/graphics/chart/demo/PieChartTest.java create mode 100644 chart/src/test/java/org/xbib/graphics/chart/demo/QuickChartTest.java create mode 100644 chart/src/test/java/org/xbib/graphics/chart/demo/ScatterChartTest.java create mode 100644 chart/src/test/java/org/xbib/graphics/chart/demo/StickChartTest.java create mode 100644 chart/src/test/java/org/xbib/graphics/chart/io/vector/GraphicsStateTest.java create mode 100644 chart/src/test/java/org/xbib/graphics/chart/io/vector/TestUtils.java create mode 100644 chart/src/test/java/org/xbib/graphics/chart/io/vector/TestUtilsTest.java create mode 100644 chart/src/test/java/org/xbib/graphics/chart/io/vector/VectorGraphics2DTest.java create mode 100644 chart/src/test/java/org/xbib/graphics/chart/io/vector/eps/EPSProcessorTest.java create mode 100644 chart/src/test/java/org/xbib/graphics/chart/io/vector/intermediate/filters/AbsoluteToRelativeTransformsFilterTest.java create mode 100644 chart/src/test/java/org/xbib/graphics/chart/io/vector/intermediate/filters/FillPaintedShapeAsImageFilterTest.java create mode 100644 chart/src/test/java/org/xbib/graphics/chart/io/vector/intermediate/filters/FilterTest.java create mode 100644 chart/src/test/java/org/xbib/graphics/chart/io/vector/intermediate/filters/GroupingFilterTest.java create mode 100644 chart/src/test/java/org/xbib/graphics/chart/io/vector/pdf/PDFProcessorTest.java create mode 100644 chart/src/test/java/org/xbib/graphics/chart/io/vector/svg/SVGProcessorTest.java create mode 100644 chart/src/test/java/org/xbib/graphics/chart/io/vector/util/ASCII85EncodeStreamTest.java create mode 100644 chart/src/test/java/org/xbib/graphics/chart/io/vector/util/Base64EncodeStreamTest.java create mode 100644 chart/src/test/java/org/xbib/graphics/chart/io/vector/util/DataUtilsTest.java create mode 100644 chart/src/test/java/org/xbib/graphics/chart/io/vector/util/GraphicsUtilsTest.java create mode 100644 chart/src/test/java/org/xbib/graphics/chart/io/vector/visual/AbstractTest.java create mode 100644 chart/src/test/java/org/xbib/graphics/chart/io/vector/visual/CharacterTest.java create mode 100644 chart/src/test/java/org/xbib/graphics/chart/io/vector/visual/ClippingTest.java create mode 100644 chart/src/test/java/org/xbib/graphics/chart/io/vector/visual/ColorTest.java create mode 100644 chart/src/test/java/org/xbib/graphics/chart/io/vector/visual/EmptyFileTest.java create mode 100644 chart/src/test/java/org/xbib/graphics/chart/io/vector/visual/FontTest.java create mode 100644 chart/src/test/java/org/xbib/graphics/chart/io/vector/visual/ImageTest.java create mode 100644 chart/src/test/java/org/xbib/graphics/chart/io/vector/visual/PaintTest.java create mode 100644 chart/src/test/java/org/xbib/graphics/chart/io/vector/visual/ShapesTest.java create mode 100644 chart/src/test/java/org/xbib/graphics/chart/io/vector/visual/StrokeTest.java create mode 100644 chart/src/test/java/org/xbib/graphics/chart/io/vector/visual/SwingExportTest.java create mode 100644 chart/src/test/java/org/xbib/graphics/chart/io/vector/visual/TestBrowser.java create mode 100644 chart/src/test/java/org/xbib/graphics/chart/io/vector/visual/TransformTest.java create mode 100644 chart/src/test/java/org/xbib/graphics/chart/swing/SwingWrapper.java create mode 100644 gradle/compile/java.gradle create mode 100644 gradle/documentation/asciidoc.gradle delete mode 100644 gradle/ext.gradle create mode 100644 gradle/ide/idea.gradle delete mode 100644 gradle/publish.gradle create mode 100644 gradle/publishing/publication.gradle create mode 100644 gradle/publishing/sonatype.gradle create mode 100644 gradle/test/junit5.gradle create mode 100644 gradle/wrapper/gradle-wrapper.jar create mode 100644 gradle/wrapper/gradle-wrapper.properties create mode 100755 gradlew create mode 100644 gradlew.bat create mode 100644 io-vector/NOTICE.txt create mode 100644 io-vector/build/docs/javadoc/allclasses-index.html create mode 100644 io-vector/build/docs/javadoc/allpackages-index.html create mode 100644 io-vector/build/docs/javadoc/constant-values.html create mode 100644 io-vector/build/docs/javadoc/deprecated-list.html create mode 100644 io-vector/build/docs/javadoc/element-list create mode 100644 io-vector/build/docs/javadoc/help-doc.html create mode 100644 io-vector/build/docs/javadoc/index-all.html create mode 100644 io-vector/build/docs/javadoc/index.html create mode 100644 io-vector/build/docs/javadoc/jquery-ui.overrides.css create mode 100644 io-vector/build/docs/javadoc/member-search-index.js create mode 100644 io-vector/build/docs/javadoc/module-search-index.js create mode 100644 io-vector/build/docs/javadoc/org.xbib.graphics.io.vector/module-summary.html create mode 100644 io-vector/build/docs/javadoc/org.xbib.graphics.io.vector/org/xbib/graphics/io/vector/Command.html create mode 100644 io-vector/build/docs/javadoc/org.xbib.graphics.io.vector/org/xbib/graphics/io/vector/GraphicsState.html create mode 100644 io-vector/build/docs/javadoc/org.xbib.graphics.io.vector/org/xbib/graphics/io/vector/PageSize.html create mode 100644 io-vector/build/docs/javadoc/org.xbib.graphics.io.vector/org/xbib/graphics/io/vector/Processor.html create mode 100644 io-vector/build/docs/javadoc/org.xbib.graphics.io.vector/org/xbib/graphics/io/vector/ProcessorResult.html create mode 100644 io-vector/build/docs/javadoc/org.xbib.graphics.io.vector/org/xbib/graphics/io/vector/VectorGraphics2D.html create mode 100644 io-vector/build/docs/javadoc/org.xbib.graphics.io.vector/org/xbib/graphics/io/vector/VectorGraphicsFormat.html create mode 100644 io-vector/build/docs/javadoc/org.xbib.graphics.io.vector/org/xbib/graphics/io/vector/commands/AffineTransformCommand.html create mode 100644 io-vector/build/docs/javadoc/org.xbib.graphics.io.vector/org/xbib/graphics/io/vector/commands/CreateCommand.html create mode 100644 io-vector/build/docs/javadoc/org.xbib.graphics.io.vector/org/xbib/graphics/io/vector/commands/DisposeCommand.html create mode 100644 io-vector/build/docs/javadoc/org.xbib.graphics.io.vector/org/xbib/graphics/io/vector/commands/DrawImageCommand.html create mode 100644 io-vector/build/docs/javadoc/org.xbib.graphics.io.vector/org/xbib/graphics/io/vector/commands/DrawShapeCommand.html create mode 100644 io-vector/build/docs/javadoc/org.xbib.graphics.io.vector/org/xbib/graphics/io/vector/commands/DrawStringCommand.html create mode 100644 io-vector/build/docs/javadoc/org.xbib.graphics.io.vector/org/xbib/graphics/io/vector/commands/FillShapeCommand.html create mode 100644 io-vector/build/docs/javadoc/org.xbib.graphics.io.vector/org/xbib/graphics/io/vector/commands/Group.html create mode 100644 io-vector/build/docs/javadoc/org.xbib.graphics.io.vector/org/xbib/graphics/io/vector/commands/RotateCommand.html create mode 100644 io-vector/build/docs/javadoc/org.xbib.graphics.io.vector/org/xbib/graphics/io/vector/commands/ScaleCommand.html create mode 100644 io-vector/build/docs/javadoc/org.xbib.graphics.io.vector/org/xbib/graphics/io/vector/commands/SetBackgroundCommand.html create mode 100644 io-vector/build/docs/javadoc/org.xbib.graphics.io.vector/org/xbib/graphics/io/vector/commands/SetClipCommand.html create mode 100644 io-vector/build/docs/javadoc/org.xbib.graphics.io.vector/org/xbib/graphics/io/vector/commands/SetColorCommand.html create mode 100644 io-vector/build/docs/javadoc/org.xbib.graphics.io.vector/org/xbib/graphics/io/vector/commands/SetCompositeCommand.html create mode 100644 io-vector/build/docs/javadoc/org.xbib.graphics.io.vector/org/xbib/graphics/io/vector/commands/SetFontCommand.html create mode 100644 io-vector/build/docs/javadoc/org.xbib.graphics.io.vector/org/xbib/graphics/io/vector/commands/SetHintCommand.html create mode 100644 io-vector/build/docs/javadoc/org.xbib.graphics.io.vector/org/xbib/graphics/io/vector/commands/SetPaintCommand.html create mode 100644 io-vector/build/docs/javadoc/org.xbib.graphics.io.vector/org/xbib/graphics/io/vector/commands/SetStrokeCommand.html create mode 100644 io-vector/build/docs/javadoc/org.xbib.graphics.io.vector/org/xbib/graphics/io/vector/commands/SetTransformCommand.html create mode 100644 io-vector/build/docs/javadoc/org.xbib.graphics.io.vector/org/xbib/graphics/io/vector/commands/SetXORModeCommand.html create mode 100644 io-vector/build/docs/javadoc/org.xbib.graphics.io.vector/org/xbib/graphics/io/vector/commands/ShearCommand.html create mode 100644 io-vector/build/docs/javadoc/org.xbib.graphics.io.vector/org/xbib/graphics/io/vector/commands/StateCommand.html create mode 100644 io-vector/build/docs/javadoc/org.xbib.graphics.io.vector/org/xbib/graphics/io/vector/commands/TransformCommand.html create mode 100644 io-vector/build/docs/javadoc/org.xbib.graphics.io.vector/org/xbib/graphics/io/vector/commands/TranslateCommand.html create mode 100644 io-vector/build/docs/javadoc/org.xbib.graphics.io.vector/org/xbib/graphics/io/vector/commands/package-summary.html create mode 100644 io-vector/build/docs/javadoc/org.xbib.graphics.io.vector/org/xbib/graphics/io/vector/commands/package-tree.html create mode 100644 io-vector/build/docs/javadoc/org.xbib.graphics.io.vector/org/xbib/graphics/io/vector/eps/EPSGraphics2D.html create mode 100644 io-vector/build/docs/javadoc/org.xbib.graphics.io.vector/org/xbib/graphics/io/vector/eps/EPSProcessor.html create mode 100644 io-vector/build/docs/javadoc/org.xbib.graphics.io.vector/org/xbib/graphics/io/vector/eps/EPSProcessorResult.html create mode 100644 io-vector/build/docs/javadoc/org.xbib.graphics.io.vector/org/xbib/graphics/io/vector/eps/package-summary.html create mode 100644 io-vector/build/docs/javadoc/org.xbib.graphics.io.vector/org/xbib/graphics/io/vector/eps/package-tree.html create mode 100644 io-vector/build/docs/javadoc/org.xbib.graphics.io.vector/org/xbib/graphics/io/vector/filters/AbsoluteToRelativeTransformsFilter.html create mode 100644 io-vector/build/docs/javadoc/org.xbib.graphics.io.vector/org/xbib/graphics/io/vector/filters/FillPaintedShapeAsImageFilter.html create mode 100644 io-vector/build/docs/javadoc/org.xbib.graphics.io.vector/org/xbib/graphics/io/vector/filters/Filter.html create mode 100644 io-vector/build/docs/javadoc/org.xbib.graphics.io.vector/org/xbib/graphics/io/vector/filters/GroupingFilter.html create mode 100644 io-vector/build/docs/javadoc/org.xbib.graphics.io.vector/org/xbib/graphics/io/vector/filters/OptimizeFilter.html create mode 100644 io-vector/build/docs/javadoc/org.xbib.graphics.io.vector/org/xbib/graphics/io/vector/filters/StateChangeGroupingFilter.html create mode 100644 io-vector/build/docs/javadoc/org.xbib.graphics.io.vector/org/xbib/graphics/io/vector/filters/package-summary.html create mode 100644 io-vector/build/docs/javadoc/org.xbib.graphics.io.vector/org/xbib/graphics/io/vector/filters/package-tree.html create mode 100644 io-vector/build/docs/javadoc/org.xbib.graphics.io.vector/org/xbib/graphics/io/vector/package-summary.html create mode 100644 io-vector/build/docs/javadoc/org.xbib.graphics.io.vector/org/xbib/graphics/io/vector/package-tree.html create mode 100644 io-vector/build/docs/javadoc/org.xbib.graphics.io.vector/org/xbib/graphics/io/vector/pdf/GeneratedPayload.html create mode 100644 io-vector/build/docs/javadoc/org.xbib.graphics.io.vector/org/xbib/graphics/io/vector/pdf/PDFGraphics2D.html create mode 100644 io-vector/build/docs/javadoc/org.xbib.graphics.io.vector/org/xbib/graphics/io/vector/pdf/PDFObject.html create mode 100644 io-vector/build/docs/javadoc/org.xbib.graphics.io.vector/org/xbib/graphics/io/vector/pdf/PDFProcessor.html create mode 100644 io-vector/build/docs/javadoc/org.xbib.graphics.io.vector/org/xbib/graphics/io/vector/pdf/PDFProcessorResult.html create mode 100644 io-vector/build/docs/javadoc/org.xbib.graphics.io.vector/org/xbib/graphics/io/vector/pdf/Payload.html create mode 100644 io-vector/build/docs/javadoc/org.xbib.graphics.io.vector/org/xbib/graphics/io/vector/pdf/Resources.html create mode 100644 io-vector/build/docs/javadoc/org.xbib.graphics.io.vector/org/xbib/graphics/io/vector/pdf/SizePayload.html create mode 100644 io-vector/build/docs/javadoc/org.xbib.graphics.io.vector/org/xbib/graphics/io/vector/pdf/package-summary.html create mode 100644 io-vector/build/docs/javadoc/org.xbib.graphics.io.vector/org/xbib/graphics/io/vector/pdf/package-tree.html create mode 100644 io-vector/build/docs/javadoc/org.xbib.graphics.io.vector/org/xbib/graphics/io/vector/svg/SVGGraphics2D.html create mode 100644 io-vector/build/docs/javadoc/org.xbib.graphics.io.vector/org/xbib/graphics/io/vector/svg/SVGProcessor.html create mode 100644 io-vector/build/docs/javadoc/org.xbib.graphics.io.vector/org/xbib/graphics/io/vector/svg/SVGProcessorResult.html create mode 100644 io-vector/build/docs/javadoc/org.xbib.graphics.io.vector/org/xbib/graphics/io/vector/svg/package-summary.html create mode 100644 io-vector/build/docs/javadoc/org.xbib.graphics.io.vector/org/xbib/graphics/io/vector/svg/package-tree.html create mode 100644 io-vector/build/docs/javadoc/org.xbib.graphics.io.vector/org/xbib/graphics/io/vector/util/ASCII85EncodeStream.html create mode 100644 io-vector/build/docs/javadoc/org.xbib.graphics.io.vector/org/xbib/graphics/io/vector/util/AlphaToMaskOp.html create mode 100644 io-vector/build/docs/javadoc/org.xbib.graphics.io.vector/org/xbib/graphics/io/vector/util/Base64EncodeStream.html create mode 100644 io-vector/build/docs/javadoc/org.xbib.graphics.io.vector/org/xbib/graphics/io/vector/util/DataUtils.html create mode 100644 io-vector/build/docs/javadoc/org.xbib.graphics.io.vector/org/xbib/graphics/io/vector/util/FlateEncodeStream.html create mode 100644 io-vector/build/docs/javadoc/org.xbib.graphics.io.vector/org/xbib/graphics/io/vector/util/FormattingWriter.html create mode 100644 io-vector/build/docs/javadoc/org.xbib.graphics.io.vector/org/xbib/graphics/io/vector/util/GraphicsUtils.html create mode 100644 io-vector/build/docs/javadoc/org.xbib.graphics.io.vector/org/xbib/graphics/io/vector/util/ImageDataStream.Interleaving.html create mode 100644 io-vector/build/docs/javadoc/org.xbib.graphics.io.vector/org/xbib/graphics/io/vector/util/ImageDataStream.html create mode 100644 io-vector/build/docs/javadoc/org.xbib.graphics.io.vector/org/xbib/graphics/io/vector/util/LineWrapOutputStream.html create mode 100644 io-vector/build/docs/javadoc/org.xbib.graphics.io.vector/org/xbib/graphics/io/vector/util/VectorHints.Key.html create mode 100644 io-vector/build/docs/javadoc/org.xbib.graphics.io.vector/org/xbib/graphics/io/vector/util/VectorHints.Value.html create mode 100644 io-vector/build/docs/javadoc/org.xbib.graphics.io.vector/org/xbib/graphics/io/vector/util/VectorHints.html create mode 100644 io-vector/build/docs/javadoc/org.xbib.graphics.io.vector/org/xbib/graphics/io/vector/util/package-summary.html create mode 100644 io-vector/build/docs/javadoc/org.xbib.graphics.io.vector/org/xbib/graphics/io/vector/util/package-tree.html create mode 100644 io-vector/build/docs/javadoc/overview-tree.html create mode 100644 io-vector/build/docs/javadoc/package-search-index.js create mode 100644 io-vector/build/docs/javadoc/resources/glass.png create mode 100644 io-vector/build/docs/javadoc/resources/x.png create mode 100644 io-vector/build/docs/javadoc/script-dir/images/ui-bg_glass_55_fbf9ee_1x400.png create mode 100644 io-vector/build/docs/javadoc/script-dir/images/ui-bg_glass_65_dadada_1x400.png create mode 100644 io-vector/build/docs/javadoc/script-dir/images/ui-bg_glass_75_dadada_1x400.png create mode 100644 io-vector/build/docs/javadoc/script-dir/images/ui-bg_glass_75_e6e6e6_1x400.png create mode 100644 io-vector/build/docs/javadoc/script-dir/images/ui-bg_glass_95_fef1ec_1x400.png create mode 100644 io-vector/build/docs/javadoc/script-dir/images/ui-bg_highlight-soft_75_cccccc_1x100.png create mode 100644 io-vector/build/docs/javadoc/script-dir/images/ui-icons_222222_256x240.png create mode 100644 io-vector/build/docs/javadoc/script-dir/images/ui-icons_2e83ff_256x240.png create mode 100644 io-vector/build/docs/javadoc/script-dir/images/ui-icons_454545_256x240.png create mode 100644 io-vector/build/docs/javadoc/script-dir/images/ui-icons_888888_256x240.png create mode 100644 io-vector/build/docs/javadoc/script-dir/images/ui-icons_cd0a0a_256x240.png create mode 100644 io-vector/build/docs/javadoc/script-dir/jquery-3.5.1.min.js create mode 100644 io-vector/build/docs/javadoc/script-dir/jquery-ui.min.css create mode 100644 io-vector/build/docs/javadoc/script-dir/jquery-ui.min.js create mode 100644 io-vector/build/docs/javadoc/script-dir/jquery-ui.structure.min.css create mode 100644 io-vector/build/docs/javadoc/script.js create mode 100644 io-vector/build/docs/javadoc/search.js create mode 100644 io-vector/build/docs/javadoc/stylesheet.css create mode 100644 io-vector/build/docs/javadoc/tag-search-index.js create mode 100644 io-vector/build/docs/javadoc/type-search-index.js create mode 100644 io-vector/build/libs/io-vector-3.0.0-javadoc.jar create mode 100644 io-vector/build/libs/io-vector-3.0.0-sources.jar create mode 100644 io-vector/build/libs/io-vector-3.0.0.jar create mode 100644 io-vector/build/reports/tests/test/classes/org.xbib.graphics.io.filters.AbsoluteToRelativeTransformsFilterTest.html create mode 100644 io-vector/build/reports/tests/test/classes/org.xbib.graphics.io.filters.FillPaintedShapeAsImageFilterTest.html create mode 100644 io-vector/build/reports/tests/test/classes/org.xbib.graphics.io.filters.FilterTest.html create mode 100644 io-vector/build/reports/tests/test/classes/org.xbib.graphics.io.filters.GroupingFilterTest.html create mode 100644 io-vector/build/reports/tests/test/classes/org.xbib.graphics.io.vector.GraphicsStateTest.html create mode 100644 io-vector/build/reports/tests/test/classes/org.xbib.graphics.io.vector.TestUtilsTest.html create mode 100644 io-vector/build/reports/tests/test/classes/org.xbib.graphics.io.vector.VectorGraphics2DTest.html create mode 100644 io-vector/build/reports/tests/test/classes/org.xbib.graphics.io.vector.eps.EPSProcessorTest.html create mode 100644 io-vector/build/reports/tests/test/classes/org.xbib.graphics.io.vector.pdf.PDFProcessorTest.html create mode 100644 io-vector/build/reports/tests/test/classes/org.xbib.graphics.io.vector.svg.SVGProcessorTest.html create mode 100644 io-vector/build/reports/tests/test/classes/org.xbib.graphics.io.vector.util.ASCII85EncodeStreamTest.html create mode 100644 io-vector/build/reports/tests/test/classes/org.xbib.graphics.io.vector.util.Base64EncodeStreamTest.html create mode 100644 io-vector/build/reports/tests/test/classes/org.xbib.graphics.io.vector.util.DataUtilsTest.html create mode 100644 io-vector/build/reports/tests/test/classes/org.xbib.graphics.io.vector.util.GraphicsUtilsTest.html create mode 100644 io-vector/build/reports/tests/test/css/base-style.css create mode 100644 io-vector/build/reports/tests/test/css/style.css create mode 100644 io-vector/build/reports/tests/test/index.html create mode 100644 io-vector/build/reports/tests/test/js/report.js create mode 100644 io-vector/build/reports/tests/test/packages/org.xbib.graphics.io.filters.html create mode 100644 io-vector/build/reports/tests/test/packages/org.xbib.graphics.io.vector.eps.html create mode 100644 io-vector/build/reports/tests/test/packages/org.xbib.graphics.io.vector.html create mode 100644 io-vector/build/reports/tests/test/packages/org.xbib.graphics.io.vector.pdf.html create mode 100644 io-vector/build/reports/tests/test/packages/org.xbib.graphics.io.vector.svg.html create mode 100644 io-vector/build/reports/tests/test/packages/org.xbib.graphics.io.vector.util.html create mode 100644 io-vector/build/test-results/test/TEST-org.xbib.graphics.io.filters.AbsoluteToRelativeTransformsFilterTest.xml create mode 100644 io-vector/build/test-results/test/TEST-org.xbib.graphics.io.filters.FillPaintedShapeAsImageFilterTest.xml create mode 100644 io-vector/build/test-results/test/TEST-org.xbib.graphics.io.filters.FilterTest.xml create mode 100644 io-vector/build/test-results/test/TEST-org.xbib.graphics.io.filters.GroupingFilterTest.xml create mode 100644 io-vector/build/test-results/test/TEST-org.xbib.graphics.io.vector.GraphicsStateTest.xml create mode 100644 io-vector/build/test-results/test/TEST-org.xbib.graphics.io.vector.TestUtilsTest.xml create mode 100644 io-vector/build/test-results/test/TEST-org.xbib.graphics.io.vector.VectorGraphics2DTest.xml create mode 100644 io-vector/build/test-results/test/TEST-org.xbib.graphics.io.vector.eps.EPSProcessorTest.xml create mode 100644 io-vector/build/test-results/test/TEST-org.xbib.graphics.io.vector.pdf.PDFProcessorTest.xml create mode 100644 io-vector/build/test-results/test/TEST-org.xbib.graphics.io.vector.svg.SVGProcessorTest.xml create mode 100644 io-vector/build/test-results/test/TEST-org.xbib.graphics.io.vector.util.ASCII85EncodeStreamTest.xml create mode 100644 io-vector/build/test-results/test/TEST-org.xbib.graphics.io.vector.util.Base64EncodeStreamTest.xml create mode 100644 io-vector/build/test-results/test/TEST-org.xbib.graphics.io.vector.util.DataUtilsTest.xml create mode 100644 io-vector/build/test-results/test/TEST-org.xbib.graphics.io.vector.util.GraphicsUtilsTest.xml create mode 100644 io-vector/build/test-results/test/binary/output.bin create mode 100644 io-vector/build/test-results/test/binary/output.bin.idx create mode 100644 io-vector/build/test-results/test/binary/results.bin create mode 100644 io-vector/build/tmp/compileJava/source-classes-mapping.txt create mode 100644 io-vector/build/tmp/compileTestJava/source-classes-mapping.txt create mode 100644 io-vector/build/tmp/jar/MANIFEST.MF create mode 100644 io-vector/build/tmp/javadoc/javadoc.options create mode 100644 io-vector/build/tmp/javadocJar/MANIFEST.MF create mode 100644 io-vector/build/tmp/sourcesJar/MANIFEST.MF create mode 100644 io-vector/src/main/java/module-info.java create mode 100644 io-vector/src/main/java/org/xbib/graphics/io/vector/Command.java create mode 100644 io-vector/src/main/java/org/xbib/graphics/io/vector/GraphicsState.java create mode 100644 io-vector/src/main/java/org/xbib/graphics/io/vector/PageSize.java create mode 100644 io-vector/src/main/java/org/xbib/graphics/io/vector/Processor.java create mode 100644 io-vector/src/main/java/org/xbib/graphics/io/vector/ProcessorResult.java create mode 100644 io-vector/src/main/java/org/xbib/graphics/io/vector/VectorGraphics2D.java create mode 100644 io-vector/src/main/java/org/xbib/graphics/io/vector/VectorGraphicsFormat.java create mode 100644 io-vector/src/main/java/org/xbib/graphics/io/vector/commands/AffineTransformCommand.java create mode 100644 io-vector/src/main/java/org/xbib/graphics/io/vector/commands/CreateCommand.java create mode 100644 io-vector/src/main/java/org/xbib/graphics/io/vector/commands/DisposeCommand.java create mode 100644 io-vector/src/main/java/org/xbib/graphics/io/vector/commands/DrawImageCommand.java create mode 100644 io-vector/src/main/java/org/xbib/graphics/io/vector/commands/DrawShapeCommand.java create mode 100644 io-vector/src/main/java/org/xbib/graphics/io/vector/commands/DrawStringCommand.java create mode 100644 io-vector/src/main/java/org/xbib/graphics/io/vector/commands/FillShapeCommand.java create mode 100644 io-vector/src/main/java/org/xbib/graphics/io/vector/commands/Group.java create mode 100644 io-vector/src/main/java/org/xbib/graphics/io/vector/commands/RotateCommand.java create mode 100644 io-vector/src/main/java/org/xbib/graphics/io/vector/commands/ScaleCommand.java create mode 100644 io-vector/src/main/java/org/xbib/graphics/io/vector/commands/SetBackgroundCommand.java create mode 100644 io-vector/src/main/java/org/xbib/graphics/io/vector/commands/SetClipCommand.java create mode 100644 io-vector/src/main/java/org/xbib/graphics/io/vector/commands/SetColorCommand.java create mode 100644 io-vector/src/main/java/org/xbib/graphics/io/vector/commands/SetCompositeCommand.java create mode 100644 io-vector/src/main/java/org/xbib/graphics/io/vector/commands/SetFontCommand.java create mode 100644 io-vector/src/main/java/org/xbib/graphics/io/vector/commands/SetHintCommand.java create mode 100644 io-vector/src/main/java/org/xbib/graphics/io/vector/commands/SetPaintCommand.java create mode 100644 io-vector/src/main/java/org/xbib/graphics/io/vector/commands/SetStrokeCommand.java create mode 100644 io-vector/src/main/java/org/xbib/graphics/io/vector/commands/SetTransformCommand.java create mode 100644 io-vector/src/main/java/org/xbib/graphics/io/vector/commands/SetXORModeCommand.java create mode 100644 io-vector/src/main/java/org/xbib/graphics/io/vector/commands/ShearCommand.java create mode 100644 io-vector/src/main/java/org/xbib/graphics/io/vector/commands/StateCommand.java create mode 100644 io-vector/src/main/java/org/xbib/graphics/io/vector/commands/TransformCommand.java create mode 100644 io-vector/src/main/java/org/xbib/graphics/io/vector/commands/TranslateCommand.java create mode 100644 io-vector/src/main/java/org/xbib/graphics/io/vector/eps/EPSGraphics2D.java create mode 100644 io-vector/src/main/java/org/xbib/graphics/io/vector/eps/EPSProcessor.java create mode 100644 io-vector/src/main/java/org/xbib/graphics/io/vector/eps/EPSProcessorResult.java create mode 100644 io-vector/src/main/java/org/xbib/graphics/io/vector/filters/AbsoluteToRelativeTransformsFilter.java create mode 100644 io-vector/src/main/java/org/xbib/graphics/io/vector/filters/FillPaintedShapeAsImageFilter.java create mode 100644 io-vector/src/main/java/org/xbib/graphics/io/vector/filters/Filter.java create mode 100644 io-vector/src/main/java/org/xbib/graphics/io/vector/filters/GroupingFilter.java create mode 100644 io-vector/src/main/java/org/xbib/graphics/io/vector/filters/OptimizeFilter.java create mode 100644 io-vector/src/main/java/org/xbib/graphics/io/vector/filters/StateChangeGroupingFilter.java create mode 100644 io-vector/src/main/java/org/xbib/graphics/io/vector/pdf/GeneratedPayload.java create mode 100644 io-vector/src/main/java/org/xbib/graphics/io/vector/pdf/PDFGraphics2D.java create mode 100644 io-vector/src/main/java/org/xbib/graphics/io/vector/pdf/PDFObject.java create mode 100644 io-vector/src/main/java/org/xbib/graphics/io/vector/pdf/PDFProcessor.java create mode 100644 io-vector/src/main/java/org/xbib/graphics/io/vector/pdf/PDFProcessorResult.java create mode 100644 io-vector/src/main/java/org/xbib/graphics/io/vector/pdf/Payload.java create mode 100644 io-vector/src/main/java/org/xbib/graphics/io/vector/pdf/Resources.java create mode 100644 io-vector/src/main/java/org/xbib/graphics/io/vector/pdf/SizePayload.java create mode 100644 io-vector/src/main/java/org/xbib/graphics/io/vector/svg/SVGGraphics2D.java create mode 100644 io-vector/src/main/java/org/xbib/graphics/io/vector/svg/SVGProcessor.java create mode 100644 io-vector/src/main/java/org/xbib/graphics/io/vector/svg/SVGProcessorResult.java create mode 100644 io-vector/src/main/java/org/xbib/graphics/io/vector/util/ASCII85EncodeStream.java create mode 100644 io-vector/src/main/java/org/xbib/graphics/io/vector/util/AlphaToMaskOp.java create mode 100644 io-vector/src/main/java/org/xbib/graphics/io/vector/util/Base64EncodeStream.java create mode 100644 io-vector/src/main/java/org/xbib/graphics/io/vector/util/DataUtils.java create mode 100644 io-vector/src/main/java/org/xbib/graphics/io/vector/util/FlateEncodeStream.java create mode 100644 io-vector/src/main/java/org/xbib/graphics/io/vector/util/FormattingWriter.java create mode 100644 io-vector/src/main/java/org/xbib/graphics/io/vector/util/GraphicsUtils.java create mode 100644 io-vector/src/main/java/org/xbib/graphics/io/vector/util/ImageDataStream.java create mode 100644 io-vector/src/main/java/org/xbib/graphics/io/vector/util/LineWrapOutputStream.java create mode 100644 io-vector/src/main/java/org/xbib/graphics/io/vector/util/VectorHints.java create mode 100644 io-vector/src/test/java/org/xbib/graphics/io/filters/AbsoluteToRelativeTransformsFilterTest.java create mode 100644 io-vector/src/test/java/org/xbib/graphics/io/filters/FillPaintedShapeAsImageFilterTest.java create mode 100644 io-vector/src/test/java/org/xbib/graphics/io/filters/FilterTest.java create mode 100644 io-vector/src/test/java/org/xbib/graphics/io/filters/GroupingFilterTest.java create mode 100644 io-vector/src/test/java/org/xbib/graphics/io/vector/GraphicsStateTest.java create mode 100644 io-vector/src/test/java/org/xbib/graphics/io/vector/TestUtils.java create mode 100644 io-vector/src/test/java/org/xbib/graphics/io/vector/TestUtilsTest.java create mode 100644 io-vector/src/test/java/org/xbib/graphics/io/vector/VectorGraphics2DTest.java create mode 100644 io-vector/src/test/java/org/xbib/graphics/io/vector/eps/EPSProcessorTest.java create mode 100644 io-vector/src/test/java/org/xbib/graphics/io/vector/pdf/PDFProcessorTest.java create mode 100644 io-vector/src/test/java/org/xbib/graphics/io/vector/svg/SVGProcessorTest.java create mode 100644 io-vector/src/test/java/org/xbib/graphics/io/vector/util/ASCII85EncodeStreamTest.java create mode 100644 io-vector/src/test/java/org/xbib/graphics/io/vector/util/Base64EncodeStreamTest.java create mode 100644 io-vector/src/test/java/org/xbib/graphics/io/vector/util/DataUtilsTest.java create mode 100644 io-vector/src/test/java/org/xbib/graphics/io/vector/util/GraphicsUtilsTest.java create mode 100644 io-vector/src/test/java/org/xbib/graphics/io/visual/AbstractTest.java create mode 100644 io-vector/src/test/java/org/xbib/graphics/io/visual/CharacterTest.java create mode 100644 io-vector/src/test/java/org/xbib/graphics/io/visual/ClippingTest.java create mode 100644 io-vector/src/test/java/org/xbib/graphics/io/visual/ColorTest.java create mode 100644 io-vector/src/test/java/org/xbib/graphics/io/visual/EmptyFileTest.java create mode 100644 io-vector/src/test/java/org/xbib/graphics/io/visual/FontTest.java create mode 100644 io-vector/src/test/java/org/xbib/graphics/io/visual/ImageTest.java create mode 100644 io-vector/src/test/java/org/xbib/graphics/io/visual/PaintTest.java create mode 100644 io-vector/src/test/java/org/xbib/graphics/io/visual/ShapesTest.java create mode 100644 io-vector/src/test/java/org/xbib/graphics/io/visual/StrokeTest.java create mode 100644 io-vector/src/test/java/org/xbib/graphics/io/visual/SwingExportTest.java create mode 100644 io-vector/src/test/java/org/xbib/graphics/io/visual/TestBrowser.java create mode 100644 io-vector/src/test/java/org/xbib/graphics/io/visual/TransformTest.java create mode 100644 layout-pdfbox/NOTICE.txt create mode 100644 layout-pdfbox/build.gradle create mode 100644 layout-pdfbox/src/main/java/org/xbib/graphics/layout/pdfbox/elements/ControlElement.java create mode 100644 layout-pdfbox/src/main/java/org/xbib/graphics/layout/pdfbox/elements/Cutter.java create mode 100644 layout-pdfbox/src/main/java/org/xbib/graphics/layout/pdfbox/elements/Dimension.java create mode 100644 layout-pdfbox/src/main/java/org/xbib/graphics/layout/pdfbox/elements/Dividable.java create mode 100644 layout-pdfbox/src/main/java/org/xbib/graphics/layout/pdfbox/elements/Document.java create mode 100644 layout-pdfbox/src/main/java/org/xbib/graphics/layout/pdfbox/elements/Drawable.java create mode 100644 layout-pdfbox/src/main/java/org/xbib/graphics/layout/pdfbox/elements/Element.java create mode 100644 layout-pdfbox/src/main/java/org/xbib/graphics/layout/pdfbox/elements/Frame.java create mode 100644 layout-pdfbox/src/main/java/org/xbib/graphics/layout/pdfbox/elements/HorizontalRuler.java create mode 100644 layout-pdfbox/src/main/java/org/xbib/graphics/layout/pdfbox/elements/ImageElement.java create mode 100644 layout-pdfbox/src/main/java/org/xbib/graphics/layout/pdfbox/elements/Orientation.java create mode 100644 layout-pdfbox/src/main/java/org/xbib/graphics/layout/pdfbox/elements/PageFormat.java create mode 100644 layout-pdfbox/src/main/java/org/xbib/graphics/layout/pdfbox/elements/Paragraph.java create mode 100644 layout-pdfbox/src/main/java/org/xbib/graphics/layout/pdfbox/elements/PositionControl.java create mode 100644 layout-pdfbox/src/main/java/org/xbib/graphics/layout/pdfbox/elements/Rectangle.java create mode 100644 layout-pdfbox/src/main/java/org/xbib/graphics/layout/pdfbox/elements/VerticalSpacer.java create mode 100644 layout-pdfbox/src/main/java/org/xbib/graphics/layout/pdfbox/elements/render/ColumnLayout.java create mode 100644 layout-pdfbox/src/main/java/org/xbib/graphics/layout/pdfbox/elements/render/ColumnLayoutHint.java create mode 100644 layout-pdfbox/src/main/java/org/xbib/graphics/layout/pdfbox/elements/render/Layout.java create mode 100644 layout-pdfbox/src/main/java/org/xbib/graphics/layout/pdfbox/elements/render/LayoutHint.java create mode 100644 layout-pdfbox/src/main/java/org/xbib/graphics/layout/pdfbox/elements/render/RenderContext.java create mode 100644 layout-pdfbox/src/main/java/org/xbib/graphics/layout/pdfbox/elements/render/RenderListener.java create mode 100644 layout-pdfbox/src/main/java/org/xbib/graphics/layout/pdfbox/elements/render/Renderer.java create mode 100644 layout-pdfbox/src/main/java/org/xbib/graphics/layout/pdfbox/elements/render/VerticalLayout.java create mode 100644 layout-pdfbox/src/main/java/org/xbib/graphics/layout/pdfbox/elements/render/VerticalLayoutHint.java create mode 100644 layout-pdfbox/src/main/java/org/xbib/graphics/layout/pdfbox/shape/AbstractShape.java create mode 100644 layout-pdfbox/src/main/java/org/xbib/graphics/layout/pdfbox/shape/Ellipse.java create mode 100644 layout-pdfbox/src/main/java/org/xbib/graphics/layout/pdfbox/shape/Rect.java create mode 100644 layout-pdfbox/src/main/java/org/xbib/graphics/layout/pdfbox/shape/RoundRect.java create mode 100644 layout-pdfbox/src/main/java/org/xbib/graphics/layout/pdfbox/shape/Shape.java create mode 100644 layout-pdfbox/src/main/java/org/xbib/graphics/layout/pdfbox/shape/Stroke.java create mode 100644 layout-pdfbox/src/main/java/org/xbib/graphics/layout/pdfbox/text/Alignment.java create mode 100644 layout-pdfbox/src/main/java/org/xbib/graphics/layout/pdfbox/text/Area.java create mode 100644 layout-pdfbox/src/main/java/org/xbib/graphics/layout/pdfbox/text/BaseFont.java create mode 100644 layout-pdfbox/src/main/java/org/xbib/graphics/layout/pdfbox/text/Constants.java create mode 100644 layout-pdfbox/src/main/java/org/xbib/graphics/layout/pdfbox/text/ControlCharacter.java create mode 100644 layout-pdfbox/src/main/java/org/xbib/graphics/layout/pdfbox/text/ControlCharacters.java create mode 100644 layout-pdfbox/src/main/java/org/xbib/graphics/layout/pdfbox/text/ControlFragment.java create mode 100644 layout-pdfbox/src/main/java/org/xbib/graphics/layout/pdfbox/text/DrawContext.java create mode 100644 layout-pdfbox/src/main/java/org/xbib/graphics/layout/pdfbox/text/DrawListener.java create mode 100644 layout-pdfbox/src/main/java/org/xbib/graphics/layout/pdfbox/text/DrawableText.java create mode 100644 layout-pdfbox/src/main/java/org/xbib/graphics/layout/pdfbox/text/FontDescriptor.java create mode 100644 layout-pdfbox/src/main/java/org/xbib/graphics/layout/pdfbox/text/Indent.java create mode 100644 layout-pdfbox/src/main/java/org/xbib/graphics/layout/pdfbox/text/IndentCharacters.java create mode 100644 layout-pdfbox/src/main/java/org/xbib/graphics/layout/pdfbox/text/NewLine.java create mode 100644 layout-pdfbox/src/main/java/org/xbib/graphics/layout/pdfbox/text/Position.java create mode 100644 layout-pdfbox/src/main/java/org/xbib/graphics/layout/pdfbox/text/ReplacedWhitespace.java create mode 100644 layout-pdfbox/src/main/java/org/xbib/graphics/layout/pdfbox/text/SpaceUnit.java create mode 100644 layout-pdfbox/src/main/java/org/xbib/graphics/layout/pdfbox/text/StyledText.java create mode 100644 layout-pdfbox/src/main/java/org/xbib/graphics/layout/pdfbox/text/TextFlow.java create mode 100644 layout-pdfbox/src/main/java/org/xbib/graphics/layout/pdfbox/text/TextFlowUtil.java create mode 100644 layout-pdfbox/src/main/java/org/xbib/graphics/layout/pdfbox/text/TextFragment.java create mode 100644 layout-pdfbox/src/main/java/org/xbib/graphics/layout/pdfbox/text/TextLine.java create mode 100644 layout-pdfbox/src/main/java/org/xbib/graphics/layout/pdfbox/text/TextSequence.java create mode 100644 layout-pdfbox/src/main/java/org/xbib/graphics/layout/pdfbox/text/TextSequenceUtil.java create mode 100644 layout-pdfbox/src/main/java/org/xbib/graphics/layout/pdfbox/text/WidthRespecting.java create mode 100644 layout-pdfbox/src/main/java/org/xbib/graphics/layout/pdfbox/text/WrappingNewLine.java create mode 100644 layout-pdfbox/src/main/java/org/xbib/graphics/layout/pdfbox/text/annotations/Annotated.java create mode 100644 layout-pdfbox/src/main/java/org/xbib/graphics/layout/pdfbox/text/annotations/AnnotatedStyledText.java create mode 100644 layout-pdfbox/src/main/java/org/xbib/graphics/layout/pdfbox/text/annotations/Annotation.java create mode 100644 layout-pdfbox/src/main/java/org/xbib/graphics/layout/pdfbox/text/annotations/AnnotationCharacters.java create mode 100644 layout-pdfbox/src/main/java/org/xbib/graphics/layout/pdfbox/text/annotations/AnnotationDrawListener.java create mode 100644 layout-pdfbox/src/main/java/org/xbib/graphics/layout/pdfbox/text/annotations/AnnotationProcessor.java create mode 100644 layout-pdfbox/src/main/java/org/xbib/graphics/layout/pdfbox/text/annotations/AnnotationProcessorFactory.java create mode 100644 layout-pdfbox/src/main/java/org/xbib/graphics/layout/pdfbox/text/annotations/Annotations.java create mode 100644 layout-pdfbox/src/main/java/org/xbib/graphics/layout/pdfbox/text/annotations/HyperlinkAnnotationProcessor.java create mode 100644 layout-pdfbox/src/main/java/org/xbib/graphics/layout/pdfbox/text/annotations/UnderlineAnnotationProcessor.java create mode 100644 layout-pdfbox/src/main/java/org/xbib/graphics/layout/pdfbox/util/CompatibilityHelper.java create mode 100644 layout-pdfbox/src/main/java/org/xbib/graphics/layout/pdfbox/util/Enumerator.java create mode 100644 layout-pdfbox/src/main/java/org/xbib/graphics/layout/pdfbox/util/EnumeratorFactory.java create mode 100644 layout-pdfbox/src/main/java/org/xbib/graphics/layout/pdfbox/util/Enumerators.java create mode 100644 layout-pdfbox/src/main/java/org/xbib/graphics/layout/pdfbox/util/Pair.java create mode 100644 layout-pdfbox/src/main/java/org/xbib/graphics/layout/pdfbox/util/WordBreaker.java create mode 100644 layout-pdfbox/src/main/java/org/xbib/graphics/layout/pdfbox/util/WordBreakerFactory.java create mode 100644 layout-pdfbox/src/main/java/org/xbib/graphics/layout/pdfbox/util/WordBreakers.java create mode 100644 layout-pdfbox/src/test/java/org/xbib/graphics/layout/pdfbox/Aligned.java create mode 100644 layout-pdfbox/src/test/java/org/xbib/graphics/layout/pdfbox/Columns.java create mode 100644 layout-pdfbox/src/test/java/org/xbib/graphics/layout/pdfbox/CustomAnnotation.java create mode 100644 layout-pdfbox/src/test/java/org/xbib/graphics/layout/pdfbox/CustomRenderer.java create mode 100644 layout-pdfbox/src/test/java/org/xbib/graphics/layout/pdfbox/Frames.java create mode 100644 layout-pdfbox/src/test/java/org/xbib/graphics/layout/pdfbox/HelloDoc.java create mode 100644 layout-pdfbox/src/test/java/org/xbib/graphics/layout/pdfbox/Indentation.java create mode 100644 layout-pdfbox/src/test/java/org/xbib/graphics/layout/pdfbox/Landscape.java create mode 100644 layout-pdfbox/src/test/java/org/xbib/graphics/layout/pdfbox/Letter.java create mode 100644 layout-pdfbox/src/test/java/org/xbib/graphics/layout/pdfbox/LineSpacing.java create mode 100644 layout-pdfbox/src/test/java/org/xbib/graphics/layout/pdfbox/Links.java create mode 100644 layout-pdfbox/src/test/java/org/xbib/graphics/layout/pdfbox/Listener.java create mode 100644 layout-pdfbox/src/test/java/org/xbib/graphics/layout/pdfbox/LowLevelText.java create mode 100644 layout-pdfbox/src/test/java/org/xbib/graphics/layout/pdfbox/Margin.java create mode 100644 layout-pdfbox/src/test/java/org/xbib/graphics/layout/pdfbox/Markup.java create mode 100644 layout-pdfbox/src/test/java/org/xbib/graphics/layout/pdfbox/MultiplePages.java create mode 100644 layout-pdfbox/src/test/java/org/xbib/graphics/layout/pdfbox/Rotation.java create mode 100644 layout-pdfbox/src/test/resources/org/xbib/graphics/layout/pdfbox/aligned.pdf create mode 100644 layout-pdfbox/src/test/resources/org/xbib/graphics/layout/pdfbox/arrow.png create mode 100644 layout-pdfbox/src/test/resources/org/xbib/graphics/layout/pdfbox/columns.pdf create mode 100644 layout-pdfbox/src/test/resources/org/xbib/graphics/layout/pdfbox/customannotation.pdf create mode 100644 layout-pdfbox/src/test/resources/org/xbib/graphics/layout/pdfbox/customrenderer.pdf create mode 100644 layout-pdfbox/src/test/resources/org/xbib/graphics/layout/pdfbox/frames.pdf create mode 100644 layout-pdfbox/src/test/resources/org/xbib/graphics/layout/pdfbox/hellodoc.pdf create mode 100644 layout-pdfbox/src/test/resources/org/xbib/graphics/layout/pdfbox/indentation.pdf create mode 100644 layout-pdfbox/src/test/resources/org/xbib/graphics/layout/pdfbox/landscape.pdf create mode 100644 layout-pdfbox/src/test/resources/org/xbib/graphics/layout/pdfbox/letter.pdf create mode 100644 layout-pdfbox/src/test/resources/org/xbib/graphics/layout/pdfbox/linespacing.pdf create mode 100644 layout-pdfbox/src/test/resources/org/xbib/graphics/layout/pdfbox/links.pdf create mode 100644 layout-pdfbox/src/test/resources/org/xbib/graphics/layout/pdfbox/listener.pdf create mode 100644 layout-pdfbox/src/test/resources/org/xbib/graphics/layout/pdfbox/lowleveltext.pdf create mode 100644 layout-pdfbox/src/test/resources/org/xbib/graphics/layout/pdfbox/margin.pdf create mode 100644 layout-pdfbox/src/test/resources/org/xbib/graphics/layout/pdfbox/markup.pdf create mode 100644 layout-pdfbox/src/test/resources/org/xbib/graphics/layout/pdfbox/multiplepages.pdf create mode 100644 layout-pdfbox/src/test/resources/org/xbib/graphics/layout/pdfbox/rotation.pdf create mode 100644 png/NOTICE.txt create mode 100644 png/build.gradle create mode 100644 png/build/resources/main/META-INF/services/javax.imageio.spi.ImageWriterSpi create mode 100644 png/build/tmp/compileJava/source-classes-mapping.txt create mode 100644 png/build/tmp/compileTestJava/source-classes-mapping.txt create mode 100644 png/src/main/java/org/xbib/graphics/imageio/plugins/png/AbstractScanlineProvider.java create mode 100644 png/src/main/java/org/xbib/graphics/imageio/plugins/png/PNGWriter.java create mode 100644 png/src/main/java/org/xbib/graphics/imageio/plugins/png/RasterByteABGRProvider.java create mode 100644 png/src/main/java/org/xbib/graphics/imageio/plugins/png/RasterByteGrayAlphaProvider.java create mode 100644 png/src/main/java/org/xbib/graphics/imageio/plugins/png/RasterByteRepackSingleBandProvider.java create mode 100644 png/src/main/java/org/xbib/graphics/imageio/plugins/png/RasterByteSingleBandProvider.java create mode 100644 png/src/main/java/org/xbib/graphics/imageio/plugins/png/RasterByteSingleBandSkippingBytesProvider.java create mode 100644 png/src/main/java/org/xbib/graphics/imageio/plugins/png/RasterIntABGRProvider.java create mode 100644 png/src/main/java/org/xbib/graphics/imageio/plugins/png/RasterShortABGRProvider.java create mode 100644 png/src/main/java/org/xbib/graphics/imageio/plugins/png/RasterShortGrayAlphaProvider.java create mode 100644 png/src/main/java/org/xbib/graphics/imageio/plugins/png/RasterShortSingleBandProvider.java create mode 100644 png/src/main/java/org/xbib/graphics/imageio/plugins/png/ScanlineCursor.java create mode 100644 png/src/main/java/org/xbib/graphics/imageio/plugins/png/ScanlineProvider.java create mode 100644 png/src/main/java/org/xbib/graphics/imageio/plugins/png/ScanlineProviderFactory.java create mode 100644 png/src/main/java/org/xbib/graphics/imageio/plugins/png/pngj/BufferedStreamFeeder.java create mode 100644 png/src/main/java/org/xbib/graphics/imageio/plugins/png/pngj/ChunkReader.java create mode 100644 png/src/main/java/org/xbib/graphics/imageio/plugins/png/pngj/ChunkSeqBuffering.java create mode 100644 png/src/main/java/org/xbib/graphics/imageio/plugins/png/pngj/ChunkSeqReader.java create mode 100644 png/src/main/java/org/xbib/graphics/imageio/plugins/png/pngj/ChunkSeqReaderPng.java create mode 100644 png/src/main/java/org/xbib/graphics/imageio/plugins/png/pngj/ChunkSeqSkipping.java create mode 100644 png/src/main/java/org/xbib/graphics/imageio/plugins/png/pngj/DeflatedChunkReader.java create mode 100644 png/src/main/java/org/xbib/graphics/imageio/plugins/png/pngj/DeflatedChunksSet.java create mode 100644 png/src/main/java/org/xbib/graphics/imageio/plugins/png/pngj/Deinterlacer.java create mode 100644 png/src/main/java/org/xbib/graphics/imageio/plugins/png/pngj/ErrorBehaviour.java create mode 100644 png/src/main/java/org/xbib/graphics/imageio/plugins/png/pngj/FilterType.java create mode 100644 png/src/main/java/org/xbib/graphics/imageio/plugins/png/pngj/IBytesConsumer.java create mode 100644 png/src/main/java/org/xbib/graphics/imageio/plugins/png/pngj/IChunkFactory.java create mode 100644 png/src/main/java/org/xbib/graphics/imageio/plugins/png/pngj/IImageLine.java create mode 100644 png/src/main/java/org/xbib/graphics/imageio/plugins/png/pngj/IImageLineArray.java create mode 100644 png/src/main/java/org/xbib/graphics/imageio/plugins/png/pngj/IImageLineFactory.java create mode 100644 png/src/main/java/org/xbib/graphics/imageio/plugins/png/pngj/IImageLineSet.java create mode 100644 png/src/main/java/org/xbib/graphics/imageio/plugins/png/pngj/IImageLineSetFactory.java create mode 100644 png/src/main/java/org/xbib/graphics/imageio/plugins/png/pngj/IPngWriterFactory.java create mode 100644 png/src/main/java/org/xbib/graphics/imageio/plugins/png/pngj/IdatChunkWriter.java create mode 100644 png/src/main/java/org/xbib/graphics/imageio/plugins/png/pngj/IdatSet.java create mode 100644 png/src/main/java/org/xbib/graphics/imageio/plugins/png/pngj/ImageInfo.java create mode 100644 png/src/main/java/org/xbib/graphics/imageio/plugins/png/pngj/ImageLineByte.java create mode 100644 png/src/main/java/org/xbib/graphics/imageio/plugins/png/pngj/ImageLineHelper.java create mode 100644 png/src/main/java/org/xbib/graphics/imageio/plugins/png/pngj/ImageLineInt.java create mode 100644 png/src/main/java/org/xbib/graphics/imageio/plugins/png/pngj/ImageLineSetDefault.java create mode 100644 png/src/main/java/org/xbib/graphics/imageio/plugins/png/pngj/PngHelperInternal.java create mode 100644 png/src/main/java/org/xbib/graphics/imageio/plugins/png/pngj/PngHelperInternal2.java create mode 100644 png/src/main/java/org/xbib/graphics/imageio/plugins/png/pngj/PngReader.java create mode 100644 png/src/main/java/org/xbib/graphics/imageio/plugins/png/pngj/PngReaderApng.java create mode 100644 png/src/main/java/org/xbib/graphics/imageio/plugins/png/pngj/PngReaderByte.java create mode 100644 png/src/main/java/org/xbib/graphics/imageio/plugins/png/pngj/PngReaderFilter.java create mode 100644 png/src/main/java/org/xbib/graphics/imageio/plugins/png/pngj/PngReaderInt.java create mode 100644 png/src/main/java/org/xbib/graphics/imageio/plugins/png/pngj/PngWriter.java create mode 100644 png/src/main/java/org/xbib/graphics/imageio/plugins/png/pngj/PngWriterHc.java create mode 100644 png/src/main/java/org/xbib/graphics/imageio/plugins/png/pngj/PngjBadCrcException.java create mode 100644 png/src/main/java/org/xbib/graphics/imageio/plugins/png/pngj/PngjBadSignature.java create mode 100644 png/src/main/java/org/xbib/graphics/imageio/plugins/png/pngj/PngjException.java create mode 100644 png/src/main/java/org/xbib/graphics/imageio/plugins/png/pngj/PngjExceptionInternal.java create mode 100644 png/src/main/java/org/xbib/graphics/imageio/plugins/png/pngj/PngjInputException.java create mode 100644 png/src/main/java/org/xbib/graphics/imageio/plugins/png/pngj/PngjOutputException.java create mode 100644 png/src/main/java/org/xbib/graphics/imageio/plugins/png/pngj/PngjPrematureEnding.java create mode 100644 png/src/main/java/org/xbib/graphics/imageio/plugins/png/pngj/PngjUnsupportedException.java create mode 100644 png/src/main/java/org/xbib/graphics/imageio/plugins/png/pngj/RowInfo.java create mode 100644 png/src/main/java/org/xbib/graphics/imageio/plugins/png/pngj/chunks/ChunkCopyBehaviour.java create mode 100644 png/src/main/java/org/xbib/graphics/imageio/plugins/png/pngj/chunks/ChunkFactory.java create mode 100644 png/src/main/java/org/xbib/graphics/imageio/plugins/png/pngj/chunks/ChunkHelper.java create mode 100644 png/src/main/java/org/xbib/graphics/imageio/plugins/png/pngj/chunks/ChunkLoadBehaviour.java create mode 100644 png/src/main/java/org/xbib/graphics/imageio/plugins/png/pngj/chunks/ChunkPredicate.java create mode 100644 png/src/main/java/org/xbib/graphics/imageio/plugins/png/pngj/chunks/ChunkRaw.java create mode 100644 png/src/main/java/org/xbib/graphics/imageio/plugins/png/pngj/chunks/ChunksList.java create mode 100644 png/src/main/java/org/xbib/graphics/imageio/plugins/png/pngj/chunks/ChunksListForWrite.java create mode 100644 png/src/main/java/org/xbib/graphics/imageio/plugins/png/pngj/chunks/PngBadCharsetException.java create mode 100644 png/src/main/java/org/xbib/graphics/imageio/plugins/png/pngj/chunks/PngChunk.java create mode 100644 png/src/main/java/org/xbib/graphics/imageio/plugins/png/pngj/chunks/PngChunkACTL.java create mode 100644 png/src/main/java/org/xbib/graphics/imageio/plugins/png/pngj/chunks/PngChunkBKGD.java create mode 100644 png/src/main/java/org/xbib/graphics/imageio/plugins/png/pngj/chunks/PngChunkCHRM.java create mode 100644 png/src/main/java/org/xbib/graphics/imageio/plugins/png/pngj/chunks/PngChunkFCTL.java create mode 100644 png/src/main/java/org/xbib/graphics/imageio/plugins/png/pngj/chunks/PngChunkFDAT.java create mode 100644 png/src/main/java/org/xbib/graphics/imageio/plugins/png/pngj/chunks/PngChunkGAMA.java create mode 100644 png/src/main/java/org/xbib/graphics/imageio/plugins/png/pngj/chunks/PngChunkHIST.java create mode 100644 png/src/main/java/org/xbib/graphics/imageio/plugins/png/pngj/chunks/PngChunkICCP.java create mode 100644 png/src/main/java/org/xbib/graphics/imageio/plugins/png/pngj/chunks/PngChunkIDAT.java create mode 100644 png/src/main/java/org/xbib/graphics/imageio/plugins/png/pngj/chunks/PngChunkIEND.java create mode 100644 png/src/main/java/org/xbib/graphics/imageio/plugins/png/pngj/chunks/PngChunkIHDR.java create mode 100644 png/src/main/java/org/xbib/graphics/imageio/plugins/png/pngj/chunks/PngChunkITXT.java create mode 100644 png/src/main/java/org/xbib/graphics/imageio/plugins/png/pngj/chunks/PngChunkMultiple.java create mode 100644 png/src/main/java/org/xbib/graphics/imageio/plugins/png/pngj/chunks/PngChunkOFFS.java create mode 100644 png/src/main/java/org/xbib/graphics/imageio/plugins/png/pngj/chunks/PngChunkPHYS.java create mode 100644 png/src/main/java/org/xbib/graphics/imageio/plugins/png/pngj/chunks/PngChunkPLTE.java create mode 100644 png/src/main/java/org/xbib/graphics/imageio/plugins/png/pngj/chunks/PngChunkSBIT.java create mode 100644 png/src/main/java/org/xbib/graphics/imageio/plugins/png/pngj/chunks/PngChunkSPLT.java create mode 100644 png/src/main/java/org/xbib/graphics/imageio/plugins/png/pngj/chunks/PngChunkSRGB.java create mode 100644 png/src/main/java/org/xbib/graphics/imageio/plugins/png/pngj/chunks/PngChunkSTER.java create mode 100644 png/src/main/java/org/xbib/graphics/imageio/plugins/png/pngj/chunks/PngChunkSingle.java create mode 100644 png/src/main/java/org/xbib/graphics/imageio/plugins/png/pngj/chunks/PngChunkTEXT.java create mode 100644 png/src/main/java/org/xbib/graphics/imageio/plugins/png/pngj/chunks/PngChunkTIME.java create mode 100644 png/src/main/java/org/xbib/graphics/imageio/plugins/png/pngj/chunks/PngChunkTRNS.java create mode 100644 png/src/main/java/org/xbib/graphics/imageio/plugins/png/pngj/chunks/PngChunkTextVar.java create mode 100644 png/src/main/java/org/xbib/graphics/imageio/plugins/png/pngj/chunks/PngChunkUNKNOWN.java create mode 100644 png/src/main/java/org/xbib/graphics/imageio/plugins/png/pngj/chunks/PngChunkZTXT.java create mode 100644 png/src/main/java/org/xbib/graphics/imageio/plugins/png/pngj/chunks/PngMetadata.java create mode 100644 png/src/main/java/org/xbib/graphics/imageio/plugins/png/pngj/pixels/CompressorStream.java create mode 100644 png/src/main/java/org/xbib/graphics/imageio/plugins/png/pngj/pixels/CompressorStreamDeflater.java create mode 100644 png/src/main/java/org/xbib/graphics/imageio/plugins/png/pngj/pixels/CompressorStreamLz4.java create mode 100644 png/src/main/java/org/xbib/graphics/imageio/plugins/png/pngj/pixels/DeflaterEstimatorHjg.java create mode 100644 png/src/main/java/org/xbib/graphics/imageio/plugins/png/pngj/pixels/DeflaterEstimatorLz4.java create mode 100644 png/src/main/java/org/xbib/graphics/imageio/plugins/png/pngj/pixels/FiltersPerformance.java create mode 100644 png/src/main/java/org/xbib/graphics/imageio/plugins/png/pngj/pixels/PixelsWriter.java create mode 100644 png/src/main/java/org/xbib/graphics/imageio/plugins/png/pngj/pixels/PixelsWriterDefault.java create mode 100644 png/src/main/java/org/xbib/graphics/imageio/plugins/png/pngj/pixels/PixelsWriterMultiple.java create mode 100644 png/src/main/resources/META-INF/services/javax.imageio.spi.ImageWriterSpi create mode 100644 png/src/test/java/org/xbib/graphics/imageio/plugins/png/BufferedImageChildTest.java create mode 100644 png/src/test/java/org/xbib/graphics/imageio/plugins/png/BufferedImageTypesTest.java create mode 100644 png/src/test/java/org/xbib/graphics/imageio/plugins/png/CustomByteIndexImageTypesTest.java create mode 100644 png/src/test/java/org/xbib/graphics/imageio/plugins/png/CustomUShortImageTypesTest.java create mode 100644 png/src/test/java/org/xbib/graphics/imageio/plugins/png/ImageAssert.java create mode 100644 png/src/test/java/org/xbib/graphics/imageio/plugins/png/PNGWriterTest.java create mode 100644 png/src/test/java/org/xbib/graphics/imageio/plugins/png/PngSuiteImagesTest.java create mode 100644 png/src/test/java/org/xbib/graphics/imageio/plugins/png/SampleImagePainter.java create mode 100644 settings.gradle delete mode 100644 src/main/java/com/sun/imageio/plugins/png/PNGImageWriterBackport.java delete mode 100644 src/main/java/com/sun/imageio/plugins/png/PNGImageWriterSpiBackport.java delete mode 100644 src/main/resources/META-INF/services/javax.imageio.spi.ImageWriterSpi delete mode 100644 src/test/java/com/sun/imageio/plugins/png/PNGImageWriterBackportTest.java delete mode 100644 src/test/resources/com/sun/imageio/plugins/png/placeholder-text.gif diff --git a/.gitignore b/.gitignore index dc122d7..c2710d1 100644 --- a/.gitignore +++ b/.gitignore @@ -1,13 +1,9 @@ -/data -/work -/logs /.idea -/target .DS_Store /.settings /.classpath /.project /.gradle -/build +build *~ /*.iml \ No newline at end of file diff --git a/CREDITS.txt b/CREDITS.txt deleted file mode 100644 index 6e66a0c..0000000 --- a/CREDITS.txt +++ /dev/null @@ -1,5 +0,0 @@ -This is a Java 8 version of the project - -https://github.com/gredler/jdk9-png-writer-backport - -by Daniel Gredler \ No newline at end of file diff --git a/barcode/NOTICE.txt b/barcode/NOTICE.txt new file mode 100644 index 0000000..91af19a --- /dev/null +++ b/barcode/NOTICE.txt @@ -0,0 +1,7 @@ +This work is based on Okapi Barcode Library + +https://github.com/woo-j/OkapiBarcode + +(Apache License 2.0) + +as of 2016 \ No newline at end of file diff --git a/barcode/build.gradle b/barcode/build.gradle new file mode 100644 index 0000000..f125b34 --- /dev/null +++ b/barcode/build.gradle @@ -0,0 +1,7 @@ +dependencies { + testImplementation project(':io-vector') + testImplementation "org.junit.jupiter:junit-jupiter-params:5.7.0" + testImplementation "junit:junit:4.12" + testImplementation "com.google.zxing:javase:${project.property('zxing.version')}" + testImplementation "org.reflections:reflections:${project.property('reflections.version')}" +} diff --git a/barcode/src/main/java/module-info.java b/barcode/src/main/java/module-info.java new file mode 100644 index 0000000..1c65a9d --- /dev/null +++ b/barcode/src/main/java/module-info.java @@ -0,0 +1,6 @@ +module org.xbib.graphics.barcode { + exports org.xbib.graphics.barcode; + exports org.xbib.graphics.barcode.util; + exports org.xbib.graphics.barcode.render; + requires transitive java.desktop; +} diff --git a/barcode/src/main/java/org/xbib/graphics/barcode/AustraliaPost.java b/barcode/src/main/java/org/xbib/graphics/barcode/AustraliaPost.java new file mode 100755 index 0000000..8ee8234 --- /dev/null +++ b/barcode/src/main/java/org/xbib/graphics/barcode/AustraliaPost.java @@ -0,0 +1,363 @@ +package org.xbib.graphics.barcode; + +import org.xbib.graphics.barcode.util.ReedSolomon; +import java.awt.geom.Rectangle2D; + +/** + * Implements the Australia Post 4-State barcode. + */ +public class AustraliaPost extends Symbol { + + private static final char[] CHARACTER_SET = { + '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D', + 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', + 'W', 'X', 'Y', 'Z', 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', + 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z', ' ', '#' + }; + + private static final String[] N_ENCODING_TABLE = { + "00", "01", "02", "10", "11", "12", "20", "21", "22", "30" + }; + + private static final String[] C_ENCODING_TABLE = { + "222", "300", "301", "302", "310", "311", "312", "320", "321", "322", + "000", "001", "002", "010", "011", "012", "020", "021", "022", "100", "101", "102", "110", + "111", "112", "120", "121", "122", "200", "201", "202", "210", "211", "212", "220", "221", + "023", "030", "031", "032", "033", "103", "113", "123", "130", "131", "132", "133", "203", + "213", "223", "230", "231", "232", "233", "303", "313", "323", "330", "331", "332", "333", + "003", "013" + }; + + private static final String[] BAR_VALUE_TABLE = { + "000", "001", "002", "003", "010", "011", "012", "013", "020", "021", + "022", "023", "030", "031", "032", "033", "100", "101", "102", "103", "110", "111", "112", + "113", "120", "121", "122", "123", "130", "131", "132", "133", "200", "201", "202", "203", + "210", "211", "212", "213", "220", "221", "222", "223", "230", "231", "232", "233", "300", + "301", "302", "303", "310", "311", "312", "313", "320", "321", "322", "323", "330", "331", + "332", "333" + }; + private ausMode mode; + + ; + + public AustraliaPost() { + mode = ausMode.AUSPOST; + } + + /** + * Specify encoding of Australia Post Standard Customer Barcode, + * Customer Barcode 2 or Customer Barcode 3 (37-bar, 52-bar and 67-bar + * symbols) depending on input data length. Valid data characters are 0-9, + * A-Z, a-z, space and hash (#). A Format Control Code (FCC) is added and + * should not be included in the input data. + *

+ * Input data should include a 8-digit Deliver Point ID + * (DPID) optionally followed by customer information as shown below. + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + *
Permitted Australia Post input data

Input Length

Required Input Format

Symbol Length

FCC

Encoding Table

8

99999999

37-bar

11

None

13

99999999AAAAA

52-bar

59

C

16

9999999999999999

52-bar

59

N

18

99999999AAAAAAAAAA

67-bar

62

C

23

99999999999999999999999

67-bar

62

N

+ */ + public void setPostMode() { + mode = ausMode.AUSPOST; + } + + /** + * Specify encoding of a Reply Paid version of the Australia Post + * 4-State Barcode (FCC 45) which requires an 8-digit DPID input. + */ + public void setReplyMode() { + mode = ausMode.AUSREPLY; + } + + /** + * Specify encoding of a Routing version of the Australia Post 4-State + * Barcode (FCC 87) which requires an 8-digit DPID input. + */ + public void setRouteMode() { + mode = ausMode.AUSROUTE; + } + + /** + * Specify encoding of a Redirection version of the Australia Post 4-State + * Barcode (FCC 92) which requires an 8-digit DPID input. + */ + public void setRedirectMode() { + mode = ausMode.AUSREDIRECT; + } + + @Override + public boolean encode() { + String formatControlCode = "00"; + String deliveryPointId; + StringBuilder barStateValues; + StringBuilder zeroPaddedInput = new StringBuilder(); + int i; + + switch (mode) { + case AUSPOST: + switch (content.length()) { + case 8: + formatControlCode = "11"; + break; + case 13: + formatControlCode = "59"; + break; + case 16: + formatControlCode = "59"; + if (!(content.matches("[0-9]+"))) { + errorMsg.append("Invalid characters in data"); + return false; + } + break; + case 18: + formatControlCode = "62"; + break; + case 23: + formatControlCode = "62"; + if (!(content.matches("[0-9]+"))) { + errorMsg.append("Invalid characters in data"); + return false; + } + break; + default: + errorMsg.append("Auspost input is wrong length"); + return false; + } + break; + case AUSREPLY: + if (content.length() > 8) { + errorMsg.append("Auspost input is too long"); + return false; + } else { + formatControlCode = "45"; + } + break; + case AUSROUTE: + if (content.length() > 8) { + errorMsg.append("Auspost input is too long"); + return false; + } else { + formatControlCode = "87"; + } + break; + case AUSREDIRECT: + if (content.length() > 8) { + errorMsg.append("Auspost input is too long"); + return false; + } else { + formatControlCode = "92"; + } + break; + } + + encodeInfo.append("FCC: ").append(formatControlCode).append('\n'); + + if (mode != ausMode.AUSPOST) { + for (i = content.length(); i < 8; i++) { + zeroPaddedInput.append("0"); + } + } + zeroPaddedInput.append(content); + + if (!(content.matches("[0-9A-Za-z #]+"))) { + errorMsg.append("Invalid characters in data"); + return false; + } + + /* Verify that the first 8 characters are numbers */ + deliveryPointId = zeroPaddedInput.substring(0, 8); + + if (!(deliveryPointId.matches("[0-9]+"))) { + errorMsg.append("Invalid characters in DPID"); + return false; + } + + encodeInfo.append("DPID: ").append(deliveryPointId).append('\n'); + + /* Start */ + barStateValues = new StringBuilder("13"); + + /* Encode the FCC */ + for (i = 0; i < 2; i++) { + barStateValues.append(N_ENCODING_TABLE[formatControlCode.charAt(i) - '0']); + } + + /* Delivery Point Identifier (DPID) */ + for (i = 0; i < 8; i++) { + barStateValues.append(N_ENCODING_TABLE[deliveryPointId.charAt(i) - '0']); + } + + /* Customer Information */ + switch (zeroPaddedInput.length()) { + case 13: + case 18: + for (i = 8; i < zeroPaddedInput.length(); i++) { + barStateValues.append(C_ENCODING_TABLE[positionOf(zeroPaddedInput.charAt(i), CHARACTER_SET)]); + } + break; + case 16: + case 23: + for (i = 8; i < zeroPaddedInput.length(); i++) { + barStateValues.append(N_ENCODING_TABLE[positionOf(zeroPaddedInput.charAt(i), CHARACTER_SET)]); + } + break; + } + + /* Filler bar */ + switch (barStateValues.length()) { + case 22: + case 37: + case 52: + barStateValues.append("3"); + break; + } + + /* Reed Solomon error correction */ + barStateValues.append(calcReedSolomon(barStateValues.toString())); + + /* Stop character */ + barStateValues.append("13"); + + encodeInfo.append("Total length: ").append(barStateValues.length()).append('\n'); + encodeInfo.append("Encoding: "); + for (i = 0; i < barStateValues.length(); i++) { + switch (barStateValues.charAt(i)) { + case '1': + encodeInfo.append("A"); + break; + case '2': + encodeInfo.append("D"); + break; + case '0': + encodeInfo.append("F"); + break; + case '3': + encodeInfo.append("T"); + break; + } + } + encodeInfo.append("\n"); + + readable = new StringBuilder(); + pattern = new String[1]; + pattern[0] = barStateValues.toString(); + rowCount = 1; + rowHeight = new int[1]; + rowHeight[0] = -1; + plotSymbol(); + return true; + } + + private String calcReedSolomon(String oldBarStateValues) { + ReedSolomon rs = new ReedSolomon(); + StringBuilder newBarStateValues = new StringBuilder(); + + /* Adds Reed-Solomon error correction to auspost */ + + int barStateCount; + int tripleValueCount = 0; + int[] tripleValue = new int[31]; + + for (barStateCount = 2; barStateCount < oldBarStateValues.length(); barStateCount += 3, tripleValueCount++) { + tripleValue[tripleValueCount] = barStateToDecimal(oldBarStateValues.charAt(barStateCount), 4) + + barStateToDecimal(oldBarStateValues.charAt(barStateCount + 1), 2) + + barStateToDecimal(oldBarStateValues.charAt(barStateCount + 2), 0); + } + + rs.init_gf(0x43); + rs.init_code(4, 1); + rs.encode(tripleValueCount, tripleValue); + + for (barStateCount = 4; barStateCount > 0; barStateCount--) { + newBarStateValues.append(BAR_VALUE_TABLE[rs.getResult(barStateCount - 1)]); + } + + return newBarStateValues.toString(); + } + + private int barStateToDecimal(char oldBarStateValues, int shift) { + return (oldBarStateValues - '0') << shift; + } + + @Override + protected void plotSymbol() { + int xBlock; + int x, y, w, h; + getRectangles().clear(); + x = 0; + w = 1; + y = 0; + h = 0; + for (xBlock = 0; xBlock < pattern[0].length(); xBlock++) { + switch (pattern[0].charAt(xBlock)) { + case '1': + y = 0; + h = 5; + break; + case '2': + y = 3; + h = 5; + break; + case '0': + y = 0; + h = 8; + break; + case '3': + y = 3; + h = 2; + break; + } + Rectangle2D.Double rect = new Rectangle2D.Double(x, y, w, h); + getRectangles().add(rect); + x += 2; + } + symbolWidth = pattern[0].length() * 3; + symbolHeight = 8; + } + + private enum ausMode {AUSPOST, AUSREPLY, AUSROUTE, AUSREDIRECT} +} diff --git a/barcode/src/main/java/org/xbib/graphics/barcode/AztecCode.java b/barcode/src/main/java/org/xbib/graphics/barcode/AztecCode.java new file mode 100755 index 0000000..bb81d3d --- /dev/null +++ b/barcode/src/main/java/org/xbib/graphics/barcode/AztecCode.java @@ -0,0 +1,1989 @@ +package org.xbib.graphics.barcode; + +import org.xbib.graphics.barcode.util.ReedSolomon; + +/** + * Implements Aztec Code bar code symbology according to ISO/IEC 24778:2008. + * Aztec Code can encode 8-bit ISO 8859-1 (Latin-1) data (except 0x00 Null + * characters) up to a maximum length of approximately 3800 numeric characters, + * 3000 alphabetic characters or 1900 bytes of data in a two-dimensional matrix + * symbol. + */ +public class AztecCode extends Symbol { + + private static final int[] COMPACT_AZTEC_MAP = { //27 x 27 data grid + 609, 608, 411, 413, 415, 417, 419, 421, 423, 425, 427, 429, 431, 433, + 435, 437, 439, 441, 443, 445, 447, 449, 451, 453, 455, 457, 459, + 607, 606, 410, 412, 414, 416, 418, 420, 422, 424, 426, 428, 430, 432, + 434, 436, 438, 440, 442, 444, 446, 448, 450, 452, 454, 456, 458, + 605, 604, 409, 408, 243, 245, 247, 249, 251, 253, 255, 257, 259, 261, + 263, 265, 267, 269, 271, 273, 275, 277, 279, 281, 283, 460, 461, + 603, 602, 407, 406, 242, 244, 246, 248, 250, 252, 254, 256, 258, 260, + 262, 264, 266, 268, 270, 272, 274, 276, 278, 280, 282, 462, 463, + 601, 600, 405, 404, 241, 240, 107, 109, 111, 113, 115, 117, 119, 121, + 123, 125, 127, 129, 131, 133, 135, 137, 139, 284, 285, 464, 465, + 599, 598, 403, 402, 239, 238, 106, 108, 110, 112, 114, 116, 118, 120, + 122, 124, 126, 128, 130, 132, 134, 136, 138, 286, 287, 466, 467, + 597, 596, 401, 400, 237, 236, 105, 104, 3, 5, 7, 9, 11, 13, 15, 17, 19, + 21, 23, 25, 27, 140, 141, 288, 289, 468, 469, + 595, 594, 399, 398, 235, 234, 103, 102, 2, 4, 6, 8, 10, 12, 14, 16, 18, + 20, 22, 24, 26, 142, 143, 290, 291, 470, 471, + 593, 592, 397, 396, 233, 232, 101, 100, 1, 1, 2000, 2001, 2002, 2003, + 2004, 2005, 2006, 0, 1, 28, 29, 144, 145, 292, 293, 472, 473, + 591, 590, 395, 394, 231, 230, 99, 98, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 30, 31, 146, 147, 294, 295, 474, 475, + 589, 588, 393, 392, 229, 228, 97, 96, 2027, 1, 0, 0, 0, 0, 0, 0, 0, 1, + 2007, 32, 33, 148, 149, 296, 297, 476, 477, + 587, 586, 391, 390, 227, 226, 95, 94, 2026, 1, 0, 1, 1, 1, 1, 1, 0, 1, + 2008, 34, 35, 150, 151, 298, 299, 478, 479, + 585, 584, 389, 388, 225, 224, 93, 92, 2025, 1, 0, 1, 0, 0, 0, 1, 0, 1, + 2009, 36, 37, 152, 153, 300, 301, 480, 481, + 583, 582, 387, 386, 223, 222, 91, 90, 2024, 1, 0, 1, 0, 1, 0, 1, 0, 1, + 2010, 38, 39, 154, 155, 302, 303, 482, 483, + 581, 580, 385, 384, 221, 220, 89, 88, 2023, 1, 0, 1, 0, 0, 0, 1, 0, 1, + 2011, 40, 41, 156, 157, 304, 305, 484, 485, + 579, 578, 383, 382, 219, 218, 87, 86, 2022, 1, 0, 1, 1, 1, 1, 1, 0, 1, + 2012, 42, 43, 158, 159, 306, 307, 486, 487, + 577, 576, 381, 380, 217, 216, 85, 84, 2021, 1, 0, 0, 0, 0, 0, 0, 0, 1, + 2013, 44, 45, 160, 161, 308, 309, 488, 489, + 575, 574, 379, 378, 215, 214, 83, 82, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 46, 47, 162, 163, 310, 311, 490, 491, + 573, 572, 377, 376, 213, 212, 81, 80, 0, 0, 2020, 2019, 2018, 2017, 2016, + 2015, 2014, 0, 0, 48, 49, 164, 165, 312, 313, 492, 493, + 571, 570, 375, 374, 211, 210, 78, 76, 74, 72, 70, 68, 66, 64, 62, 60, 58, + 56, 54, 50, 51, 166, 167, 314, 315, 494, 495, + 569, 568, 373, 372, 209, 208, 79, 77, 75, 73, 71, 69, 67, 65, 63, 61, 59, + 57, 55, 52, 53, 168, 169, 316, 317, 496, 497, + 567, 566, 371, 370, 206, 204, 202, 200, 198, 196, 194, 192, 190, 188, 186, + 184, 182, 180, 178, 176, 174, 170, 171, 318, 319, 498, 499, + 565, 564, 369, 368, 207, 205, 203, 201, 199, 197, 195, 193, 191, 189, 187, + 185, 183, 181, 179, 177, 175, 172, 173, 320, 321, 500, 501, + 563, 562, 366, 364, 362, 360, 358, 356, 354, 352, 350, 348, 346, 344, 342, + 340, 338, 336, 334, 332, 330, 328, 326, 322, 323, 502, 503, + 561, 560, 367, 365, 363, 361, 359, 357, 355, 353, 351, 349, 347, 345, 343, + 341, 339, 337, 335, 333, 331, 329, 327, 324, 325, 504, 505, + 558, 556, 554, 552, 550, 548, 546, 544, 542, 540, 538, 536, 534, 532, 530, + 528, 526, 524, 522, 520, 518, 516, 514, 512, 510, 506, 507, + 559, 557, 555, 553, 551, 549, 547, 545, 543, 541, 539, 537, 535, 533, 531, + 529, 527, 525, 523, 521, 519, 517, 515, 513, 511, 508, 509 + }; + private static final int[][] AZTEC_MAP = new int[151][151]; + private static final int[] AztecCodeSet = { /* From Table 2 */ + 32, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 12, 32, 32, 32, 32, 32, 32, + 32, 32, 32, 32, 32, 32, 32, 4, 4, 4, 4, 4, 23, 8, 8, 8, 8, 8, 8, 8, + 8, 8, 8, 8, 24, 8, 24, 8, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 8, 8, + 8, 8, 8, 8, 4, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 8, 4, 8, 4, 4, 4, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 8, 4, 8, 4, 4 + }; + private static final int[] AztecSymbolChar = { /* From Table 2 */ + 0, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 300, 14, 15, 16, 17, 18, 19, + 20, 21, 22, 23, 24, 25, 26, 15, 16, 17, 18, 19, 1, 6, 7, 8, 9, 10, 11, 12, + 13, 14, 15, 16, 301, 18, 302, 20, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 21, 22, + 23, 24, 25, 26, 20, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, + 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 27, 21, 28, 22, 23, 24, 2, 3, 4, + 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, + 25, 26, 27, 29, 25, 30, 26, 27 + }; + /* Problem characters are: + 300: Carriage Return (ASCII 13) + 301: Comma (ASCII 44) + 302: Full Stop (ASCII 46) + */ + private static final String[] pentbit = { + "00000", "00001", "00010", "00011", "00100", "00101", "00110", "00111", "01000", "01001", + "01010", "01011", "01100", "01101", "01110", "01111", "10000", "10001", "10010", "10011", "10100", "10101", + "10110", "10111", "11000", "11001", "11010", "11011", "11100", "11101", "11110", "11111" + }; + private static final String[] quadbit = { + "0000", "0001", "0010", "0011", "0100", "0101", "0110", "0111", "1000", "1001", + "1010", "1011", "1100", "1101", "1110", "1111" + }; + private static final String[] tribit = { + "000", "001", "010", "011", "100", "101", "110", "111" + }; + private static final int[] AztecSizes = { /* Codewords per symbol */ + 21, 48, 60, 88, 120, 156, 196, 240, 230, 272, 316, 364, 416, 470, 528, 588, 652, 720, 790, + 864, 940, 1020, 920, 992, 1066, 1144, 1224, 1306, 1392, 1480, 1570, 1664 + }; + private static final int[] AztecCompactSizes = { + 17, 40, 51, 76 + }; + private static final int[] Aztec10DataSizes = { /* Data bits per symbol maximum with 10% error correction */ + 96, 246, 408, 616, 840, 1104, 1392, 1704, 2040, 2420, 2820, 3250, 3720, 4200, 4730, + 5270, 5840, 6450, 7080, 7750, 8430, 9150, 9900, 10680, 11484, 12324, 13188, 14076, + 15000, 15948, 16920, 17940 + }; + private static final int[] Aztec23DataSizes = { /* Data bits per symbol maximum with 23% error correction */ + 84, 204, 352, 520, 720, 944, 1184, 1456, 1750, 2070, 2410, 2780, 3180, 3590, 4040, + 4500, 5000, 5520, 6060, 6630, 7210, 7830, 8472, 9132, 9816, 10536, 11280, 12036, + 12828, 13644, 14472, 15348 + }; + private static final int[] Aztec36DataSizes = { /* Data bits per symbol maximum with 36% error correction */ + 66, 168, 288, 432, 592, 776, 984, 1208, 1450, 1720, 2000, 2300, 2640, 2980, 3350, + 3740, 4150, 4580, 5030, 5500, 5990, 6500, 7032, 7584, 8160, 8760, 9372, 9996, 10656, + 11340, 12024, 12744 + }; + private static final int[] Aztec50DataSizes = { /* Data bits per symbol maximum with 50% error correction */ + 48, 126, 216, 328, 456, 600, 760, 936, 1120, 1330, 1550, 1790, 2050, 2320, 2610, + 2910, 3230, 3570, 3920, 4290, 4670, 5070, 5484, 5916, 6360, 6828, 7308, 7800, 8316, + 8844, 9384, 9948 + }; + private static final int[] AztecCompact10DataSizes = { + 78, 198, 336, 520 + }; + private static final int[] AztecCompact23DataSizes = { + 66, 168, 288, 440 + }; + private static final int[] AztecCompact36DataSizes = { + 48, 138, 232, 360 + }; + private static final int[] AztecCompact50DataSizes = { + 36, 102, 176, 280 + }; + private static final int[] AztecOffset = { + 66, 64, 62, 60, 57, 55, 53, 51, 49, 47, 45, 42, 40, 38, 36, 34, 32, 30, 28, 25, 23, 21, + 19, 17, 15, 13, 10, 8, 6, 4, 2, 0 + }; + private static final int[] AztecCompactOffset = { + 6, 4, 2, 0 + }; + private StringBuilder binaryString; + private int preferredSize = 0; + private int preferredEccLevel = -1; + + /** + * + */ + public AztecCode() { + int layer, start, length, n, i; + int x, y; + + for (x = 0; x < 151; x++) { + for (y = 0; y < 151; y++) { + AZTEC_MAP[x][y] = 0; + } + } + + for (layer = 1; layer < 33; layer++) { + start = (112 * (layer - 1)) + (16 * (layer - 1) * (layer - 1)) + 2; + length = 28 + ((layer - 1) * 4) + (layer * 4); + /* Top */ + i = 0; + x = 64 - ((layer - 1) * 2); + y = 63 - ((layer - 1) * 2); + for (n = start; n < (start + length); n += 2) { + AZTEC_MAP[avoidReferenceGrid(x + i)][avoidReferenceGrid(y)] = n; + AZTEC_MAP[avoidReferenceGrid(x + i)][avoidReferenceGrid(y - 1)] = n + 1; + i++; + } + /* Right */ + i = 0; + x = 78 + ((layer - 1) * 2); + y = 64 - ((layer - 1) * 2); + for (n = start + length; n < (start + (length * 2)); n += 2) { + AZTEC_MAP[avoidReferenceGrid(x)][avoidReferenceGrid(y + i)] = n; + AZTEC_MAP[avoidReferenceGrid(x + 1)][avoidReferenceGrid(y + i)] = n + 1; + i++; + } + /* Bottom */ + i = 0; + x = 77 + ((layer - 1) * 2); + y = 78 + ((layer - 1) * 2); + for (n = start + (length * 2); n < (start + (length * 3)); n += 2) { + AZTEC_MAP[avoidReferenceGrid(x - i)][avoidReferenceGrid(y)] = n; + AZTEC_MAP[avoidReferenceGrid(x - i)][avoidReferenceGrid(y + 1)] = n + 1; + i++; + } + /* Left */ + i = 0; + x = 63 - ((layer - 1) * 2); + y = 77 + ((layer - 1) * 2); + for (n = start + (length * 3); n < (start + (length * 4)); n += 2) { + AZTEC_MAP[avoidReferenceGrid(x)][avoidReferenceGrid(y - i)] = n; + AZTEC_MAP[avoidReferenceGrid(x - 1)][avoidReferenceGrid(y - i)] = n + 1; + i++; + } + } + + /* Central finder pattern */ + for (y = 69; y <= 81; y++) { + for (x = 69; x <= 81; x++) { + AZTEC_MAP[x][y] = 1; + } + } + for (y = 70; y <= 80; y++) { + for (x = 70; x <= 80; x++) { + AZTEC_MAP[x][y] = 0; + } + } + for (y = 71; y <= 79; y++) { + for (x = 71; x <= 79; x++) { + AZTEC_MAP[x][y] = 1; + } + } + for (y = 72; y <= 78; y++) { + for (x = 72; x <= 78; x++) { + AZTEC_MAP[x][y] = 0; + } + } + for (y = 73; y <= 77; y++) { + for (x = 73; x <= 77; x++) { + AZTEC_MAP[x][y] = 1; + } + } + for (y = 74; y <= 76; y++) { + for (x = 74; x <= 76; x++) { + AZTEC_MAP[x][y] = 0; + } + } + + /* Guide bars */ + for (y = 11; y < 151; y += 16) { + for (x = 1; x < 151; x += 2) { + AZTEC_MAP[x][y] = 1; + AZTEC_MAP[y][x] = 1; + } + } + + /* Descriptor */ + for (i = 0; i < 10; i++) { /* Top */ + + AZTEC_MAP[avoidReferenceGrid(66 + i)][avoidReferenceGrid(64)] = 20000 + i; + } + for (i = 0; i < 10; i++) { /* Right */ + + AZTEC_MAP[avoidReferenceGrid(77)][avoidReferenceGrid(66 + i)] = 20010 + i; + } + for (i = 0; i < 10; i++) { /* Bottom */ + + AZTEC_MAP[avoidReferenceGrid(75 - i)][avoidReferenceGrid(77)] = 20020 + i; + } + for (i = 0; i < 10; i++) { /* Left */ + + AZTEC_MAP[avoidReferenceGrid(64)][avoidReferenceGrid(75 - i)] = 20030 + i; + } + + /* Orientation */ + AZTEC_MAP[avoidReferenceGrid(64)][avoidReferenceGrid(64)] = 1; + AZTEC_MAP[avoidReferenceGrid(65)][avoidReferenceGrid(64)] = 1; + AZTEC_MAP[avoidReferenceGrid(64)][avoidReferenceGrid(65)] = 1; + AZTEC_MAP[avoidReferenceGrid(77)][avoidReferenceGrid(64)] = 1; + AZTEC_MAP[avoidReferenceGrid(77)][avoidReferenceGrid(65)] = 1; + AZTEC_MAP[avoidReferenceGrid(77)][avoidReferenceGrid(76)] = 1; + } + + /** + * Sets a preferred symbol size. This value may be ignored if data string is + * too large to fit in the specified symbol size. Values correspond to + * symbol sizes as shown in the following table: + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + *
Available Aztec Code symbol sizes

+ * Input

+ * Symbol Size

+ * Input

+ * Symbol Size

+ * 1

+ * 15 x 15*

+ * 19

+ * 79 x 79

+ * 2

+ * 19 x 19*

+ * 20

+ * 83 x 83

+ * 3

+ * 23 x 23*

+ * 21

+ * 87 x 87

+ * 4

+ * 27 x 27*

+ * 22

+ * 91 x 91

+ * 5

+ * 19 x 19

+ * 23

+ * 95 x 95

+ * 6

+ * 23 x 23

+ * 24

+ * 101 x 101

+ * 7

+ * 27 x 27

+ * 25

+ * 105 x 105

+ * 8

+ * 31 x 31

+ * 26

+ * 109 x 109

+ * 9

+ * 37 x 37

+ * 27

+ * 113 x 113

+ * 10

+ * 41 x 41

+ * 28

+ * 117 x 117

+ * 11

+ * 45 x 45

+ * 29

+ * 121 x 121

+ * 12

+ * 49 x 49

+ * 30

+ * 125 x 125

+ * 13

+ * 53 x 53

+ * 31

+ * 131 x 131

+ * 14

+ * 57 x 57

+ * 32

+ * 135 x 135

+ * 15

+ * 61 x 61

+ * 33

+ * 139 x 139

+ * 16

+ * 67 x 67

+ * 34

+ * 143 x 143

+ * 17

+ * 71 x 71

+ * 35

+ * 147 x 147

+ * 18

+ * 75 x 75

+ * 36

+ * 151 x 151

+ * + * @param size An integer in the range 1 - 36 + */ + public void setPreferredSize(int size) { + preferredSize = size; + } + + /** + * Sets the preferred minimum amount of symbol space dedicated to error + * correction. This value will be ignored if a symbol size has been set by + * setPreferredSize Valid options are: + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + *
Error correction options

+ * Mode

+ * Error Correction Capacity

+ * 1

+ * >10% + 3 codewords

+ * 2

+ * >23% + 3 codewords

+ * 3

+ * >36% + 3 codewords

+ * 4

+ * >50% + 3 codewords

+ * + * @param eccLevel An integer in the range 1 - 4 + */ + public void setPreferredEccLevel(int eccLevel) { + preferredEccLevel = eccLevel; + } + + private int avoidReferenceGrid(int input) { + int output; + + output = input; + if (output > 10) { + output++; + } + if (output > 26) { + output++; + } + if (output > 42) { + output++; + } + if (output > 58) { + output++; + } + if (output > 74) { + output++; + } + if (output > 90) { + output++; + } + if (output > 106) { + output++; + } + if (output > 122) { + output++; + } + if (output > 138) { + output++; + } + + return output; + } + + @Override + public boolean encode() { + int i, ecc_level, data_length, layers, data_maxsize; + int adjustment_size, codeword_size; + int j, count, adjusted_length, padbits, remainder; + StringBuilder adjusted_string = new StringBuilder(); + StringBuilder bit_pattern = new StringBuilder(); + int comp_loop = 4; + int data_blocks, ecc_blocks, total_bits; + boolean compact; + ReedSolomon rs = new ReedSolomon(); + ReedSolomon rs2 = new ReedSolomon(); + StringBuilder descriptor = new StringBuilder(); + int[] desc_data = new int[4]; + int[] desc_ecc = new int[6]; + int y, x, weight; + StringBuilder bin; + int t; + boolean done; + + if (readerInit) { + comp_loop = 1; + } + + eciProcess(); // Get ECI mode + + if ((inputDataType == DataType.GS1) && (readerInit)) { + errorMsg.append("Cannot encode in GS1 and Reader Initialisation mode at the same time"); + return false; + } + + if (!generateAztecBinary()) { + errorMsg.append("Input too long or too many extended ASCII characters"); + return false; + } + + // Set the error correction level + if ((preferredEccLevel <= 0) || (preferredEccLevel > 4)) { + ecc_level = 2; + } else { + ecc_level = preferredEccLevel; + } + + data_length = binaryString.length(); + + layers = 0; /* Keep compiler happy! */ + + data_maxsize = 0; /* Keep compiler happy! */ + + adjustment_size = 0; + + if ((preferredSize < 0) || (preferredSize > 36)) { + preferredSize = 0; + } + + if (preferredSize == 0) { /* The size of the symbol can be determined by Okapi */ + + do { + /* Decide what size symbol to use - the smallest that fits the data */ + compact = false; /* 1 = Aztec Compact, 0 = Normal Aztec */ + + layers = 0; + + switch (ecc_level) { + /* For each level of error correction work out the smallest symbol which + the data will fit in */ + case 1: + for (i = 32; i > 0; i--) { + if ((data_length + adjustment_size) < Aztec10DataSizes[i - 1]) { + layers = i; + compact = false; + data_maxsize = Aztec10DataSizes[i - 1]; + } + } + for (i = comp_loop; i > 0; i--) { + if ((data_length + adjustment_size) < AztecCompact10DataSizes[i - 1]) { + layers = i; + compact = true; + data_maxsize = AztecCompact10DataSizes[i - 1]; + } + } + break; + case 2: + for (i = 32; i > 0; i--) { + if ((data_length + adjustment_size) < Aztec23DataSizes[i - 1]) { + layers = i; + compact = false; + data_maxsize = Aztec23DataSizes[i - 1]; + } + } + for (i = comp_loop; i > 0; i--) { + if ((data_length + adjustment_size) < AztecCompact23DataSizes[i - 1]) { + layers = i; + compact = true; + data_maxsize = AztecCompact23DataSizes[i - 1]; + } + } + break; + case 3: + for (i = 32; i > 0; i--) { + if ((data_length + adjustment_size) < Aztec36DataSizes[i - 1]) { + layers = i; + compact = false; + data_maxsize = Aztec36DataSizes[i - 1]; + } + } + for (i = comp_loop; i > 0; i--) { + if ((data_length + adjustment_size) < AztecCompact36DataSizes[i - 1]) { + layers = i; + compact = true; + data_maxsize = AztecCompact36DataSizes[i - 1]; + } + } + break; + case 4: + for (i = 32; i > 0; i--) { + if ((data_length + adjustment_size) < Aztec50DataSizes[i - 1]) { + layers = i; + compact = false; + data_maxsize = Aztec50DataSizes[i - 1]; + } + } + for (i = comp_loop; i > 0; i--) { + if ((data_length + adjustment_size) < AztecCompact50DataSizes[i - 1]) { + layers = i; + compact = true; + data_maxsize = AztecCompact50DataSizes[i - 1]; + } + } + break; + } + + if (layers == 0) { /* Couldn't find a symbol which fits the data */ + + errorMsg.append("Input too long (too many bits for selected ECC)"); + return false; + } + + /* Determine codeword bitlength - Table 3 */ + codeword_size = 6; /* if (layers <= 2) */ + + if ((layers >= 3) && (layers <= 8)) { + codeword_size = 8; + } + if ((layers >= 9) && (layers <= 22)) { + codeword_size = 10; + } + if (layers >= 23) { + codeword_size = 12; + } + + j = 0; + i = 0; + + do { + if ((j + 1) % codeword_size == 0) { + /* Last bit of codeword */ + done = false; + count = 0; + + /* Discover how many '1's in current codeword */ + for (t = 0; t < (codeword_size - 1); t++) { + if (binaryString.charAt((i - (codeword_size - 1)) + t) == '1') { + count++; + } + } + + if (count == (codeword_size - 1)) { + adjusted_string.append('0'); + j++; + done = true; + } + + if (count == 0) { + adjusted_string.append('1'); + j++; + done = true; + } + + if (!done) { + adjusted_string.append(binaryString.charAt(i)); + j++; + i++; + } + } else { + adjusted_string.append(binaryString.charAt(i)); + j++; + i++; + } + } while (i < data_length); + + adjusted_length = adjusted_string.length(); + adjustment_size = adjusted_length - data_length; + + /* Add padding */ + remainder = adjusted_length % codeword_size; + + padbits = codeword_size - remainder; + if (padbits == codeword_size) { + padbits = 0; + } + + for (i = 0; i < padbits; i++) { + adjusted_string.append("1"); + } + adjusted_length = adjusted_string.length(); + + count = 0; + for (i = (adjusted_length - codeword_size); i < adjusted_length; i++) { + if (adjusted_string.charAt(i) == '1') { + count++; + } + } + if (count == codeword_size) { + adjusted_string = new StringBuilder(adjusted_string.substring(0, adjusted_length - 1) + '0'); + } + + encodeInfo.append("Codewords: "); + for (i = 0; i < (adjusted_length / codeword_size); i++) { + int l = 0, m = (1 << (codeword_size - 1)); + for (j = 0; j < codeword_size; j++) { + if (adjusted_string.charAt((i * codeword_size) + j) == '1') { + l += m; + } + m = m >> 1; + } + encodeInfo.append(Integer.toString(l)).append(" "); + } + encodeInfo.append("\n"); + + } while (adjusted_length > data_maxsize); + /* This loop will only repeat on the rare occasions when the rule about not having all 1s or all 0s + means that the binary string has had to be lengthened beyond the maximum number of bits that can + be encoded in a symbol of the selected size */ + } else { + /* The size of the symbol has been specified by the user */ + compact = false; + if ((readerInit) && ((preferredSize >= 2) && (preferredSize <= 4))) { + preferredSize = 5; + } + if ((preferredSize >= 1) && (preferredSize <= 4)) { + compact = true; + layers = preferredSize; + } + if ((preferredSize >= 5) && (preferredSize <= 36)) { + layers = preferredSize - 4; + } + + /* Determine codeword bitlength - Table 3 */ + codeword_size = 6; + if ((layers >= 3) && (layers <= 8)) { + codeword_size = 8; + } + if ((layers >= 9) && (layers <= 22)) { + codeword_size = 10; + } + if (layers >= 23) { + codeword_size = 12; + } + j = 0; + i = 0; + do { + if (((j + 1) % codeword_size) == 0) { + /* Last bit of codeword */ + done = false; + count = 0; + + /* Discover how many '1's in current codeword */ + for (t = 0; t < (codeword_size - 1); t++) { + if (binaryString.charAt((i - (codeword_size - 1)) + t) == '1') { + count++; + } + } + + if (count == (codeword_size - 1)) { + adjusted_string.append('0'); + j++; + done = true; + } + + if (count == 0) { + adjusted_string.append('1'); + j++; + done = true; + } + + if (!done) { + adjusted_string.append(binaryString.charAt(i)); + j++; + i++; + } + } else { + adjusted_string.append(binaryString.charAt(i)); + j++; + i++; + } + } while (i < binaryString.length()); + + adjusted_length = adjusted_string.length(); + remainder = adjusted_length % codeword_size; + padbits = codeword_size - remainder; + + if (padbits == codeword_size) { + padbits = 0; + } + for (i = 0; i < padbits; i++) { + adjusted_string.append("1"); + } + + adjusted_length = adjusted_string.length(); + count = 0; + + for (i = (adjusted_length - codeword_size); i < adjusted_length; i++) { + if (adjusted_string.charAt(i) == '1') { + count++; + } + } + + if (count == codeword_size) { + adjusted_string = new StringBuilder(adjusted_string.substring(0, adjusted_length - 1) + '0'); + } + + /* Check if the data actually fits into the selected symbol size */ + if (compact) { + data_maxsize = codeword_size * (AztecCompactSizes[layers - 1] - 3); + } else { + data_maxsize = codeword_size * (AztecSizes[layers - 1] - 3); + } + + if (adjusted_length > data_maxsize) { + errorMsg.append("Data too long for specified Aztec Code symbol size"); + return false; + } + + encodeInfo.append("Codewords: "); + for (i = 0; i < (adjusted_length / codeword_size); i++) { + int l = 0, m = (1 << (codeword_size - 1)); + for (j = 0; j < codeword_size; j++) { + if (adjusted_string.charAt((i * codeword_size) + j) == '1') { + l += m; + } + m = m >> 1; + } + encodeInfo.append(Integer.toString(l)).append(" "); + } + encodeInfo.append("\n"); + } + + if (readerInit && (layers > 22)) { + errorMsg.append("Data too long for reader initialisation symbol"); + return false; + } + + data_blocks = adjusted_length / codeword_size; + + if (compact) { + ecc_blocks = AztecCompactSizes[layers - 1] - data_blocks; + } else { + ecc_blocks = AztecSizes[layers - 1] - data_blocks; + } + + encodeInfo.append("Compact Mode: "); + if (compact) { + encodeInfo.append("TRUE\n"); + } else { + encodeInfo.append("FALSE\n"); + } + encodeInfo.append("Layers: ").append(layers).append('\n'); + encodeInfo.append("Codeword Length: ").append(codeword_size).append(" bits\n"); + encodeInfo.append("Data Codewords: ").append(data_blocks).append('\n'); + encodeInfo.append("ECC Codewords: ").append(ecc_blocks).append('\n'); + + int[] data_part = new int[data_blocks + 3]; + int[] ecc_part = new int[ecc_blocks + 3]; + + /* Split into codewords and calculate reed-colomon error correction codes */ + switch (codeword_size) { + case 6: + for (i = 0; i < data_blocks; i++) { + for (weight = 0; weight < 6; weight++) { + if (adjusted_string.charAt((i * codeword_size) + weight) == '1') { + data_part[i] += (32 >> weight); + } + } + } + rs.init_gf(0x43); + rs.init_code(ecc_blocks, 1); + rs.encode(data_blocks, data_part); + for (i = 0; i < ecc_blocks; i++) { + ecc_part[i] = rs.getResult(i); + } + for (i = (ecc_blocks - 1); i >= 0; i--) { + for (weight = 0x20; weight > 0; weight = weight >> 1) { + if ((ecc_part[i] & weight) != 0) { + adjusted_string.append("1"); + } else { + adjusted_string.append("0"); + } + } + } + break; + case 8: + for (i = 0; i < data_blocks; i++) { + for (weight = 0; weight < 8; weight++) { + if (adjusted_string.charAt((i * codeword_size) + weight) == '1') { + data_part[i] += (128 >> weight); + } + } + } + rs.init_gf(0x12d); + rs.init_code(ecc_blocks, 1); + rs.encode(data_blocks, data_part); + for (i = 0; i < ecc_blocks; i++) { + ecc_part[i] = rs.getResult(i); + } + for (i = (ecc_blocks - 1); i >= 0; i--) { + for (weight = 0x80; weight > 0; weight = weight >> 1) { + if ((ecc_part[i] & weight) != 0) { + adjusted_string.append("1"); + } else { + adjusted_string.append("0"); + } + } + } + break; + case 10: + for (i = 0; i < data_blocks; i++) { + for (weight = 0; weight < 10; weight++) { + if (adjusted_string.charAt((i * codeword_size) + weight) == '1') { + data_part[i] += (512 >> weight); + } + } + } + rs.init_gf(0x409); + rs.init_code(ecc_blocks, 1); + rs.encode(data_blocks, data_part); + for (i = 0; i < ecc_blocks; i++) { + ecc_part[i] = rs.getResult(i); + } + for (i = (ecc_blocks - 1); i >= 0; i--) { + for (weight = 0x200; weight > 0; weight = weight >> 1) { + if ((ecc_part[i] & weight) != 0) { + adjusted_string.append("1"); + } else { + adjusted_string.append("0"); + } + } + } + break; + case 12: + for (i = 0; i < data_blocks; i++) { + for (weight = 0; weight < 12; weight++) { + if (adjusted_string.charAt((i * codeword_size) + weight) == '1') { + data_part[i] += (2048 >> weight); + } + } + } + rs.init_gf(0x1069); + rs.init_code(ecc_blocks, 1); + rs.encode(data_blocks, data_part); + for (i = 0; i < ecc_blocks; i++) { + ecc_part[i] = rs.getResult(i); + } + for (i = (ecc_blocks - 1); i >= 0; i--) { + for (weight = 0x800; weight > 0; weight = weight >> 1) { + if ((ecc_part[i] & weight) != 0) { + adjusted_string.append("1"); + } else { + adjusted_string.append("0"); + } + } + } + break; + } + + /* Invert the data so that actual data is on the outside and reed-solomon on the inside */ + total_bits = (data_blocks + ecc_blocks) * codeword_size; + for (i = 0; i < total_bits; i++) { + bit_pattern.append(adjusted_string.charAt(total_bits - i - 1)); + } + + if (compact) { + /* The first 2 bits represent the number of layers minus 1 */ + if (((layers - 1) & 0x02) != 0) { + descriptor.append('1'); + } else { + descriptor.append('0'); + } + if (((layers - 1) & 0x01) != 0) { + descriptor.append('1'); + } else { + descriptor.append('0'); + } + /* The next 6 bits represent the number of data blocks minus 1 */ + if (readerInit) { + descriptor.append('1'); + } else { + if (((data_blocks - 1) & 0x20) != 0) { + descriptor.append('1'); + } else { + descriptor.append('0'); + } + } + for (i = 0x10; i > 0; i = i >> 1) { + if (((data_blocks - 1) & i) != 0) { + descriptor.append('1'); + } else { + descriptor.append('0'); + } + } + encodeInfo.append("Mode Message: ").append(descriptor).append("\n"); + j = 2; + } else { + /* The first 5 bits represent the number of layers minus 1 */ + for (i = 0x10; i > 0; i = i >> 1) { + if (((layers - 1) & i) != 0) { + descriptor.append('1'); + } else { + descriptor.append('0'); + } + } + + /* The next 11 bits represent the number of data blocks minus 1 */ + if (readerInit) { + descriptor.append('1'); + } else { + if (((data_blocks - 1) & 0x400) != 0) { + descriptor.append('1'); + } else { + descriptor.append('0'); + } + } + for (i = 0x200; i > 0; i = i >> 1) { + if (((data_blocks - 1) & i) != 0) { + descriptor.append('1'); + } else { + descriptor.append('0'); + } + } + + encodeInfo.append("Mode Message: ").append(descriptor).append("\n"); + j = 4; + } + + /* Split into 4-bit codewords */ + for (i = 0; i < j; i++) { + for (weight = 0; weight < 4; weight++) { + if (descriptor.charAt((i * 4) + weight) == '1') { + desc_data[i] += (8 >> weight); + } + } + } + + /* Add reed-solomon error correction with Galois field GF(16) and prime modulus + x^4 + x + 1 (section 7.2.3)*/ + rs2.init_gf(0x13); + if (compact) { + rs2.init_code(5, 1); + rs2.encode(2, desc_data); + for (j = 0; j < 5; j++) { + desc_ecc[j] = rs2.getResult(j); + } + for (i = 0; i < 5; i++) { + for (weight = 0x08; weight > 0; weight = weight >> 1) { + if ((desc_ecc[4 - i] & weight) != 0) { + descriptor.append('1'); + } else { + descriptor.append('0'); + } + } + } + } else { + rs2.init_code(6, 1); + rs2.encode(4, desc_data); + for (j = 0; j < 6; j++) { + desc_ecc[j] = rs2.getResult(j); + } + for (i = 0; i < 6; i++) { + for (weight = 0x08; weight > 0; weight = weight >> 1) { + if ((desc_ecc[5 - i] & weight) != 0) { + descriptor.append('1'); + } else { + descriptor.append('0'); + } + } + } + } + + readable = new StringBuilder(); + + /* Plot all of the data into the symbol in pre-defined spiral pattern */ + if (compact) { + + rowCount = 27 - (2 * AztecCompactOffset[layers - 1]); + rowHeight = new int[rowCount]; + rowHeight[0] = -1; + pattern = new String[rowCount]; + bin = new StringBuilder(); + for (y = AztecCompactOffset[layers - 1]; y < (27 - AztecCompactOffset[layers - 1]); y++) { + for (x = AztecCompactOffset[layers - 1]; x < (27 - AztecCompactOffset[layers - 1]); x++) { + j = COMPACT_AZTEC_MAP[(y * 27) + x]; + + if (j == 0) { + bin.append("0"); + } + if (j == 1) { + bin.append("1"); + } + + if (j >= 2) { + if ((j - 2) < bit_pattern.length()) { + bin.append(bit_pattern.charAt(j - 2)); + } else { + if (j > 2000) { + bin.append(descriptor.charAt(j - 2000)); + } else { + bin.append("0"); + } + } + } + } + rowHeight[y - AztecCompactOffset[layers - 1]] = 1; + pattern[y - AztecCompactOffset[layers - 1]] = bin2pat(bin.toString()); + bin = new StringBuilder(); + } + + } else { + rowCount = 151 - (2 * AztecOffset[layers - 1]); + rowHeight = new int[rowCount]; + rowHeight[0] = -1; + pattern = new String[rowCount]; + bin = new StringBuilder(); + for (y = AztecOffset[layers - 1]; y < (151 - AztecOffset[layers - 1]); y++) { + for (x = AztecOffset[layers - 1]; x < (151 - AztecOffset[layers - 1]); x++) { + j = AZTEC_MAP[x][y]; + if (j == 1) { + bin.append("1"); + } + if (j == 0) { + bin.append("0"); + } + if (j >= 2) { + if ((j - 2) < bit_pattern.length()) { + bin.append(bit_pattern.charAt(j - 2)); + } else { + if (j > 20000) { + bin.append(descriptor.charAt(j - 20000)); + } else { + bin.append("0"); + } + } + } + } + rowHeight[y - AztecOffset[layers - 1]] = 1; + pattern[y - AztecOffset[layers - 1]] = bin2pat(bin.toString()); + bin = new StringBuilder(); + } + } + + plotSymbol(); + return true; + } + + private boolean generateAztecBinary() { + /* Encode input data into a binary string */ + int i, j, k, bytes; + int curtable, newtable, lasttable, chartype, maplength, blocks; + int[] charmap = new int[2 * inputBytes.length]; + int[] typemap = new int[2 * inputBytes.length]; + int[] blockType = new int[inputBytes.length + 1]; + int[] blockLength = new int[inputBytes.length + 1]; + int weight; + + /* Lookup input string in encoding table */ + maplength = 0; + + if (inputDataType == DataType.GS1) { + /* Add FNC1 to beginning of GS1 messages */ + charmap[maplength] = 0; // FLG + typemap[maplength++] = 8; // PUNC + charmap[maplength] = 400; // (0) + typemap[maplength++] = 8; // PUNC + } + + if (eciMode != 3) { + int flagNumber; + + charmap[maplength] = 0; // FLG + typemap[maplength++] = 8; // PUNC + + flagNumber = 6; + + if (eciMode < 100000) { + flagNumber = 5; + } + + if (eciMode < 10000) { + flagNumber = 4; + } + + if (eciMode < 1000) { + flagNumber = 3; + } + + if (eciMode < 100) { + flagNumber = 2; + } + + if (eciMode < 10) { + flagNumber = 1; + } + + charmap[maplength] = 400 + flagNumber; + typemap[maplength++] = 8; // PUNC + } + + for (i = 0; i < inputBytes.length; i++) { + if ((inputDataType == DataType.GS1) && ((inputBytes[i] & 0xFF) == '[')) { + /* FNC1 represented by FLG(0) */ + charmap[maplength] = 0; // FLG + typemap[maplength++] = 8; // PUNC + charmap[maplength] = 400; // (0) + typemap[maplength++] = 8; // PUNC + } else { + if (((inputBytes[i] & 0xFF) > 0x7F) || ((inputBytes[i] & 0xFF) == 0x00)) { + charmap[maplength] = (inputBytes[i] & 0xFF); + typemap[maplength++] = 32; //BINARY + } else { + charmap[maplength] = AztecSymbolChar[(inputBytes[i] & 0xFF)]; + typemap[maplength++] = AztecCodeSet[(inputBytes[i] & 0xFF)]; + } + } + } + + /* Look for double character encoding possibilities */ + i = 0; + do { + if (((charmap[i] == 300) && (charmap[i + 1] == 11)) && ((typemap[i] == 8 /*PUNC */) && (typemap[i + 1] == 8 /*PUNC*/))) { + /* CR LF combination */ + charmap[i] = 2; + typemap[i] = 8; // PUNC + if ((i + 1) != maplength) { + for (j = i + 1; j < maplength; j++) { + charmap[j] = charmap[j + 1]; + typemap[j] = typemap[j + 1]; + } + } + maplength--; + } + + if (((charmap[i] == 302) && (charmap[i + 1] == 1)) && ((typemap[i] == 24) && (typemap[i + 1] == 23))) { + /* . SP combination */ + charmap[i] = 3; + typemap[i] = 8; // PUNC; + if ((i + 1) != maplength) { + for (j = i + 1; j < maplength; j++) { + charmap[j] = charmap[j + 1]; + typemap[j] = typemap[j + 1]; + } + } + maplength--; + } + + if (((charmap[i] == 301) && (charmap[i + 1] == 1)) && ((typemap[i] == 24) && (typemap[i + 1] == 23))) { + /* , SP combination */ + charmap[i] = 4; + typemap[i] = 8; //PUNC; + if ((i + 1) != maplength) { + for (j = i + 1; j < maplength; j++) { + charmap[j] = charmap[j + 1]; + typemap[j] = typemap[j + 1]; + } + } + maplength--; + } + + if (((charmap[i] == 21) && (charmap[i + 1] == 1)) && ((typemap[i] == 8) && (typemap[i + 1] == 23))) { + /* : SP combination */ + charmap[i] = 5; + typemap[i] = 8; //PUNC; + if ((i + 1) != maplength) { + for (j = i + 1; j < maplength; j++) { + charmap[j] = charmap[j + 1]; + typemap[j] = typemap[j + 1]; + } + } + maplength--; + } + + i++; + } while (i < (maplength - 1)); + + /* look for blocks of characters which use the same table */ + blocks = 1; + blockType[0] = typemap[0]; + blockLength[0] = 1; + for (i = 1; i < maplength; i++) { + if (typemap[i] == typemap[i - 1]) { + blockLength[blocks - 1]++; + } else { + blocks++; + blockType[blocks - 1] = typemap[i]; + blockLength[blocks - 1] = 1; + } + } + + if ((blockType[0] & 1) != 0) { + blockType[0] = 1; + } + if ((blockType[0] & 2) != 0) { + blockType[0] = 2; + } + if ((blockType[0] & 4) != 0) { + blockType[0] = 4; + } + if ((blockType[0] & 8) != 0) { + blockType[0] = 8; + } + + if (blocks > 1) { + + /* look for adjacent blocks which can use the same table (left to right search) */ + for (i = 1; i < blocks; i++) { + if ((blockType[i] & blockType[i - 1]) != 0) { + blockType[i] = (blockType[i] & blockType[i - 1]); + } + } + + if ((blockType[blocks - 1] & 1) != 0) { + blockType[blocks - 1] = 1; + } + if ((blockType[blocks - 1] & 2) != 0) { + blockType[blocks - 1] = 2; + } + if ((blockType[blocks - 1] & 4) != 0) { + blockType[blocks - 1] = 4; + } + if ((blockType[blocks - 1] & 8) != 0) { + blockType[blocks - 1] = 8; + } + + /* look for adjacent blocks which can use the same table (right to left search) */ + for (i = blocks - 2; i > 0; i--) { + if ((blockType[i] & blockType[i + 1]) != 0) { + blockType[i] = (blockType[i] & blockType[i + 1]); + } + } + + /* determine the encoding table for characters which do not fit with adjacent blocks */ + for (i = 1; i < blocks; i++) { + if ((blockType[i] & 8) != 0) { + blockType[i] = 8; + } + if ((blockType[i] & 4) != 0) { + blockType[i] = 4; + } + if ((blockType[i] & 2) != 0) { + blockType[i] = 2; + } + if ((blockType[i] & 1) != 0) { + blockType[i] = 1; + } + } + + /* if less than 4 characters are preceeded and followed by binary blocks + then it is more efficient to also encode these in binary + */ + + /* Combine blocks of the same type */ + i = 0; + do { + if (blockType[i] == blockType[i + 1]) { + blockLength[i] += blockLength[i + 1]; + for (j = i + 1; j < blocks - 1; j++) { + blockType[j] = blockType[j + 1]; + blockLength[j] = blockLength[j + 1]; + } + blocks--; + } else { + i++; + } + } while (i < blocks - 1); + } + + /* Put the adjusted block data back into typemap */ + j = 0; + for (i = 0; i < blocks; i++) { + if ((blockLength[i] < 3) && (blockType[i] != 32)) { /* Shift character(s) needed */ + + for (k = 0; k < blockLength[i]; k++) { + typemap[j + k] = blockType[i] + 64; + } + } else { /* Latch character (or byte mode) needed */ + + for (k = 0; k < blockLength[i]; k++) { + typemap[j + k] = blockType[i]; + } + } + j += blockLength[i]; + } + + /* Don't shift an initial capital letter */ + if (typemap[0] == 65) { + typemap[0] = 1; + } + + /* Problem characters (those that appear in different tables with different values) can now be resolved into their tables */ + for (i = 0; i < maplength; i++) { + if ((charmap[i] >= 300) && (charmap[i] < 400)) { + curtable = typemap[i]; + if (curtable > 64) { + curtable -= 64; + } + switch (charmap[i]) { + case 300: + /* Carriage Return */ + switch (curtable) { + case 8: + charmap[i] = 1; + break; // PUNC + case 4: + charmap[i] = 14; + break; // PUNC + } + break; + case 301: + /* Comma */ + switch (curtable) { + case 8: + charmap[i] = 17; + break; // PUNC + case 16: + charmap[i] = 12; + break; // DIGIT + } + break; + case 302: + /* Full Stop */ + switch (curtable) { + case 8: + charmap[i] = 19; + break; // PUNC + case 16: + charmap[i] = 13; + break; // DIGIT + } + break; + } + } + } + + binaryString = new StringBuilder(); + + encodeInfo.append("Encoding: "); + + curtable = 1; /* start with 1 table */ + + lasttable = 1; + for (i = 0; i < maplength; i++) { + newtable = curtable; + if ((typemap[i] != curtable) && (charmap[i] < 400)) { + /* Change table */ + if (curtable == 32) { + /* If ending binary mode the current table is the same as when entering binary mode */ + curtable = lasttable; + newtable = lasttable; + } + if (typemap[i] > 64) { + /* Shift character */ + switch (typemap[i]) { + case (64 + 1): + /* To UPPER */ + switch (curtable) { + case 2: + /* US */ + binaryString.append(pentbit[28]); + encodeInfo.append("US "); + break; + case 4: + /* UL */ + binaryString.append(pentbit[29]); + encodeInfo.append("UL "); + newtable = 1; + break; + case 8: + /* UL */ + binaryString.append(pentbit[31]); + encodeInfo.append("UL "); + newtable = 1; + break; + case 16: + /* US */ + binaryString.append(quadbit[15]); + encodeInfo.append("US "); + break; + } + break; + case (64 + 2): + /* To LOWER */ + switch (curtable) { + case 1: + /* LL */ + binaryString.append(pentbit[28]); + encodeInfo.append("LL "); + newtable = 2; + break; + case 4: + /* LL */ + binaryString.append(pentbit[28]); + encodeInfo.append("LL "); + newtable = 2; + break; + case 8: + /* UL LL */ + binaryString.append(pentbit[31]); + encodeInfo.append("UL "); + binaryString.append(pentbit[28]); + encodeInfo.append("LL "); + newtable = 2; + break; + case 16: + /* UL LL */ + binaryString.append(quadbit[14]); + encodeInfo.append("UL "); + binaryString.append(pentbit[28]); + encodeInfo.append("LL "); + newtable = 2; + break; + } + break; + case (64 + 4): + /* To MIXED */ + switch (curtable) { + case 1: + /* ML */ + binaryString.append(pentbit[29]); + encodeInfo.append("ML "); + newtable = 4; + break; + case 2: + /* ML */ + binaryString.append(pentbit[29]); + encodeInfo.append("ML "); + newtable = 4; + break; + case 8: + /* UL ML */ + binaryString.append(pentbit[31]); + encodeInfo.append("UL "); + binaryString.append(pentbit[29]); + encodeInfo.append("ML "); + newtable = 4; + break; + case 16: + /* UL ML */ + binaryString.append(quadbit[14]); + encodeInfo.append("UL "); + binaryString.append(pentbit[29]); + encodeInfo.append("ML "); + newtable = 4; + break; + } + break; + case (64 + 8): + /* To PUNC */ + switch (curtable) { + case 1: + /* PS */ + binaryString.append(pentbit[0]); + encodeInfo.append("PS "); + break; + case 2: + /* PS */ + binaryString.append(pentbit[0]); + encodeInfo.append("PS "); + break; + case 4: + /* PS */ + binaryString.append(pentbit[0]); + encodeInfo.append("PS "); + break; + case 16: + /* PS */ + binaryString.append(quadbit[0]); + encodeInfo.append("PS "); + break; + } + break; + case (64 + 16): + /* To DIGIT */ + switch (curtable) { + case 1: + /* DL */ + binaryString.append(pentbit[30]); + encodeInfo.append("DL "); + newtable = 16; + break; + case 2: + /* DL */ + binaryString.append(pentbit[30]); + encodeInfo.append("DL "); + newtable = 16; + break; + case 4: + /* UL DL */ + binaryString.append(pentbit[29]); + encodeInfo.append("UL "); + binaryString.append(pentbit[30]); + encodeInfo.append("DL "); + newtable = 16; + break; + case 8: + /* UL DL */ + binaryString.append(pentbit[31]); + encodeInfo.append("UL "); + binaryString.append(pentbit[30]); + encodeInfo.append("DL "); + newtable = 16; + break; + } + break; + } + } else { + /* Latch character */ + switch (typemap[i]) { + case 1: + /* To UPPER */ + switch (curtable) { + case 2: + /* ML UL */ + binaryString.append(pentbit[29]); + encodeInfo.append("ML "); + binaryString.append(pentbit[29]); + encodeInfo.append("UL "); + newtable = 1; + break; + case 4: + /* UL */ + binaryString.append(pentbit[29]); + encodeInfo.append("UL "); + newtable = 1; + break; + case 8: + /* UL */ + binaryString.append(pentbit[31]); + encodeInfo.append("UL "); + newtable = 1; + break; + case 16: + /* UL */ + binaryString.append(quadbit[14]); + encodeInfo.append("UL "); + newtable = 1; + break; + } + break; + case 2: + /* To LOWER */ + switch (curtable) { + case 1: + /* LL */ + binaryString.append(pentbit[28]); + encodeInfo.append("LL "); + newtable = 2; + break; + case 4: + /* LL */ + binaryString.append(pentbit[28]); + encodeInfo.append("LL "); + newtable = 2; + break; + case 8: + /* UL LL */ + binaryString.append(pentbit[31]); + encodeInfo.append("UL "); + binaryString.append(pentbit[28]); + encodeInfo.append("LL "); + newtable = 2; + break; + case 16: + /* UL LL */ + binaryString.append(quadbit[14]); + encodeInfo.append("UL "); + binaryString.append(pentbit[28]); + encodeInfo.append("LL "); + newtable = 2; + break; + } + break; + case 4: + /* To MIXED */ + switch (curtable) { + case 1: + /* ML */ + binaryString.append(pentbit[29]); + encodeInfo.append("ML "); + newtable = 4; + break; + case 2: + /* ML */ + binaryString.append(pentbit[29]); + encodeInfo.append("ML "); + newtable = 4; + break; + case 8: + /* UL ML */ + binaryString.append(pentbit[31]); + encodeInfo.append("UL "); + binaryString.append(pentbit[29]); + encodeInfo.append("ML "); + newtable = 4; + break; + case 16: + /* UL ML */ + binaryString.append(quadbit[14]); + encodeInfo.append("UL "); + binaryString.append(pentbit[29]); + encodeInfo.append("ML "); + newtable = 4; + break; + } + break; + case 8: + /* To PUNC */ + switch (curtable) { + case 1: + /* ML PL */ + binaryString.append(pentbit[29]); + encodeInfo.append("ML "); + binaryString.append(pentbit[30]); + encodeInfo.append("PL "); + newtable = 8; + break; + case 2: + /* ML PL */ + binaryString.append(pentbit[29]); + encodeInfo.append("ML "); + binaryString.append(pentbit[30]); + encodeInfo.append("PL "); + newtable = 8; + break; + case 4: + /* PL */ + binaryString.append(pentbit[30]); + encodeInfo.append("PL "); + newtable = 8; + break; + case 16: + /* UL ML PL */ + binaryString.append(quadbit[14]); + encodeInfo.append("UL "); + binaryString.append(pentbit[29]); + encodeInfo.append("ML "); + binaryString.append(pentbit[30]); + encodeInfo.append("PL "); + newtable = 8; + break; + } + break; + case 16: + /* To DIGIT */ + switch (curtable) { + case 1: + /* DL */ + binaryString.append(pentbit[30]); + encodeInfo.append("DL "); + newtable = 16; + break; + case 2: + /* DL */ + binaryString.append(pentbit[30]); + encodeInfo.append("DL "); + newtable = 16; + break; + case 4: + /* UL DL */ + binaryString.append(pentbit[29]); + encodeInfo.append("UL "); + binaryString.append(pentbit[30]); + encodeInfo.append("DL "); + newtable = 16; + break; + case 8: + /* UL DL */ + binaryString.append(pentbit[31]); + encodeInfo.append("UL "); + binaryString.append(pentbit[30]); + encodeInfo.append("DL "); + newtable = 16; + break; + } + break; + case 32: + /* To BINARY */ + lasttable = curtable; + switch (curtable) { + case 1: + /* BS */ + binaryString.append(pentbit[31]); + encodeInfo.append("BS "); + newtable = 32; + break; + case 2: + /* BS */ + binaryString.append(pentbit[31]); + encodeInfo.append("BS "); + newtable = 32; + break; + case 4: + /* BS */ + binaryString.append(pentbit[31]); + encodeInfo.append("BS "); + newtable = 32; + break; + case 8: + /* UL BS */ + binaryString.append(pentbit[31]); + encodeInfo.append("UL "); + binaryString.append(pentbit[31]); + encodeInfo.append("BS "); + lasttable = 1; + newtable = 32; + break; + case 16: + /* UL BS */ + binaryString.append(quadbit[14]); + encodeInfo.append("UL "); + binaryString.append(pentbit[31]); + encodeInfo.append("BS "); + lasttable = 1; + newtable = 32; + break; + } + + bytes = 0; + do { + bytes++; + } while (typemap[i + (bytes - 1)] == 32); + bytes--; + + if (bytes > 2079) { + errorMsg.append("Input too long"); + return false; + } + + if (bytes > 31) { /* Put 00000 followed by 11-bit number of bytes less 31 */ + + binaryString.append("00000"); + for (weight = 0x400; weight > 0; weight = weight >> 1) { + if (((bytes - 31) & weight) != 0) { + binaryString.append("1"); + } else { + binaryString.append("0"); + } + } + } else { /* Put 5-bit number of bytes */ + + for (weight = 0x10; weight > 0; weight = weight >> 1) { + if ((bytes & weight) != 0) { + binaryString.append("1"); + } else { + binaryString.append("0"); + } + } + } + + break; + } + } + } + /* Add data to the binary string */ + curtable = newtable; + chartype = typemap[i]; + if (chartype > 64) { + chartype -= 64; + } + switch (chartype) { + case 1: + case 2: + case 4: + case 8: + if (charmap[i] >= 400) { + encodeInfo.append("FLG(").append(Integer.toString(charmap[i] - 400)).append(") "); + binaryString.append(tribit[charmap[i] - 400]); + if (charmap[i] != 400) { + /* ECI */ + binaryString.append(eciToBinary()); + } + } else { + binaryString.append(pentbit[charmap[i]]); + encodeInfo.append(Integer.toString(charmap[i])).append(" "); + } + break; + case 16: + binaryString.append(quadbit[charmap[i]]); + encodeInfo.append(Integer.toString(charmap[i])); + break; + case 32: + for (weight = 0x80; weight > 0; weight = weight >> 1) { + if ((charmap[i] & weight) != 0) { + binaryString.append("1"); + } else { + binaryString.append("0"); + } + } + encodeInfo.append(Integer.toString(charmap[i])); + break; + } + + } + encodeInfo.append("\n"); + return true; + } + + private String eciToBinary() { + StringBuilder binary = new StringBuilder(); + String eciNumber = Integer.toString(eciMode); + int i; + + for (i = 0; i < eciNumber.length(); i++) { + binary.append(quadbit[(eciNumber.charAt(i) - '0') + 2]); + encodeInfo.append(Character.toString(eciNumber.charAt(i))).append(" "); + } + + return binary.toString(); + } +} diff --git a/barcode/src/main/java/org/xbib/graphics/barcode/AztecRune.java b/barcode/src/main/java/org/xbib/graphics/barcode/AztecRune.java new file mode 100755 index 0000000..2077831 --- /dev/null +++ b/barcode/src/main/java/org/xbib/graphics/barcode/AztecRune.java @@ -0,0 +1,168 @@ +package org.xbib.graphics.barcode; + +import org.xbib.graphics.barcode.util.ReedSolomon; + +/** + * Implements Aztec Runes bar code symbology. + * According to ISO/IEC 24778:2008 Annex A + * Aztec Runes is a fixed-size matrix symbology which can encode whole + * integer values between 0 and 255. + */ +public class AztecRune extends Symbol { + + private int[] bitPlacementMap = { + 1, 1, 2, 3, 4, 5, 6, 7, 8, 0, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 29, 1, 0, 0, 0, 0, 0, 0, 0, 1, 9, + 28, 1, 0, 1, 1, 1, 1, 1, 0, 1, 10, + 27, 1, 0, 1, 0, 0, 0, 1, 0, 1, 11, + 26, 1, 0, 1, 0, 1, 0, 1, 0, 1, 12, + 25, 1, 0, 1, 0, 0, 0, 1, 0, 1, 13, + 24, 1, 0, 1, 1, 1, 1, 1, 0, 1, 14, + 23, 1, 0, 0, 0, 0, 0, 0, 0, 1, 15, + 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 0, 0, 22, 21, 20, 19, 18, 17, 16, 0, 0 + }; + + @Override + public boolean encode() { + int decimalValue = 0; + int i; + int row; + int column; + StringBuilder binaryDataStream; + StringBuilder reversedBinaryDataStream; + int[] dataCodeword = new int[3]; + int[] errorCorrectionCodeword = new int[6]; + ReedSolomon rs = new ReedSolomon(); + StringBuilder rowBinary; + + if (content.length() > 3) { + errorMsg.append("Input too large"); + return false; + } + + if (!(content.matches("[0-9]+"))) { + errorMsg.append("Invalid input data"); + return false; + } + + switch (content.length()) { + case 3: + decimalValue = 100 * (content.charAt(0) - '0'); + decimalValue += 10 * (content.charAt(1) - '0'); + decimalValue += (content.charAt(2) - '0'); + break; + case 2: + decimalValue = 10 * (content.charAt(0) - '0'); + decimalValue += (content.charAt(1) - '0'); + break; + case 1: + decimalValue = (content.charAt(0) - '0'); + break; + } + + if (decimalValue > 255) { + errorMsg.append("Input too large"); + return false; + } + + binaryDataStream = new StringBuilder(); + for (i = 0x80; i > 0; i = i >> 1) { + if ((decimalValue & i) != 0) { + binaryDataStream.append("1"); + } else { + binaryDataStream.append("0"); + } + } + + dataCodeword[0] = 0; + dataCodeword[1] = 0; + + for (i = 0; i < 2; i++) { + if (binaryDataStream.charAt(i * 4) == '1') { + dataCodeword[i] += 8; + } + if (binaryDataStream.charAt((i * 4) + 1) == '1') { + dataCodeword[i] += 4; + } + if (binaryDataStream.charAt((i * 4) + 2) == '1') { + dataCodeword[i] += 2; + } + if (binaryDataStream.charAt((i * 4) + 3) == '1') { + dataCodeword[i] += 1; + } + } + + rs.init_gf(0x13); + rs.init_code(5, 1); + rs.encode(2, dataCodeword); + + for (i = 0; i < 5; i++) { + errorCorrectionCodeword[i] = rs.getResult(i); + } + + for (i = 0; i < 5; i++) { + if ((errorCorrectionCodeword[4 - i] & 0x08) != 0) { + binaryDataStream.append('1'); + } else { + binaryDataStream.append('0'); + } + if ((errorCorrectionCodeword[4 - i] & 0x04) != 0) { + binaryDataStream.append('1'); + } else { + binaryDataStream.append('0'); + } + if ((errorCorrectionCodeword[4 - i] & 0x02) != 0) { + binaryDataStream.append('1'); + } else { + binaryDataStream.append('0'); + } + if ((errorCorrectionCodeword[4 - i] & 0x01) != 0) { + binaryDataStream.append('1'); + } else { + binaryDataStream.append('0'); + } + } + + reversedBinaryDataStream = new StringBuilder(); + for (i = 0; i < binaryDataStream.length(); i++) { + if ((i & 1) == 0) { + if (binaryDataStream.charAt(i) == '0') { + reversedBinaryDataStream.append("1"); + } else { + reversedBinaryDataStream.append("0"); + } + } else { + reversedBinaryDataStream.append(binaryDataStream.charAt(i)); + } + } + + encodeInfo.append("Binary: ").append(reversedBinaryDataStream).append("\n"); + + rowBinary = new StringBuilder(); + readable = new StringBuilder(); + pattern = new String[11]; + rowCount = 11; + rowHeight = new int[11]; + for (row = 0; row < 11; row++) { + for (column = 0; column < 11; column++) { + if (bitPlacementMap[(row * 11) + column] == 1) { + rowBinary.append("1"); + } + if (bitPlacementMap[(row * 11) + column] == 0) { + rowBinary.append("0"); + } + if (bitPlacementMap[(row * 11) + column] >= 2) { + rowBinary.append(reversedBinaryDataStream.charAt(bitPlacementMap[(row * 11) + column] - 2)); + } + } + pattern[row] = bin2pat(rowBinary.toString()); + rowHeight[row] = 1; + rowBinary = new StringBuilder(); + } + + plotSymbol(); + return true; + } +} diff --git a/barcode/src/main/java/org/xbib/graphics/barcode/ChannelCode.java b/barcode/src/main/java/org/xbib/graphics/barcode/ChannelCode.java new file mode 100755 index 0000000..ddd7301 --- /dev/null +++ b/barcode/src/main/java/org/xbib/graphics/barcode/ChannelCode.java @@ -0,0 +1,155 @@ +package org.xbib.graphics.barcode; + +/** + * Implements Channel Code according to ANSI/AIM BC12-1998. + * Channel code encodes whole integer values between 0 and 7,742,862. + */ +public class ChannelCode extends Symbol { + private int[] space = new int[11]; + private int[] bar = new int[11]; + private double currentValue; + private double targetValue; + private String horizontalSpacing; + private int requestedNumberOfChannels = 0; + + /** + * Set the number of channels used to encode data. This setting will be + * ignored if the value to be encoded requires more channels. + * + * @param channels Number of channels in range 3 - 8 + */ + public void setNumberOfChannels(int channels) { + if ((channels >= 3) && (channels <= 7)) { + requestedNumberOfChannels = channels; + } + } + + @Override + @SuppressWarnings("fallthrough") + public boolean encode() { + int numberOfChannels; + int i; + int leadingZeroCount; + + targetValue = 0; + horizontalSpacing = ""; + + if (content.length() > 7) { + errorMsg.append("Input too long"); + return false; + } + + if (!(content.matches("[0-9]+"))) { + errorMsg.append("Invalid characters in input"); + return false; + } + + if ((requestedNumberOfChannels <= 2) || (requestedNumberOfChannels > 8)) { + numberOfChannels = 3; + } else { + numberOfChannels = requestedNumberOfChannels; + } + + for (i = 0; i < content.length(); i++) { + targetValue *= 10; + targetValue += Character.getNumericValue(content.charAt(i)); + } + + switch (numberOfChannels) { + case 3: + if (targetValue > 26) { + numberOfChannels++; + } + case 4: + if (targetValue > 292) { + numberOfChannels++; + } + case 5: + if (targetValue > 3493) { + numberOfChannels++; + } + case 6: + if (targetValue > 44072) { + numberOfChannels++; + } + case 7: + if (targetValue > 576688) { + numberOfChannels++; + } + case 8: + if (targetValue > 7742862) { + numberOfChannels++; + } + } + + if (numberOfChannels == 9) { + errorMsg.append("Value out of range"); + return false; + } + + encodeInfo.append("Channels Used: ").append(numberOfChannels).append('\n'); + + for (i = 0; i < 11; i++) { + bar[i] = 0; + space[i] = 0; + } + + bar[0] = space[1] = bar[1] = space[2] = bar[2] = 1; + currentValue = 0; + nextSpace(numberOfChannels, 3, numberOfChannels, numberOfChannels); + + leadingZeroCount = numberOfChannels - 1 - content.length(); + + readable = new StringBuilder(); + for (i = 0; i < leadingZeroCount; i++) { + readable.append("0"); + } + readable.append(content); + + pattern = new String[1]; + pattern[0] = horizontalSpacing; + rowCount = 1; + rowHeight = new int[1]; + rowHeight[0] = -1; + plotSymbol(); + return true; + } + + private void nextSpace(int channels, int i, int maxSpace, int maxBar) { + int s; + + for (s = (i < channels + 2) ? 1 : maxSpace; s <= maxSpace; s++) { + space[i] = s; + nextBar(channels, i, maxBar, maxSpace + 1 - s); + } + } + + private void nextBar(int channels, int i, int maxBar, int maxSpace) { + int b; + + b = (space[i] + bar[i - 1] + space[i - 1] + bar[i - 2] > 4) ? 1 : 2; + if (i < channels + 2) { + for (; b <= maxBar; b++) { + bar[i] = b; + nextSpace(channels, i + 1, maxSpace, maxBar + 1 - b); + } + } else if (b <= maxBar) { + bar[i] = maxBar; + checkIfDone(); + currentValue++; + } + } + + private void checkIfDone() { + int i; + + if (currentValue == targetValue) { + /* Target reached - save the generated pattern */ + horizontalSpacing = "11110"; + for (i = 0; i < 11; i++) { + horizontalSpacing += (char) (space[i] + '0'); + horizontalSpacing += (char) (bar[i] + '0'); + } + } + } +} diff --git a/barcode/src/main/java/org/xbib/graphics/barcode/Codabar.java b/barcode/src/main/java/org/xbib/graphics/barcode/Codabar.java new file mode 100755 index 0000000..87aa226 --- /dev/null +++ b/barcode/src/main/java/org/xbib/graphics/barcode/Codabar.java @@ -0,0 +1,86 @@ +package org.xbib.graphics.barcode; + +/** + * Implements Codabar barcode symbology according to BS EN 798:1996. + * Also known as NW-7, Monarch, ABC Codabar, USD-4, Ames Code and Code 27. + * Codabar can encode any length string starting and ending with the letters + * A-D and containing between these letters the numbers 0-9, dash (-), dollar + * ($), colon (:), slash (/), full stop (.) or plus (+). No check digit is + * generated. + */ +public class Codabar extends Symbol { + + private static final String[] CODABAR_TABLE = { + "11111221", "11112211", "11121121", "22111111", "11211211", + "21111211", "12111121", "12112111", "12211111", "21121111", + "11122111", "11221111", "21112121", "21211121", "21212111", + "11212121", "11221211", "12121121", "11121221", "11122211" + }; + + private static final char[] CHARACTER_SET = { + '0', '1', '2', '3', '4', + '5', '6', '7', '8', '9', + '-', '$', ':', '/', '.', + '+', 'A', 'B', 'C', 'D' + }; + + /** + * Ratio of wide bar width to narrow bar width. + */ + private double moduleWidthRatio = 2; + + /** + * Returns the ratio of wide bar width to narrow bar width. + * + * @return the ratio of wide bar width to narrow bar width + */ + public double getModuleWidthRatio() { + return moduleWidthRatio; + } + + /** + * Sets the ratio of wide bar width to narrow bar width. Valid values are usually + * between {@code 2} and {@code 3}. The default value is {@code 2}. + * + * @param moduleWidthRatio the ratio of wide bar width to narrow bar width + */ + public void setModuleWidthRatio(double moduleWidthRatio) { + this.moduleWidthRatio = moduleWidthRatio; + } + + @Override + public boolean encode() { + if (!(content.matches("[A-D]{1}[0-9:/\\$\\.\\+\u002D]+[A-D]{1}"))) { + errorMsg.append("Invalid characters in input"); + return false; + } + + StringBuilder horizontalSpacing = new StringBuilder(); + + int l = content.length(); + for (int i = 0; i < l; i++) { + horizontalSpacing.append(CODABAR_TABLE[positionOf(content.charAt(i), CHARACTER_SET)]); + } + + readable = new StringBuilder(content); + pattern = new String[]{horizontalSpacing.toString()}; + rowCount = 1; + rowHeight = new int[]{-1}; + plotSymbol(); + return true; + } + + @Override + protected double getModuleWidth(int originalWidth) { + if (originalWidth == 1) { + return 1; + } else { + return moduleWidthRatio; + } + } + + @Override + protected int[] getCodewords() { + return getPatternAsCodewords(8); + } +} diff --git a/barcode/src/main/java/org/xbib/graphics/barcode/CodablockF.java b/barcode/src/main/java/org/xbib/graphics/barcode/CodablockF.java new file mode 100755 index 0000000..a3df7e4 --- /dev/null +++ b/barcode/src/main/java/org/xbib/graphics/barcode/CodablockF.java @@ -0,0 +1,852 @@ +package org.xbib.graphics.barcode; + +import java.awt.geom.Rectangle2D; +import java.io.UnsupportedEncodingException; + +/** + * Implements Codablock-F according to AIM Europe "Uniform Symbology Specification - Codablock F", 1995. + * Codablock F is a multi-row symbology using Code 128 encoding. It can + * encode any 8-bit ISO 8859-1 (Latin-1) data up to approximately 1000 + * alpha-numeric characters or 2000 numeric digits in length. + */ +public class CodablockF extends Symbol { + + /* Annex A Table A.1 */ + private String[] C128Table = {"212222", "222122", "222221", "121223", "121322", "131222", "122213", + "122312", "132212", "221213", "221312", "231212", "112232", "122132", "122231", "113222", + "123122", "123221", "223211", "221132", "221231", "213212", "223112", "312131", "311222", + "321122", "321221", "312212", "322112", "322211", "212123", "212321", "232121", "111323", + "131123", "131321", "112313", "132113", "132311", "211313", "231113", "231311", "112133", + "112331", "132131", "113123", "113321", "133121", "313121", "211331", "231131", "213113", + "213311", "213131", "311123", "311321", "331121", "312113", "312311", "332111", "314111", + "221411", "431111", "111224", "111422", "121124", "121421", "141122", "141221", "112214", + "112412", "122114", "122411", "142112", "142211", "241211", "221114", "413111", "241112", + "134111", "111242", "121142", "121241", "114212", "124112", "124211", "411212", "421112", + "421211", "212141", "214121", "412121", "111143", "111341", "131141", "114113", "114311", + "411113", "411311", "113141", "114131", "311141", "411131", "211412", "211214", "211232", + "2331112"}; + private final int[][] blockmatrix = new int[44][62]; + private int columns_needed; + private int[] source; + private int rows_needed; + private cfMode final_mode; + private final cfMode[] subset_selector = new cfMode[44]; + + @Override + public boolean encode() { + int input_length, i, j, k; + int min_module_height; + Mode last_mode, this_mode; + double estimate_codelength; + StringBuilder row_pattern; + int[] row_indicator = new int[44]; + int[] row_check = new int[44]; + int k1_sum, k2_sum; + int k1_check, k2_check; + + input_length = content.length(); + final_mode = cfMode.MODEA; + + if (input_length > 5450) { + errorMsg.append("Input data too long"); + return false; + } + + if (!content.matches("[\u0000-\u00FF]+")) { + errorMsg.append("Invalid characters in input data"); + return false; + } + + try { + inputBytes = content.getBytes("ISO8859_1"); + } catch (UnsupportedEncodingException e) { + errorMsg.append("Character encoding error"); + return false; + } + + source = new int[input_length + 1]; + for (i = 0; i < input_length; i++) { + source[i] = inputBytes[i] & 0xFF; + } + source[input_length] = 0x00; + + /* Make a guess at how many characters will be needed to encode the data */ + estimate_codelength = 0.0; + last_mode = Mode.AORB; /* Codablock always starts with Code A */ + for (i = 0; i < input_length; i++) { + this_mode = findSubset(source[i]); + if (this_mode != last_mode) { + estimate_codelength += 1.0; + } + if (this_mode != Mode.ABORC) { + estimate_codelength += 1.0; + } else { + estimate_codelength += 0.5; + } + if (source[i] > 127) { + estimate_codelength += 1.0; + } + last_mode = this_mode; + } + + /* Decide symbol size based on the above guess */ + rows_needed = (int) (0.5 + Math.sqrt((estimate_codelength + 2) / 1.45)); + if (rows_needed < 2) { + rows_needed = 2; + } + if (rows_needed > 44) { + rows_needed = 44; + } + columns_needed = (int) (estimate_codelength + 2) / rows_needed; + if (columns_needed < 4) { + columns_needed = 4; + } + if (columns_needed > 62) { + errorMsg.append("Input data too long"); + return false; + } + + /* Encode the data */ + if (!(data_encode_blockf())) { + return false; + } + + /* Add check digits - Annex F */ + k1_sum = 0; + k2_sum = 0; + for (i = 0; i < input_length; i++) { + if ((inputDataType == DataType.GS1) && source[i] == '[') { + k1_sum += (i + 1) * 29; /* GS */ + k2_sum += i * 29; + } else { + k1_sum += (i + 1) * source[i]; + k2_sum += i * source[i]; + } + } + k1_check = k1_sum % 86; + k2_check = k2_sum % 86; + if ((final_mode == cfMode.MODEA) || (final_mode == cfMode.MODEB)) { + k1_check = k1_check + 64; + if (k1_check > 95) { + k1_check -= 96; + } + k2_check = k2_check + 64; + if (k2_check > 95) { + k2_check -= 96; + } + } + blockmatrix[rows_needed - 1][columns_needed - 2] = k1_check; + blockmatrix[rows_needed - 1][columns_needed - 1] = k2_check; + + /* Calculate row height (4.6.1.a) */ + min_module_height = (int) (0.55 * (columns_needed + 3)) + 3; + if (min_module_height < 8) { + min_module_height = 8; + } + + /* Encode the Row Indicator in the First Row of the Symbol - Table D2 */ + if (subset_selector[0] == cfMode.MODEC) { + /* Code C */ + row_indicator[0] = rows_needed - 2; + } else { + /* Code A or B */ + row_indicator[0] = rows_needed + 62; + + if (row_indicator[0] > 95) { + row_indicator[0] -= 95; + } + } + + /* Encode the Row Indicator in the Second and Subsequent Rows of the Symbol - Table D3 */ + for (i = 1; i < rows_needed; i++) { + /* Note that the second row is row number 1 because counting starts from 0 */ + if (subset_selector[i] == cfMode.MODEC) { + /* Code C */ + row_indicator[i] = i + 42; + } else { + /* Code A or B */ + if (i < 6) + row_indicator[i] = i + 10; + else + row_indicator[i] = i + 20; + } + } + + /* Calculate row check digits - Annex E */ + for (i = 0; i < rows_needed; i++) { + k = 103; + switch (subset_selector[i]) { + case MODEA: + k += 98; + break; + case MODEB: + k += 100; + break; + case MODEC: + k += 99; + break; + } + k += 2 * row_indicator[i]; + for (j = 0; j < columns_needed; j++) { + k += (j + 3) * blockmatrix[i][j]; + } + row_check[i] = k % 103; + } + + readable = new StringBuilder(); + rowCount = rows_needed; + pattern = new String[rowCount]; + rowHeight = new int[rowCount]; + + encodeInfo.append("Grid size: ").append(columns_needed).append(" X ").append(rows_needed).append('\n'); + encodeInfo.append("K1 Check Digit: ").append(k1_check).append("\n"); + encodeInfo.append("K2 Check Digit: ").append(k2_check).append("\n"); + + /* Resolve the data into patterns and place in symbol structure */ + encodeInfo.append("Encoding: "); + for (i = 0; i < rows_needed; i++) { + + row_pattern = new StringBuilder(); + /* Start character */ + row_pattern.append(C128Table[103]); /* Always Start A */ + + switch (subset_selector[i]) { + case MODEA: + row_pattern.append(C128Table[98]); + encodeInfo.append("MODEA "); + break; + case MODEB: + row_pattern.append(C128Table[100]); + encodeInfo.append("MODEB "); + break; + case MODEC: + row_pattern.append(C128Table[99]); + encodeInfo.append("MODEC "); + break; + } + row_pattern.append(C128Table[row_indicator[i]]); + encodeInfo.append(Integer.toString(row_indicator[i])).append(" "); + + for (j = 0; j < columns_needed; j++) { + row_pattern.append(C128Table[blockmatrix[i][j]]); + encodeInfo.append(Integer.toString(blockmatrix[i][j])).append(" "); + } + + row_pattern.append(C128Table[row_check[i]]); + encodeInfo.append("(").append(Integer.toString(row_check[i])).append(") "); + + /* Stop character */ + row_pattern.append(C128Table[106]); + + /* Write the information into the symbol */ + pattern[i] = row_pattern.toString(); + rowHeight[i] = 15; + } + encodeInfo.append("\n"); + + symbolHeight = rows_needed * 15; + plotSymbol(); + return true; + } + + private Mode findSubset(int letter) { + Mode mode; + + if (letter <= 31) { + mode = Mode.SHIFTA; + } else if ((letter >= 48) && (letter <= 57)) { + mode = Mode.ABORC; + } else if (letter <= 95) { + mode = Mode.AORB; + } else if (letter <= 127) { + mode = Mode.SHIFTB; + } else if (letter <= 159) { + mode = Mode.SHIFTA; + } else if (letter <= 223) { + mode = Mode.AORB; + } else { + mode = Mode.SHIFTB; + } + + return mode; + } + + private boolean data_encode_blockf() { + int i, j, input_position, current_row; + int column_position, c; + cfMode current_mode; + boolean done, exit_status; + + exit_status = false; + current_row = 0; + current_mode = cfMode.MODEA; + column_position = 0; + input_position = 0; + c = 0; + + do { + done = false; + /* 'done' ensures that the instructions are followed in the correct order for each input character */ + + if (column_position == 0) { + /* The Beginning of a row */ + c = columns_needed; + current_mode = character_subset_select(input_position); + subset_selector[current_row] = current_mode; + if ((current_row == 0) && (inputDataType == DataType.GS1)) { + /* Section 4.4.7.1 */ + blockmatrix[current_row][column_position] = 102; /* FNC1 */ + column_position++; + c--; + } + } + + if ((inputDataType == DataType.GS1) && (source[input_position] == '[')) { + blockmatrix[current_row][column_position] = 102; /* FNC1 */ + column_position++; + c--; + input_position++; + done = true; + } + + if (!done) { + if (c <= 2) { + /* Annex B section 1 rule 1 */ + /* Ensure that there is sufficient encodation capacity to continue (using the rules of Annex B.2). */ + switch (current_mode) { + case MODEA: /* Table B1 applies */ + if (findSubset(source[input_position]) == Mode.ABORC) { + blockmatrix[current_row][column_position] = a3_convert(source[input_position]); + column_position++; + c--; + input_position++; + done = true; + } + + if ((findSubset(source[input_position]) == Mode.SHIFTB) && (c == 1)) { + /* Needs two symbols */ + blockmatrix[current_row][column_position] = 100; /* Code B */ + column_position++; + c--; + done = true; + } + + if ((source[input_position] >= 244) && (!done)) { + /* Needs three symbols */ + blockmatrix[current_row][column_position] = 100; /* Code B */ + column_position++; + c--; + if (c == 1) { + blockmatrix[current_row][column_position] = 101; /* Code A */ + column_position++; + c--; + } + done = true; + } + + if ((source[input_position] >= 128) && (!done) && c == 1) { + /* Needs two symbols */ + blockmatrix[current_row][column_position] = 100; /* Code B */ + column_position++; + c--; + done = true; + } + break; + case MODEB: /* Table B2 applies */ + if (findSubset(source[input_position]) == Mode.ABORC) { + blockmatrix[current_row][column_position] = a3_convert(source[input_position]); + column_position++; + c--; + input_position++; + done = true; + } + + if ((findSubset(source[input_position]) == Mode.SHIFTA) && (c == 1)) { + /* Needs two symbols */ + blockmatrix[current_row][column_position] = 101; /* Code A */ + column_position++; + c--; + done = true; + } + + if (((source[input_position] >= 128) + && (source[input_position] <= 159)) && (!done)) { + /* Needs three symbols */ + blockmatrix[current_row][column_position] = 101; /* Code A */ + column_position++; + c--; + if (c == 1) { + blockmatrix[current_row][column_position] = 100; /* Code B */ + column_position++; + c--; + } + done = true; + } + + if ((source[input_position] >= 160) && (!done) && c == 1) { + /* Needs two symbols */ + blockmatrix[current_row][column_position] = 101; /* Code A */ + column_position++; + c--; + done = true; + } + break; + case MODEC: /* Table B3 applies */ + if ((findSubset(source[input_position]) != Mode.ABORC) && (c == 1)) { + /* Needs two symbols */ + blockmatrix[current_row][column_position] = 101; /* Code A */ + column_position++; + c--; + done = true; + } + + if (((findSubset(source[input_position]) == Mode.ABORC) + && (findSubset(source[input_position + 1]) != Mode.ABORC)) + && (c == 1)) { + /* Needs two symbols */ + blockmatrix[current_row][column_position] = 101; /* Code A */ + column_position++; + c--; + done = true; + } + + if (source[input_position] >= 128) { + /* Needs three symbols */ + blockmatrix[current_row][column_position] = 101; /* Code A */ + column_position++; + c--; + if (c == 1) { + blockmatrix[current_row][column_position] = 100; /* Code B */ + column_position++; + c--; + } + } + break; + } + } + } + + if (!done) { + if (((findSubset(source[input_position]) == Mode.AORB) + || (findSubset(source[input_position]) == Mode.SHIFTA)) + && (current_mode == cfMode.MODEA)) { + /* Annex B section 1 rule 2 */ + /* If in Code Subset A and the next data character can be encoded in Subset A encode the next + character. */ + if (source[input_position] >= 128) { + /* Extended ASCII character */ + blockmatrix[current_row][column_position] = 101; /* FNC4 */ + column_position++; + c--; + } + blockmatrix[current_row][column_position] = a3_convert(source[input_position]); + column_position++; + c--; + input_position++; + done = true; + } + } + + if (!done) { + if (((findSubset(source[input_position]) == Mode.AORB) + || (findSubset(source[input_position]) == Mode.SHIFTB)) + && (current_mode == cfMode.MODEB)) { + /* Annex B section 1 rule 3 */ + /* If in Code Subset B and the next data character can be encoded in subset B, encode the next + character. */ + if (source[input_position] >= 128) { + /* Extended ASCII character */ + blockmatrix[current_row][column_position] = 100; /* FNC4 */ + column_position++; + c--; + } + blockmatrix[current_row][column_position] = a3_convert(source[input_position]); + column_position++; + c--; + input_position++; + done = true; + } + } + + if (!done) { + if (((findSubset(source[input_position]) == Mode.ABORC) + && (findSubset(source[input_position + 1]) == Mode.ABORC)) + && (current_mode == cfMode.MODEC)) { + /* Annex B section 1 rule 4 */ + /* If in Code Subset C and the next data are 2 digits, encode them. */ + blockmatrix[current_row][column_position] + = ((source[input_position] - '0') * 10) + + (source[input_position + 1] - '0'); + column_position++; + c--; + input_position += 2; + done = true; + } + } + + if (!done) { + if (((current_mode == cfMode.MODEA) || (current_mode == cfMode.MODEB)) + && ((findSubset(source[input_position]) == Mode.ABORC) + || ((inputDataType == DataType.GS1) && (source[input_position] == '[')))) { + /* Count the number of numeric digits */ + /* If 4 or more numeric data characters occur together when in subsets A or B: + a. If there is an even number of numeric data characters, insert a Code C character before the + first numeric digit to change to subset C. + b. If there is an odd number of numeric data characters, insert a Code Set C character immedi- + ately after the first numeric digit to change to subset C. */ + i = 0; + j = 0; + do { + i++; + if ((inputDataType == DataType.GS1) && (source[input_position + j] == '[')) { + i++; + } + j++; + } while ((findSubset(source[input_position + j]) == Mode.ABORC) + || ((inputDataType == DataType.GS1) && (source[input_position + j] == '['))); + i--; + + if (i >= 4) { + /* Annex B section 1 rule 5 */ + if ((i % 2) == 1) { + /* Annex B section 1 rule 5a */ + blockmatrix[current_row][column_position] = 99; /* Code C */ + column_position++; + c--; + blockmatrix[current_row][column_position] = ((source[input_position] - '0') * 10) + + (source[input_position + 1] - '0'); + column_position++; + c--; + input_position += 2; + current_mode = cfMode.MODEC; + } else { + /* Annex B section 1 rule 5b */ + blockmatrix[current_row][column_position] = a3_convert(source[input_position]); + column_position++; + c--; + input_position++; + } + done = true; + } else { + blockmatrix[current_row][column_position] = a3_convert(source[input_position]); + column_position++; + c--; + input_position++; + done = true; + } + } + } + + if (!done) { + if ((current_mode == cfMode.MODEB) && (findSubset(source[input_position]) == Mode.SHIFTA)) { + /* Annex B section 1 rule 6 */ + /* When in subset B and an ASCII control character occurs in the data: + a. If there is a lower case character immediately following the control character, insert a Shift + character before the control character. + b. Otherwise, insert a Code A character before the control character to change to subset A. */ + if ((source[input_position + 1] >= 96) && (source[input_position + 1] <= 127)) { + /* Annex B section 1 rule 6a */ + blockmatrix[current_row][column_position] = 98; /* Shift */ + column_position++; + c--; + if (source[input_position] >= 128) { + /* Extended ASCII character */ + blockmatrix[current_row][column_position] = 100; /* FNC4 */ + column_position++; + c--; + } + blockmatrix[current_row][column_position] = a3_convert(source[input_position]); + column_position++; + c--; + input_position++; + } else { + /* Annex B section 1 rule 6b */ + blockmatrix[current_row][column_position] = 101; /* Code A */ + column_position++; + c--; + if (source[input_position] >= 128) { + /* Extended ASCII character */ + blockmatrix[current_row][column_position] = 100; /* FNC4 */ + column_position++; + c--; + } + blockmatrix[current_row][column_position] = a3_convert(source[input_position]); + column_position++; + c--; + input_position++; + current_mode = cfMode.MODEA; + } + done = true; + } + } + + if (!done) { + if ((current_mode == cfMode.MODEA) && (findSubset(source[input_position]) == Mode.SHIFTB)) { + /* Annex B section 1 rule 7 */ + /* When in subset A and a lower case character occurs in the data: + a. If following that character, a control character occurs in the data before the occurrence of + another lower case character, insert a Shift character before the lower case character. + b. Otherwise, insert a Code B character before the lower case character to change to subset B. */ + if ((findSubset(source[input_position + 1]) == Mode.SHIFTA) + && (findSubset(source[input_position + 2]) == Mode.SHIFTB)) { + /* Annex B section 1 rule 7a */ + blockmatrix[current_row][column_position] = 98; /* Shift */ + column_position++; + c--; + if (source[input_position] >= 128) { + /* Extended ASCII character */ + blockmatrix[current_row][column_position] = 101; /* FNC4 */ + column_position++; + c--; + } + blockmatrix[current_row][column_position] = a3_convert(source[input_position]); + column_position++; + c--; + input_position++; + } else { + /* Annex B section 1 rule 7b */ + blockmatrix[current_row][column_position] = 100; /* Code B */ + column_position++; + c--; + if (source[input_position] >= 128) { + /* Extended ASCII character */ + blockmatrix[current_row][column_position] = 101; /* FNC4 */ + column_position++; + c--; + } + blockmatrix[current_row][column_position] = a3_convert(source[input_position]); + column_position++; + c--; + input_position++; + current_mode = cfMode.MODEB; + } + done = true; + } + } + + if (!done) { + if ((current_mode == cfMode.MODEC) && ((findSubset(source[input_position]) != Mode.ABORC) + || (findSubset(source[input_position + 1]) != Mode.ABORC))) { + /* Annex B section 1 rule 8 */ + /* When in subset C and a non-numeric character (or a single digit) occurs in the data, insert a Code + A or Code B character before that character, following rules 8a and 8b to determine between code + subsets A and B. + a. If an ASCII control character (eg NUL) occurs in the data before any lower case character, use + Code A. + b. Otherwise use Code B. */ + if (findSubset(source[input_position]) == Mode.SHIFTA) { + /* Annex B section 1 rule 8a */ + blockmatrix[current_row][column_position] = 101; /* Code A */ + column_position++; + c--; + if (source[input_position] >= 128) { + /* Extended ASCII character */ + blockmatrix[current_row][column_position] = 101; /* FNC4 */ + column_position++; + c--; + } + blockmatrix[current_row][column_position] = a3_convert(source[input_position]); + column_position++; + c--; + input_position++; + current_mode = cfMode.MODEA; + } else { + /* Annex B section 1 rule 8b */ + blockmatrix[current_row][column_position] = 100; /* Code B */ + column_position++; + c--; + if (source[input_position] >= 128) { + /* Extended ASCII character */ + blockmatrix[current_row][column_position] = 100; /* FNC4 */ + column_position++; + c--; + } + blockmatrix[current_row][column_position] = a3_convert(source[input_position]); + column_position++; + c--; + input_position++; + current_mode = cfMode.MODEB; + } + } + } + + if (input_position == content.length()) { + /* End of data - Annex B rule 5a */ + if (c == 1) { + if (current_mode == cfMode.MODEA) { + blockmatrix[current_row][column_position] = 100; /* Code B */ + current_mode = cfMode.MODEB; + } else { + blockmatrix[current_row][column_position] = 101; /* Code A */ + current_mode = cfMode.MODEA; + } + column_position++; + c--; + } + + if (c == 0) { + /* Another row is needed */ + column_position = 0; + c = columns_needed; + current_row++; + subset_selector[current_row] = cfMode.MODEA; + current_mode = cfMode.MODEA; + } + + if (c > 2) { + /* Fill up the last row */ + do { + if (current_mode == cfMode.MODEA) { + blockmatrix[current_row][column_position] = 100; /* Code B */ + current_mode = cfMode.MODEB; + } else { + blockmatrix[current_row][column_position] = 101; /* Code A */ + current_mode = cfMode.MODEA; + } + column_position++; + c--; + } while (c > 2); + } + + /* If (c == 2) { do nothing } */ + + exit_status = true; + final_mode = current_mode; + } else { + if (c <= 0) { + /* Start new row - Annex B rule 5b */ + column_position = 0; + current_row++; + if (current_row > 43) { + return false; + } + } + } + + } while (!exit_status); + + if (current_row == 0) { + /* fill up the first row */ + for (c = column_position; c <= columns_needed; c++) { + if (current_mode == cfMode.MODEA) { + blockmatrix[current_row][c] = 100; /* Code B */ + current_mode = cfMode.MODEB; + } else { + blockmatrix[current_row][c] = 101; /* Code A */ + current_mode = cfMode.MODEA; + } + } + current_row++; + /* add a second row */ + subset_selector[current_row] = cfMode.MODEA; + current_mode = cfMode.MODEA; + for (c = 0; c <= columns_needed - 2; c++) { + if (current_mode == cfMode.MODEA) { + blockmatrix[current_row][c] = 100; /* Code B */ + current_mode = cfMode.MODEB; + } else { + blockmatrix[current_row][c] = 101; /* Code A */ + current_mode = cfMode.MODEA; + } + } + } + rows_needed = current_row + 1; + + return true; + } + + private cfMode character_subset_select(int input_position) { + /* Section 4.5.2 - Determining the Character Subset Selector in a Row */ + + if ((source[input_position] >= '0') && (source[input_position + 1] <= '9')) { + /* Rule 1 */ + return cfMode.MODEC; + } + + if ((source[input_position] >= 128) && (source[input_position] <= 160)) { + /* Rule 2 (i) */ + return cfMode.MODEA; + } + + if ((source[input_position] >= 0) && (source[input_position] <= 31)) { + /* Rule 3 */ + return cfMode.MODEA; + } + + /* Rule 4 */ + return cfMode.MODEB; + } + + private int a3_convert(int source) { + /* Annex A section 3 */ + if (source < 32) { + return source + 64; + } + if (source <= 127) { + return source - 32; + } + if (source <= 159) { + return (source - 128) + 64; + } + /* if source >= 160 */ + return (source - 128) - 32; + } + + @Override + protected void plotSymbol() { + int xBlock, yBlock; + int x, y, w, h; + boolean black; + getRectangles().clear(); + y = 1; + h = 1; + for (yBlock = 0; yBlock < rowCount; yBlock++) { + black = true; + x = 0; + for (xBlock = 0; xBlock < pattern[yBlock].length(); xBlock++) { + if (black) { + black = false; + w = pattern[yBlock].charAt(xBlock) - '0'; + if (rowHeight[yBlock] == -1) { + h = defaultHeight; + } else { + h = rowHeight[yBlock]; + } + if (w != 0 && h != 0) { + Rectangle2D.Double rect = new Rectangle2D.Double(x, y, w, h); + getRectangles().add(rect); + } + if ((x + w) > symbolWidth) { + symbolWidth = x + w; + } + } else { + black = true; + } + x += (double) (pattern[yBlock].charAt(xBlock) - '0'); + } + y += h; + if ((y + h) > symbolHeight) { + symbolHeight = y + h; + } + /* Add bars between rows */ + if (yBlock != (rowCount - 1)) { + Rectangle2D.Double rect = new Rectangle2D.Double(11, y - 1, (symbolWidth - 24), 2); + getRectangles().add(rect); + } + } + Rectangle2D.Double top = new Rectangle2D.Double(0, 0, symbolWidth, 2); + getRectangles().add(top); + Rectangle2D.Double bottom = new Rectangle2D.Double(0, y - 1, symbolWidth, 2); + getRectangles().add(bottom); + symbolHeight += 2; + mergeVerticalBlocks(); + } + + private enum Mode { + SHIFTA, LATCHA, SHIFTB, LATCHB, SHIFTC, LATCHC, AORB, ABORC, CANDB, CANDBB + } + + private enum cfMode { + MODEA, MODEB, MODEC + } +} diff --git a/barcode/src/main/java/org/xbib/graphics/barcode/Code11.java b/barcode/src/main/java/org/xbib/graphics/barcode/Code11.java new file mode 100755 index 0000000..b4e3dd1 --- /dev/null +++ b/barcode/src/main/java/org/xbib/graphics/barcode/Code11.java @@ -0,0 +1,208 @@ +package org.xbib.graphics.barcode; + +/** + * Implements Code 11 bar code symbology. + * Code 11 can encode any length string consisting of the digits 0-9 and the + * dash character (-). One or two modulo-11 check digits are calculated. + */ +public class Code11 extends Symbol { + + private static final String[] CODE_11_TABLE = { + "111121", "211121", "121121", "221111", "112121", "212111", + "122111", "111221", "211211", "211111", "112111" + }; + + private static final char[] CHARACTER_SET = { + '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '-' + }; + + /** + * Ratio of wide bar width to narrow bar width. + */ + private double moduleWidthRatio = 2; + + /** + * The number of check digits to calculate ({@code 1} or {@code 2}). + */ + private int checkDigitCount = 2; + + /** + * Optional start delimiter to be shown in the human-readable text. + */ + private Character startDelimiter; + + /** + * Optional stop delimiter to be shown in the human-readable text. + */ + private Character stopDelimiter; + + private static int getCheckDigitC(int[] weight, int length) { + int countC = 0; + int weightC = 1; + for (int i = length - 1; i >= 0; i--) { + countC += (weightC * weight[i]); + weightC++; + if (weightC > 10) { + weightC = 1; + } + } + return countC % 11; + } + + private static int getCheckDigitK(int[] weight, int length) { + int countK = 0; + int weightK = 1; + for (int i = length - 1; i >= 0; i--) { + countK += (weightK * weight[i]); + weightK++; + if (weightK > 9) { + weightK = 1; + } + } + return countK % 11; + } + + /** + * Returns the ratio of wide bar width to narrow bar width. + * + * @return the ratio of wide bar width to narrow bar width + */ + public double getModuleWidthRatio() { + return moduleWidthRatio; + } + + /** + * Sets the ratio of wide bar width to narrow bar width. Valid values are usually + * between {@code 2} and {@code 3}. The default value is {@code 2}. + * + * @param moduleWidthRatio the ratio of wide bar width to narrow bar width + */ + public void setModuleWidthRatio(double moduleWidthRatio) { + this.moduleWidthRatio = moduleWidthRatio; + } + + /** + * Returns the number of check digits to calculate (1 or 2). + * + * @return the number of check digits to calculate + */ + public int getCheckDigitCount() { + return checkDigitCount; + } + + /** + * Sets the number of check digits to calculate ({@code 1} or {@code 2}). The default value is {@code 2}. + * + * @param checkDigitCount the number of check digits to calculate + */ + public void setCheckDigitCount(int checkDigitCount) { + if (checkDigitCount < 1 || checkDigitCount > 2) { + throw new IllegalArgumentException("Check digit count must be 1 or 2."); + } + this.checkDigitCount = checkDigitCount; + } + + /** + * Returns the optional start delimiter to be shown in the human-readable text. + * + * @return the optional start delimiter to be shown in the human-readable text + */ + public Character getStartDelimiter() { + return startDelimiter; + } + + /** + * Sets an optional start delimiter to be shown in the human-readable text (defaults to null). + * + * @param startDelimiter an optional start delimiter to be shown in the human-readable text + */ + public void setStartDelimiter(Character startDelimiter) { + this.startDelimiter = startDelimiter; + } + + /** + * Returns the optional stop delimiter to be shown in the human-readable text. + * + * @return the optional stop delimiter to be shown in the human-readable text + */ + public Character getStopDelimiter() { + return stopDelimiter; + } + + /** + * Sets an optional stop delimiter to be shown in the human-readable text (defaults to null). + * + * @param stopDelimiter an optional stop delimiter to be shown in the human-readable text + */ + public void setStopDelimiter(Character stopDelimiter) { + this.stopDelimiter = stopDelimiter; + } + + /** + * {@inheritDoc} + */ + @Override + public boolean encode() { + + if (!(content.matches("[0-9-]+"))) { + errorMsg.append("Invalid characters in input"); + return false; + } + + StringBuilder horizontalSpacing = new StringBuilder("112211"); + String humanReadable = content; + int length = content.length(); + int[] weight = new int[length + 1]; + + for (int i = 0; i < length; i++) { + char c = content.charAt(i); + weight[i] = positionOf(c, CHARACTER_SET); + horizontalSpacing.append(CODE_11_TABLE[weight[i]]); + } + + int checkDigitC = getCheckDigitC(weight, length); + horizontalSpacing.append(CODE_11_TABLE[checkDigitC]); + humanReadable += CHARACTER_SET[checkDigitC]; + encodeInfo.append("Check Digit C: ").append(checkDigitC).append("\n"); + + if (checkDigitCount == 2) { + weight[length] = checkDigitC; + int checkDigitK = getCheckDigitK(weight, length + 1); + horizontalSpacing.append(CODE_11_TABLE[checkDigitK]); + humanReadable += CHARACTER_SET[checkDigitK]; + encodeInfo.append("Check Digit K: ").append(checkDigitK).append("\n"); + } + + horizontalSpacing.append("112211"); + + readable = new StringBuilder(humanReadable); + if (startDelimiter != null) { + readable = new StringBuilder(startDelimiter).append(readable); + } + if (stopDelimiter != null) { + readable.append(stopDelimiter); + } + + pattern = new String[]{horizontalSpacing.toString()}; + rowCount = 1; + rowHeight = new int[]{-1}; + + plotSymbol(); + + return true; + } + + @Override + protected double getModuleWidth(int originalWidth) { + if (originalWidth == 1) { + return 1; + } else { + return moduleWidthRatio; + } + } + + @Override + protected int[] getCodewords() { + return getPatternAsCodewords(6); + } +} diff --git a/barcode/src/main/java/org/xbib/graphics/barcode/Code128.java b/barcode/src/main/java/org/xbib/graphics/barcode/Code128.java new file mode 100755 index 0000000..3272c45 --- /dev/null +++ b/barcode/src/main/java/org/xbib/graphics/barcode/Code128.java @@ -0,0 +1,843 @@ +package org.xbib.graphics.barcode; + +import java.io.UnsupportedEncodingException; + +/** + * Implements Code 128 bar code symbology according to ISO/IEC 15417:2007. + * Code 128 supports encoding of 8-bit ISO 8859-1 (Latin-1) characters. + * Setting GS1 mode allows encoding in GS1-128 (also known as UPC/EAN-128). + */ +public class Code128 extends Symbol { + private String[] code128Table = { + "212222", "222122", "222221", "121223", "121322", "131222", "122213", + "122312", "132212", "221213", "221312", "231212", "112232", "122132", + "122231", "113222", "123122", "123221", "223211", "221132", "221231", + "213212", "223112", "312131", "311222", "321122", "321221", "312212", + "322112", "322211", "212123", "212321", "232121", "111323", "131123", + "131321", "112313", "132113", "132311", "211313", "231113", "231311", + "112133", "112331", "132131", "113123", "113321", "133121", "313121", + "211331", "231131", "213113", "213311", "213131", "311123", "311321", + "331121", "312113", "312311", "332111", "314111", "221411", "431111", + "111224", "111422", "121124", "121421", "141122", "141221", "112214", + "112412", "122114", "122411", "142112", "142211", "241211", "221114", + "413111", "241112", "134111", "111242", "121142", "121241", "114212", + "124112", "124211", "411212", "421112", "421211", "212141", "214121", + "412121", "111143", "111341", "131141", "114113", "114311", "411113", + "411311", "113141", "114131", "311141", "411131", "211412", "211214", + "211232", "2331112" + }; + private Mode[] mode_type = new Mode[200]; + private int[] mode_length = new int[200]; + private int index_point = 0, read = 0; + private boolean modeCSupression; + private Composite compositeMode; + + public Code128() { + modeCSupression = false; + compositeMode = Composite.OFF; + } + + /** + * Allow the use of subset C (numeric compression) in encoding (default). + */ + public void useModeC() { + modeCSupression = false; + } + + ; + + /** + * Disallow the use of subset C (numeric compression) in encoding. + * Numeric values will be encoded using subset B. + */ + public void stopModeC() { + modeCSupression = true; + } + + protected void setCca() { + compositeMode = Composite.CCA; + } + + protected void setCcb() { + compositeMode = Composite.CCB; + } + + protected void setCcc() { + compositeMode = Composite.CCC; + } + + public void unsetCc() { + compositeMode = Composite.OFF; + } + + @Override + public boolean encode() { + int sourcelen = content.length(); + int i, j, k; + int input_point = 0; + Mode mode, last_mode; + Mode last_set, current_set; + double glyph_count; + int bar_characters = 0, total_sum = 0; + FMode f_state = FMode.LATCHN; + int[] values = new int[200]; + int c; + StringBuilder dest = new StringBuilder(); + int[] inputData; + int c_count; + int linkage_flag = 0; + + if (!content.matches("[\u0000-\u00FF]+")) { + errorMsg.append("Invalid characters in input data"); + return false; + } + + try { + inputBytes = content.getBytes("ISO8859_1"); + } catch (UnsupportedEncodingException e) { + errorMsg.append("Character encoding error"); + return false; + } + + inputData = new int[sourcelen]; + for (i = 0; i < sourcelen; i++) { + inputData[i] = inputBytes[i] & 0xFF; + } + + FMode[] fset = new FMode[200]; + Mode[] set = new Mode[200]; /* set[] = Calculated mode for each character */ + + if (sourcelen > 170) { + errorMsg.append("Input data too long"); + return false; + } + + /* Detect extended ASCII characters */ + for (i = 0; i < sourcelen; i++) { + if (inputData[i] >= 128) { + fset[i] = FMode.SHIFTF; + } else { + fset[i] = FMode.LATCHN; + } + } + + /* Decide when to latch to extended mode - Annex E note 3 */ + j = 0; + for (i = 0; i < sourcelen; i++) { + if (fset[i] == FMode.SHIFTF) { + j++; + } else { + j = 0; + } + + if (j >= 5) { + for (k = i; k > (i - 5); k--) { + fset[k] = FMode.LATCHF; + } + } + + if ((j >= 3) && (i == (sourcelen - 1))) { + for (k = i; k > (i - 3); k--) { + fset[k] = FMode.LATCHF; + } + } + } + + /* Decide if it is worth reverting to 646 encodation for a few characters as described in 4.3.4.2 (d) */ + for (i = 1; i < sourcelen; i++) { + if ((fset[i - 1] == FMode.LATCHF) && (fset[i] == FMode.LATCHN)) { + /* Detected a change from 8859-1 to 646 - count how long for */ + for (j = 0; (fset[i + j] == FMode.LATCHN) && ((i + j) < sourcelen); j++) ; + if ((j < 5) || ((j < 3) && ((i + j) == (sourcelen - 1)))) { + /* Uses the same figures recommended by Annex E note 3 */ + /* Change to shifting back rather than latching back */ + for (k = 0; k < j; k++) { + fset[i + k] = FMode.SHIFTN; + } + } + } + } + + /* Decide on mode using same system as PDF417 and rules of ISO 15417 Annex E */ + mode = findSubset(inputData[input_point]); + mode_type[0] = mode; + mode_length[0] = 1; + + if (inputDataType == DataType.GS1) { + mode = Mode.ABORC; + } + + if ((modeCSupression) && (mode == Mode.ABORC)) { + mode = Mode.AORB; + } + + for (i = 1; i < sourcelen; i++) { + last_mode = mode; + mode = findSubset(inputData[i]); + if ((inputDataType == DataType.GS1) && inputData[i] == '[') { + mode = Mode.ABORC; + } + if ((modeCSupression) && (mode == Mode.ABORC)) { + mode = Mode.AORB; + } + if (mode == last_mode) { + mode_length[index_point]++; + } else { + index_point++; + mode_type[index_point] = mode; + mode_length[index_point] = 1; + } + } + index_point++; + + reduceSubsetChanges(); + + + if (inputDataType == DataType.GS1) { + /* Put set data into set[] */ + read = 0; + for (i = 0; i < index_point; i++) { + for (j = 0; j < mode_length[i]; j++) { + set[read] = mode_type[i]; + read++; + } + } + + /* Resolve odd length LATCHC blocks */ + c_count = 0; + for (i = 0; i < read; i++) { + if (set[i] == Mode.LATCHC) { + if (inputData[i] == '[') { + if ((c_count & 1) != 0) { + if ((i - c_count) != 0) { + set[i - c_count] = Mode.LATCHB; + } else { + set[i - 1] = Mode.LATCHB; + } + } + c_count = 0; + } else { + c_count++; + } + } else { + if ((c_count & 1) != 0) { + if ((i - c_count) != 0) { + set[i - c_count] = Mode.LATCHB; + } else { + set[i - 1] = Mode.LATCHB; + } + } + c_count = 0; + } + } + if ((c_count & 1) != 0) { + if ((i - c_count) != 0) { + set[i - c_count] = Mode.LATCHB; + } else { + set[i - 1] = Mode.LATCHB; + } + } + for (i = 1; i < read - 1; i++) { + if ((set[i] == Mode.LATCHC) && ((set[i - 1] == Mode.LATCHB) + && (set[i + 1] == Mode.LATCHB))) { + set[i] = Mode.LATCHB; + } + } + } else { + /* Resolve odd length LATCHC blocks */ + + if ((mode_type[0] == Mode.LATCHC) && ((mode_length[0] & 1) != 0)) { + /* Rule 2 */ + mode_length[1]++; + mode_length[0]--; + if (index_point == 1) { + mode_length[1] = 1; + mode_type[1] = Mode.LATCHB; + index_point = 2; + } + } + if (index_point > 1) { + for (i = 1; i < index_point; i++) { + if ((mode_type[i] == Mode.LATCHC) && ((mode_length[i] & 1) != 0)) { + /* Rule 3b */ + mode_length[i - 1]++; + mode_length[i]--; + } + } + } + + /* Put set data into set[] */ + for (i = 0; i < index_point; i++) { + for (j = 0; j < mode_length[i]; j++) { + set[read] = mode_type[i]; + read++; + } + } + } + + /* Adjust for strings which start with shift characters - make them latch instead */ + if (set[0] == Mode.SHIFTA) { + i = 0; + do { + set[i] = Mode.LATCHA; + i++; + } while (set[i] == Mode.SHIFTA); + } + + if (set[0] == Mode.SHIFTB) { + i = 0; + do { + set[i] = Mode.LATCHB; + i++; + } while (set[i] == Mode.SHIFTB); + } + + /* Now we can calculate how long the barcode is going to be - and stop it from + being too long */ + last_set = Mode.NULL; + glyph_count = 0.0; + for (i = 0; i < sourcelen; i++) { + if ((set[i] == Mode.SHIFTA) || (set[i] == Mode.SHIFTB)) { + glyph_count += 1.0; + } + if ((fset[i] == FMode.SHIFTF) || (fset[i] == FMode.SHIFTN)) { + glyph_count += 1.0; + } + if (((set[i] == Mode.LATCHA) || (set[i] == Mode.LATCHB)) || (set[i] == Mode.LATCHC)) { + if (set[i] != last_set) { + last_set = set[i]; + glyph_count += 1.0; + } + } + if (i == 0) { + if (fset[i] == FMode.LATCHF) { + glyph_count += 2.0; + } + } else { + if ((fset[i] == FMode.LATCHF) && (fset[i - 1] != FMode.LATCHF)) { + glyph_count += 2.0; + } + if ((fset[i] != FMode.LATCHF) && (fset[i - 1] == FMode.LATCHF)) { + glyph_count += 2.0; + } + } + + if (set[i] == Mode.LATCHC) { + if ((inputDataType == DataType.GS1) && (inputData[i] == '[')) { + glyph_count += 1.0; + } else { + glyph_count += 0.5; + } + } else { + glyph_count += 1.0; + } + } + if (glyph_count > 80.0) { + errorMsg.append("Input data too long"); + return false; + } + + encodeInfo.append("Encoding: "); + + /* So now we know what start character to use - we can get on with it! */ + if (readerInit) { + /* Reader Initialisation mode */ + switch (set[0]) { + case LATCHA: /* Start A */ + dest.append(code128Table[103]); + values[0] = 103; + current_set = Mode.LATCHA; + dest.append(code128Table[96]); /* FNC3 */ + values[1] = 96; + bar_characters++; + encodeInfo.append("STARTA FNC3 "); + break; + case LATCHB: /* Start B */ + dest.append(code128Table[104]); + values[0] = 104; + current_set = Mode.LATCHB; + dest.append(code128Table[96]); /* FNC3 */ + values[1] = 96; + bar_characters++; + encodeInfo.append("STARTB FNC3 "); + break; + default: /* Start C */ + dest.append(code128Table[104]); /* Start B */ + values[0] = 105; + dest.append(code128Table[96]); /* FNC3 */ + values[1] = 96; + dest.append(code128Table[99]); /* Code C */ + values[2] = 99; + bar_characters += 2; + current_set = Mode.LATCHC; + encodeInfo.append("STARTB FNC3 CODEC "); + break; + } + } else { + /* Normal mode */ + switch (set[0]) { + case LATCHA: + /* Start A */ + dest.append(code128Table[103]); + values[0] = 103; + current_set = Mode.LATCHA; + encodeInfo.append("STARTA "); + break; + case LATCHB: + /* Start B */ + dest.append(code128Table[104]); + values[0] = 104; + current_set = Mode.LATCHB; + encodeInfo.append("STARTB "); + break; + default: + /* Start C */ + dest.append(code128Table[105]); + values[0] = 105; + current_set = Mode.LATCHC; + encodeInfo.append("STARTC "); + break; + } + } + bar_characters++; + + if (inputDataType == DataType.GS1) { + dest.append(code128Table[102]); + values[1] = 102; + bar_characters++; + encodeInfo.append("FNC1 "); + } + + if (fset[0] == FMode.LATCHF) { + switch (current_set) { + case LATCHA: + dest.append(code128Table[101]); + dest.append(code128Table[101]); + values[bar_characters] = 101; + values[bar_characters + 1] = 101; + encodeInfo.append("FNC4 FNC4 "); + break; + case LATCHB: + dest.append(code128Table[100]); + dest.append(code128Table[100]); + values[bar_characters] = 100; + values[bar_characters + 1] = 100; + encodeInfo.append("FNC4 FNC4 "); + break; + } + bar_characters += 2; + f_state = FMode.LATCHF; + } + + /* Encode the data */ + read = 0; + do { + + if ((read != 0) && (set[read] != current_set)) { /* Latch different code set */ + switch (set[read]) { + case LATCHA: + dest.append(code128Table[101]); + values[bar_characters] = 101; + bar_characters++; + current_set = Mode.LATCHA; + encodeInfo.append("CODEA "); + break; + case LATCHB: + dest.append(code128Table[100]); + values[bar_characters] = 100; + bar_characters++; + current_set = Mode.LATCHB; + encodeInfo.append("CODEB "); + break; + case LATCHC: + dest.append(code128Table[99]); + values[bar_characters] = 99; + bar_characters++; + current_set = Mode.LATCHC; + encodeInfo.append("CODEC "); + break; + } + } + + if (read != 0) { + if ((fset[read] == FMode.LATCHF) && (f_state == FMode.LATCHN)) { + /* Latch beginning of extended mode */ + switch (current_set) { + case LATCHA: + dest.append(code128Table[101]); + dest.append(code128Table[101]); + values[bar_characters] = 101; + values[bar_characters + 1] = 101; + encodeInfo.append("FNC4 FNC4 "); + break; + case LATCHB: + dest.append(code128Table[100]); + dest.append(code128Table[100]); + values[bar_characters] = 100; + values[bar_characters + 1] = 100; + encodeInfo.append("FNC4 FNC4 "); + break; + } + bar_characters += 2; + f_state = FMode.LATCHN; + } + if ((fset[read] == FMode.LATCHN) && (f_state == FMode.LATCHF)) { + /* Latch end of extended mode */ + switch (current_set) { + case LATCHA: + dest.append(code128Table[101]); + dest.append(code128Table[101]); + values[bar_characters] = 101; + values[bar_characters + 1] = 101; + encodeInfo.append("FNC4 FNC4 "); + break; + case LATCHB: + dest.append(code128Table[100]); + dest.append(code128Table[100]); + values[bar_characters] = 100; + values[bar_characters + 1] = 100; + encodeInfo.append("FNC4 FNC4 "); + break; + } + bar_characters += 2; + f_state = FMode.LATCHN; + } + } + + if ((fset[read] == FMode.SHIFTF) || (fset[read] == FMode.SHIFTN)) { + /* Shift to or from extended mode */ + switch (current_set) { + case LATCHA: + dest.append(code128Table[101]); /* FNC 4 */ + values[bar_characters] = 101; + encodeInfo.append("FNC4 "); + break; + case LATCHB: + dest.append(code128Table[100]); /* FNC 4 */ + values[bar_characters] = 100; + encodeInfo.append("FNC4 "); + break; + } + bar_characters++; + } + + if ((set[read] == Mode.SHIFTA) || (set[read] == Mode.SHIFTB)) { + /* Insert shift character */ + dest.append(code128Table[98]); + values[bar_characters] = 98; + encodeInfo.append("SHFT "); + bar_characters++; + } + + if (!((inputDataType == DataType.GS1) && (inputData[read] == '['))) { + /* Encode data characters */ + c = inputData[read]; + switch (set[read]) { + case SHIFTA: + case LATCHA: + if (c > 127) { + if (c < 160) { + dest.append(code128Table[(c - 128) + 64]); + values[bar_characters] = (c - 128) + 64; + } else { + dest.append(code128Table[(c - 128) - 32]); + values[bar_characters] = (c - 128) - 32; + } + } else { + if (c < 32) { + dest.append(code128Table[c + 64]); + values[bar_characters] = c + 64; + } else { + dest.append(code128Table[c - 32]); + values[bar_characters] = c - 32; + } + } + encodeInfo.append(Integer.toString(values[bar_characters])).append(" "); + bar_characters++; + read++; + break; + case SHIFTB: + case LATCHB: + if (c > 127) { + dest.append(code128Table[c - 32 - 128]); + values[bar_characters] = c - 32 - 128; + } else { + dest.append(code128Table[c - 32]); + values[bar_characters] = c - 32; + } + encodeInfo.append(Integer.toString(values[bar_characters])).append(" "); + bar_characters++; + read++; + break; + case LATCHC: + int weight; + int d = inputData[read + 1]; + + weight = (10 * (c - '0')) + (d - '0'); + dest.append(code128Table[weight]); + values[bar_characters] = weight; + encodeInfo.append(Integer.toString(values[bar_characters])).append(" "); + bar_characters++; + read += 2; + break; + } + } else { + // FNC1 + dest.append(code128Table[102]); + values[bar_characters] = 102; + bar_characters++; + read++; + encodeInfo.append("FNC1 "); + } + + } while (read < sourcelen); + + encodeInfo.append("\n"); + + /* "...note that the linkage flag is an extra code set character between + the last data character and the Symbol Check Character" (GS1 Specification) */ + + /* Linkage flags in GS1-128 are determined by ISO/IEC 24723 section 7.4 */ + + switch (compositeMode) { + case CCA: + case CCB: + /* CC-A or CC-B 2D component */ + switch (set[sourcelen - 1]) { + case LATCHA: + linkage_flag = 100; + break; + case LATCHB: + linkage_flag = 99; + break; + case LATCHC: + linkage_flag = 101; + break; + } + encodeInfo.append("Linkage flag: ").append(linkage_flag).append('\n'); + break; + case CCC: + /* CC-C 2D component */ + switch (set[sourcelen - 1]) { + case LATCHA: + linkage_flag = 99; + break; + case LATCHB: + linkage_flag = 101; + break; + case LATCHC: + linkage_flag = 100; + break; + } + encodeInfo.append("Linkage flag: ").append(linkage_flag).append('\n'); + break; + default: + break; + } + + if (linkage_flag != 0) { + dest.append(code128Table[linkage_flag]); + values[bar_characters] = linkage_flag; + bar_characters++; + } + + /* check digit calculation */ + for (i = 0; i < bar_characters; i++) { + if (i > 0) { + values[i] *= i; + } + total_sum += values[i]; + } + dest.append(code128Table[total_sum % 103]); + encodeInfo.append("Data Codewords: ").append(bar_characters).append('\n'); + encodeInfo.append("Check Digit: ").append(total_sum % 103).append('\n'); + + /* Stop character */ + dest.append(code128Table[106]); + + if (!(inputDataType == DataType.GS1)) { + readable = new StringBuilder(content); + } + + if (inputDataType == DataType.HIBC) { + readable.append("*").append(content).append("*"); + } + + if (compositeMode == Composite.OFF) { + pattern = new String[1]; + pattern[0] = dest.toString(); + rowCount = 1; + rowHeight = new int[1]; + rowHeight[0] = -1; + } else { + /* Add the separator pattern for composite symbols */ + pattern = new String[2]; + pattern[0] = "0" + dest; + pattern[1] = dest.toString(); + rowCount = 2; + rowHeight = new int[2]; + rowHeight[0] = 1; + rowHeight[1] = -1; + } + plotSymbol(); + return true; + } + + private Mode findSubset(int letter) { + Mode mode; + + if (letter <= 31) { + mode = Mode.SHIFTA; + } else if ((letter >= 48) && (letter <= 57)) { + mode = Mode.ABORC; + } else if (letter <= 95) { + mode = Mode.AORB; + } else if (letter <= 127) { + mode = Mode.SHIFTB; + } else if (letter <= 159) { + mode = Mode.SHIFTA; + } else if (letter <= 223) { + mode = Mode.AORB; + } else { + mode = Mode.SHIFTB; + } + + return mode; + } + + private void reduceSubsetChanges() { /* Implements rules from ISO 15417 Annex E */ + int i, length; + Mode current, last, next; + + for (i = 0; i < index_point; i++) { + current = mode_type[i]; + length = mode_length[i]; + if (i != 0) { + last = mode_type[i - 1]; + } else { + last = Mode.NULL; + } + if (i != index_point - 1) { + next = mode_type[i + 1]; + } else { + next = Mode.NULL; + } + + if (i == 0) { /* first block */ + if ((index_point == 1) && ((length == 2) && (current == Mode.ABORC))) { /* Rule 1a */ + mode_type[i] = Mode.LATCHC; + } + if (current == Mode.ABORC) { + if (length >= 4) { /* Rule 1b */ + mode_type[i] = Mode.LATCHC; + } else { + mode_type[i] = Mode.AORB; + current = Mode.AORB; + } + } + if (current == Mode.SHIFTA) { /* Rule 1c */ + mode_type[i] = Mode.LATCHA; + } + if ((current == Mode.AORB) && (next == Mode.SHIFTA)) { /* Rule 1c */ + mode_type[i] = Mode.LATCHA; + current = Mode.LATCHA; + } + if (current == Mode.AORB) { /* Rule 1d */ + mode_type[i] = Mode.LATCHB; + } + } else { + if ((current == Mode.ABORC) && (length >= 4)) { /* Rule 3 */ + mode_type[i] = Mode.LATCHC; + current = Mode.LATCHC; + } + if (current == Mode.ABORC) { + mode_type[i] = Mode.AORB; + current = Mode.AORB; + } + if ((current == Mode.AORB) && (last == Mode.LATCHA)) { + mode_type[i] = Mode.LATCHA; + current = Mode.LATCHA; + } + if ((current == Mode.AORB) && (last == Mode.LATCHB)) { + mode_type[i] = Mode.LATCHB; + current = Mode.LATCHB; + } + if ((current == Mode.AORB) && (next == Mode.SHIFTA)) { + mode_type[i] = Mode.LATCHA; + current = Mode.LATCHA; + } + if ((current == Mode.AORB) && (next == Mode.SHIFTB)) { + mode_type[i] = Mode.LATCHB; + current = Mode.LATCHB; + } + if (current == Mode.AORB) { + mode_type[i] = Mode.LATCHB; + current = Mode.LATCHB; + } + if ((current == Mode.SHIFTA) && (length > 1)) { /* Rule 4 */ + mode_type[i] = Mode.LATCHA; + current = Mode.LATCHA; + } + if ((current == Mode.SHIFTB) && (length > 1)) { /* Rule 5 */ + mode_type[i] = Mode.LATCHB; + current = Mode.LATCHB; + } + if ((current == Mode.SHIFTA) && (last == Mode.LATCHA)) { + mode_type[i] = Mode.LATCHA; + current = Mode.LATCHA; + } + if ((current == Mode.SHIFTB) && (last == Mode.LATCHB)) { + mode_type[i] = Mode.LATCHB; + current = Mode.LATCHB; + } + if ((current == Mode.SHIFTA) && (last == Mode.LATCHC)) { + mode_type[i] = Mode.LATCHA; + current = Mode.LATCHA; + } + if ((current == Mode.SHIFTB) && (last == Mode.LATCHC)) { + mode_type[i] = Mode.LATCHB; + current = Mode.LATCHB; + } + } /* Rule 2 is implemented elsewhere, Rule 6 is implied */ + } + + combineSubsetBlocks(); + + } + + private void combineSubsetBlocks() { + int i, j; + + /* bring together same type blocks */ + if (index_point > 1) { + i = 1; + while (i < index_point) { + if (mode_type[i - 1] == mode_type[i]) { + /* bring together */ + mode_length[i - 1] = mode_length[i - 1] + mode_length[i]; + j = i + 1; + + /* decreace the list */ + while (j < index_point) { + mode_length[j - 1] = mode_length[j]; + mode_type[j - 1] = mode_type[j]; + j++; + } + index_point--; + i--; + } + i++; + } + } + } + + private enum Mode { + NULL, SHIFTA, LATCHA, SHIFTB, LATCHB, SHIFTC, LATCHC, AORB, ABORC + } + + private enum FMode { + SHIFTN, LATCHN, SHIFTF, LATCHF + } + + private enum Composite {OFF, CCA, CCB, CCC} +} diff --git a/barcode/src/main/java/org/xbib/graphics/barcode/Code16k.java b/barcode/src/main/java/org/xbib/graphics/barcode/Code16k.java new file mode 100755 index 0000000..eb2ae61 --- /dev/null +++ b/barcode/src/main/java/org/xbib/graphics/barcode/Code16k.java @@ -0,0 +1,769 @@ +package org.xbib.graphics.barcode; + +import java.awt.geom.Rectangle2D; +import java.io.UnsupportedEncodingException; + +/** + * Implements Code 16K symbology + * According to BS EN 12323:2005 + * Encodes using a stacked symbology based on Code 128. Supports encoding + * of any 8-bit ISO 8859-1 (Latin-1) data with a maximum data capacity of 77 + * alpha-numeric characters or 154 numerical digits. + */ +public class Code16k extends Symbol { + + /* EN 12323 Table 1 - "Code 16K" character encodations */ + private static final String[] C_16_K_TABLE = { + "212222", "222122", "222221", "121223", "121322", "131222", "122213", + "122312", "132212", "221213", "221312", "231212", "112232", "122132", + "122231", "113222", "123122", "123221", "223211", "221132", "221231", + "213212", "223112", "312131", "311222", "321122", "321221", "312212", + "322112", "322211", "212123", "212321", "232121", "111323", "131123", + "131321", "112313", "132113", "132311", "211313", "231113", "231311", + "112133", "112331", "132131", "113123", "113321", "133121", "313121", + "211331", "231131", "213113", "213311", "213131", "311123", "311321", + "331121", "312113", "312311", "332111", "314111", "221411", "431111", + "111224", "111422", "121124", "121421", "141122", "141221", "112214", + "112412", "122114", "122411", "142112", "142211", "241211", "221114", + "413111", "241112", "134111", "111242", "121142", "121241", "114212", + "124112", "124211", "411212", "421112", "421211", "212141", "214121", + "412121", "111143", "111341", "131141", "114113", "114311", "411113", + "411311", "113141", "114131", "311141", "411131", "211412", "211214", + "211232", "211133" + }; + /* EN 12323 Table 3 and Table 4 - Start patterns and stop patterns */ + private static final String[] C_16_K_START_STOP = { + "3211", "2221", "2122", "1411", "1132", "1231", "1114", "3112" + }; + /* EN 12323 Table 5 - Start and stop values defining row numbers */ + private static final int[] C_16_K_START_VALUES = { + 0, 1, 2, 3, 4, 5, 6, 7, 0, 1, 2, 3, 4, 5, 6, 7 + }; + private static final int[] C_16_K_STOP_VALUES = { + 0, 1, 2, 3, 4, 5, 6, 7, 4, 5, 6, 7, 0, 1, 2, 3 + }; + private final Mode[] block_mode = new Mode[170]; /* RENAME block_mode */ + private final int[] block_length = new int[170]; /* RENAME block_length */ + + @Override + public boolean encode() { + StringBuilder width_pattern; + int current_row, rows_needed, first_check, second_check; + int indexchaine, pads_needed; + char[] set, fset; + Mode mode; + char last_set, current_set; + int i, j, k, m, read; + int[] values; + int bar_characters; + double glyph_count; + int first_sum, second_sum; + int input_length; + int c_count; + boolean f_state; + int[] inputData; + + if (!content.matches("[\u0000-\u00FF]+")) { + errorMsg.append("Invalid characters in input data"); + return false; + } + + try { + inputBytes = content.getBytes("ISO8859_1"); + } catch (UnsupportedEncodingException e) { + errorMsg.append("Character encoding error"); + return false; + } + + input_length = content.length(); + inputData = new int[input_length]; + for (i = 0; i < input_length; i++) { + inputData[i] = inputBytes[i] & 0xFF; + } + + bar_characters = 0; + set = new char[160]; + fset = new char[160]; + values = new int[160]; + + if (input_length > 157) { + errorMsg.append("Input too long"); + return false; + } + + /* Detect extended ASCII characters */ + for (i = 0; i < input_length; i++) { + if (inputData[i] >= 128) { + fset[i] = 'f'; + } else { + fset[i] = ' '; + } + } + + /* Decide when to latch to extended mode */ + for (i = 0; i < input_length; i++) { + j = 0; + if (fset[i] == 'f') { + do { + j++; + } while (fset[i + j] == 'f'); + if ((j >= 5) || ((j >= 3) && ((i + j) == (input_length - 1)))) { + for (k = 0; k <= j; k++) { + fset[i + k] = 'F'; + } + } + } + } + + /* Decide if it is worth reverting to 646 encodation for a few characters */ + if (input_length > 1) { + for (i = 1; i < input_length; i++) { + if ((fset[i - 1] == 'F') && (fset[i] == ' ')) { + /* Detected a change from 8859-1 to 646 - count how long for */ + for (j = 0; (fset[i + j] == ' ') && ((i + j) < input_length); j++) + ; + if (j < 5) { + /* Change to shifting back rather than latching back */ + for (k = 0; k < j; k++) { + fset[i + k] = 'n'; + } + } + } + } + } + + /* Detect mode A, B and C characters */ + int block_count = 0; + indexchaine = 0; + + mode = findSubset(inputData[indexchaine]); + if ((inputDataType == DataType.GS1) && (inputData[indexchaine] == '[')) { + mode = Mode.ABORC; + } /* FNC1 */ + + for (i = 0; i < 160; i++) { + block_length[i] = 0; + } + + do { + block_mode[block_count] = mode; + while ((block_mode[block_count] == mode) && (indexchaine < input_length)) { + block_length[block_count]++; + indexchaine++; + if (indexchaine < input_length) { + mode = findSubset(inputData[indexchaine]); + if ((inputDataType == DataType.GS1) && (inputData[indexchaine] == '[')) { + mode = Mode.ABORC; + } /* FNC1 */ + } + } + block_count++; + } while (indexchaine < input_length); + + reduceSubsetChanges(block_count); + + + /* Put set data into set[] */ + read = 0; + for (i = 0; i < block_count; i++) { + for (j = 0; j < block_length[i]; j++) { + switch (block_mode[i]) { + case SHIFTA: + set[read] = 'a'; + break; + case LATCHA: + set[read] = 'A'; + break; + case SHIFTB: + set[read] = 'b'; + break; + case LATCHB: + set[read] = 'B'; + break; + case LATCHC: + set[read] = 'C'; + break; + } + read++; + } + } + + /* Adjust for strings which start with shift characters - make them latch instead */ + if (set[0] == 'a') { + i = 0; + do { + set[i] = 'A'; + i++; + } while (set[i] == 'a'); + } + + if (set[0] == 'b') { + i = 0; + do { + set[i] = 'B'; + i++; + } while (set[i] == 'b'); + } + + /* Watch out for odd-length Mode C blocks */ + c_count = 0; + for (i = 0; i < read; i++) { + if (set[i] == 'C') { + if (inputData[i] == '[') { + if ((c_count & 1) != 0) { + if ((i - c_count) != 0) { + set[i - c_count] = 'B'; + } else { + set[i - 1] = 'B'; + } + } + c_count = 0; + } else { + c_count++; + } + } else { + if ((c_count & 1) != 0) { + if ((i - c_count) != 0) { + set[i - c_count] = 'B'; + } else { + set[i - 1] = 'B'; + } + } + c_count = 0; + } + } + if ((c_count & 1) != 0) { + if ((i - c_count) != 0) { + set[i - c_count] = 'B'; + } else { + set[i - 1] = 'B'; + } + } + for (i = 1; i < read - 1; i++) { + if ((set[i] == 'C') && ((set[i - 1] == 'B') && (set[i + 1] == 'B'))) { + set[i] = 'B'; + } + } + + /* Make sure the data will fit in the symbol */ + last_set = ' '; + glyph_count = 0.0; + for (i = 0; i < input_length; i++) { + if ((set[i] == 'a') || (set[i] == 'b')) { + glyph_count = glyph_count + 1.0; + } + if ((fset[i] == 'f') || (fset[i] == 'n')) { + glyph_count = glyph_count + 1.0; + } + if (((set[i] == 'A') || (set[i] == 'B')) || (set[i] == 'C')) { + if (set[i] != last_set) { + last_set = set[i]; + glyph_count = glyph_count + 1.0; + } + } + if (i == 0) { + if ((set[i] == 'B') && (set[1] == 'C')) { + glyph_count = glyph_count - 1.0; + } + if ((set[i] == 'B') && (set[1] == 'B') && set[2] == 'C') { + glyph_count = glyph_count - 1.0; + } + if (fset[i] == 'F') { + glyph_count = glyph_count + 2.0; + } + } else { + if ((fset[i] == 'F') && (fset[i - 1] != 'F')) { + glyph_count = glyph_count + 2.0; + } + if ((fset[i] != 'F') && (fset[i - 1] == 'F')) { + glyph_count = glyph_count + 2.0; + } + } + + if ((set[i] == 'C') && (!((inputDataType == DataType.GS1) && (content.charAt(i) == '[')))) { + glyph_count = glyph_count + 0.5; + } else { + glyph_count = glyph_count + 1.0; + } + } + + if ((inputDataType == DataType.GS1) && (set[0] != 'A')) { + /* FNC1 can be integrated with mode character */ + glyph_count--; + } + + if (glyph_count > 77.0) { + errorMsg.append("Input too long"); + return false; + } + + /* Calculate how tall the symbol will be */ + glyph_count = glyph_count + 2.0; + i = (int) glyph_count; + rows_needed = (i / 5); + if (i % 5 > 0) { + rows_needed++; + } + + if (rows_needed == 1) { + rows_needed = 2; + } + + /* start with the mode character - Table 2 */ + m = 0; + switch (set[0]) { + case 'A': + m = 0; + break; + case 'B': + m = 1; + break; + case 'C': + m = 2; + break; + } + + if (readerInit) { + if (m == 2) { + m = 5; + } + if (inputDataType == DataType.GS1) { + errorMsg.append("Cannot use both GS1 mode and Reader Initialisation"); + return false; + } else { + if ((set[0] == 'B') && (set[1] == 'C')) { + m = 6; + } + } + values[bar_characters] = (7 * (rows_needed - 2)) + m; /* see 4.3.4.2 */ + values[bar_characters + 1] = 96; /* FNC3 */ + bar_characters += 2; + } else { + if (inputDataType == DataType.GS1) { + /* Integrate FNC1 */ + switch (set[0]) { + case 'B': + m = 3; + break; + case 'C': + m = 4; + break; + } + } else { + if ((set[0] == 'B') && (set[1] == 'C')) { + m = 5; + } + if (((set[0] == 'B') && (set[1] == 'B')) && (set[2] == 'C')) { + m = 6; + } + } + } + values[bar_characters] = (7 * (rows_needed - 2)) + m; /* see 4.3.4.2 */ + bar_characters++; + //} + current_set = set[0]; + f_state = false; + /* f_state remembers if we are in Extended ASCII mode (value 1) or + in ISO/IEC 646 mode (value 0) */ + if (fset[0] == 'F') { + switch (current_set) { + case 'A': + values[bar_characters] = 101; + values[bar_characters + 1] = 101; + break; + case 'B': + values[bar_characters] = 100; + values[bar_characters + 1] = 100; + break; + } + bar_characters += 2; + f_state = true; + } + + read = 0; + + /* Encode the data */ + do { + + if ((read != 0) && (set[read] != set[read - 1])) { /* Latch different code set */ + switch (set[read]) { + case 'A': + values[bar_characters] = 101; + bar_characters++; + current_set = 'A'; + break; + case 'B': + values[bar_characters] = 100; + bar_characters++; + current_set = 'B'; + break; + case 'C': + if (!((read == 1) && (set[0] == 'B'))) { /* Not Mode C/Shift B */ + if (!((read == 2) && ((set[0] == 'B') && (set[1] == 'B')))) { + /* Not Mode C/Double Shift B */ + values[bar_characters] = 99; + bar_characters++; + } + } + current_set = 'C'; + break; + } + } + if (read != 0) { + if ((fset[read] == 'F') && !f_state) { + /* Latch beginning of extended mode */ + switch (current_set) { + case 'A': + values[bar_characters] = 101; + values[bar_characters + 1] = 101; + break; + case 'B': + values[bar_characters] = 100; + values[bar_characters + 1] = 100; + break; + } + bar_characters += 2; + f_state = true; + } + if ((fset[read] == ' ') && f_state) { + /* Latch end of extended mode */ + switch (current_set) { + case 'A': + values[bar_characters] = 101; + values[bar_characters + 1] = 101; + break; + case 'B': + values[bar_characters] = 100; + values[bar_characters + 1] = 100; + break; + } + bar_characters += 2; + f_state = false; + } + } + + if ((fset[i] == 'f') || (fset[i] == 'n')) { + /* Shift extended mode */ + switch (current_set) { + case 'A': + values[bar_characters] = 101; /* FNC 4 */ + break; + case 'B': + values[bar_characters] = 100; /* FNC 4 */ + break; + } + bar_characters++; + } + + if ((set[i] == 'a') || (set[i] == 'b')) { + /* Insert shift character */ + values[bar_characters] = 98; + bar_characters++; + } + + if (!((inputDataType == DataType.GS1) && (inputData[read] == '['))) { + switch (set[read]) { /* Encode data characters */ + case 'A': + case 'a': + getValueSubsetA(inputData[read], values, bar_characters); + bar_characters++; + read++; + break; + case 'B': + case 'b': + getValueSubsetB(inputData[read], values, bar_characters); + bar_characters++; + read++; + break; + case 'C': + getValueSubsetC(inputData[read], inputData[read + 1], values, bar_characters); + bar_characters++; + read += 2; + break; + } + } else { + values[bar_characters] = 102; + bar_characters++; + read++; + } + + } while (read < input_length); + + pads_needed = 5 - ((bar_characters + 2) % 5); + if (pads_needed == 5) { + pads_needed = 0; + } + if ((bar_characters + pads_needed) < 8) { + pads_needed += 8 - (bar_characters + pads_needed); + } + for (i = 0; i < pads_needed; i++) { + values[bar_characters] = 106; + bar_characters++; + } + + /* Calculate check digits */ + first_sum = 0; + second_sum = 0; + for (i = 0; i < bar_characters; i++) { + first_sum += (i + 2) * values[i]; + second_sum += (i + 1) * values[i]; + } + first_check = first_sum % 107; + second_sum += first_check * (bar_characters + 1); + second_check = second_sum % 107; + values[bar_characters] = first_check; + values[bar_characters + 1] = second_check; + readable = new StringBuilder(); + pattern = new String[rows_needed]; + rowCount = rows_needed; + rowHeight = new int[rows_needed]; + encodeInfo.append("Symbol Rows: ").append(rows_needed).append("\n"); + encodeInfo.append("First Check Digit: ").append(first_check).append("\n"); + encodeInfo.append("Second Check Digit: ").append(second_check).append("\n"); + encodeInfo.append("Codewords: "); + for (current_row = 0; current_row < rows_needed; current_row++) { + width_pattern = new StringBuilder(); + width_pattern.append(C_16_K_START_STOP[C_16_K_START_VALUES[current_row]]); + width_pattern.append("1"); + for (i = 0; i < 5; i++) { + width_pattern.append(C_16_K_TABLE[values[(current_row * 5) + i]]); + encodeInfo.append(values[(current_row * 5) + i]).append(" "); + } + width_pattern.append(C_16_K_START_STOP[C_16_K_STOP_VALUES[current_row]]); + + pattern[current_row] = width_pattern.toString(); + rowHeight[current_row] = 10; + } + encodeInfo.append("\n"); + plotSymbol(); + return true; + } + + private void getValueSubsetA(int source, int[] values, int bar_chars) { + if (source > 127) { + if (source < 160) { + values[bar_chars] = source + 64 - 128; + } else { + values[bar_chars] = source - 32 - 128; + } + } else { + if (source < 32) { + values[bar_chars] = source + 64; + } else { + values[bar_chars] = source - 32; + } + } + } + + private void getValueSubsetB(int source, int[] values, int bar_chars) { + if (source > 127) { + values[bar_chars] = source - 32 - 128; + } else { + values[bar_chars] = source - 32; + } + } + + private void getValueSubsetC(int source_a, int source_b, int[] values, int bar_chars) { + int weight; + + weight = (10 * Character.getNumericValue(source_a)) + Character.getNumericValue(source_b); + values[bar_chars] = weight; + } + + private Mode findSubset(int letter) { + Mode mode; + + if (letter <= 31) { + mode = Mode.SHIFTA; + } else if ((letter >= 48) && (letter <= 57)) { + mode = Mode.ABORC; + } else if (letter <= 95) { + mode = Mode.AORB; + } else if (letter <= 127) { + mode = Mode.SHIFTB; + } else if (letter <= 159) { + mode = Mode.SHIFTA; + } else if (letter <= 223) { + mode = Mode.AORB; + } else { + mode = Mode.SHIFTB; + } + + return mode; + } + + private void reduceSubsetChanges(int block_count) { /* Implements rules from ISO 15417 Annex E */ + int i, length; + Mode current, last, next; + + for (i = 0; i < block_count; i++) { + current = block_mode[i]; + length = block_length[i]; + if (i != 0) { + last = block_mode[i - 1]; + } else { + last = Mode.NULL; + } + if (i != block_count - 1) { + next = block_mode[i + 1]; + } else { + next = Mode.NULL; + } + + if (i == 0) { /* first block */ + if ((block_count == 1) && ((length == 2) && (current == Mode.ABORC))) { /* Rule 1a */ + block_mode[i] = Mode.LATCHC; + } + if (current == Mode.ABORC) { + if (length >= 4) { /* Rule 1b */ + block_mode[i] = Mode.LATCHC; + } else { + block_mode[i] = Mode.AORB; + current = Mode.AORB; + } + } + if (current == Mode.SHIFTA) { /* Rule 1c */ + block_mode[i] = Mode.LATCHA; + } + if ((current == Mode.AORB) && (next == Mode.SHIFTA)) { /* Rule 1c */ + block_mode[i] = Mode.LATCHA; + current = Mode.LATCHA; + } + if (current == Mode.AORB) { /* Rule 1d */ + block_mode[i] = Mode.LATCHB; + } + } else { + if ((current == Mode.ABORC) && (length >= 4)) { /* Rule 3 */ + block_mode[i] = Mode.LATCHC; + current = Mode.LATCHC; + } + if (current == Mode.ABORC) { + block_mode[i] = Mode.AORB; + current = Mode.AORB; + } + if ((current == Mode.AORB) && (last == Mode.LATCHA)) { + block_mode[i] = Mode.LATCHA; + current = Mode.LATCHA; + } + if ((current == Mode.AORB) && (last == Mode.LATCHB)) { + block_mode[i] = Mode.LATCHB; + current = Mode.LATCHB; + } + if ((current == Mode.AORB) && (next == Mode.SHIFTA)) { + block_mode[i] = Mode.LATCHA; + current = Mode.LATCHA; + } + if ((current == Mode.AORB) && (next == Mode.SHIFTB)) { + block_mode[i] = Mode.LATCHB; + current = Mode.LATCHB; + } + if (current == Mode.AORB) { + block_mode[i] = Mode.LATCHB; + current = Mode.LATCHB; + } + if ((current == Mode.SHIFTA) && (length > 1)) { /* Rule 4 */ + block_mode[i] = Mode.LATCHA; + current = Mode.LATCHA; + } + if ((current == Mode.SHIFTB) && (length > 1)) { /* Rule 5 */ + block_mode[i] = Mode.LATCHB; + current = Mode.LATCHB; + } + if ((current == Mode.SHIFTA) && (last == Mode.LATCHA)) { + block_mode[i] = Mode.LATCHA; + current = Mode.LATCHA; + } + if ((current == Mode.SHIFTB) && (last == Mode.LATCHB)) { + block_mode[i] = Mode.LATCHB; + current = Mode.LATCHB; + } + if ((current == Mode.SHIFTA) && (last == Mode.LATCHC)) { + block_mode[i] = Mode.LATCHA; + current = Mode.LATCHA; + } + if ((current == Mode.SHIFTB) && (last == Mode.LATCHC)) { + block_mode[i] = Mode.LATCHB; + current = Mode.LATCHB; + } + } + } + combineSubsetBlocks(block_count); + } + + private void combineSubsetBlocks(int block_count) { + int i, j; + if (block_count > 1) { + i = 1; + while (i < block_count) { + if (block_mode[i - 1] == block_mode[i]) { + block_length[i - 1] = block_length[i - 1] + block_length[i]; + j = i + 1; + while (j < block_count) { + block_length[j - 1] = block_length[j]; + block_mode[j - 1] = block_mode[j]; + j++; + } + block_count = block_count - 1; + i--; + } + i++; + } + } + } + + @Override + protected void plotSymbol() { + int xBlock, yBlock; + int x, y, w, h; + boolean black; + getRectangles().clear(); + y = 1; + h = 1; + for (yBlock = 0; yBlock < rowCount; yBlock++) { + black = true; + x = 15; + for (xBlock = 0; xBlock < pattern[yBlock].length(); xBlock++) { + if (black) { + black = false; + w = pattern[yBlock].charAt(xBlock) - '0'; + if (rowHeight[yBlock] == -1) { + h = defaultHeight; + } else { + h = rowHeight[yBlock]; + } + if (w != 0 && h != 0) { + Rectangle2D.Double rect = new Rectangle2D.Double(x, y, w, h); + getRectangles().add(rect); + } + if ((x + w) > symbolWidth) { + symbolWidth = x + w; + } + } else { + black = true; + } + x += (double) (pattern[yBlock].charAt(xBlock) - '0'); + } + y += h; + if ((y + h) > symbolHeight) { + symbolHeight = y + h; + } + /* Add bars between rows */ + if (yBlock != (rowCount - 1)) { + Rectangle2D.Double rect = new Rectangle2D.Double(15, y - 1, (symbolWidth - 15), 2); + getRectangles().add(rect); + } + } + Rectangle2D.Double top = new Rectangle2D.Double(0, 0, (symbolWidth + 15), 2); + getRectangles().add(top); + Rectangle2D.Double bottom = new Rectangle2D.Double(0, y - 1, (symbolWidth + 15), 2); + getRectangles().add(bottom); + symbolWidth += 30; + symbolHeight += 2; + mergeVerticalBlocks(); + } + + private enum Mode { + NULL, SHIFTA, LATCHA, SHIFTB, LATCHB, SHIFTC, LATCHC, AORB, ABORC, CANDB, CANDBB + } + +} diff --git a/barcode/src/main/java/org/xbib/graphics/barcode/Code2Of5.java b/barcode/src/main/java/org/xbib/graphics/barcode/Code2Of5.java new file mode 100755 index 0000000..6dba07d --- /dev/null +++ b/barcode/src/main/java/org/xbib/graphics/barcode/Code2Of5.java @@ -0,0 +1,493 @@ +package org.xbib.graphics.barcode; + +import static org.xbib.graphics.barcode.HumanReadableLocation.NONE; +import static org.xbib.graphics.barcode.HumanReadableLocation.TOP; +import org.xbib.graphics.barcode.util.TextBox; +import java.awt.geom.Rectangle2D; + +/** + * Implements the Code 2 of 5 family of barcode standards. + */ +public class Code2Of5 extends Symbol { + + private static final String[] C25_MATRIX_TABLE = { + "113311", "311131", "131131", "331111", "113131", "313111", "133111", "111331", "311311", "131311" + }; + private static final String[] C25_INDUSTRIAL_TABLE = { + "1111313111", "3111111131", "1131111131", "3131111111", "1111311131", "3111311111", "1131311111", "1111113131", "3111113111", "1131113111" + }; + private static final String[] C25_INTERLEAVED_TABLE = { + "11331", "31113", "13113", "33111", "11313", "31311", "13311", "11133", "31131", "13131" + }; + /** + * The 2-of-5 mode. + */ + private ToFMode mode = ToFMode.MATRIX; + /** + * Ratio of wide bar width to narrow bar width. + */ + private double moduleWidthRatio = 3; + + /** + * Returns the ratio of wide bar width to narrow bar width. + * + * @return the ratio of wide bar width to narrow bar width + */ + public double getModuleWidthRatio() { + return moduleWidthRatio; + } + + /** + * Sets the ratio of wide bar width to narrow bar width. Valid values are usually + * between {@code 2} and {@code 3}. The default value is {@code 3}. + * + * @param moduleWidthRatio the ratio of wide bar width to narrow bar width + */ + public void setModuleWidthRatio(double moduleWidthRatio) { + this.moduleWidthRatio = moduleWidthRatio; + } + + /** + * Select Standard Code 2 of 5 mode, also known as Code 2 of 5 Matrix. (default) + * Encodes any length numeric input (digits 0-9). + */ + public void setMatrixMode() { + mode = ToFMode.MATRIX; + } + + /** + * Select Industrial Code 2 of 5 which can encode any length numeric input + * (digits 0-9) and does not include a check digit. + */ + public void setIndustrialMode() { + mode = ToFMode.INDUSTRIAL; + } + + /** + * Select International Air Transport Agency variation of Code 2 of 5. + * Encodes any length numeric input (digits 0-9) and does not include + * a check digit. + */ + public void setIATAMode() { + mode = ToFMode.IATA; + } + + /** + * Select Code 2 of 5 Data Logic. Encodes any length numeric input + * (digits 0-9) and does not include a check digit. + */ + public void setDataLogicMode() { + mode = ToFMode.DATA_LOGIC; + } + + /** + * Select Interleaved Code 2 of 5. encodes pairs of numbers, and so can + * only encode an even number of digits (0-9). If an odd number of digits + * is entered a leading zero is added. No check digit is calculated. + */ + public void setInterleavedMode() { + mode = ToFMode.INTERLEAVED; + } + + /** + * Select ITF-14, also known as UPC Shipping Container Symbol or Case Code. + * Requires a 13 digit numeric input (digits 0-9). One modulo-10 check + * digit is calculated. + */ + public void setITF14Mode() { + mode = ToFMode.ITF14; + } + + /** + * Select Deutsche Post Leitcode. Requires a 13-digit numerical input. + * Check digit is calculated. + */ + public void setDPLeitMode() { + mode = ToFMode.DPLEIT; + } + + /** + * Select Deutsche Post Identcode. Requires an 11-digit numerical input. + * Check digit is calculated. + */ + public void setDPIdentMode() { + mode = ToFMode.DPIDENT; + } + + @Override + public boolean encode() { + boolean retval = false; + + switch (mode) { + case MATRIX: + retval = dataMatrixTof(); + break; + case INDUSTRIAL: + retval = industrialTof(); + break; + case IATA: + retval = iataTof(); + break; + case INTERLEAVED: + retval = interleavedTof(); + break; + case DATA_LOGIC: + retval = dataLogic(); + break; + case ITF14: + retval = itf14(); + break; + case DPLEIT: + retval = deutschePostLeitcode(); + break; + case DPIDENT: + retval = deutschePostIdentcode(); + break; + } + + if (retval) { + plotSymbol(); + } + + return retval; + } + + private boolean dataMatrixTof() { + + if (!(content.matches("[0-9]+"))) { + errorMsg.append("Invalid characters in input"); + return false; + } + + StringBuilder dest = new StringBuilder("311111"); + + for (int i = 0; i < content.length(); i++) { + dest.append(C25_MATRIX_TABLE[Character.getNumericValue(content.charAt(i))]); + } + dest.append("31111"); + + readable = new StringBuilder(content); + pattern = new String[]{dest.toString()}; + rowCount = 1; + rowHeight = new int[]{-1}; + return true; + } + + private boolean industrialTof() { + if (!(content.matches("[0-9]+"))) { + errorMsg.append("Invalid characters in input"); + return false; + } + + StringBuilder dest = new StringBuilder("313111"); + readable = new StringBuilder(content); + for (int i = 0; i < readable.length(); i++) { + dest.append(C25_INDUSTRIAL_TABLE[Character.getNumericValue(readable.charAt(i))]); + } + dest.append("31113"); + + pattern = new String[]{dest.toString()}; + rowCount = 1; + rowHeight = new int[]{-1}; + return true; + } + + private boolean iataTof() { + if (!(content.matches("[0-9]+"))) { + errorMsg.append("Invalid characters in input"); + return false; + } + + StringBuilder dest = new StringBuilder("1111"); + readable = new StringBuilder(content); + for (int i = 0; i < readable.length(); i++) { + dest.append(C25_INDUSTRIAL_TABLE[Character.getNumericValue(readable.charAt(i))]); + } + dest.append("311"); + + pattern = new String[]{dest.toString()}; + rowCount = 1; + rowHeight = new int[]{-1}; + return true; + } + + private boolean dataLogic() { + if (!(content.matches("[0-9]+"))) { + errorMsg.append("Invalid characters in input"); + return false; + } + + StringBuilder dest = new StringBuilder("1111"); + readable = new StringBuilder(content); + + for (int i = 0; i < readable.length(); i++) { + dest.append(C25_MATRIX_TABLE[Character.getNumericValue(readable.charAt(i))]); + } + + dest.append("311"); + + pattern = new String[]{dest.toString()}; + rowCount = 1; + rowHeight = new int[]{-1}; + return true; + } + + private boolean interleavedTof() { + int i; + StringBuilder dest; + + if ((content.length() & 1) == 0) { + readable = new StringBuilder(content); + } else { + readable = new StringBuilder("0").append(content); + } + if (!(readable.toString().matches("[0-9]+"))) { + errorMsg.append("Invalid characters in input"); + return false; + } + + dest = new StringBuilder("1111"); + + for (i = 0; i < readable.length(); i += 2) { + dest.append(interlace(i, i + 1)); + } + + dest.append("311"); + + pattern = new String[]{dest.toString()}; + rowCount = 1; + rowHeight = new int[]{-1}; + return true; + } + + private String interlace(int x, int y) { + char a = readable.charAt(x); + char b = readable.charAt(y); + + String one = C25_INTERLEAVED_TABLE[Character.getNumericValue(a)]; + String two = C25_INTERLEAVED_TABLE[Character.getNumericValue(b)]; + String f = ""; + + for (int i = 0; i < 5; i++) { + f += one.charAt(i); + f += two.charAt(i); + } + + return f; + } + + private boolean itf14() { + int i, count = 0; + int input_length = content.length(); + StringBuilder dest; + + if (!(content.matches("[0-9]+"))) { + errorMsg.append("Invalid characters in input"); + return false; + } + + if (input_length > 13) { + errorMsg.append("Input data too long"); + return false; + } + + readable = new StringBuilder(); + for (i = input_length; i < 13; i++) { + readable.append("0"); + } + + readable.append(content); + for (i = 12; i >= 0; i--) { + count += readable.charAt(i) - '0'; + + if ((i & 1) == 0) { + count += 2 * (readable.charAt(i) - '0'); + } + } + + readable.append((char) (((10 - (count % 10)) % 10) + '0')); + encodeInfo.append("Check Digit: ").append((char) (((10 - (count % 10)) % 10) + '0')); + encodeInfo.append('\n'); + + dest = new StringBuilder("1111"); + + for (i = 0; i < readable.length(); i += 2) { + dest.append(interlace(i, i + 1)); + } + + dest.append("311"); + + pattern = new String[]{dest.toString()}; + rowCount = 1; + rowHeight = new int[]{-1}; + return true; + } + + private boolean deutschePostLeitcode() { + int i, count = 0; + int input_length = content.length(); + StringBuilder dest; + + if (!(content.matches("[0-9]+"))) { + errorMsg.append("Invalid characters in input"); + return false; + } + + if (input_length > 13) { + errorMsg.append("Input data too long"); + return false; + } + + readable = new StringBuilder(); + for (i = input_length; i < 13; i++) { + readable.append("0"); + } + + readable.append(content); + + for (i = 12; i >= 0; i--) { + count += 4 * (readable.charAt(i) - '0'); + + if ((i & 1) != 0) { + count += 5 * (readable.charAt(i) - '0'); + } + } + + readable.append((char) (((10 - (count % 10)) % 10) + '0')); + encodeInfo.append("Check digit: ").append((char) (((10 - (count % 10)) % 10) + '0')); + encodeInfo.append('\n'); + + dest = new StringBuilder("1111"); + + for (i = 0; i < readable.length(); i += 2) { + dest.append(interlace(i, i + 1)); + } + + dest.append("311"); + + pattern = new String[]{dest.toString()}; + rowCount = 1; + rowHeight = new int[]{-1}; + return true; + } + + private boolean deutschePostIdentcode() { + int i, count = 0; + int input_length = content.length(); + StringBuilder dest; + + if (!(content.matches("[0-9]+"))) { + errorMsg.append("Invalid characters in input"); + return false; + } + + if (input_length > 11) { + errorMsg.append("Input data too long"); + return false; + } + + readable = new StringBuilder(); + for (i = input_length; i < 11; i++) { + readable.append("0"); + } + + readable.append(content); + for (i = 10; i >= 0; i--) { + count += 4 * (readable.charAt(i) - '0'); + + if ((i & 1) != 0) { + count += 5 * (readable.charAt(i) - '0'); + } + } + + readable.append((char) (((10 - (count % 10)) % 10) + '0')); + encodeInfo.append("Check Digit: ").append((char) (((10 - (count % 10)) % 10) + '0')); + encodeInfo.append('\n'); + + dest = new StringBuilder("1111"); + + for (i = 0; i < readable.length(); i += 2) { + dest.append(interlace(i, i + 1)); + } + + dest.append("311"); + + pattern = new String[]{dest.toString()}; + rowCount = 1; + rowHeight = new int[]{-1}; + return true; + } + + @Override + protected void plotSymbol() { + int xBlock; + getRectangles().clear(); + getTexts().clear(); + int baseY; + if (getHumanReadableLocation() == TOP) { + baseY = getTheoreticalHumanReadableHeight(); + } else { + baseY = 0; + } + double x = 0; + int y = baseY; + int h = 0; + boolean black = true; + int offset = 0; + if (mode == ToFMode.ITF14) { + offset = 20; + } + for (xBlock = 0; xBlock < pattern[0].length(); xBlock++) { + char c = pattern[0].charAt(xBlock); + double w = getModuleWidth(c - '0') * moduleWidth; + if (black) { + if (rowHeight[0] == -1) { + h = defaultHeight; + } else { + h = rowHeight[0]; + } + if (w != 0 && h != 0) { + Rectangle2D.Double rect = new Rectangle2D.Double(x + offset, y, w, h); + getRectangles().add(rect); + } + symbolWidth = (int) Math.ceil(x + w + (2 * offset)); + } + black = !black; + x += w; + } + symbolHeight = h; + if (mode == ToFMode.ITF14) { + Rectangle2D.Double topBar = new Rectangle2D.Double(0, baseY, symbolWidth, 4); + Rectangle2D.Double bottomBar = new Rectangle2D.Double(0, baseY + symbolHeight - 4, symbolWidth, 4); + Rectangle2D.Double leftBar = new Rectangle2D.Double(0, baseY, 4, symbolHeight); + Rectangle2D.Double rightBar = new Rectangle2D.Double(symbolWidth - 4, baseY, 4, symbolHeight); + getRectangles().add(topBar); + getRectangles().add(bottomBar); + getRectangles().add(leftBar); + getRectangles().add(rightBar); + } + if (getHumanReadableLocation() != NONE && readable.length() > 0) { + double baseline; + if (getHumanReadableLocation() == TOP) { + baseline = fontSize; + } else { + baseline = getHeight() + fontSize; + } + double centerX = getWidth() / 2.0; + getTexts().add(new TextBox(centerX, baseline, readable.toString())); + } + } + + @Override + protected double getModuleWidth(int originalWidth) { + if (originalWidth == 1) { + return 1; + } else { + return moduleWidthRatio; + } + } + + private enum ToFMode { + MATRIX, INDUSTRIAL, IATA, DATA_LOGIC, INTERLEAVED, ITF14, DPLEIT, DPIDENT + } +} diff --git a/barcode/src/main/java/org/xbib/graphics/barcode/Code32.java b/barcode/src/main/java/org/xbib/graphics/barcode/Code32.java new file mode 100755 index 0000000..6c35717 --- /dev/null +++ b/barcode/src/main/java/org/xbib/graphics/barcode/Code32.java @@ -0,0 +1,103 @@ +package org.xbib.graphics.barcode; + +/** + * Implements Code 32, also known as Italian Pharmacode, A variation of Code + * 39 used by the Italian Ministry of Health ("Ministero della Sanità") + * Requires a numeric input up to 8 digits in length. Check digit is + * calculated. + */ +public class Code32 extends Symbol { + private char[] tabella = { + '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'B', 'C', 'D', 'F', + 'G', 'H', 'J', 'K', 'L', 'M', 'N', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', + 'W', 'X', 'Y', 'Z' + }; + + @Override + public boolean encode() { + int i, checksum, checkpart, checkdigit; + int pharmacode, remainder, devisor; + StringBuilder localstr; + StringBuilder risultante; + int[] codeword = new int[6]; + Code3Of9 c39 = new Code3Of9(); + + if (content.length() > 8) { + errorMsg.append("Input too long"); + return false; + } + + if (!(content.matches("[0-9]+"))) { + errorMsg.append("Invalid characters in input"); + return false; + } + + /* Add leading zeros as required */ + localstr = new StringBuilder(); + for (i = content.length(); i < 8; i++) { + localstr.append("0"); + } + localstr.append(content); + + /* Calculate the check digit */ + checksum = 0; + checkpart = 0; + for (i = 0; i < 4; i++) { + checkpart = Character.getNumericValue(localstr.charAt(i * 2)); + checksum += checkpart; + checkpart = 2 * Character.getNumericValue(localstr.charAt((i * 2) + 1)); + if (checkpart >= 10) { + checksum += (checkpart - 10) + 1; + } else { + checksum += checkpart; + } + } + + /* Add check digit to data string */ + checkdigit = checksum % 10; + localstr.append((char) (checkdigit + '0')); + encodeInfo.append("Check Digit: ").append((char) (checkdigit + '0')); + encodeInfo.append('\n'); + + /* Convert string into an integer value */ + pharmacode = 0; + for (i = 0; i < localstr.length(); i++) { + pharmacode *= 10; + pharmacode += Character.getNumericValue(localstr.charAt(i)); + } + + /* Convert from decimal to base-32 */ + devisor = 33554432; + for (i = 5; i >= 0; i--) { + codeword[i] = pharmacode / devisor; + remainder = pharmacode % devisor; + pharmacode = remainder; + devisor /= 32; + } + + /* Look up values in 'Tabella di conversione' */ + risultante = new StringBuilder(); + for (i = 5; i >= 0; i--) { + risultante.append(tabella[codeword[i]]); + } + + /* Plot the barcode using Code 39 */ + + readable = new StringBuilder("A").append(localstr); + pattern = new String[1]; + rowCount = 1; + rowHeight = new int[1]; + rowHeight[0] = -1; + encodeInfo.append("Code 39 Equivalent: ").append(risultante).append('\n'); + try { + c39.setContent(risultante.toString()); + } catch (Exception e) { + errorMsg.append(e.getMessage()); + return false; + } + + this.pattern[0] = c39.pattern[0]; + this.plotSymbol(); + return true; + } +} diff --git a/barcode/src/main/java/org/xbib/graphics/barcode/Code3Of9.java b/barcode/src/main/java/org/xbib/graphics/barcode/Code3Of9.java new file mode 100755 index 0000000..7fb0378 --- /dev/null +++ b/barcode/src/main/java/org/xbib/graphics/barcode/Code3Of9.java @@ -0,0 +1,155 @@ +package org.xbib.graphics.barcode; + +/** + * Implements Code 39 bar code symbology according to ISO/IEC 16388:2007. + * Input data can be of any length and supports the characters 0-9, A-Z, dash + * (-), full stop (.), space, asterisk (*), dollar ($), slash (/), plus (+) + * and percent (%). The standard does not require a check digit but a + * modulo-43 check digit can be added if required. + */ +public class Code3Of9 extends Symbol { + + private static final String[] CODE_39 = { + "1112212111", "2112111121", "1122111121", "2122111111", "1112211121", + "2112211111", "1122211111", "1112112121", "2112112111", "1122112111", + "2111121121", "1121121121", "2121121111", "1111221121", "2111221111", + "1121221111", "1111122121", "2111122111", "1121122111", "1111222111", + "2111111221", "1121111221", "2121111211", "1111211221", "2111211211", + "1121211211", "1111112221", "2111112211", "1121112211", "1111212211", + "2211111121", "1221111121", "2221111111", "1211211121", "2211211111", + "1221211111", "1211112121", "2211112111", "1221112111", "1212121111", + "1212111211", "1211121211", "1112121211" + }; + private static final char[] LOOKUP = { + '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D', + 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R', + 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z', '-', '.', ' ', '$', '/', '+', + '%' + }; + private CheckDigit checkOption = CheckDigit.NONE; + /** + * Ratio of wide bar width to narrow bar width. + */ + private double moduleWidthRatio = 2; + + /** + * Returns the ratio of wide bar width to narrow bar width. + * + * @return the ratio of wide bar width to narrow bar width + */ + public double getModuleWidthRatio() { + return moduleWidthRatio; + } + + /** + * Sets the ratio of wide bar width to narrow bar width. Valid values are usually + * between {@code 2} and {@code 3}. The default value is {@code 2}. + * + * @param moduleWidthRatio the ratio of wide bar width to narrow bar width + */ + public void setModuleWidthRatio(double moduleWidthRatio) { + this.moduleWidthRatio = moduleWidthRatio; + } + + /** + * Select addition of optional Modulo-43 check digit or encoding without + * check digit. + * + * @param checkMode Check digit option. + */ + public void setCheckDigit(CheckDigit checkMode) { + checkOption = checkMode; + } + + @Override + public boolean encode() { + if (!(content.matches("[0-9A-Z\\. \\-$/+%]+"))) { + errorMsg.append("Invalid characters in input"); + return false; + } + StringBuilder p = new StringBuilder(); + StringBuilder dest = new StringBuilder(); + int l = content.length(); + int charval; + char thischar; + int counter = 0; + char check_digit = ' '; + dest.append("1211212111"); + for (int i = 0; i < l; i++) { + thischar = content.charAt(i); + charval = positionOf(thischar, LOOKUP); + counter += charval; + p.append(CODE_39[charval]); + } + dest.append(p); + if (checkOption == CheckDigit.MOD43) { + counter = counter % 43; + if (counter < 10) { + check_digit = (char) (counter + '0'); + } else { + if (counter < 36) { + check_digit = (char) ((counter - 10) + 'A'); + } else { + switch (counter) { + case 36: + check_digit = '-'; + break; + case 37: + check_digit = '.'; + break; + case 38: + check_digit = ' '; + break; + case 39: + check_digit = '$'; + break; + case 40: + check_digit = '/'; + break; + case 41: + check_digit = '+'; + break; + default: + check_digit = 37; + break; + } + } + } + charval = positionOf(check_digit, LOOKUP); + p.append(CODE_39[charval]); + if (check_digit == ' ') { + check_digit = '_'; + } + } + dest.append("121121211"); + if (checkOption == CheckDigit.MOD43) { + readable = new StringBuilder("*").append(content).append(check_digit).append("*"); + encodeInfo.append("Check Digit: ").append(check_digit).append("\n"); + } else { + readable = new StringBuilder("*").append(content).append("*"); + } + pattern = new String[]{dest.toString()}; + rowCount = 1; + rowHeight = new int[]{-1}; + plotSymbol(); + return true; + } + + @Override + protected double getModuleWidth(int originalWidth) { + if (originalWidth == 1) { + return 1; + } else { + return moduleWidthRatio; + } + } + + @Override + protected int[] getCodewords() { + return getPatternAsCodewords(10); + } + + public enum CheckDigit { + NONE, MOD43 + } +} diff --git a/barcode/src/main/java/org/xbib/graphics/barcode/Code3Of9Extended.java b/barcode/src/main/java/org/xbib/graphics/barcode/Code3Of9Extended.java new file mode 100755 index 0000000..30d9e63 --- /dev/null +++ b/barcode/src/main/java/org/xbib/graphics/barcode/Code3Of9Extended.java @@ -0,0 +1,79 @@ +package org.xbib.graphics.barcode; + +/** + * Implements Code 3 of 9 Extended, also known as Code 39e and Code39+. + * Supports encoding of all characters in the 7-bit ASCII table. A + * modulo-43 check digit can be added if required. + */ +public class Code3Of9Extended extends Symbol { + + private final String[] ECode39 = { + "%U", "$A", "$B", "$C", "$D", "$E", "$F", "$G", "$H", "$I", "$J", "$K", + "$L", "$M", "$N", "$O", "$P", "$Q", "$R", "$S", "$T", "$U", "$V", "$W", + "$X", "$Y", "$Z", "%A", "%B", "%C", "%D", "%E", " ", "/A", "/B", "/C", + "/D", "/E", "/F", "/G", "/H", "/I", "/J", "/K", "/L", "-", ".", "/O", + "0", "1", "2", "3", "4", "5", "6", "7", "8", "9", "/Z", "%F", "%G", + "%H", "%I", "%J", "%V", "A", "B", "C", "D", "E", "F", "G", "H", "I", + "J", "K", "L", "M", "N", "O", "P", "Q", "R", "S", "T", "U", "V", "W", + "X", "Y", "Z", "%K", "%L", "%M", "%N", "%O", "%W", "+A", "+B", "+C", + "+D", "+E", "+F", "+G", "+H", "+I", "+J", "+K", "+L", "+M", "+N", "+O", + "+P", "+Q", "+R", "+S", "+T", "+U", "+V", "+W", "+X", "+Y", "+Z", "%P", + "%Q", "%R", "%S", "%T" + }; + private CheckDigit checkOption; + + public Code3Of9Extended() { + checkOption = CheckDigit.NONE; + } + + /** + * Select addition of optional Modulo-43 check digit or encoding without + * check digit. + * + * @param checkMode Check digit option + */ + public void setCheckDigit(CheckDigit checkMode) { + checkOption = checkMode; + } + + @Override + public boolean encode() { + StringBuilder buffer = new StringBuilder(); + int l = content.length(); + int asciicode; + Code3Of9 c = new Code3Of9(); + + if (checkOption == CheckDigit.MOD43) { + c.setCheckDigit(Code3Of9.CheckDigit.MOD43); + } + + if (!content.matches("[\u0000-\u007F]+")) { + errorMsg.append("Invalid characters in input data"); + return false; + } + + for (int i = 0; i < l; i++) { + asciicode = content.charAt(i); + buffer.append(ECode39[asciicode]); + } + + try { + c.setContent(buffer.toString()); + } catch (Exception e) { + errorMsg.append(e.getMessage()); + return false; + } + readable = new StringBuilder(content); + pattern = new String[1]; + pattern[0] = c.pattern[0]; + rowCount = 1; + rowHeight = new int[1]; + rowHeight[0] = -1; + plotSymbol(); + return true; + } + + public enum CheckDigit { + NONE, MOD43 + } +} diff --git a/barcode/src/main/java/org/xbib/graphics/barcode/Code49.java b/barcode/src/main/java/org/xbib/graphics/barcode/Code49.java new file mode 100755 index 0000000..ef55083 --- /dev/null +++ b/barcode/src/main/java/org/xbib/graphics/barcode/Code49.java @@ -0,0 +1,1345 @@ +package org.xbib.graphics.barcode; + +import java.awt.geom.Rectangle2D; + +/** + * Implements Code 49 according to ANSI/AIM-BC6-2000. + * Encoding supports full 7-bit ASCII input up to a maximum of 49 characters + * or 81 numeric digits. GS1 data encoding is also supported. + */ +public class Code49 extends Symbol { + + private final String[] c49_table7 = { + /* Table 7: Code 49 ASCII Chart */ + "! ", "!A", "!B", "!C", "!D", "!E", "!F", "!G", "!H", "!I", "!J", "!K", + "!L", "!M", "!N", "!O", "!P", "!Q", "!R", "!S", "!T", "!U", "!V", "!W", + "!X", "!Y", "!Z", "!1", "!2", "!3", "!4", "!5", " ", "!6", "!7", "!8", + "$", "%", "!9", "!0", "!-", "!.", "!$", "+", "!/", "-", ".", "/", "0", + "1", "2", "3", "4", "5", "6", "7", "8", "9", "!+", "&1", "&2", "&3", + "&4", "&5", "&6", "A", "B", "C", "D", "E", "F", "G", "H", "I", "J", "K", + "L", "M", "N", "O", "P", "Q", "R", "S", "T", "U", "V", "W", "X", "Y", + "Z", "&7", "&8", "&9", "&0", "&-", "&.", "&A", "&B", "&C", "&D", "&E", + "&F", "&G", "&H", "&I", "&J", "&K", "&L", "&M", "&N", "&O", "&P", "&Q", + "&R", "&S", "&T", "&U", "&V", "&W", "&X", "&Y", "&Z", "&$", "&/", "&+", + "&%", "& " + }; + + /* Table 5: Check Character Weighting Values */ + private final int[] c49_x_weight = { + 1, 9, 31, 26, 2, 12, 17, 23, 37, 18, 22, 6, 27, 44, 15, 43, 39, 11, 13, + 5, 41, 33, 36, 8, 4, 32, 3, 19, 40, 25, 29, 10 + }; + + private final int[] c49_y_weight = { + 9, 31, 26, 2, 12, 17, 23, 37, 18, 22, 6, 27, 44, 15, 43, 39, 11, 13, 5, + 41, 33, 36, 8, 4, 32, 3, 19, 40, 25, 29, 10, 24 + }; + + private final int[] c49_z_weight = { + 31, 26, 2, 12, 17, 23, 37, 18, 22, 6, 27, 44, 15, 43, 39, 11, 13, 5, 41, + 33, 36, 8, 4, 32, 3, 19, 40, 25, 29, 10, 24, 30 + }; + + private final String[] c49_table4 = { + /* Table 4: Row Parity Pattern for Code 49 Symbols */ + "OEEO", "EOEO", "OOEE", "EEOO", "OEOE", "EOOE", "OOOO", "EEEE" + }; + + private final String[] c49_appxe_even = { + /* Appendix E - Code 49 Encodation Patterns (Even Symbol Character Parity) */ + /* Column 1 */ + "11521132", "25112131", "14212132", "25121221", "14221222", "12412132", + "23321221", "12421222", "21521221", "15112222", "15121312", "13312222", + "24221311", "13321312", "11512222", "22421311", "11521312", "25112311", + "14212312", "23312311", "12412312", "21512311", "16121131", "14321131", + "12521131", "15212131", "15221221", "13412131", "13421221", "11612131", + "16112221", "16121311", "14312221", "14321311", "12512221", "12521311", + "15212311", "13412311", "11612311", "11131135", "31131133", "51131131", + "21122134", "41122132", "21131224", "41131222", "11113135", "31113133", + "51113131", "11122225", "31122223", "51122221", "11131315", "31131313", + "51131311", "21113224", "41113222", "21122314", + /* Column 2 */ + "41122312", "11113315", "31113313", "51113311", "12131134", "32131132", + "21231133", "41231131", "22122133", "42122131", "11222134", "22131223", + "42131221", "11231224", "31231222", "12113134", "32113132", "12122224", + "32122222", "12131314", "32131312", "21231313", "41231311", "22113223", + "42113221", "11213224", "22122313", "42122311", "11222314", "31222312", + "12113314", "32113312", "21213313", "41213311", "13131133", "33131131", + "22231132", "11331133", "31331131", "23122132", "12222133", "23131222", + "12231223", "32231221", "21331222", "13113133", "33113131", "13122223", + "33122221", "11313133", "13131313", "33131311", "11322223", "22231312", + "11331313", "31331311", "23113222", "12213223", + /* Column 3 */ + "23122312", "12222313", "32222311", "21322312", "13113313", "33113311", + "22213312", "11313313", "31313311", "14131132", "23231131", "12331132", + "21431131", "24122131", "13222132", "24131221", "13231222", "11422132", + "22331221", "11431222", "14113132", "14122222", "12313132", "14131312", + "12322222", "23231311", "12331312", "21431311", "24113221", "13213222", + "24122311", "13222312", "11413222", "22322311", "11422312", "14113312", + "23213311", "12313312", "21413311", "15131131", "13331131", "14222131", + "14231221", "12422131", "12431221", "15113131", "15122221", "13313131", + "15131311", "13322221", "11513131", "13331311", "11522221", "14213221", + "14222311", "12413221", "12422311", "15113311", + /* Column 4 */ + "13313311", "11513311", "11141134", "31141132", "21132133", "41132131", + "21141223", "41141221", "11123134", "31123132", "11132224", "31132222", + "11141314", "31141312", "21114133", "41114131", "21123223", "41123221", + "21132313", "41132311", "11114224", "31114222", "11123314", "31123312", + "21114313", "41114311", "12141133", "32141131", "21241132", "22132132", + "11232133", "22141222", "11241223", "31241221", "12123133", "32123131", + "12132223", "32132221", "12141313", "32141311", "21241312", "22114132", + "11214133", "22123222", "11223223", "22132312", "11232313", "31232311", + "12114223", "32114221", "12123313", "32123311", "21223312", "22114312", + "11214313", "31214311", "13141132", "22241131", + /* Column 5 */ + "11341132", "23132131", "12232132", "23141221", "12241222", "21341221", + "13123132", "13132222", "11323132", "13141312", "11332222", "22241311", + "11341312", "23114131", "12214132", "23123221", "12223222", "23132311", + "12232312", "21332311", "13114222", "13123312", "11314222", "22223311", + "11323312", "23114311", "12214312", "21314311", "14141131", "12341131", + "13232131", "13241221", "11432131", "14123131", "14132221", "12323131", + "14141311", "12332221", "12341311", "13214131", "13223221", "11414131", + "13232311", "11423221", "11432311", "14114221", "14123311", "12314221", + "12323311", "13214311", "11414311", "11151133", "31151131", "21142132", + "21151222", "11133133", "31133131", "11142223", + /* Column 6 */ + "31142221", "11151313", "31151311", "21124132", "21133222", "21142312", + "11115133", "31115131", "11124223", "31124221", "11133313", "31133311", + "21115222", "21124312", "12151132", "21251131", "22142131", "11242132", + "22151221", "11251222", "12133132", "12142222", "12151312", "21251311", + "22124131", "11224132", "22133221", "11233222", "22142311", "11242312", + "12115132", "12124222", "12133312", "21233311", "22115221", "11215222", + "22124311", "11224312", "13151131", "12242131", "12251221", "13133131", + "13142221", "11333131", "13151311", "11342221", "12224131", "12233221", + "12242311", "13115131", "13124221", "11315131", "13133311", "11324221", + "11333311", "12215221", "12224311", "11161132", + /* Column 7 */ + "21152131", "21161221", "11143132", "11152222", "11161312", "21134131", + "21143221", "21152311", "11125132", "11134222", "11143312", "21116131", + "21125221", "21134311", "12161131", "11252131", "12143131", "12152221", + "12161311", "11234131", "11243221", "11252311", "12125131", "12134221", + "12143311", "11216131", "11225221", "11234311", "11111236", "31111234", + "51111232", "21111325", "41111323", "61111321", "11111416", "31111414", + "51111412", "31211143", "51211141", "12111235", "32111233", "52111231", + "21211234", "41211232", "22111324", "42111322", "11211325", "31211323", + "51211321", "12111415", "32111413", "52111411", "21211414", "41211412", + "12211144", "32211142", "21311143", "41311141", + /* Column 8 */ + "13111234", "33111232", "22211233", "42211231", "11311234", "31311232", + "23111323", "43111321", "12211324", "32211322", "21311323", "41311321", + "13111414", "33111412", "22211413", "42211411", "11311414", "31311412", + "13211143", "33211141", "22311142", "11411143", "31411141", "14111233", + "34111231", "23211232", "12311233", "32311231", "21411232", "24111322", + "13211323", "33211321", "22311322", "11411323", "31411321", "14111413", + "34111411", "23211412", "12311413", "32311411", "21411412", "14211142", + "23311141", "12411142", "21511141", "15111232", "24211231", "13311232", + "22411231", "11511232", "25111321", "14211322", "23311321", "12411322", + "21511321", "15111412", "24211411", "13311412", + /* Column 9 */ + "22411411", "11511412", "15211141", "13411141", "11611141", "16111231", + "14311231", "12511231", "15211321", "13411321", "11611321", "16111411", + "14311411", "12511411", "21121144", "41121142", "11112145", "31112143", + "51112141", "11121235", "31121233", "51121231", "21112234", "41112232", + "21121324", "41121322", "11112325", "31112323", "51112321", "11121415", + "31121413", "51121411", "21112414", "41112412", "22121143", "42121141", + "11221144", "31221142", "12112144", "32112142", "12121234", "32121232", + "21221233", "41221231", "22112233", "42112231", "11212234", "22121323", + "42121321", "11221324", "31221322", "12112324", "32112322", "12121414", + "32121412", "21221413", "41221411", "22112413", + /* Column 10 */ + "42112411", "11212414", "31212412", "23121142", "12221143", "32221141", + "21321142", "13112143", "33112141", "13121233", "33121231", "11312143", + "22221232", "11321233", "31321231", "23112232", "12212233", "23121322", + "12221323", "32221321", "21321322", "13112323", "33112321", "13121413", + "33121411", "11312323", "22221412", "11321413", "31321411", "23112412", + "12212413", "32212411", "21312412", "24121141", "13221142", "22321141", + "11421142", "14112142", "14121232", "12312142", "23221231", "12321232", + "21421231", "24112231", "13212232", "24121321", "13221322", "11412232", + "22321321", "11421322", "14112322", "14121412", "12312322", "23221411", + "12321412", "21421411", "24112411", "13212412", + /* Column 11 */ + "22312411", "11412412", "14221141", "12421141", "15112141", "15121231", + "13312141", "13321231", "11512141", "11521231", "14212231", "14221321", + "12412231", "12421321", "15112321", "15121411", "13312321", "13321411", + "11512321", "11521411", "14212411", "12412411", "21131143", "41131141", + "11122144", "31122142", "11131234", "31131232", "21113143", "41113141", + "21122233", "41122231", "21131323", "41131321", "11113234", "31113232", + "11122324", "31122322", "11131414", "31131412", "21113323", "41113321", + "21122413", "41122411", "11113414", "31113412", "22131142", "11231143", + "31231141", "12122143", "32122141", "12131233", "32131231", "21231232", + "22113142", "11213143", "22122232", "11222233", + /* Column 12 */ + "22131322", "11231323", "31231321", "12113233", "32113231", "12122323", + "32122321", "12131413", "32131411", "21231412", "22113322", "11213323", + "22122412", "11222413", "31222411", "12113413", "32113411", "21213412", + "23131141", "12231142", "21331141", "13122142", "13131232", "11322142", + "22231231", "11331232", "23113141", "12213142", "23122231", "12222232", + "23131321", "12231322", "21331321", "13113232", "13122322", "11313232", + "13131412", "11322322", "22231411", "11331412", "23113321", "12213322", + "23122411", "12222412", "21322411", "13113412", "22213411", "11313412", + "13231141", "11431141", "14122141", "14131231", "12322141", "12331231", + "13213141", "13222231", "11413141", "13231321", + /* Column 13 */ + "11422231", "11431321", "14113231", "14122321", "12313231", "14131411", + "12322321", "12331411", "13213321", "13222411", "11413321", "11422411", + "14113411", "12313411", "21141142", "11132143", "31132141", "11141233", + "31141231", "21123142", "21132232", "21141322", "11114143", "31114141", + "11123233", "31123231", "11132323", "31132321", "11141413", "31141411", + "21114232", "21123322", "21132412", "11114323", "31114321", "11123413", + "31123411", "22141141", "11241142", "12132142", "12141232", "21241231", + "22123141", "11223142", "22132231", "11232232", "22141321", "11241322", + "12114142", "12123232", "12132322", "12141412", "21241411", "22114231", + "11214232", "22123321", "11223322", "22132411", + /* Column 14 */ + "11232412", "12114322", "12123412", "21223411", "12241141", "13132141", + "13141231", "11332141", "11341231", "12223141", "12232231", "12241321", + "13114141", "13123231", "11314141", "13132321", "11323231", "13141411", + "11332321", "11341411", "12214231", "12223321", "12232411", "13114321", + "13123411", "11314321", "11323411", "21151141", "11142142", "11151232", + "21133141", "21142231", "21151321", "11124142", "11133232", "11142322", + "11151412", "21115141", "21124231", "21133321", "21142411", "11115232", + "11124322", "11133412", "11251141", "12142141", "12151231", "11233141", + "11242231", "11251321", "12124141", "12133231", "12142321", "12151411", + "11215141", "11224231", "11233321", "11242411", + /* Column 15 */ + "12115231", "12124321", "12133411", "11152141", "11161231", "11134141", + "11143231", "11152321", "11161411", "11116141", "11125231", "11134321", + "11143411", "21111244", "41111242", "11111335", "31111333", "51111331", + "21111424", "41111422", "11111515", "31111513", "51111511", "21211153", + "41211151", "22111243", "42111241", "11211244", "31211242", "12111334", + "32111332", "21211333", "41211331", "22111423", "42111421", "11211424", + "31211422", "12111514", "32111512", "21211513", "41211511", "22211152", + "11311153", "31311151", "23111242", "12211243", "32211241", "21311242", + "13111333", "33111331", "22211332", "11311333", "31311331", "23111422", + "12211423", "32211421", "21311422", "13111513", + /* Column 16 */ + "33111511", "22211512", "11311513", "31311511", "23211151", "12311152", + "21411151", "24111241", "13211242", "22311241", "11411242", "14111332", + "23211331", "12311332", "21411331", "24111421", "13211422", "22311421", + "11411422", "14111512", "23211511", "12311512", "21411511", "13311151", + "11511151", "14211241", "12411241", "15111331", "13311331", "11511331", + "14211421", "12411421", "15111511", "13311511", "11511511", "31121152", + "21112153", "41112151", "21121243", "41121241", "11112244", "31112242", + "11121334", "31121332", "21112333", "41112331", "21121423", "41121421", + "11112424", "31112422", "11121514", "31121512", "21112513", "41112511", + "12121153", "32121151", "21221152", "22112152", + /* Column 17 */ + "11212153", "22121242", "11221243", "31221241", "12112243", "32112241", + "12121333", "32121331", "21221332", "22112332", "11212333", "22121422", + "11221423", "31221421", "12112423", "32112421", "12121513", "32121511", + "21221512", "22112512", "11212513", "31212511", "13121152", "22221151", + "11321152", "23112151", "12212152", "23121241", "12221242", "21321241", + "13112242", "13121332", "11312242", "22221331", "11321332", "23112331", + "12212332", "23121421", "12221422", "21321421", "13112422", "13121512", + "11312422", "22221511", "11321512", "23112511", "12212512", "21312511", + "14121151", "12321151", "13212151", "13221241", "11412151", "11421241", + "14112241", "14121331", "12312241", "12321331", + /* Column 18 */ + "13212331", "13221421", "11412331", "11421421", "14112421", "14121511", + "12312421", "12321511", "13212511", "11412511", "11131153", "31131151", + "21122152", "21131242", "11113153", "31113151", "11122243", "31122241", + "11131333", "31131331", "21113242", "21122332", "21131422", "11113333", + "31113331", "11122423", "31122421", "11131513", "31131511", "21113422", + "21122512", "12131152", "21231151", "22122151", "11222152", "22131241", + "11231242", "12113152", "12122242", "12131332", "21231331", "22113241", + "11213242", "22122331", "11222332", "22131421", "11231422", "12113332", + "12122422", "12131512", "21231511", "22113421", "11213422", "22122511", + "11222512", "13131151", "11331151", "12222151", + /* Column 19 */ + "12231241", "13113151", "13122241", "11313151", "13131331", "11322241", + "11331331", "12213241", "12222331", "12231421", "13113331", "13122421", + "11313331", "13131511", "11322421", "11331511", "12213421", "12222511", + "11141152", "21132151", "21141241", "11123152", "11132242", "11141332", + "21114151", "21123241", "21132331", "21141421", "11114242", "11123332", + "11132422", "11141512", "21114331", "21123421", "21132511", "12141151", + "11232151", "11241241", "12123151", "12132241", "12141331", "11214151", + "11223241", "11232331", "11241421", "12114241", "12123331", "12132421", + "12141511", "11214331", "11223421", "11232511", "11151151", "11133151", + "11142241", "11151331", "11115151", "11124241", + /* Column 20 */ + "11133331", "11142421", "11151511", "11111254", "31111252", "21111343", + "41111341", "11111434", "31111432", "21111523", "41111521", "11111614", + "31111612", "31211161", "12111253", "32111251", "21211252", "22111342", + "11211343", "31211341", "12111433", "32111431", "21211432", "22111522", + "11211523", "31211521", "12111613", "32111611", "21211612", "12211162", + "21311161", "13111252", "22211251", "11311252", "23111341", "12211342", + "21311341", "13111432", "22211431", "11311432", "23111521", "12211522", + "21311521", "13111612", "22211611", "11311612", "13211161", "11411161", + "14111251", "12311251", "13211341", "11411341", "14111431", "12311431", + "13211521", "11411521", "14111611", "12311611", + /* Column 21 */ + "21121162", "11112163", "31112161", "11121253", "31121251", "21112252", + "21121342", "11112343", "31112341", "11121433", "31121431", "21112432", + "21121522", "11112523", "31112521", "11121613", "31121611", "22121161", + "11221162", "12112162", "12121252", "21221251", "22112251", "11212252", + "22121341", "11221342", "12112342", "12121432", "21221431", "22112431", + "11212432", "22121521", "11221522", "12112522", "12121612", "21221611", + "12221161", "13112161", "13121251", "11312161", "11321251", "32121115", + "52121113", "21221116", "41221114", "61221112", "22112116", "42112114", + "31212115", "51212113", "13121116", "33121114", "22221115", "42221113", + "11321116", "31321114", "51321112", "23112115", + /* Column 22 */ + "43112113", "12212116", "32212114", "52212112", "21312115", "41312113", + "61312111", "14121115", "34121113", "23221114", "43221112", "12321115", + "32321113", "52321111", "21421114", "41421112", "24112114", "13212115", + "33212113", "22312114", "42312112", "11412115", "31412113", "51412111", + "15121114", "24221113", "13321114", "33321112", "22421113", "42421111", + "11521114", "31521112", "25112113", "14212114", "34212112", "23312113", + "43312111", "12412114", "32412112", "21512113", "41512111", "16121113", + "25221112", "14321113", "34321111", "23421112", "12521113", "32521111", + "15212113", "24312112", "13412113", "33412111", "22512112", "11612113", + "31612111", "31131115", "51131113", "21122116", + /* Column 23 */ + "41122114", "61122112", "31113115", "51113113", "12131116", "32131114", + "52131112", "21231115", "41231113", "61231111", "22122115", "42122113", + "11222116", "31222114", "51222112", "12113116", "32113114", "52113112", + "21213115", "41213113", "61213111", "13131115", "33131113", "22231114", + "42231112", "11331115", "31331113", "51331111", "23122114", "43122112", + "12222115", "32222113", "52222111", "21322114", "41322112", "13113115", + "33113113", "22213114", "42213112", "11313115", "31313113", "51313111", + "14131114", "34131112", "23231113", "43231111", "12331114", "32331112", + "21431113", "41431111", "24122113", "13222114", "33222112", "22322113", + "42322111", "11422114", "31422112", "14113114", + /* Column 24 */ + "34113112", "23213113", "43213111", "12313114", "32313112", "21413113", + "41413111", "15131113", "24231112", "13331113", "33331111", "22431112", + "25122112", "14222113", "34222111", "23322112", "12422113", "32422111", + "21522112", "15113113", "24213112", "13313113", "33313111", "22413112", + "11513113", "31513111", "16131112", "25231111", "14331112", "23431111", + "15222112", "24322111", "13422112", "22522111", "16113112", "25213111", + "14313112", "23413111", "12513112", "21613111", "11141116", "31141114", + "51141112", "21132115", "41132113", "61132111", "11123116", "31123114", + "51123112", "21114115", "41114113", "61114111", "12141115", "32141113", + "52141111", "21241114", "41241112", "22132114", + /* Column 25 */ + "42132112", "11232115", "31232113", "51232111", "12123115", "32123113", + "52123111", "21223114", "41223112", "22114114", "42114112", "11214115", + "31214113", "51214111", "13141114", "33141112", "22241113", "42241111", + "11341114", "31341112", "23132113", "43132111", "12232114", "32232112", + "21332113", "41332111", "13123114", "33123112", "22223113", "42223111", + "11323114", "31323112", "23114113", "43114111", "12214114", "32214112", + "21314113", "41314111", "14141113", "34141111", "23241112", "12341113", + "32341111", "24132112", "13232113", "33232111", "22332112", "11432113", + "31432111", "14123113", "34123111", "23223112", "12323113", "32323111", + "21423112", "24114112", "13214113", "33214111", + /* Column 26 */ + "22314112", "11414113", "31414111", "15141112", "24241111", "13341112", + "25132111", "14232112", "23332111", "12432112", "15123112", "24223111", + "13323112", "22423111", "11523112", "25114111", "14214112", "23314111", + "12414112", "21514111", "16141111", "14341111", "15232111", "13432111", + "16123111", "14323111", "12523111", "15214111", "13414111", "11614111", + "11151115", "31151113", "51151111", "21142114", "41142112", "11133115", + "31133113", "51133111", "21124114", "41124112", "11115115", "31115113", + "51115111", "12151114", "32151112", "21251113", "41251111", "22142113", + "42142111", "11242114", "31242112", "12133114", "32133112", "21233113", + "41233111", "22124113", "42124111", "11224114", + /* Column 27 */ + "31224112", "12115114", "32115112", "21215113", "41215111", "13151113", + "33151111", "22251112", "23142112", "12242113", "32242111", "21342112", + "13133113", "33133111", "22233112", "11333113", "31333111", "23124112", + "12224113", "32224111", "21324112", "13115113", "33115111", "22215112", + "11315113", "31315111", "14151112", "23251111", "24142111", "13242112", + "22342111", "14133112", "23233111", "12333112", "21433111", "24124111", + "13224112", "22324111", "11424112", "14115112", "23215111", "12315112", + "21415111", "15151111", "14242111", "15133111", "13333111", "14224111", + "12424111", "15115111", "13315111", "11515111", "11161114", "31161112", + "21152113", "41152111", "11143114", "31143112", + /* Column 28 */ + "21134113", "41134111", "11125114", "31125112", "21116113", "41116111", + "12161113", "32161111", "22152112", "11252113", "31252111", "12143113", + "32143111", "21243112", "22134112", "11234113", "31234111", "12125113", + "32125111", "21225112", "22116112", "11216113", "31216111", "13161112", + "23152111", "12252112", "13143112", "22243111", "11343112", "23134111", + "12234112", "21334111", "13125112", "22225111", "11325112", "23116111", + "12216112", "21316111", "14161111", "13252111", "14143111", "12343111", + "13234111", "11434111", "14125111", "12325111", "13216111", "11416111", + "31111216", "51111214", "31211125", "51211123", "32111215", "52111213", + "21211216", "41211214", "61211212", "12211126", + /* Column 29 */ + "32211124", "52211122", "21311125", "41311123", "61311121", "13111216", + "33111214", "22211215", "42211213", "11311216", "31311214", "51311212", + "13211125", "33211123", "22311124", "42311122", "11411125", "31411123", + "51411121", "14111215", "34111213", "23211214", "43211212", "12311215", + "32311213", "52311211", "21411214", "41411212", "14211124", "34211122", + "23311123", "43311121", "12411124", "32411122", "21511123", "41511121", + "15111214", "24211213", "13311214", "33311212", "22411213", "42411211", + "11511214", "31511212", "15211123", "24311122", "13411123", "33411121", + "22511122", "11611123", "31611121", "16111213", "25211212", "14311213", + "34311211", "23411212", "12511213", "32511211", + /* Column 30 */ + "21611212", "21121126", "41121124", "61121122", "31112125", "51112123", + "31121215", "51121213", "21112216", "41112214", "61112212", "22121125", + "42121123", "11221126", "31221124", "51221122", "12112126", "32112124", + "52112122", "12121216", "32121214", "52121212", "21221215", "41221213", + "61221211", "22112215", "42112213", "11212216", "31212214", "51212212", + "23121124", "43121122", "12221125", "32221123", "52221121", "21321124", + "41321122", "13112125", "33112123", "13121215", "33121213", "11312125", + "22221214", "42221212", "11321215", "31321213", "51321211", "23112214", + "43112212", "12212215", "32212213", "52212211", "21312214", "41312212", + "24121123", "13221124", "33221122", "22321123", + /* Column 31 */ + "42321121", "11421124", "31421122", "14112124", "34112122", "14121214", + "34121212", "12312124", "23221213", "43221211", "12321214", "32321212", + "21421213", "41421211", "24112213", "13212214", "33212212", "22312213", + "42312211", "11412214", "31412212", "25121122", "14221123", "34221121", + "23321122", "12421123", "32421121", "21521122", "15112123", "15121213", + "13312123", "24221212", "13321213", "33321211", "11512123", "22421212", + "11521213", "31521211", "25112212", "14212213", "34212211", "23312212", + "12412213", "32412211", "21512212", "15221122", "24321121", "13421122", + "22521121", "16112122", "16121212", "14312122", "25221211", "14321212", + "12512122", "23421211", "12521212", "15212212", + /* Column 32 */ + "24312211", "13412212", "22512211", "11612212", "21131125", "41131123", + "61131121", "11122126", "31122124", "51122122", "11131216", "31131214", + "51131212", "21113125", "41113123", "61113121", "21122215", "41122213", + "61122211", "11113216", "31113214", "51113212", "22131124", "42131122", + "11231125", "31231123", "51231121", "12122125", "32122123", "52122121", + "12131215", "32131213", "52131211", "21231214", "41231212", "22113124", + "42113122", "11213125", "22122214", "42122212", "11222215", "31222213", + "51222211", "12113215", "32113213", "52113211", "21213214", "41213212", + "23131123", "43131121", "12231124", "32231122", "21331123", "41331121", + "13122124", "33122122", "13131214", "33131212", + /* Column 33 */ + "11322124", "22231213", "42231211", "11331214", "31331212", "23113123", + "43113121", "12213124", "23122213", "43122211", "12222214", "32222212", + "21322213", "41322211", "13113214", "33113212", "22213213", "42213211", + "11313214", "31313212", "24131122", "13231123", "33231121", "22331122", + "11431123", "31431121", "14122123", "34122121", "14131213", "34131211", + "12322123", "23231212", "12331213", "32331211", "21431212", "24113122", + "13213123", "24122212", "13222213", "33222211", "11413123", "22322212", + "11422213", "31422211", "14113213", "34113211", "23213212", "12313213", + "32313211", "21413212", "25131121", "14231122", "23331121", "12431122", + "15122122", "15131212", "13322122", "24231211", + /* Column 34 */ + "13331212", "11522122", "22431211", "25113121", "14213122", "25122211", + "14222212", "12413122", "23322211", "12422212", "21522211", "15113212", + "24213211", "13313212", "22413211", "11513212", "15231121", "13431121", + "16122121", "16131211", "14322121", "14331211", "12522121", "15213121", + "15222211", "13413121", "13422211", "11613121", "16113211", "14313211", + "12513211", "21141124", "41141122", "11132125", "31132123", "51132121", + "11141215", "31141213", "51141211", "21123124", "41123122", "21132214", + "41132212", "11114125", "31114123", "51114121", "11123215", "31123213", + "51123211", "21114214", "41114212", "22141123", "42141121", "11241124", + "31241122", "12132124", "32132122", "12141214", + /* Column 35 */ + "32141212", "21241213", "41241211", "22123123", "42123121", "11223124", + "22132213", "42132211", "11232214", "31232212", "12114124", "32114122", + "12123214", "32123212", "21223213", "41223211", "22114213", "42114211", + "11214214", "31214212", "23141122", "12241123", "32241121", "21341122", + "13132123", "33132121", "13141213", "33141211", "11332123", "22241212", + "11341213", "31341211", "23123122", "12223123", "23132212", "12232213", + "32232211", "21332212", "13114123", "33114121", "13123213", "33123211", + "11314123", "22223212", "11323213", "31323211", "23114212", "12214213", + "32214211", "21314212", "24141121", "13241122", "22341121", "14132122", + "14141212", "12332122", "23241211", "12341212", + /* Column 36 */ + "24123121", "13223122", "24132211", "13232212", "11423122", "22332211", + "11432212", "14114122", "14123212", "12314122", "23223211", "12323212", + "21423211", "24114211", "13214212", "22314211", "11414212", "14241121", + "15132121", "15141211", "13332121", "13341211", "14223121", "14232211", + "12423121", "12432211", "15114121", "15123211", "13314121", "13323211", + "11514121", "11523211", "14214211", "12414211", "21151123", "41151121", + "11142124", "31142122", "11151214", "31151212", "21133123", "41133121", + "21142213", "41142211", "11124124", "31124122", "11133214", "31133212", + "21115123", "41115121", "21124213", "41124211", "11115214", "31115212", + "22151122", "11251123", "31251121", "12142123", + /* Column 37 */ + "32142121", "12151213", "32151211", "21251212", "22133122", "11233123", + "22142212", "11242213", "31242211", "12124123", "32124121", "12133213", + "32133211", "21233212", "22115122", "11215123", "22124212", "11224213", + "31224211", "12115213", "32115211", "21215212", "23151121", "12251122", + "13142122", "13151212", "11342122", "22251211", "23133121", "12233122", + "23142211", "12242212", "21342211", "13124122", "13133212", "11324122", + "22233211", "11333212", "23115121", "12215122", "23124211", "12224212", + "21324211", "13115212", "22215211", "11315212", "13251121", "14142121", + "14151211", "12342121", "13233121", "13242211", "11433121", "14124121", + "14133211", "12324121", "12333211", "13215121", + /* Column 38 */ + "13224211", "11415121", "11424211", "14115211", "12315211", "21161122", + "11152123", "31152121", "11161213", "31161211", "21143122", "21152212", + "11134123", "31134121", "11143213", "31143211", "21125122", "21134212", + "11116123", "31116121", "11125213", "31125211", "22161121", "12152122", + "12161212", "22143121", "11243122", "22152211", "11252212", "12134122", + "12143212", "21243211", "22125121", "11225122", "22134211", "11234212", + "12116122", "12125212", "21225211", "13152121", "13161211", "12243121", + "12252211", "13134121", "13143211", "11334121", "11343211", "12225121", + "12234211", "13116121", "13125211", "11316121", "11325211", "21111226", + "41111224", "61111222", "31111315", "51111313", + /* Column 39 */ + "21211135", "41211133", "61211131", "22111225", "42111223", "11211226", + "31211224", "51211222", "12111316", "32111314", "52111312", "21211315", + "41211313", "61211311", "22211134", "42211132", "11311135", "31311133", + "51311131", "23111224", "43111222", "12211225", "32211223", "52211221", + "21311224", "41311222", "13111315", "33111313", "22211314", "42211312", + "11311315", "31311313", "51311311", "23211133", "43211131", "12311134", + "32311132", "21411133", "41411131", "24111223", "13211224", "33211222", + "22311223", "42311221", "11411224", "31411222", "14111314", "34111312", + "23211313", "43211311", "12311314", "32311312", "21411313", "41411311", + "24211132", "13311133", "33311131", "22411132", + /* Column 40 */ + "11511133", "31511131", "25111222", "14211223", "34211221", "23311222", + "12411223", "32411221", "21511222", "15111313", "24211312", "13311313", + "33311311", "22411312", "11511313", "31511311", "25211131", "14311132", + "23411131", "12511132", "21611131", "15211222", "24311221", "13411222", + "22511221", "11611222", "16111312", "25211311", "14311312", "23411311", + "12511312", "21611311", "31121134", "51121132", "21112135", "41112133", + "61112131", "21121225", "41121223", "61121221", "11112226", "31112224", + "51112222", "11121316", "31121314", "51121312", "21112315", "41112313", + "61112311", "12121135", "32121133", "52121131", "21221134", "41221132", + "22112134", "42112132", "11212135", "22121224", + /* Column 41 */ + "42121222", "11221225", "31221223", "51221221", "12112225", "32112223", + "52112221", "12121315", "32121313", "52121311", "21221314", "41221312", + "22112314", "42112312", "11212315", "31212313", "51212311", "13121134", + "33121132", "22221133", "42221131", "11321134", "31321132", "23112133", + "43112131", "12212134", "23121223", "43121221", "12221224", "32221222", + "21321223", "41321221", "13112224", "33112222", "13121314", "33121312", + "11312224", "22221313", "42221311", "11321314", "31321312", + /* Column 42 */ + "23112313", "43112311", "12212314", "32212312", "21312313", "41312311", + "14121133", "34121131", "23221132", "12321133", "32321131", "21421132", + "24112132", "13212133", "24121222", "13221223", "33221221", "11412133", + "22321222", "11421223", "31421221", "14112223", "34112221", "14121313", + "34121311", "12312223", "23221312", "12321313", "32321311", "21421312", + "24112312", "13212313", "33212311", "22312312", "11412313", "31412311", + "15121132", "24221131", "13321132", "22421131" + }; + + private String[] c49_appxe_odd = { + /* Appendix E - Code 49 Encodation Patterns (Odd Symbol Character Parity) */ + /* Column 1 */ + "22121116", "42121114", "31221115", "51221113", "32112115", "52112113", + "21212116", "41212114", "61212112", "23121115", "43121113", "12221116", + "32221114", "52221112", "21321115", "41321113", "61321111", "13112116", + "33112114", "22212115", "42212113", "11312116", "31312114", "51312112", + "24121114", "13221115", "33221113", "22321114", "42321112", "11421115", + "31421113", "51421111", "14112115", "34112113", "23212114", "43212112", + "12312115", "32312113", "52312111", "21412114", "41412112", "25121113", + "14221114", "34221112", "23321113", "43321111", "12421114", "32421112", + "21521113", "41521111", "15112114", "24212113", "13312114", "33312112", + "22412113", "42412111", "11512114", "31512112", + /* Column 2 */ + "15221113", "24321112", "13421113", "33421111", "22521112", "16112113", + "25212112", "14312113", "34312111", "23412112", "12512113", "32512111", + "21612112", "21131116", "41131114", "61131112", "31122115", "51122113", + "21113116", "41113114", "61113112", "22131115", "42131113", "11231116", + "31231114", "51231112", "12122116", "32122114", "52122112", "21222115", + "41222113", "61222111", "22113115", "42113113", "11213116", "31213114", + "51213112", "23131114", "43131112", "12231115", "32231113", "52231111", + "21331114", "41331112", "13122115", "33122113", "22222114", "42222112", + "11322115", "31322113", "51322111", "23113114", "43113112", "12213115", + "32213113", "52213111", "21313114", "41313112", + /* Column 3 */ + "24131113", "13231114", "33231112", "22331113", "42331111", "11431114", + "31431112", "14122114", "34122112", "23222113", "43222111", "12322114", + "32322112", "21422113", "41422111", "24113113", "13213114", "33213112", + "22313113", "42313111", "11413114", "31413112", "25131112", "14231113", + "34231111", "23331112", "12431113", "32431111", "15122113", "24222112", + "13322113", "33322111", "22422112", "11522113", "31522111", "25113112", + "14213113", "34213111", "23313112", "12413113", "32413111", "21513112", + "15231112", "24331111", "13431112", "16122112", "25222111", "14322112", + "23422111", "12522112", "15213112", "24313111", "13413112", "22513111", + "11613112", "21141115", "41141113", "61141111", + /* Column 4 */ + "11132116", "31132114", "51132112", "21123115", "41123113", "61123111", + "11114116", "31114114", "51114112", "22141114", "42141112", "11241115", + "31241113", "51241111", "12132115", "32132113", "52132111", "21232114", + "41232112", "22123114", "42123112", "11223115", "31223113", "51223111", + "12114115", "32114113", "52114111", "21214114", "41214112", "23141113", + "43141111", "12241114", "32241112", "21341113", "41341111", "13132114", + "33132112", "22232113", "42232111", "11332114", "31332112", "23123113", + "43123111", "12223114", "32223112", "21323113", "41323111", "13114114", + "33114112", "22214113", "42214111", "11314114", "31314112", "24141112", + "13241113", "33241111", "22341112", "14132113", + /* Column 5 */ + "34132111", "23232112", "12332113", "32332111", "21432112", "24123112", + "13223113", "33223111", "22323112", "11423113", "31423111", "14114113", + "34114111", "23214112", "12314113", "32314111", "21414112", "25141111", + "14241112", "23341111", "15132112", "24232111", "13332112", "22432111", + "25123111", "14223112", "23323111", "12423112", "21523111", "15114112", + "24214111", "13314112", "22414111", "11514112", "15241111", "16132111", + "14332111", "15223111", "13423111", "16114111", "14314111", "12514111", + "21151114", "41151112", "11142115", "31142113", "51142111", "21133114", + "41133112", "11124115", "31124113", "51124111", "21115114", "41115112", + "22151113", "42151111", "11251114", "31251112", + /* Column 6 */ + "12142114", "32142112", "21242113", "41242111", "22133113", "42133111", + "11233114", "31233112", "12124114", "32124112", "21224113", "41224111", + "22115113", "42115111", "11215114", "31215112", "23151112", "12251113", + "32251111", "13142113", "33142111", "22242112", "11342113", "31342111", + "23133112", "12233113", "32233111", "21333112", "13124113", "33124111", + "22224112", "11324113", "31324111", "23115112", "12215113", "32215111", + "21315112", "24151111", "13251112", "14142112", "23242111", "12342112", + "24133111", "13233112", "22333111", "11433112", "14124112", "23224111", + "12324112", "21424111", "24115111", "13215112", "22315111", "11415112", + "14251111", "15142111", "13342111", "14233111", + /* Column 7 */ + "12433111", "15124111", "13324111", "11524111", "14215111", "12415111", + "21161113", "41161111", "11152114", "31152112", "21143113", "41143111", + "11134114", "31134112", "21125113", "41125111", "11116114", "31116112", + "22161112", "12152113", "32152111", "21252112", "22143112", "11243113", + "31243111", "12134113", "32134111", "21234112", "22125112", "11225113", + "31225111", "12116113", "32116111", "21216112", "23161111", "13152112", + "22252111", "23143111", "12243112", "21343111", "13134112", "22234111", + "11334112", "23125111", "12225112", "21325111", "13116112", "22216111", + "11316112", "14152111", "13243111", "14134111", "12334111", "13225111", + "11425111", "14116111", "12316111", "41111215", + /* Column 8 */ + "61111213", "21211126", "41211124", "61211122", "22111216", "42111214", + "31211215", "51211213", "22211125", "42211123", "11311126", "31311124", + "51311122", "23111215", "43111213", "12211216", "32211214", "52211212", + "21311215", "41311213", "61311211", "23211124", "43211122", "12311125", + "32311123", "52311121", "21411124", "41411122", "24111214", "13211215", + "33211213", "22311214", "42311212", "11411215", "31411213", "51411211", + "24211123", "13311124", "33311122", "22411123", "42411121", "11511124", + "31511122", "25111213", "14211214", "34211212", "23311213", "43311211", + "12411214", "32411212", "21511213", "41511211", "25211122", "14311123", + "34311121", "23411122", "12511123", "32511121", + /* Column 9 */ + "21611122", "15211213", "24311212", "13411213", "33411211", "22511212", + "11611213", "31611211", "31121125", "51121123", "21112126", "41112124", + "61112122", "21121216", "41121214", "61121212", "31112215", "51112213", + "12121126", "32121124", "52121122", "21221125", "41221123", "61221121", + "22112125", "42112123", "11212126", "22121215", "42121213", "11221216", + "31221214", "51221212", "12112216", "32112214", "52112212", "21212215", + "41212213", "61212211", "13121125", "33121123", "22221124", "42221122", + "11321125", "31321123", "51321121", "23112124", "43112122", "12212125", + "23121214", "43121212", "12221215", "32221213", "52221211", "21321214", + "41321212", "13112215", "33112213", "22212214", + /* Column 10 */ + "42212212", "11312215", "31312213", "51312211", "14121124", "34121122", + "23221123", "43221121", "12321124", "32321122", "21421123", "41421121", + "24112123", "13212124", "24121213", "13221214", "33221212", "11412124", + "22321213", "42321211", "11421214", "31421212", "14112214", "34112212", + "23212213", "43212211", "12312214", "32312212", "21412213", "41412211", + "15121123", "24221122", "13321123", "33321121", "22421122", "11521123", + "31521121", "25112122", "14212123", "25121212", "14221213", "34221211", + "12412123", "23321212", "12421213", "32421211", "21521212", "15112213", + "24212212", "13312213", "33312211", "22412212", "11512213", "31512211", + "16121122", "25221121", "14321122", "23421121", + /* Column 11 */ + "12521122", "15212122", "15221212", "13412122", "24321211", "13421212", + "11612122", "22521211", "16112212", "25212211", "14312212", "23412211", + "12512212", "21612211", "11131126", "31131124", "51131122", "21122125", + "41122123", "61122121", "21131215", "41131213", "61131211", "11113126", + "31113124", "51113122", "11122216", "31122214", "51122212", "21113215", + "41113213", "61113211", "12131125", "32131123", "52131121", "21231124", + "41231122", "22122124", "42122122", "11222125", "22131214", "42131212", + "11231215", "31231213", "51231211", "12113125", "32113123", "52113121", + "12122215", "32122213", "52122211", "21222214", "41222212", "22113214", + "42113212", "11213215", "31213213", "51213211", + /* Column 12 */ + "13131124", "33131122", "22231123", "42231121", "11331124", "31331122", + "23122123", "43122121", "12222124", "23131213", "43131211", "12231214", + "32231212", "21331213", "41331211", "13113124", "33113122", "13122214", + "33122212", "11313124", "22222213", "42222211", "11322214", "31322212", + "23113213", "43113211", "12213214", "32213212", "21313213", "41313211", + "14131123", "34131121", "23231122", "12331123", "32331121", "21431122", + "24122122", "13222123", "24131212", "13231213", "33231211", "11422123", + "22331212", "11431213", "31431211", "14113123", "34113121", "14122213", + "34122211", "12313123", "23222212", "12322213", "32322211", "21422212", + "24113212", "13213213", "33213211", "22313212", + /* Column 13 */ + "11413213", "31413211", "15131122", "24231121", "13331122", "22431121", + "25122121", "14222122", "25131211", "14231212", "12422122", "23331211", + "12431212", "15113122", "15122212", "13313122", "24222211", "13322212", + "11513122", "22422211", "11522212", "25113211", "14213212", "23313211", + "12413212", "21513211", "16131121", "14331121", "15222121", "15231211", + "13422121", "13431211", "16113121", "16122211", "14313121", "14322211", + "12513121", "12522211", "15213211", "13413211", "11613211", "11141125", + "31141123", "51141121", "21132124", "41132122", "21141214", "41141212", + "11123125", "31123123", "51123121", "11132215", "31132213", "51132211", + "21114124", "41114122", "21123214", "41123212", + /* Column 14 */ + "11114215", "31114213", "51114211", "12141124", "32141122", "21241123", + "41241121", "22132123", "42132121", "11232124", "22141213", "42141211", + "11241214", "31241212", "12123124", "32123122", "12132214", "32132212", + "21232213", "41232211", "22114123", "42114121", "11214124", "22123213", + "42123211", "11223214", "31223212", "12114214", "32114212", "21214213", + "41214211", "13141123", "33141121", "22241122", "11341123", "31341121", + "23132122", "12232123", "23141212", "12241213", "32241211", "21341212", + "13123123", "33123121", "13132213", "33132211", "11323123", "22232212", + "11332213", "31332211", "23114122", "12214123", "23123212", "12223213", + "32223211", "21323212", "13114213", "33114211", + /* Column 15 */ + "22214212", "11314213", "31314211", "14141122", "23241121", "12341122", + "24132121", "13232122", "24141211", "13241212", "11432122", "22341211", + "14123122", "14132212", "12323122", "23232211", "12332212", "21432211", + "24114121", "13214122", "24123211", "13223212", "11414122", "22323211", + "11423212", "14114212", "23214211", "12314212", "21414211", "15141121", + "13341121", "14232121", "14241211", "12432121", "15123121", "15132211", + "13323121", "13332211", "11523121", "14214121", "14223211", "12414121", + "12423211", "15114211", "13314211", "11514211", "11151124", "31151122", + "21142123", "41142121", "21151213", "41151211", "11133124", "31133122", + "11142214", "31142212", "21124123", "41124121", + /* Column 16 */ + "21133213", "41133211", "11115124", "31115122", "11124214", "31124212", + "21115213", "41115211", "12151123", "32151121", "21251122", "22142122", + "11242123", "22151212", "11251213", "31251211", "12133123", "32133121", + "12142213", "32142211", "21242212", "22124122", "11224123", "22133212", + "11233213", "31233211", "12115123", "32115121", "12124213", "32124211", + "21224212", "22115212", "11215213", "31215211", "13151122", "22251121", + "23142121", "12242122", "23151211", "12251212", "13133122", "13142212", + "11333122", "22242211", "11342212", "23124121", "12224122", "23133211", + "12233212", "21333211", "13115122", "13124212", "11315122", "22224211", + "11324212", "23115211", "12215212", "21315211", + /* Column 17 */ + "14151121", "13242121", "13251211", "14133121", "14142211", "12333121", + "12342211", "13224121", "13233211", "11424121", "11433211", "14115121", + "14124211", "12315121", "12324211", "13215211", "11415211", "11161123", + "31161121", "21152122", "21161212", "11143123", "31143121", "11152213", + "31152211", "21134122", "21143212", "11125123", "31125121", "11134213", + "31134211", "21116122", "21125212", "12161122", "22152121", "11252122", + "22161211", "12143122", "12152212", "21252211", "22134121", "11234122", + "22143211", "11243212", "12125122", "12134212", "21234211", "22116121", + "11216122", "22125211", "11225212", "13161121", "12252121", "13143121", + "13152211", "11343121", "12234121", "12243211", + /* Column 18 */ + "13125121", "13134211", "11325121", "11334211", "12216121", "12225211", + "31111225", "51111223", "21111316", "41111314", "61111312", "31211134", + "51211132", "12111226", "32111224", "52111222", "21211225", "41211223", + "61211221", "22111315", "42111313", "11211316", "31211314", "51211312", + "12211135", "32211133", "52211131", "21311134", "41311132", "13111225", + "33111223", "22211224", "42211222", "11311225", "31311223", "51311221", + "23111314", "43111312", "12211315", "32211313", "52211311", "21311314", + "41311312", "13211134", "33211132", "22311133", "42311131", "11411134", + "31411132", "14111224", "34111222", "23211223", "43211221", "12311224", + "32311222", "21411223", "41411221", "24111313", + /* Column 19 */ + "13211314", "33211312", "22311313", "42311311", "11411314", "31411312", + "14211133", "34211131", "23311132", "12411133", "32411131", "21511132", + "15111223", "24211222", "13311223", "33311221", "22411222", "11511223", + "31511221", "25111312", "14211313", "34211311", "23311312", "12411313", + "32411311", "21511312", "15211132", "24311131", "13411132", "22511131", + "11611132", "16111222", "25211221", "14311222", "23411221", "12511222", + "21611221", "15211312", "24311311", "13411312", "22511311", "11611312", + "21121135", "41121133", "61121131", "11112136", "31112134", "51112132", + "11121226", "31121224", "51121222", "21112225", "41112223", "61112221", + "21121315", "41121313", "61121311", "11112316", + /* Column 20 */ + "31112314", "51112312", "22121134", "42121132", "11221135", "31221133", + "51221131", "12112135", "32112133", "52112131", "12121225", "32121223", + "52121221", "21221224", "41221222", "22112224", "42112222", "11212225", + "22121314", "42121312", "11221315", "31221313", "51221311", "12112315", + "32112313", "52112311", "21212314", "41212312", "23121133", "43121131", + "12221134", "32221132", "21321133", "41321131", "13112134", "33112132", + "13121224", "33121222", "11312134", "22221223", "42221221", "11321224", + "31321222", "23112223", "43112221", "12212224", "23121313", "43121311", + "12221314", "32221312", "21321313", "41321311", "13112314", "33112312", + "22212313", "42212311", "11312314", "31312312", + /* Column 21 */ + "24121132", "13221133", "33221131", "22321132", "11421133", "31421131", + "14112133", "34112131", "14121223", "34121221", "12312133", "23221222", + "12321223", "32321221", "21421222", "24112222", "13212223", "24121312", + "13221313", "33221311", "11412223", "22321312", "11421313", "31421311", + "14112313", "34112311", "23212312", "12312313", "32312311", "21412312", + "25121131", "14221132", "23321131", "12421132", "21521131", "15112132", + "15121222", "13312132", "24221221", "13321222", "11512132", "22421221", + "11521222", "25112221", "14212222", "25121311", "14221312", "12412222", + "23321311", "12421312", "21521311", "15112312", "24212311", "13312312", + "22412311", "11512312", "15221131", "13421131", + /* Column 22 */ + "16112131", "16121221", "14312131", "14321221", "12512131", "12521221", + "15212221", "15221311", "13412221", "13421311", "11612221", "16112311", + "14312311", "12512311", "21131134", "41131132", "11122135", "31122133", + "51122131", "11131225", "31131223", "51131221", "21113134", "41113132", + "21122224", "41122222", "21131314", "41131312", "11113225", "31113223", + "51113221", "11122315", "31122313", "51122311", "21113314", "41113312", + "22131133", "42131131", "11231134", "31231132", "12122134", "32122132", + "12131224", "32131222", "21231223", "41231221", "22113133", "42113131", + "11213134", "22122223", "42122221", "11222224", "22131313", "42131311", + "11231314", "31231312", "12113224", "32113222", + /* Column 23 */ + "12122314", "32122312", "21222313", "41222311", "22113313", "42113311", + "11213314", "31213312", "23131132", "12231133", "32231131", "21331132", + "13122133", "33122131", "13131223", "33131221", "11322133", "22231222", + "11331223", "31331221", "23113132", "12213133", "23122222", "12222223", + "23131312", "12231313", "32231311", "21331312", "13113223", "33113221", + "13122313", "33122311", "11313223", "22222312", "11322313", "31322311", + "23113312", "12213313", "32213311", "21313312", "24131131", "13231132", + "22331131", "11431132", "14122132", "14131222", "12322132", "23231221", + "12331222", "21431221", "24113131", "13213132", "24122221", "13222222", + "24131311", "11413132", "13231312", "11422222", + /* Column 24 */ + "22331311", "11431312", "14113222", "14122312", "12313222", "23222311", + "12322312", "21422311", "24113311", "13213312", "22313311", "11413312", + "14231131", "12431131", "15122131", "15131221", "13322131", "13331221", + "11522131", "14213131", "14222221", "12413131", "14231311", "12422221", + "12431311", "15113221", "15122311", "13313221", "13322311", "11513221", + "11522311", "14213311", "12413311", "21141133", "41141131", "11132134", + "31132132", "11141224", "31141222", "21123133", "41123131", "21132223", + "41132221", "21141313", "41141311", "11114134", "31114132", "11123224", + "31123222", "11132314", "31132312", "21114223", "41114221", "21123313", + "41123311", "11114314", "31114312", "22141132", + /* Column 25 */ + "11241133", "31241131", "12132133", "32132131", "12141223", "32141221", + "21241222", "22123132", "11223133", "22132222", "11232223", "22141312", + "11241313", "31241311", "12114133", "32114131", "12123223", "32123221", + "12132313", "32132311", "21232312", "22114222", "11214223", "22123312", + "11223313", "31223311", "12114313", "32114311", "21214312", "23141131", + "12241132", "21341131", "13132132", "13141222", "11332132", "22241221", + "11341222", "23123131", "12223132", "23132221", "12232222", "23141311", + "12241312", "21341311", "13114132", "13123222", "11314132", "13132312", + "11323222", "22232311", "11332312", "23114221", "12214222", "23123311", + "12223312", "21323311", "13114312", "22214311", + /* Column 26 */ + "11314312", "13241131", "14132131", "14141221", "12332131", "12341221", + "13223131", "13232221", "11423131", "13241311", "11432221", "14114131", + "14123221", "12314131", "14132311", "12323221", "12332311", "13214221", + "13223311", "11414221", "11423311", "14114311", "12314311", "21151132", + "11142133", "31142131", "11151223", "31151221", "21133132", "21142222", + "21151312", "11124133", "31124131", "11133223", "31133221", "11142313", + "31142311", "21115132", "21124222", "21133312", "11115223", "31115221", + "11124313", "31124311", "22151131", "11251132", "12142132", "12151222", + "21251221", "22133131", "11233132", "22142221", "11242222", "22151311", + "11251312", "12124132", "12133222", "12142312", + /* Column 27 */ + "21242311", "22115131", "11215132", "22124221", "11224222", "22133311", + "11233312", "12115222", "12124312", "21224311", "12251131", "13142131", + "13151221", "11342131", "12233131", "12242221", "12251311", "13124131", + "13133221", "11324131", "13142311", "11333221", "11342311", "12215131", + "12224221", "12233311", "13115221", "13124311", "11315221", "11324311", + "21161131", "11152132", "11161222", "21143131", "21152221", "21161311", + "11134132", "11143222", "11152312", "21125131", "21134221", "21143311", + "11116132", "11125222", "11134312", "12152131", "12161221", "11243131", + "11252221", "12134131", "12143221", "12152311", "11225131", "11234221", + "11243311", "12116131", "12125221", "12134311", + /* Column 28 */ + "21111235", "41111233", "61111231", "11111326", "31111324", "51111322", + "21111415", "41111413", "61111411", "21211144", "41211142", "22111234", + "42111232", "11211235", "31211233", "51211231", "12111325", "32111323", + "52111321", "21211324", "41211322", "22111414", "42111412", "11211415", + "31211413", "51211411", "22211143", "42211141", "11311144", "31311142", + "23111233", "43111231", "12211234", "32211232", "21311233", "41311231", + "13111324", "33111322", "22211323", "42211321", "11311324", "31311322", + "23111413", "43111411", "12211414", "32211412", "21311413", "41311411", + "23211142", "12311143", "32311141", "21411142", "24111232", "13211233", + "33211231", "22311232", "11411233", "31411231", + /* Column 29 */ + "14111323", "34111321", "23211322", "12311323", "32311321", "21411322", + "24111412", "13211413", "33211411", "22311412", "11411413", "31411411", + "24211141", "13311142", "22411141", "11511142", "25111231", "14211232", + "23311231", "12411232", "21511231", "15111322", "24211321", "13311322", + "22411321", "11511322", "25111411", "14211412", "23311411", "12411412", + "21511411", "14311141", "12511141", "15211231", "13411231", "11611231", + "16111321", "14311321", "12511321", "15211411", "13411411", "11611411", + "31121143", "51121141", "21112144", "41112142", "21121234", "41121232", + "11112235", "31112233", "51112231", "11121325", "31121323", "51121321", + "21112324", "41112322", "21121414", "41121412", + /* Column 30 */ + "11112415", "31112413", "51112411", "12121144", "32121142", "21221143", + "41221141", "22112143", "42112141", "11212144", "22121233", "42121231", + "11221234", "31221232", "12112234", "32112232", "12121324", "32121322", + "21221323", "41221321", "22112323", "42112321", "11212324", "22121413", + "42121411", "11221414", "31221412", "12112414", "32112412", "21212413", + "41212411", "13121143", "33121141", "22221142", "11321143", "31321141", + "23112142", "12212143", "23121232", "12221233", "32221231", "21321232", + "13112233", "33112231", "13121323", "33121321", "11312233", "22221322", + "11321323", "31321321", "23112322", "12212323", "23121412", "12221413", + "32221411", "21321412", "13112413", "33112411", + /* Column 31 */ + "22212412", "11312413", "31312411", "14121142", "23221141", "12321142", + "21421141", "24112141", "13212142", "24121231", "13221232", "11412142", + "22321231", "11421232", "14112232", "14121322", "12312232", "23221321", + "12321322", "21421321", "24112321", "13212322", "24121411", "13221412", + "11412322", "22321411", "11421412", "14112412", "23212411", "12312412", + "21412411", "15121141", "13321141", "11521141", "14212141", "14221231", + "12412141", "12421231", "15112231", "15121321", "13312231", "13321321", + "11512231", "11521321", "14212321", "14221411", "12412321", "12421411", + "15112411", "13312411", "11512411", "11131144", "31131142", "21122143", + "41122141", "21131233", "41131231", "11113144", + /* Column 32 */ + "31113142", "11122234", "31122232", "11131324", "31131322", "21113233", + "41113231", "21122323", "41122321", "21131413", "41131411", "11113324", + "31113322", "11122414", "31122412", "21113413", "41113411", "12131143", + "32131141", "21231142", "22122142", "11222143", "22131232", "11231233", + "31231231", "12113143", "32113141", "12122233", "32122231", "12131323", + "32131321", "21231322", "22113232", "11213233", "22122322", "11222323", + "22131412", "11231413", "31231411", "12113323", "32113321", "12122413", + "32122411", "21222412", "22113412", "11213413", "31213411", "13131142", + "22231141", "11331142", "23122141", "12222142", "23131231", "12231232", + "21331231", "13113142", "13122232", "11313142", + /* Column 33 */ + "13131322", "11322232", "22231321", "11331322", "23113231", "12213232", + "23122321", "12222322", "23131411", "12231412", "21331411", "13113322", + "13122412", "11313322", "22222411", "11322412", "23113411", "12213412", + "21313411", "14131141", "12331141", "13222141", "13231231", "11422141", + "11431231", "14113141", "14122231", "12313141", "14131321", "12322231", + "12331321", "13213231", "13222321", "11413231", "13231411", "11422321", + "11431411", "14113321", "14122411", "12313321", "12322411", "13213411", + "11413411", "11141143", "31141141", "21132142", "21141232", "11123143", + "31123141", "11132233", "31132231", "11141323", "31141321", "21114142", + "21123232", "21132322", "21141412", "11114233", + /* Column 34 */ + "31114231", "11123323", "31123321", "11132413", "31132411", "21114322", + "21123412", "12141142", "21241141", "22132141", "11232142", "22141231", + "11241232", "12123142", "12132232", "12141322", "21241321", "22114141", + "11214142", "22123231", "11223232", "22132321", "11232322", "22141411", + "11241412", "12114232", "12123322", "12132412", "21232411", "22114321", + "11214322", "22123411", "11223412", "13141141", "11341141", "12232141", + "12241231", "13123141", "13132231", "11323141", "13141321", "11332231", + "11341321", "12214141", "12223231", "12232321", "12241411", "13114231", + "13123321", "11314231", "13132411", "11323321", "11332411", "12214321", + "12223411", "11151142", "21142141", "21151231", + /* Column 35 */ + "11133142", "11142232", "11151322", "21124141", "21133231", "21142321", + "21151411", "11115142", "11124232", "11133322", "11142412", "21115231", + "21124321", "21133411", "12151141", "11242141", "11251231", "12133141", + "12142231", "12151321", "11224141", "11233231", "11242321", "11251411", + "12115141", "12124231", "12133321", "12142411", "11215231", "11224321", + "11233411", "11161141", "11143141", "11152231", "11161321", "11125141", + "11134231", "11143321", "11152411", "11111245", "31111243", "51111241", + "21111334", "41111332", "11111425", "31111423", "51111421", "21111514", + "41111512", "31211152", "12111244", "32111242", "21211243", "41211241", + "22111333", "42111331", "11211334", "31211332", + /* Column 36 */ + "12111424", "32111422", "21211423", "41211421", "22111513", "42111511", + "11211514", "31211512", "12211153", "32211151", "21311152", "13111243", + "33111241", "22211242", "11311243", "31311241", "23111332", "12211333", + "32211331", "21311332", "13111423", "33111421", "22211422", "11311423", + "31311421", "23111512", "12211513", "32211511", "21311512", "13211152", + "22311151", "11411152", "14111242", "23211241", "12311242", "21411241", + "24111331", "13211332", "22311331", "11411332", "14111422", "23211421", + "12311422", "21411421", "24111511", "13211512", "22311511", "11411512", + "14211151", "12411151", "15111241", "13311241", "11511241", "14211331", + "12411331", "15111421", "13311421", "11511421", + /* Column 37 */ + "14211511", "12411511", "21121153", "41121151", "11112154", "31112152", + "11121244", "31121242", "21112243", "41112241", "21121333", "41121331", + "11112334", "31112332", "11121424", "31121422", "21112423", "41112421", + "21121513", "41121511", "11112514", "31112512", "22121152", "11221153", + "31221151", "12112153", "32112151", "12121243", "32121241", "21221242", + "22112242", "11212243", "22121332", "11221333", "31221331", "12112333", + "32112331", "12121423", "32121421", "21221422", "22112422", "11212423", + "22121512", "11221513", "31221511", "12112513", "32112511", "21212512", + "23121151", "12221152", "21321151", "13112152", "13121242", "11312152", + "22221241", "11321242", "23112241", "12212242", + /* Column 38 */ + "23121331", "12221332", "21321331", "13112332", "13121422", "11312332", + "22221421", "11321422", "23112421", "12212422", "23121511", "12221512", + "21321511", "13112512", "22212511", "11312512", "13221151", "11421151", + "14112151", "14121241", "12312151", "12321241", "13212241", "13221331", + "11412241", "11421331", "14112331", "14121421", "12312331", "12321421", + "13212421", "13221511", "11412421", "11421511", "14112511", "12312511", + "21131152", "11122153", "31122151", "11131243", "31131241", "21113152", + "21122242", "21131332", "11113243", "31113241", "11122333", "31122331", + "11131423", "31131421", "21113332", "21122422", "21131512", "11113423", + "31113421", "11122513", "31122511", "22131151", + /* Column 39 */ + "11231152", "12122152", "12131242", "21231241", "22113151", "11213152", + "22122241", "11222242", "22131331", "11231332", "12113242", "12122332", + "12131422", "21231421", "22113331", "11213332", "22122421", "11222422", + "22131511", "11231512", "12113422", "12122512", "21222511", "12231151", + "13122151", "13131241", "11322151", "11331241", "12213151", "12222241", + "12231331", "13113241", "13122331", "11313241", "13131421", "11322331", + "11331421", "12213331", "12222421", "12231511", "13113421", "13122511", + "11313421", "11322511", "21141151", "11132152", "11141242", "21123151", + "21132241", "21141331", "11114152", "11123242", "11132332", "11141422", + "21114241", "21123331", "21132421", "21141511", + /* Column 40 */ + "11114332", "11123422", "11132512", "11241151", "12132151", "12141241", + "11223151", "11232241", "11241331", "12114151", "12123241", "12132331", + "12141421", "11214241", "11223331", "11232421", "11241511", "12114331", + "12123421", "12132511", "11142151", "11151241", "11124151", "11133241", + "11142331", "11151421", "11115241", "11124331", "11133421", "11142511", + "21111253", "41111251", "11111344", "31111342", "21111433", "41111431", + "11111524", "31111522", "21111613", "41111611", "21211162", "22111252", + "11211253", "31211251", "12111343", "32111341", "21211342", "22111432", + "11211433", "31211431", "12111523", "32111521", "21211522", "22111612", + "11211613", "31211611", "22211161", "11311162", + /* Column 41 */ + "23111251", "12211252", "21311251", "13111342", "22211341", "11311342", + "23111431", "12211432", "21311431", "13111522", "22211521", "11311522", + "23111611", "12211612", "21311611", "12311161", "13211251", "11411251", + "14111341", "12311341", "13211431", "11411431", "14111521", "12311521", + "13211611", "11411611", "31121161", "21112162", "21121252", "11112253", + "31112251", "11121343", "31121341", "21112342", "21121432", "11112433", + "31112431", "11121523", "31121521", "21112522", "21121612", + /* Column 42 */ + "12121162", "21221161", "22112161", "11212162", "22121251", "11221252", + "12112252", "12121342", "21221341", "22112341", "11212342", "22121431", + "11221432", "12112432", "12121522", "21221521", "22112521", "11212522", + "22121611", "11221612", "13121161", "11321161", "12212161", "12221251", + "13112251", "13121341", "11312251", "11321341", "12212341", "12221431", + "13112431", "13121521", "11312431", "11321521", "12212521", "12221611", + "11131162", "21122161", "21131251", "11113162" + }; + + private char[] C49_Set = { + '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D', + 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R', + 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z', '-', '.', ' ', '$', '/', '+', + '%', '!', '&', '*' + }; + + @Override + public boolean encode() { + int length = content.length(); + int i, codeword_count = 0, h, j, M, rows, pad_count = 0; + int x_count, y_count, z_count, posn_val, local_value; + StringBuilder intermediate = new StringBuilder(); + StringBuilder localpattern; + int[] codewords = new int[170]; + int[][] c_grid = new int[8][8]; + int[][] w_grid = new int[8][4]; + + if (!content.matches("[\u0000-\u007F]+")) { + errorMsg.append("Invalid characters in input data"); + return false; + } + + if (inputDataType == DataType.GS1) { + intermediate.append("*"); // FNC1 + } + for (i = 0; i < length; i++) { + if (content.charAt(i) > 127) { + errorMsg.append("Invalid characters in input"); + return false; + } + if ((inputDataType == DataType.GS1) && (content.charAt(i) == '[')) { + intermediate.append("*"); // FNC1 + } else { + intermediate.append(c49_table7[content.charAt(i)]); + } + } + + h = intermediate.length(); + + i = 0; + do { + if ((intermediate.charAt(i) >= '0') && (intermediate.charAt(i) <= '9')) { + + /* Numeric data */ + int latch = 0; + j = 0; + do { + if ((i + j) >= h) { + latch = 1; + } else { + if ((intermediate.charAt(i + j) >= '0') && (intermediate.charAt(i + j) <= '9')) { + j++; + } else { + latch = 1; + } + } + } while (latch == 0); + if (j >= 5) { + /* Use Numeric Encodation Method */ + int block_count, c; + int block_remain; + int block_value; + + codewords[codeword_count] = 48; /* Numeric Shift */ + codeword_count++; + + block_count = j / 5; + block_remain = j % 5; + + for (c = 0; c < block_count; c++) { + if ((c == block_count - 1) && (block_remain == 2)) { + /* Rule (d) */ + block_value = 100000; + block_value += (intermediate.charAt(i) - '0') * 1000; + block_value += (intermediate.charAt(i + 1) - '0') * 100; + block_value += (intermediate.charAt(i + 2) - '0') * 10; + block_value += intermediate.charAt(i + 3) - '0'; + + codewords[codeword_count] = block_value / (48 * 48); + block_value = block_value - (48 * 48) * codewords[codeword_count]; + codeword_count++; + codewords[codeword_count] = block_value / 48; + block_value = block_value - 48 * codewords[codeword_count]; + codeword_count++; + codewords[codeword_count] = block_value; + codeword_count++; + i += 4; + block_value = (intermediate.charAt(i) - '0') * 100; + block_value += (intermediate.charAt(i + 1) - '0') * 10; + block_value += intermediate.charAt(i + 2) - '0'; + + codewords[codeword_count] = block_value / 48; + block_value = block_value - 48 * codewords[codeword_count]; + codeword_count++; + codewords[codeword_count] = block_value; + codeword_count++; + i += 3; + } else { + block_value = (intermediate.charAt(i) - '0') * 10000; + block_value += (intermediate.charAt(i + 1) - '0') * 1000; + block_value += (intermediate.charAt(i + 2) - '0') * 100; + block_value += (intermediate.charAt(i + 3) - '0') * 10; + block_value += intermediate.charAt(i + 4) - '0'; + + codewords[codeword_count] = block_value / (48 * 48); + block_value = block_value - (48 * 48) * codewords[codeword_count]; + codeword_count++; + codewords[codeword_count] = block_value / 48; + block_value = block_value - 48 * codewords[codeword_count]; + codeword_count++; + codewords[codeword_count] = block_value; + codeword_count++; + i += 5; + } + } + + switch (block_remain) { + case 1: + /* Rule (a) */ + codewords[codeword_count] = positionOf(intermediate.charAt(i), C49_Set); + codeword_count++; + i++; + break; + case 3: + /* Rule (b) */ + block_value = (intermediate.charAt(i) - '0') * 100; + block_value += (intermediate.charAt(i + 1) - '0') * 10; + block_value += intermediate.charAt(i + 2) - '0'; + + codewords[codeword_count] = block_value / 48; + block_value = block_value - 48 * codewords[codeword_count]; + codeword_count++; + codewords[codeword_count] = block_value; + codeword_count++; + i += 3; + break; + case 4: + /* Rule (c) */ + block_value = 100000; + block_value += (intermediate.charAt(i) - '0') * 1000; + block_value += (intermediate.charAt(i + 1) - '0') * 100; + block_value += (intermediate.charAt(i + 2) - '0') * 10; + block_value += intermediate.charAt(i + 3) - '0'; + + codewords[codeword_count] = block_value / (48 * 48); + block_value = block_value - (48 * 48) * codewords[codeword_count]; + codeword_count++; + codewords[codeword_count] = block_value / 48; + block_value = block_value - 48 * codewords[codeword_count]; + codeword_count++; + codewords[codeword_count] = block_value; + codeword_count++; + i += 4; + break; + } + if (i < h) { + /* There is more to add */ + codewords[codeword_count] = 48; /* Numeric Shift */ + codeword_count++; + } + } else { + codewords[codeword_count] = positionOf(intermediate.charAt(i), C49_Set); + codeword_count++; + i++; + } + } else { + codewords[codeword_count] = positionOf(intermediate.charAt(i), C49_Set); + codeword_count++; + i++; + } + } while (i < h); + + switch (codewords[0]) { /* Set starting mode value */ + case 48: + M = 2; + break; + case 43: + M = 4; + break; + case 44: + M = 5; + break; + default: + M = 0; + break; + } + + if (M != 0) { + for (i = 0; i < codeword_count; i++) { + codewords[i] = codewords[i + 1]; + } + codeword_count--; + } + + if (codeword_count > 49) { + errorMsg.append("Input too long"); + return false; + } + + encodeInfo.append("Starting Mode (M): ").append(M).append("\n"); + + /* Place codewords in code character array (c grid) */ + rows = 0; + do { + for (i = 0; i < 7; i++) { + if (((rows * 7) + i) < codeword_count) { + c_grid[rows][i] = codewords[(rows * 7) + i]; + } else { + c_grid[rows][i] = 48; /* Pad */ + pad_count++; + } + } + rows++; + } while ((rows * 7) < codeword_count); + + if ((((rows <= 6) && (pad_count < 5))) || (rows > 6) || (rows == 1)) { + /* Add a row */ + for (i = 0; i < 7; i++) { + c_grid[rows][i] = 48; /* Pad */ + } + rows++; + } + + /* Add row count and mode character */ + c_grid[rows - 1][6] = (7 * (rows - 2)) + M; + + /* Add row check character */ + for (i = 0; i < rows - 1; i++) { + int row_sum = 0; + + for (j = 0; j < 7; j++) { + row_sum += c_grid[i][j]; + } + c_grid[i][7] = row_sum % 49; + } + + /* Calculate Symbol Check Characters */ + posn_val = 0; + x_count = c_grid[rows - 1][6] * 20; + y_count = c_grid[rows - 1][6] * 16; + z_count = c_grid[rows - 1][6] * 38; + for (i = 0; i < rows - 1; i++) { + for (j = 0; j < 4; j++) { + local_value = (c_grid[i][2 * j] * 49) + c_grid[i][(2 * j) + 1]; + x_count += c49_x_weight[posn_val] * local_value; + y_count += c49_y_weight[posn_val] * local_value; + z_count += c49_z_weight[posn_val] * local_value; + posn_val++; + } + } + + if (rows > 6) { + /* Add Z Symbol Check */ + c_grid[rows - 1][0] = (z_count % 2401) / 49; + c_grid[rows - 1][1] = (z_count % 2401) % 49; + } + + local_value = (c_grid[rows - 1][0] * 49) + c_grid[rows - 1][1]; + x_count += c49_x_weight[posn_val] * local_value; + y_count += c49_y_weight[posn_val] * local_value; + posn_val++; + + /* Add Y Symbol Check */ + c_grid[rows - 1][2] = (y_count % 2401) / 49; + c_grid[rows - 1][3] = (y_count % 2401) % 49; + + local_value = (c_grid[rows - 1][2] * 49) + c_grid[rows - 1][3]; + x_count += c49_x_weight[posn_val] * local_value; + + /* Add X Symbol Check */ + c_grid[rows - 1][4] = (x_count % 2401) / 49; + c_grid[rows - 1][5] = (x_count % 2401) % 49; + + encodeInfo.append("Check Characters: ").append(Integer.toString(z_count % 2401)).append(" ").append(Integer.toString(y_count % 2401)).append("\n"); + + /* Add last row check character */ + j = 0; + for (i = 0; i < 7; i++) { + j += c_grid[rows - 1][i]; + } + c_grid[rows - 1][7] = j % 49; + + encodeInfo.append("Codewords: "); + /* Transfer data to symbol character array (w grid) */ + for (i = 0; i < rows; i++) { + for (j = 0; j < 4; j++) { + w_grid[i][j] = (c_grid[i][2 * j] * 49) + c_grid[i][(2 * j) + 1]; + encodeInfo.append(Integer.toString(c_grid[i][2 * j])).append(" ").append(Integer.toString(c_grid[i][(2 * j) + 1])).append(" "); + } + } + encodeInfo.append("\n"); + + readable = new StringBuilder(); + pattern = new String[rows]; + rowCount = rows; + rowHeight = new int[rows]; + + encodeInfo.append("Symbol Characters: "); + for (i = 0; i < rows; i++) { + localpattern = new StringBuilder("11"); /* Start character */ + for (j = 0; j < 4; j++) { + encodeInfo.append(Integer.toString(w_grid[i][j])).append(" "); + if (i != (rows - 1)) { + if (c49_table4[i].charAt(j) == 'E') { + /* Even Parity */ + localpattern.append(c49_appxe_even[w_grid[i][j]]); + } else { + /* Odd Parity */ + localpattern.append(c49_appxe_odd[w_grid[i][j]]); + } + } else { + /* Last row uses all even parity */ + localpattern.append(c49_appxe_even[w_grid[i][j]]); + } + } + localpattern.append("4"); /* Stop character */ + + pattern[i] = localpattern.toString(); + rowHeight[i] = 10; + + } + encodeInfo.append("\n"); + plotSymbol(); + return true; + } + + @Override + protected void plotSymbol() { + int xBlock, yBlock; + int x, y, w, h; + boolean black; + getRectangles().clear(); + y = 1; + h = 1; + for (yBlock = 0; yBlock < rowCount; yBlock++) { + black = true; + x = 15; + for (xBlock = 0; xBlock < pattern[yBlock].length(); xBlock++) { + if (black) { + black = false; + w = pattern[yBlock].charAt(xBlock) - '0'; + if (rowHeight[yBlock] == -1) { + h = defaultHeight; + } else { + h = rowHeight[yBlock]; + } + if (w != 0 && h != 0) { + Rectangle2D.Double rect = new Rectangle2D.Double(x, y, w, h); + getRectangles().add(rect); + } + if ((x + w) > symbolWidth) { + symbolWidth = x + w; + } + } else { + black = true; + } + x += (double) (pattern[yBlock].charAt(xBlock) - '0'); + } + y += h; + if ((y + h) > symbolHeight) { + symbolHeight = y + h; + } + /* Add bars between rows */ + if (yBlock != (rowCount - 1)) { + Rectangle2D.Double rect = new Rectangle2D.Double(15, y - 1, (symbolWidth - 15), 2); + getRectangles().add(rect); + } + } + Rectangle2D.Double top = new Rectangle2D.Double(0, 0, (symbolWidth + 15), 2); + getRectangles().add(top); + Rectangle2D.Double bottom = new Rectangle2D.Double(0, y - 1, (symbolWidth + 15), 2); + getRectangles().add(bottom); + symbolWidth += 30; + symbolHeight += 2; + mergeVerticalBlocks(); + } +} diff --git a/barcode/src/main/java/org/xbib/graphics/barcode/Code93.java b/barcode/src/main/java/org/xbib/graphics/barcode/Code93.java new file mode 100755 index 0000000..a286650 --- /dev/null +++ b/barcode/src/main/java/org/xbib/graphics/barcode/Code93.java @@ -0,0 +1,192 @@ +package org.xbib.graphics.barcode; + +/** + * Implements Code 93. + * Supports encoding of 7-bit ASCII text. Two check digits are added. + */ +public class Code93 extends Symbol { + + /** + * Code 93 control characters, indexed by ASCII codes (NOTE: a = Ctrl $, + * b = Ctrl %, c = Ctrl /, d = Ctrl + for sequences of two characters). + */ + private static final String[] CODE_93_CTRL = { + "bU", "aA", "aB", "aC", "aD", "aE", "aF", "aG", "aH", "aI", + "aJ", "aK", "aL", "aM", "aN", "aO", "aP", "aQ", "aR", "aS", + "aT", "aU", "aV", "aW", "aX", "aY", "aZ", "bA", "bB", "bC", + "bD", "bE", " ", "cA", "cB", "cC", "$", "%", "cF", "cG", + "cH", "cI", "cJ", "+", "cL", "-", ".", "/", "0", "1", + "2", "3", "4", "5", "6", "7", "8", "9", "cZ", "bF", + "bG", "bH", "bI", "bJ", "bV", "A", "B", "C", "D", "E", + "F", "G", "H", "I", "J", "K", "L", "M", "N", "O", + "P", "Q", "R", "S", "T", "U", "V", "W", "X", "Y", + "Z", "bK", "bL", "bM", "bN", "bO", "bW", "dA", "dB", "dC", + "dD", "dE", "dF", "dG", "dH", "dI", "dJ", "dK", "dL", "dM", + "dN", "dO", "dP", "dQ", "dR", "dS", "dT", "dU", "dV", "dW", + "dX", "dY", "dZ", "bP", "bQ", "bR", "bS", "bT"}; + + /** + * Mapping of control characters to pattern table index (NOTE: a = Ctrl $, + * b = Ctrl %, c = Ctrl /, d = Ctrl + for sequences of two characters). + */ + private static final char[] CODE_93_LOOKUP = { + '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', + 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', + 'K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T', + 'U', 'V', 'W', 'X', 'Y', 'Z', '-', '.', ' ', '$', + '/', '+', '%', 'a', 'b', 'c', 'd'}; + + /** + * Code 93 pattern table. + */ + private static final String[] CODE_93_TABLE = { + "131112", "111213", "111312", "111411", "121113", + "121212", "121311", "111114", "131211", "141111", + "211113", "211212", "211311", "221112", "221211", + "231111", "112113", "112212", "112311", "122112", + "132111", "111123", "111222", "111321", "121122", + "131121", "212112", "212211", "211122", "211221", + "221121", "222111", "112122", "112221", "122121", + "123111", "121131", "311112", "311211", "321111", + "112131", "113121", "211131", "121221", "312111", + "311121", "122211"}; + + /** + * Whether or not to show check digits in the human-readable text. + */ + private boolean showCheckDigits = true; + + /** + * Optional start/stop delimiter to be shown in the human-readable text. + */ + private Character startStopDelimiter; + + private static char[] toControlChars(String s) { + StringBuilder buffer = new StringBuilder(); + char[] chars = s.toCharArray(); + for (char asciiCode : chars) { + buffer.append(CODE_93_CTRL[asciiCode]); + } + return buffer.toString().toCharArray(); + } + + private static int calculateCheckDigitC(int[] values, int length) { + int c = 0; + int weight = 1; + for (int i = length - 1; i >= 0; i--) { + c += values[i] * weight; + weight++; + if (weight == 21) { + weight = 1; + } + } + c = c % 47; + return c; + } + + private static int calculateCheckDigitK(int[] values, int length) { + int k = 0; + int weight = 1; + for (int i = length - 1; i >= 0; i--) { + k += values[i] * weight; + weight++; + if (weight == 16) { + weight = 1; + } + } + k = k % 47; + return k; + } + + private static String toPattern(int[] values) { + StringBuilder buffer = new StringBuilder("111141"); + for (int value : values) { + buffer.append(CODE_93_TABLE[value]); + } + buffer.append("1111411"); + return buffer.toString(); + } + + /** + * Returns whether or not this symbol shows check digits in the human-readable text. + * + * @return whether or not this symbol shows check digits in the human-readable text + */ + public boolean getShowCheckDigits() { + return showCheckDigits; + } + + /** + * Sets whether or not to show check digits in the human-readable text (defaults to true). + * + * @param showCheckDigits whether or not to show check digits in the human-readable text + */ + public void setShowCheckDigits(boolean showCheckDigits) { + this.showCheckDigits = showCheckDigits; + } + + /** + * Returns the optional start/stop delimiter to be shown in the human-readable text. + * + * @return the optional start/stop delimiter to be shown in the human-readable text + */ + public Character getStartStopDelimiter() { + return startStopDelimiter; + } + + /** + * Sets an optional start/stop delimiter to be shown in the human-readable text (defaults to null). + * + * @param startStopDelimiter an optional start/stop delimiter to be shown in the human-readable text + */ + public void setStartStopDelimiter(Character startStopDelimiter) { + this.startStopDelimiter = startStopDelimiter; + } + + @Override + public boolean encode() { + + char[] controlChars = toControlChars(content); + int l = controlChars.length; + + if (!content.matches("[\u0000-\u007F]+")) { + errorMsg.append("Invalid characters in input data"); + return false; + } + + int[] values = new int[controlChars.length + 2]; + for (int i = 0; i < l; i++) { + values[i] = positionOf(controlChars[i], CODE_93_LOOKUP); + } + + int c = calculateCheckDigitC(values, l); + values[l] = c; + l++; + + int k = calculateCheckDigitK(values, l); + values[l] = k; + + readable = new StringBuilder(content); + if (showCheckDigits) { + readable.append(CODE_93_LOOKUP[c]).append(CODE_93_LOOKUP[k]); + } + if (startStopDelimiter != null) { + readable = new StringBuilder(startStopDelimiter).append(readable).append(startStopDelimiter); + } + + encodeInfo.append("Check Digit C: ").append(c).append("\n"); + encodeInfo.append("Check Digit K: ").append(k).append("\n"); + pattern = new String[]{toPattern(values)}; + rowCount = 1; + rowHeight = new int[]{-1}; + + plotSymbol(); + + return true; + } + + @Override + protected int[] getCodewords() { + return getPatternAsCodewords(6); + } +} diff --git a/barcode/src/main/java/org/xbib/graphics/barcode/CodeOne.java b/barcode/src/main/java/org/xbib/graphics/barcode/CodeOne.java new file mode 100755 index 0000000..78cb569 --- /dev/null +++ b/barcode/src/main/java/org/xbib/graphics/barcode/CodeOne.java @@ -0,0 +1,1946 @@ +package org.xbib.graphics.barcode; + +import org.xbib.graphics.barcode.util.ReedSolomon; +import java.io.UnsupportedEncodingException; +import java.math.BigInteger; + +/** + * Implements Code One. + * Code One is able to encode the ISO 8859-1 (Latin-1) character set or GS1 + * data. There are two types of Code One symbol - variable height symbols + * which are roughly square (versions A thought to H) and fixed-height + * versions (version S and T). Version S symbols can only encode numeric data. + * The width of version S and version T symbols is determined by the length + * of the input data. + */ +public class CodeOne extends Symbol { + private final int[] c40_shift = { + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 0, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 2, 2, 2, 2, 2, 2, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 2, 2, 2, 2, + 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, + 3, 3, 3, 3, 3, 3, 3, 3 + }; + + private final int[] c40_value = { + 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, + 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 3, 0, 1, 2, 3, 4, 5, 6, + 7, 8, 9, 10, 11, 12, 13, 14, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 15, 16, + 17, 18, 19, 20, 21, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, + 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 22, 23, 24, 25, 26, + 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, + 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31 + }; + + private final int[] text_shift = { + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 0, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 2, 2, 2, 2, 2, 2, 3, 3, 3, 3, 3, 3, 3, + 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 2, 2, 2, 2, 2, + 3, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 3, 3, 3, 3, 3 + }; + + private final int[] text_value = { + 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, + 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 3, 0, 1, 2, 3, 4, 5, 6, + 7, 8, 9, 10, 11, 12, 13, 14, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 15, 16, + 17, 18, 19, 20, 21, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, + 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 22, 23, 24, 25, 26, 0, 14, + 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, + 33, 34, 35, 36, 37, 38, 39, 27, 28, 29, 30, 31 + }; + + private final int[] c1_height = { + 16, 22, 28, 40, 52, 70, 104, 148 + }; + private final int[] c1_width = { + 18, 22, 32, 42, 54, 76, 98, 134 + }; + private final int[] c1_data_length = { + 10, 19, 44, 91, 182, 370, 732, 1480 + }; + private final int[] c1_ecc_length = { + 10, 16, 26, 44, 70, 140, 280, 560 + }; + private final int[] c1_blocks = { + 1, 1, 1, 1, 1, 2, 4, 8 + }; + private final int[] c1_data_blocks = { + 10, 19, 44, 91, 182, 185, 183, 185 + }; + private final int[] c1_ecc_blocks = { + 10, 16, 26, 44, 70, 70, 70, 70 + }; + private final int[] c1_grid_width = { + 4, 5, 7, 9, 12, 17, 22, 30 + }; + private final int[] c1_grid_height = { + 5, 7, 10, 15, 21, 30, 46, 68 + }; + private int[] data = new int[1500]; + + ; + private byte[] source; + private int[][] datagrid = new int[136][120]; + private boolean[][] outputGrid = new boolean[148][134]; + private Version preferredVersion = Version.NONE; + + /** + * Set symbol size by "version". Versions A to H are square symbols. + * This value may be ignored if the input data does not fit in the + * specified version. Version S and T are fixed height symbols. + * + * @param version Symbol version + */ + public void setPreferredVersion(Version version) { + preferredVersion = version; + } + + @Override + public boolean encode() { + int size = 1, i, j, data_blocks; + int row, col; + int sub_version = 0; + int codewords; + BigInteger elreg; + BigInteger codewordValue; + int[] ecc = new int[600]; + int[] stream = new int[2100]; + int block_width; + int length = content.length(); + ReedSolomon rs = new ReedSolomon(); + int data_length; + int data_cw, ecc_cw; + int[] sub_data = new int[190]; + StringBuilder bin; + + if (!content.matches("[\u0000-\u00FF]+")) { + errorMsg.append("Invalid characters in input data"); + return false; + } + + if (preferredVersion == Version.S) { + /* Version S */ + + encodeInfo.append("Version: S"); + + if (length > 18) { + errorMsg.append("Input data too long"); + return false; + } + + if (!(content.matches("[0-9]+?"))) { + errorMsg.append("Invalid characters in input"); + return false; + } + + sub_version = 3; + codewords = 12; + block_width = 6; /* Version S-30 */ + if (length <= 12) { + sub_version = 2; + codewords = 8; + block_width = 4; + } /* Version S-20 */ + if (length <= 6) { + sub_version = 1; + codewords = 4; + block_width = 2; + } /* Version S-10 */ + + elreg = new BigInteger(content); + + for (i = 0; i < codewords; i++) { + codewordValue = elreg.shiftRight(5 * i); + codewordValue = codewordValue.or(BigInteger.valueOf(32)); + data[codewords - i - 1] = codewordValue.intValue(); + } + + rs.init_gf(0x25); + rs.init_code(codewords, 1); + rs.encode(codewords, data); + + for (i = 0; i < codewords; i++) { + stream[i] = data[i]; + stream[i + codewords] = rs.getResult(codewords - i - 1); + } + + for (i = 0; i < 136; i++) { + for (j = 0; j < 120; j++) { + datagrid[i][j] = '0'; + } + } + + i = 0; + for (row = 0; row < 2; row++) { + for (col = 0; col < block_width; col++) { + if ((stream[i] & 0x10) != 0) { + datagrid[row * 2][col * 5] = '1'; + } + if ((stream[i] & 0x08) != 0) { + datagrid[row * 2][(col * 5) + 1] = '1'; + } + if ((stream[i] & 0x04) != 0) { + datagrid[row * 2][(col * 5) + 2] = '1'; + } + if ((stream[i] & 0x02) != 0) { + datagrid[(row * 2) + 1][col * 5] = '1'; + } + if ((stream[i] & 0x01) != 0) { + datagrid[(row * 2) + 1][(col * 5) + 1] = '1'; + } + if ((stream[i + 1] & 0x10) != 0) { + datagrid[row * 2][(col * 5) + 3] = '1'; + } + if ((stream[i + 1] & 0x08) != 0) { + datagrid[row * 2][(col * 5) + 4] = '1'; + } + if ((stream[i + 1] & 0x04) != 0) { + datagrid[(row * 2) + 1][(col * 5) + 2] = '1'; + } + if ((stream[i + 1] & 0x02) != 0) { + datagrid[(row * 2) + 1][(col * 5) + 3] = '1'; + } + if ((stream[i + 1] & 0x01) != 0) { + datagrid[(row * 2) + 1][(col * 5) + 4] = '1'; + } + i += 2; + } + } + + size = 9; + rowCount = 8; + symbolWidth = 10 * sub_version + 1; + } + + if (preferredVersion == Version.T) { + /* Version T */ + + encodeInfo.append("Version: T\n"); + + for (i = 0; i < 40; i++) { + data[i] = 0; + } + data_length = encodeAsCode1Data(); + + if (data_length == 0) { + errorMsg.append("Input data too long"); + return false; + } + + if (data_length > 38) { + errorMsg.append("Input data too long"); + return false; + } + + size = 10; + sub_version = 3; + data_cw = 38; + ecc_cw = 22; + block_width = 12; + if (data_length <= 24) { + sub_version = 2; + data_cw = 24; + ecc_cw = 16; + block_width = 8; + } + if (data_length <= 10) { + sub_version = 1; + data_cw = 10; + ecc_cw = 10; + block_width = 4; + } + + for (i = data_length; i < data_cw; i++) { + data[i] = 129; /* Pad */ + } + + /* Calculate error correction data */ + rs.init_gf(0x12d); + rs.init_code(ecc_cw, 1); + rs.encode(data_cw, data); + + /* "Stream" combines data and error correction data */ + for (i = 0; i < data_cw; i++) { + stream[i] = data[i]; + } + for (i = 0; i < ecc_cw; i++) { + stream[data_cw + i] = rs.getResult(ecc_cw - i - 1); + } + + for (i = 0; i < 136; i++) { + for (j = 0; j < 120; j++) { + datagrid[i][j] = '0'; + } + } + + i = 0; + for (row = 0; row < 5; row++) { + for (col = 0; col < block_width; col++) { + if ((stream[i] & 0x80) != 0) { + datagrid[row * 2][col * 4] = '1'; + } + if ((stream[i] & 0x40) != 0) { + datagrid[row * 2][(col * 4) + 1] = '1'; + } + if ((stream[i] & 0x20) != 0) { + datagrid[row * 2][(col * 4) + 2] = '1'; + } + if ((stream[i] & 0x10) != 0) { + datagrid[row * 2][(col * 4) + 3] = '1'; + } + if ((stream[i] & 0x08) != 0) { + datagrid[(row * 2) + 1][col * 4] = '1'; + } + if ((stream[i] & 0x04) != 0) { + datagrid[(row * 2) + 1][(col * 4) + 1] = '1'; + } + if ((stream[i] & 0x02) != 0) { + datagrid[(row * 2) + 1][(col * 4) + 2] = '1'; + } + if ((stream[i] & 0x01) != 0) { + datagrid[(row * 2) + 1][(col * 4) + 3] = '1'; + } + i++; + } + } + + rowCount = 16; + symbolWidth = (sub_version * 16) + 1; + } + + if ((preferredVersion != Version.S) && (preferredVersion != Version.T)) { + /* Version A to H */ + for (i = 0; i < 1500; i++) { + data[i] = 0; + } + data_length = encodeAsCode1Data(); + + if (data_length == 0) { + errorMsg.append("Input data too long"); + return false; + } + + for (i = 7; i >= 0; i--) { + if (c1_data_length[i] >= data_length) { + size = i + 1; + } + } + + if (getSize(preferredVersion) > size) { + size = getSize(preferredVersion); + } + + encodeInfo.append("Version: ").append((char) ((size - 1) + 'A')).append("\n"); + + encodeInfo.append("Codewords: "); + for (i = 0; i < data_length; i++) { + encodeInfo.append(Integer.toString(data[i])).append(" "); + } + encodeInfo.append("\n"); + + for (i = data_length; i < c1_data_length[size - 1]; i++) { + data[i] = 129; /* Pad */ + } + + /* Calculate error correction data */ + data_length = c1_data_length[size - 1]; + + data_blocks = c1_blocks[size - 1]; + + rs.init_gf(0x12d); + rs.init_code(c1_ecc_blocks[size - 1], 0); + for (i = 0; i < data_blocks; i++) { + for (j = 0; j < c1_data_blocks[size - 1]; j++) { + + sub_data[j] = data[j * data_blocks + i]; + } + rs.encode(c1_data_blocks[size - 1], sub_data); + for (j = 0; j < c1_ecc_blocks[size - 1]; j++) { + ecc[c1_ecc_length[size - 1] - (j * data_blocks + i) - 1] + = rs.getResult(j); + } + } + + encodeInfo.append("ECC Codeword Count: ").append(c1_ecc_length[size - 1]).append("\n"); + + /* "Stream" combines data and error correction data */ + for (i = 0; i < data_length; i++) { + stream[i] = data[i]; + } + for (i = 0; i < c1_ecc_length[size - 1]; i++) { + stream[data_length + i] = ecc[i]; + } + + for (i = 0; i < 136; i++) { + for (j = 0; j < 120; j++) { + datagrid[i][j] = '0'; + } + } + + i = 0; + for (row = 0; row < c1_grid_height[size - 1]; row++) { + for (col = 0; col < c1_grid_width[size - 1]; col++) { + if ((stream[i] & 0x80) != 0) { + datagrid[row * 2][col * 4] = '1'; + } + if ((stream[i] & 0x40) != 0) { + datagrid[row * 2][(col * 4) + 1] = '1'; + } + if ((stream[i] & 0x20) != 0) { + datagrid[row * 2][(col * 4) + 2] = '1'; + } + if ((stream[i] & 0x10) != 0) { + datagrid[row * 2][(col * 4) + 3] = '1'; + } + if ((stream[i] & 0x08) != 0) { + datagrid[(row * 2) + 1][col * 4] = '1'; + } + if ((stream[i] & 0x04) != 0) { + datagrid[(row * 2) + 1][(col * 4) + 1] = '1'; + } + if ((stream[i] & 0x02) != 0) { + datagrid[(row * 2) + 1][(col * 4) + 2] = '1'; + } + if ((stream[i] & 0x01) != 0) { + datagrid[(row * 2) + 1][(col * 4) + 3] = '1'; + } + i++; + } + } + + encodeInfo.append("Grid Size: ").append(c1_grid_width[size - 1]).append(" X ").append(c1_grid_height[size - 1]).append("\n"); + + rowCount = c1_height[size - 1]; + symbolWidth = c1_width[size - 1]; + } + + for (i = 0; i < 148; i++) { + for (j = 0; j < 134; j++) { + outputGrid[i][j] = false; + } + } + + switch (size) { + case 1: + /* Version A */ + plotCentralFinder(6, 3, 1); + plotVerticalBar(4, 6, 1); + plotVerticalBar(12, 5, 0); + setGridModule(5, 12); + plotSpigot(0); + plotSpigot(15); + plotDataBlock(0, 0, 5, 4, 0, 0); + plotDataBlock(0, 4, 5, 12, 0, 2); + plotDataBlock(5, 0, 5, 12, 6, 0); + plotDataBlock(5, 12, 5, 4, 6, 2); + break; + case 2: + /* Version B */ + plotCentralFinder(8, 4, 1); + plotVerticalBar(4, 8, 1); + plotVerticalBar(16, 7, 0); + setGridModule(7, 16); + plotSpigot(0); + plotSpigot(21); + plotDataBlock(0, 0, 7, 4, 0, 0); + plotDataBlock(0, 4, 7, 16, 0, 2); + plotDataBlock(7, 0, 7, 16, 8, 0); + plotDataBlock(7, 16, 7, 4, 8, 2); + break; + case 3: + /* Version C */ + plotCentralFinder(11, 4, 2); + plotVerticalBar(4, 11, 1); + plotVerticalBar(26, 13, 1); + plotVerticalBar(4, 10, 0); + plotVerticalBar(26, 10, 0); + plotSpigot(0); + plotSpigot(27); + plotDataBlock(0, 0, 10, 4, 0, 0); + plotDataBlock(0, 4, 10, 20, 0, 2); + plotDataBlock(0, 24, 10, 4, 0, 4); + plotDataBlock(10, 0, 10, 4, 8, 0); + plotDataBlock(10, 4, 10, 20, 8, 2); + plotDataBlock(10, 24, 10, 4, 8, 4); + break; + case 4: + /* Version D */ + plotCentralFinder(16, 5, 1); + plotVerticalBar(4, 16, 1); + plotVerticalBar(20, 16, 1); + plotVerticalBar(36, 16, 1); + plotVerticalBar(4, 15, 0); + plotVerticalBar(20, 15, 0); + plotVerticalBar(36, 15, 0); + plotSpigot(0); + plotSpigot(12); + plotSpigot(27); + plotSpigot(39); + plotDataBlock(0, 0, 15, 4, 0, 0); + plotDataBlock(0, 4, 15, 14, 0, 2); + plotDataBlock(0, 18, 15, 14, 0, 4); + plotDataBlock(0, 32, 15, 4, 0, 6); + plotDataBlock(15, 0, 15, 4, 10, 0); + plotDataBlock(15, 4, 15, 14, 10, 2); + plotDataBlock(15, 18, 15, 14, 10, 4); + plotDataBlock(15, 32, 15, 4, 10, 6); + break; + case 5: + /* Version E */ + plotCentralFinder(22, 5, 2); + plotVerticalBar(4, 22, 1); + plotVerticalBar(26, 24, 1); + plotVerticalBar(48, 22, 1); + plotVerticalBar(4, 21, 0); + plotVerticalBar(26, 21, 0); + plotVerticalBar(48, 21, 0); + plotSpigot(0); + plotSpigot(12); + plotSpigot(39); + plotSpigot(51); + plotDataBlock(0, 0, 21, 4, 0, 0); + plotDataBlock(0, 4, 21, 20, 0, 2); + plotDataBlock(0, 24, 21, 20, 0, 4); + plotDataBlock(0, 44, 21, 4, 0, 6); + plotDataBlock(21, 0, 21, 4, 10, 0); + plotDataBlock(21, 4, 21, 20, 10, 2); + plotDataBlock(21, 24, 21, 20, 10, 4); + plotDataBlock(21, 44, 21, 4, 10, 6); + break; + case 6: + /* Version F */ + plotCentralFinder(31, 5, 3); + plotVerticalBar(4, 31, 1); + plotVerticalBar(26, 35, 1); + plotVerticalBar(48, 31, 1); + plotVerticalBar(70, 35, 1); + plotVerticalBar(4, 30, 0); + plotVerticalBar(26, 30, 0); + plotVerticalBar(48, 30, 0); + plotVerticalBar(70, 30, 0); + plotSpigot(0); + plotSpigot(12); + plotSpigot(24); + plotSpigot(45); + plotSpigot(57); + plotSpigot(69); + plotDataBlock(0, 0, 30, 4, 0, 0); + plotDataBlock(0, 4, 30, 20, 0, 2); + plotDataBlock(0, 24, 30, 20, 0, 4); + plotDataBlock(0, 44, 30, 20, 0, 6); + plotDataBlock(0, 64, 30, 4, 0, 8); + plotDataBlock(30, 0, 30, 4, 10, 0); + plotDataBlock(30, 4, 30, 20, 10, 2); + plotDataBlock(30, 24, 30, 20, 10, 4); + plotDataBlock(30, 44, 30, 20, 10, 6); + plotDataBlock(30, 64, 30, 4, 10, 8); + break; + case 7: + /* Version G */ + plotCentralFinder(47, 6, 2); + plotVerticalBar(6, 47, 1); + plotVerticalBar(27, 49, 1); + plotVerticalBar(48, 47, 1); + plotVerticalBar(69, 49, 1); + plotVerticalBar(90, 47, 1); + plotVerticalBar(6, 46, 0); + plotVerticalBar(27, 46, 0); + plotVerticalBar(48, 46, 0); + plotVerticalBar(69, 46, 0); + plotVerticalBar(90, 46, 0); + plotSpigot(0); + plotSpigot(12); + plotSpigot(24); + plotSpigot(36); + plotSpigot(67); + plotSpigot(79); + plotSpigot(91); + plotSpigot(103); + plotDataBlock(0, 0, 46, 6, 0, 0); + plotDataBlock(0, 6, 46, 19, 0, 2); + plotDataBlock(0, 25, 46, 19, 0, 4); + plotDataBlock(0, 44, 46, 19, 0, 6); + plotDataBlock(0, 63, 46, 19, 0, 8); + plotDataBlock(0, 82, 46, 6, 0, 10); + plotDataBlock(46, 0, 46, 6, 12, 0); + plotDataBlock(46, 6, 46, 19, 12, 2); + plotDataBlock(46, 25, 46, 19, 12, 4); + plotDataBlock(46, 44, 46, 19, 12, 6); + plotDataBlock(46, 63, 46, 19, 12, 8); + plotDataBlock(46, 82, 46, 6, 12, 10); + break; + case 8: + /* Version H */ + plotCentralFinder(69, 6, 3); + plotVerticalBar(6, 69, 1); + plotVerticalBar(26, 73, 1); + plotVerticalBar(46, 69, 1); + plotVerticalBar(66, 73, 1); + plotVerticalBar(86, 69, 1); + plotVerticalBar(106, 73, 1); + plotVerticalBar(126, 69, 1); + plotVerticalBar(6, 68, 0); + plotVerticalBar(26, 68, 0); + plotVerticalBar(46, 68, 0); + plotVerticalBar(66, 68, 0); + plotVerticalBar(86, 68, 0); + plotVerticalBar(106, 68, 0); + plotVerticalBar(126, 68, 0); + plotSpigot(0); + plotSpigot(12); + plotSpigot(24); + plotSpigot(36); + plotSpigot(48); + plotSpigot(60); + plotSpigot(87); + plotSpigot(99); + plotSpigot(111); + plotSpigot(123); + plotSpigot(135); + plotSpigot(147); + plotDataBlock(0, 0, 68, 6, 0, 0); + plotDataBlock(0, 6, 68, 18, 0, 2); + plotDataBlock(0, 24, 68, 18, 0, 4); + plotDataBlock(0, 42, 68, 18, 0, 6); + plotDataBlock(0, 60, 68, 18, 0, 8); + plotDataBlock(0, 78, 68, 18, 0, 10); + plotDataBlock(0, 96, 68, 18, 0, 12); + plotDataBlock(0, 114, 68, 6, 0, 14); + plotDataBlock(68, 0, 68, 6, 12, 0); + plotDataBlock(68, 6, 68, 18, 12, 2); + plotDataBlock(68, 24, 68, 18, 12, 4); + plotDataBlock(68, 42, 68, 18, 12, 6); + plotDataBlock(68, 60, 68, 18, 12, 8); + plotDataBlock(68, 78, 68, 18, 12, 10); + plotDataBlock(68, 96, 68, 18, 12, 12); + plotDataBlock(68, 114, 68, 6, 12, 14); + break; + case 9: + /* Version S */ + plotHorizontalBar(5, 1); + plotHorizontalBar(7, 1); + setGridModule(6, 0); + setGridModule(6, symbolWidth - 1); + resetGridModule(7, 1); + resetGridModule(7, symbolWidth - 2); + switch (sub_version) { + case 1: + /* Version S-10 */ + setGridModule(0, 5); + plotDataBlock(0, 0, 4, 5, 0, 0); + plotDataBlock(0, 5, 4, 5, 0, 1); + break; + case 2: + /* Version S-20 */ + setGridModule(0, 10); + setGridModule(4, 10); + plotDataBlock(0, 0, 4, 10, 0, 0); + plotDataBlock(0, 10, 4, 10, 0, 1); + break; + case 3: + /* Version S-30 */ + setGridModule(0, 15); + setGridModule(4, 15); + setGridModule(6, 15); + plotDataBlock(0, 0, 4, 15, 0, 0); + plotDataBlock(0, 15, 4, 15, 0, 1); + break; + } + break; + case 10: + /* Version T */ + plotHorizontalBar(11, 1); + plotHorizontalBar(13, 1); + plotHorizontalBar(15, 1); + setGridModule(12, 0); + setGridModule(12, symbolWidth - 1); + setGridModule(14, 0); + setGridModule(14, symbolWidth - 1); + resetGridModule(13, 1); + resetGridModule(13, symbolWidth - 2); + resetGridModule(15, 1); + resetGridModule(15, symbolWidth - 2); + switch (sub_version) { + case 1: + /* Version T-16 */ + setGridModule(0, 8); + setGridModule(10, 8); + plotDataBlock(0, 0, 10, 8, 0, 0); + plotDataBlock(0, 8, 10, 8, 0, 1); + break; + case 2: + /* Version T-32 */ + setGridModule(0, 16); + setGridModule(10, 16); + setGridModule(12, 16); + plotDataBlock(0, 0, 10, 16, 0, 0); + plotDataBlock(0, 16, 10, 16, 0, 1); + break; + case 3: + /* Verion T-48 */ + setGridModule(0, 24); + setGridModule(10, 24); + setGridModule(12, 24); + setGridModule(14, 24); + plotDataBlock(0, 0, 10, 24, 0, 0); + plotDataBlock(0, 24, 10, 24, 0, 1); + break; + } + break; + } + + readable = new StringBuilder(); + pattern = new String[rowCount]; + rowHeight = new int[rowCount]; + for (i = 0; i < rowCount; i++) { + bin = new StringBuilder(); + for (j = 0; j < symbolWidth; j++) { + if (outputGrid[i][j]) { + bin.append("1"); + } else { + bin.append("0"); + } + } + pattern[i] = bin2pat(bin.toString()); + rowHeight[i] = 1; + } + plotSymbol(); + return true; + } + + private int encodeAsCode1Data() { + c1Mode current_mode, next_mode; + boolean latch; + boolean done; + int sourcePoint, targetPoint, i, j; + int c40_p; + int text_p; + int edi_p; + int byte_start = 0; + int[] c40_buffer = new int[6]; + int[] text_buffer = new int[6]; + int[] edi_buffer = new int[6]; + StringBuilder decimal_binary = new StringBuilder(); + int length = content.length(); + int shift_set, value; + int data_left, decimal_count; + int sub_value; + int bits_left_in_byte, target_count; + boolean isTwoDigits; + + try { + source = content.getBytes("ISO8859_1"); + } catch (UnsupportedEncodingException e) { + errorMsg.append("Invalid character in input data"); + return 0; + } + + sourcePoint = 0; + targetPoint = 0; + c40_p = 0; + text_p = 0; + edi_p = 0; + + if (inputDataType == DataType.GS1) { + data[targetPoint] = 232; + targetPoint++; + } /* FNC1 */ + + /* Step A */ + current_mode = c1Mode.C1_ASCII; + next_mode = c1Mode.C1_ASCII; + + do { + if (current_mode != next_mode) { + /* Change mode */ + switch (next_mode) { + case C1_C40: + data[targetPoint] = 230; + targetPoint++; + break; + case C1_TEXT: + data[targetPoint] = 239; + targetPoint++; + break; + case C1_EDI: + data[targetPoint] = 238; + targetPoint++; + break; + case C1_BYTE: + data[targetPoint] = 231; + targetPoint++; + break; + } + } + + if ((current_mode != c1Mode.C1_BYTE) && (next_mode == c1Mode.C1_BYTE)) { + byte_start = targetPoint; + } + current_mode = next_mode; + + if (current_mode == c1Mode.C1_ASCII) { /* Step B - ASCII encodation */ + next_mode = c1Mode.C1_ASCII; + + if ((length - sourcePoint) >= 21) { /* Step B1 */ + j = 0; + + for (i = 0; i < 21; i++) { + if ((source[sourcePoint + i] >= '0') && (source[sourcePoint + i] <= '9')) { + j++; + } + } + + if (j == 21) { + next_mode = c1Mode.C1_DECIMAL; + decimal_binary.append("1111"); + } + } + + if ((next_mode == c1Mode.C1_ASCII) && ((length - sourcePoint) >= 13)) { /* Step B2 */ + j = 0; + + for (i = 0; i < 13; i++) { + if ((source[sourcePoint + i] >= '0') && (source[sourcePoint + i] <= '9')) { + j++; + } + } + + if (j == 13) { + latch = false; + for (i = sourcePoint + 13; i < length; i++) { + if (!((source[i] >= '0') && + (source[i] <= '9'))) { + latch = true; + } + } + + if (!(latch)) { + next_mode = c1Mode.C1_DECIMAL; + decimal_binary.append("1111"); + } + } + } + + if (next_mode == c1Mode.C1_ASCII) { /* Step B3 */ + isTwoDigits = false; + if ((sourcePoint + 1) != length) { + if ((source[sourcePoint] >= '0') && (source[sourcePoint] <= '9')) { + if ((source[sourcePoint + 1] >= '0') && (source[sourcePoint + 1] <= '9')) { + // remaining data consists of two numeric digits + data[targetPoint] = (10 * (source[sourcePoint] - '0')) + + (source[sourcePoint + 1] - '0') + 130; + targetPoint++; + sourcePoint += 2; + isTwoDigits = true; + } + } + } + + if (!(isTwoDigits)) { + if ((inputDataType == DataType.GS1) && (source[sourcePoint] == '[')) { + if ((length - sourcePoint) >= 15) { /* Step B4 */ + j = 0; + + for (i = 0; i < 15; i++) { + if ((source[sourcePoint + i] >= '0') + && (source[sourcePoint + i] <= '9')) { + j++; + } + } + + if (j == 15) { + data[targetPoint] = 236; /* FNC1 and change to Decimal */ + targetPoint++; + sourcePoint++; + next_mode = c1Mode.C1_DECIMAL; + } + } + + if ((length - sourcePoint) >= 7) { /* Step B5 */ + j = 0; + + for (i = 0; i < 7; i++) { + if ((source[sourcePoint + i] >= '0') + && (source[sourcePoint + i] <= '9')) { + j++; + } + } + + if (j == 7) { + latch = false; + for (i = sourcePoint + 7; i < length; i++) { + if (!((source[sourcePoint + i] >= '0') + && (source[sourcePoint + i] <= '9'))) { + latch = true; + } + } + + if (!(latch)) { + data[targetPoint] = 236; /* FNC1 and change to Decimal */ + targetPoint++; + sourcePoint++; + next_mode = c1Mode.C1_DECIMAL; + } + } + } + } + + if (next_mode == c1Mode.C1_ASCII) { + + /* Step B6 */ + next_mode = lookAheadTest(length, sourcePoint, current_mode); + + if (next_mode == c1Mode.C1_ASCII) { + if (source[sourcePoint] > 127) { + /* Step B7 */ + data[targetPoint] = 235; + targetPoint++; /* FNC4 */ + data[targetPoint] = (source[sourcePoint] - 128) + 1; + targetPoint++; + sourcePoint++; + } else { + /* Step B8 */ + if ((inputDataType == DataType.GS1) && (source[sourcePoint] == '[')) { + data[targetPoint] = 232; + targetPoint++; + sourcePoint++; /* FNC1 */ + } else { + data[targetPoint] = source[sourcePoint] + 1; + targetPoint++; + sourcePoint++; + } + } + } + } + } + } + } + + if (current_mode == c1Mode.C1_C40) { /* Step C - C40 encodation */ + done = false; + next_mode = c1Mode.C1_C40; + if (c40_p == 0) { + if ((length - sourcePoint) >= 12) { + j = 0; + + for (i = 0; i < 12; i++) { + if ((source[sourcePoint + i] >= '0') + && (source[sourcePoint + i] <= '9')) { + j++; + } + } + + if (j == 12) { + next_mode = c1Mode.C1_ASCII; + done = true; + } + } + + if ((length - sourcePoint) >= 8) { + j = 0; + + for (i = 0; i < 8; i++) { + if ((source[sourcePoint + i] >= '0') + && (source[sourcePoint + i] <= '9')) { + j++; + } + } + + if ((length - sourcePoint) == 8) { + latch = true; + } else { + latch = true; + for (j = sourcePoint + 8; j < length; j++) { + if ((source[j] <= '0') || (source[j] >= '9')) { + latch = false; + } + } + } + + if ((j == 8) && latch) { + next_mode = c1Mode.C1_ASCII; + done = true; + } + } + + if (!(done)) { + next_mode = lookAheadTest(length, sourcePoint, current_mode); + } + } + + if (next_mode != c1Mode.C1_C40) { + data[targetPoint] = 255; + targetPoint++; /* Unlatch */ + } else { + if (source[sourcePoint] > 127) { + c40_buffer[c40_p] = 1; + c40_p++; + c40_buffer[c40_p] = 30; + c40_p++; /* Upper Shift */ + shift_set = c40_shift[source[sourcePoint] - 128]; + value = c40_value[source[sourcePoint] - 128]; + } else { + shift_set = c40_shift[source[sourcePoint]]; + value = c40_value[source[sourcePoint]]; + } + + if ((inputDataType == DataType.GS1) && (source[sourcePoint] == '[')) { + shift_set = 2; + value = 27; /* FNC1 */ + } + + if (shift_set != 0) { + c40_buffer[c40_p] = shift_set - 1; + c40_p++; + } + c40_buffer[c40_p] = value; + c40_p++; + + if (c40_p >= 3) { + int iv; + + iv = (1600 * c40_buffer[0]) + (40 * c40_buffer[1]) + + (c40_buffer[2]) + 1; + data[targetPoint] = iv / 256; + targetPoint++; + data[targetPoint] = iv % 256; + targetPoint++; + + c40_buffer[0] = c40_buffer[3]; + c40_buffer[1] = c40_buffer[4]; + c40_buffer[2] = c40_buffer[5]; + c40_buffer[3] = 0; + c40_buffer[4] = 0; + c40_buffer[5] = 0; + c40_p -= 3; + } + sourcePoint++; + } + } + + if (current_mode == c1Mode.C1_TEXT) { /* Step D - Text encodation */ + done = false; + next_mode = c1Mode.C1_TEXT; + if (text_p == 0) { + if ((length - sourcePoint) >= 12) { + j = 0; + + for (i = 0; i < 12; i++) { + if ((source[sourcePoint + i] >= '0') + && (source[sourcePoint + i] <= '9')) { + j++; + } + } + + if (j == 12) { + next_mode = c1Mode.C1_ASCII; + done = true; + } + } + + if ((length - sourcePoint) >= 8) { + j = 0; + + for (i = 0; i < 8; i++) { + if ((source[sourcePoint + i] >= '0') + && (source[sourcePoint + i] <= '9')) { + j++; + } + } + + if ((length - sourcePoint) == 8) { + latch = true; + } else { + latch = true; + for (j = sourcePoint + 8; j < length; j++) { + if ((source[j] <= '0') || (source[j] >= '9')) { + latch = false; + } + } + } + + if ((j == 8) && latch) { + next_mode = c1Mode.C1_ASCII; + done = true; + } + } + + if (!(done)) { + next_mode = lookAheadTest(length, sourcePoint, current_mode); + } + } + + if (next_mode != c1Mode.C1_TEXT) { + data[targetPoint] = 255; + targetPoint++; /* Unlatch */ + } else { + if (source[sourcePoint] > 127) { + text_buffer[text_p] = 1; + text_p++; + text_buffer[text_p] = 30; + text_p++; /* Upper Shift */ + shift_set = text_shift[source[sourcePoint] - 128]; + value = text_value[source[sourcePoint] - 128]; + } else { + shift_set = text_shift[source[sourcePoint]]; + value = text_value[source[sourcePoint]]; + } + + if ((inputDataType == DataType.GS1) && (source[sourcePoint] == '[')) { + shift_set = 2; + value = 27; /* FNC1 */ + } + + if (shift_set != 0) { + text_buffer[text_p] = shift_set - 1; + text_p++; + } + text_buffer[text_p] = value; + text_p++; + + if (text_p >= 3) { + int iv; + + iv = (1600 * text_buffer[0]) + (40 * text_buffer[1]) + + (text_buffer[2]) + 1; + data[targetPoint] = iv / 256; + targetPoint++; + data[targetPoint] = iv % 256; + targetPoint++; + + text_buffer[0] = text_buffer[3]; + text_buffer[1] = text_buffer[4]; + text_buffer[2] = text_buffer[5]; + text_buffer[3] = 0; + text_buffer[4] = 0; + text_buffer[5] = 0; + text_p -= 3; + } + sourcePoint++; + } + } + + if (current_mode == c1Mode.C1_EDI) { /* Step E - EDI Encodation */ + + value = 0; + next_mode = c1Mode.C1_EDI; + if (edi_p == 0) { + if ((length - sourcePoint) >= 12) { + j = 0; + + for (i = 0; i < 12; i++) { + if ((source[sourcePoint + i] >= '0') + && (source[sourcePoint + i] <= '9')) { + j++; + } + } + + if (j == 12) { + next_mode = c1Mode.C1_ASCII; + } + } + + if ((length - sourcePoint) >= 8) { + j = 0; + + for (i = 0; i < 8; i++) { + if ((source[sourcePoint + i] >= '0') && + (source[sourcePoint + i] <= '9')) { + j++; + } + } + + if ((length - sourcePoint) == 8) { + latch = true; + } else { + latch = true; + for (j = sourcePoint + 8; j < length; j++) { + if ((source[j] <= '0') || (source[j] >= '9')) { + latch = false; + } + } + } + + if ((j == 8) && latch) { + next_mode = c1Mode.C1_ASCII; + } + } + + if (!((isEdiEncodable(source[sourcePoint]) + && isEdiEncodable(source[sourcePoint + 1])) + && isEdiEncodable(source[sourcePoint + 2]))) { + next_mode = c1Mode.C1_ASCII; + } + } + + if (next_mode != c1Mode.C1_EDI) { + data[targetPoint] = 255; + targetPoint++; /* Unlatch */ + } else { + if (source[sourcePoint] == 13) { + value = 0; + } + if (source[sourcePoint] == '*') { + value = 1; + } + if (source[sourcePoint] == '>') { + value = 2; + } + if (source[sourcePoint] == ' ') { + value = 3; + } + if ((source[sourcePoint] >= '0') && (source[sourcePoint] <= '9')) { + value = source[sourcePoint] - '0' + 4; + } + if ((source[sourcePoint] >= 'A') && (source[sourcePoint] <= 'Z')) { + value = source[sourcePoint] - 'A' + 14; + } + + edi_buffer[edi_p] = value; + edi_p++; + + if (edi_p >= 3) { + int iv; + + iv = (1600 * edi_buffer[0]) + (40 * edi_buffer[1]) + + (edi_buffer[2]) + 1; + data[targetPoint] = iv / 256; + targetPoint++; + data[targetPoint] = iv % 256; + targetPoint++; + + edi_buffer[0] = edi_buffer[3]; + edi_buffer[1] = edi_buffer[4]; + edi_buffer[2] = edi_buffer[5]; + edi_buffer[3] = 0; + edi_buffer[4] = 0; + edi_buffer[5] = 0; + edi_p -= 3; + } + sourcePoint++; + } + } + + if (current_mode == c1Mode.C1_DECIMAL) { /* Step F - Decimal encodation */ + + next_mode = c1Mode.C1_DECIMAL; + + data_left = length - sourcePoint; + decimal_count = 0; + + if (data_left >= 1) { + if ((source[sourcePoint] >= '0') && (source[sourcePoint] <= '9')) { + decimal_count = 1; + } + } + if (data_left >= 2) { + if ((decimal_count == 1) && ((source[sourcePoint + 1] >= '0') + && (source[sourcePoint + 1] <= '9'))) { + decimal_count = 2; + } + } + if (data_left >= 3) { + if ((decimal_count == 2) && ((source[sourcePoint + 2] >= '0') + && (source[sourcePoint + 2] <= '9'))) { + decimal_count = 3; + } + } + + if (decimal_count != 3) { + + /* Finish Decimal mode and go back to ASCII */ + + decimal_binary.append("111111"); /* Unlatch */ + + target_count = 3; + if (decimal_binary.length() <= 16) { + target_count = 2; + } + if (decimal_binary.length() <= 8) { + target_count = 1; + } + bits_left_in_byte = (8 * target_count) - decimal_binary.length(); + if (bits_left_in_byte == 8) { + bits_left_in_byte = 0; + } + + if (bits_left_in_byte == 2) { + decimal_binary.append("01"); + } + + if ((bits_left_in_byte == 4) || (bits_left_in_byte == 6)) { + if (decimal_count >= 1) { + sub_value = source[sourcePoint] - '0' + 1; + + for (i = 0x08; i > 0; i = i >> 1) { + if ((sub_value & i) != 0) { + decimal_binary.append("1"); + } else { + decimal_binary.append("0"); + } + } + sourcePoint++; + } else { + decimal_binary.append("1111"); + } + } + + if (bits_left_in_byte == 6) { + decimal_binary.append("01"); + } + + /* Binary buffer is full - transfer to data */ + if (target_count >= 1) { + for (i = 0; i < 8; i++) { + if (decimal_binary.charAt(i) == '1') { + data[targetPoint] += 128 >> i; + } + } + targetPoint++; + } + if (target_count >= 2) { + for (i = 0; i < 8; i++) { + if (decimal_binary.charAt(8 + i) == '1') { + data[targetPoint] += 128 >> i; + } + + } + targetPoint++; + } + if (target_count == 3) { + for (i = 0; i < 8; i++) { + if (decimal_binary.charAt(16 + i) == '1') { + data[targetPoint] += 128 >> i; + } + + } + targetPoint++; + } + + next_mode = c1Mode.C1_ASCII; + } else { + /* There are three digits - convert the value to binary */ + value = (100 * (source[sourcePoint] - '0')) + + (10 * (source[sourcePoint + 1] - '0')) + + (source[sourcePoint + 2] - '0') + 1; + + for (i = 0x200; i > 0; i = i >> 1) { + if ((value & i) != 0) { + decimal_binary.append("1"); + } else { + decimal_binary.append("0"); + } + } + sourcePoint += 3; + } + + if (decimal_binary.length() >= 24) { + /* Binary buffer is full - transfer to data */ + for (i = 0; i < 8; i++) { + if (decimal_binary.charAt(i) == '1') { + data[targetPoint] += 128 >> i; + } + + if (decimal_binary.charAt(8 + i) == '1') { + data[targetPoint + 1] += 128 >> i; + } + + if (decimal_binary.charAt(16 + i) == '1') { + data[targetPoint + 2] += 128 >> i; + } + + } + targetPoint += 3; + + if (decimal_binary.length() > 24) { + decimal_binary = new StringBuilder(decimal_binary.substring(24)); + } + } + } + + if (current_mode == c1Mode.C1_BYTE) { + next_mode = c1Mode.C1_BYTE; + + if ((inputDataType == DataType.GS1) && (source[sourcePoint] == '[')) { + next_mode = c1Mode.C1_ASCII; + } else { + if (source[sourcePoint] <= 127) { + next_mode = lookAheadTest(length, sourcePoint, current_mode); + } + } + + if (next_mode != c1Mode.C1_BYTE) { + /* Insert byte field length */ + if ((targetPoint - byte_start) <= 249) { + for (i = targetPoint; i >= byte_start; i--) { + data[i + 1] = data[i]; + } + data[byte_start] = (targetPoint - byte_start); + targetPoint++; + } else { + for (i = targetPoint; i >= byte_start; i--) { + data[i + 2] = data[i]; + } + data[byte_start] = 249 + ((targetPoint - byte_start) / 250); + data[byte_start + 1] = ((targetPoint - byte_start) % 250); + targetPoint += 2; + } + } else { + data[targetPoint] = source[sourcePoint]; + targetPoint++; + sourcePoint++; + } + } + + if (targetPoint > 1480) { + /* Data is too large for symbol */ + errorMsg.append("Input data too long"); + return 0; + } + } while (sourcePoint < length); + + /* Empty buffers */ + if (c40_p == 2) { + int iv; + + c40_buffer[2] = 1; + iv = (1600 * c40_buffer[0]) + (40 * c40_buffer[1]) + + (c40_buffer[2]) + 1; + data[targetPoint] = iv / 256; + targetPoint++; + data[targetPoint] = iv % 256; + targetPoint++; + data[targetPoint] = 255; + targetPoint++; /* Unlatch */ + } + if (c40_p == 1) { + int iv; + + c40_buffer[1] = 1; + c40_buffer[2] = 31; /* Pad */ + iv = (1600 * c40_buffer[0]) + (40 * c40_buffer[1]) + + (c40_buffer[2]) + 1; + data[targetPoint] = iv / 256; + targetPoint++; + data[targetPoint] = iv % 256; + targetPoint++; + data[targetPoint] = 255; + targetPoint++; /* Unlatch */ + } + if (text_p == 2) { + int iv; + + text_buffer[2] = 1; + iv = (1600 * text_buffer[0]) + (40 * text_buffer[1]) + + (text_buffer[2]) + 1; + data[targetPoint] = iv / 256; + targetPoint++; + data[targetPoint] = iv % 256; + targetPoint++; + data[targetPoint] = 255; + targetPoint++; /* Unlatch */ + } + if (text_p == 1) { + int iv; + + text_buffer[1] = 1; + text_buffer[2] = 31; /* Pad */ + iv = (1600 * text_buffer[0]) + (40 * text_buffer[1]) + + (text_buffer[2]) + 1; + data[targetPoint] = iv / 256; + targetPoint++; + data[targetPoint] = iv % 256; + targetPoint++; + data[targetPoint] = 255; + targetPoint++; /* Unlatch */ + } + + if (current_mode == c1Mode.C1_DECIMAL) { + /* Finish Decimal mode and go back to ASCII */ + + decimal_binary.append("111111"); /* Unlatch */ + + target_count = 3; + if (decimal_binary.length() <= 16) { + target_count = 2; + } + if (decimal_binary.length() <= 8) { + target_count = 1; + } + bits_left_in_byte = (8 * target_count) - decimal_binary.length(); + if (bits_left_in_byte == 8) { + bits_left_in_byte = 0; + } + + if (bits_left_in_byte == 2) { + decimal_binary.append("01"); + } + + if ((bits_left_in_byte == 4) || (bits_left_in_byte == 6)) { + decimal_binary.append("1111"); + } + + if (bits_left_in_byte == 6) { + decimal_binary.append("01"); + } + + /* Binary buffer is full - transfer to data */ + if (target_count >= 1) { + for (i = 0; i < 8; i++) { + if (decimal_binary.charAt(i) == '1') { + data[targetPoint] += 128 >> i; + } + } + targetPoint++; + } + if (target_count >= 2) { + for (i = 0; i < 8; i++) { + if (decimal_binary.charAt(8 + i) == '1') { + data[targetPoint] += 128 >> i; + } + } + targetPoint++; + } + if (target_count == 3) { + for (i = 0; i < 8; i++) { + if (decimal_binary.charAt(16 + i) == '1') { + data[targetPoint] += 128 >> i; + } + } + targetPoint++; + } + } + + if (current_mode == c1Mode.C1_BYTE) { + /* Insert byte field length */ + if ((targetPoint - byte_start) <= 249) { + for (i = targetPoint; i >= byte_start; i--) { + data[i + 1] = data[i]; + } + data[byte_start] = (targetPoint - byte_start); + targetPoint++; + } else { + for (i = targetPoint; i >= byte_start; i--) { + data[i + 2] = data[i]; + } + data[byte_start] = 249 + ((targetPoint - byte_start) / 250); + data[byte_start + 1] = ((targetPoint - byte_start) % 250); + targetPoint += 2; + } + } + + /* Re-check length of data */ + if (targetPoint > 1480) { + /* Data is too large for symbol */ + errorMsg.append("Input data too long"); + return 0; + } + + return targetPoint; + } + + private c1Mode lookAheadTest(int sourcelen, int position, + c1Mode current_mode) { + double ascii_count, c40_count, text_count, edi_count, byte_count; + int reduced_char; + int done, best_count, sp; + c1Mode best_scheme; + + /* Step J */ + if (current_mode == c1Mode.C1_ASCII) { + ascii_count = 0.0; + c40_count = 1.0; + text_count = 1.0; + edi_count = 1.0; + byte_count = 2.0; + } else { + ascii_count = 1.0; + c40_count = 2.0; + text_count = 2.0; + edi_count = 2.0; + byte_count = 3.0; + } + + switch (current_mode) { + case C1_C40: + c40_count = 0.0; + break; + case C1_TEXT: + text_count = 0.0; + break; + case C1_BYTE: + byte_count = 0.0; + break; + case C1_EDI: + edi_count = 0.0; + break; + } + + for (sp = position; + (sp < sourcelen) && (sp <= (position + 8)); sp++) { + + if (source[sp] <= 127) { + reduced_char = source[sp]; + } else { + reduced_char = source[sp] - 127; + } + + /* Step L */ + if ((source[sp] >= '0') && (source[sp] <= '9')) { + ascii_count += 0.5; + } else { + ascii_count = roundUpToNextInteger(ascii_count); + if (source[sp] > 127) { + ascii_count += 2.0; + } else { + ascii_count += 1.0; + } + } + + /* Step M */ + done = 0; + if (reduced_char == ' ') { + c40_count += (2.0 / 3.0); + done = 1; + } + if ((reduced_char >= '0') && (reduced_char <= '9')) { + c40_count += (2.0 / 3.0); + done = 1; + } + if ((reduced_char >= 'A') && (reduced_char <= 'Z')) { + c40_count += (2.0 / 3.0); + done = 1; + } + if (source[sp] > 127) { + c40_count += (4.0 / 3.0); + } + if (done == 0) { + c40_count += (4.0 / 3.0); + } + + /* Step N */ + done = 0; + if (reduced_char == ' ') { + text_count += (2.0 / 3.0); + done = 1; + } + if ((reduced_char >= '0') && (reduced_char <= '9')) { + text_count += (2.0 / 3.0); + done = 1; + } + if ((reduced_char >= 'a') && (reduced_char <= 'z')) { + text_count += (2.0 / 3.0); + done = 1; + } + if (source[sp] > 127) { + text_count += (4.0 / 3.0); + } + if (done == 0) { + text_count += (4.0 / 3.0); + } + + /* Step O */ + done = 0; + if (source[sp] == 13) { + edi_count += (2.0 / 3.0); + done = 1; + } + if (source[sp] == '*') { + edi_count += (2.0 / 3.0); + done = 1; + } + if (source[sp] == '>') { + edi_count += (2.0 / 3.0); + done = 1; + } + if (source[sp] == ' ') { + edi_count += (2.0 / 3.0); + done = 1; + } + if ((source[sp] >= '0') && (source[sp] <= '9')) { + edi_count += (2.0 / 3.0); + done = 1; + } + if ((source[sp] >= 'A') && (source[sp] <= 'Z')) { + edi_count += (2.0 / 3.0); + done = 1; + } + if (source[sp] > 127) { + edi_count += (13.0 / 3.0); + } else { + if (done == 0) { + edi_count += (10.0 / 3.0); + } + } + + /* Step P */ + if ((inputDataType == DataType.GS1) && (source[sp] == '[')) { + byte_count += 3.0; + } else { + byte_count += 1.0; + } + + } + + ascii_count = roundUpToNextInteger(ascii_count); + c40_count = roundUpToNextInteger(c40_count); + text_count = roundUpToNextInteger(text_count); + edi_count = roundUpToNextInteger(edi_count); + byte_count = roundUpToNextInteger(byte_count); + best_scheme = c1Mode.C1_ASCII; + + if (sp == sourcelen) { + /* Step K */ + best_count = (int) edi_count; + + if (text_count <= best_count) { + best_count = (int) text_count; + best_scheme = c1Mode.C1_TEXT; + } + + if (c40_count <= best_count) { + best_count = (int) c40_count; + best_scheme = c1Mode.C1_C40; + } + + if (ascii_count <= best_count) { + best_count = (int) ascii_count; + best_scheme = c1Mode.C1_ASCII; + } + + if (byte_count <= best_count) { + best_scheme = c1Mode.C1_BYTE; + } + } else { + /* Step Q */ + + if (((edi_count + 1.0 <= ascii_count) + && (edi_count + 1.0 <= c40_count)) + && ((edi_count + 1.0 <= byte_count) + && (edi_count + 1.0 <= text_count))) { + best_scheme = c1Mode.C1_EDI; + } + + if ((c40_count + 1.0 <= ascii_count) + && (c40_count + 1.0 <= text_count)) { + + if (c40_count < edi_count) { + best_scheme = c1Mode.C1_C40; + } else { + if (c40_count == edi_count) { + if (preferEdi(sourcelen, position)) { + best_scheme = c1Mode.C1_EDI; + } else { + best_scheme = c1Mode.C1_C40; + } + } + } + } + + if (((text_count + 1.0 <= ascii_count) + && (text_count + 1.0 <= c40_count)) + && ((text_count + 1.0 <= byte_count) + && (text_count + 1.0 <= edi_count))) { + best_scheme = c1Mode.C1_TEXT; + } + + if (((ascii_count + 1.0 <= byte_count) + && (ascii_count + 1.0 <= c40_count)) + && ((ascii_count + 1.0 <= text_count) + && (ascii_count + 1.0 <= edi_count))) { + best_scheme = c1Mode.C1_ASCII; + } + + if (((byte_count + 1.0 <= ascii_count) + && (byte_count + 1.0 <= c40_count)) + && ((byte_count + 1.0 <= text_count) + && (byte_count + 1.0 <= edi_count))) { + best_scheme = c1Mode.C1_BYTE; + } + } + return best_scheme; + } + + private double roundUpToNextInteger(double input) { + double fraction, output; + + fraction = input - (int) input; + if (fraction > 0.01) { + output = (input - fraction) + 1.0; + } else { + output = input; + } + + return output; + } + + private boolean preferEdi(int sourcelen, int position) { + int i; + + for (i = position; isEdiEncodable(source[position + i]) + && ((position + i) < sourcelen); i++) { + ; + } + + if ((position + i) == sourcelen) { + /* Reached end of input */ + return false; + } + + if (source[position + i - 1] == 13) { + return true; + } + if (source[position + i - 1] == '*') { + return true; + } + if (source[position + i - 1] == '>') { + return true; + } + + return false; + } + + private boolean isEdiEncodable(int input) { + boolean result = false; + + if (input == 13) { + result = true; + } + if (input == '*') { + result = true; + } + if (input == '>') { + result = true; + } + if (input == ' ') { + result = true; + } + if ((input >= '0') && (input <= '9')) { + result = true; + } + if ((input >= 'A') && (input <= 'Z')) { + result = true; + } + + return result; + } + + private void plotCentralFinder(int start_row, int row_count, int full_rows) { + int i; + + for (i = 0; i < row_count; i++) { + if (i < full_rows) { + plotHorizontalBar(start_row + (i * 2), 1); + } else { + plotHorizontalBar(start_row + (i * 2), 0); + if (i != row_count - 1) { + setGridModule(start_row + (i * 2) + 1, 1); + setGridModule(start_row + (i * 2) + 1, symbolWidth - 2); + } + } + } + } + + private void plotHorizontalBar(int row_no, int full) { + int i; + + if (full != 0) { + for (i = 0; i < symbolWidth; i++) { + setGridModule(row_no, i); + } + } else { + for (i = 1; i < symbolWidth - 1; i++) { + setGridModule(row_no, i); + } + } + } + + private void plotVerticalBar(int column, int height, int top) { + int i; + + if (top != 0) { + for (i = 0; i < height; i++) { + setGridModule(i, column); + } + } else { + for (i = 0; i < height; i++) { + setGridModule(rowCount - i - 1, column); + } + } + } + + private void plotSpigot(int row_no) { + int i; + + for (i = symbolWidth - 1; i > 0; i--) { + if (outputGrid[row_no][i - 1]) { + setGridModule(row_no, i); + } + } + } + + private void plotDataBlock(int start_row, int start_col, int height, + int width, int row_offset, int col_offset) { + int i, j; + + for (i = start_row; i < (start_row + height); i++) { + for (j = start_col; j < (start_col + width); j++) { + if (datagrid[i][j] == '1') { + setGridModule(i + row_offset, j + col_offset); + } + } + } + } + + private void setGridModule(int row, int column) { + outputGrid[row][column] = true; + } + + private void resetGridModule(int row, int column) { + outputGrid[row][column] = false; + } + + private int getSize(Version version) { + int size = 0; + + switch (version) { + case A: + size = 1; + break; + case B: + size = 2; + break; + case C: + size = 3; + break; + case D: + size = 4; + break; + case E: + size = 5; + break; + case F: + size = 6; + break; + case G: + size = 7; + break; + case H: + size = 8; + break; + } + + return size; + } + + private enum c1Mode { + C1_ASCII, C1_C40, C1_DECIMAL, C1_TEXT, C1_EDI, C1_BYTE + } + + public enum Version { + NONE, A, B, C, D, E, F, G, H, S, T + } +} diff --git a/barcode/src/main/java/org/xbib/graphics/barcode/Composite.java b/barcode/src/main/java/org/xbib/graphics/barcode/Composite.java new file mode 100755 index 0000000..fd0ef27 --- /dev/null +++ b/barcode/src/main/java/org/xbib/graphics/barcode/Composite.java @@ -0,0 +1,2846 @@ +package org.xbib.graphics.barcode; + +import org.xbib.graphics.barcode.util.TextBox; +import java.awt.geom.Rectangle2D; +import java.math.BigInteger; +import java.util.ArrayList; +import java.util.List; + +/** + * Implements GS1 Composite symbology according to ISO/IEC 24723:2010. + * Composite symbols comprise a 2D element which encodes GS1 data + * and a "linear" element which can be UPC, EAN, Code 128 or + * GS1 DataBar symbol. + */ +public class Composite extends Symbol { + /* CC-A component coefficients from ISO/IEC 24728:2006 Annex F */ + private int[] ccaCoeffs = { + /* k = 4 */ + 522, 568, 723, 809, + /* k = 5 */ + 427, 919, 460, 155, 566, + /* k = 6 */ + 861, 285, 19, 803, 17, 766, + /* k = 7 */ + 76, 925, 537, 597, 784, 691, 437, + /* k = 8 */ + 237, 308, 436, 284, 646, 653, 428, 379 + }; + private int[] coefrs = { + /* k = 2 */ + 27, 917, + /* k = 4 */ + 522, 568, 723, 809, + /* k = 8 */ + 237, 308, 436, 284, 646, 653, 428, 379, + /* k = 16 */ + 274, 562, 232, 755, 599, 524, 801, 132, 295, 116, 442, 428, 295, 42, 176, 65, + /* k = 32 */ + 361, 575, 922, 525, 176, 586, 640, 321, 536, 742, 677, 742, 687, 284, 193, 517, + 273, 494, 263, 147, 593, 800, 571, 320, 803, 133, 231, 390, 685, 330, 63, 410, + /* k = 64 */ + 539, 422, 6, 93, 862, 771, 453, 106, 610, 287, 107, 505, 733, 877, 381, 612, + 723, 476, 462, 172, 430, 609, 858, 822, 543, 376, 511, 400, 672, 762, 283, 184, + 440, 35, 519, 31, 460, 594, 225, 535, 517, 352, 605, 158, 651, 201, 488, 502, + 648, 733, 717, 83, 404, 97, 280, 771, 840, 629, 4, 381, 843, 623, 264, 543, + /* k = 128 */ + 521, 310, 864, 547, 858, 580, 296, 379, 53, 779, 897, 444, 400, 925, 749, 415, + 822, 93, 217, 208, 928, 244, 583, 620, 246, 148, 447, 631, 292, 908, 490, 704, + 516, 258, 457, 907, 594, 723, 674, 292, 272, 96, 684, 432, 686, 606, 860, 569, + 193, 219, 129, 186, 236, 287, 192, 775, 278, 173, 40, 379, 712, 463, 646, 776, + 171, 491, 297, 763, 156, 732, 95, 270, 447, 90, 507, 48, 228, 821, 808, 898, + 784, 663, 627, 378, 382, 262, 380, 602, 754, 336, 89, 614, 87, 432, 670, 616, + 157, 374, 242, 726, 600, 269, 375, 898, 845, 454, 354, 130, 814, 587, 804, 34, + 211, 330, 539, 297, 827, 865, 37, 517, 834, 315, 550, 86, 801, 4, 108, 539, + /* k = 256 */ + 524, 894, 75, 766, 882, 857, 74, 204, 82, 586, 708, 250, 905, 786, 138, 720, + 858, 194, 311, 913, 275, 190, 375, 850, 438, 733, 194, 280, 201, 280, 828, 757, + 710, 814, 919, 89, 68, 569, 11, 204, 796, 605, 540, 913, 801, 700, 799, 137, + 439, 418, 592, 668, 353, 859, 370, 694, 325, 240, 216, 257, 284, 549, 209, 884, + 315, 70, 329, 793, 490, 274, 877, 162, 749, 812, 684, 461, 334, 376, 849, 521, + 307, 291, 803, 712, 19, 358, 399, 908, 103, 511, 51, 8, 517, 225, 289, 470, + 637, 731, 66, 255, 917, 269, 463, 830, 730, 433, 848, 585, 136, 538, 906, 90, + 2, 290, 743, 199, 655, 903, 329, 49, 802, 580, 355, 588, 188, 462, 10, 134, + 628, 320, 479, 130, 739, 71, 263, 318, 374, 601, 192, 605, 142, 673, 687, 234, + 722, 384, 177, 752, 607, 640, 455, 193, 689, 707, 805, 641, 48, 60, 732, 621, + 895, 544, 261, 852, 655, 309, 697, 755, 756, 60, 231, 773, 434, 421, 726, 528, + 503, 118, 49, 795, 32, 144, 500, 238, 836, 394, 280, 566, 319, 9, 647, 550, + 73, 914, 342, 126, 32, 681, 331, 792, 620, 60, 609, 441, 180, 791, 893, 754, + 605, 383, 228, 749, 760, 213, 54, 297, 134, 54, 834, 299, 922, 191, 910, 532, + 609, 829, 189, 20, 167, 29, 872, 449, 83, 402, 41, 656, 505, 579, 481, 173, + 404, 251, 688, 95, 497, 555, 642, 543, 307, 159, 924, 558, 648, 55, 497, 10, + /* k = 512 */ + 352, 77, 373, 504, 35, 599, 428, 207, 409, 574, 118, 498, 285, 380, 350, 492, + 197, 265, 920, 155, 914, 299, 229, 643, 294, 871, 306, 88, 87, 193, 352, 781, + 846, 75, 327, 520, 435, 543, 203, 666, 249, 346, 781, 621, 640, 268, 794, 534, + 539, 781, 408, 390, 644, 102, 476, 499, 290, 632, 545, 37, 858, 916, 552, 41, + 542, 289, 122, 272, 383, 800, 485, 98, 752, 472, 761, 107, 784, 860, 658, 741, + 290, 204, 681, 407, 855, 85, 99, 62, 482, 180, 20, 297, 451, 593, 913, 142, + 808, 684, 287, 536, 561, 76, 653, 899, 729, 567, 744, 390, 513, 192, 516, 258, + 240, 518, 794, 395, 768, 848, 51, 610, 384, 168, 190, 826, 328, 596, 786, 303, + 570, 381, 415, 641, 156, 237, 151, 429, 531, 207, 676, 710, 89, 168, 304, 402, + 40, 708, 575, 162, 864, 229, 65, 861, 841, 512, 164, 477, 221, 92, 358, 785, + 288, 357, 850, 836, 827, 736, 707, 94, 8, 494, 114, 521, 2, 499, 851, 543, + 152, 729, 771, 95, 248, 361, 578, 323, 856, 797, 289, 51, 684, 466, 533, 820, + 669, 45, 902, 452, 167, 342, 244, 173, 35, 463, 651, 51, 699, 591, 452, 578, + 37, 124, 298, 332, 552, 43, 427, 119, 662, 777, 475, 850, 764, 364, 578, 911, + 283, 711, 472, 420, 245, 288, 594, 394, 511, 327, 589, 777, 699, 688, 43, 408, + 842, 383, 721, 521, 560, 644, 714, 559, 62, 145, 873, 663, 713, 159, 672, 729, + 624, 59, 193, 417, 158, 209, 563, 564, 343, 693, 109, 608, 563, 365, 181, 772, + 677, 310, 248, 353, 708, 410, 579, 870, 617, 841, 632, 860, 289, 536, 35, 777, + 618, 586, 424, 833, 77, 597, 346, 269, 757, 632, 695, 751, 331, 247, 184, 45, + 787, 680, 18, 66, 407, 369, 54, 492, 228, 613, 830, 922, 437, 519, 644, 905, + 789, 420, 305, 441, 207, 300, 892, 827, 141, 537, 381, 662, 513, 56, 252, 341, + 242, 797, 838, 837, 720, 224, 307, 631, 61, 87, 560, 310, 756, 665, 397, 808, + 851, 309, 473, 795, 378, 31, 647, 915, 459, 806, 590, 731, 425, 216, 548, 249, + 321, 881, 699, 535, 673, 782, 210, 815, 905, 303, 843, 922, 281, 73, 469, 791, + 660, 162, 498, 308, 155, 422, 907, 817, 187, 62, 16, 425, 535, 336, 286, 437, + 375, 273, 610, 296, 183, 923, 116, 667, 751, 353, 62, 366, 691, 379, 687, 842, + 37, 357, 720, 742, 330, 5, 39, 923, 311, 424, 242, 749, 321, 54, 669, 316, + 342, 299, 534, 105, 667, 488, 640, 672, 576, 540, 316, 486, 721, 610, 46, 656, + 447, 171, 616, 464, 190, 531, 297, 321, 762, 752, 533, 175, 134, 14, 381, 433, + 717, 45, 111, 20, 596, 284, 736, 138, 646, 411, 877, 669, 141, 919, 45, 780, + 407, 164, 332, 899, 165, 726, 600, 325, 498, 655, 357, 752, 768, 223, 849, 647, + 63, 310, 863, 251, 366, 304, 282, 738, 675, 410, 389, 244, 31, 121, 303, 263 + }; + + /* rows, error codewords, k-offset of valid CC-A sizes from ISO/IEC 24723:2006 Table 9 */ + private int[] ccaVariants = { + 5, 6, 7, 8, 9, 10, 12, 4, 5, 6, 7, 8, 3, 4, 5, 6, 7, 4, 4, 5, 5, 6, 6, 7, 4, 5, 6, 7, 7, 4, 5, 6, 7, 8, 0, 0, 4, 4, 9, 9, 15, 0, 4, 9, 15, 15, 0, 4, 9, 15, 22 + }; + + /* following is Left RAP, Centre RAP, Right RAP and Start Cluster from ISO/IEC 24723:2006 tables 10 and 11 */ + private int[] aRAPTable = { + 39, 1, 32, 8, 14, 43, 20, 11, 1, 5, 15, 21, 40, 43, 46, 34, 29, 0, 0, 0, 0, 0, 0, 0, 43, 33, 37, 47, 1, 20, 23, 26, 14, 9, 19, 33, 12, 40, 46, 23, 52, 23, 13, 17, 27, 33, 52, 3, 6, 46, 41, 6, 0, 3, 3, 3, 0, 3, 3, 0, 3, 6, 6, 0, 0, 0, 0, 3 + }; + private String[] codagemc = { + "urA", "xfs", "ypy", "unk", "xdw", "yoz", "pDA", "uls", "pBk", "eBA", + "pAs", "eAk", "prA", "uvs", "xhy", "pnk", "utw", "xgz", "fDA", "pls", "fBk", "frA", "pvs", + "uxy", "fnk", "ptw", "uwz", "fls", "psy", "fvs", "pxy", "ftw", "pwz", "fxy", "yrx", "ufk", + "xFw", "ymz", "onA", "uds", "xEy", "olk", "ucw", "dBA", "oks", "uci", "dAk", "okg", "dAc", + "ovk", "uhw", "xaz", "dnA", "ots", "ugy", "dlk", "osw", "ugj", "dks", "osi", "dvk", "oxw", + "uiz", "dts", "owy", "dsw", "owj", "dxw", "oyz", "dwy", "dwj", "ofA", "uFs", "xCy", "odk", + "uEw", "xCj", "clA", "ocs", "uEi", "ckk", "ocg", "ckc", "ckE", "cvA", "ohs", "uay", "ctk", + "ogw", "uaj", "css", "ogi", "csg", "csa", "cxs", "oiy", "cww", "oij", "cwi", "cyy", "oFk", + "uCw", "xBj", "cdA", "oEs", "uCi", "cck", "oEg", "uCb", "ccc", "oEa", "ccE", "oED", "chk", + "oaw", "uDj", "cgs", "oai", "cgg", "oab", "cga", "cgD", "obj", "cib", "cFA", "oCs", "uBi", + "cEk", "oCg", "uBb", "cEc", "oCa", "cEE", "oCD", "cEC", "cas", "cag", "caa", "cCk", "uAr", + "oBa", "oBD", "cCB", "tfk", "wpw", "yez", "mnA", "tds", "woy", "mlk", "tcw", "woj", "FBA", + "mks", "FAk", "mvk", "thw", "wqz", "FnA", "mts", "tgy", "Flk", "msw", "Fks", "Fkg", "Fvk", + "mxw", "tiz", "Fts", "mwy", "Fsw", "Fsi", "Fxw", "myz", "Fwy", "Fyz", "vfA", "xps", "yuy", + "vdk", "xow", "yuj", "qlA", "vcs", "xoi", "qkk", "vcg", "xob", "qkc", "vca", "mfA", "tFs", + "wmy", "qvA", "mdk", "tEw", "wmj", "qtk", "vgw", "xqj", "hlA", "Ekk", "mcg", "tEb", "hkk", + "qsg", "hkc", "EvA", "mhs", "tay", "hvA", "Etk", "mgw", "taj", "htk", "qww", "vij", "hss", + "Esg", "hsg", "Exs", "miy", "hxs", "Eww", "mij", "hww", "qyj", "hwi", "Eyy", "hyy", "Eyj", + "hyj", "vFk", "xmw", "ytj", "qdA", "vEs", "xmi", "qck", "vEg", "xmb", "qcc", "vEa", "qcE", + "qcC", "mFk", "tCw", "wlj", "qhk", "mEs", "tCi", "gtA", "Eck", "vai", "tCb", "gsk", "Ecc", + "mEa", "gsc", "qga", "mED", "EcC", "Ehk", "maw", "tDj", "gxk", "Egs", "mai", "gws", "qii", + "mab", "gwg", "Ega", "EgD", "Eiw", "mbj", "gyw", "Eii", "gyi", "Eib", "gyb", "gzj", "qFA", + "vCs", "xli", "qEk", "vCg", "xlb", "qEc", "vCa", "qEE", "vCD", "qEC", "qEB", "EFA", "mCs", + "tBi", "ghA", "EEk", "mCg", "tBb", "ggk", "qag", "vDb", "ggc", "EEE", "mCD", "ggE", "qaD", + "ggC", "Eas", "mDi", "gis", "Eag", "mDb", "gig", "qbb", "gia", "EaD", "giD", "gji", "gjb", + "qCk", "vBg", "xkr", "qCc", "vBa", "qCE", "vBD", "qCC", "qCB", "ECk", "mBg", "tAr", "gak", + "ECc", "mBa", "gac", "qDa", "mBD", "gaE", "ECC", "gaC", "ECB", "EDg", "gbg", "gba", "gbD", + "vAq", "vAn", "qBB", "mAq", "EBE", "gDE", "gDC", "gDB", "lfA", "sps", "wey", "ldk", "sow", + "ClA", "lcs", "soi", "Ckk", "lcg", "Ckc", "CkE", "CvA", "lhs", "sqy", "Ctk", "lgw", "sqj", + "Css", "lgi", "Csg", "Csa", "Cxs", "liy", "Cww", "lij", "Cwi", "Cyy", "Cyj", "tpk", "wuw", + "yhj", "ndA", "tos", "wui", "nck", "tog", "wub", "ncc", "toa", "ncE", "toD", "lFk", "smw", + "wdj", "nhk", "lEs", "smi", "atA", "Cck", "tqi", "smb", "ask", "ngg", "lEa", "asc", "CcE", + "asE", "Chk", "law", "snj", "axk", "Cgs", "trj", "aws", "nii", "lab", "awg", "Cga", "awa", + "Ciw", "lbj", "ayw", "Cii", "ayi", "Cib", "Cjj", "azj", "vpA", "xus", "yxi", "vok", "xug", + "yxb", "voc", "xua", "voE", "xuD", "voC", "nFA", "tms", "wti", "rhA", "nEk", "xvi", "wtb", + "rgk", "vqg", "xvb", "rgc", "nEE", "tmD", "rgE", "vqD", "nEB", "CFA", "lCs", "sli", "ahA", + "CEk", "lCg", "slb", "ixA", "agk", "nag", "tnb", "iwk", "rig", "vrb", "lCD", "iwc", "agE", + "naD", "iwE", "CEB", "Cas", "lDi", "ais", "Cag", "lDb", "iys", "aig", "nbb", "iyg", "rjb", + "CaD", "aiD", "Cbi", "aji", "Cbb", "izi", "ajb", "vmk", "xtg", "ywr", "vmc", "xta", "vmE", + "xtD", "vmC", "vmB", "nCk", "tlg", "wsr", "rak", "nCc", "xtr", "rac", "vna", "tlD", "raE", + "nCC", "raC", "nCB", "raB", "CCk", "lBg", "skr", "aak", "CCc", "lBa", "iik", "aac", "nDa", + "lBD", "iic", "rba", "CCC", "iiE", "aaC", "CCB", "aaB", "CDg", "lBr", "abg", "CDa", "ijg", + "aba", "CDD", "ija", "abD", "CDr", "ijr", "vlc", "xsq", "vlE", "xsn", "vlC", "vlB", "nBc", + "tkq", "rDc", "nBE", "tkn", "rDE", "vln", "rDC", "nBB", "rDB", "CBc", "lAq", "aDc", "CBE", + "lAn", "ibc", "aDE", "nBn", "ibE", "rDn", "CBB", "ibC", "aDB", "ibB", "aDq", "ibq", "ibn", + "xsf", "vkl", "tkf", "nAm", "nAl", "CAo", "aBo", "iDo", "CAl", "aBl", "kpk", "BdA", "kos", + "Bck", "kog", "seb", "Bcc", "koa", "BcE", "koD", "Bhk", "kqw", "sfj", "Bgs", "kqi", "Bgg", + "kqb", "Bga", "BgD", "Biw", "krj", "Bii", "Bib", "Bjj", "lpA", "sus", "whi", "lok", "sug", + "loc", "sua", "loE", "suD", "loC", "BFA", "kms", "sdi", "DhA", "BEk", "svi", "sdb", "Dgk", + "lqg", "svb", "Dgc", "BEE", "kmD", "DgE", "lqD", "BEB", "Bas", "kni", "Dis", "Bag", "knb", + "Dig", "lrb", "Dia", "BaD", "Bbi", "Dji", "Bbb", "Djb", "tuk", "wxg", "yir", "tuc", "wxa", + "tuE", "wxD", "tuC", "tuB", "lmk", "stg", "nqk", "lmc", "sta", "nqc", "tva", "stD", "nqE", + "lmC", "nqC", "lmB", "nqB", "BCk", "klg", "Dak", "BCc", "str", "bik", "Dac", "lna", "klD", + "bic", "nra", "BCC", "biE", "DaC", "BCB", "DaB", "BDg", "klr", "Dbg", "BDa", "bjg", "Dba", + "BDD", "bja", "DbD", "BDr", "Dbr", "bjr", "xxc", "yyq", "xxE", "yyn", "xxC", "xxB", "ttc", + "wwq", "vvc", "xxq", "wwn", "vvE", "xxn", "vvC", "ttB", "vvB", "llc", "ssq", "nnc", "llE", + "ssn", "rrc", "nnE", "ttn", "rrE", "vvn", "llB", "rrC", "nnB", "rrB", "BBc", "kkq", "DDc", + "BBE", "kkn", "bbc", "DDE", "lln", "jjc", "bbE", "nnn", "BBB", "jjE", "rrn", "DDB", "jjC", + "BBq", "DDq", "BBn", "bbq", "DDn", "jjq", "bbn", "jjn", "xwo", "yyf", "xwm", "xwl", "tso", + "wwf", "vto", "xwv", "vtm", "tsl", "vtl", "lko", "ssf", "nlo", "lkm", "rno", "nlm", "lkl", + "rnm", "nll", "rnl", "BAo", "kkf", "DBo", "lkv", "bDo", "DBm", "BAl", "jbo", "bDm", "DBl", + "jbm", "bDl", "jbl", "DBv", "jbv", "xwd", "vsu", "vst", "nku", "rlu", "rlt", "DAu", "bBu", + "jDu", "jDt", "ApA", "Aok", "keg", "Aoc", "AoE", "AoC", "Aqs", "Aqg", "Aqa", "AqD", "Ari", + "Arb", "kuk", "kuc", "sha", "kuE", "shD", "kuC", "kuB", "Amk", "kdg", "Bqk", "kvg", "kda", + "Bqc", "kva", "BqE", "kvD", "BqC", "AmB", "BqB", "Ang", "kdr", "Brg", "kvr", "Bra", "AnD", + "BrD", "Anr", "Brr", "sxc", "sxE", "sxC", "sxB", "ktc", "lvc", "sxq", "sgn", "lvE", "sxn", + "lvC", "ktB", "lvB", "Alc", "Bnc", "AlE", "kcn", "Drc", "BnE", "AlC", "DrE", "BnC", "AlB", + "DrC", "BnB", "Alq", "Bnq", "Aln", "Drq", "Bnn", "Drn", "wyo", "wym", "wyl", "swo", "txo", + "wyv", "txm", "swl", "txl", "kso", "sgf", "lto", "swv", "nvo", "ltm", "ksl", "nvm", "ltl", + "nvl", "Ako", "kcf", "Blo", "ksv", "Dno", "Blm", "Akl", "bro", "Dnm", "Bll", "brm", "Dnl", + "Akv", "Blv", "Dnv", "brv", "yze", "yzd", "wye", "xyu", "wyd", "xyt", "swe", "twu", "swd", + "vxu", "twt", "vxt", "kse", "lsu", "ksd", "ntu", "lst", "rvu", "ypk", "zew", "xdA", "yos", + "zei", "xck", "yog", "zeb", "xcc", "yoa", "xcE", "yoD", "xcC", "xhk", "yqw", "zfj", "utA", + "xgs", "yqi", "usk", "xgg", "yqb", "usc", "xga", "usE", "xgD", "usC", "uxk", "xiw", "yrj", + "ptA", "uws", "xii", "psk", "uwg", "xib", "psc", "uwa", "psE", "uwD", "psC", "pxk", "uyw", + "xjj", "ftA", "pws", "uyi", "fsk", "pwg", "uyb", "fsc", "pwa", "fsE", "pwD", "fxk", "pyw", + "uzj", "fws", "pyi", "fwg", "pyb", "fwa", "fyw", "pzj", "fyi", "fyb", "xFA", "yms", "zdi", + "xEk", "ymg", "zdb", "xEc", "yma", "xEE", "ymD", "xEC", "xEB", "uhA", "xas", "yni", "ugk", + "xag", "ynb", "ugc", "xaa", "ugE", "xaD", "ugC", "ugB", "oxA", "uis", "xbi", "owk", "uig", + "xbb", "owc", "uia", "owE", "uiD", "owC", "owB", "dxA", "oys", "uji", "dwk", "oyg", "ujb", + "dwc", "oya", "dwE", "oyD", "dwC", "dys", "ozi", "dyg", "ozb", "dya", "dyD", "dzi", "dzb", + "xCk", "ylg", "zcr", "xCc", "yla", "xCE", "ylD", "xCC", "xCB", "uak", "xDg", "ylr", "uac", + "xDa", "uaE", "xDD", "uaC", "uaB", "oik", "ubg", "xDr", "oic", "uba", "oiE", "ubD", "oiC", + "oiB", "cyk", "ojg", "ubr", "cyc", "oja", "cyE", "ojD", "cyC", "cyB", "czg", "ojr", "cza", + "czD", "czr", "xBc", "ykq", "xBE", "ykn", "xBC", "xBB", "uDc", "xBq", "uDE", "xBn", "uDC", + "uDB", "obc", "uDq", "obE", "uDn", "obC", "obB", "cjc", "obq", "cjE", "obn", "cjC", "cjB", + "cjq", "cjn", "xAo", "ykf", "xAm", "xAl", "uBo", "xAv", "uBm", "uBl", "oDo", "uBv", "oDm", + "oDl", "cbo", "oDv", "cbm", "cbl", "xAe", "xAd", "uAu", "uAt", "oBu", "oBt", "wpA", "yes", + "zFi", "wok", "yeg", "zFb", "woc", "yea", "woE", "yeD", "woC", "woB", "thA", "wqs", "yfi", + "tgk", "wqg", "yfb", "tgc", "wqa", "tgE", "wqD", "tgC", "tgB", "mxA", "tis", "wri", "mwk", + "tig", "wrb", "mwc", "tia", "mwE", "tiD", "mwC", "mwB", "FxA", "mys", "tji", "Fwk", "myg", + "tjb", "Fwc", "mya", "FwE", "myD", "FwC", "Fys", "mzi", "Fyg", "mzb", "Fya", "FyD", "Fzi", + "Fzb", "yuk", "zhg", "hjs", "yuc", "zha", "hbw", "yuE", "zhD", "hDy", "yuC", "yuB", "wmk", + "ydg", "zEr", "xqk", "wmc", "zhr", "xqc", "yva", "ydD", "xqE", "wmC", "xqC", "wmB", "xqB", + "tak", "wng", "ydr", "vik", "tac", "wna", "vic", "xra", "wnD", "viE", "taC", "viC", "taB", + "viB", "mik", "tbg", "wnr", "qyk", "mic", "tba", "qyc", "vja", "tbD", "qyE", "miC", "qyC", + "miB", "qyB", "Eyk", "mjg", "tbr", "hyk", "Eyc", "mja", "hyc", "qza", "mjD", "hyE", "EyC", + "hyC", "EyB", "Ezg", "mjr", "hzg", "Eza", "hza", "EzD", "hzD", "Ezr", "ytc", "zgq", "grw", + "ytE", "zgn", "gny", "ytC", "glz", "ytB", "wlc", "ycq", "xnc", "wlE", "ycn", "xnE", "ytn", + "xnC", "wlB", "xnB", "tDc", "wlq", "vbc", "tDE", "wln", "vbE", "xnn", "vbC", "tDB", "vbB", + "mbc", "tDq", "qjc", "mbE", "tDn", "qjE", "vbn", "qjC", "mbB", "qjB", "Ejc", "mbq", "gzc", + "EjE", "mbn", "gzE", "qjn", "gzC", "EjB", "gzB", "Ejq", "gzq", "Ejn", "gzn", "yso", "zgf", + "gfy", "ysm", "gdz", "ysl", "wko", "ycf", "xlo", "ysv", "xlm", "wkl", "xll", "tBo", "wkv", + "vDo", "tBm", "vDm", "tBl", "vDl", "mDo", "tBv", "qbo", "vDv", "qbm", "mDl", "qbl", "Ebo", + "mDv", "gjo", "Ebm", "gjm", "Ebl", "gjl", "Ebv", "gjv", "yse", "gFz", "ysd", "wke", "xku", + "wkd", "xkt", "tAu", "vBu", "tAt", "vBt", "mBu", "qDu", "mBt", "qDt", "EDu", "gbu", "EDt", + "gbt", "ysF", "wkF", "xkh", "tAh", "vAx", "mAx", "qBx", "wek", "yFg", "zCr", "wec", "yFa", + "weE", "yFD", "weC", "weB", "sqk", "wfg", "yFr", "sqc", "wfa", "sqE", "wfD", "sqC", "sqB", + "lik", "srg", "wfr", "lic", "sra", "liE", "srD", "liC", "liB", "Cyk", "ljg", "srr", "Cyc", + "lja", "CyE", "ljD", "CyC", "CyB", "Czg", "ljr", "Cza", "CzD", "Czr", "yhc", "zaq", "arw", + "yhE", "zan", "any", "yhC", "alz", "yhB", "wdc", "yEq", "wvc", "wdE", "yEn", "wvE", "yhn", + "wvC", "wdB", "wvB", "snc", "wdq", "trc", "snE", "wdn", "trE", "wvn", "trC", "snB", "trB", + "lbc", "snq", "njc", "lbE", "snn", "njE", "trn", "njC", "lbB", "njB", "Cjc", "lbq", "azc", + "CjE", "lbn", "azE", "njn", "azC", "CjB", "azB", "Cjq", "azq", "Cjn", "azn", "zio", "irs", + "rfy", "zim", "inw", "rdz", "zil", "ily", "ikz", "ygo", "zaf", "afy", "yxo", "ziv", "ivy", + "adz", "yxm", "ygl", "itz", "yxl", "wco", "yEf", "wto", "wcm", "xvo", "yxv", "wcl", "xvm", + "wtl", "xvl", "slo", "wcv", "tno", "slm", "vro", "tnm", "sll", "vrm", "tnl", "vrl", "lDo", + "slv", "nbo", "lDm", "rjo", "nbm", "lDl", "rjm", "nbl", "rjl", "Cbo", "lDv", "ajo", "Cbm", + "izo", "ajm", "Cbl", "izm", "ajl", "izl", "Cbv", "ajv", "zie", "ifw", "rFz", "zid", "idy", + "icz", "yge", "aFz", "ywu", "ygd", "ihz", "ywt", "wce", "wsu", "wcd", "xtu", "wst", "xtt", + "sku", "tlu", "skt", "vnu", "tlt", "vnt", "lBu", "nDu", "lBt", "rbu", "nDt", "rbt", "CDu", + "abu", "CDt", "iju", "abt", "ijt", "ziF", "iFy", "iEz", "ygF", "ywh", "wcF", "wsh", "xsx", + "skh", "tkx", "vlx", "lAx", "nBx", "rDx", "CBx", "aDx", "ibx", "iCz", "wFc", "yCq", "wFE", + "yCn", "wFC", "wFB", "sfc", "wFq", "sfE", "wFn", "sfC", "sfB", "krc", "sfq", "krE", "sfn", + "krC", "krB", "Bjc", "krq", "BjE", "krn", "BjC", "BjB", "Bjq", "Bjn", "yao", "zDf", "Dfy", + "yam", "Ddz", "yal", "wEo", "yCf", "who", "wEm", "whm", "wEl", "whl", "sdo", "wEv", "svo", + "sdm", "svm", "sdl", "svl", "kno", "sdv", "lro", "knm", "lrm", "knl", "lrl", "Bbo", "knv", + "Djo", "Bbm", "Djm", "Bbl", "Djl", "Bbv", "Djv", "zbe", "bfw", "npz", "zbd", "bdy", "bcz", + "yae", "DFz", "yiu", "yad", "bhz", "yit", "wEe", "wgu", "wEd", "wxu", "wgt", "wxt", "scu", + "stu", "sct", "tvu", "stt", "tvt", "klu", "lnu", "klt", "nru", "lnt", "nrt", "BDu", "Dbu", + "BDt", "bju", "Dbt", "bjt", "jfs", "rpy", "jdw", "roz", "jcy", "jcj", "zbF", "bFy", "zjh", + "jhy", "bEz", "jgz", "yaF", "yih", "yyx", "wEF", "wgh", "wwx", "xxx", "sch", "ssx", "ttx", + "vvx", "kkx", "llx", "nnx", "rrx", "BBx", "DDx", "bbx", "jFw", "rmz", "jEy", "jEj", "bCz", + "jaz", "jCy", "jCj", "jBj", "wCo", "wCm", "wCl", "sFo", "wCv", "sFm", "sFl", "kfo", "sFv", + "kfm", "kfl", "Aro", "kfv", "Arm", "Arl", "Arv", "yDe", "Bpz", "yDd", "wCe", "wau", "wCd", + "wat", "sEu", "shu", "sEt", "sht", "kdu", "kvu", "kdt", "kvt", "Anu", "Bru", "Ant", "Brt", + "zDp", "Dpy", "Doz", "yDF", "ybh", "wCF", "wah", "wix", "sEh", "sgx", "sxx", "kcx", "ktx", + "lvx", "Alx", "Bnx", "Drx", "bpw", "nuz", "boy", "boj", "Dmz", "bqz", "jps", "ruy", "jow", + "ruj", "joi", "job", "bmy", "jqy", "bmj", "jqj", "jmw", "rtj", "jmi", "jmb", "blj", "jnj", + "jli", "jlb", "jkr", "sCu", "sCt", "kFu", "kFt", "Afu", "Aft", "wDh", "sCh", "sax", "kEx", + "khx", "Adx", "Avx", "Buz", "Duy", "Duj", "buw", "nxj", "bui", "bub", "Dtj", "bvj", "jus", + "rxi", "jug", "rxb", "jua", "juD", "bti", "jvi", "btb", "jvb", "jtg", "rwr", "jta", "jtD", + "bsr", "jtr", "jsq", "jsn", "Bxj", "Dxi", "Dxb", "bxg", "nyr", "bxa", "bxD", "Dwr", "bxr", + "bwq", "bwn", "pjk", "urw", "ejA", "pbs", "uny", "ebk", "pDw", "ulz", "eDs", "pBy", "eBw", + "zfc", "fjk", "prw", "zfE", "fbs", "pny", "zfC", "fDw", "plz", "zfB", "fBy", "yrc", "zfq", + "frw", "yrE", "zfn", "fny", "yrC", "flz", "yrB", "xjc", "yrq", "xjE", "yrn", "xjC", "xjB", + "uzc", "xjq", "uzE", "xjn", "uzC", "uzB", "pzc", "uzq", "pzE", "uzn", "pzC", "djA", "ors", + "ufy", "dbk", "onw", "udz", "dDs", "oly", "dBw", "okz", "dAy", "zdo", "drs", "ovy", "zdm", + "dnw", "otz", "zdl", "dly", "dkz", "yno", "zdv", "dvy", "ynm", "dtz", "ynl", "xbo", "ynv", + "xbm", "xbl", "ujo", "xbv", "ujm", "ujl", "ozo", "ujv", "ozm", "ozl", "crk", "ofw", "uFz", + "cns", "ody", "clw", "ocz", "cky", "ckj", "zcu", "cvw", "ohz", "zct", "cty", "csz", "ylu", + "cxz", "ylt", "xDu", "xDt", "ubu", "ubt", "oju", "ojt", "cfs", "oFy", "cdw", "oEz", "ccy", + "ccj", "zch", "chy", "cgz", "ykx", "xBx", "uDx", "cFw", "oCz", "cEy", "cEj", "caz", "cCy", + "cCj", "FjA", "mrs", "tfy", "Fbk", "mnw", "tdz", "FDs", "mly", "FBw", "mkz", "FAy", "zFo", + "Frs", "mvy", "zFm", "Fnw", "mtz", "zFl", "Fly", "Fkz", "yfo", "zFv", "Fvy", "yfm", "Ftz", + "yfl", "wro", "yfv", "wrm", "wrl", "tjo", "wrv", "tjm", "tjl", "mzo", "tjv", "mzm", "mzl", + "qrk", "vfw", "xpz", "hbA", "qns", "vdy", "hDk", "qlw", "vcz", "hBs", "qky", "hAw", "qkj", + "hAi", "Erk", "mfw", "tFz", "hrk", "Ens", "mdy", "hns", "qty", "mcz", "hlw", "Eky", "hky", + "Ekj", "hkj", "zEu", "Evw", "mhz", "zhu", "zEt", "hvw", "Ety", "zht", "hty", "Esz", "hsz", + "ydu", "Exz", "yvu", "ydt", "hxz", "yvt", "wnu", "xru", "wnt", "xrt", "tbu", "vju", "tbt", + "vjt", "mju", "mjt", "grA", "qfs", "vFy", "gnk", "qdw", "vEz", "gls", "qcy", "gkw", "qcj", + "gki", "gkb", "Efs", "mFy", "gvs", "Edw", "mEz", "gtw", "qgz", "gsy", "Ecj", "gsj", "zEh", + "Ehy", "zgx", "gxy", "Egz", "gwz", "ycx", "ytx", "wlx", "xnx", "tDx", "vbx", "mbx", "gfk", + "qFw", "vCz", "gds", "qEy", "gcw", "qEj", "gci", "gcb", "EFw", "mCz", "ghw", "EEy", "ggy", + "EEj", "ggj", "Eaz", "giz", "gFs", "qCy", "gEw", "qCj", "gEi", "gEb", "ECy", "gay", "ECj", + "gaj", "gCw", "qBj", "gCi", "gCb", "EBj", "gDj", "gBi", "gBb", "Crk", "lfw", "spz", "Cns", + "ldy", "Clw", "lcz", "Cky", "Ckj", "zCu", "Cvw", "lhz", "zCt", "Cty", "Csz", "yFu", "Cxz", + "yFt", "wfu", "wft", "sru", "srt", "lju", "ljt", "arA", "nfs", "tpy", "ank", "ndw", "toz", + "als", "ncy", "akw", "ncj", "aki", "akb", "Cfs", "lFy", "avs", "Cdw", "lEz", "atw", "ngz", + "asy", "Ccj", "asj", "zCh", "Chy", "zax", "axy", "Cgz", "awz", "yEx", "yhx", "wdx", "wvx", + "snx", "trx", "lbx", "rfk", "vpw", "xuz", "inA", "rds", "voy", "ilk", "rcw", "voj", "iks", + "rci", "ikg", "rcb", "ika", "afk", "nFw", "tmz", "ivk", "ads", "nEy", "its", "rgy", "nEj", + "isw", "aci", "isi", "acb", "isb", "CFw", "lCz", "ahw", "CEy", "ixw", "agy", "CEj", "iwy", + "agj", "iwj", "Caz", "aiz", "iyz", "ifA", "rFs", "vmy", "idk", "rEw", "vmj", "ics", "rEi", + "icg", "rEb", "ica", "icD", "aFs", "nCy", "ihs", "aEw", "nCj", "igw", "raj", "igi", "aEb", + "igb", "CCy", "aay", "CCj", "iiy", "aaj", "iij", "iFk", "rCw", "vlj", "iEs", "rCi", "iEg", + "rCb", "iEa", "iED", "aCw", "nBj", "iaw", "aCi", "iai", "aCb", "iab", "CBj", "aDj", "ibj", + "iCs", "rBi", "iCg", "rBb", "iCa", "iCD", "aBi", "iDi", "aBb", "iDb", "iBg", "rAr", "iBa", + "iBD", "aAr", "iBr", "iAq", "iAn", "Bfs", "kpy", "Bdw", "koz", "Bcy", "Bcj", "Bhy", "Bgz", + "yCx", "wFx", "sfx", "krx", "Dfk", "lpw", "suz", "Dds", "loy", "Dcw", "loj", "Dci", "Dcb", + "BFw", "kmz", "Dhw", "BEy", "Dgy", "BEj", "Dgj", "Baz", "Diz", "bfA", "nps", "tuy", "bdk", + "now", "tuj", "bcs", "noi", "bcg", "nob", "bca", "bcD", "DFs", "lmy", "bhs", "DEw", "lmj", + "bgw", "DEi", "bgi", "DEb", "bgb", "BCy", "Day", "BCj", "biy", "Daj", "bij", "rpk", "vuw", + "xxj", "jdA", "ros", "vui", "jck", "rog", "vub", "jcc", "roa", "jcE", "roD", "jcC", "bFk", + "nmw", "ttj", "jhk", "bEs", "nmi", "jgs", "rqi", "nmb", "jgg", "bEa", "jga", "bED", "jgD", + "DCw", "llj", "baw", "DCi", "jiw", "bai", "DCb", "jii", "bab", "jib", "BBj", "DDj", "bbj", + "jjj", "jFA", "rms", "vti", "jEk", "rmg", "vtb", "jEc", "rma", "jEE", "rmD", "jEC", "jEB", + "bCs", "nli", "jas", "bCg", "nlb", "jag", "rnb", "jaa", "bCD", "jaD", "DBi", "bDi", "DBb", + "jbi", "bDb", "jbb", "jCk", "rlg", "vsr", "jCc", "rla", "jCE", "rlD", "jCC", "jCB", "bBg", + "nkr", "jDg", "bBa", "jDa", "bBD", "jDD", "DAr", "bBr", "jDr", "jBc", "rkq", "jBE", "rkn", + "jBC", "jBB", "bAq", "jBq", "bAn", "jBn", "jAo", "rkf", "jAm", "jAl", "bAf", "jAv", "Apw", + "kez", "Aoy", "Aoj", "Aqz", "Bps", "kuy", "Bow", "kuj", "Boi", "Bob", "Amy", "Bqy", "Amj", + "Bqj", "Dpk", "luw", "sxj", "Dos", "lui", "Dog", "lub", "Doa", "DoD", "Bmw", "ktj", "Dqw", + "Bmi", "Dqi", "Bmb", "Dqb", "Alj", "Bnj", "Drj", "bpA", "nus", "txi", "bok", "nug", "txb", + "boc", "nua", "boE", "nuD", "boC", "boB", "Dms", "lti", "bqs", "Dmg", "ltb", "bqg", "nvb", + "bqa", "DmD", "bqD", "Bli", "Dni", "Blb", "bri", "Dnb", "brb", "ruk", "vxg", "xyr", "ruc", + "vxa", "ruE", "vxD", "ruC", "ruB", "bmk", "ntg", "twr", "jqk", "bmc", "nta", "jqc", "rva", + "ntD", "jqE", "bmC", "jqC", "bmB", "jqB", "Dlg", "lsr", "bng", "Dla", "jrg", "bna", "DlD", + "jra", "bnD", "jrD", "Bkr", "Dlr", "bnr", "jrr", "rtc", "vwq", "rtE", "vwn", "rtC", "rtB", + "blc", "nsq", "jnc", "blE", "nsn", "jnE", "rtn", "jnC", "blB", "jnB", "Dkq", "blq", "Dkn", + "jnq", "bln", "jnn", "rso", "vwf", "rsm", "rsl", "bko", "nsf", "jlo", "bkm", "jlm", "bkl", + "jll", "Dkf", "bkv", "jlv", "rse", "rsd", "bke", "jku", "bkd", "jkt", "Aey", "Aej", "Auw", + "khj", "Aui", "Aub", "Adj", "Avj", "Bus", "kxi", "Bug", "kxb", "Bua", "BuD", "Ati", "Bvi", + "Atb", "Bvb", "Duk", "lxg", "syr", "Duc", "lxa", "DuE", "lxD", "DuC", "DuB", "Btg", "kwr", + "Dvg", "lxr", "Dva", "BtD", "DvD", "Asr", "Btr", "Dvr", "nxc", "tyq", "nxE", "tyn", "nxC", + "nxB", "Dtc", "lwq", "bvc", "nxq", "lwn", "bvE", "DtC", "bvC", "DtB", "bvB", "Bsq", "Dtq", + "Bsn", "bvq", "Dtn", "bvn", "vyo", "xzf", "vym", "vyl", "nwo", "tyf", "rxo", "nwm", "rxm", + "nwl", "rxl", "Dso", "lwf", "bto", "Dsm", "jvo", "btm", "Dsl", "jvm", "btl", "jvl", "Bsf", + "Dsv", "btv", "jvv", "vye", "vyd", "nwe", "rwu", "nwd", "rwt", "Dse", "bsu", "Dsd", "jtu", + "bst", "jtt", "vyF", "nwF", "rwh", "DsF", "bsh", "jsx", "Ahi", "Ahb", "Axg", "kir", "Axa", + "AxD", "Agr", "Axr", "Bxc", "kyq", "BxE", "kyn", "BxC", "BxB", "Awq", "Bxq", "Awn", "Bxn", + "lyo", "szf", "lym", "lyl", "Bwo", "kyf", "Dxo", "lyv", "Dxm", "Bwl", "Dxl", "Awf", "Bwv", + "Dxv", "tze", "tzd", "lye", "nyu", "lyd", "nyt", "Bwe", "Dwu", "Bwd", "bxu", "Dwt", "bxt", + "tzF", "lyF", "nyh", "BwF", "Dwh", "bwx", "Aiq", "Ain", "Ayo", "kjf", "Aym", "Ayl", "Aif", + "Ayv", "kze", "kzd", "Aye", "Byu", "Ayd", "Byt", "szp" + }; + private char[] brSet = { + 'A', 'B', 'C', 'D', 'E', 'F', 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', + 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z', '*', '+', '-' + }; + private String[] PDFttf = { + "00000", "00001", "00010", "00011", "00100", "00101", "00110", "00111", + "01000", "01001", "01010", "01011", "01100", "01101", "01110", "01111", "10000", "10001", + "10010", "10011", "10100", "10101", "10110", "10111", "11000", "11001", "11010", + "11011", "11100", "11101", "11110", "11111", "01", "1111111101010100", "11111101000101001" + }; + /* Left and Right Row Address Pattern from Table 2 */ + private String[] RAPLR = {"", "221311", "311311", "312211", "222211", "213211", "214111", "223111", + "313111", "322111", "412111", "421111", "331111", "241111", "232111", "231211", "321211", + "411211", "411121", "411112", "321112", "312112", "311212", "311221", "311131", "311122", + "311113", "221113", "221122", "221131", "221221", "222121", "312121", "321121", "231121", + "231112", "222112", "213112", "212212", "212221", "212131", "212122", "212113", "211213", + "211123", "211132", "211141", "211231", "211222", "211312", "211321", "211411", "212311"}; + + /* Centre Row Address Pattern from Table 2 */ + private String[] RAPC = {"", "112231", "121231", "122131", "131131", "131221", "132121", "141121", + "141211", "142111", "133111", "132211", "131311", "122311", "123211", "124111", "115111", + "114211", "114121", "123121", "123112", "122212", "122221", "121321", "121411", "112411", + "113311", "113221", "113212", "113122", "122122", "131122", "131113", "122113", "113113", + "112213", "112222", "112312", "112321", "111421", "111331", "111322", "111232", "111223", + "111133", "111124", "111214", "112114", "121114", "121123", "121132", "112132", "112141"}; + private int[] MicroVariants = {1, 1, 1, 1, 1, 1, 2, 2, 2, 2, 2, 2, 2, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, + 11, 14, 17, 20, 24, 28, 8, 11, 14, 17, 20, 23, 26, 6, 8, 10, 12, 15, 20, 26, 32, 38, 44, 4, 6, 8, 10, 12, 15, 20, 26, 32, 38, 44, + 7, 7, 7, 8, 8, 8, 8, 9, 9, 10, 11, 13, 15, 12, 14, 16, 18, 21, 26, 32, 38, 44, 50, 8, 12, 14, 16, 18, 21, 26, 32, 38, 44, 50, + 0, 0, 0, 7, 7, 7, 7, 15, 15, 24, 34, 57, 84, 45, 70, 99, 115, 133, 154, 180, 212, 250, 294, 7, 45, 70, 99, 115, 133, 154, 180, 212, 250, 294}; + /* rows, columns, error codewords, k-offset */ + /* MicroPDF417 coefficients from ISO/IEC 24728:2006 Annex F */ + private int[] Microcoeffs = { + /* k = 7 */ + 76, 925, 537, 597, 784, 691, 437, + /* k = 8 */ + 237, 308, 436, 284, 646, 653, 428, 379, + /* k = 9 */ + 567, 527, 622, 257, 289, 362, 501, 441, 205, + /* k = 10 */ + 377, 457, 64, 244, 826, 841, 818, 691, 266, 612, + /* k = 11 */ + 462, 45, 565, 708, 825, 213, 15, 68, 327, 602, 904, + /* k = 12 */ + 597, 864, 757, 201, 646, 684, 347, 127, 388, 7, 69, 851, + /* k = 13 */ + 764, 713, 342, 384, 606, 583, 322, 592, 678, 204, 184, 394, 692, + /* k = 14 */ + 669, 677, 154, 187, 241, 286, 274, 354, 478, 915, 691, 833, 105, 215, + /* k = 15 */ + 460, 829, 476, 109, 904, 664, 230, 5, 80, 74, 550, 575, 147, 868, 642, + /* k = 16 */ + 274, 562, 232, 755, 599, 524, 801, 132, 295, 116, 442, 428, 295, 42, 176, 65, + /* k = 18 */ + 279, 577, 315, 624, 37, 855, 275, 739, 120, 297, 312, 202, 560, 321, 233, 756, + 760, 573, + /* k = 21 */ + 108, 519, 781, 534, 129, 425, 681, 553, 422, 716, 763, 693, 624, 610, 310, 691, + 347, 165, 193, 259, 568, + /* k = 26 */ + 443, 284, 887, 544, 788, 93, 477, 760, 331, 608, 269, 121, 159, 830, 446, 893, + 699, 245, 441, 454, 325, 858, 131, 847, 764, 169, + /* k = 32 */ + 361, 575, 922, 525, 176, 586, 640, 321, 536, 742, 677, 742, 687, 284, 193, 517, + 273, 494, 263, 147, 593, 800, 571, 320, 803, 133, 231, 390, 685, 330, 63, 410, + /* k = 38 */ + 234, 228, 438, 848, 133, 703, 529, 721, 788, 322, 280, 159, 738, 586, 388, 684, + 445, 680, 245, 595, 614, 233, 812, 32, 284, 658, 745, 229, 95, 689, 920, 771, + 554, 289, 231, 125, 117, 518, + /* k = 44 */ + 476, 36, 659, 848, 678, 64, 764, 840, 157, 915, 470, 876, 109, 25, 632, 405, + 417, 436, 714, 60, 376, 97, 413, 706, 446, 21, 3, 773, 569, 267, 272, 213, + 31, 560, 231, 758, 103, 271, 572, 436, 339, 730, 82, 285, + /* k = 50 */ + 923, 797, 576, 875, 156, 706, 63, 81, 257, 874, 411, 416, 778, 50, 205, 303, + 188, 535, 909, 155, 637, 230, 534, 96, 575, 102, 264, 233, 919, 593, 865, 26, + 579, 623, 766, 146, 10, 739, 246, 127, 71, 244, 211, 477, 920, 876, 427, 820, + 718, 435}; + + /* following is Left RAP, Centre RAP, Right RAP and Start Cluster from ISO/IEC 24728:2006 tables 10, 11 and 12 */ + private int[] RAPTable = {1, 8, 36, 19, 9, 25, 1, 1, 8, 36, 19, 9, 27, 1, 7, 15, 25, 37, 1, 1, 21, 15, 1, 47, 1, 7, 15, 25, 37, 1, 1, 21, 15, 1, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 7, 15, 25, 37, 17, 9, 29, 31, 25, 19, 1, 7, 15, 25, 37, 17, 9, 29, 31, 25, + 9, 8, 36, 19, 17, 33, 1, 9, 8, 36, 19, 17, 35, 1, 7, 15, 25, 37, 33, 17, 37, 47, 49, 43, 1, 7, 15, 25, 37, 33, 17, 37, 47, 49, + 0, 3, 6, 0, 6, 0, 0, 0, 3, 6, 0, 6, 6, 0, 0, 6, 0, 0, 0, 0, 6, 6, 0, 3, 0, 0, 6, 0, 0, 0, 0, 6, 6, 0}; + + private StringBuilder binaryString; + private int ecc; + private LinearEncoding symbology = LinearEncoding.CODE_128; + private String generalField; + private gfMode[] generalFieldType; + + ; + private int ccWidth; + private int[][] pwr928 = new int[69][7]; + private int[] codeWords = new int[180]; + private int codeWordCount; + private int[] bitStr = new int[13]; + private int[] inputData; + private CompositeMode ccMode; + private String linearContent; + private CompositeMode userPreferredMode = CompositeMode.CC_A; + private int targetBitsize; + private int remainder; + private int linearWidth; // Width of Code 128 linear + + public Composite() { + inputDataType = DataType.GS1; + } + + @Override + public void setDataType(DataType dummy) { + // Do nothing! + } + + /** + * Set the type of linear component included in the composite symbol, + * this will determine how the lower part of the symbol is encoded. + * + * @param linearSymbology The symbology of the linear component + */ + public void setSymbology(LinearEncoding linearSymbology) { + symbology = linearSymbology; + } + + /** + * Set the data to be encoded in the linear component of the composite + * symbol. + * + * @param input The linear data in GS1 format + */ + public void setLinear(String input) { + linearContent = input; + } + + /** + * Set the preferred encoding method for the 2D component of the + * composite symbol. This value may be ignored if the amount of data + * supplied is too big for the selected encoding. Mode CC-C can only be + * used with a Code 128 linear component. + * + * @param userMode Preferred mode + */ + public void setPreferredMode(CompositeMode userMode) { + userPreferredMode = userMode; + } + + @Override + public boolean encode() { + List linearRect = new ArrayList<>(); + List linearTxt = new ArrayList<>(); + List combineRect = new ArrayList<>(); + List combineTxt = new ArrayList<>(); + StringBuilder linearEncodeInfo = null; + String linearErrorMsg = ""; + int linearHeight = 0; + int topShift = 0; + int bottomShift = 0; + int maxX = 0; + int i; + linearWidth = 0; + + if (linearContent.isEmpty()) { + errorMsg.append("No linear data set"); + return false; + } + + // Manage composite component encoding first + if (!(encodeComposite())) { + return false; + } + + // Then encode linear component + try { + switch (symbology) { + case UPCA: + Upc upca = new Upc(); + upca.setMode(Upc.Mode.UPCA); + upca.setLinkageFlag(); + upca.setContent(linearContent); + linearRect = upca.getRectangles(); + linearTxt = upca.getTexts(); + linearHeight = upca.symbolHeight; + linearEncodeInfo = upca.encodeInfo; + topShift = 3; + break; + case UPCE: + Upc upce = new Upc(); + upce.setMode(Upc.Mode.UPCE); + upce.setLinkageFlag(); + upce.setContent(linearContent); + linearRect = upce.getRectangles(); + linearTxt = upce.getTexts(); + linearHeight = upce.symbolHeight; + linearEncodeInfo = upce.encodeInfo; + topShift = 3; + break; + case EAN: + Ean ean = new Ean(); + if (eanCalculateVersion() == 8) { + ean.setMode(Ean.Mode.EAN8); + bottomShift = 8; + } else { + ean.setMode(Ean.Mode.EAN13); + topShift = 3; + } + ean.setLinkageFlag(); + ean.setContent(linearContent); + linearRect = ean.getRectangles(); + linearTxt = ean.getTexts(); + linearHeight = ean.symbolHeight; + linearEncodeInfo = ean.encodeInfo; + break; + case CODE_128: + Code128 code128 = new Code128(); + switch (ccMode) { + case CC_A: + code128.setCca(); + break; + case CC_B: + code128.setCcb(); + break; + case CC_C: + code128.setCcc(); + bottomShift = 7; + break; + } + code128.setDataType(DataType.GS1); + code128.setContent(linearContent); + linearWidth = code128.symbolWidth; + linearRect = code128.getRectangles(); + linearTxt = code128.getTexts(); + linearHeight = code128.symbolHeight; + linearEncodeInfo = code128.encodeInfo; + break; + case DATABAR_14: + DataBar14 dataBar14 = new DataBar14(); + dataBar14.setLinkageFlag(); + dataBar14.setLinearMode(); + dataBar14.setContent(linearContent); + linearRect = dataBar14.getRectangles(); + linearTxt = dataBar14.getTexts(); + linearHeight = dataBar14.symbolHeight; + linearEncodeInfo = dataBar14.encodeInfo; + bottomShift = 4; + break; + case DATABAR_14_STACK_OMNI: + DataBar14 dataBar14SO = new DataBar14(); + dataBar14SO.setLinkageFlag(); + dataBar14SO.setOmnidirectionalMode(); + dataBar14SO.setContent(linearContent); + linearRect = dataBar14SO.getRectangles(); + linearTxt = dataBar14SO.getTexts(); + linearHeight = dataBar14SO.symbolHeight; + linearEncodeInfo = dataBar14SO.encodeInfo; + topShift = 1; + break; + case DATABAR_14_STACK: + DataBar14 dataBar14S = new DataBar14(); + dataBar14S.setLinkageFlag(); + dataBar14S.setStackedMode(); + dataBar14S.setContent(linearContent); + linearRect = dataBar14S.getRectangles(); + linearTxt = dataBar14S.getTexts(); + linearHeight = dataBar14S.symbolHeight; + linearEncodeInfo = dataBar14S.encodeInfo; + topShift = 1; + break; + case DATABAR_LIMITED: + DataBarLimited dataBarLimited = new DataBarLimited(); + dataBarLimited.setLinkageFlag(); + dataBarLimited.setContent(linearContent); + linearRect = dataBarLimited.getRectangles(); + linearTxt = dataBarLimited.getTexts(); + linearHeight = dataBarLimited.symbolHeight; + linearEncodeInfo = dataBarLimited.encodeInfo; + topShift = 1; + break; + case DATABAR_EXPANDED: + DataBarExpanded dataBarExpanded = new DataBarExpanded(); + dataBarExpanded.setLinkageFlag(); + dataBarExpanded.setNotStacked(); + dataBarExpanded.setContent(linearContent); + linearRect = dataBarExpanded.getRectangles(); + linearTxt = dataBarExpanded.getTexts(); + linearHeight = dataBarExpanded.symbolHeight; + linearEncodeInfo = dataBarExpanded.encodeInfo; + topShift = 2; + break; + case DATABAR_EXPANDED_STACK: + DataBarExpanded dataBarExpandedS = new DataBarExpanded(); + dataBarExpandedS.setLinkageFlag(); + dataBarExpandedS.setStacked(); + dataBarExpandedS.setContent(linearContent); + linearRect = dataBarExpandedS.getRectangles(); + linearTxt = dataBarExpandedS.getTexts(); + linearHeight = dataBarExpandedS.symbolHeight; + linearEncodeInfo = dataBarExpandedS.encodeInfo; + topShift = 2; + break; + default: + linearErrorMsg = "Linear symbol not recognised"; + break; + } + } catch (Exception e) { + linearErrorMsg = e.getMessage(); + } + + if (!linearErrorMsg.isEmpty()) { + errorMsg.append(linearErrorMsg); + return false; + } + + if ((ccMode == CompositeMode.CC_C) && (symbology == LinearEncoding.CODE_128)) { + /* Width of composite component depends on width of linear component, + so recalculate. */ + rowCount = 0; + getRectangles().clear(); + symbolHeight = 0; + symbolWidth = 0; + encodeInfo = new StringBuilder(); + if (!(encodeComposite())) { + return false; + } + } + + if ((ccMode != CompositeMode.CC_C) && (symbology == LinearEncoding.CODE_128)) { + if (linearWidth > symbolWidth) { + topShift = (linearWidth - symbolWidth) / 2; + } + } + + for (i = 0; i < getRectangles().size(); i++) { + Rectangle2D.Double comprect = new Rectangle2D.Double(getRectangles().get(i).x + topShift, + getRectangles().get(i).y, + getRectangles().get(i).width, + getRectangles().get(i).height); + if ((getRectangles().get(i).x + topShift + getRectangles().get(i).width) > maxX) { + maxX = (int) (getRectangles().get(i).x + topShift + getRectangles().get(i).width); + } + combineRect.add(comprect); + } + + for (i = 0; i < linearRect.size(); i++) { + Rectangle2D.Double linrect = new Rectangle2D.Double(linearRect.get(i).x + bottomShift, linearRect.get(i).y, linearRect.get(i).width, linearRect.get(i).height); + linrect.y += symbolHeight; + if ((linearRect.get(i).x + bottomShift + linearRect.get(i).width) > maxX) { + maxX = (int) (linearRect.get(i).x + bottomShift + linearRect.get(i).width); + } + combineRect.add(linrect); + } + + for (i = 0; i < linearTxt.size(); i++) { + double x = linearTxt.get(i).x + bottomShift; + double y = linearTxt.get(i).y + symbolHeight; + String text = linearTxt.get(i).text; + TextBox lintxt = new TextBox(x, y, text); + combineTxt.add(lintxt); + } + getRectangles().clear(); + getRectangles().addAll(combineRect); + getTexts().clear(); + getTexts().addAll(combineTxt); + symbolHeight += linearHeight; + symbolWidth = maxX; + + encodeInfo.append(linearEncodeInfo); + + return true; + } + + private boolean encodeComposite() { + + if (content.length() > 2990) { + errorMsg.append("2D component input data too long"); + return false; + } + + ccMode = userPreferredMode; + + if ((ccMode == CompositeMode.CC_C) && (symbology != LinearEncoding.CODE_128)) { + /* CC-C can only be used with a GS1-128 linear part */ + errorMsg.append("Invalid mode (CC-C only valid with GS1-128 linear component)"); + return false; + } + + switch (symbology) { + /* Determine width of 2D component according to ISO/IEC 24723 Table 1 */ + case EAN: + if (eanCalculateVersion() == 8) { + ccWidth = 3; + } else { + ccWidth = 4; + } + break; + case UPCE: + case DATABAR_14_STACK_OMNI: + case DATABAR_14_STACK: + ccWidth = 2; + break; + case DATABAR_LIMITED: + ccWidth = 3; + break; + case CODE_128: + case DATABAR_14: + case DATABAR_EXPANDED: + case UPCA: + case DATABAR_EXPANDED_STACK: + ccWidth = 4; + break; + } + + encodeInfo.append("Composite width: ").append(Integer.toString(ccWidth)).append("\n"); + + if (ccMode == CompositeMode.CC_A && !ccBinaryString()) { + ccMode = CompositeMode.CC_B; + } + + if (ccMode == CompositeMode.CC_B) { /* If the data didn't fit into CC-A it is recalculated for CC-B */ + if (!(ccBinaryString())) { + if (symbology != LinearEncoding.CODE_128) { + errorMsg.append("Input too long"); + return false; + } else { + ccMode = CompositeMode.CC_C; + } + } + } + + if (ccMode == CompositeMode.CC_C) { + /* If the data didn't fit in CC-B (and linear + * part is GS1-128) it is recalculated for CC-C */ + if (!(ccBinaryString())) { + errorMsg.append("Input too long"); + return false; + } + } + + switch (ccMode) { /* Note that ecc_level is only relevant to CC-C */ + case CC_A: + ccA(); + encodeInfo.append("Composite type: CC-A\n"); + break; + case CC_B: + ccB(); + encodeInfo.append("Composite type: CC-B\n"); + break; + case CC_C: + ccC(); + encodeInfo.append("Composite type: CC-C\n"); + break; + } + + plotSymbol(); + return true; + } + + private int eanCalculateVersion() { + /* Determine if EAN-8 or EAN-13 is being used */ + + int length = 0; + int i; + boolean latch; + + latch = true; + for (i = 0; i < linearContent.length(); i++) { + if ((linearContent.charAt(i) >= '0') && (linearContent.charAt(i) <= '9')) { + if (latch) { + length++; + } + } else { + latch = false; + } + } + + if (length <= 7) { + // EAN-8 + return 8; + } else { + // EAN-13 + return 13; + } + } + + private boolean calculateSymbolSize() { + int i; + int binaryLength = binaryString.length(); + if (ccMode == CompositeMode.CC_A) { + /* CC-A 2D component - calculate remaining space */ + switch (ccWidth) { + case 2: + if (binaryLength > 167) { + return true; + } + targetBitsize = 167; + if (binaryLength <= 138) { + targetBitsize = 138; + } + if (binaryLength <= 118) { + targetBitsize = 118; + } + if (binaryLength <= 108) { + targetBitsize = 108; + } + if (binaryLength <= 88) { + targetBitsize = 88; + } + if (binaryLength <= 78) { + targetBitsize = 78; + } + if (binaryLength <= 59) { + targetBitsize = 59; + } + break; + case 3: + if (binaryLength > 167) { + return true; + } + targetBitsize = 167; + if (binaryLength <= 138) { + targetBitsize = 138; + } + if (binaryLength <= 118) { + targetBitsize = 118; + } + if (binaryLength <= 98) { + targetBitsize = 98; + } + if (binaryLength <= 78) { + targetBitsize = 78; + } + break; + case 4: + if (binaryLength > 197) { + return true; + } + targetBitsize = 197; + if (binaryLength <= 167) { + targetBitsize = 167; + } + if (binaryLength <= 138) { + targetBitsize = 138; + } + if (binaryLength <= 108) { + targetBitsize = 108; + } + if (binaryLength <= 78) { + targetBitsize = 78; + } + break; + } + } + + if (ccMode == CompositeMode.CC_B) { + /* CC-B 2D component - calculated from ISO/IEC 24728 Table 1 */ + switch (ccWidth) { + case 2: + if (binaryLength > 336) { + return true; + } + targetBitsize = 336; + if (binaryLength <= 296) { + targetBitsize = 296; + } + if (binaryLength <= 256) { + targetBitsize = 256; + } + if (binaryLength <= 208) { + targetBitsize = 208; + } + if (binaryLength <= 160) { + targetBitsize = 160; + } + if (binaryLength <= 104) { + targetBitsize = 104; + } + if (binaryLength <= 56) { + targetBitsize = 56; + } + break; + case 3: + if (binaryLength > 768) { + return true; + } + targetBitsize = 768; + if (binaryLength <= 648) { + targetBitsize = 648; + } + if (binaryLength <= 536) { + targetBitsize = 536; + } + if (binaryLength <= 416) { + targetBitsize = 416; + } + if (binaryLength <= 304) { + targetBitsize = 304; + } + if (binaryLength <= 208) { + targetBitsize = 208; + } + if (binaryLength <= 152) { + targetBitsize = 152; + } + if (binaryLength <= 112) { + targetBitsize = 112; + } + if (binaryLength <= 72) { + targetBitsize = 72; + } + if (binaryLength <= 32) { + targetBitsize = 32; + } + break; + case 4: + if (binaryLength > 1184) { + return true; + } + targetBitsize = 1184; + if (binaryLength <= 1016) { + targetBitsize = 1016; + } + if (binaryLength <= 840) { + targetBitsize = 840; + } + if (binaryLength <= 672) { + targetBitsize = 672; + } + if (binaryLength <= 496) { + targetBitsize = 496; + } + if (binaryLength <= 352) { + targetBitsize = 352; + } + if (binaryLength <= 264) { + targetBitsize = 264; + } + if (binaryLength <= 208) { + targetBitsize = 208; + } + if (binaryLength <= 152) { + targetBitsize = 152; + } + if (binaryLength <= 96) { + targetBitsize = 96; + } + if (binaryLength <= 56) { + targetBitsize = 56; + } + break; + } + } + + if (ccMode == CompositeMode.CC_C) { + /* CC-C 2D Component is a bit more complex! */ + int byteLength, codewordsUsed, eccLevel, eccCodewords, rows; + int codewordsTotal, targetCodewords, targetBytesize; + + byteLength = binaryLength / 8; + if (binaryLength % 8 != 0) { + byteLength++; + } + + codewordsUsed = (byteLength / 6) * 5; + codewordsUsed += byteLength % 6; + + eccLevel = 7; + if (codewordsUsed <= 1280) { + eccLevel = 6; + } + if (codewordsUsed <= 640) { + eccLevel = 5; + } + if (codewordsUsed <= 320) { + eccLevel = 4; + } + if (codewordsUsed <= 160) { + eccLevel = 3; + } + if (codewordsUsed <= 40) { + eccLevel = 2; + } + ecc = eccLevel; + eccCodewords = 1; + for (i = 1; i <= (eccLevel + 1); i++) { + eccCodewords *= 2; + } + + codewordsUsed += eccCodewords; + codewordsUsed += 3; + + if (linearWidth == 0) { + /* Linear component not yet calculated */ + ccWidth = (int) (0.5 + Math.sqrt((codewordsUsed) / 3.0)); + } else { + ccWidth = (linearWidth - 53) / 17; + } + + if ((codewordsUsed / ccWidth) > 90) { + /* stop the symbol from becoming too high */ + ccWidth = ccWidth + 1; + } + + rows = codewordsUsed / ccWidth; + if (codewordsUsed % ccWidth != 0) { + rows++; + } + + while (ccWidth > (3 * rows)) { + /* stop the symbol from becoming too wide (section 10) */ + ccWidth--; + + rows = codewordsUsed / ccWidth; + if (codewordsUsed % ccWidth != 0) { + rows++; + } + } + ; + + codewordsTotal = ccWidth * rows; + + targetCodewords = codewordsTotal - eccCodewords; + targetCodewords -= 3; + + targetBytesize = 6 * (targetCodewords / 5); + targetBytesize += targetCodewords % 5; + + targetBitsize = 8 * targetBytesize; + } + + remainder = targetBitsize - binaryLength; + return false; + } + + private boolean ccBinaryString() { + /* Handles all data encoding from section 5 of ISO/IEC 24723 */ + int encodingMethod, readPosn, d1, d2, value, alphaPad; + int i, j, aiCrop, fnc1Latch; + int groupVal; + int ai90Mode; + boolean latch; + int alpha, alphanum, numeric, test1, test2, test3, nextAiPosn; + int numericValue, table3Letter; + String numericPart; + String ninety; + int latchOffset; + + encodingMethod = 1; + readPosn = 0; + aiCrop = 0; + fnc1Latch = 0; + alphaPad = 0; + ai90Mode = 0; + ecc = 0; + value = 0; + targetBitsize = 0; + + if ((content.charAt(0) == '1') && ((content.charAt(1) == '0') || (content.charAt(1) == '1') || (content.charAt(1) == '7')) && (content.length() >= 8)) { + /* Source starts (10), (11) or (17) */ + encodingMethod = 2; + } + + if ((content.charAt(0) == '9') && (content.charAt(1) == '0')) { + /* Source starts (90) */ + encodingMethod = 3; + } + + encodeInfo.append("Composite Encodation: "); + switch (encodingMethod) { + case 1: + encodeInfo.append("0\n"); + break; + case 2: + encodeInfo.append("10\n"); + break; + case 3: + encodeInfo.append("11\n"); + break; + } + + binaryString = new StringBuilder(); + + if (encodingMethod == 1) { + binaryString.append("0"); + } + + if (encodingMethod == 2) { + /* Encoding Method field "10" - date and lot number */ + + binaryString.append("10"); + + if (content.charAt(1) == '0') { + /* No date data */ + binaryString.append("11"); + readPosn = 2; + } else { + /* Production Date (11) or Expiration Date (17) */ + groupVal = ((10 * (content.charAt(2) - '0')) + (content.charAt(3) - '0')) * 384; + groupVal += (((10 * (content.charAt(4) - '0')) + (content.charAt(5) - '0')) - 1) * 32; + groupVal += (10 * (content.charAt(6) - '0')) + (content.charAt(7) - '0'); + + for (j = 0; j < 16; j++) { + if ((groupVal & (0x8000 >> j)) == 0) { + binaryString.append("0"); + } else { + binaryString.append("1"); + } + } + + if (content.charAt(1) == '1') { + /* Production Date AI 11 */ + binaryString.append("0"); + } else { + /* Expiration Date AI 17 */ + binaryString.append("1"); + } + readPosn = 8; + } + + if ((readPosn + 2) < content.length()) { + if ((content.charAt(readPosn) == '1') && (content.charAt(readPosn + 1) == '0')) { + /* Followed by AI 10 - strip this from general field */ + readPosn += 2; + } else { + /* An FNC1 character needs to be inserted in the general field */ + fnc1Latch = 1; + } + } else { + fnc1Latch = 1; + } + } + + if (encodingMethod == 3) { + /* Encodation Method field of "11" - AI 90 */ + /* "This encodation method may be used if an element string with an AI + 90 occurs at the start of the data message, and if the data field + following the two-digit AI 90 starts with an alphanumeric string which + complies with a specific format." (para 5.2.2) */ + + j = content.length(); + for (i = content.length(); i > 2; i--) { + if (content.charAt(i - 1) == '[') { + j = i; + } + } + + ninety = content.substring(2, j - 1); + + + /* Find out if the AI 90 data is alphabetic or numeric or both */ + + alpha = 0; + alphanum = 0; + numeric = 0; + + for (i = 0; i < ninety.length(); i++) { + + if ((ninety.charAt(i) >= 'A') && (ninety.charAt(i) <= 'Z')) { + /* Character is alphabetic */ + alpha += 1; + } + + if ((ninety.charAt(i) >= '0') && (ninety.charAt(i) <= '9')) { + /* Character is numeric */ + numeric += 1; + } + + switch (ninety.charAt(i)) { + case '*': + case ',': + case '-': + case '.': + case '/': + alphanum += 1; + break; + } + + if (!(((ninety.charAt(i) >= '0') && (ninety.charAt(i) <= '9')) || ((ninety.charAt(i) >= 'A') && (ninety.charAt(i) <= 'Z')))) { + if ((ninety.charAt(i) != '*') && (ninety.charAt(i) != ',') && (ninety.charAt(i) != '-') && (ninety.charAt(i) != '.') && (ninety.charAt(i) != '/')) { + /* An Invalid AI 90 character */ + errorMsg.append("Invalid AI 90 data"); + return false; + } + } + } + + /* must start with 0, 1, 2 or 3 digits followed by an uppercase character */ + test1 = -1; + for (i = 3; i >= 0; i--) { + if ((ninety.charAt(i) >= 'A') && (ninety.charAt(i) <= 'Z')) { + test1 = i; + } + } + + test2 = 0; + for (i = 0; i < test1; i++) { + if (!((ninety.charAt(i) >= '0') && (ninety.charAt(i) <= '9'))) { + test2 = 1; + } + } + + /* leading zeros are not permitted */ + test3 = 0; + if ((test1 >= 1) && (ninety.charAt(0) == '0')) { + test3 = 1; + } + + if ((test1 != -1) && (test2 != 1) && (test3 == 0)) { + /* Encodation method "11" can be used */ + binaryString.append("11"); + + numeric -= test1; + alpha--; + + /* Decide on numeric, alpha or alphanumeric mode */ + /* Alpha mode is a special mode for AI 90 */ + + if (alphanum > 0) { + /* Alphanumeric mode */ + binaryString.append("0"); + ai90Mode = 1; + } else { + if (alpha > numeric) { + /* Alphabetic mode */ + binaryString.append("11"); + ai90Mode = 2; + } else { + /* Numeric mode */ + binaryString.append("10"); + ai90Mode = 3; + } + } + + nextAiPosn = 2 + ninety.length(); + + if (content.charAt(nextAiPosn) == '[') { + /* There are more AIs afterwords */ + if ((content.charAt(nextAiPosn + 1) == '2') && (content.charAt(nextAiPosn + 2) == '1')) { + /* AI 21 follows */ + aiCrop = 1; + } + + if ((content.charAt(nextAiPosn + 1) == '8') && (content.charAt(nextAiPosn + 2) == '0') && (content.charAt(nextAiPosn + 3) == '0') && (content.charAt(nextAiPosn + 4) == '4')) { + /* AI 8004 follows */ + aiCrop = 2; + } + } + + switch (aiCrop) { + case 0: + binaryString.append("0"); + break; + case 1: + binaryString.append("10"); + break; + case 2: + binaryString.append("11"); + break; + } + + if (test1 == 0) { + numericPart = "0"; + } else { + numericPart = ninety.substring(0, test1); + } + + numericValue = 0; + for (i = 0; i < numericPart.length(); i++) { + numericValue *= 10; + numericValue += numericPart.charAt(i) - '0'; + } + + table3Letter = -1; + if (numericValue < 31) { + switch (ninety.charAt(test1)) { + case 'B': + table3Letter = 0; + break; + case 'D': + table3Letter = 1; + break; + case 'H': + table3Letter = 2; + break; + case 'I': + table3Letter = 3; + break; + case 'J': + table3Letter = 4; + break; + case 'K': + table3Letter = 5; + break; + case 'L': + table3Letter = 6; + break; + case 'N': + table3Letter = 7; + break; + case 'P': + table3Letter = 8; + break; + case 'Q': + table3Letter = 9; + break; + case 'R': + table3Letter = 10; + break; + case 'S': + table3Letter = 11; + break; + case 'T': + table3Letter = 12; + break; + case 'V': + table3Letter = 13; + break; + case 'W': + table3Letter = 14; + break; + case 'Z': + table3Letter = 15; + break; + } + } + + if (table3Letter != -1) { + /* Encoding can be done according to 5.2.2 c) 2) */ + /* five bit binary string representing value before letter */ + for (j = 0; j < 5; j++) { + if ((numericValue & (0x10 >> j)) == 0x00) { + binaryString.append("0"); + } else { + binaryString.append("1"); + } + } + + /* followed by four bit representation of letter from Table 3 */ + for (j = 0; j < 4; j++) { + if ((table3Letter & (0x08 >> j)) == 0x00) { + binaryString.append("0"); + } else { + binaryString.append("1"); + } + } + } else { + /* Encoding is done according to 5.2.2 c) 3) */ + binaryString.append("11111"); + /* ten bit representation of number */ + for (j = 0; j < 10; j++) { + if ((numericValue & (0x200 >> j)) == 0x00) { + binaryString.append("0"); + } else { + binaryString.append("1"); + } + } + + /* five bit representation of ASCII character */ + for (j = 0; j < 5; j++) { + if (((ninety.charAt(test1) - 65) & (0x10 >> j)) == 0x00) { + binaryString.append("0"); + } else { + binaryString.append("1"); + } + } + } + + readPosn = test1 + 3; + } else { + /* Use general field encodation instead */ + binaryString.append("0"); + readPosn = 0; + } + + + /* Now encode the rest of the AI 90 data field */ + if (ai90Mode == 2) { + /* Alpha encodation (section 5.2.3) */ + do { + if ((content.charAt(readPosn) >= '0') && (content.charAt(readPosn) <= '9')) { + for (j = 0; j < 5; j++) { + if (((content.charAt(readPosn) + 4) & (0x10 >> j)) == 0x00) { + binaryString.append("0"); + } else { + binaryString.append("1"); + } + } + } + + if ((content.charAt(readPosn) >= 'A') && (content.charAt(readPosn) <= 'Z')) { + for (j = 0; j < 6; j++) { + if (((content.charAt(readPosn) - 65) & (0x20 >> j)) == 0x00) { + binaryString.append("0"); + } else { + binaryString.append("1"); + } + } + } + + if (content.charAt(readPosn) == '[') { + binaryString.append("11111"); + } + + readPosn++; + } while ((content.charAt(readPosn - 1) != '[') && (readPosn < content.length())); + alphaPad = 1; /* This is overwritten if a general field is encoded */ + } + + if (ai90Mode == 1) { + /* Alphanumeric mode */ + do { + if ((content.charAt(readPosn) >= '0') && (content.charAt(readPosn) <= '9')) { + for (j = 0; j < 5; j++) { + if (((content.charAt(readPosn) - 43) & (0x10 >> j)) == 0x00) { + binaryString.append("0"); + } else { + binaryString.append("1"); + } + } + } + + if ((content.charAt(readPosn) >= 'A') && (content.charAt(readPosn) <= 'Z')) { + for (j = 0; j < 6; j++) { + if (((content.charAt(readPosn) - 33) & (0x20 >> j)) == 0x00) { + binaryString.append("0"); + } else { + binaryString.append("1"); + } + } + } + + switch (content.charAt(readPosn)) { + case '[': + binaryString.append("01111"); + break; + case '*': + binaryString.append("111010"); + break; + case ',': + binaryString.append("111011"); + break; + case '-': + binaryString.append("111100"); + break; + case '.': + binaryString.append("111101"); + break; + case '/': + binaryString.append("111110"); + break; + } + + readPosn++; + } while ((content.charAt(readPosn - 1) != '[') && (content.charAt(readPosn - 1) != '\0')); + } + + readPosn += (2 * aiCrop); + } + + + /* The compressed data field has been processed if appropriate - the + rest of the data (if any) goes into a general-purpose data compaction field */ + + j = 0; + generalField = ""; + if (fnc1Latch == 1) { + /* Encodation method "10" has been used but it is not followed by + AI 10, so a FNC1 character needs to be added */ + generalField += "["; + } + + generalField += content.substring(readPosn); + + + latch = false; + if (generalField.length() != 0) { + alphaPad = 0; + + + generalFieldType = new gfMode[generalField.length()]; + + for (i = 0; i < generalField.length(); i++) { + /* Table 13 - ISO/IEC 646 encodation */ + if ((generalField.charAt(i) < ' ') || (generalField.charAt(i) > 'z')) { + generalFieldType[i] = gfMode.INVALID_CHAR; + latch = true; + } else { + generalFieldType[i] = gfMode.ISOIEC; + } + + if (generalField.charAt(i) == '#') { + generalFieldType[i] = gfMode.INVALID_CHAR; + latch = true; + } + if (generalField.charAt(i) == '$') { + generalFieldType[i] = gfMode.INVALID_CHAR; + latch = true; + } + if (generalField.charAt(i) == '@') { + generalFieldType[i] = gfMode.INVALID_CHAR; + latch = true; + } + if (generalField.charAt(i) == 92) { + generalFieldType[i] = gfMode.INVALID_CHAR; + latch = true; + } + if (generalField.charAt(i) == '^') { + generalFieldType[i] = gfMode.INVALID_CHAR; + latch = true; + } + if (generalField.charAt(i) == 96) { + generalFieldType[i] = gfMode.INVALID_CHAR; + latch = true; + } + + /* Table 12 - Alphanumeric encodation */ + if ((generalField.charAt(i) >= 'A') && (generalField.charAt(i) <= 'Z')) { + generalFieldType[i] = gfMode.ALPHA_OR_ISO; + } + if (generalField.charAt(i) == '*') { + generalFieldType[i] = gfMode.ALPHA_OR_ISO; + } + if (generalField.charAt(i) == ',') { + generalFieldType[i] = gfMode.ALPHA_OR_ISO; + } + if (generalField.charAt(i) == '-') { + generalFieldType[i] = gfMode.ALPHA_OR_ISO; + } + if (generalField.charAt(i) == '.') { + generalFieldType[i] = gfMode.ALPHA_OR_ISO; + } + if (generalField.charAt(i) == '/') { + generalFieldType[i] = gfMode.ALPHA_OR_ISO; + } + + /* Numeric encodation */ + if ((generalField.charAt(i) >= '0') && (generalField.charAt(i) <= '9')) { + generalFieldType[i] = gfMode.ANY_ENC; + } + if (generalField.charAt(i) == '[') { + /* FNC1 can be encoded in any system */ + generalFieldType[i] = gfMode.ANY_ENC; + } + + } + + if (latch) { + /* Invalid characters in input data */ + errorMsg.append("Invalid characters in input data"); + return false; + } + + for (i = 0; i < generalField.length() - 1; i++) { + if ((generalFieldType[i] == gfMode.ISOIEC) && (generalField.charAt(i + 1) == '[')) { + generalFieldType[i + 1] = gfMode.ISOIEC; + } + } + + for (i = 0; i < generalField.length() - 1; i++) { + if ((generalFieldType[i] == gfMode.ALPHA_OR_ISO) && (generalField.charAt(i + 1) == '[')) { + generalFieldType[i + 1] = gfMode.ALPHA_OR_ISO; + } + } + + latch = applyGeneralFieldRules(); + + i = 0; + do { + switch (generalFieldType[i]) { + case NUMERIC: + if (i != 0) { + if ((generalFieldType[i - 1] != gfMode.NUMERIC) && (generalField.charAt(i - 1) != '[')) { + binaryString.append("000"); /* Numeric latch */ + } + } + + if (generalField.charAt(i) != '[') { + d1 = generalField.charAt(i) - '0'; + } else { + d1 = 10; + } + + if (i < generalField.length() - 1) { + if (generalField.charAt(i + 1) != '[') { + d2 = generalField.charAt(i + 1) - '0'; + } else { + d2 = 10; + } + } else { + d2 = 10; + } + + if ((d1 != 10) || (d2 != 10)) { + /* If (d1==10)&&(d2==10) then input is either FNC1,FNC1 or FNC1,EOL */ + value = (11 * d1) + d2 + 8; + + for (j = 0; j < 7; j++) { + if ((value & 0x40 >> j) == 0x00) { + binaryString.append("0"); + } else { + binaryString.append("1"); + } + } + + i += 2; + } + break; + + case ALPHA: + if (i != 0) { + if ((generalFieldType[i - 1] == gfMode.NUMERIC) || (generalField.charAt(i - 1) == '[')) { + binaryString.append("0000"); /* Alphanumeric latch */ + } + if (generalFieldType[i - 1] == gfMode.ISOIEC) { + binaryString.append("00100"); /* ISO/IEC 646 latch */ + } + } + + if ((generalField.charAt(i) >= '0') && (generalField.charAt(i) <= '9')) { + + value = generalField.charAt(i) - 43; + + for (j = 0; j < 5; j++) { + if ((value & (0x10 >> j)) == 0x00) { + binaryString.append("0"); + } else { + binaryString.append("1"); + } + } + } + + if ((generalField.charAt(i) >= 'A') && (generalField.charAt(i) <= 'Z')) { + + value = generalField.charAt(i) - 33; + + for (j = 0; j < 6; j++) { + if ((value & (0x20 >> j)) == 0x00) { + binaryString.append("0"); + } else { + binaryString.append("1"); + } + } + } + + if (generalField.charAt(i) == '[') { + binaryString.append("01111"); /* FNC1/Numeric latch */ + } + if (generalField.charAt(i) == '*') { + binaryString.append("111010"); /* asterisk */ + } + if (generalField.charAt(i) == ',') { + binaryString.append("111011"); /* comma */ + } + if (generalField.charAt(i) == '-') { + binaryString.append("111100"); /* minus or hyphen */ + } + if (generalField.charAt(i) == '.') { + binaryString.append("111101"); /* period or full stop */ + } + if (generalField.charAt(i) == '/') { + binaryString.append("111110"); /* slash or solidus */ + } + + i++; + break; + + case ISOIEC: + if (i != 0) { + if ((generalFieldType[i - 1] == gfMode.NUMERIC) || (generalField.charAt(i - 1) == '[')) { + binaryString.append("0000"); /* Alphanumeric latch */ + binaryString.append("00100"); /* ISO/IEC 646 latch */ + } + if (generalFieldType[i - 1] == gfMode.ALPHA) { + binaryString.append("00100"); /* ISO/IEC 646 latch */ + } + } + + if ((generalField.charAt(i) >= '0') && (generalField.charAt(i) <= '9')) { + + value = generalField.charAt(i) - 43; + + for (j = 0; j < 5; j++) { + if ((value & (0x10 >> j)) == 0x00) { + binaryString.append("0"); + } else { + binaryString.append("1"); + } + } + } + + if ((generalField.charAt(i) >= 'A') && (generalField.charAt(i) <= 'Z')) { + + value = generalField.charAt(i) - 1; + + for (j = 0; j < 7; j++) { + if ((value & (0x40 >> j)) == 0x00) { + binaryString.append("0"); + } else { + binaryString.append("1"); + } + } + } + + if ((generalField.charAt(i) >= 'a') && (generalField.charAt(i) <= 'z')) { + + value = generalField.charAt(i) - 7; + + for (j = 0; j < 7; j++) { + if ((value & (0x40 >> j)) == 0x00) { + binaryString.append("0"); + } else { + binaryString.append("1"); + } + } + } + + if (generalField.charAt(i) == '[') { + binaryString.append("01111"); /* FNC1/Numeric latch */ + } + if (generalField.charAt(i) == '!') { + binaryString.append("11101000"); /* exclamation mark */ + } + if (generalField.charAt(i) == 34) { + binaryString.append("11101001"); /* quotation mark */ + } + if (generalField.charAt(i) == 37) { + binaryString.append("11101010"); /* percent sign */ + } + if (generalField.charAt(i) == '&') { + binaryString.append("11101011"); /* ampersand */ + } + if (generalField.charAt(i) == 39) { + binaryString.append("11101100"); /* apostrophe */ + } + if (generalField.charAt(i) == '(') { + binaryString.append("11101101"); /* left parenthesis */ + } + if (generalField.charAt(i) == ')') { + binaryString.append("11101110"); /* right parenthesis */ + } + if (generalField.charAt(i) == '*') { + binaryString.append("11101111"); /* asterisk */ + } + if (generalField.charAt(i) == '+') { + binaryString.append("11110000"); /* plus sign */ + } + if (generalField.charAt(i) == ',') { + binaryString.append("11110001"); /* comma */ + } + if (generalField.charAt(i) == '-') { + binaryString.append("11110010"); /* minus or hyphen */ + } + if (generalField.charAt(i) == '.') { + binaryString.append("11110011"); /* period or full stop */ + } + if (generalField.charAt(i) == '/') { + binaryString.append("11110100"); /* slash or solidus */ + } + if (generalField.charAt(i) == ':') { + binaryString.append("11110101"); /* colon */ + } + if (generalField.charAt(i) == ';') { + binaryString.append("11110110"); /* semicolon */ + } + if (generalField.charAt(i) == '<') { + binaryString.append("11110111"); /* less-than sign */ + } + if (generalField.charAt(i) == '=') { + binaryString.append("11111000"); /* equals sign */ + } + if (generalField.charAt(i) == '>') { + binaryString.append("11111001"); /* greater-than sign */ + } + if (generalField.charAt(i) == '?') { + binaryString.append("11111010"); /* question mark */ + } + if (generalField.charAt(i) == '_') { + binaryString.append("11111011"); /* underline or low line */ + } + if (generalField.charAt(i) == ' ') { + binaryString.append("11111100"); /* space */ + } + i++; + break; + } + + + latchOffset = 0; + if (latch) { + latchOffset = 1; + } + } while ((i + latchOffset) < generalField.length()); + } + + if (calculateSymbolSize()) { + return false; + } + + if (latch) { + i = generalField.length() - 1; + /* There is still one more numeric digit to encode */ + + if (generalField.charAt(i) == '[') { + binaryString.append("000001111"); + } else { + if ((remainder >= 4) && (remainder <= 6)) { + d1 = generalField.charAt(i) - '0'; + for (j = 0; j < 4; j++) { + if ((value & (0x08 >> j)) == 0x00) { + binaryString.append("0"); + } else { + binaryString.append("1"); + } + } + } else { + d1 = generalField.charAt(i) - '0'; + d2 = 10; + + value = (11 * d1) + d2 + 8; + + for (j = 0; j < 7; j++) { + if ((value & (0x40 >> j)) == 0x00) { + binaryString.append("0"); + } else { + binaryString.append("1"); + } + } + /* This may push the symbol up to the next size */ + } + } + } + + if (binaryString.length() > 11805) { /* (2361 * 5) */ + errorMsg.append("Input too long"); + return false; + } + + /* size of the symbol may have changed when adding data in the above sequence */ + if (calculateSymbolSize()) { + return false; + } + + encodeInfo.append("Composite Binary Length: ").append(Integer.toString(binaryString.length())).append("\n"); + displayBinaryString(); + + if (binaryString.length() < targetBitsize) { + /* Now add padding to binary string */ + if (alphaPad == 1) { + binaryString.append("11111"); + /* Extra FNC1 character required after Alpha encodation (section 5.2.3) */ + } + + if ((generalField.length() != 0) && (generalFieldType[generalField.length() - 1] == gfMode.NUMERIC)) { + binaryString.append("0000"); + } + + while (binaryString.length() < targetBitsize) { + binaryString.append("00100"); + } + + binaryString = new StringBuilder(binaryString.substring(0, targetBitsize)); + } + + return true; + } + + private void displayBinaryString() { + int i, nibble; + /* Display binary string as hexadecimal */ + + encodeInfo.append("Composite Binary String: "); + nibble = 0; + for (i = 0; i < binaryString.length(); i++) { + switch (i % 4) { + case 0: + if (binaryString.charAt(i) == '1') { + nibble += 8; + } + break; + case 1: + if (binaryString.charAt(i) == '1') { + nibble += 4; + } + break; + case 2: + if (binaryString.charAt(i) == '1') { + nibble += 2; + } + break; + case 3: + if (binaryString.charAt(i) == '1') { + nibble += 1; + } + encodeInfo.append(Integer.toHexString(nibble)); + nibble = 0; + break; + } + } + + if ((binaryString.length() % 4) != 0) { + encodeInfo.append(Integer.toHexString(nibble)); + } + encodeInfo.append("\n"); + } + + private boolean applyGeneralFieldRules() { + /* Attempts to apply encoding rules from secions 7.2.5.5.1 to 7.2.5.5.3 + of ISO/IEC 24724:2006 */ + + int blockCount, i, j, k; + gfMode current, next, last; + int[] blockLength = new int[200]; + gfMode[] blockType = new gfMode[200]; + + blockCount = 0; + + blockLength[blockCount] = 1; + blockType[blockCount] = generalFieldType[0]; + + for (i = 1; i < generalField.length(); i++) { + current = generalFieldType[i]; + last = generalFieldType[i - 1]; + + if (current == last) { + blockLength[blockCount] = blockLength[blockCount] + 1; + } else { + blockCount++; + blockLength[blockCount] = 1; + blockType[blockCount] = generalFieldType[i]; + } + } + + blockCount++; + + for (i = 0; i < blockCount; i++) { + current = blockType[i]; + next = blockType[i + 1]; + + if ((current == gfMode.ISOIEC) && (i != (blockCount - 1))) { + if ((next == gfMode.ANY_ENC) && (blockLength[i + 1] >= 4)) { + blockType[i + 1] = gfMode.NUMERIC; + } + if ((next == gfMode.ANY_ENC) && (blockLength[i + 1] < 4)) { + blockType[i + 1] = gfMode.ISOIEC; + } + if ((next == gfMode.ALPHA_OR_ISO) && (blockLength[i + 1] >= 5)) { + blockType[i + 1] = gfMode.ALPHA; + } + if ((next == gfMode.ALPHA_OR_ISO) && (blockLength[i + 1] < 5)) { + blockType[i + 1] = gfMode.ISOIEC; + } + } + + if (current == gfMode.ALPHA_OR_ISO) { + blockType[i] = gfMode.ALPHA; + } + + if ((current == gfMode.ALPHA) && (i != (blockCount - 1))) { + if ((next == gfMode.ANY_ENC) && (blockLength[i + 1] >= 6)) { + blockType[i + 1] = gfMode.NUMERIC; + } + if ((next == gfMode.ANY_ENC) && (blockLength[i + 1] < 6)) { + if ((i == blockCount - 2) && (blockLength[i + 1] >= 4)) { + blockType[i + 1] = gfMode.NUMERIC; + } else { + blockType[i + 1] = gfMode.ALPHA; + } + } + } + + if (current == gfMode.ANY_ENC) { + blockType[i] = gfMode.NUMERIC; + } + } + + if (blockCount > 1) { + i = 1; + while (i < blockCount) { + if (blockType[i - 1] == blockType[i]) { + /* bring together */ + blockLength[i - 1] = blockLength[i - 1] + blockLength[i]; + j = i + 1; + + /* decreace the list */ + while (j < blockCount) { + blockLength[j - 1] = blockLength[j]; + blockType[j - 1] = blockType[j]; + j++; + } + blockCount--; + i--; + } + i++; + } + } + + for (i = 0; i < blockCount - 1; i++) { + if ((blockType[i] == gfMode.NUMERIC) && ((blockLength[i] & 1) != 0)) { + /* Odd size numeric block */ + blockLength[i] = blockLength[i] - 1; + blockLength[i + 1] = blockLength[i + 1] + 1; + } + } + + j = 0; + for (i = 0; i < blockCount; i++) { + for (k = 0; k < blockLength[i]; k++) { + generalFieldType[j] = blockType[i]; + j++; + } + } + + /* If the last block is numeric and an odd size, further + processing needs to be done outside this procedure */ + return (blockType[blockCount - 1] == gfMode.NUMERIC) && ((blockLength[blockCount - 1] & 1) != 0); + } + + private void ccA() { + /* CC-A 2D component */ + int i, strpos, segment, cwCnt, variant, rows; + int k, offset, j, total; + int[] rsCodeWords = new int[8]; + int LeftRAPStart, RightRAPStart, CentreRAPStart, StartCluster; + int LeftRAP, RightRAP, CentreRAP, Cluster; + int[] dummy = new int[5]; + int flip, loop; + String codebarre; + StringBuilder bin; + StringBuilder localSource; /* A copy of source but with padding zeroes to make 208 bits */ + + variant = 0; + + for (i = 0; i < 13; i++) { + bitStr[i] = 0; + } + for (i = 0; i < 28; i++) { + codeWords[i] = 0; + } + + localSource = binaryString; + for (i = binaryString.length(); i < 208; i++) { + localSource.append("0"); + } + + for (segment = 0; segment < 13; segment++) { + strpos = segment * 16; + bitStr[segment] = 0; + for (i = 0; i < 16; i++) { + if (localSource.charAt(strpos + i) == '1') { + bitStr[segment] += 0x8000 >> i; + } + } + } + + init928(); + /* encode codeWords from bitStr */ + cwCnt = encode928(binaryString.length()); + + switch (ccWidth) { + case 2: + switch (cwCnt) { + case 6: + variant = 0; + break; + case 8: + variant = 1; + break; + case 9: + variant = 2; + break; + case 11: + variant = 3; + break; + case 12: + variant = 4; + break; + case 14: + variant = 5; + break; + case 17: + variant = 6; + break; + } + break; + case 3: + switch (cwCnt) { + case 8: + variant = 7; + break; + case 10: + variant = 8; + break; + case 12: + variant = 9; + break; + case 14: + variant = 10; + break; + case 17: + variant = 11; + break; + } + break; + case 4: + switch (cwCnt) { + case 8: + variant = 12; + break; + case 11: + variant = 13; + break; + case 14: + variant = 14; + break; + case 17: + variant = 15; + break; + case 20: + variant = 16; + break; + } + break; + } + + rows = ccaVariants[variant]; + k = ccaVariants[17 + variant]; + offset = ccaVariants[34 + variant]; + + /* Reed-Solomon error correction */ + + for (i = 0; i < 8; i++) { + rsCodeWords[i] = 0; + } + total = 0; + encodeInfo.append("Composite Codewords: "); + for (i = 0; i < cwCnt; i++) { + total = (codeWords[i] + rsCodeWords[k - 1]) % 929; + for (j = k - 1; j >= 0; j--) { + if (j == 0) { + rsCodeWords[j] = (929 - (total * ccaCoeffs[offset + j]) % 929) % 929; + } else { + rsCodeWords[j] = (rsCodeWords[j - 1] + 929 - (total * ccaCoeffs[offset + j]) % 929) % 929; + } + } + encodeInfo.append(Integer.toString(codeWords[i])).append(" "); + } + encodeInfo.append("\n"); + + for (j = 0; j < k; j++) { + if (rsCodeWords[j] != 0) { + rsCodeWords[j] = 929 - rsCodeWords[j]; + } + } + + for (i = k - 1; i >= 0; i--) { + codeWords[cwCnt] = rsCodeWords[i]; + cwCnt++; + } + + /* Place data into table */ + LeftRAPStart = aRAPTable[variant]; + CentreRAPStart = aRAPTable[variant + 17]; + RightRAPStart = aRAPTable[variant + 34]; + StartCluster = aRAPTable[variant + 51] / 3; + + LeftRAP = LeftRAPStart; + CentreRAP = CentreRAPStart; + RightRAP = RightRAPStart; + Cluster = StartCluster; /* Cluster can be 0, 1 or 2 for Cluster(0), Cluster(3) and Cluster(6) */ + + readable = new StringBuilder(); + rowCount = rows; + pattern = new String[rowCount]; + rowHeight = new int[rowCount]; + + for (i = 0; i < rows; i++) { + codebarre = ""; + offset = 929 * Cluster; + for (j = 0; j < 5; j++) { + dummy[j] = 0; + } + for (j = 0; j < ccWidth; j++) { + dummy[j + 1] = codeWords[i * ccWidth + j]; + } + /* Copy the data into codebarre */ + codebarre += RAPLR[LeftRAP]; + codebarre += "1"; + codebarre += codagemc[offset + dummy[1]]; + codebarre += "1"; + if (ccWidth == 3) { + codebarre += RAPC[CentreRAP]; + } + if (ccWidth >= 2) { + codebarre += "1"; + codebarre += codagemc[offset + dummy[2]]; + codebarre += "1"; + } + if (ccWidth == 4) { + codebarre += RAPC[CentreRAP]; + } + if (ccWidth >= 3) { + codebarre += "1"; + codebarre += codagemc[offset + dummy[3]]; + codebarre += "1"; + } + if (ccWidth == 4) { + codebarre += "1"; + codebarre += codagemc[offset + dummy[4]]; + codebarre += "1"; + } + codebarre += RAPLR[RightRAP]; + codebarre += "1"; /* stop */ + + /* Now codebarre is a mixture of letters and numbers */ + + flip = 1; + bin = new StringBuilder(); + for (loop = 0; loop < codebarre.length(); loop++) { + if ((codebarre.charAt(loop) >= '0') && (codebarre.charAt(loop) <= '9')) { + for (k = 0; k < codebarre.charAt(loop) - '0'; k++) { + if (flip == 0) { + bin.append('0'); + } else { + bin.append('1'); + } + } + if (flip == 0) { + flip = 1; + } else { + flip = 0; + } + } else { + bin.append(PDFttf[positionOf(codebarre.charAt(loop), brSet)]); + } + } + + rowHeight[i] = 2; + pattern[i] = bin2pat(bin.toString()); + + /* Set up RAPs and Cluster for next row */ + LeftRAP++; + CentreRAP++; + RightRAP++; + Cluster++; + + if (LeftRAP == 53) { + LeftRAP = 1; + } + if (CentreRAP == 53) { + CentreRAP = 1; + } + if (RightRAP == 53) { + RightRAP = 1; + } + if (Cluster == 3) { + Cluster = 0; + } + } + } + + /* initialize pwr928 encoding table */ + private void init928() { + int i, j, v; + int[] cw = new int[7]; + cw[6] = 1; + for (i = 5; i >= 0; i--) { + cw[i] = 0; + } + + for (i = 0; i < 7; i++) { + pwr928[0][i] = cw[i]; + } + for (j = 1; j < 69; j++) { + for (v = 0, i = 6; i >= 1; i--) { + v = (2 * cw[i]) + (v / 928); + pwr928[j][i] = cw[i] = v % 928; + } + pwr928[j][0] = cw[0] = (2 * cw[0]) + (v / 928); + } + } + + /* converts bit string to base 928 values, codeWords[0] is highest order */ + int encode928(int bitLng) { + int i, j, b, bitCnt, cwNdx, cwCnt, cwLng; + for (cwNdx = cwLng = b = 0; b < bitLng; b += 69, cwNdx += 7) { + bitCnt = min(bitLng - b, 69); + cwLng += cwCnt = bitCnt / 10 + 1; + for (i = 0; i < cwCnt; i++) { + codeWords[cwNdx + i] = 0; /* init 0 */ + } + for (i = 0; i < bitCnt; i++) { + if (getBit(b + bitCnt - i - 1)) { + for (j = 0; j < cwCnt; j++) { + codeWords[cwNdx + j] += pwr928[i][j + 7 - cwCnt]; + } + } + } + for (i = cwCnt - 1; i > 0; i--) { + /* add "carries" */ + codeWords[cwNdx + i - 1] += codeWords[cwNdx + i] / 928L; + codeWords[cwNdx + i] %= 928L; + } + } + return (cwLng); + } + + private int min(int first, int second) { + if (first <= second) { + return first; + } else { + return second; + } + } + + /* gets bit in bitString at bitPos */ + private boolean getBit(int arg) { + return (bitStr[arg >> 4] & (0x8000 >> (arg & 15))) != 0; + } + + private void ccB() { + /* CC-B 2D component */ + int length, i, binloc; + int k, j, longueur, offset; + int[] mccorrection = new int[50]; + int total; + int[] dummy = new int[5]; + String codebarre; + String bin; + int variant, LeftRAPStart, CentreRAPStart, RightRAPStart, StartCluster; + int LeftRAP, CentreRAP, RightRAP, Cluster, flip, loop; + int option2, rows; + inputData = new int[(binaryString.length() / 8) + 3]; + + length = binaryString.length() / 8; + + for (i = 0; i < length; i++) { + binloc = i * 8; + + inputData[i] = 0; + for (j = 0; j < 8; j++) { + if (binaryString.charAt(binloc + j) == '1') { + inputData[i] += 0x80 >> j; + } + } + } + + codeWordCount = 0; + + /* "the CC-B component shall have codeword 920 in the first symbol character position" (section 9a) */ + codeWords[codeWordCount] = 920; + codeWordCount++; + + byteprocess(0, length); + + /* Now figure out which variant of the symbol to use and load values accordingly */ + + variant = 0; + + if (ccWidth == 2) { + variant = 13; + if (codeWordCount <= 33) { + variant = 12; + } + if (codeWordCount <= 29) { + variant = 11; + } + if (codeWordCount <= 24) { + variant = 10; + } + if (codeWordCount <= 19) { + variant = 9; + } + if (codeWordCount <= 13) { + variant = 8; + } + if (codeWordCount <= 8) { + variant = 7; + } + } + + if (ccWidth == 3) { + variant = 23; + if (codeWordCount <= 70) { + variant = 22; + } + if (codeWordCount <= 58) { + variant = 21; + } + if (codeWordCount <= 46) { + variant = 20; + } + if (codeWordCount <= 34) { + variant = 19; + } + if (codeWordCount <= 24) { + variant = 18; + } + if (codeWordCount <= 18) { + variant = 17; + } + if (codeWordCount <= 14) { + variant = 16; + } + if (codeWordCount <= 10) { + variant = 15; + } + if (codeWordCount <= 6) { + variant = 14; + } + } + + if (ccWidth == 4) { + variant = 34; + if (codeWordCount <= 108) { + variant = 33; + } + if (codeWordCount <= 90) { + variant = 32; + } + if (codeWordCount <= 72) { + variant = 31; + } + if (codeWordCount <= 54) { + variant = 30; + } + if (codeWordCount <= 39) { + variant = 29; + } + if (codeWordCount <= 30) { + variant = 28; + } + if (codeWordCount <= 24) { + variant = 27; + } + if (codeWordCount <= 18) { + variant = 26; + } + if (codeWordCount <= 12) { + variant = 25; + } + if (codeWordCount <= 8) { + variant = 24; + } + } + + /* Now we have the variant we can load the data - from here on the same as MicroPDF417 code */ + variant--; + option2 = MicroVariants[variant]; /* columns */ + rows = MicroVariants[variant + 34]; /* rows */ + k = MicroVariants[variant + 68]; /* number of EC CWs */ + longueur = (option2 * rows) - k; /* number of non-EC CWs */ + i = longueur - codeWordCount; /* amount of padding required */ + offset = MicroVariants[variant + 102]; /* coefficient offset */ + + /* We add the padding */ + while (i > 0) { + codeWords[codeWordCount] = 900; + codeWordCount++; + i--; + } + + /* Reed-Solomon error correction */ + longueur = codeWordCount; + for (loop = 0; loop < 50; loop++) { + mccorrection[loop] = 0; + } + encodeInfo.append("Composite Codewords: "); + for (i = 0; i < longueur; i++) { + total = (codeWords[i] + mccorrection[k - 1]) % 929; + for (j = k - 1; j >= 0; j--) { + if (j == 0) { + mccorrection[j] = (929 - (total * Microcoeffs[offset + j]) % 929) % 929; + } else { + mccorrection[j] = (mccorrection[j - 1] + 929 - (total * Microcoeffs[offset + j]) % 929) % 929; + } + } + encodeInfo.append(Integer.toString(codeWords[i])).append(" "); + } + encodeInfo.append("\n"); + + for (j = 0; j < k; j++) { + if (mccorrection[j] != 0) { + mccorrection[j] = 929 - mccorrection[j]; + } + } + /* we add these codes to the string */ + for (i = k - 1; i >= 0; i--) { + codeWords[codeWordCount] = mccorrection[i]; + codeWordCount++; + } + + /* Now get the RAP (Row Address Pattern) start values */ + LeftRAPStart = RAPTable[variant]; + CentreRAPStart = RAPTable[variant + 34]; + RightRAPStart = RAPTable[variant + 68]; + StartCluster = RAPTable[variant + 102] / 3; + + /* That's all values loaded, get on with the encoding */ + + LeftRAP = LeftRAPStart; + CentreRAP = CentreRAPStart; + RightRAP = RightRAPStart; + Cluster = StartCluster; /* Cluster can be 0, 1 or 2 for Cluster(0), Cluster(3) and Cluster(6) */ + + readable = new StringBuilder(); + rowCount = rows; + pattern = new String[rowCount]; + rowHeight = new int[rowCount]; + + for (i = 0; i < rows; i++) { + codebarre = ""; + offset = 929 * Cluster; + for (j = 0; j < 5; j++) { + dummy[j] = 0; + } + for (j = 0; j < option2; j++) { + dummy[j + 1] = codeWords[i * option2 + j]; + } + /* Copy the data into codebarre */ + codebarre += RAPLR[LeftRAP]; + codebarre += "1"; + codebarre += codagemc[offset + dummy[1]]; + codebarre += "1"; + if (ccWidth == 3) { + codebarre += RAPC[CentreRAP]; + } + if (ccWidth >= 2) { + codebarre += "1"; + codebarre += codagemc[offset + dummy[2]]; + codebarre += "1"; + } + if (ccWidth == 4) { + codebarre += RAPC[CentreRAP]; + } + if (ccWidth >= 3) { + codebarre += "1"; + codebarre += codagemc[offset + dummy[3]]; + codebarre += "1"; + } + if (ccWidth == 4) { + codebarre += "1"; + codebarre += codagemc[offset + dummy[4]]; + codebarre += "1"; + } + codebarre += RAPLR[RightRAP]; + codebarre += "1"; /* stop */ + + /* Now codebarre is a mixture of letters and numbers */ + + flip = 1; + bin = ""; + for (loop = 0; loop < codebarre.length(); loop++) { + if ((codebarre.charAt(loop) >= '0') && (codebarre.charAt(loop) <= '9')) { + for (k = 0; k < codebarre.charAt(loop) - '0'; k++) { + if (flip == 0) { + bin += '0'; + } else { + bin += '1'; + } + } + if (flip == 0) { + flip = 1; + } else { + flip = 0; + } + } else { + bin += PDFttf[positionOf(codebarre.charAt(loop), brSet)]; + } + } + + pattern[i] = bin2pat(bin); + rowHeight[i] = 2; + + /* Set up RAPs and Cluster for next row */ + LeftRAP++; + CentreRAP++; + RightRAP++; + Cluster++; + + if (LeftRAP == 53) { + LeftRAP = 1; + } + if (CentreRAP == 53) { + CentreRAP = 1; + } + if (RightRAP == 53) { + RightRAP = 1; + } + if (Cluster == 3) { + Cluster = 0; + } + + } + } + + private void ccC() { + /* CC-C 2D component - byte compressed PDF417 */ + int length, i, binloc, k; + int offset, longueur, loop, total, j; + int[] mccorrection = new int[520]; + int c1, c2, c3; + int[] dummy = new int[35]; + String codebarre; + String bin; + inputData = new int[(binaryString.length() / 8) + 4]; + + length = binaryString.length() / 8; + + for (i = 0; i < length; i++) { + binloc = i * 8; + + inputData[i] = 0; + for (j = 0; j < 8; j++) { + if (binaryString.charAt(binloc + j) == '1') { + inputData[i] += 0x80 >> j; + } + } + } + + codeWordCount = 0; + + codeWords[codeWordCount] = 0; /* space for length descriptor */ + codeWordCount++; + codeWords[codeWordCount] = 920; /* CC-C identifier */ + codeWordCount++; + + byteprocess(0, length); + + codeWords[0] = codeWordCount; + + k = 1; + for (i = 1; i <= (ecc + 1); i++) { + k *= 2; + } + + /* 796 - we now take care of the Reed Solomon codes */ + switch (ecc) { + case 1: + offset = 2; + break; + case 2: + offset = 6; + break; + case 3: + offset = 14; + break; + case 4: + offset = 30; + break; + case 5: + offset = 62; + break; + case 6: + offset = 126; + break; + case 7: + offset = 254; + break; + case 8: + offset = 510; + break; + default: + offset = 0; + break; + } + + longueur = codeWordCount; + for (loop = 0; loop < 520; loop++) { + mccorrection[loop] = 0; + } + encodeInfo.append("Composite Codewords: "); + for (i = 0; i < longueur; i++) { + total = (codeWords[i] + mccorrection[k - 1]) % 929; + for (j = k - 1; j >= 0; j--) { + if (j == 0) { + mccorrection[j] = (929 - (total * coefrs[offset + j]) % 929) % 929; + } else { + mccorrection[j] = (mccorrection[j - 1] + 929 - (total * coefrs[offset + j]) % 929) % 929; + } + } + encodeInfo.append(Integer.toString(codeWords[i])).append(" "); + } + encodeInfo.append("\n"); + + for (j = 0; j < k; j++) { + if (mccorrection[j] != 0) { + mccorrection[j] = 929 - mccorrection[j]; + } + } + /* we add these codes to the string */ + for (i = k - 1; i >= 0; i--) { + codeWords[codeWordCount] = mccorrection[i]; + codeWordCount++; + } + + /* 818 - The CW string is finished */ + c1 = (codeWordCount / ccWidth - 1) / 3; + c2 = ecc * 3 + (codeWordCount / ccWidth - 1) % 3; + c3 = ccWidth - 1; + + readable = new StringBuilder(); + rowCount = codeWordCount / ccWidth; + pattern = new String[rowCount]; + rowHeight = new int[rowCount]; + + /* we now encode each row */ + for (i = 0; i <= (codeWordCount / ccWidth) - 1; i++) { + for (j = 0; j < ccWidth; j++) { + dummy[j + 1] = codeWords[i * ccWidth + j]; + } + k = (i / 3) * 30; + switch (i % 3) { + /* follows this pattern from US Patent 5,243,655: + Row 0: L0 (row #, # of rows) R0 (row #, # of columns) + Row 1: L1 (row #, security level) R1 (row #, # of rows) + Row 2: L2 (row #, # of columns) R2 (row #, security level) + Row 3: L3 (row #, # of rows) R3 (row #, # of columns) + etc. */ + case 0: + dummy[0] = k + c1; + dummy[ccWidth + 1] = k + c3; + break; + case 1: + dummy[0] = k + c2; + dummy[ccWidth + 1] = k + c1; + break; + case 2: + dummy[0] = k + c3; + dummy[ccWidth + 1] = k + c2; + break; + } + codebarre = "+*"; /* Start with a start char and a separator */ + + for (j = 0; j <= ccWidth + 1; j++) { + switch (i % 3) { + case 1: + offset = 929; /* cluster(3) */ + break; + case 2: + offset = 1858; /* cluster(6) */ + break; + default: + offset = 0; /* cluster(0) */ + break; + } + codebarre += codagemc[offset + dummy[j]]; + codebarre += "*"; + } + codebarre += "-"; + + bin = ""; + for (loop = 0; loop < codebarre.length(); loop++) { + bin += PDFttf[positionOf(codebarre.charAt(loop), brSet)]; + } + pattern[i] = bin2pat(bin); + rowHeight[i] = 3; + } + } + + private void byteprocess(int start, int length) { + int len = 0; + int chunkLen = 0; + BigInteger mantisa; + BigInteger total; + BigInteger word; + + /* select the switch for multiple of 6 bytes */ + if ((binaryString.length() % 6) == 0) { + codeWords[codeWordCount++] = 924; + } else { + codeWords[codeWordCount++] = 901; + } + + while (len < length) { + chunkLen = length - len; + if (6 <= chunkLen) /* Take groups of 6 */ { + chunkLen = 6; + len += chunkLen; + total = BigInteger.valueOf(0); + + while ((chunkLen--) != 0) { + mantisa = BigInteger.valueOf(inputData[start++]); + total = total.or(mantisa.shiftLeft(chunkLen * 8)); + } + + chunkLen = 5; + + while ((chunkLen--) != 0) { + + word = total.mod(BigInteger.valueOf(900)); + codeWords[codeWordCount + chunkLen] = word.intValue(); + total = total.divide(BigInteger.valueOf(900)); + } + codeWordCount += 5; + } else /* If it remain a group of less than 6 bytes */ { + len += chunkLen; + while ((chunkLen--) != 0) { + codeWords[codeWordCount++] = inputData[start++]; + } + } + } + } + + public enum LinearEncoding { + UPCA, UPCE, EAN, CODE_128, DATABAR_14, DATABAR_14_STACK, + DATABAR_14_STACK_OMNI, DATABAR_LIMITED, DATABAR_EXPANDED, + DATABAR_EXPANDED_STACK + } + + private enum gfMode { + NUMERIC, ALPHA, ISOIEC, INVALID_CHAR, ANY_ENC, ALPHA_OR_ISO + } + + public enum CompositeMode { + CC_A, CC_B, CC_C + } +} diff --git a/barcode/src/main/java/org/xbib/graphics/barcode/DataBar14.java b/barcode/src/main/java/org/xbib/graphics/barcode/DataBar14.java new file mode 100755 index 0000000..c553eb7 --- /dev/null +++ b/barcode/src/main/java/org/xbib/graphics/barcode/DataBar14.java @@ -0,0 +1,698 @@ +package org.xbib.graphics.barcode; + +import java.math.BigInteger; + +/** + * Implements GS1 DataBar Omnidirectional and GS1 DataBar Truncated according to ISO/IEC 24724:2011. + * Input data should be a 13 digit Global Trade Identification Number + * without check digit or Application Identifier [01]. + */ +public class DataBar14 extends Symbol { + + private int[] g_sum_table = { + 0, 161, 961, 2015, 2715, 0, 336, 1036, 1516 + }; + private int[] t_table = { + 1, 10, 34, 70, 126, 4, 20, 48, 81 + }; + private int[] widths = new int[8]; + private int[] modules_odd = { + 12, 10, 8, 6, 4, 5, 7, 9, 11 + }; + private int[] modules_even = { + 4, 6, 8, 10, 12, 10, 8, 6, 4 + }; + private int[] widest_odd = { + 8, 6, 4, 3, 1, 2, 4, 6, 8 + }; + private int[] widest_even = { + 1, 3, 5, 6, 8, 7, 5, 3, 1 + }; + private int[] checksum_weight = { /* Table 5 */ + 1, 3, 9, 27, 2, 6, 18, 54, 4, 12, 36, 29, 8, 24, 72, 58, 16, 48, 65, + 37, 32, 17, 51, 74, 64, 34, 23, 69, 49, 68, 46, 59 + }; + private int[] finder_pattern = { + 3, 8, 2, 1, 1, 3, 5, 5, 1, 1, 3, 3, 7, 1, 1, 3, 1, 9, 1, 1, 2, 7, 4, + 1, 1, 2, 5, 6, 1, 1, 2, 3, 8, 1, 1, 1, 5, 7, 1, 1, 1, 3, 9, 1, 1 + }; + + private boolean linkageFlag; + private gb14Mode symbolType; + + private boolean[][] grid = new boolean[5][100]; + private boolean[] seperator = new boolean[100]; + + public DataBar14() { + linkageFlag = false; + symbolType = gb14Mode.LINEAR; + } + + @Override + public void setDataType(DataType dummy) { + // Do nothing! + } + + protected void setLinkageFlag() { + linkageFlag = true; + } + + protected void unsetLinkageFlag() { + linkageFlag = false; + } + + /** + * Set symbol type to DataBar-14 + */ + public void setLinearMode() { + symbolType = gb14Mode.LINEAR; + } + + /** + * Set symbol type to DataBar-14 Omnidirectional + */ + public void setOmnidirectionalMode() { + symbolType = gb14Mode.OMNI; + } + + /** + * Set symbol type to DataBar-14 Omnidirectional Stacked + */ + public void setStackedMode() { + symbolType = gb14Mode.STACKED; + } + + @Override + public boolean encode() { + BigInteger accum; + BigInteger left_reg; + BigInteger right_reg; + int[] data_character = new int[4]; + int[] data_group = new int[4]; + int[] v_odd = new int[4]; + int[] v_even = new int[4]; + int i; + int[][] data_widths = new int[8][4]; + int checksum; + int c_left; + int c_right; + int[] total_widths = new int[46]; + int writer; + char latch; + int j; + int count; + int check_digit; + String hrt; + String bin; + int compositeOffset = 0; + + if (content.length() > 13) { + errorMsg.append("Input too long"); + return false; + } + + if (!(content.matches("[0-9]+?"))) { + errorMsg.append("Invalid characters in input"); + return false; + } + + accum = new BigInteger(content); + if (linkageFlag) { + accum = accum.add(new BigInteger("10000000000000")); + compositeOffset = 1; + } + + /* Calculate left and right pair values */ + left_reg = accum.divide(new BigInteger("4537077")); + right_reg = accum.mod(new BigInteger("4537077")); + + /* Calculate four data characters */ + accum = left_reg.divide(new BigInteger("1597")); + data_character[0] = accum.intValue(); + accum = left_reg.mod(new BigInteger("1597")); + data_character[1] = accum.intValue(); + accum = right_reg.divide(new BigInteger("1597")); + data_character[2] = accum.intValue(); + accum = right_reg.mod(new BigInteger("1597")); + data_character[3] = accum.intValue(); + + encodeInfo.append("Data characters: "); + for (i = 0; i < 4; i++) { + encodeInfo.append(Integer.toString(data_character[i])).append(" "); + } + encodeInfo.append("\n"); + + /* Calculate odd and even subset values */ + if ((data_character[0] >= 0) && (data_character[0] <= 160)) { + data_group[0] = 0; + } + if ((data_character[0] >= 161) && (data_character[0] <= 960)) { + data_group[0] = 1; + } + if ((data_character[0] >= 961) && (data_character[0] <= 2014)) { + data_group[0] = 2; + } + if ((data_character[0] >= 2015) && (data_character[0] <= 2714)) { + data_group[0] = 3; + } + if ((data_character[0] >= 2715) && (data_character[0] <= 2840)) { + data_group[0] = 4; + } + if ((data_character[1] >= 0) && (data_character[1] <= 335)) { + data_group[1] = 5; + } + if ((data_character[1] >= 336) && (data_character[1] <= 1035)) { + data_group[1] = 6; + } + if ((data_character[1] >= 1036) && (data_character[1] <= 1515)) { + data_group[1] = 7; + } + if ((data_character[1] >= 1516) && (data_character[1] <= 1596)) { + data_group[1] = 8; + } + if ((data_character[3] >= 0) && (data_character[3] <= 335)) { + data_group[3] = 5; + } + if ((data_character[3] >= 336) && (data_character[3] <= 1035)) { + data_group[3] = 6; + } + if ((data_character[3] >= 1036) && (data_character[3] <= 1515)) { + data_group[3] = 7; + } + if ((data_character[3] >= 1516) && (data_character[3] <= 1596)) { + data_group[3] = 8; + } + if ((data_character[2] >= 0) && (data_character[2] <= 160)) { + data_group[2] = 0; + } + if ((data_character[2] >= 161) && (data_character[2] <= 960)) { + data_group[2] = 1; + } + if ((data_character[2] >= 961) && (data_character[2] <= 2014)) { + data_group[2] = 2; + } + if ((data_character[2] >= 2015) && (data_character[2] <= 2714)) { + data_group[2] = 3; + } + if ((data_character[2] >= 2715) && (data_character[2] <= 2840)) { + data_group[2] = 4; + } + + v_odd[0] = (data_character[0] - g_sum_table[data_group[0]]) / t_table[data_group[0]]; + v_even[0] = (data_character[0] - g_sum_table[data_group[0]]) % t_table[data_group[0]]; + v_odd[1] = (data_character[1] - g_sum_table[data_group[1]]) % t_table[data_group[1]]; + v_even[1] = (data_character[1] - g_sum_table[data_group[1]]) / t_table[data_group[1]]; + v_odd[3] = (data_character[3] - g_sum_table[data_group[3]]) % t_table[data_group[3]]; + v_even[3] = (data_character[3] - g_sum_table[data_group[3]]) / t_table[data_group[3]]; + v_odd[2] = (data_character[2] - g_sum_table[data_group[2]]) / t_table[data_group[2]]; + v_even[2] = (data_character[2] - g_sum_table[data_group[2]]) % t_table[data_group[2]]; + + /* Use RSS subset width algorithm */ + for (i = 0; i < 4; i++) { + if ((i == 0) || (i == 2)) { + getWidths(v_odd[i], modules_odd[data_group[i]], 4, widest_odd[data_group[i]], 1); + data_widths[0][i] = widths[0]; + data_widths[2][i] = widths[1]; + data_widths[4][i] = widths[2]; + data_widths[6][i] = widths[3]; + getWidths(v_even[i], modules_even[data_group[i]], 4, widest_even[data_group[i]], 0); + data_widths[1][i] = widths[0]; + data_widths[3][i] = widths[1]; + data_widths[5][i] = widths[2]; + data_widths[7][i] = widths[3]; + } else { + getWidths(v_odd[i], modules_odd[data_group[i]], 4, widest_odd[data_group[i]], 0); + data_widths[0][i] = widths[0]; + data_widths[2][i] = widths[1]; + data_widths[4][i] = widths[2]; + data_widths[6][i] = widths[3]; + getWidths(v_even[i], modules_even[data_group[i]], 4, widest_even[data_group[i]], 1); + data_widths[1][i] = widths[0]; + data_widths[3][i] = widths[1]; + data_widths[5][i] = widths[2]; + data_widths[7][i] = widths[3]; + } + } + + checksum = 0; + /* Calculate the checksum */ + for (i = 0; i < 8; i++) { + checksum += checksum_weight[i] * data_widths[i][0]; + checksum += checksum_weight[i + 8] * data_widths[i][1]; + checksum += checksum_weight[i + 16] * data_widths[i][2]; + checksum += checksum_weight[i + 24] * data_widths[i][3]; + } + checksum %= 79; + + /* Calculate the two check characters */ + if (checksum >= 8) { + checksum++; + } + if (checksum >= 72) { + checksum++; + } + c_left = checksum / 9; + c_right = checksum % 9; + + encodeInfo.append("Checksum: ").append(Integer.toString(checksum)).append("\n"); + + /* Put element widths together */ + total_widths[0] = 1; + total_widths[1] = 1; + total_widths[44] = 1; + total_widths[45] = 1; + for (i = 0; i < 8; i++) { + total_widths[i + 2] = data_widths[i][0]; + total_widths[i + 15] = data_widths[7 - i][1]; + total_widths[i + 23] = data_widths[i][3]; + total_widths[i + 36] = data_widths[7 - i][2]; + } + for (i = 0; i < 5; i++) { + total_widths[i + 10] = finder_pattern[i + (5 * c_left)]; + total_widths[i + 31] = finder_pattern[(4 - i) + (5 * c_right)]; + } + + rowCount = 0; + for (i = 0; i < 100; i++) { + seperator[i] = false; + } + /* Put this data into the symbol */ + if (symbolType == gb14Mode.LINEAR) { + writer = 0; + latch = '0'; + for (i = 0; i < 46; i++) { + for (j = 0; j < total_widths[i]; j++) { + if (latch == '1') { + setGridModule(rowCount, writer); + } + writer++; + } + if (latch == '1') { + latch = '0'; + } else { + latch = '1'; + } + } + if (symbolWidth < writer) { + symbolWidth = writer; + } + + if (linkageFlag) { + /* separator pattern for composite symbol */ + for (i = 4; i < 92; i++) { + seperator[i] = (!(grid[0][i])); + } + latch = '1'; + for (i = 16; i < 32; i++) { + if (!(grid[0][i])) { + if (latch == '1') { + seperator[i] = true; + latch = '0'; + } else { + seperator[i] = false; + latch = '1'; + } + } else { + seperator[i] = false; + latch = '1'; + } + } + latch = '1'; + for (i = 63; i < 78; i++) { + if (!(grid[0][i])) { + if (latch == '1') { + seperator[i] = true; + latch = '0'; + } else { + seperator[i] = false; + latch = '1'; + } + } else { + seperator[i] = false; + latch = '1'; + } + } + } + rowCount = rowCount + 1; + + count = 0; + check_digit = 0; + + /* Calculate check digit from Annex A and place human readable text */ + readable = new StringBuilder("(01)"); + hrt = ""; + for (i = content.length(); i < 13; i++) { + hrt += "0"; + } + hrt += content; + + for (i = 0; i < 13; i++) { + count += hrt.charAt(i) - '0'; + + if ((i & 1) == 0) { + count += 2 * (hrt.charAt(i) - '0'); + } + } + + check_digit = 10 - (count % 10); + if (check_digit == 10) { + check_digit = 0; + } + hrt += (char) (check_digit + '0'); + + readable.append(hrt); + } + + if (symbolType == gb14Mode.STACKED) { + /* top row */ + writer = 0; + latch = '0'; + for (i = 0; i < 23; i++) { + for (j = 0; j < total_widths[i]; j++) { + if (latch == '1') { + setGridModule(rowCount, writer); + } else { + unsetGridModule(rowCount, writer); + } + writer++; + } + if (latch == '1') { + latch = '0'; + } else { + latch = '1'; + } + } + setGridModule(rowCount, writer); + unsetGridModule(rowCount, writer + 1); + + /* bottom row */ + rowCount = rowCount + 2; + setGridModule(rowCount, 0); + unsetGridModule(rowCount, 1); + writer = 0; + latch = '1'; + for (i = 23; i < 46; i++) { + for (j = 0; j < total_widths[i]; j++) { + if (latch == '1') { + setGridModule(rowCount, writer + 2); + } else { + unsetGridModule(rowCount, writer + 2); + } + writer++; + } + if (latch == '1') { + latch = '0'; + } else { + latch = '1'; + } + } + + /* separator pattern */ + for (i = 4; i < 46; i++) { + if (gridModuleIsSet(rowCount - 2, i) == gridModuleIsSet(rowCount, i)) { + if (!(gridModuleIsSet(rowCount - 2, i))) { + setGridModule(rowCount - 1, i); + } + } else { + if (!(gridModuleIsSet(rowCount - 1, i - 1))) { + setGridModule(rowCount - 1, i); + } + } + } + + if (linkageFlag) { + /* separator pattern for composite symbol */ + for (i = 4; i < 46; i++) { + seperator[i] = (!(grid[0][i])); + } + latch = '1'; + for (i = 16; i < 32; i++) { + if (!(grid[0][i])) { + if (latch == '1') { + seperator[i] = true; + latch = '0'; + } else { + seperator[i] = false; + latch = '1'; + } + } else { + seperator[i] = false; + latch = '1'; + } + } + } + rowCount = rowCount + 1; + if (symbolWidth < 50) { + symbolWidth = 50; + } + } + + if (symbolType == gb14Mode.OMNI) { + /* top row */ + writer = 0; + latch = '0'; + for (i = 0; i < 23; i++) { + for (j = 0; j < total_widths[i]; j++) { + if (latch == '1') { + setGridModule(rowCount, writer); + } else { + unsetGridModule(rowCount, writer); + } + writer++; + } + latch = (latch == '1' ? '0' : '1'); + } + setGridModule(rowCount, writer); + unsetGridModule(rowCount, writer + 1); + + /* bottom row */ + rowCount = rowCount + 4; + setGridModule(rowCount, 0); + unsetGridModule(rowCount, 1); + writer = 0; + latch = '1'; + for (i = 23; i < 46; i++) { + for (j = 0; j < total_widths[i]; j++) { + if (latch == '1') { + setGridModule(rowCount, writer + 2); + } else { + unsetGridModule(rowCount, writer + 2); + } + writer++; + } + if (latch == '1') { + latch = '0'; + } else { + latch = '1'; + } + } + + /* middle separator */ + for (i = 5; i < 46; i += 2) { + setGridModule(rowCount - 2, i); + } + + /* top separator */ + for (i = 4; i < 46; i++) { + if (!(gridModuleIsSet(rowCount - 4, i))) { + setGridModule(rowCount - 3, i); + } + } + latch = '1'; + for (i = 17; i < 33; i++) { + if (!(gridModuleIsSet(rowCount - 4, i))) { + if (latch == '1') { + setGridModule(rowCount - 3, i); + latch = '0'; + } else { + unsetGridModule(rowCount - 3, i); + latch = '1'; + } + } else { + unsetGridModule(rowCount - 3, i); + latch = '1'; + } + } + + /* bottom separator */ + for (i = 4; i < 46; i++) { + if (!(gridModuleIsSet(rowCount, i))) { + setGridModule(rowCount - 1, i); + } + } + latch = '1'; + for (i = 16; i < 32; i++) { + if (!(gridModuleIsSet(rowCount, i))) { + if (latch == '1') { + setGridModule(rowCount - 1, i); + latch = '0'; + } else { + unsetGridModule(rowCount - 1, i); + latch = '1'; + } + } else { + unsetGridModule(rowCount - 1, i); + latch = '1'; + } + } + + if (symbolWidth < 50) { + symbolWidth = 50; + } + if (linkageFlag) { + /* separator pattern for composite symbol */ + for (i = 4; i < 46; i++) { + seperator[i] = (!(grid[0][i])); + } + latch = '1'; + for (i = 16; i < 32; i++) { + if (!(grid[0][i])) { + if (latch == '1') { + seperator[i] = true; + latch = '0'; + } else { + seperator[i] = false; + latch = '1'; + } + } else { + seperator[i] = false; + latch = '1'; + } + } + } + rowCount = rowCount + 1; + } + + pattern = new String[rowCount + compositeOffset]; + rowHeight = new int[rowCount + compositeOffset]; + + if (linkageFlag) { + bin = ""; + for (j = 0; j < symbolWidth; j++) { + if (seperator[j]) { + bin += "1"; + } else { + bin += "0"; + } + } + pattern[0] = bin2pat(bin); + rowHeight[0] = 1; + } + + for (i = 0; i < rowCount; i++) { + bin = ""; + for (j = 0; j < symbolWidth; j++) { + if (grid[i][j]) { + bin += "1"; + } else { + bin += "0"; + } + } + pattern[i + compositeOffset] = bin2pat(bin); + } + + if (symbolType == gb14Mode.LINEAR) { + rowHeight[0 + compositeOffset] = -1; + } + if (symbolType == gb14Mode.STACKED) { + rowHeight[0 + compositeOffset] = 5; + rowHeight[1 + compositeOffset] = 1; + rowHeight[2 + compositeOffset] = 7; + } + if (symbolType == gb14Mode.OMNI) { + rowHeight[0 + compositeOffset] = -1; + rowHeight[1 + compositeOffset] = 1; + rowHeight[2 + compositeOffset] = 1; + rowHeight[3 + compositeOffset] = 1; + rowHeight[4 + compositeOffset] = -1; + } + + if (linkageFlag) { + rowCount++; + } + + plotSymbol(); + return true; + } + + private int getCombinations(int n, int r) { + int i, j; + int maxDenom, minDenom; + int val; + + if (n - r > r) { + minDenom = r; + maxDenom = n - r; + } else { + minDenom = n - r; + maxDenom = r; + } + val = 1; + j = 1; + for (i = n; i > maxDenom; i--) { + val *= i; + if (j <= minDenom) { + val /= j; + j++; + } + } + for (; j <= minDenom; j++) { + val /= j; + } + return (val); + } + + private void getWidths(int val, int n, int elements, int maxWidth, int noNarrow) { + int bar; + int elmWidth; + int mxwElement; + int subVal, lessVal; + int narrowMask = 0; + for (bar = 0; bar < elements - 1; bar++) { + for (elmWidth = 1, narrowMask |= (1 << bar); ; + elmWidth++, narrowMask &= ~(1 << bar)) { + /* get all combinations */ + subVal = getCombinations(n - elmWidth - 1, elements - bar - 2); + /* less combinations with no single-module element */ + if ((noNarrow == 0) && (narrowMask == 0) + && (n - elmWidth - (elements - bar - 1) >= elements - bar - 1)) { + subVal -= getCombinations(n - elmWidth - (elements - bar), elements - bar - 2); + } + /* less combinations with elements > maxVal */ + if (elements - bar - 1 > 1) { + lessVal = 0; + for (mxwElement = n - elmWidth - (elements - bar - 2); + mxwElement > maxWidth; + mxwElement--) { + lessVal += getCombinations(n - elmWidth - mxwElement - 1, elements - bar - 3); + } + subVal -= lessVal * (elements - 1 - bar); + } else if (n - elmWidth > maxWidth) { + subVal--; + } + val -= subVal; + if (val < 0) break; + } + val += subVal; + n -= elmWidth; + widths[bar] = elmWidth; + } + widths[bar] = n; + } + + private void setGridModule(int row, int column) { + grid[row][column] = true; + } + + private void unsetGridModule(int row, int column) { + grid[row][column] = false; + } + + private boolean gridModuleIsSet(int row, int column) { + return grid[row][column]; + } + + private enum gb14Mode { + LINEAR, OMNI, STACKED + } +} diff --git a/barcode/src/main/java/org/xbib/graphics/barcode/DataBarExpanded.java b/barcode/src/main/java/org/xbib/graphics/barcode/DataBarExpanded.java new file mode 100755 index 0000000..fe0da9d --- /dev/null +++ b/barcode/src/main/java/org/xbib/graphics/barcode/DataBarExpanded.java @@ -0,0 +1,1652 @@ +package org.xbib.graphics.barcode; + +/** + * Implements GS1 DataBar Expanded Omnidirectional and GS1 Expanded Stacked + * Omnidirectional according to ISO/IEC 24724:2011. + * DataBar expanded encodes GS1 data in either a linear or stacked + * format. + */ +public class DataBarExpanded extends Symbol { + + private static final int[] G_SUM_EXP = { + 0, 348, 1388, 2948, 3988 + }; + private static final int[] T_EVEN_EXP = { + 4, 20, 52, 104, 204 + }; + private static final int[] MODULES_ODD_EXP = { + 12, 10, 8, 6, 4 + }; + private static final int[] MODULES_EVEN_EXP = { + 5, 7, 9, 11, 13 + }; + private static final int[] WIDEST_ODD_EXP = { + 7, 5, 4, 3, 1 + }; + private static final int[] WIDEST_EVEN_EXP = { + 2, 4, 5, 6, 8 + }; + private static final int[] CHECKSUM_WEIGHT_EXP = { /* Table 14 */ + 1, 3, 9, 27, 81, 32, 96, 77, 20, 60, 180, 118, 143, 7, 21, 63, 189, + 145, 13, 39, 117, 140, 209, 205, 193, 157, 49, 147, 19, 57, 171, 91, + 62, 186, 136, 197, 169, 85, 44, 132, 185, 133, 188, 142, 4, 12, 36, + 108, 113, 128, 173, 97, 80, 29, 87, 50, 150, 28, 84, 41, 123, 158, 52, + 156, 46, 138, 203, 187, 139, 206, 196, 166, 76, 17, 51, 153, 37, 111, + 122, 155, 43, 129, 176, 106, 107, 110, 119, 146, 16, 48, 144, 10, 30, + 90, 59, 177, 109, 116, 137, 200, 178, 112, 125, 164, 70, 210, 208, 202, + 184, 130, 179, 115, 134, 191, 151, 31, 93, 68, 204, 190, 148, 22, 66, + 198, 172, 94, 71, 2, 6, 18, 54, 162, 64, 192, 154, 40, 120, 149, 25, + 75, 14, 42, 126, 167, 79, 26, 78, 23, 69, 207, 199, 175, 103, 98, 83, + 38, 114, 131, 182, 124, 161, 61, 183, 127, 170, 88, 53, 159, 55, 165, + 73, 8, 24, 72, 5, 15, 45, 135, 194, 160, 58, 174, 100, 89 + }; + private static final int[] FINDER_PATTERN_EXP = { /* Table 15 */ + 1, 8, 4, 1, 1, 1, 1, 4, 8, 1, 3, 6, 4, 1, 1, 1, 1, 4, 6, 3, 3, 4, 6, 1, + 1, 1, 1, 6, 4, 3, 3, 2, 8, 1, 1, 1, 1, 8, 2, 3, 2, 6, 5, 1, 1, 1, 1, 5, + 6, 2, 2, 2, 9, 1, 1, 1, 1, 9, 2, 2 + }; + private static final int[] FINDER_SEQUENCE = { /* Table 16 */ + 1, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 4, 3, 0, 0, 0, 0, 0, 0, 0, 0, 1, 6, + 3, 8, 0, 0, 0, 0, 0, 0, 0, 1, 10, 3, 8, 5, 0, 0, 0, 0, 0, 0, 1, 10, 3, + 8, 7, 12, 0, 0, 0, 0, 0, 1, 10, 3, 8, 9, 12, 11, 0, 0, 0, 0, 1, 2, 3, + 4, 5, 6, 7, 8, 0, 0, 0, 1, 2, 3, 4, 5, 6, 7, 10, 9, 0, 0, 1, 2, 3, 4, + 5, 6, 7, 10, 11, 12, 0, 1, 2, 3, 4, 5, 8, 7, 10, 9, 12, 11 + }; + private static final int[] WEIGHT_ROWS = { + 0, 1, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 5, 6, + 3, 4, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 9, 10, 3, 4, + 13, 14, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 17, 18, 3, 4, 13, + 14, 7, 8, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 17, 18, 3, 4, 13, 14, + 11, 12, 21, 22, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 17, 18, 3, 4, 13, 14, + 15, 16, 21, 22, 19, 20, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 2, 3, 4, 5, 6, 7, + 8, 9, 10, 11, 12, 13, 14, 0, 0, 0, 0, 0, 0, 0, 1, 2, 3, 4, 5, 6, 7, 8, + 9, 10, 11, 12, 17, 18, 15, 16, 0, 0, 0, 0, 0, 1, 2, 3, 4, 5, 6, 7, 8, + 9, 10, 11, 12, 17, 18, 19, 20, 21, 22, 0, 0, 0, 1, 2, 3, 4, 5, 6, 7, 8, + 13, 14, 11, 12, 17, 18, 15, 16, 21, 22, 19, 20 + }; + + private String source; + private StringBuilder binaryString = new StringBuilder(); + private String generalField; + private EncodeMode[] generalFieldType; + private int[] widths = new int[8]; + private boolean linkageFlag; + + private int preferredNoOfColumns = 0; + private dbeMode symbolType; + + public DataBarExpanded() { + linkageFlag = false; + inputDataType = DataType.GS1; + } + + private static int calculateRemainder(int binaryStringLength) { + int remainder = 12 - (binaryStringLength % 12); + if (remainder == 12) { + remainder = 0; + } + if (binaryStringLength < 36) { + remainder = 36 - binaryStringLength; + } + return remainder; + } + + /** + * Set the width of a stacked symbol by selecting the number + * of "columns" or symbol segments in each row of data. + * + * @param columns Number of segments in each row + */ + public void setNoOfColumns(int columns) { + preferredNoOfColumns = columns; + } + + ; + + @Override + public void setDataType(DataType dummy) { + // Do nothing! + } + + /** + * Set symbology to DataBar Expanded Stacked + */ + public void setStacked() { + symbolType = dbeMode.STACKED; + } + + /** + * Set symbology to DataBar Expanded + */ + public void setNotStacked() { + symbolType = dbeMode.UNSTACKED; + } + + protected void setLinkageFlag() { + linkageFlag = true; + } + + protected void unsetLinkageFlag() { + linkageFlag = false; + } + + @Override + public boolean encode() { + int i; + int j; + int k; + int dataChars; + int[] vs = new int[21]; + int[] group = new int[21]; + int[] vOdd = new int[21]; + int[] vEven = new int[21]; + int[][] charWidths = new int[21][8]; + int checksum; + int row; + int checkChar; + int cGroup; + int cOdd; + int cEven; + int[] checkWidths = new int[8]; + int patternWidth; + int[] elements = new int[235]; + int codeblocks; + int stackRows; + int blocksPerRow; + int currentBlock; + int currentRow; + boolean specialCaseRow; + int elementsInSub; + int reader; + int writer; + int[] subElements = new int[235]; + int l; + int symbolRow; + String seperatorBinary; + String seperatorPattern; + boolean black; + boolean leftToRight; + int compositeOffset; + + source = content; + + if (linkageFlag) { + binaryString = new StringBuilder("1"); + compositeOffset = 1; + } else { + binaryString = new StringBuilder("0"); + compositeOffset = 0; + } + if (!calculateBinaryString()) { + return false; + } + + dataChars = binaryString.length() / 12; + + encodeInfo.append("Data characters: "); + for (i = 0; i < dataChars; i++) { + vs[i] = 0; + for (j = 0; j < 12; j++) { + if (binaryString.charAt((i * 12) + j) == '1') { + vs[i] += 2048 >> j; + } + } + encodeInfo.append(Integer.toString(vs[i])).append(" "); + } + encodeInfo.append("\n"); + + for (i = 0; i < dataChars; i++) { + if (vs[i] <= 347) { + group[i] = 1; + } + if ((vs[i] >= 348) && (vs[i] <= 1387)) { + group[i] = 2; + } + if ((vs[i] >= 1388) && (vs[i] <= 2947)) { + group[i] = 3; + } + if ((vs[i] >= 2948) && (vs[i] <= 3987)) { + group[i] = 4; + } + if (vs[i] >= 3988) { + group[i] = 5; + } + vOdd[i] = (vs[i] - G_SUM_EXP[group[i] - 1]) / T_EVEN_EXP[group[i] - 1]; + vEven[i] = (vs[i] - G_SUM_EXP[group[i] - 1]) % T_EVEN_EXP[group[i] - 1]; + + getWidths(vOdd[i], MODULES_ODD_EXP[group[i] - 1], 4, WIDEST_ODD_EXP[group[i] - 1], 0); + charWidths[i][0] = widths[0]; + charWidths[i][2] = widths[1]; + charWidths[i][4] = widths[2]; + charWidths[i][6] = widths[3]; + getWidths(vEven[i], MODULES_EVEN_EXP[group[i] - 1], 4, WIDEST_EVEN_EXP[group[i] - 1], 1); + charWidths[i][1] = widths[0]; + charWidths[i][3] = widths[1]; + charWidths[i][5] = widths[2]; + charWidths[i][7] = widths[3]; + } + + /* 7.2.6 Check character */ + /* The checksum value is equal to the mod 211 residue of the weighted sum of the widths of the + elements in the data characters. */ + checksum = 0; + for (i = 0; i < dataChars; i++) { + row = WEIGHT_ROWS[(((dataChars - 2) / 2) * 21) + i]; + for (j = 0; j < 8; j++) { + checksum += (charWidths[i][j] * CHECKSUM_WEIGHT_EXP[(row * 8) + j]); + + } + } + + checkChar = (211 * ((dataChars + 1) - 4)) + (checksum % 211); + + encodeInfo.append("Check Character: ").append(Integer.toString(checkChar)).append("\n"); + + cGroup = 1; + if ((checkChar >= 348) && (checkChar <= 1387)) { + cGroup = 2; + } + if ((checkChar >= 1388) && (checkChar <= 2947)) { + cGroup = 3; + } + if ((checkChar >= 2948) && (checkChar <= 3987)) { + cGroup = 4; + } + if (checkChar >= 3988) { + cGroup = 5; + } + + cOdd = (checkChar - G_SUM_EXP[cGroup - 1]) / T_EVEN_EXP[cGroup - 1]; + cEven = (checkChar - G_SUM_EXP[cGroup - 1]) % T_EVEN_EXP[cGroup - 1]; + + getWidths(cOdd, MODULES_ODD_EXP[cGroup - 1], 4, WIDEST_ODD_EXP[cGroup - 1], 0); + checkWidths[0] = widths[0]; + checkWidths[2] = widths[1]; + checkWidths[4] = widths[2]; + checkWidths[6] = widths[3]; + getWidths(cEven, MODULES_EVEN_EXP[cGroup - 1], 4, WIDEST_EVEN_EXP[cGroup - 1], 1); + checkWidths[1] = widths[0]; + checkWidths[3] = widths[1]; + checkWidths[5] = widths[2]; + checkWidths[7] = widths[3]; + + /* Initialise element array */ + patternWidth = ((((dataChars + 1) / 2) + ((dataChars + 1) & 1)) * 5) + ((dataChars + 1) * 8) + 4; + for (i = 0; i < patternWidth; i++) { + elements[i] = 0; + } + + elements[0] = 1; + elements[1] = 1; + elements[patternWidth - 2] = 1; + elements[patternWidth - 1] = 1; + + /* Put finder patterns in element array */ + for (i = 0; i < (((dataChars + 1) / 2) + ((dataChars + 1) & 1)); i++) { + k = ((((((dataChars + 1) - 2) / 2) + ((dataChars + 1) & 1)) - 1) * 11) + i; + for (j = 0; j < 5; j++) { + elements[(21 * i) + j + 10] = FINDER_PATTERN_EXP[((FINDER_SEQUENCE[k] - 1) * 5) + j]; + } + } + + /* Put check character in element array */ + for (i = 0; i < 8; i++) { + elements[i + 2] = checkWidths[i]; + } + + /* Put forward reading data characters in element array */ + for (i = 1; i < dataChars; i += 2) { + for (j = 0; j < 8; j++) { + elements[(((i - 1) / 2) * 21) + 23 + j] = charWidths[i][j]; + } + } + + /* Put reversed data characters in element array */ + for (i = 0; i < dataChars; i += 2) { + for (j = 0; j < 8; j++) { + elements[((i / 2) * 21) + 15 + j] = charWidths[i][7 - j]; + } + } + + + if (symbolType == dbeMode.UNSTACKED) { + /* Copy elements into symbol */ + rowCount = 1 + compositeOffset; + rowHeight = new int[1 + compositeOffset]; + rowHeight[0 + compositeOffset] = -1; + pattern = new String[1 + compositeOffset]; + + pattern[0 + compositeOffset] = "0"; + writer = 0; + black = false; + seperatorBinary = ""; + for (i = 0; i < patternWidth; i++) { + pattern[0 + compositeOffset] += (char) (elements[i] + '0'); + for (j = 0; j < elements[i]; j++) { + if (black) { + seperatorBinary += "0"; + } else { + seperatorBinary += "1"; + } + } + + black = !(black); + writer += elements[i]; + } + seperatorBinary = "0000" + seperatorBinary.substring(4, writer - 4); + for (j = 0; j < (writer / 49); j++) { + k = (49 * j) + 18; + for (i = 0; i < 15; i++) { + if ((seperatorBinary.charAt(i + k - 1) == '1') + && (seperatorBinary.charAt(i + k) == '1')) { + seperatorBinary = seperatorBinary.substring(0, (i + k)) + + "0" + seperatorBinary.substring(i + k + 1); + } + } + } + if (linkageFlag) { + // Add composite code seperator + pattern[0] = bin2pat(seperatorBinary); + rowHeight[0] = 1; + } + + } else { + /* RSS Expanded Stacked */ + codeblocks = (dataChars + 1) / 2 + ((dataChars + 1) % 2); + + blocksPerRow = preferredNoOfColumns; + if ((blocksPerRow < 1) || (blocksPerRow > 10)) { + blocksPerRow = 2; + } + + if (linkageFlag && (blocksPerRow == 1)) { + /* "There shall be a minimum of four symbol characters in the + first row of an RSS Expanded Stacked symbol when it is the linear + component of an EAN.UCC Composite symbol." */ + blocksPerRow = 2; + } + + stackRows = codeblocks / blocksPerRow; + if (codeblocks % blocksPerRow > 0) { + stackRows++; + } + + rowCount = (stackRows * 4) - 3; + rowHeight = new int[rowCount + compositeOffset]; + pattern = new String[rowCount + compositeOffset]; + symbolRow = 0; + + currentBlock = 0; + for (currentRow = 1; currentRow <= stackRows; currentRow++) { + for (i = 0; i < 235; i++) { + subElements[i] = 0; + } + specialCaseRow = false; + + /* Row Start */ + subElements[0] = 1; + subElements[1] = 1; + elementsInSub = 2; + + /* Row Data */ + reader = 0; + do { + if ((((blocksPerRow & 1) != 0) || ((currentRow & 1) != 0)) + || ((currentRow == stackRows) + && (codeblocks != (currentRow * blocksPerRow)) + && ((((currentRow * blocksPerRow) - codeblocks) & 1)) != 0)) { + /* left to right */ + leftToRight = true; + i = 2 + (currentBlock * 21); + for (j = 0; j < 21; j++) { + if ((i + j) < patternWidth) { + subElements[j + (reader * 21) + 2] = elements[i + j]; + elementsInSub++; + } + } + } else { + /* right to left */ + leftToRight = false; + if ((currentRow * blocksPerRow) < codeblocks) { + /* a full row */ + i = 2 + (((currentRow * blocksPerRow) - reader - 1) * 21); + for (j = 0; j < 21; j++) { + if ((i + j) < patternWidth) { + subElements[(20 - j) + (reader * 21) + 2] = elements[i + j]; + elementsInSub++; + } + } + } else { + /* a partial row */ + k = ((currentRow * blocksPerRow) - codeblocks); + l = (currentRow * blocksPerRow) - reader - 1; + i = 2 + ((l - k) * 21); + for (j = 0; j < 21; j++) { + if ((i + j) < patternWidth) { + subElements[(20 - j) + (reader * 21) + 2] = elements[i + j]; + elementsInSub++; + } + } + } + } + reader++; + currentBlock++; + } while ((reader < blocksPerRow) && (currentBlock < codeblocks)); + + /* Row Stop */ + subElements[elementsInSub] = 1; + subElements[elementsInSub + 1] = 1; + elementsInSub += 2; + + pattern[symbolRow + compositeOffset] = ""; + black = true; + rowHeight[symbolRow + compositeOffset] = -1; + + if ((currentRow & 1) != 0) { + pattern[symbolRow + compositeOffset] = "0"; + black = false; + } else { + if ((currentRow == stackRows) + && (codeblocks != (currentRow * blocksPerRow)) + && ((((currentRow * blocksPerRow) - codeblocks) & 1) != 0)) { + /* Special case bottom row */ + specialCaseRow = true; + subElements[0] = 2; + pattern[symbolRow + compositeOffset] = "0"; + black = false; + } + } + + writer = 0; + + seperatorBinary = ""; + for (i = 0; i < elementsInSub; i++) { + pattern[symbolRow + compositeOffset] += (char) (subElements[i] + '0'); + for (j = 0; j < subElements[i]; j++) { + if (black) { + seperatorBinary += "0"; + } else { + seperatorBinary += "1"; + } + } + + black = !(black); + writer += subElements[i]; + } + seperatorBinary = "0000" + seperatorBinary.substring(4, writer - 4); + for (j = 0; j < reader; j++) { + k = (49 * j) + (specialCaseRow ? 19 : 18); + if (leftToRight) { + for (i = 0; i < 15; i++) { + if ((seperatorBinary.charAt(i + k - 1) == '1') + && (seperatorBinary.charAt(i + k) == '1')) { + seperatorBinary = seperatorBinary.substring(0, (i + k)) + + "0" + seperatorBinary.substring(i + k + 1); + } + } + } else { + for (i = 14; i >= 0; i--) { + if ((seperatorBinary.charAt(i + k + 1) == '1') + && (seperatorBinary.charAt(i + k) == '1')) { + seperatorBinary = seperatorBinary.substring(0, (i + k)) + + "0" + seperatorBinary.substring(i + k + 1); + } + } + } + } + seperatorPattern = bin2pat(seperatorBinary); + + if ((currentRow == 1) && linkageFlag) { + // Add composite code seperator + rowHeight[0] = 1; + pattern[0] = seperatorPattern; + } + + if (currentRow != 1) { + /* middle separator pattern (above current row) */ + pattern[symbolRow - 2 + compositeOffset] = "05"; + for (j = 5; j < (49 * blocksPerRow); j += 2) { + pattern[symbolRow - 2 + compositeOffset] += "11"; + } + rowHeight[symbolRow - 2 + compositeOffset] = 1; + /* bottom separator pattern (above current row) */ + rowHeight[symbolRow - 1 + compositeOffset] = 1; + pattern[symbolRow - 1 + compositeOffset] = seperatorPattern; + } + + if (currentRow != stackRows) { + rowHeight[symbolRow + 1 + compositeOffset] = 1; + pattern[symbolRow + 1 + compositeOffset] = seperatorPattern; + } + + symbolRow += 4; + } + readable = new StringBuilder(); + rowCount += compositeOffset; + } + + plotSymbol(); + return true; + } + + private boolean calculateBinaryString() { + /* Handles all data encodation from section 7.2.5 of ISO/IEC 24724 */ + EncodeMode lastMode = EncodeMode.NUMERIC; + int encodingMethod, i, j, readPosn; + boolean latch; + int remainder, d1, d2, value; + StringBuilder padstring; + double weight; + int groupVal; + int currentLength; + String patch; + + readPosn = 0; + + /* Decide whether a compressed data field is required and if so what + method to use - method 2 = no compressed data field */ + + if ((source.length() >= 16) && ((source.charAt(0) == '0') + && (source.charAt(1) == '1'))) { + /* (01) and other AIs */ + encodingMethod = 1; + } else { + /* any AIs */ + encodingMethod = 2; + } + + if (((source.length() >= 20) && (encodingMethod == 1)) + && ((source.charAt(2) == '9') && (source.charAt(16) == '3'))) { + /* Possibly encoding method > 2 */ + + if ((source.length() >= 26) && (source.charAt(17) == '1')) { + /* Methods 3, 7, 9, 11 and 13 */ + + if (source.charAt(18) == '0') { + /* (01) and (310x) */ + /* In kilos */ + + weight = 0.0; + for (i = 0; i < 6; i++) { + weight *= 10; + weight += (source.charAt(20 + i) - '0'); + } + + if (weight < 99999.0) { /* Maximum weight = 99999 */ + + if ((source.charAt(19) == '3') && (source.length() == 26)) { + /* (01) and (3103) */ + weight /= 1000.0; + + if (weight <= 32.767) { + encodingMethod = 3; + } + } + + if (source.length() == 34) { + if ((source.charAt(26) == '1') && (source.charAt(27) == '1')) { + /* (01), (310x) and (11) - metric weight and production date */ + encodingMethod = 7; + } + + if ((source.charAt(26) == '1') && (source.charAt(27) == '3')) { + /* (01), (310x) and (13) - metric weight and packaging date */ + encodingMethod = 9; + } + + if ((source.charAt(26) == '1') && (source.charAt(27) == '5')) { + /* (01), (310x) and (15) - metric weight and "best before" date */ + encodingMethod = 11; + } + + if ((source.charAt(26) == '1') && (source.charAt(27) == '7')) { + /* (01), (310x) and (17) - metric weight and expiration date */ + encodingMethod = 13; + } + } + } + } + } + + if ((source.length() >= 26) && (source.charAt(17) == '2')) { + /* Methods 4, 8, 10, 12 and 14 */ + + if (source.charAt(18) == '0') { + /* (01) and (320x) */ + /* In pounds */ + + weight = 0.0; + for (i = 0; i < 6; i++) { + weight *= 10; + weight += (source.charAt(20 + i) - '0'); + } + + if (weight < 99999.0) { /* Maximum weight = 99999 */ + + if (((source.charAt(19) == '2') || (source.charAt(19) == '3')) + && (source.length() == 26)) { + /* (01) and (3202)/(3203) */ + + if (source.charAt(19) == '3') { + weight /= 1000.0; + if (weight <= 22.767) { + encodingMethod = 4; + } + } else { + weight /= 100.0; + if (weight <= 99.99) { + encodingMethod = 4; + } + } + + } + + if (source.length() == 34) { + if ((source.charAt(26) == '1') && (source.charAt(27) == '1')) { + /* (01), (320x) and (11) - English weight and production date */ + encodingMethod = 8; + } + + if ((source.charAt(26) == '1') && (source.charAt(27) == '3')) { + /* (01), (320x) and (13) - English weight and packaging date */ + encodingMethod = 10; + } + + if ((source.charAt(26) == '1') && (source.charAt(27) == '5')) { + /* (01), (320x) and (15) - English weight and "best before" date */ + encodingMethod = 12; + } + + if ((source.charAt(26) == '1') && (source.charAt(27) == '7')) { + /* (01), (320x) and (17) - English weight and expiration date */ + encodingMethod = 14; + } + } + } + } + } + + if (source.charAt(17) == '9') { + /* Methods 5 and 6 */ + if ((source.charAt(18) == '2') && ((source.charAt(19) >= '0') + && (source.charAt(19) <= '3'))) { + /* (01) and (392x) */ + encodingMethod = 5; + } + if ((source.charAt(18) == '3') && ((source.charAt(19) >= '0') + && (source.charAt(19) <= '3'))) { + /* (01) and (393x) */ + encodingMethod = 6; + } + } + } + + encodeInfo.append("Encoding Method: ").append(Integer.toString(encodingMethod)).append("\n"); + switch (encodingMethod) { /* Encoding method - Table 10 */ + case 1: + binaryString.append("1XX"); + readPosn = 16; + break; + case 2: + binaryString.append("00XX"); + readPosn = 0; + break; + case 3: + binaryString.append("0100"); + readPosn = source.length(); + break; + case 4: + binaryString.append("0101"); + readPosn = source.length(); + break; + case 5: + binaryString.append("01100XX"); + readPosn = 20; + break; + case 6: + binaryString.append("01101XX"); + readPosn = 23; + break; + case 7: + binaryString.append("0111000"); + readPosn = source.length(); + break; + case 8: + binaryString.append("0111001"); + readPosn = source.length(); + break; + case 9: + binaryString.append("0111010"); + readPosn = source.length(); + break; + case 10: + binaryString.append("0111011"); + readPosn = source.length(); + break; + case 11: + binaryString.append("0111100"); + readPosn = source.length(); + break; + case 12: + binaryString.append("0111101"); + readPosn = source.length(); + break; + case 13: + binaryString.append("0111110"); + readPosn = source.length(); + break; + case 14: + binaryString.append("0111111"); + readPosn = source.length(); + break; + } + + + /* Variable length symbol bit field is just given a place holder (XX) + for the time being */ + + /* Verify that the data to be placed in the compressed data field is all + numeric data before carrying out compression */ + for (i = 0; i < readPosn; i++) { + if ((source.charAt(i) < '0') || (source.charAt(i) > '9')) { + if ((source.charAt(i) != '[') && (source.charAt(i) != ']')) { + /* Something is wrong */ + errorMsg.append("Invalid characters in input data"); + return false; + } + } + } + + /* Now encode the compressed data field */ + + if (encodingMethod == 1) { + /* Encoding method field "1" - general item identification data */ + groupVal = source.charAt(2) - '0'; + + for (j = 0; j < 4; j++) { + if ((groupVal & (0x08 >> j)) == 0) { + binaryString.append("0"); + } else { + binaryString.append("1"); + } + } + + for (i = 1; i < 5; i++) { + groupVal = 100 * (source.charAt(i * 3) - '0'); + groupVal += 10 * (source.charAt((i * 3) + 1) - '0'); + groupVal += source.charAt((i * 3) + 2) - '0'; + + for (j = 0; j < 10; j++) { + if ((groupVal & (0x200 >> j)) == 0) { + binaryString.append("0"); + } else { + binaryString.append("1"); + } + } + } + } + + if (encodingMethod == 3) { + /* Encoding method field "0100" - variable weight item + (0,001 kilogram icrements) */ + + for (i = 1; i < 5; i++) { + groupVal = 100 * (source.charAt(i * 3) - '0'); + groupVal += 10 * (source.charAt((i * 3) + 1) - '0'); + groupVal += (source.charAt((i * 3) + 2) - '0'); + + for (j = 0; j < 10; j++) { + if ((groupVal & (0x200 >> j)) == 0) { + binaryString.append("0"); + } else { + binaryString.append("1"); + } + } + } + + groupVal = 0; + for (i = 0; i < 6; i++) { + groupVal *= 10; + groupVal += source.charAt(20 + i) - '0'; + } + + for (j = 0; j < 15; j++) { + if ((groupVal & (0x4000 >> j)) == 0) { + binaryString.append("0"); + } else { + binaryString.append("1"); + } + } + } + + if (encodingMethod == 4) { + /* Encoding method field "0101" - variable weight item (0,01 or + 0,001 pound increment) */ + + for (i = 1; i < 5; i++) { + groupVal = 100 * (source.charAt(i * 3) - '0'); + groupVal += 10 * (source.charAt((i * 3) + 1) - '0'); + groupVal += (source.charAt((i * 3) + 2) - '0'); + + for (j = 0; j < 10; j++) { + if ((groupVal & (0x200 >> j)) == 0) { + binaryString.append("0"); + } else { + binaryString.append("1"); + } + } + } + + + groupVal = 0; + for (i = 0; i < 6; i++) { + groupVal *= 10; + groupVal += source.charAt(20 + i) - '0'; + } + + if (source.charAt(19) == '3') { + groupVal = groupVal + 10000; + } + + for (j = 0; j < 15; j++) { + if ((groupVal & (0x4000 >> j)) == 0) { + binaryString.append("0"); + } else { + binaryString.append("1"); + } + } + } + + if (encodingMethod >= 7) { + /* Encoding method fields "0111000" through "0111111" - variable + weight item plus date */ + + for (i = 1; i < 5; i++) { + groupVal = 100 * (source.charAt(i * 3) - '0'); + groupVal += 10 * (source.charAt((i * 3) + 1) - '0'); + groupVal += (source.charAt((i * 3) + 2) - '0'); + + for (j = 0; j < 10; j++) { + if ((groupVal & (0x200 >> j)) == 0) { + binaryString.append("0"); + } else { + binaryString.append("1"); + } + } + } + + groupVal = source.charAt(19) - '0'; + + for (i = 0; i < 5; i++) { + groupVal *= 10; + groupVal += source.charAt(21 + i) - '0'; + } + + for (j = 0; j < 20; j++) { + if ((groupVal & (0x80000 >> j)) == 0) { + binaryString.append("0"); + } else { + binaryString.append("1"); + } + } + + if (source.length() == 34) { + /* Date information is included */ + groupVal = ((10 * (source.charAt(28) - '0')) + + (source.charAt(29) - '0')) * 384; + groupVal += (((10 * (source.charAt(30) - '0')) + + (source.charAt(31) - '0')) - 1) * 32; + groupVal += (10 * (source.charAt(32) - '0')) + + (source.charAt(33) - '0'); + } else { + groupVal = 38400; + } + + for (j = 0; j < 16; j++) { + if ((groupVal & (0x8000 >> j)) == 0) { + binaryString.append("0"); + } else { + binaryString.append("1"); + } + } + } + + if (encodingMethod == 5) { + /* Encoding method field "01100" - variable measure item and price */ + + for (i = 1; i < 5; i++) { + groupVal = 100 * (source.charAt(i * 3) - '0'); + groupVal += 10 * (source.charAt((i * 3) + 1) - '0'); + groupVal += (source.charAt((i * 3) + 2) - '0'); + + for (j = 0; j < 10; j++) { + if ((groupVal & (0x200 >> j)) == 0) { + binaryString.append("0"); + } else { + binaryString.append("1"); + } + } + } + + switch (source.charAt(19)) { + case '0': + binaryString.append("00"); + break; + case '1': + binaryString.append("01"); + break; + case '2': + binaryString.append("10"); + break; + case '3': + binaryString.append("11"); + break; + } + } + + if (encodingMethod == 6) { + /* Encoding method "01101" - variable measure item and price with ISO 4217 + Currency Code */ + + for (i = 1; i < 5; i++) { + groupVal = 100 * (source.charAt(i * 3) - '0'); + groupVal += 10 * (source.charAt((i * 3) + 1) - '0'); + groupVal += (source.charAt((i * 3) + 2) - '0'); + + for (j = 0; j < 10; j++) { + if ((groupVal & (0x200 >> j)) == 0) { + binaryString.append("0"); + } else { + binaryString.append("1"); + } + } + } + + switch (source.charAt(19)) { + case '0': + binaryString.append("00"); + break; + case '1': + binaryString.append("01"); + break; + case '2': + binaryString.append("10"); + break; + case '3': + binaryString.append("11"); + break; + } + + groupVal = 0; + for (i = 0; i < 3; i++) { + groupVal *= 10; + groupVal += source.charAt(20 + i) - '0'; + } + + for (j = 0; j < 10; j++) { + if ((groupVal & (0x200 >> j)) == 0) { + binaryString.append("0"); + } else { + binaryString.append("1"); + } + } + } + + /* The compressed data field has been processed if appropriate - the + rest of the data (if any) goes into a general-purpose data compaction field */ + + generalField = source.substring(readPosn); + generalFieldType = new EncodeMode[generalField.length()]; + + if (generalField.length() != 0) { + latch = false; + for (i = 0; i < generalField.length(); i++) { + /* Table 13 - ISO/IEC 646 encodation */ + if ((generalField.charAt(i) < ' ') || (generalField.charAt(i) > 'z')) { + generalFieldType[i] = EncodeMode.INVALID_CHAR; + latch = true; + } else { + generalFieldType[i] = EncodeMode.ISOIEC; + } + + if (generalField.charAt(i) == '#') { + generalFieldType[i] = EncodeMode.INVALID_CHAR; + latch = true; + } + if (generalField.charAt(i) == '$') { + generalFieldType[i] = EncodeMode.INVALID_CHAR; + latch = true; + } + if (generalField.charAt(i) == '@') { + generalFieldType[i] = EncodeMode.INVALID_CHAR; + latch = true; + } + if (generalField.charAt(i) == 92) { + generalFieldType[i] = EncodeMode.INVALID_CHAR; + latch = true; + } + if (generalField.charAt(i) == '^') { + generalFieldType[i] = EncodeMode.INVALID_CHAR; + latch = true; + } + if (generalField.charAt(i) == 96) { + generalFieldType[i] = EncodeMode.INVALID_CHAR; + latch = true; + } + + /* Table 12 - Alphanumeric encodation */ + if ((generalField.charAt(i) >= 'A') && (generalField.charAt(i) <= 'Z')) { + generalFieldType[i] = EncodeMode.ALPHA_OR_ISO; + } + if (generalField.charAt(i) == '*') { + generalFieldType[i] = EncodeMode.ALPHA_OR_ISO; + } + if (generalField.charAt(i) == ',') { + generalFieldType[i] = EncodeMode.ALPHA_OR_ISO; + } + if (generalField.charAt(i) == '-') { + generalFieldType[i] = EncodeMode.ALPHA_OR_ISO; + } + if (generalField.charAt(i) == '.') { + generalFieldType[i] = EncodeMode.ALPHA_OR_ISO; + } + if (generalField.charAt(i) == '/') { + generalFieldType[i] = EncodeMode.ALPHA_OR_ISO; + } + + /* Numeric encodation */ + if ((generalField.charAt(i) >= '0') && (generalField.charAt(i) <= '9')) { + generalFieldType[i] = EncodeMode.ANY_ENC; + } + if (generalField.charAt(i) == '[') { + /* FNC1 can be encoded in any system */ + generalFieldType[i] = EncodeMode.ANY_ENC; + } + } + + if (latch) { + errorMsg.append("Invalid characters in input data"); + return false; + } + + for (i = 0; i < generalField.length() - 1; i++) { + if ((generalFieldType[i] == EncodeMode.ISOIEC) + && (generalField.charAt(i + 1) == '[')) { + generalFieldType[i + 1] = EncodeMode.ISOIEC; + } + } + + for (i = 0; i < generalField.length() - 1; i++) { + if ((generalFieldType[i] == EncodeMode.ALPHA_OR_ISO) + && (generalField.charAt(i + 1) == '[')) { + generalFieldType[i + 1] = EncodeMode.ALPHA_OR_ISO; + } + } + + latch = applyGeneralFieldRules(); + + /* Set initial mode if not NUMERIC */ + if (generalFieldType[0] == EncodeMode.ALPHA) { + binaryString.append("0000"); /* Alphanumeric latch */ + lastMode = EncodeMode.ALPHA; + } + if (generalFieldType[0] == EncodeMode.ISOIEC) { + binaryString.append("0000"); /* Alphanumeric latch */ + binaryString.append("00100"); /* ISO/IEC 646 latch */ + lastMode = EncodeMode.ISOIEC; + } + + i = 0; + do { + switch (generalFieldType[i]) { + case NUMERIC: + + if (lastMode != EncodeMode.NUMERIC) { + binaryString.append("000"); /* Numeric latch */ + } + + if (generalField.charAt(i) != '[') { + d1 = generalField.charAt(i) - '0'; + } else { + d1 = 10; + } + + if (generalField.charAt(i + 1) != '[') { + d2 = generalField.charAt(i + 1) - '0'; + } else { + d2 = 10; + } + + value = (11 * d1) + d2 + 8; + + for (j = 0; j < 7; j++) { + if ((value & (0x40 >> j)) != 0) { + binaryString.append("1"); + } else { + binaryString.append("0"); + } + } + + i += 2; + lastMode = EncodeMode.NUMERIC; + break; + + case ALPHA: + if (i != 0) { + if (lastMode == EncodeMode.NUMERIC) { + binaryString.append("0000"); /* Alphanumeric latch */ + } + if (lastMode == EncodeMode.ISOIEC) { + binaryString.append("00100"); /* Alphanumeric latch */ + } + } + + if ((generalField.charAt(i) >= '0') && (generalField.charAt(i) <= '9')) { + + value = generalField.charAt(i) - 43; + + for (j = 0; j < 5; j++) { + if ((value & (0x10 >> j)) != 0) { + binaryString.append("1"); + } else { + binaryString.append("0"); + } + } + } + + if ((generalField.charAt(i) >= 'A') && (generalField.charAt(i) <= 'Z')) { + + value = generalField.charAt(i) - 33; + + for (j = 0; j < 6; j++) { + if ((value & (0x20 >> j)) != 0) { + binaryString.append("1"); + } else { + binaryString.append("0"); + } + } + } + + lastMode = EncodeMode.ALPHA; + if (generalField.charAt(i) == '[') { + binaryString.append("01111"); + lastMode = EncodeMode.NUMERIC; + } /* FNC1/Numeric latch */ + if (generalField.charAt(i) == '*') { + binaryString.append("111010"); /* asterisk */ + } + if (generalField.charAt(i) == ',') { + binaryString.append("111011"); /* comma */ + } + if (generalField.charAt(i) == '-') { + binaryString.append("111100"); /* minus or hyphen */ + } + if (generalField.charAt(i) == '.') { + binaryString.append("111101"); /* period or full stop */ + } + if (generalField.charAt(i) == '/') { + binaryString.append("111110"); /* slash or solidus */ + } + + i++; + break; + + case ISOIEC: + if (i != 0) { + if (lastMode == EncodeMode.NUMERIC) { + binaryString.append("0000"); /* Alphanumeric latch */ + binaryString.append("00100"); /* ISO/IEC 646 latch */ + } + if (lastMode == EncodeMode.ALPHA) { + binaryString.append("00100"); /* ISO/IEC 646 latch */ + } + } + + if ((generalField.charAt(i) >= '0') + && (generalField.charAt(i) <= '9')) { + + value = generalField.charAt(i) - 43; + + for (j = 0; j < 5; j++) { + if ((value & (0x10 >> j)) != 0) { + binaryString.append("1"); + } else { + binaryString.append("0"); + } + } + } + + if ((generalField.charAt(i) >= 'A') + && (generalField.charAt(i) <= 'Z')) { + + value = generalField.charAt(i) - 1; + + for (j = 0; j < 7; j++) { + if ((value & (0x40 >> j)) != 0) { + binaryString.append("1"); + } else { + binaryString.append("0"); + } + } + } + + if ((generalField.charAt(i) >= 'a') + && (generalField.charAt(i) <= 'z')) { + + value = generalField.charAt(i) - 7; + + for (j = 0; j < 7; j++) { + if ((value & (0x40 >> j)) != 0) { + binaryString.append("1"); + } else { + binaryString.append("0"); + } + } + } + + lastMode = EncodeMode.ISOIEC; + if (generalField.charAt(i) == '[') { + binaryString.append("01111"); + lastMode = EncodeMode.NUMERIC; + } /* FNC1/Numeric latch */ + if (generalField.charAt(i) == '!') { + binaryString.append("11101000"); /* exclamation mark */ + } + if (generalField.charAt(i) == 34) { + binaryString.append("11101001"); /* quotation mark */ + } + if (generalField.charAt(i) == 37) { + binaryString.append("11101010"); /* percent sign */ + } + if (generalField.charAt(i) == '&') { + binaryString.append("11101011"); /* ampersand */ + } + if (generalField.charAt(i) == 39) { + binaryString.append("11101100"); /* apostrophe */ + } + if (generalField.charAt(i) == '(') { + binaryString.append("11101101"); /* left parenthesis */ + } + if (generalField.charAt(i) == ')') { + binaryString.append("11101110"); /* right parenthesis */ + } + if (generalField.charAt(i) == '*') { + binaryString.append("11101111"); /* asterisk */ + } + if (generalField.charAt(i) == '+') { + binaryString.append("11110000"); /* plus sign */ + } + if (generalField.charAt(i) == ',') { + binaryString.append("11110001"); /* comma */ + } + if (generalField.charAt(i) == '-') { + binaryString.append("11110010"); /* minus or hyphen */ + } + if (generalField.charAt(i) == '.') { + binaryString.append("11110011"); /* period or full stop */ + } + if (generalField.charAt(i) == '/') { + binaryString.append("11110100"); /* slash or solidus */ + } + if (generalField.charAt(i) == ':') { + binaryString.append("11110101"); /* colon */ + } + if (generalField.charAt(i) == ';') { + binaryString.append("11110110"); /* semicolon */ + } + if (generalField.charAt(i) == '<') { + binaryString.append("11110111"); /* less-than sign */ + } + if (generalField.charAt(i) == '=') { + binaryString.append("11111000"); /* equals sign */ + } + if (generalField.charAt(i) == '>') { + binaryString.append("11111001"); /* greater-than sign */ + } + if (generalField.charAt(i) == '?') { + binaryString.append("11111010"); /* question mark */ + } + if (generalField.charAt(i) == '_') { + binaryString.append("11111011"); /* underline or low line */ + } + if (generalField.charAt(i) == ' ') { + binaryString.append("11111100"); /* space */ + } + + i++; + break; + } + currentLength = i; + if (latch) { + currentLength++; + } + } while (currentLength < generalField.length()); + + remainder = calculateRemainder(binaryString.length()); + + if (latch) { + /* There is still one more numeric digit to encode */ + + if (lastMode == EncodeMode.NUMERIC) { + if ((remainder >= 4) && (remainder <= 6)) { + value = generalField.charAt(i) - '0'; + value++; + + for (j = 0; j < 4; j++) { + if ((value & (0x08 >> j)) != 0) { + binaryString.append("1"); + } else { + binaryString.append("0"); + } + } + } else { + d1 = generalField.charAt(i) - '0'; + d2 = 10; + + value = (11 * d1) + d2 + 8; + + for (j = 0; j < 7; j++) { + if ((value & (0x40 >> j)) != 0) { + binaryString.append("1"); + } else { + binaryString.append("0"); + } + } + } + } else { + value = generalField.charAt(i) - 43; + + for (j = 0; j < 5; j++) { + if ((value & (0x10 >> j)) != 0) { + binaryString.append("1"); + } else { + binaryString.append("0"); + } + } + } + } + } + + if (binaryString.length() > 252) { + errorMsg.append("Input too long"); + return false; + } + + remainder = calculateRemainder(binaryString.length()); + + /* Now add padding to binary string (7.2.5.5.4) */ + i = remainder; + if ((generalField.length() != 0) && (lastMode == EncodeMode.NUMERIC)) { + padstring = new StringBuilder("0000"); + i -= 4; + } else { + padstring = new StringBuilder(); + } + for (; i > 0; i -= 5) { + padstring.append("00100"); + } + + binaryString.append(padstring.substring(0, remainder)); + + /* Patch variable length symbol bit field */ + patch = ""; + if ((((binaryString.length() / 12) + 1) & 1) == 0) { + patch += "0"; + } else { + patch += "1"; + } + if (binaryString.length() <= 156) { + patch += "0"; + } else { + patch += "1"; + } + + if (encodingMethod == 1) { + binaryString = new StringBuilder(binaryString.substring(0, 2)) + .append(patch) + .append(binaryString.substring(4)); + } + if (encodingMethod == 2) { + binaryString = new StringBuilder(binaryString.substring(0, 3)) + .append(patch) + .append(binaryString.substring(5)); + } + if ((encodingMethod == 5) || (encodingMethod == 6)) { + binaryString = new StringBuilder(binaryString.substring(0, 6)) + .append(patch) + .append(binaryString.substring(8)); + } + + encodeInfo.append("Binary length: ").append(Integer.toString(binaryString.length())).append("\n"); + displayBinaryString(); + return true; + } + + private void displayBinaryString() { + int i, nibble; + /* Display binary string as hexadecimal */ + + encodeInfo.append("Binary String: "); + nibble = 0; + for (i = 0; i < binaryString.length(); i++) { + switch (i % 4) { + case 0: + if (binaryString.charAt(i) == '1') { + nibble += 8; + } + break; + case 1: + if (binaryString.charAt(i) == '1') { + nibble += 4; + } + break; + case 2: + if (binaryString.charAt(i) == '1') { + nibble += 2; + } + break; + case 3: + if (binaryString.charAt(i) == '1') { + nibble += 1; + } + encodeInfo.append(Integer.toHexString(nibble)); + nibble = 0; + break; + } + } + + if ((binaryString.length() % 4) != 0) { + encodeInfo.append(Integer.toHexString(nibble)); + } + encodeInfo.append("\n"); + } + + private boolean applyGeneralFieldRules() { + /* Attempts to apply encoding rules from secions 7.2.5.5.1 to 7.2.5.5.3 + of ISO/IEC 24724:2006 */ + + int blockCount, i, j, k; + EncodeMode current, next, last; + int[] blockLength = new int[200]; + EncodeMode[] blockType = new EncodeMode[200]; + + blockCount = 0; + + blockLength[blockCount] = 1; + blockType[blockCount] = generalFieldType[0]; + + for (i = 1; i < generalField.length(); i++) { + current = generalFieldType[i]; + last = generalFieldType[i - 1]; + + if (current == last) { + blockLength[blockCount] = blockLength[blockCount] + 1; + } else { + blockCount++; + blockLength[blockCount] = 1; + blockType[blockCount] = generalFieldType[i]; + } + } + + blockCount++; + + for (i = 0; i < blockCount; i++) { + current = blockType[i]; + next = blockType[i + 1]; + + if ((current == EncodeMode.ISOIEC) && (i != (blockCount - 1))) { + if ((next == EncodeMode.ANY_ENC) && (blockLength[i + 1] >= 4)) { + blockType[i + 1] = EncodeMode.NUMERIC; + } + if ((next == EncodeMode.ANY_ENC) && (blockLength[i + 1] < 4)) { + blockType[i + 1] = EncodeMode.ISOIEC; + } + if ((next == EncodeMode.ALPHA_OR_ISO) && (blockLength[i + 1] >= 5)) { + blockType[i + 1] = EncodeMode.ALPHA; + } + if ((next == EncodeMode.ALPHA_OR_ISO) && (blockLength[i + 1] < 5)) { + blockType[i + 1] = EncodeMode.ISOIEC; + } + } + + if (current == EncodeMode.ALPHA_OR_ISO) { + blockType[i] = EncodeMode.ALPHA; + current = EncodeMode.ALPHA; + } + + if ((current == EncodeMode.ALPHA) && (i != (blockCount - 1))) { + if ((next == EncodeMode.ANY_ENC) && (blockLength[i + 1] >= 6)) { + blockType[i + 1] = EncodeMode.NUMERIC; + } + if ((next == EncodeMode.ANY_ENC) && (blockLength[i + 1] < 6)) { + if ((i == blockCount - 2) && (blockLength[i + 1] >= 4)) { + blockType[i + 1] = EncodeMode.NUMERIC; + } else { + blockType[i + 1] = EncodeMode.ALPHA; + } + } + } + + if (current == EncodeMode.ANY_ENC) { + blockType[i] = EncodeMode.NUMERIC; + } + } + + if (blockCount > 1) { + i = 1; + while (i < blockCount) { + if (blockType[i - 1] == blockType[i]) { + /* bring together */ + blockLength[i - 1] = blockLength[i - 1] + blockLength[i]; + j = i + 1; + + /* decreace the list */ + while (j < blockCount) { + blockLength[j - 1] = blockLength[j]; + blockType[j - 1] = blockType[j]; + j++; + } + blockCount--; + i--; + } + i++; + } + } + + for (i = 0; i < blockCount - 1; i++) { + if ((blockType[i] == EncodeMode.NUMERIC) && ((blockLength[i] & 1) != 0)) { + /* Odd size numeric block */ + blockLength[i] = blockLength[i] - 1; + blockLength[i + 1] = blockLength[i + 1] + 1; + } + } + + j = 0; + for (i = 0; i < blockCount; i++) { + for (k = 0; k < blockLength[i]; k++) { + generalFieldType[j] = blockType[i]; + j++; + } + } + + /* If the last block is numeric and an odd size, further + processing needs to be done outside this procedure */ + return (blockType[blockCount - 1] == EncodeMode.NUMERIC) + && ((blockLength[blockCount - 1] & 1) != 0); + } + + private int getCombinations(int n, int r) { + int i, j; + int maxDenom, minDenom; + int val; + + if (n - r > r) { + minDenom = r; + maxDenom = n - r; + } else { + minDenom = n - r; + maxDenom = r; + } + val = 1; + j = 1; + for (i = n; i > maxDenom; i--) { + val *= i; + if (j <= minDenom) { + val /= j; + j++; + } + } + for (; j <= minDenom; j++) { + val /= j; + } + return (val); + } + + private void getWidths(int val, int n, int elements, int maxWidth, int noNarrow) { + int bar; + int elmWidth; + int mxwElement; + int subVal, lessVal; + int narrowMask = 0; + for (bar = 0; bar < elements - 1; bar++) { + for (elmWidth = 1, narrowMask |= (1 << bar); ; + elmWidth++, narrowMask &= ~(1 << bar)) { + /* get all combinations */ + subVal = getCombinations(n - elmWidth - 1, elements - bar - 2); + /* less combinations with no single-module element */ + if ((noNarrow == 0) && (narrowMask == 0) + && (n - elmWidth - (elements - bar - 1) >= elements - bar - 1)) { + subVal -= getCombinations(n - elmWidth - (elements - bar), + elements - bar - 2); + } + /* less combinations with elements > maxVal */ + if (elements - bar - 1 > 1) { + lessVal = 0; + for (mxwElement = n - elmWidth - (elements - bar - 2); + mxwElement > maxWidth; + mxwElement--) { + lessVal += getCombinations(n - elmWidth - mxwElement - 1, + elements - bar - 3); + } + subVal -= lessVal * (elements - 1 - bar); + } else if (n - elmWidth > maxWidth) { + subVal--; + } + val -= subVal; + if (val < 0) break; + } + val += subVal; + n -= elmWidth; + widths[bar] = elmWidth; + } + widths[bar] = n; + } + + private enum dbeMode { + UNSTACKED, STACKED + } + + private enum EncodeMode { + NUMERIC, ALPHA, ISOIEC, INVALID_CHAR, ANY_ENC, ALPHA_OR_ISO + } +} diff --git a/barcode/src/main/java/org/xbib/graphics/barcode/DataBarLimited.java b/barcode/src/main/java/org/xbib/graphics/barcode/DataBarLimited.java new file mode 100755 index 0000000..4163543 --- /dev/null +++ b/barcode/src/main/java/org/xbib/graphics/barcode/DataBarLimited.java @@ -0,0 +1,476 @@ +package org.xbib.graphics.barcode; + +import java.math.BigInteger; + +/** + * Implements GS1 DataBar Limited according to ISO/IEC 24724:2011. + * Input data should be a 12 digit Global Trade Identification Number + * without check digit or Application Identifier [01]. + */ +public class DataBarLimited extends Symbol { + + private static final int[] t_even_ltd = { + 28, 728, 6454, 203, 2408, 1, 16632 + }; + private static final int[] modules_odd_ltd = { + 17, 13, 9, 15, 11, 19, 7 + }; + private static final int[] modules_even_ltd = { + 9, 13, 17, 11, 15, 7, 19 + }; + private static final int[] widest_odd_ltd = { + 6, 5, 3, 5, 4, 8, 1 + }; + private static final int[] widest_even_ltd = { + 3, 4, 6, 4, 5, 1, 8 + }; + private static final int[] checksum_weight_ltd = { /* Table 7 */ + 1, 3, 9, 27, 81, 65, 17, 51, 64, 14, 42, 37, 22, 66, + 20, 60, 2, 6, 18, 54, 73, 41, 34, 13, 39, 28, 84, 74 + }; + private static final int[] finder_pattern_ltd = { + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 3, 3, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 2, 3, 2, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 3, 3, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 2, 1, 1, 3, 2, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 2, 1, 2, 3, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 3, 1, 1, 3, 1, 1, 1, + 1, 1, 1, 1, 1, 2, 1, 1, 1, 1, 3, 2, 1, 1, + 1, 1, 1, 1, 1, 2, 1, 1, 1, 2, 3, 1, 1, 1, + 1, 1, 1, 1, 1, 2, 1, 2, 1, 1, 3, 1, 1, 1, + 1, 1, 1, 1, 1, 3, 1, 1, 1, 1, 3, 1, 1, 1, + 1, 1, 1, 2, 1, 1, 1, 1, 1, 1, 3, 2, 1, 1, + 1, 1, 1, 2, 1, 1, 1, 1, 1, 2, 3, 1, 1, 1, + 1, 1, 1, 2, 1, 1, 1, 2, 1, 1, 3, 1, 1, 1, + 1, 1, 1, 2, 1, 2, 1, 1, 1, 1, 3, 1, 1, 1, + 1, 1, 1, 3, 1, 1, 1, 1, 1, 1, 3, 1, 1, 1, + 1, 2, 1, 1, 1, 1, 1, 1, 1, 1, 3, 2, 1, 1, + 1, 2, 1, 1, 1, 1, 1, 1, 1, 2, 3, 1, 1, 1, + 1, 2, 1, 1, 1, 1, 1, 2, 1, 1, 3, 1, 1, 1, + 1, 2, 1, 1, 1, 2, 1, 1, 1, 1, 3, 1, 1, 1, + 1, 2, 1, 2, 1, 1, 1, 1, 1, 1, 3, 1, 1, 1, + 1, 3, 1, 1, 1, 1, 1, 1, 1, 1, 3, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 2, 1, 2, 3, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 2, 2, 2, 2, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 2, 3, 2, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 2, 2, 1, 2, 2, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 2, 2, 2, 2, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 3, 2, 1, 2, 1, 1, 1, + 1, 1, 1, 1, 1, 2, 1, 1, 2, 1, 2, 2, 1, 1, + 1, 1, 1, 1, 1, 2, 1, 1, 2, 2, 2, 1, 1, 1, + 1, 1, 1, 1, 1, 2, 1, 2, 2, 1, 2, 1, 1, 1, + 1, 1, 1, 1, 1, 3, 1, 1, 2, 1, 2, 1, 1, 1, + 1, 1, 1, 2, 1, 1, 1, 1, 2, 1, 2, 2, 1, 1, + 1, 1, 1, 2, 1, 1, 1, 1, 2, 2, 2, 1, 1, 1, + 1, 1, 1, 2, 1, 1, 1, 2, 2, 1, 2, 1, 1, 1, + 1, 1, 1, 2, 1, 2, 1, 1, 2, 1, 2, 1, 1, 1, + 1, 1, 1, 3, 1, 1, 1, 1, 2, 1, 2, 1, 1, 1, + 1, 2, 1, 1, 1, 1, 1, 1, 2, 1, 2, 2, 1, 1, + 1, 2, 1, 1, 1, 1, 1, 1, 2, 2, 2, 1, 1, 1, + 1, 2, 1, 1, 1, 1, 1, 2, 2, 1, 2, 1, 1, 1, + 1, 2, 1, 1, 1, 2, 1, 1, 2, 1, 2, 1, 1, 1, + 1, 2, 1, 2, 1, 1, 1, 1, 2, 1, 2, 1, 1, 1, + 1, 3, 1, 1, 1, 1, 1, 1, 2, 1, 2, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 3, 1, 1, 3, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 3, 2, 1, 2, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 2, 3, 1, 1, 2, 1, 1, + 1, 1, 1, 2, 1, 1, 1, 1, 3, 1, 1, 2, 1, 1, + 1, 2, 1, 1, 1, 1, 1, 1, 3, 1, 1, 2, 1, 1, + 1, 1, 1, 1, 1, 1, 2, 1, 1, 1, 2, 3, 1, 1, + 1, 1, 1, 1, 1, 1, 2, 1, 1, 2, 2, 2, 1, 1, + 1, 1, 1, 1, 1, 1, 2, 1, 1, 3, 2, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 2, 2, 1, 1, 2, 2, 1, 1, + 1, 1, 1, 2, 1, 1, 2, 1, 1, 1, 2, 2, 1, 1, + 1, 1, 1, 2, 1, 1, 2, 1, 1, 2, 2, 1, 1, 1, + 1, 1, 1, 2, 1, 1, 2, 2, 1, 1, 2, 1, 1, 1, + 1, 1, 1, 2, 1, 2, 2, 1, 1, 1, 2, 1, 1, 1, + 1, 1, 1, 3, 1, 1, 2, 1, 1, 1, 2, 1, 1, 1, + 1, 2, 1, 1, 1, 1, 2, 1, 1, 1, 2, 2, 1, 1, + 1, 2, 1, 1, 1, 1, 2, 1, 1, 2, 2, 1, 1, 1, + 1, 2, 1, 2, 1, 1, 2, 1, 1, 1, 2, 1, 1, 1, + 1, 1, 1, 1, 2, 1, 1, 1, 1, 1, 2, 3, 1, 1, + 1, 1, 1, 1, 2, 1, 1, 1, 1, 2, 2, 2, 1, 1, + 1, 1, 1, 1, 2, 1, 1, 1, 1, 3, 2, 1, 1, 1, + 1, 1, 1, 1, 2, 1, 1, 2, 1, 1, 2, 2, 1, 1, + 1, 1, 1, 1, 2, 1, 1, 2, 1, 2, 2, 1, 1, 1, + 1, 1, 1, 1, 2, 2, 1, 1, 1, 1, 2, 2, 1, 1, + 1, 2, 1, 1, 2, 1, 1, 1, 1, 1, 2, 2, 1, 1, + 1, 2, 1, 1, 2, 1, 1, 1, 1, 2, 2, 1, 1, 1, + 1, 2, 1, 1, 2, 1, 1, 2, 1, 1, 2, 1, 1, 1, + 1, 2, 1, 1, 2, 2, 1, 1, 1, 1, 2, 1, 1, 1, + 1, 2, 1, 2, 2, 1, 1, 1, 1, 1, 2, 1, 1, 1, + 1, 3, 1, 1, 2, 1, 1, 1, 1, 1, 2, 1, 1, 1, + 1, 1, 2, 1, 1, 1, 1, 1, 1, 1, 2, 3, 1, 1, + 1, 1, 2, 1, 1, 1, 1, 1, 1, 2, 2, 2, 1, 1, + 1, 1, 2, 1, 1, 1, 1, 1, 1, 3, 2, 1, 1, 1, + 1, 1, 2, 1, 1, 1, 1, 2, 1, 1, 2, 2, 1, 1, + 1, 1, 2, 1, 1, 1, 1, 2, 1, 2, 2, 1, 1, 1, + 1, 1, 2, 1, 1, 1, 1, 3, 1, 1, 2, 1, 1, 1, + 1, 1, 2, 1, 1, 2, 1, 1, 1, 1, 2, 2, 1, 1, + 1, 1, 2, 1, 1, 2, 1, 1, 1, 2, 2, 1, 1, 1, + 1, 1, 2, 2, 1, 1, 1, 1, 1, 1, 2, 2, 1, 1, + 2, 1, 1, 1, 1, 1, 1, 1, 1, 2, 2, 2, 1, 1, + 2, 1, 1, 1, 1, 1, 1, 1, 1, 3, 2, 1, 1, 1, + 2, 1, 1, 1, 1, 1, 1, 2, 1, 1, 2, 2, 1, 1, + 2, 1, 1, 1, 1, 1, 1, 2, 1, 2, 2, 1, 1, 1, + 2, 1, 1, 1, 1, 1, 1, 3, 1, 1, 2, 1, 1, 1, + 2, 1, 1, 1, 1, 2, 1, 1, 1, 2, 2, 1, 1, 1, + 2, 1, 1, 1, 1, 2, 1, 2, 1, 1, 2, 1, 1, 1, + 2, 1, 1, 2, 1, 1, 1, 1, 1, 2, 2, 1, 1, 1, + 2, 1, 1, 1, 1, 1, 1, 1, 2, 2, 1, 2, 1, 1 /* ГОСТ ISO/IEC 24724-2011 Ñтраница 57 */ + }; + + private boolean linkageFlag; + private int[] widths = new int[8]; + + public DataBarLimited() { + linkageFlag = false; + } + + @Override + public void setDataType(DataType dummy) { + // Do nothing! + } + + protected void setLinkageFlag() { + linkageFlag = true; + } + + @Override + public boolean encode() { + BigInteger accum; + BigInteger left_reg; + BigInteger right_reg; + int left_group; + int right_group; + int i, j; + int left_character; + int right_character; + int left_odd; + int right_odd; + int left_even; + int right_even; + int[] left_widths = new int[14]; + int[] right_widths = new int[14]; + int checksum; + int[] check_elements = new int[14]; + int[] total_widths = new int[46]; + StringBuilder bin; + StringBuilder notbin; + boolean bar_latch; + int writer; + int check_digit; + int count = 0; + StringBuilder hrt; + int compositeOffset = 0; + + if (content.length() > 13) { + errorMsg.append("Input too long"); + return false; + } + + if (!(content.matches("[0-9]+?"))) { + errorMsg.append("Invalid characters in input"); + return false; + } + + if (content.length() == 13) { + if ((content.charAt(0) != '0') && (content.charAt(0) != '1')) { + errorMsg.append("Input out of range"); + return false; + } + } + + accum = new BigInteger(content); + + if (linkageFlag) { + /* Add symbol linkage flag */ + accum = accum.add(new BigInteger("2015133531096")); + } + + /* Calculate left and right pair values */ + left_reg = accum.divide(new BigInteger("2013571")); + right_reg = accum.mod(new BigInteger("2013571")); + + left_group = 0; + if (left_reg.compareTo(new BigInteger("183063")) > 0) { + left_group = 1; + } + if (left_reg.compareTo(new BigInteger("820063")) > 0) { + left_group = 2; + } + if (left_reg.compareTo(new BigInteger("1000775")) > 0) { + left_group = 3; + } + if (left_reg.compareTo(new BigInteger("1491020")) > 0) { + left_group = 4; + } + if (left_reg.compareTo(new BigInteger("1979844")) > 0) { + left_group = 5; + } + if (left_reg.compareTo(new BigInteger("1996938")) > 0) { + left_group = 6; + } + + right_group = 0; + if (right_reg.compareTo(new BigInteger("183063")) > 0) { + right_group = 1; + } + if (right_reg.compareTo(new BigInteger("820063")) > 0) { + right_group = 2; + } + if (right_reg.compareTo(new BigInteger("1000775")) > 0) { + right_group = 3; + } + if (right_reg.compareTo(new BigInteger("1491020")) > 0) { + right_group = 4; + } + if (right_reg.compareTo(new BigInteger("1979844")) > 0) { + right_group = 5; + } + if (right_reg.compareTo(new BigInteger("1996938")) > 0) { + right_group = 6; + } + + encodeInfo.append("Data Characters: ").append(Integer.toString(left_group + 1)).append(" ").append(Integer.toString(right_group + 1)).append("\n"); + + switch (left_group) { + case 1: + left_reg = left_reg.subtract(new BigInteger("183064")); + break; + case 2: + left_reg = left_reg.subtract(new BigInteger("820064")); + break; + case 3: + left_reg = left_reg.subtract(new BigInteger("1000776")); + break; + case 4: + left_reg = left_reg.subtract(new BigInteger("1491021")); + break; + case 5: + left_reg = left_reg.subtract(new BigInteger("1979845")); + break; + case 6: + left_reg = left_reg.subtract(new BigInteger("1996939")); + break; + } + + switch (right_group) { + case 1: + right_reg = right_reg.subtract(new BigInteger("183064")); + break; + case 2: + right_reg = right_reg.subtract(new BigInteger("820064")); + break; + case 3: + right_reg = right_reg.subtract(new BigInteger("1000776")); + break; + case 4: + right_reg = right_reg.subtract(new BigInteger("1491021")); + break; + case 5: + right_reg = right_reg.subtract(new BigInteger("1979845")); + break; + case 6: + right_reg = right_reg.subtract(new BigInteger("1996939")); + break; + } + + left_character = left_reg.intValue(); + right_character = right_reg.intValue(); + + left_odd = left_character / t_even_ltd[left_group]; + left_even = left_character % t_even_ltd[left_group]; + right_odd = right_character / t_even_ltd[right_group]; + right_even = right_character % t_even_ltd[right_group]; + + getWidths(left_odd, modules_odd_ltd[left_group], 7, widest_odd_ltd[left_group], 1); + left_widths[0] = widths[0]; + left_widths[2] = widths[1]; + left_widths[4] = widths[2]; + left_widths[6] = widths[3]; + left_widths[8] = widths[4]; + left_widths[10] = widths[5]; + left_widths[12] = widths[6]; + getWidths(left_even, modules_even_ltd[left_group], 7, widest_even_ltd[left_group], 0); + left_widths[1] = widths[0]; + left_widths[3] = widths[1]; + left_widths[5] = widths[2]; + left_widths[7] = widths[3]; + left_widths[9] = widths[4]; + left_widths[11] = widths[5]; + left_widths[13] = widths[6]; + getWidths(right_odd, modules_odd_ltd[right_group], 7, widest_odd_ltd[right_group], 1); + right_widths[0] = widths[0]; + right_widths[2] = widths[1]; + right_widths[4] = widths[2]; + right_widths[6] = widths[3]; + right_widths[8] = widths[4]; + right_widths[10] = widths[5]; + right_widths[12] = widths[6]; + getWidths(right_even, modules_even_ltd[right_group], 7, widest_even_ltd[right_group], 0); + right_widths[1] = widths[0]; + right_widths[3] = widths[1]; + right_widths[5] = widths[2]; + right_widths[7] = widths[3]; + right_widths[9] = widths[4]; + right_widths[11] = widths[5]; + right_widths[13] = widths[6]; + + checksum = 0; + /* Calculate the checksum */ + for (i = 0; i < 14; i++) { + checksum += checksum_weight_ltd[i] * left_widths[i]; + checksum += checksum_weight_ltd[i + 14] * right_widths[i]; + } + checksum %= 89; + + encodeInfo.append("Checksum: ").append(Integer.toString(checksum)).append("\n"); + + for (i = 0; i < 14; i++) { + check_elements[i] = finder_pattern_ltd[i + (checksum * 14)]; + } + + total_widths[0] = 1; + total_widths[1] = 1; + total_widths[44] = 1; + total_widths[45] = 1; + for (i = 0; i < 14; i++) { + total_widths[i + 2] = left_widths[i]; + total_widths[i + 16] = check_elements[i]; + total_widths[i + 30] = right_widths[i]; + } + + bin = new StringBuilder(); + notbin = new StringBuilder(); + writer = 0; + bar_latch = false; + for (i = 0; i < 46; i++) { + for (j = 0; j < total_widths[i]; j++) { + if (bar_latch) { + bin.append("1"); + notbin.append("0"); + } else { + bin.append("0"); + notbin.append("1"); + } + writer++; + } + bar_latch = !bar_latch; + } + + if (symbolWidth < (writer + 20)) { + symbolWidth = writer + 20; + } + + /* Calculate check digit from Annex A and place human readable text */ + + readable = new StringBuilder("(01)"); + hrt = new StringBuilder(); + for (i = content.length(); i < 13; i++) { + hrt.append("0"); + } + hrt.append(content); + + for (i = 0; i < 13; i++) { + count += (hrt.charAt(i) - '0'); + + if ((i & 1) == 0) { + count += 2 * (hrt.charAt(i) - '0'); + } + } + + check_digit = 10 - (count % 10); + if (check_digit == 10) { + check_digit = 0; + } + + hrt.append((char) (check_digit + '0')); + readable.append(hrt); + + if (linkageFlag) { + compositeOffset = 1; + } + + rowCount = 1 + compositeOffset; + rowHeight = new int[1 + compositeOffset]; + rowHeight[compositeOffset] = -1; + pattern = new String[1 + compositeOffset]; + pattern[compositeOffset] = "0:" + bin2pat(bin.toString()); + + if (linkageFlag) { + // Add composite symbol seperator + notbin = new StringBuilder(notbin.substring(4, 70)); + rowHeight[0] = 1; + pattern[0] = "0:04" + bin2pat(notbin.toString()); + } + + plotSymbol(); + return true; + } + + private int getCombinations(int n, int r) { + int i, j; + int maxDenom, minDenom; + int val; + + if (n - r > r) { + minDenom = r; + maxDenom = n - r; + } else { + minDenom = n - r; + maxDenom = r; + } + val = 1; + j = 1; + for (i = n; i > maxDenom; i--) { + val *= i; + if (j <= minDenom) { + val /= j; + j++; + } + } + for (; j <= minDenom; j++) { + val /= j; + } + return (val); + } + + private void getWidths(int val, int n, int elements, int maxWidth, int noNarrow) { + int bar; + int elmWidth; + int mxwElement; + int subVal, lessVal; + int narrowMask = 0; + for (bar = 0; bar < elements - 1; bar++) { + for (elmWidth = 1, narrowMask |= (1 << bar); ; + elmWidth++, narrowMask &= ~(1 << bar)) { + /* get all combinations */ + subVal = getCombinations(n - elmWidth - 1, elements - bar - 2); + /* less combinations with no single-module element */ + if ((noNarrow == 0) && (narrowMask == 0) + && (n - elmWidth - (elements - bar - 1) >= elements - bar - 1)) { + subVal -= getCombinations(n - elmWidth - (elements - bar), elements - bar - 2); + } + /* less combinations with elements > maxVal */ + if (elements - bar - 1 > 1) { + lessVal = 0; + for (mxwElement = n - elmWidth - (elements - bar - 2); + mxwElement > maxWidth; + mxwElement--) { + lessVal += getCombinations(n - elmWidth - mxwElement - 1, elements - bar - 3); + } + subVal -= lessVal * (elements - 1 - bar); + } else if (n - elmWidth > maxWidth) { + subVal--; + } + val -= subVal; + if (val < 0) break; + } + val += subVal; + n -= elmWidth; + widths[bar] = elmWidth; + } + widths[bar] = n; + } +} diff --git a/barcode/src/main/java/org/xbib/graphics/barcode/DataMatrix.java b/barcode/src/main/java/org/xbib/graphics/barcode/DataMatrix.java new file mode 100755 index 0000000..774db2a --- /dev/null +++ b/barcode/src/main/java/org/xbib/graphics/barcode/DataMatrix.java @@ -0,0 +1,1636 @@ +package org.xbib.graphics.barcode; + +import org.xbib.graphics.barcode.util.ReedSolomon; + +/** + * Implements Data Matrix ECC 200 bar code symbology according to ISO/IEC + * 16022:2006. + * Data Matrix is a 2D matrix symbology capable of encoding characters in the + * ISO/IEC 8859-1 (Latin-1) character set. + */ +public class DataMatrix extends Symbol { + + private static final int[] c40_shift = { + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 0, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 2, 2, 2, 2, 2, 2, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 2, 2, 2, 2, + 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, + 3, 3, 3, 3, 3, 3, 3, 3 + }; + + private static final int[] c40_value = { + 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, + 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 3, 0, 1, 2, 3, 4, 5, 6, + 7, 8, 9, 10, 11, 12, 13, 14, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 15, 16, + 17, 18, 19, 20, 21, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, + 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 22, 23, 24, 25, 26, + 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, + 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31 + }; + + private static final int[] text_shift = { + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 0, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 2, 2, 2, 2, 2, 2, 3, 3, 3, 3, 3, 3, 3, + 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 2, 2, 2, 2, 2, + 3, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 3, 3, 3, 3, 3 + }; + + private static final int[] text_value = { + 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, + 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 3, 0, 1, 2, 3, 4, 5, 6, + 7, 8, 9, 10, 11, 12, 13, 14, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 15, 16, + 17, 18, 19, 20, 21, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, + 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 22, 23, 24, 25, 26, 0, 14, + 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, + 33, 34, 35, 36, 37, 38, 39, 27, 28, 29, 30, 31 + }; + + private static final int[] intsymbol = { + 0, 1, 3, 5, 7, 8, 10, 12, 13, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, + 25, 26, 27, 28, 29, 2, 4, 6, 9, 11, 14 + }; + + private static final int[] matrixH = { + 10, 12, 8, 14, 8, 16, 12, 18, 20, 12, 22, 16, 24, 26, 16, 32, 36, 40, + 44, 48, 52, 64, 72, 80, 88, 96, 104, 120, 132, 144 + }; + + private static final int[] matrixW = { + 10, 12, 18, 14, 32, 16, 26, 18, 20, 36, 22, 36, 24, 26, 48, 32, 36, 40, + 44, 48, 52, 64, 72, 80, 88, 96, 104, 120, 132, 144 + }; + + private static final int[] matrixFH = { + 10, 12, 8, 14, 8, 16, 12, 18, 20, 12, 22, 16, 24, 26, 16, 16, 18, 20, + 22, 24, 26, 16, 18, 20, 22, 24, 26, 20, 22, 24 + }; + + private static final int[] matrixFW = { + 10, 12, 18, 14, 16, 16, 26, 18, 20, 18, 22, 18, 24, 26, 24, 16, 18, 20, + 22, 24, 26, 16, 18, 20, 22, 24, 26, 20, 22, 24 + }; + + private static final int[] matrixbytes = { + 3, 5, 5, 8, 10, 12, 16, 18, 22, 22, 30, 32, 36, 44, 49, 62, 86, 114, + 144, 174, 204, 280, 368, 456, 576, 696, 816, 1050, 1304, 1558 + }; + + private static final int[] matrixdatablock = { + 3, 5, 5, 8, 10, 12, 16, 18, 22, 22, 30, 32, 36, 44, 49, 62, 86, 114, + 144, 174, 102, 140, 92, 114, 144, 174, 136, 175, 163, 156 + }; + + private static final int[] matrixrsblock = { + 5, 7, 7, 10, 11, 12, 14, 14, 18, 18, 20, 24, 24, 28, 28, 36, 42, 48, 56, + 68, 42, 56, 36, 48, 56, 68, 56, 68, 62, 62 + }; + private int[] target = new int[2200]; + private int[] binary = new int[2200]; + private int binary_length; + private dm_mode last_mode; + private int[] places; + private boolean isSquare; + private int[] inputData; + private int preferredSize = 0; + private int process_p; + private int[] process_buffer = new int[8]; + + public DataMatrix() { + isSquare = true; + } + + /** + * Override selection of symbol size. When set as false the + * symbol will be the smallest available for the amount of data given. When + * set as true the encoding will not use rectangular symbols. + * + * @param input Forces a square symbol when set to true + */ + public void forceSquare(boolean input) { + isSquare = input; + } + + /** + * Set the prefereed symbol size according to the values in the following + * table. Values may be ignored if the data is too big to fit in the + * specified symbol, or if forceSquare mode has been invoked. + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + *
Available Data Matrix symbol sizes

+ * Input

+ * Symbol Size

+ * Input

+ * Symbol Size

+ * 1

+ * 10 x 10

+ * 16

+ * 64 x 64

+ * 2

+ * 12 x 12

+ * 17

+ * 72 x 72

+ * 3

+ * 14 x 14

+ * 18

+ * 80 x 80

+ * 4

+ * 16 x 16

+ * 19

+ * 88 x 88

+ * 5

+ * 18 x 18

+ * 20

+ * 96 x 96

+ * 6

+ * 20 x 20

+ * 21

+ * 104 x 104

+ * 7

+ * 22 x 22

+ * 22

+ * 120 x 120

+ * 8

+ * 24 x 24

+ * 23

+ * 132 x 132

+ * 9

+ * 26 x 26

+ * 24

+ * 144 x 144

+ * 10

+ * 32 x 32

+ * 25

+ * 8 x 18

+ * 11

+ * 36 x 36

+ * 26

+ * 8 x 32

+ * 12

+ * 40 x 40

+ * 27

+ * 12 x 26

+ * 13

+ * 44 x 44

+ * 28

+ * 12 x 36

+ * 14

+ * 48 x 48

+ * 29

+ * 16 x 36

+ * 15

+ * 52 x 52

+ * 30

+ * 16 x 48

+ * + * @param size Symbol size + */ + public void setPreferredSize(int size) { + preferredSize = size; + } + + @Override + public boolean encode() { + int i, binlen, skew = 0; + int symbolsize, optionsize, calcsize; + int taillength; + int H, W, FH, FW, datablock, bytes, rsblock; + int x, y, NC, NR, v; + int[] grid; + StringBuilder bin; + + eciProcess(); // Get ECI mode + + inputData = new int[content.length()]; + for (i = 0; i < content.length(); i++) { + inputData[i] = inputBytes[i] & 0xFF; + } + + binlen = generateCodewords(); + + if (binlen == 0) { + errorMsg.append("Data too long to fit in symbol"); + return false; + } + + if ((preferredSize >= 1) && (preferredSize <= 30)) { + optionsize = intsymbol[preferredSize - 1]; + } else { + optionsize = -1; + } + + calcsize = 29; + for (i = 29; i > -1; i--) { + if (matrixbytes[i] >= (binlen + process_p)) { + calcsize = i; + } + } + + if (isSquare) { + // Force to use square symbol + switch (calcsize) { + case 2: + case 4: + case 6: + case 9: + case 11: + case 14: + calcsize++; + break; + default: + break; + } + } + + symbolsize = optionsize; + if (calcsize > optionsize) { + symbolsize = calcsize; + if (optionsize != -1) { + /* flag an error */ + errorMsg.append("Data does not fit in selected symbol size"); + return false; + } + } + + // Now we know the symbol size we can handle the remaining data in the process buffer. + if (process_p != 0) { + binlen = encodeRemainder(matrixbytes[symbolsize] - binlen, binlen); + } + + H = matrixH[symbolsize]; + W = matrixW[symbolsize]; + FH = matrixFH[symbolsize]; + FW = matrixFW[symbolsize]; + bytes = matrixbytes[symbolsize]; + datablock = matrixdatablock[symbolsize]; + rsblock = matrixrsblock[symbolsize]; + + taillength = bytes - binlen; + + if (taillength != 0) { + addPadBits(binlen, taillength); + } + + // ecc code + if (symbolsize == 29) { + skew = 1; + } + calculateErrorCorrection(bytes, datablock, rsblock, skew); + NC = W - 2 * (W / FW); + NR = H - 2 * (H / FH); + places = new int[NC * NR]; + placeData(NR, NC); + grid = new int[W * H]; + for (i = 0; i < (W * H); i++) { + grid[i] = 0; + } + for (y = 0; y < H; y += FH) { + for (x = 0; x < W; x++) { + grid[y * W + x] = 1; + } + for (x = 0; x < W; x += 2) { + grid[(y + FH - 1) * W + x] = 1; + } + } + for (x = 0; x < W; x += FW) { + for (y = 0; y < H; y++) { + grid[y * W + x] = 1; + } + for (y = 0; y < H; y += 2) { + grid[y * W + x + FW - 1] = 1; + } + } + for (y = 0; y < NR; y++) { + for (x = 0; x < NC; x++) { + v = places[(NR - y - 1) * NC + x]; + if (v == 1 || (v > 7 && (target[(v >> 3) - 1] & (1 << (v & 7))) != 0)) { + grid[(1 + y + 2 * (y / (FH - 2))) * W + 1 + x + 2 * (x / (FW - 2))] = 1; + } + } + } + + readable = new StringBuilder(); + pattern = new String[H]; + rowCount = H; + rowHeight = new int[H]; + for (y = H - 1; y >= 0; y--) { + bin = new StringBuilder(); + for (x = 0; x < W; x++) { + if (grid[W * y + x] == 1) { + bin.append("1"); + } else { + bin.append("0"); + } + } + pattern[(H - y) - 1] = bin2pat(bin.toString()); + rowHeight[(H - y) - 1] = 1; + } + + encodeInfo.append("Grid Size: ").append(W).append(" X ").append(H).append("\n"); + encodeInfo.append("Data Codewords: ").append(datablock).append("\n"); + encodeInfo.append("ECC Codewords: ").append(rsblock).append("\n"); + + plotSymbol(); + return true; + } + + private int generateCodewords() { + /* Encodes data using ASCII, C40, Text, X12, EDIFACT or Base 256 modes as appropriate */ + /* Supports encoding FNC1 in supporting systems */ + /* Supports ECI encoding for whole message only, not inline switching */ + + encodeInfo.append("Encoding: "); + int sp, tp, i; + dm_mode current_mode, next_mode; + int inputlen = content.length(); + + sp = 0; + tp = 0; + process_p = 0; + + for (i = 0; i < 8; i++) { + process_buffer[i] = 0; + } + binary_length = 0; + + /* step (a) */ + current_mode = dm_mode.DM_ASCII; + next_mode = dm_mode.DM_ASCII; + + if (inputDataType == DataType.GS1) { + target[tp] = 232; + tp++; + binary[binary_length] = ' '; + binary_length++; + encodeInfo.append("FNC1 "); + } /* FNC1 */ + + if (eciMode != 3) { + target[tp] = 241; // ECI + tp++; + + if (eciMode <= 126) { + target[tp] = eciMode + 1; + tp++; + } + + if ((eciMode >= 127) && (eciMode <= 16382)) { + target[tp] = ((eciMode - 127) / 254) + 128; + tp++; + target[tp] = ((eciMode - 127) % 254) + 1; + tp++; + } + + if (eciMode >= 16383) { + target[tp] = ((eciMode - 16383) / 64516) + 192; + tp++; + target[tp] = (((eciMode - 16383) / 254) % 254) + 1; + tp++; + target[tp] = ((eciMode - 16383) & 254) + 1; + tp++; + } + + encodeInfo.append("ECI "); + } + + if (readerInit) { + if (inputDataType == DataType.GS1) { + errorMsg.append("Cannot encode in GS1 mode and Reader Initialisation at the same time"); + return 0; + } else { + target[tp] = 234; + tp++; /* Reader Programming */ + + binary[binary_length] = ' '; + binary_length++; + encodeInfo.append("RP "); + } + } + + /* Check for Macro05/Macro06 */ + /* "[)>[RS]05[GS]...[RS][EOT]" -> CW 236 */ + /* "[)>[RS]06[GS]...[RS][EOT]" -> CW 237 */ + if (tp == 0 & sp == 0 && inputlen >= 9) { + if (inputData[0] == '[' && inputData[1] == ')' && inputData[2] == '>' + && inputData[3] == '\u001e' && inputData[4] == '0' + && (inputData[5] == '5' || inputData[5] == '6') + && inputData[6] == '\u001d' + && inputData[inputlen - 2] == '\u001e' + && inputData[inputlen - 1] == '\u0004') { + /* Output macro Codeword */ + if (inputData[5] == '5') { + target[tp] = 236; + encodeInfo.append("Micro05 "); + } else { + target[tp] = 237; + encodeInfo.append("Macro06 "); + } + tp++; + binary[binary_length] = ' '; + binary_length++; + /* Remove macro characters from input string */ + sp = 7; + inputlen -= 2; + } + } + + while (sp < inputlen) { + + current_mode = next_mode; + + /* step (b) - ASCII encodation */ + if (current_mode == dm_mode.DM_ASCII) { + next_mode = dm_mode.DM_ASCII; + + for (i = 0; i < 8; i++) { + process_buffer[i] = 0; + } + + if (isTwoDigits(sp)) { + target[tp] = (10 * Character.getNumericValue(inputData[sp])) + + Character.getNumericValue(inputData[sp + 1]) + 130; + encodeInfo.append(Integer.toString(target[tp] - 130)).append(" "); + tp++; + binary[binary_length] = ' '; + binary_length++; + sp += 2; + } else { + next_mode = lookAheadTest(sp, current_mode); + + if (next_mode != dm_mode.DM_ASCII) { + switch (next_mode) { + case DM_C40: + target[tp] = 230; + tp++; + binary[binary_length] = ' '; + binary_length++; + encodeInfo.append("C40 "); + break; + case DM_TEXT: + target[tp] = 239; + tp++; + binary[binary_length] = ' '; + binary_length++; + encodeInfo.append("TEX "); + break; + case DM_X12: + target[tp] = 238; + tp++; + binary[binary_length] = ' '; + binary_length++; + encodeInfo.append("X12 "); + break; + case DM_EDIFACT: + target[tp] = 240; + tp++; + binary[binary_length] = ' '; + binary_length++; + encodeInfo.append("EDI "); + break; + case DM_BASE256: + target[tp] = 231; + tp++; + binary[binary_length] = ' '; + binary_length++; + encodeInfo.append("BAS "); + break; + } + } else { + if (inputData[sp] > 127) { + target[tp] = 235; /* FNC4 */ + + encodeInfo.append("FNC4 "); + tp++; + target[tp] = (inputData[sp] - 128) + 1; + encodeInfo.append(Integer.toString(target[tp] - 1)).append(" "); + tp++; + binary[binary_length] = ' '; + binary_length++; + binary[binary_length] = ' '; + binary_length++; + } else { + if ((inputDataType == DataType.GS1) && (inputData[sp] == '[')) { + target[tp] = 232; /* FNC1 */ + encodeInfo.append("FNC1 "); + } else { + target[tp] = inputData[sp] + 1; + encodeInfo.append(Integer.toString(target[tp] - 1)).append(" "); + } + tp++; + binary[binary_length] = ' '; + binary_length++; + } + sp++; + } + } + + } + + /* step (c) C40 encodation */ + if (current_mode == dm_mode.DM_C40) { + int shift_set, value; + + next_mode = dm_mode.DM_C40; + if (process_p == 0) { + next_mode = lookAheadTest(sp, current_mode); + } + + if (next_mode != dm_mode.DM_C40) { + target[tp] = 254; + tp++; + binary[binary_length] = ' '; + binary_length++; /* Unlatch */ + + next_mode = dm_mode.DM_ASCII; + encodeInfo.append("ASC "); + } else { + if (inputData[sp] > 127) { + process_buffer[process_p] = 1; + process_p++; + process_buffer[process_p] = 30; + process_p++; /* Upper Shift */ + + shift_set = c40_shift[inputData[sp] - 128]; + value = c40_value[inputData[sp] - 128]; + } else { + shift_set = c40_shift[inputData[sp]]; + value = c40_value[inputData[sp]]; + } + + if ((inputDataType == DataType.GS1) && (inputData[sp] == '[')) { + shift_set = 2; + value = 27; /* FNC1 */ + + } + + if (shift_set != 0) { + process_buffer[process_p] = shift_set - 1; + process_p++; + } + process_buffer[process_p] = value; + process_p++; + + if (process_p >= 3) { + int iv; + + iv = (1600 * process_buffer[0]) + (40 * process_buffer[1]) + + (process_buffer[2]) + 1; + target[tp] = iv / 256; + tp++; + target[tp] = iv % 256; + tp++; + binary[binary_length] = ' '; + binary_length++; + binary[binary_length] = ' '; + binary_length++; + encodeInfo.append("(").append(Integer.toString(process_buffer[0])) + .append(" ").append(Integer.toString(process_buffer[1])) + .append(" ").append(Integer.toString(process_buffer[2])) + .append(") "); + + process_buffer[0] = process_buffer[3]; + process_buffer[1] = process_buffer[4]; + process_buffer[2] = process_buffer[5]; + process_buffer[3] = 0; + process_buffer[4] = 0; + process_buffer[5] = 0; + process_p -= 3; + } + sp++; + } + } + + /* step (d) Text encodation */ + if (current_mode == dm_mode.DM_TEXT) { + int shift_set, value; + + next_mode = dm_mode.DM_TEXT; + if (process_p == 0) { + next_mode = lookAheadTest(sp, current_mode); + } + + if (next_mode != dm_mode.DM_TEXT) { + target[tp] = 254; + tp++; + binary[binary_length] = ' '; + binary_length++; /* Unlatch */ + + next_mode = dm_mode.DM_ASCII; + encodeInfo.append("ASC "); + } else { + if (inputData[sp] > 127) { + process_buffer[process_p] = 1; + process_p++; + process_buffer[process_p] = 30; + process_p++; /* Upper Shift */ + + shift_set = text_shift[inputData[sp] - 128]; + value = text_value[inputData[sp] - 128]; + } else { + shift_set = text_shift[inputData[sp]]; + value = text_value[inputData[sp]]; + } + + if ((inputDataType == DataType.GS1) && (inputData[sp] == '[')) { + shift_set = 2; + value = 27; /* FNC1 */ + + } + + if (shift_set != 0) { + process_buffer[process_p] = shift_set - 1; + process_p++; + } + process_buffer[process_p] = value; + process_p++; + + if (process_p >= 3) { + int iv; + + iv = (1600 * process_buffer[0]) + (40 * process_buffer[1]) + + (process_buffer[2]) + 1; + target[tp] = iv / 256; + tp++; + target[tp] = iv % 256; + tp++; + binary[binary_length] = ' '; + binary_length++; + binary[binary_length] = ' '; + binary_length++; + encodeInfo.append("(").append(Integer.toString(process_buffer[0])).append(" ") + .append(Integer.toString(process_buffer[1])).append(" ") + .append(Integer.toString(process_buffer[2])).append(") "); + + process_buffer[0] = process_buffer[3]; + process_buffer[1] = process_buffer[4]; + process_buffer[2] = process_buffer[5]; + process_buffer[3] = 0; + process_buffer[4] = 0; + process_buffer[5] = 0; + process_p -= 3; + } + sp++; + } + } + + /* step (e) X12 encodation */ + if (current_mode == dm_mode.DM_X12) { + int value = 0; + + next_mode = dm_mode.DM_X12; + if (process_p == 0) { + next_mode = lookAheadTest(sp, current_mode); + } + + if (next_mode != dm_mode.DM_X12) { + target[tp] = 254; + tp++; + binary[binary_length] = ' '; + binary_length++; /* Unlatch */ + + next_mode = dm_mode.DM_ASCII; + encodeInfo.append("ASC "); + } else { + if (inputData[sp] == 13) { + value = 0; + } + if (inputData[sp] == '*') { + value = 1; + } + if (inputData[sp] == '>') { + value = 2; + } + if (inputData[sp] == ' ') { + value = 3; + } + if ((inputData[sp] >= '0') && (inputData[sp] <= '9')) { + value = (inputData[sp] - '0') + 4; + } + if ((inputData[sp] >= 'A') && (inputData[sp] <= 'Z')) { + value = (inputData[sp] - 'A') + 14; + } + + process_buffer[process_p] = value; + process_p++; + + if (process_p >= 3) { + int iv; + + iv = (1600 * process_buffer[0]) + (40 * process_buffer[1]) + + (process_buffer[2]) + 1; + target[tp] = iv / 256; + tp++; + target[tp] = iv % 256; + tp++; + binary[binary_length] = ' '; + binary_length++; + binary[binary_length] = ' '; + binary_length++; + encodeInfo.append("(").append(Integer.toString(process_buffer[0])).append(" ") + .append(Integer.toString(process_buffer[1])).append(" ") + .append(Integer.toString(process_buffer[2])).append(") "); + + process_buffer[0] = process_buffer[3]; + process_buffer[1] = process_buffer[4]; + process_buffer[2] = process_buffer[5]; + process_buffer[3] = 0; + process_buffer[4] = 0; + process_buffer[5] = 0; + process_p -= 3; + } + sp++; + } + } + + /* step (f) EDIFACT encodation */ + if (current_mode == dm_mode.DM_EDIFACT) { + int value = 0; + + next_mode = dm_mode.DM_EDIFACT; + if (process_p == 3) { + next_mode = lookAheadTest(sp, current_mode); + } + + if (next_mode != dm_mode.DM_EDIFACT) { + process_buffer[process_p] = 31; + process_p++; + next_mode = dm_mode.DM_ASCII; + } else { + if ((inputData[sp] >= '@') && (inputData[sp] <= '^')) { + value = inputData[sp] - '@'; + } + if ((inputData[sp] >= ' ') && (inputData[sp] <= '?')) { + value = inputData[sp]; + } + + process_buffer[process_p] = value; + process_p++; + sp++; + } + + if (process_p >= 4) { + target[tp] = (process_buffer[0] << 2) + + ((process_buffer[1] & 0x30) >> 4); + tp++; + target[tp] = ((process_buffer[1] & 0x0f) << 4) + + ((process_buffer[2] & 0x3c) >> 2); + tp++; + target[tp] = ((process_buffer[2] & 0x03) << 6) + + process_buffer[3]; + tp++; + binary[binary_length] = ' '; + binary_length++; + binary[binary_length] = ' '; + binary_length++; + binary[binary_length] = ' '; + binary_length++; + encodeInfo.append("(").append(Integer.toString(process_buffer[0])).append(" ") + .append(Integer.toString(process_buffer[1])).append(" ") + .append(Integer.toString(process_buffer[2])).append(") "); + + process_buffer[0] = process_buffer[4]; + process_buffer[1] = process_buffer[5]; + process_buffer[2] = process_buffer[6]; + process_buffer[3] = process_buffer[7]; + process_buffer[4] = 0; + process_buffer[5] = 0; + process_buffer[6] = 0; + process_buffer[7] = 0; + process_p -= 4; + } + } + + /* step (g) Base 256 encodation */ + if (current_mode == dm_mode.DM_BASE256) { + next_mode = lookAheadTest(sp, current_mode); + + if (next_mode == dm_mode.DM_BASE256) { + target[tp] = inputData[sp]; + encodeInfo.append(Integer.toString(target[tp])).append(" "); + tp++; + sp++; + binary[binary_length] = 'b'; + binary_length++; + } else { + next_mode = dm_mode.DM_ASCII; + encodeInfo.append("ASC "); + } + } + + if (tp > 1558) { + return 0; + } + + } /* while */ + + /* Add length and randomising algorithm to b256 */ + i = 0; + while (i < tp) { + if (binary[i] == 'b') { + if ((i == 0) || ((i != 0) && (binary[i - 1] != 'b'))) { + /* start of binary data */ + int binary_count; /* length of b256 data */ + + for (binary_count = 0; binary[binary_count + i] == 'b'; + binary_count++) + ; + + if (binary_count <= 249) { + insertAt(i, 'b'); + insertValueAt(i, tp, (char) binary_count); + tp++; + } else { + insertAt(i, 'b'); + insertAt(i + 1, 'b'); + insertValueAt(i, tp, (char) ((binary_count / 250) + 249)); + tp++; + insertValueAt(i + 1, tp, (char) (binary_count % 250)); + tp++; + } + } + } + i++; + } + + for (i = 0; i < tp; i++) { + if (binary[i] == 'b') { + int prn, temp; + + prn = ((149 * (i + 1)) % 255) + 1; + temp = target[i] + prn; + if (temp <= 255) { + target[i] = temp; + } else { + target[i] = temp - 256; + } + } + } + + encodeInfo.append("\n"); + + encodeInfo.append("Codewords: "); + for (i = 0; i < tp; i++) { + encodeInfo.append(Integer.toString(target[i])).append(" "); + } + encodeInfo.append("\n"); + last_mode = current_mode; + return tp; + } + + private int encodeRemainder(int symbols_left, int target_length) { + + int inputlen = content.length(); + + switch (last_mode) { + case DM_C40: + case DM_TEXT: + if (symbols_left == process_p) { // No unlatch required! + + if (process_p == 1) { // 1 data character left to encode. + target[target_length] = inputData[inputlen - 1] + 1; + target_length++; + } + + if (process_p == 2) { // 2 data characters left to encode. + + // Pad with shift 1 value (0) and encode as double. + int intValue = (1600 * process_buffer[0]) + (40 * process_buffer[1]) + 1; // ie (0 + 1). + target[target_length] = intValue / 256; + target_length++; + target[target_length] = intValue % 256; + target_length++; + } + } + + if (symbols_left > process_p) { + target[target_length] = (254); + target_length++; // Unlatch and encode remaining data in ascii. + if (process_p == 1 || (process_p == 2 && process_buffer[0] < 3)) { // Check for a shift value. + + target[target_length] = inputData[inputlen - 1] + 1; + target_length++; + } else if (process_p == 2) { + target[target_length] = inputData[inputlen - 2] + 1; + target_length++; + target[target_length] = inputData[inputlen - 1] + 1; + target_length++; + } + } + break; + + case DM_X12: + if (symbols_left == process_p) { // Unlatch not required! + + if (process_p == 1) { // 1 data character left to encode. + target[target_length] = inputData[inputlen - 1] + 1; + target_length++; + } + + if (process_p == 2) { + // Encode last 2 bytes as ascii. + target[target_length] = inputData[inputlen - 2] + 1; + target_length++; + target[target_length] = inputData[inputlen - 1] + 1; + target_length++; + } + } + + if (symbols_left > process_p) { // Unlatch and encode remaining data in ascii. + + target[target_length] = (254); + target_length++; // Unlatch. + + if (process_p == 1) { + target[target_length] = inputData[inputlen - 1] + 1; + target_length++; + } + + if (process_p == 2) { + target[target_length] = inputData[inputlen - 2] + 1; + target_length++; + target[target_length] = inputData[inputlen - 1] + 1; + target_length++; + } + } + break; + + case DM_EDIFACT: + if (symbols_left == process_p) // Unlatch not required! + { + if (process_p == 1) { + target[target_length] = inputData[inputlen - 1] + 1; + target_length++; + } + + if (process_p == 2) { + target[target_length] = inputData[inputlen - 2] + 1; + target_length++; + target[target_length] = inputData[inputlen - 1] + 1; + target_length++; + } + + if (process_p == 3) { // Append edifact unlatch value (31) and encode as triple. + target[target_length] = (process_buffer[0] << 2) + + ((process_buffer[1] & 0x30) >> 4); + target_length++; + target[target_length] = ((process_buffer[1] & 0x0f) << 4) + + ((process_buffer[2] & 0x3c) >> 2); + target_length++; + target[target_length] = ((process_buffer[2] & 0x03) << 6) + + 31; + target_length++; + } + } + + if (symbols_left > process_p) // Unlatch and encode remaining data in ascii. + { + // Edifact unlatch. + if (symbols_left < 3) { + target[target_length] = 31; + target_length++; + } else { + target[target_length] = (31 << 2); + target_length++; + } + + if (process_p == 1) { + target[target_length] = inputData[inputlen - 1] + 1; + target_length++; + } + + if (process_p == 2) { + target[target_length] = inputData[inputlen - 2] + 1; + target_length++; + target[target_length] = inputData[inputlen - 1] + 1; + target_length++; + } + + if (process_p == 3) { + target[target_length] = inputData[inputlen - 3] + 1; + target_length++; + target[target_length] = inputData[inputlen - 2] + 1; + target_length++; + target[target_length] = inputData[inputlen - 1] + 1; + target_length++; + } + } + break; + } + + return target_length; + } + + private boolean isTwoDigits(int pos) { + if (Character.isDigit((char) inputData[pos])) { + if (pos + 1 >= content.length()) { + return false; + } + if (Character.isDigit((char) inputData[pos + 1])) { + return true; + } + return false; + } + return false; + } + + private dm_mode lookAheadTest(int position, dm_mode current_mode) { + /* 'look ahead test' from Annex P */ + + double ascii_count, c40_count, text_count, x12_count, edf_count, b256_count, best_count; + int sp; + int sourcelen = content.length(); + dm_mode best_scheme = dm_mode.NULL; + + /* step (j) */ + if (current_mode == dm_mode.DM_ASCII) { + ascii_count = 0.0; + c40_count = 1.0; + text_count = 1.0; + x12_count = 1.0; + edf_count = 1.0; + b256_count = 1.25; + } else { + ascii_count = 1.0; + c40_count = 2.0; + text_count = 2.0; + x12_count = 2.0; + edf_count = 2.0; + b256_count = 2.25; + } + + switch (current_mode) { + case DM_C40: // (j)(2) + c40_count = 0.0; + break; + case DM_TEXT: // (j)(3) + text_count = 0.0; + break; + case DM_X12: // (j)(4) + x12_count = 0.0; + break; + case DM_EDIFACT: // (j)(5) + edf_count = 0.0; + break; + case DM_BASE256: // (j)(6) + b256_count = 0.0; + break; + } + + sp = position; + + do { + if (sp == (sourcelen - 1)) { + /* At the end of data ... step (k) */ + ascii_count = Math.ceil(ascii_count); + b256_count = Math.ceil(b256_count); + edf_count = Math.ceil(edf_count); + text_count = Math.ceil(text_count); + x12_count = Math.ceil(x12_count); + c40_count = Math.ceil(c40_count); + + best_count = c40_count; + best_scheme = dm_mode.DM_C40; // (k)(7) + + if (x12_count < best_count) { + best_count = x12_count; + best_scheme = dm_mode.DM_X12; // (k)(6) + } + + if (text_count < best_count) { + best_count = text_count; + best_scheme = dm_mode.DM_TEXT; // (k)(5) + } + + if (edf_count < best_count) { + best_count = edf_count; + best_scheme = dm_mode.DM_EDIFACT; // (k)(4) + } + + if (b256_count < best_count) { + best_count = b256_count; + best_scheme = dm_mode.DM_BASE256; // (k)(3) + } + + if (ascii_count <= best_count) { + best_scheme = dm_mode.DM_ASCII; // (k)(2) + } + } else { + + /* ascii ... step (l) */ + if ((inputData[sp] >= '0') && (inputData[sp] <= '9')) { + ascii_count += 0.5; // (l)(1) + } else { + if (inputData[sp] > 127) { + ascii_count = Math.ceil(ascii_count) + 2.0; // (l)(2) + } else { + ascii_count = Math.ceil(ascii_count) + 1.0; // (l)(3) + } + } + + /* c40 ... step (m) */ + if ((inputData[sp] == ' ') || + (((inputData[sp] >= '0') && (inputData[sp] <= '9')) || + ((inputData[sp] >= 'A') && (inputData[sp] <= 'Z')))) { + c40_count += (2.0 / 3.0); // (m)(1) + } else { + if (inputData[sp] > 127) { + c40_count += (8.0 / 3.0); // (m)(2) + } else { + c40_count += (4.0 / 3.0); // (m)(3) + } + } + + /* text ... step (n) */ + if ((inputData[sp] == ' ') || + (((inputData[sp] >= '0') && (inputData[sp] <= '9')) || + ((inputData[sp] >= 'a') && (inputData[sp] <= 'z')))) { + text_count += (2.0 / 3.0); // (n)(1) + } else { + if (inputData[sp] > 127) { + text_count += (8.0 / 3.0); // (n)(2) + } else { + text_count += (4.0 / 3.0); // (n)(3) + } + } + + /* x12 ... step (o) */ + if (isX12(inputData[sp])) { + x12_count += (2.0 / 3.0); // (o)(1) + } else { + if (inputData[sp] > 127) { + x12_count += (13.0 / 3.0); // (o)(2) + } else { + x12_count += (10.0 / 3.0); // (o)(3) + } + } + + /* edifact ... step (p) */ + if ((inputData[sp] >= ' ') && (inputData[sp] <= '^')) { + edf_count += (3.0 / 4.0); // (p)(1) + } else { + if (inputData[sp] > 127) { + edf_count += (17.0 / 4.0); // (p)(2) + } else { + edf_count += (13.0 / 4.0); // (p)(3) + } + } + if ((inputDataType == DataType.GS1) && (inputData[sp] == '[')) { + edf_count += 6.0; + } + + /* base 256 ... step (q) */ + if ((inputDataType == DataType.GS1) && (inputData[sp] == '[')) { + b256_count += 4.0; // (q)(1) + } else { + b256_count += 1.0; // (q)(2) + } + } + + + if (sp > (position + 3)) { + /* 4 data characters processed ... step (r) */ + + /* step (r)(6) */ + if (((c40_count + 1.0) < ascii_count) && + ((c40_count + 1.0) < b256_count) && + ((c40_count + 1.0) < edf_count) && + ((c40_count + 1.0) < text_count)) { + + if (c40_count < x12_count) { + best_scheme = dm_mode.DM_C40; + } + + if (c40_count == x12_count) { + if (p_r_6_2_1(sp, sourcelen)) { + // Test (r)(6)(ii)(i) + best_scheme = dm_mode.DM_X12; + } else { + best_scheme = dm_mode.DM_C40; + } + } + } + + /* step (r)(5) */ + if (((x12_count + 1.0) < ascii_count) && + ((x12_count + 1.0) < b256_count) && + ((x12_count + 1.0) < edf_count) && + ((x12_count + 1.0) < text_count) && + ((x12_count + 1.0) < c40_count)) { + best_scheme = dm_mode.DM_X12; + } + + /* step (r)(4) */ + if (((text_count + 1.0) < ascii_count) && + ((text_count + 1.0) < b256_count) && + ((text_count + 1.0) < edf_count) && + ((text_count + 1.0) < x12_count) && + ((text_count + 1.0) < c40_count)) { + best_scheme = dm_mode.DM_TEXT; + } + + /* step (r)(3) */ + if (((edf_count + 1.0) < ascii_count) && + ((edf_count + 1.0) < b256_count) && + ((edf_count + 1.0) < text_count) && + ((edf_count + 1.0) < x12_count) && + ((edf_count + 1.0) < c40_count)) { + best_scheme = dm_mode.DM_EDIFACT; + } + + /* step (r)(2) */ + if (((b256_count + 1.0) <= ascii_count) || + (((b256_count + 1.0) < edf_count) && + ((b256_count + 1.0) < text_count) && + ((b256_count + 1.0) < x12_count) && + ((b256_count + 1.0) < c40_count))) { + best_scheme = dm_mode.DM_BASE256; + } + + /* step (r)(1) */ + if (((ascii_count + 1.0) <= b256_count) && + ((ascii_count + 1.0) <= edf_count) && + ((ascii_count + 1.0) <= text_count) && + ((ascii_count + 1.0) <= x12_count) && + ((ascii_count + 1.0) <= c40_count)) { + best_scheme = dm_mode.DM_ASCII; + } + } + + sp++; + } while (best_scheme == dm_mode.NULL); // step (s) + + return best_scheme; + } + + private boolean p_r_6_2_1(int position, int sourcelen) { + /* Annex P section (r)(6)(ii)(I) + "If one of the three X12 terminator/separator characters first + occurs in the yet to be processed data before a non-X12 character..." + */ + + int i; + int nonX12Position = 0; + int specialX12Position = 0; + boolean retval = false; + + for (i = position; i < sourcelen; i++) { + if (nonX12Position == 0 && !isX12(i)) { + nonX12Position = i; + } + + if (specialX12Position == 0) { + if ((inputData[i] == (char) 13) || + (inputData[i] == '*') || + (inputData[i] == '>')) { + specialX12Position = i; + } + } + } + + if ((nonX12Position != 0) && (specialX12Position != 0)) { + if (specialX12Position < nonX12Position) { + retval = true; + } + } + + return retval; + } + + private boolean isX12(int source) { + if (source == 13) { + return true; + } + if (source == 42) { + return true; + } + if (source == 62) { + return true; + } + if (source == 32) { + return true; + } + if ((source >= '0') && (source <= '9')) { + return true; + } + if ((source >= 'A') && (source <= 'Z')) { + return true; + } + + return false; + } + + private void calculateErrorCorrection(int bytes, int datablock, int rsblock, int skew) { + // calculate and append ecc code, and if necessary interleave + int blocks = (bytes + 2) / datablock, b; + int n, p; + ReedSolomon rs = new ReedSolomon(); + + rs.init_gf(0x12d); + rs.init_code(rsblock, 1); + + for (b = 0; b < blocks; b++) { + int[] buf = new int[256]; + int[] ecc = new int[256]; + + p = 0; + for (n = b; n < bytes; n += blocks) { + buf[p++] = target[n]; + } + rs.encode(p, buf); + for (n = 0; n < rsblock; n++) { + ecc[n] = rs.getResult(n); + } + p = rsblock - 1; // comes back reversed + for (n = b; n < rsblock * blocks; n += blocks) { + if (skew == 1) { + /* Rotate ecc data to make 144x144 size symbols acceptable */ + /* See http://groups.google.com/group/postscriptbarcode/msg/5ae8fda7757477da */ + if (b < 8) { + target[bytes + n + 2] = ecc[p--]; + } else { + target[bytes + n - 8] = ecc[p--]; + } + } else { + target[bytes + n] = ecc[p--]; + } + } + } + } + + private void insertAt(int pos, char newbit) { + /* Insert a character into the middle of a string at position posn */ + int i; + + for (i = binary_length; i > pos; i--) { + binary[i] = binary[i - 1]; + } + binary[pos] = newbit; + } + + private void insertValueAt(int posn, int streamlen, char newbit) { + int i; + + for (i = streamlen; i > posn; i--) { + target[i] = target[i - 1]; + } + target[posn] = newbit; + } + + private void addPadBits(int tp, int tail_length) { + /* adds unlatch and pad bits */ + int i, prn, temp; + + switch (last_mode) { + case DM_C40: + case DM_TEXT: + case DM_X12: + target[tp] = 254; + tp++; /* Unlatch */ + + tail_length--; + } + + for (i = tail_length; i > 0; i--) { + if (i == tail_length) { + target[tp] = 129; + tp++; /* Pad */ + + } else { + prn = ((149 * (tp + 1)) % 253) + 1; + temp = 129 + prn; + if (temp <= 254) { + target[tp] = temp; + tp++; + } else { + target[tp] = temp - 254; + tp++; + } + } + } + } + + private void placeData(int NR, int NC) { + int r, c, p; + // invalidate + for (r = 0; r < NR; r++) { + for (c = 0; c < NC; c++) { + places[r * NC + c] = 0; + } + } + // start + p = 1; + r = 4; + c = 0; + do { + // check corner + if (r == NR && (c == 0)) { + placeCornerA(NR, NC, p++); + } + if (r == NR - 2 && (c == 0) && ((NC % 4) != 0)) { + placeCornerB(NR, NC, p++); + } + if (r == NR - 2 && (c == 0) && (NC % 8) == 4) { + placeCornerC(NR, NC, p++); + } + if (r == NR + 4 && c == 2 && ((NC % 8) == 0)) { + placeCornerD(NR, NC, p++); + } + // up/right + do { + if (r < NR && c >= 0 && (places[r * NC + c] == 0)) { + placeBlock(NR, NC, r, c, p++); + } + r -= 2; + c += 2; + } while (r >= 0 && c < NC); + r++; + c += 3; + // down/left + do { + if (r >= 0 && c < NC && (places[r * NC + c] == 0)) { + placeBlock(NR, NC, r, c, p++); + } + r += 2; + c -= 2; + } while (r < NR && c >= 0); + r += 3; + c++; + } while (r < NR || c < NC); + // unfilled corner + if (places[NR * NC - 1] == 0) { + places[NR * NC - 1] = places[NR * NC - NC - 2] = 1; + } + } + + private void placeCornerA(int NR, int NC, int p) { + placeBit(NR, NC, NR - 1, 0, p, 7); + placeBit(NR, NC, NR - 1, 1, p, 6); + placeBit(NR, NC, NR - 1, 2, p, 5); + placeBit(NR, NC, 0, NC - 2, p, 4); + placeBit(NR, NC, 0, NC - 1, p, 3); + placeBit(NR, NC, 1, NC - 1, p, 2); + placeBit(NR, NC, 2, NC - 1, p, 1); + placeBit(NR, NC, 3, NC - 1, p, 0); + } + + private void placeCornerB(int NR, int NC, int p) { + placeBit(NR, NC, NR - 3, 0, p, 7); + placeBit(NR, NC, NR - 2, 0, p, 6); + placeBit(NR, NC, NR - 1, 0, p, 5); + placeBit(NR, NC, 0, NC - 4, p, 4); + placeBit(NR, NC, 0, NC - 3, p, 3); + placeBit(NR, NC, 0, NC - 2, p, 2); + placeBit(NR, NC, 0, NC - 1, p, 1); + placeBit(NR, NC, 1, NC - 1, p, 0); + } + + private void placeCornerC(int NR, int NC, int p) { + placeBit(NR, NC, NR - 3, 0, p, 7); + placeBit(NR, NC, NR - 2, 0, p, 6); + placeBit(NR, NC, NR - 1, 0, p, 5); + placeBit(NR, NC, 0, NC - 2, p, 4); + placeBit(NR, NC, 0, NC - 1, p, 3); + placeBit(NR, NC, 1, NC - 1, p, 2); + placeBit(NR, NC, 2, NC - 1, p, 1); + placeBit(NR, NC, 3, NC - 1, p, 0); + } + + private void placeCornerD(int NR, int NC, int p) { + placeBit(NR, NC, NR - 1, 0, p, 7); + placeBit(NR, NC, NR - 1, NC - 1, p, 6); + placeBit(NR, NC, 0, NC - 3, p, 5); + placeBit(NR, NC, 0, NC - 2, p, 4); + placeBit(NR, NC, 0, NC - 1, p, 3); + placeBit(NR, NC, 1, NC - 3, p, 2); + placeBit(NR, NC, 1, NC - 2, p, 1); + placeBit(NR, NC, 1, NC - 1, p, 0); + } + + private void placeBlock(int NR, int NC, int r, int c, int p) { + placeBit(NR, NC, r - 2, c - 2, p, 7); + placeBit(NR, NC, r - 2, c - 1, p, 6); + placeBit(NR, NC, r - 1, c - 2, p, 5); + placeBit(NR, NC, r - 1, c - 1, p, 4); + placeBit(NR, NC, r - 1, c, p, 3); + placeBit(NR, NC, r, c - 2, p, 2); + placeBit(NR, NC, r, c - 1, p, 1); + placeBit(NR, NC, r, c, p, 0); + } + + private void placeBit(int NR, int NC, int r, int c, int p, int b) { + if (r < 0) { + r += NR; + c += 4 - ((NR + 4) % 8); + } + if (c < 0) { + c += NC; + r += 4 - ((NC + 4) % 8); + } + places[r * NC + c] = (p << 3) + b; + } + + private enum dm_mode { + + NULL, DM_ASCII, DM_C40, DM_TEXT, DM_X12, DM_EDIFACT, DM_BASE256 + } +} diff --git a/barcode/src/main/java/org/xbib/graphics/barcode/Ean.java b/barcode/src/main/java/org/xbib/graphics/barcode/Ean.java new file mode 100755 index 0000000..d378a06 --- /dev/null +++ b/barcode/src/main/java/org/xbib/graphics/barcode/Ean.java @@ -0,0 +1,366 @@ +package org.xbib.graphics.barcode; + +import static org.xbib.graphics.barcode.HumanReadableLocation.NONE; +import static org.xbib.graphics.barcode.HumanReadableLocation.TOP; +import org.xbib.graphics.barcode.util.AddOn; +import org.xbib.graphics.barcode.util.TextBox; +import java.awt.geom.Rectangle2D; + +/** + * Implements EAN bar code symbology according to BS EN 797:1996. + * European Article Number data can be encoded in EAN-8 or EAN-13 format + * requiring a 7-digit or 12-digit input respectively. EAN-13 numbers map to + * Global Trade Identification Numbers (GTIN) whereas EAN-8 symbols are + * generally for internal use only. Check digit is calculated and should not + * be in input data. Leading zeroes are added as required. + */ +public class Ean extends Symbol { + + private boolean useAddOn; + + private String addOnContent; + private Mode mode; + private boolean linkageFlag; + private String[] EAN13Parity = { + "AAAAAA", "AABABB", "AABBAB", "AABBBA", "ABAABB", "ABBAAB", "ABBBAA", + "ABABAB", "ABABBA", "ABBABA" + }; + private String[] EANsetA = { + "3211", "2221", "2122", "1411", "1132", "1231", "1114", "1312", "1213", + "3112" + }; + private String[] EANsetB = { + "1123", "1222", "2212", "1141", "2311", "1321", "4111", "2131", "3121", + "2113" + }; + + public Ean() { + mode = Mode.EAN13; + useAddOn = false; + addOnContent = ""; + linkageFlag = false; + } + + public Mode getMode() { + return mode; + } + + public void setMode(Mode mode) { + this.mode = mode; + } + + protected void setLinkageFlag() { + linkageFlag = true; + } + + @Override + public void setHumanReadableLocation(HumanReadableLocation humanReadableLocation) { + if (humanReadableLocation == TOP) { + throw new IllegalArgumentException("Cannot display human-readable text above EAN bar codes."); + } else { + super.setHumanReadableLocation(humanReadableLocation); + } + } + + @Override + public boolean encode() { + boolean retval = false; + AddOn addOn = new AddOn(); + String addOnData; + + separateContent(); + + if (content.length() == 0) { + errorMsg.append("Missing EAN data"); + retval = false; + } else { + switch (mode) { + case EAN8: + retval = ean8(); + break; + case EAN13: + retval = ean13(); + break; + } + } + + if ((retval) && (useAddOn)) { + addOnData = addOn.calcAddOn(addOnContent); + if (addOnData.length() == 0) { + errorMsg.append("Invalid Add-On data"); + retval = false; + } else { + pattern[0] = pattern[0] + "9" + addOnData; + + //add leading zeroes to add-on text + if (addOnContent.length() == 1) { + addOnContent = "0" + addOnContent; + } + if (addOnContent.length() == 3) { + addOnContent = "0" + addOnContent; + } + if (addOnContent.length() == 4) { + addOnContent = "0" + addOnContent; + } + } + } + + if (retval) { + plotSymbol(); + } + + return retval; + } + + private void separateContent() { + int splitPoint; + + splitPoint = content.indexOf('+'); + if (splitPoint != -1) { + // There is a '+' in the input data, use an add-on EAN2 or EAN5 + useAddOn = true; + addOnContent = content.substring(splitPoint + 1); + content = content.substring(0, splitPoint); + } + } + + private boolean ean13() { + StringBuilder accumulator = new StringBuilder(); + StringBuilder dest; + String parity; + int i; + + if (!(content.matches("[0-9]+"))) { + errorMsg.append("Invalid characters in input"); + return false; + } + + if (content.length() > 12) { + errorMsg.append("Input data too long"); + return false; + } + + for (i = content.length(); i < 12; i++) { + accumulator.append("0"); + } + accumulator.append(content); + + accumulator.append(calcDigit(accumulator.toString())); + + parity = EAN13Parity[accumulator.charAt(0) - '0']; + + encodeInfo.append("Parity Digit: ").append(accumulator.charAt(0)).append("\n"); + + /* Start character */ + dest = new StringBuilder("111"); + + for (i = 1; i < 13; i++) { + if (i == 7) { + dest.append("11111"); + } + + if ((i >= 1) && (i <= 6)) { + if (parity.charAt(i - 1) == 'B') { + dest.append(EANsetB[accumulator.charAt(i) - '0']); + } else { + dest.append(EANsetA[accumulator.charAt(i) - '0']); + } + } else { + dest.append(EANsetA[accumulator.charAt(i) - '0']); + } + } + + dest.append("111"); + + readable = new StringBuilder(accumulator.toString()); + pattern = new String[1]; + pattern[0] = dest.toString(); + rowCount = 1; + rowHeight = new int[1]; + rowHeight[0] = -1; + return true; + } + + private boolean ean8() { + StringBuilder accumulator = new StringBuilder(); + int i; + StringBuilder dest; + + if (!(content.matches("[0-9]+"))) { + errorMsg.append("Invalid characters in input"); + return false; + } + + if (content.length() > 7) { + errorMsg.append("Input data too long"); + return false; + } + + for (i = content.length(); i < 7; i++) { + accumulator.append("0"); + } + accumulator.append(content); + + accumulator.append(calcDigit(accumulator.toString())); + + dest = new StringBuilder("111"); + for (i = 0; i < 8; i++) { + if (i == 4) { + dest.append("11111"); + } + dest.append(EANsetA[Character.getNumericValue(accumulator.charAt(i))]); + } + dest.append("111"); + + readable = new StringBuilder(accumulator.toString()); + pattern = new String[1]; + pattern[0] = dest.toString(); + rowCount = 1; + rowHeight = new int[1]; + rowHeight[0] = -1; + return true; + } + + private char calcDigit(String x) { + int count = 0; + int c, cdigit; + int p = 0; + for (int i = x.length() - 1; i >= 0; i--) { + c = Character.getNumericValue(x.charAt(i)); + if ((p % 2) == 0) { + c = c * 3; + } + count += c; + p++; + } + cdigit = 10 - (count % 10); + if (cdigit == 10) { + cdigit = 0; + } + + encodeInfo.append("Check Digit: ").append(cdigit).append("\n"); + + return (char) (cdigit + '0'); + } + + @Override + protected void plotSymbol() { + int xBlock; + int x, y, w, h; + boolean black; + int compositeOffset = 0; + int shortLongDiff = 5; + getRectangles().clear(); + getTexts().clear(); + black = true; + x = 0; + if (linkageFlag) { + compositeOffset = 6; + } + for (xBlock = 0; xBlock < pattern[0].length(); xBlock++) { + if (black) { + y = 0; + black = false; + w = pattern[0].charAt(xBlock) - '0'; + h = defaultHeight; + /* Add extension to guide bars */ + if (mode == Mode.EAN13) { + if ((x < 3) || (x > 91)) { + h += shortLongDiff; + } + if ((x > 45) && (x < 49)) { + h += shortLongDiff; + } + if (x > 95) { + // Drop add-on + h -= 8; + y = 8; + } + if (linkageFlag) { + if ((x == 0) || (x == 94)) { + h += 2; + y -= 2; + } + } + } + if (mode == Mode.EAN8) { + if ((x < 3) || (x > 62)) { + h += shortLongDiff; + } + if ((x > 30) && (x < 35)) { + h += shortLongDiff; + } + if (x > 66) { + // Drop add-on + h -= 8; + y = 8; + } + if (linkageFlag) { + if ((x == 0) || (x == 66)) { + h += 2; + y -= 2; + } + } + } + Rectangle2D.Double rect = new Rectangle2D.Double(x + 6, y + compositeOffset, w, h); + getRectangles().add(rect); + if ((x + w + 12) > symbolWidth) { + symbolWidth = x + w + 12; + } + } else { + black = true; + } + x += (double) (pattern[0].charAt(xBlock) - '0'); + + } + + if (linkageFlag) { + // Add separator for composite symbology + if (mode == Mode.EAN13) { + getRectangles().add(new Rectangle2D.Double(6, 0, 1, 2)); + getRectangles().add(new Rectangle2D.Double(94 + 6, 0, 1, 2)); + getRectangles().add(new Rectangle2D.Double(-1 + 6, 2, 1, 2)); + getRectangles().add(new Rectangle2D.Double(95 + 6, 2, 1, 2)); + } else { + getRectangles().add(new Rectangle2D.Double(6, 0, 1, 2)); + getRectangles().add(new Rectangle2D.Double(66 + 6, 0, 1, 2)); + getRectangles().add(new Rectangle2D.Double(-1 + 6, 2, 1, 2)); + getRectangles().add(new Rectangle2D.Double(67 + 6, 2, 1, 2)); + } + } + + symbolHeight = defaultHeight + 5; + + /* Now add the text */ + if (getHumanReadableLocation() != NONE) { + double baseline = getHeight() + fontSize - shortLongDiff + compositeOffset; + double addOnBaseline = 6.0 + compositeOffset; + if (mode == Mode.EAN13) { + getTexts().add(new TextBox(3, baseline, readable.substring(0, 1))); + getTexts().add(new TextBox(30, baseline, readable.substring(1, 7))); + getTexts().add(new TextBox(77, baseline, readable.substring(7, 13))); + if (useAddOn) { + if (addOnContent.length() == 2) { + getTexts().add(new TextBox(118, addOnBaseline, addOnContent)); + } else { + getTexts().add(new TextBox(133, addOnBaseline, addOnContent)); + } + } + } else { // EAN8 + getTexts().add(new TextBox(23, baseline, readable.substring(0, 4))); + getTexts().add(new TextBox(55, baseline, readable.substring(4, 8))); + if (useAddOn) { + if (addOnContent.length() == 2) { + getTexts().add(new TextBox(93, addOnBaseline, addOnContent)); + } else { + getTexts().add(new TextBox(105, addOnBaseline, addOnContent)); + } + } + } + } + } + + public enum Mode { + EAN8, EAN13 + } +} diff --git a/barcode/src/main/java/org/xbib/graphics/barcode/GridMatrix.java b/barcode/src/main/java/org/xbib/graphics/barcode/GridMatrix.java new file mode 100755 index 0000000..59835e0 --- /dev/null +++ b/barcode/src/main/java/org/xbib/graphics/barcode/GridMatrix.java @@ -0,0 +1,2015 @@ +package org.xbib.graphics.barcode; + +import org.xbib.graphics.barcode.util.ReedSolomon; +import java.io.UnsupportedEncodingException; + +/** + * Implements Grid Matrix bar code symbology according to AIMD014. + * Grid Matrix is a matrix symbology which can encode characters in the ISO/IEC + * 8859-1 (Latin-1) character set as well as those in the GB-2312 character set. + * Input is assumed to be formatted as a UTF string. + */ +public class GridMatrix extends Symbol { + + private final char[] shift_set = { + /* From Table 7 - Encoding of control characters */ + 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, /* NULL -> SI */ + 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f, /* DLE -> US */ + '!', '"', '#', '$', '%', '&', '\'', '(', ')', '*', '+', ',', '-', '.', '/', ':', + ';', '<', '=', '>', '?', '@', '[', '\\', ']', '^', '_', '`', '{', '|', '}', '~' + }; + + private final int[] gm_recommend_cw = { + 9, 30, 59, 114, 170, 237, 315, 405, 506, 618, 741, 875, 1021 + }; + private final int[] gm_max_cw = { + 11, 40, 79, 146, 218, 305, 405, 521, 650, 794, 953, 1125, 1313 + }; + + private final int[] gm_data_codewords = { + 0, 15, 13, 11, 9, + 45, 40, 35, 30, 25, + 89, 79, 69, 59, 49, + 146, 130, 114, 98, 81, + 218, 194, 170, 146, 121, + 305, 271, 237, 203, 169, + 405, 360, 315, 270, 225, + 521, 463, 405, 347, 289, + 650, 578, 506, 434, 361, + 794, 706, 618, 530, 441, + 953, 847, 741, 635, 529, + 1125, 1000, 875, 750, 625, + 1313, 1167, 1021, 875, 729 + }; + + private final int[] gm_n1 = { + 18, 50, 98, 81, 121, 113, 113, 116, 121, 126, 118, 125, 122 + }; + private final int[] gm_b1 = { + 1, 1, 1, 2, 2, 2, 2, 3, 2, 7, 5, 10, 6 + }; + private final int[] gm_b2 = { + 0, 0, 0, 0, 0, 1, 2, 2, 4, 0, 4, 0, 6 + }; + + private final int[] gm_ebeb = { + /* E1 B3 E2 B4 */ + 0, 0, 0, 0, // version 1 + 3, 1, 0, 0, + 5, 1, 0, 0, + 7, 1, 0, 0, + 9, 1, 0, 0, + 5, 1, 0, 0, // version 2 + 10, 1, 0, 0, + 15, 1, 0, 0, + 20, 1, 0, 0, + 25, 1, 0, 0, + 9, 1, 0, 0, // version 3 + 19, 1, 0, 0, + 29, 1, 0, 0, + 39, 1, 0, 0, + 49, 1, 0, 0, + 8, 2, 0, 0, // version 4 + 16, 2, 0, 0, + 24, 2, 0, 0, + 32, 2, 0, 0, + 41, 1, 10, 1, + 12, 2, 0, 0, // version 5 + 24, 2, 0, 0, + 36, 2, 0, 0, + 48, 2, 0, 0, + 61, 1, 60, 1, + 11, 3, 0, 0, // version 6 + 23, 1, 22, 2, + 34, 2, 33, 1, + 45, 3, 0, 0, + 57, 1, 56, 2, + 12, 1, 11, 3, // version 7 + 23, 2, 22, 2, + 34, 3, 33, 1, + 45, 4, 0, 0, + 57, 1, 56, 3, + 12, 2, 11, 3, // version 8 + 23, 5, 0, 0, + 35, 3, 34, 2, + 47, 1, 46, 4, + 58, 4, 57, 1, + 12, 6, 0, 0, // version 9 + 24, 6, 0, 0, + 36, 6, 0, 0, + 48, 6, 0, 0, + 61, 1, 60, 5, + 13, 4, 12, 3, // version 10 + 26, 1, 25, 6, + 38, 5, 37, 2, + 51, 2, 50, 5, + 63, 7, 0, 0, + 12, 6, 11, 3, // version 11 + 24, 4, 23, 5, + 36, 2, 35, 7, + 47, 9, 0, 0, + 59, 7, 58, 2, + 13, 5, 12, 5, // version 12 + 25, 10, 0, 0, + 38, 5, 37, 5, + 50, 10, 0, 0, + 63, 5, 62, 5, + 13, 1, 12, 11, //version 13 + 25, 3, 24, 9, + 37, 5, 36, 7, + 49, 7, 48, 5, + 61, 9, 60, 3 + }; + + private final int[] gm_macro_matrix = { + 728, 625, 626, 627, 628, 629, 630, 631, 632, 633, 634, 635, 636, 637, 638, 639, 640, 641, 642, 643, 644, 645, 646, 647, 648, 649, 650, + 727, 624, 529, 530, 531, 532, 533, 534, 535, 536, 537, 538, 539, 540, 541, 542, 543, 544, 545, 546, 547, 548, 549, 550, 551, 552, 651, + 726, 623, 528, 441, 442, 443, 444, 445, 446, 447, 448, 449, 450, 451, 452, 453, 454, 455, 456, 457, 458, 459, 460, 461, 462, 553, 652, + 725, 622, 527, 440, 361, 362, 363, 364, 365, 366, 367, 368, 369, 370, 371, 372, 373, 374, 375, 376, 377, 378, 379, 380, 463, 554, 653, + 724, 621, 526, 439, 360, 289, 290, 291, 292, 293, 294, 295, 296, 297, 298, 299, 300, 301, 302, 303, 304, 305, 306, 381, 464, 555, 654, + 723, 620, 525, 438, 359, 288, 225, 226, 227, 228, 229, 230, 231, 232, 233, 234, 235, 236, 237, 238, 239, 240, 307, 382, 465, 556, 655, + 722, 619, 524, 437, 358, 287, 224, 169, 170, 171, 172, 173, 174, 175, 176, 177, 178, 179, 180, 181, 182, 241, 308, 383, 466, 557, 656, + 721, 618, 523, 436, 357, 286, 223, 168, 121, 122, 123, 124, 125, 126, 127, 128, 129, 130, 131, 132, 183, 242, 309, 384, 467, 558, 657, + 720, 617, 522, 435, 356, 285, 222, 167, 120, 81, 82, 83, 84, 85, 86, 87, 88, 89, 90, 133, 184, 243, 310, 385, 468, 559, 658, + 719, 616, 521, 434, 355, 284, 221, 166, 119, 80, 49, 50, 51, 52, 53, 54, 55, 56, 91, 134, 185, 244, 311, 386, 469, 560, 659, + 718, 615, 520, 433, 354, 283, 220, 165, 118, 79, 48, 25, 26, 27, 28, 29, 30, 57, 92, 135, 186, 245, 312, 387, 470, 561, 660, + 717, 614, 519, 432, 353, 282, 219, 164, 117, 78, 47, 24, 9, 10, 11, 12, 31, 58, 93, 136, 187, 246, 313, 388, 471, 562, 661, + 716, 613, 518, 431, 352, 281, 218, 163, 116, 77, 46, 23, 8, 1, 2, 13, 32, 59, 94, 137, 188, 247, 314, 389, 472, 563, 662, + 715, 612, 517, 430, 351, 280, 217, 162, 115, 76, 45, 22, 7, 0, 3, 14, 33, 60, 95, 138, 189, 248, 315, 390, 473, 564, 663, + 714, 611, 516, 429, 350, 279, 216, 161, 114, 75, 44, 21, 6, 5, 4, 15, 34, 61, 96, 139, 190, 249, 316, 391, 474, 565, 664, + 713, 610, 515, 428, 349, 278, 215, 160, 113, 74, 43, 20, 19, 18, 17, 16, 35, 62, 97, 140, 191, 250, 317, 392, 475, 566, 665, + 712, 609, 514, 427, 348, 277, 214, 159, 112, 73, 42, 41, 40, 39, 38, 37, 36, 63, 98, 141, 192, 251, 318, 393, 476, 567, 666, + 711, 608, 513, 426, 347, 276, 213, 158, 111, 72, 71, 70, 69, 68, 67, 66, 65, 64, 99, 142, 193, 252, 319, 394, 477, 568, 667, + 710, 607, 512, 425, 346, 275, 212, 157, 110, 109, 108, 107, 106, 105, 104, 103, 102, 101, 100, 143, 194, 253, 320, 395, 478, 569, 668, + 709, 606, 511, 424, 345, 274, 211, 156, 155, 154, 153, 152, 151, 150, 149, 148, 147, 146, 145, 144, 195, 254, 321, 396, 479, 570, 669, + 708, 605, 510, 423, 344, 273, 210, 209, 208, 207, 206, 205, 204, 203, 202, 201, 200, 199, 198, 197, 196, 255, 322, 397, 480, 571, 670, + 707, 604, 509, 422, 343, 272, 271, 270, 269, 268, 267, 266, 265, 264, 263, 262, 261, 260, 259, 258, 257, 256, 323, 398, 481, 572, 671, + 706, 603, 508, 421, 342, 341, 340, 339, 338, 337, 336, 335, 334, 333, 332, 331, 330, 329, 328, 327, 326, 325, 324, 399, 482, 573, 672, + 705, 602, 507, 420, 419, 418, 417, 416, 415, 414, 413, 412, 411, 410, 409, 408, 407, 406, 405, 404, 403, 402, 401, 400, 483, 574, 673, + 704, 601, 506, 505, 504, 503, 502, 501, 500, 499, 498, 497, 496, 495, 494, 493, 492, 491, 490, 489, 488, 487, 486, 485, 484, 575, 674, + 703, 600, 599, 598, 597, 596, 595, 594, 593, 592, 591, 590, 589, 588, 587, 586, 585, 584, 583, 582, 581, 580, 579, 578, 577, 576, 675, + 702, 701, 700, 699, 698, 697, 696, 695, 694, 693, 692, 691, 690, 689, 688, 687, 686, 685, 684, 683, 682, 681, 680, 679, 678, 677, 676 + }; + + private final char[] MIXED_ALPHANUM_SET = { + '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D', + 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T', + 'U', 'V', 'W', 'X', 'Y', 'Z', 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', + 'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', + 'w', 'x', 'y', 'z', ' ' + }; + private int[] inputIntArray; + + private StringBuilder binary = new StringBuilder("1"); + private int[] word = new int[1460]; + private boolean[] grid; + private gmMode appxDnextSection = gmMode.NULL; + private gmMode appxDlastSection = gmMode.NULL; + private int preferredVersion = 0; + private int preferredEccLevel = -1; + + /** + * Set preferred size, or "version" of the symbol according to the following + * table. This value may be ignored if the data to be encoded does not fit + * into a symbol of the selected size. + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + *
Available Grid Matrix symbol sizes

+ * Input

+ * Size

+ * 1

+ * 18 x 18

+ * 2

+ * 30 x 30

+ * 3

+ * 42 x 42

+ * 4

+ * 54 x 54

+ * 5

+ * 66 x 66

+ * 6

+ * 78 x 78

+ * 7

+ * 90x 90

+ * 8

+ * 102 x 102

+ * 9

+ * 114 x 114

+ * 10

+ * 126 x 126

+ * 11

+ * 138 x 138

+ * 12

+ * 150 x 150

+ * 13

+ * 162 x 162

+ * + * @param version Symbol version + */ + public void setPreferredVersion(int version) { + preferredVersion = version; + } + + /** + * Set the preferred amount of the symbol which should be dedicated to error + * correction data. Values should be selected from the following table: + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + *
Available options for error correction capacity

+ * Mode

+ * Error Correction Capacity

+ * 1

+ * Approximately 10%

+ * 2

+ * Approximately 20%

+ * 3

+ * Approximately 30%

+ * 4

+ * Approximately 40%

+ * 5

+ * Approximately 50%

+ * + * @param eccLevel Error correction mode + */ + public void setPreferredEccLevel(int eccLevel) { + preferredEccLevel = eccLevel; + } + + @Override + public boolean encode() { + int size, modules, dark, error_number; + int auto_layers, min_layers, layers, auto_ecc_level, min_ecc_level, ecc_level; + int x, y, i; + int data_cw, input_latch = 0; + int data_max; + int length; + StringBuilder bin; + int qmarksBefore, qmarksAfter; + + for (i = 0; i < 1460; i++) { + word[i] = 0; + } + + try { + /* Try converting to GB2312 */ + qmarksBefore = 0; + for (i = 0; i < content.length(); i++) { + if (content.charAt(i) == '?') { + qmarksBefore++; + } + } + inputBytes = content.getBytes("EUC_CN"); + qmarksAfter = 0; + for (i = 0; i < inputBytes.length; i++) { + if (inputBytes[i] == '?') { + qmarksAfter++; + } + } + if (qmarksBefore == qmarksAfter) { + /* GB2312 encoding worked, use chinese compaction */ + inputIntArray = new int[inputBytes.length]; + length = 0; + for (i = 0; i < inputBytes.length; i++) { + if (((inputBytes[i] & 0xFF) >= 0xA1) && ((inputBytes[i] & 0xFF) <= 0xF7)) { + /* Double byte character */ + inputIntArray[length] = ((inputBytes[i] & 0xFF) * 256) + (inputBytes[i + 1] & 0xFF); + i++; + length++; + } else { + /* Single byte character */ + inputIntArray[length] = inputBytes[i] & 0xFF; + length++; + } + } + encodeInfo.append("Using GB2312 character encoding\n"); + eciMode = 29; + } else { + /* GB2312 encoding didn't work, use other ECI mode */ + eciProcess(); // Get ECI mode + length = inputBytes.length; + inputIntArray = new int[length]; + for (i = 0; i < length; i++) { + inputIntArray[i] = inputBytes[i] & 0xFF; + } + } + } catch (UnsupportedEncodingException e) { + errorMsg.append("Byte conversion encoding error"); + return false; + } + + error_number = encodeGridMatrixBinary(length, readerInit); + if (error_number != 0) { + errorMsg.append("Input data too long"); + return false; + } + + /* Determine the size of the symbol */ + data_cw = binary.length() / 7; + + auto_layers = 1; + for (i = 0; i < 13; i++) { + if (gm_recommend_cw[i] < data_cw) { + auto_layers = i + 1; + } + } + + min_layers = 13; + for (i = 12; i > 0; i--) { + if (gm_max_cw[(i - 1)] >= data_cw) { + min_layers = i; + } + } + layers = auto_layers; + auto_ecc_level = 3; + if (layers == 1) { + auto_ecc_level = 5; + } + if ((layers == 2) || (layers == 3)) { + auto_ecc_level = 4; + } + min_ecc_level = 1; + if (layers == 1) { + min_ecc_level = 4; + } + if ((layers == 2) || (layers == 3)) { + min_ecc_level = 2; + } + ecc_level = auto_ecc_level; + + if ((preferredVersion >= 1) && (preferredVersion <= 13)) { + input_latch = 1; + if (preferredVersion > min_layers) { + layers = preferredVersion; + } else { + layers = min_layers; + } + } + + if (input_latch == 1) { + auto_ecc_level = 3; + if (layers == 1) { + auto_ecc_level = 5; + } + if ((layers == 2) || (layers == 3)) { + auto_ecc_level = 4; + } + ecc_level = auto_ecc_level; + if (data_cw > gm_data_codewords[(5 * (layers - 1)) + (ecc_level - 1)]) { + layers++; + } + } + + if (input_latch == 0) { + if ((preferredEccLevel >= 1) && (preferredEccLevel <= 5)) { + if (preferredEccLevel > min_ecc_level) { + ecc_level = preferredEccLevel; + } else { + ecc_level = min_ecc_level; + } + } + if (data_cw > gm_data_codewords[(5 * (layers - 1)) + (ecc_level - 1)]) { + do { + layers++; + } while ((data_cw > gm_data_codewords[(5 * (layers - 1)) + (ecc_level - 1)]) && (layers <= 13)); + } + } + + data_max = 1313; + switch (ecc_level) { + case 2: + data_max = 1167; + break; + case 3: + data_max = 1021; + break; + case 4: + data_max = 875; + break; + case 5: + data_max = 729; + break; + } + + if (data_cw > data_max) { + errorMsg.append("Input data too long"); + return false; + } + + addErrorCorrection(data_cw, layers, ecc_level); + size = 6 + (layers * 12); + modules = 1 + (layers * 2); + + encodeInfo.append("Layers: ").append(layers).append("\n"); + encodeInfo.append("ECC Level: ").append(ecc_level).append("\n"); + encodeInfo.append("Data Codewords: ").append(data_cw).append("\n"); + encodeInfo.append("ECC Codewords: ").append(gm_data_codewords[((layers - 1) * 5) + + (ecc_level - 1)]).append("\n"); + encodeInfo.append("Grid Size: ").append(modules).append(" X ").append(modules).append("\n"); + + grid = new boolean[size * size]; + + for (x = 0; x < size; x++) { + for (y = 0; y < size; y++) { + grid[(y * size) + x] = false; + } + } + + placeDataInGrid(modules, size); + addLayerId(size, layers, modules, ecc_level); + + /* Add macromodule frames */ + for (x = 0; x < modules; x++) { + dark = 1 - (x & 1); + for (y = 0; y < modules; y++) { + if (dark == 1) { + for (i = 0; i < 5; i++) { + grid[((y * 6) * size) + (x * 6) + i] = true; + grid[(((y * 6) + 5) * size) + (x * 6) + i] = true; + grid[(((y * 6) + i) * size) + (x * 6)] = true; + grid[(((y * 6) + i) * size) + (x * 6) + 5] = true; + } + grid[(((y * 6) + 5) * size) + (x * 6) + 5] = true; + dark = 0; + } else { + dark = 1; + } + } + } + + /* Copy values to symbol */ + symbolWidth = size; + rowCount = size; + rowHeight = new int[rowCount]; + pattern = new String[rowCount]; + + for (x = 0; x < size; x++) { + bin = new StringBuilder(); + for (y = 0; y < size; y++) { + if (grid[(x * size) + y]) { + bin.append("1"); + } else { + bin.append("0"); + } + } + rowHeight[x] = 1; + pattern[x] = bin2pat(bin.toString()); + } + + plotSymbol(); + return true; + } + + private int encodeGridMatrixBinary(int length, boolean reader) { + /* Create a binary stream representation of the input data. + 7 sets are defined - Chinese characters, Numerals, Lower case letters, Upper case letters, + Mixed numerals and latters, Control characters and 8-bit binary data */ + int sp, glyph = 0; + gmMode current_mode, next_mode, last_mode; + int c1, c2; + boolean done; + int p = 0, ppos; + int punt = 0; + int number_pad_posn; + int byte_count_posn = 0, byte_count = 0; + int shift, i; + int[] numbuf = new int[3]; + StringBuilder temp_binary; + gmMode[] modeMap = calculateModeMap(length); + + binary = new StringBuilder(); + + sp = 0; + current_mode = gmMode.NULL; + number_pad_posn = 0; + + encodeInfo.append("Encoding: "); + + if (reader) { + binary.append("1010"); /* FNC3 - Reader Initialisation */ + encodeInfo.append("INIT "); + } + + if ((eciMode != 3) && (eciMode != 29)) { + binary.append("1100"); /* ECI */ + + if ((eciMode >= 0) && (eciMode <= 1023)) { + binary.append("0"); + for (i = 0x200; i > 0; i = i >> 1) { + if ((eciMode & i) != 0) { + binary.append("1"); + } else { + binary.append("0"); + } + } + } + + if ((eciMode >= 1024) && (eciMode <= 32767)) { + binary.append("10"); + for (i = 0x4000; i > 0; i = i >> 1) { + if ((eciMode & i) != 0) { + binary.append("1"); + } else { + binary.append("0"); + } + } + } + + if ((eciMode >= 32768) && (eciMode <= 811799)) { + binary.append("11"); + for (i = 0x80000; i > 0; i = i >> 1) { + if ((eciMode & i) != 0) { + binary.append("1"); + } else { + binary.append("0"); + } + } + } + + encodeInfo.append("ECI ").append(Integer.toString(eciMode)).append(" "); + } + + do { + next_mode = modeMap[sp]; + + if (next_mode != current_mode) { + switch (current_mode) { + case NULL: + switch (next_mode) { + case GM_CHINESE: + binary.append("0001"); + break; + case GM_NUMBER: + binary.append("0010"); + break; + case GM_LOWER: + binary.append("0011"); + break; + case GM_UPPER: + binary.append("0100"); + break; + case GM_MIXED: + binary.append("0101"); + break; + case GM_BYTE: + binary.append("0111"); + break; + } + break; + case GM_CHINESE: + switch (next_mode) { + case GM_NUMBER: + binary.append("1111111100001"); + break; // 8161 + case GM_LOWER: + binary.append("1111111100010"); + break; // 8162 + case GM_UPPER: + binary.append("1111111100011"); + break; // 8163 + case GM_MIXED: + binary.append("1111111100100"); + break; // 8164 + case GM_BYTE: + binary.append("1111111100101"); + break; // 8165 + } + break; + case GM_NUMBER: + /* add numeric block padding value */ + temp_binary = new StringBuilder(binary.substring(0, number_pad_posn)); + switch (p) { + case 1: + temp_binary.append("10"); + break; // 2 pad digits + case 2: + temp_binary.append("01"); + break; // 1 pad digit + case 3: + temp_binary.append("00"); + break; // 0 pad digits + } + temp_binary.append(binary.substring(number_pad_posn, binary.length())); + binary = temp_binary; + + switch (next_mode) { + case GM_CHINESE: + binary.append("1111111011"); + break; // 1019 + case GM_LOWER: + binary.append("1111111100"); + break; // 1020 + case GM_UPPER: + binary.append("1111111101"); + break; // 1021 + case GM_MIXED: + binary.append("1111111110"); + break; // 1022 + case GM_BYTE: + binary.append("1111111111"); + break; // 1023 + } + break; + case GM_LOWER: + case GM_UPPER: + switch (next_mode) { + case GM_CHINESE: + binary.append("11100"); + break; // 28 + case GM_NUMBER: + binary.append("11101"); + break; // 29 + case GM_LOWER: + case GM_UPPER: + binary.append("11110"); + break; // 30 + case GM_MIXED: + binary.append("1111100"); + break; // 124 + case GM_BYTE: + binary.append("1111110"); + break; // 126 + } + break; + case GM_MIXED: + switch (next_mode) { + case GM_CHINESE: + binary.append("1111110001"); + break; // 1009 + case GM_NUMBER: + binary.append("1111110010"); + break; // 1010 + case GM_LOWER: + binary.append("1111110011"); + break; // 1011 + case GM_UPPER: + binary.append("1111110100"); + break; // 1012 + case GM_BYTE: + binary.append("1111110111"); + break; // 1015 + } + break; + case GM_BYTE: + /* add byte block length indicator */ + addByteCount(byte_count_posn, byte_count); + byte_count = 0; + switch (next_mode) { + case GM_CHINESE: + binary.append("0001"); + break; // 1 + case GM_NUMBER: + binary.append("0010"); + break; // 2 + case GM_LOWER: + binary.append("0011"); + break; // 3 + case GM_UPPER: + binary.append("0100"); + break; // 4 + case GM_MIXED: + binary.append("0101"); + break; // 5 + } + break; + } + + switch (next_mode) { + case GM_CHINESE: + encodeInfo.append("CHIN "); + break; + case GM_NUMBER: + encodeInfo.append("NUMB "); + break; + case GM_LOWER: + encodeInfo.append("LOWR "); + break; + case GM_UPPER: + encodeInfo.append("UPPR "); + break; + case GM_MIXED: + encodeInfo.append("MIXD "); + break; + case GM_BYTE: + encodeInfo.append("BYTE "); + break; + } + + } + last_mode = current_mode; + current_mode = next_mode; + + switch (current_mode) { + case GM_CHINESE: + done = false; + if (inputIntArray[sp] > 0xff) { + /* GB2312 character */ + c1 = (inputIntArray[sp] & 0xff00) >> 8; + c2 = inputIntArray[sp] & 0xff; + + if ((c1 >= 0xa0) && (c1 <= 0xa9)) { + glyph = (0x60 * (c1 - 0xa1)) + (c2 - 0xa0); + } + if ((c1 >= 0xb0) && (c1 <= 0xf7)) { + glyph = (0x60 * (c1 - 0xb0 + 9)) + (c2 - 0xa0); + } + done = true; + } + if (!(done)) { + if (sp != (length - 1)) { + if ((inputIntArray[sp] == 13) && (inputIntArray[sp + 1] == 10)) { + /* End of Line */ + glyph = 7776; + sp++; + } + done = true; + } + } + if (!(done)) { + if (sp != (length - 1)) { + if (((inputIntArray[sp] >= '0') && (inputIntArray[sp] <= '9')) && ((inputIntArray[sp + 1] >= '0') && (inputIntArray[sp + 1] <= '9'))) { + /* Two digits */ + glyph = 8033 + (10 * (inputIntArray[sp] - '0')) + (inputIntArray[sp + 1] - '0'); + sp++; + } + } + } + if (!(done)) { + /* Byte value */ + glyph = 7777 + inputIntArray[sp]; + } + + encodeInfo.append(Integer.toString(glyph)).append(" "); + + for (i = 0x1000; i > 0; i = i >> 1) { + if ((glyph & i) != 0) { + binary.append("1"); + } else { + binary.append("0"); + } + } + sp++; + break; + + case GM_NUMBER: + if (last_mode != current_mode) { + /* Reserve a space for numeric digit padding value (2 bits) */ + number_pad_posn = binary.length(); + } + p = 0; + ppos = -1; + + /* Numeric compression can also include certain combinations of + non-numeric character */ + numbuf[0] = '0'; + numbuf[1] = '0'; + numbuf[2] = '0'; + do { + if ((inputIntArray[sp] >= '0') && (inputIntArray[sp] <= '9')) { + numbuf[p] = inputIntArray[sp]; + p++; + } + switch (inputIntArray[sp]) { + case ' ': + case '+': + case '-': + case '.': + case ',': + punt = inputIntArray[sp]; + ppos = p; + break; + } + if (sp < (length - 1)) { + if ((inputIntArray[sp] == 13) && (inputIntArray[sp + 1] == 10)) { + /* */ + punt = inputIntArray[sp]; + sp++; + ppos = p; + } + } + sp++; + } while ((p < 3) && (sp < length)); + + if (ppos != -1) { + switch (punt) { + case ' ': + glyph = 0; + break; + case '+': + glyph = 3; + break; + case '-': + glyph = 6; + break; + case '.': + glyph = 9; + break; + case ',': + glyph = 12; + break; + case 0x13: + glyph = 15; + break; + } + glyph += ppos; + glyph += 1000; + + encodeInfo.append(Integer.toString(glyph)).append(" "); + + for (i = 0x200; i > 0; i = i >> 1) { + if ((glyph & i) != 0) { + binary.append("1"); + } else { + binary.append("0"); + } + } + } + + glyph = (100 * (numbuf[0] - '0')) + (10 * (numbuf[1] - '0')) + (numbuf[2] - '0'); + encodeInfo.append(Integer.toString(glyph)).append(" "); + + for (i = 0x200; i > 0; i = i >> 1) { + if ((glyph & i) != 0) { + binary.append("1"); + } else { + binary.append("0"); + } + } + break; + + case GM_BYTE: + if (last_mode != current_mode) { + /* Reserve space for byte block length indicator (9 bits) */ + byte_count_posn = binary.length(); + } + if (byte_count == 512) { + /* Maximum byte block size is 512 bytes. If longer is needed then start a new block */ + addByteCount(byte_count_posn, byte_count); + binary.append("0111"); + byte_count_posn = binary.length(); + byte_count = 0; + } + + glyph = inputIntArray[sp]; + encodeInfo.append(Integer.toString(glyph)).append(" "); + for (i = 0x80; i > 0; i = i >> 1) { + if ((glyph & i) != 0) { + binary.append("1"); + } else { + binary.append("0"); + } + } + sp++; + byte_count++; + break; + + case GM_MIXED: + shift = 1; + if ((inputIntArray[sp] >= '0') && (inputIntArray[sp] <= '9')) { + shift = 0; + } + if ((inputIntArray[sp] >= 'A') && (inputIntArray[sp] <= 'Z')) { + shift = 0; + } + if ((inputIntArray[sp] >= 'a') && (inputIntArray[sp] <= 'z')) { + shift = 0; + } + if (inputIntArray[sp] == ' ') { + shift = 0; + } + + if (shift == 0) { + /* Mixed Mode character */ + glyph = positionOf((char) inputIntArray[sp], MIXED_ALPHANUM_SET); + encodeInfo.append(Integer.toString(glyph)).append(" "); + + for (i = 0x20; i > 0; i = i >> 1) { + if ((glyph & i) != 0) { + binary.append("1"); + } else { + binary.append("0"); + } + } + } else { + /* Shift Mode character */ + binary.append("1111110110"); /* 1014 - shift indicator */ + + addShiftCharacter(inputIntArray[sp]); + } + + sp++; + break; + + case GM_UPPER: + shift = 1; + if ((inputIntArray[sp] >= 'A') && (inputIntArray[sp] <= 'Z')) { + shift = 0; + } + if (inputIntArray[sp] == ' ') { + shift = 0; + } + + if (shift == 0) { + /* Upper Case character */ + glyph = positionOf((char) inputIntArray[sp], MIXED_ALPHANUM_SET) - 10; + if (glyph == 52) { + // Space character + glyph = 26; + } + encodeInfo.append(Integer.toString(glyph)).append(" "); + + for (i = 0x10; i > 0; i = i >> 1) { + if ((glyph & i) != 0) { + binary.append("1"); + } else { + binary.append("0"); + } + } + + } else { + /* Shift Mode character */ + binary.append("1111101"); /* 127 - shift indicator */ + + addShiftCharacter(inputIntArray[sp]); + } + + sp++; + break; + + case GM_LOWER: + shift = 1; + if ((inputIntArray[sp] >= 'a') && (inputIntArray[sp] <= 'z')) { + shift = 0; + } + if (inputIntArray[sp] == ' ') { + shift = 0; + } + + if (shift == 0) { + /* Lower Case character */ + glyph = positionOf((char) inputIntArray[sp], MIXED_ALPHANUM_SET) - 36; + encodeInfo.append(Integer.toString(glyph)).append(" "); + + for (i = 0x10; i > 0; i = i >> 1) { + if ((glyph & i) != 0) { + binary.append("1"); + } else { + binary.append("0"); + } + } + + } else { + /* Shift Mode character */ + binary.append("1111101"); /* 127 - shift indicator */ + + addShiftCharacter(inputIntArray[sp]); + } + + sp++; + break; + } + if (binary.length() > 9191) { + return 1; + } + + } while (sp < length); + + encodeInfo.append("\n"); + + if (current_mode == gmMode.GM_NUMBER) { + /* add numeric block padding value */ + temp_binary = new StringBuilder(binary.substring(0, number_pad_posn)); + switch (p) { + case 1: + temp_binary.append("10"); + break; // 2 pad digits + case 2: + temp_binary.append("01"); + break; // 1 pad digit + case 3: + temp_binary.append("00"); + break; // 0 pad digits + } + temp_binary.append(binary.substring(number_pad_posn, binary.length())); + binary = temp_binary; + } + + if (current_mode == gmMode.GM_BYTE) { + /* Add byte block length indicator */ + addByteCount(byte_count_posn, byte_count); + } + + /* Add "end of data" character */ + switch (current_mode) { + case GM_CHINESE: + binary.append("1111111100000"); + break; // 8160 + case GM_NUMBER: + binary.append("1111111010"); + break; // 1018 + case GM_LOWER: + case GM_UPPER: + binary.append("11011"); + break; // 27 + case GM_MIXED: + binary.append("1111110000"); + break; // 1008 + case GM_BYTE: + binary.append("0000"); + break; // 0 + } + + /* Add padding bits if required */ + p = 7 - (binary.length() % 7); + if (p == 7) { + p = 0; + } + for (i = 0; i < p; i++) { + binary.append("0"); + } + + if (binary.length() > 9191) { + return 1; + } + + return 0; + } + + private gmMode[] calculateModeMap(int length) { + gmMode[] modeMap = new gmMode[length]; + int i; + int digitStart, digitLength; + boolean digits; + int spaceStart, spaceLength; + boolean spaces; + int[] segmentLength; + gmMode[] segmentType; + int[] segmentStart; + int segmentCount; + + // Step 1 + // Characters in GB2312 are encoded as Chinese characters + for (i = 0; i < length; i++) { + modeMap[i] = gmMode.NULL; + if (inputIntArray[i] > 0xFF) { + modeMap[i] = gmMode.GM_CHINESE; + } + } + + // Consecutive characters, if preceeded by or followed + // by chinese characters, are encoded as chinese characters. + if (length > 3) { + i = 1; + do { + if ((inputIntArray[i] == 13) && (inputIntArray[i + 1] == 10)) { + // End of line (CR/LF) + + if (modeMap[i - 1] == gmMode.GM_CHINESE) { + modeMap[i] = gmMode.GM_CHINESE; + modeMap[i + 1] = gmMode.GM_CHINESE; + } + i += 2; + } else { + i++; + } + } while (i < length - 1); + + i = length - 3; + do { + if ((inputIntArray[i] == 13) && (inputIntArray[i + 1] == 10)) { + // End of line (CR/LF) + if (modeMap[i + 2] == gmMode.GM_CHINESE) { + modeMap[i] = gmMode.GM_CHINESE; + modeMap[i + 1] = gmMode.GM_CHINESE; + } + i -= 2; + } else { + i--; + } + } while (i > 0); + } + + // Digit pairs between chinese characters encode as chinese characters. + digits = false; + digitLength = 0; + digitStart = 0; + for (i = 1; i < length - 1; i++) { + if ((inputIntArray[i] >= 48) && (inputIntArray[i] <= 57)) { + // '0' to '9' + if (!digits) { + digits = true; + digitLength = 1; + digitStart = i; + } else { + digitLength++; + } + } else { + if (digits) { + if ((digitLength % 2) == 0) { + if ((modeMap[digitStart - 1] == gmMode.GM_CHINESE) && + (modeMap[i] == gmMode.GM_CHINESE)) { + for (int j = 0; j < digitLength; j++) { + modeMap[i - j - 1] = gmMode.GM_CHINESE; + } + } + } + digits = false; + } + } + } + + // Step 2: all characters 'a' to 'z' are lowercase. + for (i = 0; i < length; i++) { + if ((inputIntArray[i] >= 97) && (inputIntArray[i] <= 122)) { + modeMap[i] = gmMode.GM_LOWER; + } + } + + // Step 3: all characters 'A' to 'Z' are uppercase. + for (i = 0; i < length; i++) { + if ((inputIntArray[i] >= 65) && (inputIntArray[i] <= 90)) { + modeMap[i] = gmMode.GM_UPPER; + } + } + + // Step 4: find consecutive characters preceeded or followed + // by uppercase or lowercase. + spaces = false; + spaceLength = 0; + spaceStart = 0; + for (i = 1; i < length - 1; i++) { + if (inputIntArray[i] == 32) { + if (!spaces) { + spaces = true; + spaceLength = 1; + spaceStart = i; + } else { + spaceLength++; + } + } else { + if (spaces) { + + gmMode modeX = modeMap[spaceStart - 1]; + gmMode modeY = modeMap[i]; + + if ((modeX == gmMode.GM_LOWER) || (modeX == gmMode.GM_UPPER)) { + for (int j = 0; j < spaceLength; j++) { + modeMap[i - j - 1] = modeX; + } + } else { + if ((modeY == gmMode.GM_LOWER) || (modeY == gmMode.GM_UPPER)) { + for (int j = 0; j < spaceLength; j++) { + modeMap[i - j - 1] = modeY; + } + } + } + spaces = false; + } + } + } + + // Step 5: Unassigned characters '0' to '9' are assigned as numerals. + // Non-numeric characters in table 7 are also assigned as numerals. + for (i = 0; i < length; i++) { + if (modeMap[i] == gmMode.NULL) { + if ((inputIntArray[i] >= 48) && (inputIntArray[i] <= 57)) { + // '0' to '9' + modeMap[i] = gmMode.GM_NUMBER; + } else { + switch (inputIntArray[i]) { + case 32: // Space + case 43: // '+' + case 45: // '-' + case 46: // "." + case 44: // "," + modeMap[i] = gmMode.GM_NUMBER; + break; + case 13: // CR + if (i < length - 1) { + if (inputIntArray[i + 1] == 10) { // LF + // + modeMap[i] = gmMode.GM_NUMBER; + modeMap[i + 1] = gmMode.GM_NUMBER; + } + } + } + } + } + } + + // Step 6: The remining unassigned bytes are assigned as 8-bit binary + for (i = 0; i < length; i++) { + if (modeMap[i] == gmMode.NULL) { + modeMap[i] = gmMode.GM_BYTE; + } + } + + // break into segments + segmentLength = new int[length]; + segmentType = new gmMode[length]; + segmentStart = new int[length]; + + segmentCount = 0; + segmentLength[0] = 1; + segmentType[0] = modeMap[0]; + segmentStart[0] = 0; + for (i = 1; i < length; i++) { + if (modeMap[i] == modeMap[i - 1]) { + segmentLength[segmentCount]++; + } else { + segmentCount++; + segmentLength[segmentCount] = 1; + segmentType[segmentCount] = modeMap[i]; + segmentStart[segmentCount] = i; + } + } + + // A segment can be a control segment if + // a) It is not the start segment of the data stream + // b) All characters are control characters + // c) The length of the segment is no more than 3 + // d) The previous segment is not chinese + if (segmentCount > 1) { + for (i = 1; i < segmentCount; i++) { // (a) + if ((segmentLength[i] <= 3) && (segmentType[i - 1] != gmMode.GM_CHINESE)) { // (c) and (d) + boolean controlLatch = true; + for (int j = 0; j < segmentLength[i]; j++) { + boolean thischarLatch = false; + for (int k = 0; k < 63; k++) { + if (inputIntArray[segmentStart[i] + j] == shift_set[k]) { + thischarLatch = true; + } + } + + if (!(thischarLatch)) { + // This character is not a control character + controlLatch = false; + } + } + + if (controlLatch) { // (b) + segmentType[i] = gmMode.GM_CONTROL; + } + } + } + } + + // Stages 7 to 9 + if (segmentCount >= 3) { + for (i = 0; i < segmentCount - 1; i++) { + gmMode pm, tm, nm, lm; + int tl, nl, ll, position; + boolean lastSegment = false; + + if (i == 0) { + pm = gmMode.NULL; + } else { + pm = segmentType[i - 1]; + } + + tm = segmentType[i]; + tl = segmentLength[i]; + + nm = segmentType[i + 1]; + nl = segmentLength[i + 1]; + + lm = segmentType[i + 2]; + ll = segmentLength[i + 2]; + + position = segmentStart[i]; + + if (i + 2 == segmentCount) { + lastSegment = true; + } + + segmentType[i] = getBestMode(pm, tm, nm, lm, tl, nl, ll, position, lastSegment); + + if (segmentType[i] == gmMode.GM_CONTROL) { + segmentType[i] = segmentType[i - 1]; + } + } + + segmentType[i] = appxDnextSection; + segmentType[i + 1] = appxDlastSection; + + if (segmentType[i] == gmMode.GM_CONTROL) { + segmentType[i] = segmentType[i - 1]; + } + if (segmentType[i + 1] == gmMode.GM_CONTROL) { + segmentType[i + 1] = segmentType[i]; + } + +// Uncomment these lines to override mode selection and generate symbol as shown +// in image D.1 for the test data "AAT2556 电池充电器+é™åŽ‹è½¬æ¢å™¨ 200mA至2A tel:86 019 82512738" +// segmentType[9] = gmMode.GM_LOWER; +// segmentType[10] = gmMode.GM_LOWER; + } + + // Copy segments back to modeMap + for (i = 0; i < segmentCount; i++) { + for (int j = 0; j < segmentLength[i]; j++) { + modeMap[segmentStart[i] + j] = segmentType[i]; + } + } + + return modeMap; + } + + private boolean isTransitionValid(gmMode previousMode, gmMode thisMode) { + // Filters possible encoding data types from table D.1 + boolean isValid = false; + + switch (previousMode) { + case GM_CHINESE: + switch (thisMode) { + case GM_CHINESE: + case GM_BYTE: + isValid = true; + break; + } + break; + case GM_NUMBER: + switch (thisMode) { + case GM_NUMBER: + case GM_MIXED: + case GM_BYTE: + case GM_CHINESE: + isValid = true; + break; + } + break; + case GM_LOWER: + switch (thisMode) { + case GM_LOWER: + case GM_MIXED: + case GM_BYTE: + case GM_CHINESE: + isValid = true; + break; + } + break; + case GM_UPPER: + switch (thisMode) { + case GM_UPPER: + case GM_MIXED: + case GM_BYTE: + case GM_CHINESE: + isValid = true; + break; + } + break; + case GM_CONTROL: + switch (thisMode) { + case GM_CONTROL: + case GM_BYTE: + case GM_CHINESE: + isValid = true; + break; + } + break; + case GM_BYTE: + switch (thisMode) { + case GM_BYTE: + case GM_CHINESE: + isValid = true; + break; + } + break; + } + + return isValid; + } + + private gmMode intToMode(int input) { + gmMode retVal; + + switch (input) { + case 1: + retVal = gmMode.GM_CHINESE; + break; + case 2: + retVal = gmMode.GM_BYTE; + break; + case 3: + retVal = gmMode.GM_CONTROL; + break; + case 4: + retVal = gmMode.GM_MIXED; + break; + case 5: + retVal = gmMode.GM_UPPER; + break; + case 6: + retVal = gmMode.GM_LOWER; + break; + case 7: + retVal = gmMode.GM_NUMBER; + break; + default: + retVal = gmMode.NULL; + break; + } + + return retVal; + } + + private gmMode getBestMode(gmMode pm, gmMode tm, gmMode nm, gmMode lm, int tl, int nl, int ll, int position, boolean lastSegment) { + int tmi, nmi, lmi; + gmMode bestMode = tm; + int binaryLength; + int bestBinaryLength = Integer.MAX_VALUE; + + for (tmi = 1; tmi < 8; tmi++) { + if (isTransitionValid(tm, intToMode(tmi))) { + for (nmi = 1; nmi < 8; nmi++) { + if (isTransitionValid(nm, intToMode(nmi))) { + for (lmi = 1; lmi < 8; lmi++) { + if (isTransitionValid(lm, intToMode(lmi))) { + binaryLength = getBinaryLength(pm, intToMode(tmi), intToMode(nmi), intToMode(lmi), tl, nl, ll, position, lastSegment); + + if (binaryLength <= bestBinaryLength) { + bestMode = intToMode(tmi); + appxDnextSection = intToMode(nmi); + appxDlastSection = intToMode(lmi); + bestBinaryLength = binaryLength; + } + } + } + } + } + } + } + + return bestMode; + } + + private int getBinaryLength(gmMode pm, gmMode tm, gmMode nm, gmMode lm, int tl, int nl, int ll, int position, boolean lastSegment) { + int binaryLength; + + binaryLength = getChunkLength(pm, tm, tl, position); + binaryLength += getChunkLength(tm, nm, nl, (position + tl)); + binaryLength += getChunkLength(nm, lm, ll, (position + tl + nl)); + + if (lastSegment) { + switch (lm) { + case GM_CHINESE: + binaryLength += 13; + break; + case GM_NUMBER: + binaryLength += 10; + break; + case GM_LOWER: + case GM_UPPER: + binaryLength += 5; + break; + case GM_MIXED: + binaryLength += 10; + break; + case GM_BYTE: + binaryLength += 4; + break; + } + } + + return binaryLength; + } + + private int getChunkLength(gmMode lastMode, gmMode thisMode, int thisLength, int position) { + int byteLength; + + switch (thisMode) { + case GM_CHINESE: + byteLength = calcChineseLength(position, thisLength); + break; + case GM_NUMBER: + byteLength = calcNumberLength(position, thisLength); + break; + case GM_LOWER: + byteLength = 5 * thisLength; + break; + case GM_UPPER: + byteLength = 5 * thisLength; + break; + case GM_MIXED: + byteLength = calcMixedLength(position, thisLength); + break; + case GM_CONTROL: + byteLength = 6 * thisLength; + break; + default: + //case GM_BYTE: + byteLength = calcByteLength(position, thisLength); + break; + } + + switch (lastMode) { + case NULL: + byteLength += 4; + break; + case GM_CHINESE: + if ((thisMode != gmMode.GM_CHINESE) && (thisMode != gmMode.GM_CONTROL)) { + byteLength += 13; + } + break; + case GM_NUMBER: + if ((thisMode != gmMode.GM_CHINESE) && (thisMode != gmMode.GM_CONTROL)) { + byteLength += 10; + } + break; + case GM_LOWER: + switch (thisMode) { + case GM_CHINESE: + case GM_NUMBER: + case GM_UPPER: + byteLength += 5; + break; + case GM_MIXED: + case GM_CONTROL: + case GM_BYTE: + byteLength += 7; + break; + } + break; + case GM_UPPER: + switch (thisMode) { + case GM_CHINESE: + case GM_NUMBER: + case GM_LOWER: + byteLength += 5; + break; + case GM_MIXED: + case GM_CONTROL: + case GM_BYTE: + byteLength += 7; + break; + } + break; + case GM_MIXED: + if (thisMode != gmMode.GM_MIXED) { + byteLength += 10; + } + break; + case GM_BYTE: + if (thisMode != gmMode.GM_BYTE) { + byteLength += 4; + } + break; + } + + if ((lastMode != gmMode.GM_BYTE) && (thisMode == gmMode.GM_BYTE)) { + byteLength += 9; + } + + if ((lastMode != gmMode.GM_NUMBER) && (thisMode == gmMode.GM_NUMBER)) { + byteLength += 2; + } + + return byteLength; + } + + private int calcChineseLength(int position, int length) { + int i = 0; + int bits = 0; + + do { + bits += 13; + + if (i < length) { + if ((inputIntArray[position + i] == 13) && (inputIntArray[position + i + 1] == 10)) { + // + i++; + } + + if (((inputIntArray[position + i] >= 48) && (inputIntArray[position + i] <= 57)) && + ((inputIntArray[position + i + 1] >= 48) && (inputIntArray[position + i + 1] <= 57))) { + // two digits + i++; + } + } + i++; + } while (i < length); + + return bits; + } + + private int calcMixedLength(int position, int length) { + int bits = 0; + int i; + + for (i = 0; i < length; i++) { + bits += 6; + for (int k = 0; k < 63; k++) { + if (inputIntArray[position + i] == shift_set[k]) { + bits += 10; + } + } + } + + return bits; + } + + private int calcNumberLength(int position, int length) { + int i; + int bits = 0; + int numbers = 0; + int nonnumbers = 0; + + for (i = 0; i < length; i++) { + if ((inputIntArray[position + i] >= 48) && (inputIntArray[position + i] <= 57)) { + numbers++; + } else { + nonnumbers++; + } + + if (i != 0) { + if ((inputIntArray[position + i] == 10) && (inputIntArray[position + i - 1] == 13)) { + // + nonnumbers--; + } + } + + if (numbers == 3) { + if (nonnumbers == 1) { + bits += 20; + } else { + bits += 10; + } + if (nonnumbers > 1) { + // Invalid encoding + bits += 100; + } + numbers = 0; + nonnumbers = 0; + } + } + + if (numbers > 0) { + if (nonnumbers == 1) { + bits += 20; + } else { + bits += 10; + } + } + + if (nonnumbers > 1) { + // Invalid + bits += 100; + } + + if (!((inputIntArray[position + i - 1] >= 48) && (inputIntArray[position + i - 1] <= 57))) { + // Data must end with a digit + bits += 100; + } + + + return bits; + } + + private int calcByteLength(int position, int length) { + int i; + int bits = 0; + + for (i = 0; i < length; i++) { + if (inputIntArray[position + i] <= 0xFF) { + bits += 8; + } else { + bits += 16; + } + } + + return bits; + } + + private void addByteCount(int byte_count_posn, int byte_count) { + /* Add the length indicator for byte encoded blocks */ + int i; + StringBuilder temp_binary; + + temp_binary = new StringBuilder(binary.substring(0, byte_count_posn)); + for (i = 0; i < 9; i++) { + if ((byte_count & (0x100 >> i)) != 0) { + temp_binary.append("0"); + } else { + temp_binary.append("1"); + } + } + temp_binary.append(binary.substring(byte_count_posn, binary.length())); + binary = temp_binary; + } + + void addShiftCharacter(int shifty) { + /* Add a control character to the data stream */ + int i; + int glyph = 0; + + for (i = 0; i < 64; i++) { + if (shift_set[i] == shifty) { + glyph = i; + } + } + + encodeInfo.append("SHT/").append(Integer.toString(glyph)).append(" "); + + for (i = 0x20; i > 0; i = i >> 1) { + if ((glyph & i) != 0) { + binary.append("1"); + } else { + binary.append("0"); + } + } + } + + private void addErrorCorrection(int data_posn, int layers, int ecc_level) { + int data_cw, i, j, wp; + int n1, b1, n2, b2, e1, b3, e2; + int block_size, data_size, ecc_size; + int[] data = new int[1320]; + int[] block = new int[130]; + int[] data_block = new int[115]; + int[] ecc_block = new int[70]; + ReedSolomon rs = new ReedSolomon(); + + data_cw = gm_data_codewords[((layers - 1) * 5) + (ecc_level - 1)]; + + for (i = 0; i < 1320; i++) { + data[i] = 0; + } + + /* Convert from binary sream to 7-bit codewords */ + for (i = 0; i < data_posn; i++) { + for (j = 0; j < 7; j++) { + if (binary.charAt((i * 7) + j) == '1') { + data[i] += 0x40 >> j; + } + } + } + + encodeInfo.append("Codewords: "); + for (i = 0; i < data_posn; i++) { + encodeInfo.append(Integer.toString(data[i])).append(" "); + } + encodeInfo.append("\n"); + + /* Add padding codewords */ + data[data_posn] = 0x00; + for (i = (data_posn + 1); i < data_cw; i++) { + if ((i & 1) != 0) { + data[i] = 0x7e; + } else { + data[i] = 0x00; + } + } + + /* Get block sizes */ + n1 = gm_n1[(layers - 1)]; + b1 = gm_b1[(layers - 1)]; + n2 = n1 - 1; + b2 = gm_b2[(layers - 1)]; + e1 = gm_ebeb[((layers - 1) * 20) + ((ecc_level - 1) * 4)]; + b3 = gm_ebeb[((layers - 1) * 20) + ((ecc_level - 1) * 4) + 1]; + e2 = gm_ebeb[((layers - 1) * 20) + ((ecc_level - 1) * 4) + 2]; + + /* Split the data into blocks */ + wp = 0; + for (i = 0; i < (b1 + b2); i++) { + if (i < b1) { + block_size = n1; + } else { + block_size = n2; + } + if (i < b3) { + ecc_size = e1; + } else { + ecc_size = e2; + } + data_size = block_size - ecc_size; + + /* printf("block %d/%d: data %d / ecc %d\n", i + 1, (b1 + b2), data_size, ecc_size);*/ + for (j = 0; j < data_size; j++) { + data_block[j] = data[wp]; + wp++; + } + + /* Calculate ECC data for this block */ + rs.init_gf(0x89); + rs.init_code(ecc_size, 1); + rs.encode(data_size, data_block); + for (j = 0; j < ecc_size; j++) { + ecc_block[j] = rs.getResult(j); + } + + /* Correct error correction data but in reverse order */ + for (j = 0; j < data_size; j++) { + block[j] = data_block[j]; + } + for (j = 0; j < ecc_size; j++) { + block[(j + data_size)] = ecc_block[ecc_size - j - 1]; + } + + for (j = 0; j < n2; j++) { + word[((b1 + b2) * j) + i] = block[j]; + } + if (block_size == n1) { + word[((b1 + b2) * (n1 - 1)) + i] = block[(n1 - 1)]; + } + } + } + + private void placeDataInGrid(int modules, int size) { + int x, y, macromodule, offset; + + offset = 13 - ((modules - 1) / 2); + for (y = 0; y < modules; y++) { + for (x = 0; x < modules; x++) { + macromodule = gm_macro_matrix[((y + offset) * 27) + (x + offset)]; + placeMacroModule(x, y, word[macromodule * 2], word[(macromodule * 2) + 1], size); + } + } + } + + private void placeMacroModule(int x, int y, int word1, int word2, int size) { + int i, j; + + i = (x * 6) + 1; + j = (y * 6) + 1; + + if ((word2 & 0x40) != 0) { + grid[(j * size) + i + 2] = true; + } + if ((word2 & 0x20) != 0) { + grid[(j * size) + i + 3] = true; + } + if ((word2 & 0x10) != 0) { + grid[((j + 1) * size) + i] = true; + } + if ((word2 & 0x08) != 0) { + grid[((j + 1) * size) + i + 1] = true; + } + if ((word2 & 0x04) != 0) { + grid[((j + 1) * size) + i + 2] = true; + } + if ((word2 & 0x02) != 0) { + grid[((j + 1) * size) + i + 3] = true; + } + if ((word2 & 0x01) != 0) { + grid[((j + 2) * size) + i] = true; + } + if ((word1 & 0x40) != 0) { + grid[((j + 2) * size) + i + 1] = true; + } + if ((word1 & 0x20) != 0) { + grid[((j + 2) * size) + i + 2] = true; + } + if ((word1 & 0x10) != 0) { + grid[((j + 2) * size) + i + 3] = true; + } + if ((word1 & 0x08) != 0) { + grid[((j + 3) * size) + i] = true; + } + if ((word1 & 0x04) != 0) { + grid[((j + 3) * size) + i + 1] = true; + } + if ((word1 & 0x02) != 0) { + grid[((j + 3) * size) + i + 2] = true; + } + if ((word1 & 0x01) != 0) { + grid[((j + 3) * size) + i + 3] = true; + } + } + + private void addLayerId(int size, int layers, int modules, int ecc_level) { + /* Place the layer ID into each macromodule */ + + int i, j, layer, start, stop; + int[] layerid = new int[layers + 1]; + int[] id = new int[modules * modules]; + + + /* Calculate Layer IDs */ + for (i = 0; i <= layers; i++) { + if (ecc_level == 1) { + layerid[i] = 3 - (i % 4); + } else { + layerid[i] = (i + 5 - ecc_level) % 4; + } + } + + for (i = 0; i < modules; i++) { + for (j = 0; j < modules; j++) { + id[(i * modules) + j] = 0; + } + } + + /* Calculate which value goes in each macromodule */ + start = modules / 2; + stop = modules / 2; + for (layer = 0; layer <= layers; layer++) { + for (i = start; i <= stop; i++) { + id[(start * modules) + i] = layerid[layer]; + id[(i * modules) + start] = layerid[layer]; + id[((modules - start - 1) * modules) + i] = layerid[layer]; + id[(i * modules) + (modules - start - 1)] = layerid[layer]; + } + start--; + stop++; + } + + /* Place the data in the grid */ + for (i = 0; i < modules; i++) { + for (j = 0; j < modules; j++) { + if ((id[(i * modules) + j] & 0x02) != 0) { + grid[(((i * 6) + 1) * size) + (j * 6) + 1] = true; + } + if ((id[(i * modules) + j] & 0x01) != 0) { + grid[(((i * 6) + 1) * size) + (j * 6) + 2] = true; + } + } + } + } + + private enum gmMode { + + NULL, GM_NUMBER, GM_LOWER, GM_UPPER, GM_MIXED, GM_CONTROL, GM_BYTE, GM_CHINESE + } +} diff --git a/barcode/src/main/java/org/xbib/graphics/barcode/HumanReadableLocation.java b/barcode/src/main/java/org/xbib/graphics/barcode/HumanReadableLocation.java new file mode 100755 index 0000000..423f4df --- /dev/null +++ b/barcode/src/main/java/org/xbib/graphics/barcode/HumanReadableLocation.java @@ -0,0 +1,22 @@ +package org.xbib.graphics.barcode; + +/** + * The location of a bar code's human-readable text. + */ +public enum HumanReadableLocation { + + /** + * Display the human-readable text below the bar code. + */ + BOTTOM, + + /** + * Display the human-readable text above the bar code. + */ + TOP, + + /** + * Do not display the human-readable text. + */ + NONE +} diff --git a/barcode/src/main/java/org/xbib/graphics/barcode/JapanPost.java b/barcode/src/main/java/org/xbib/graphics/barcode/JapanPost.java new file mode 100755 index 0000000..ebfab19 --- /dev/null +++ b/barcode/src/main/java/org/xbib/graphics/barcode/JapanPost.java @@ -0,0 +1,138 @@ +package org.xbib.graphics.barcode; + +import java.awt.geom.Rectangle2D; +import java.util.Locale; + +/** + * Implements the Japanese Postal Code symbology as used to encode address + * data for mail items in Japan. Valid input characters are digits 0-9, + * characters A-Z and the dash (-) character. A modulo-19 check digit is + * added and should not be included in the input data. + */ +public class JapanPost extends Symbol { + + private static final String[] JAPAN_TABLE = { + "FFT", "FDA", "DFA", "FAD", "FTF", "DAF", "AFD", "ADF", "TFF", "FTT", + "TFT", "DAT", "DTA", "ADT", "TDA", "ATD", "TAD", "TTF", "FFF" + }; + + private static final char[] KASUT_SET = { + '1', '2', '3', '4', '5', '6', '7', '8', '9', '0', '-', 'a', 'b', 'c', + 'd', 'e', 'f', 'g', 'h' + }; + + private static final char[] CH_KASUT_SET = { + '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '-', 'a', 'b', 'c', + 'd', 'e', 'f', 'g', 'h' + }; + + @Override + public boolean encode() { + StringBuilder dest; + StringBuilder inter; + int i, sum, check; + char c; + + content = content.toUpperCase(Locale.ENGLISH); + if (!(content.matches("[0-9A-Z\\-]+"))) { + errorMsg.append("Invalid characters in data"); + return false; + } + + inter = new StringBuilder(); + + for (i = 0; + (i < content.length()) && (inter.length() < 20); i++) { + c = content.charAt(i); + + if ((c >= '0') && (c <= '9')) { + inter.append(c); + } + if (c == '-') { + inter.append(c); + } + if ((c >= 'A') && (c <= 'J')) { + inter.append('a'); + inter.append(CH_KASUT_SET[(c - 'A')]); + } + + if ((c >= 'K') && (c <= 'O')) { + inter.append('b'); + inter.append(CH_KASUT_SET[(c - 'K')]); + } + + if ((c >= 'U') && (c <= 'Z')) { + inter.append('c'); + inter.append(CH_KASUT_SET[(c - 'U')]); + } + } + + for (i = inter.length(); i < 20; i++) { + inter.append("d"); + } + + dest = new StringBuilder("FD"); + + sum = 0; + for (i = 0; i < 20; i++) { + dest.append(JAPAN_TABLE[positionOf(inter.charAt(i), KASUT_SET)]); + sum += positionOf(inter.charAt(i), CH_KASUT_SET); + } + + /* Calculate check digit */ + check = 19 - (sum % 19); + if (check == 19) { + check = 0; + } + dest.append(JAPAN_TABLE[positionOf(CH_KASUT_SET[check], KASUT_SET)]); + dest.append("DF"); + + encodeInfo.append("Encoding: ").append(dest).append("\n"); + encodeInfo.append("Check Digit: ").append(check).append("\n"); + + readable = new StringBuilder(); + pattern = new String[]{dest.toString()}; + rowCount = 1; + rowHeight = new int[]{-1}; + + plotSymbol(); + + return true; + } + + @Override + protected void plotSymbol() { + int xBlock; + int x, y, w, h; + getRectangles().clear(); + x = 0; + w = 1; + y = 0; + h = 0; + for (xBlock = 0; xBlock < pattern[0].length(); xBlock++) { + switch (pattern[0].charAt(xBlock)) { + case 'A': + y = 0; + h = 5; + break; + case 'D': + y = 3; + h = 5; + break; + case 'F': + y = 0; + h = 8; + break; + case 'T': + y = 3; + h = 2; + break; + } + Rectangle2D.Double rect = new Rectangle2D.Double(x, y, w, h); + getRectangles().add(rect); + x += 2; + } + symbolWidth = pattern[0].length() * 3; + symbolHeight = 8; + } +} diff --git a/barcode/src/main/java/org/xbib/graphics/barcode/KixCode.java b/barcode/src/main/java/org/xbib/graphics/barcode/KixCode.java new file mode 100755 index 0000000..faecb34 --- /dev/null +++ b/barcode/src/main/java/org/xbib/graphics/barcode/KixCode.java @@ -0,0 +1,95 @@ +package org.xbib.graphics.barcode; + +import java.awt.geom.Rectangle2D; +import java.util.Locale; + +/** + * Implements Dutch Post KIX Code as used by Royal Dutch TPG Post + * (Netherlands). Input data can consist of digits 0-9 and characters A-Z. + * Input should be 11 characters in length. No check digit is added. + */ +public class KixCode extends Symbol { + + /* Handles Dutch Post TNT KIX symbols */ + /* The same as RM4SCC but without check digit */ + /* Specification at http://www.tntpost.nl/zakelijk/klantenservice/downloads/kIX_code/download.aspx */ + + private final String[] RoyalTable = { + "TTFF", "TDAF", "TDFA", "DTAF", "DTFA", "DDAA", "TADF", "TFTF", "TFDA", + "DATF", "DADA", "DFTA", "TAFD", "TFAD", "TFFT", "DAAD", "DAFT", "DFAT", + "ATDF", "ADTF", "ADDA", "FTTF", "FTDA", "FDTA", "ATFD", "ADAD", "ADFT", + "FTAD", "FTFT", "FDAT", "AADD", "AFTD", "AFDT", "FATD", "FADT", "FFTT" + }; + + private final char[] krSet = { + '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D', + 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R', + 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z' + }; + + @Override + public boolean encode() { + StringBuilder dest; + int i; + + content = content.toUpperCase(Locale.ENGLISH); + + if (!(content.matches("[0-9A-Z]+"))) { + errorMsg.append("Invalid characters in data"); + return false; + } + + dest = new StringBuilder(); + + for (i = 0; i < content.length(); i++) { + dest.append(RoyalTable[positionOf(content.charAt(i), krSet)]); + } + + encodeInfo.append("Encoding: ").append(dest).append("\n"); + + readable = new StringBuilder(); + pattern = new String[1]; + pattern[0] = dest.toString(); + rowCount = 1; + rowHeight = new int[1]; + rowHeight[0] = -1; + plotSymbol(); + return true; + } + + @Override + protected void plotSymbol() { + int xBlock; + int x, y, w, h; + getRectangles().clear(); + x = 0; + w = 1; + y = 0; + h = 0; + for (xBlock = 0; xBlock < pattern[0].length(); xBlock++) { + switch (pattern[0].charAt(xBlock)) { + case 'A': + y = 0; + h = 5; + break; + case 'D': + y = 3; + h = 5; + break; + case 'F': + y = 0; + h = 8; + break; + case 'T': + y = 3; + h = 2; + break; + } + Rectangle2D.Double rect = new Rectangle2D.Double(x, y, w, h); + getRectangles().add(rect); + x += 2; + } + symbolWidth = pattern[0].length() * 3; + symbolHeight = 8; + } +} diff --git a/barcode/src/main/java/org/xbib/graphics/barcode/KoreaPost.java b/barcode/src/main/java/org/xbib/graphics/barcode/KoreaPost.java new file mode 100755 index 0000000..6ca94e3 --- /dev/null +++ b/barcode/src/main/java/org/xbib/graphics/barcode/KoreaPost.java @@ -0,0 +1,61 @@ +package org.xbib.graphics.barcode; + +/** + * Implements Korea Post Barcode. Input should consist of of a six-digit + * number. A Modulo-10 check digit is calculated and added, and should not form + * part of the input data. + */ +public class KoreaPost extends Symbol { + + String[] koreaTable = { + "1313150613", "0713131313", "0417131313", "1506131313", "0413171313", + "17171313", "1315061313", "0413131713", "17131713", "13171713" + }; + + @Override + public boolean encode() { + StringBuilder accumulator = new StringBuilder(); + + if (!(content.matches("[0-9]+"))) { + errorMsg.append("Invalid characters in input"); + return false; + } + + if (content.length() > 6) { + errorMsg.append("Input data too long"); + return false; + } + + StringBuilder add_zero = new StringBuilder(); + int i, j, total = 0, checkd; + + for (i = 0; i < (6 - content.length()); i++) { + add_zero.append("0"); + } + add_zero.append(content); + + + for (i = 0; i < add_zero.length(); i++) { + j = Character.getNumericValue(add_zero.charAt(i)); + accumulator.append(koreaTable[j]); + total += j; + } + + checkd = 10 - (total % 10); + if (checkd == 10) { + checkd = 0; + } + encodeInfo.append("Check Digit: ").append(checkd).append("\n"); + + accumulator.append(koreaTable[checkd]); + + readable.append(add_zero).append(checkd); + pattern = new String[1]; + pattern[0] = accumulator.toString(); + rowCount = 1; + rowHeight = new int[1]; + rowHeight[0] = -1; + plotSymbol(); + return true; + } +} diff --git a/barcode/src/main/java/org/xbib/graphics/barcode/Logmars.java b/barcode/src/main/java/org/xbib/graphics/barcode/Logmars.java new file mode 100755 index 0000000..71ea571 --- /dev/null +++ b/barcode/src/main/java/org/xbib/graphics/barcode/Logmars.java @@ -0,0 +1,97 @@ +package org.xbib.graphics.barcode; + +/** + * Implements the LOGMARS (Logistics Applications of Automated Marking + * and Reading Symbols) standard used by the US Department of Defense. + * Input data can be of any length and supports the characters 0-9, A-Z, dash + * (-), full stop (.), space, dollar ($), slash (/), plus (+) and percent (%). + * A Modulo-43 check digit is calculated and added, and should not form part + * of the input data. + */ +public class Logmars extends Symbol { + + private static final String[] CODE39LM = { + "1113313111", "3113111131", "1133111131", "3133111111", "1113311131", + "3113311111", "1133311111", "1113113131", "3113113111", "1133113111", + "3111131131", "1131131131", "3131131111", "1111331131", "3111331111", + "1131331111", "1111133131", "3111133111", "1131133111", "1111333111", + "3111111331", "1131111331", "3131111311", "1111311331", "3111311311", + "1131311311", "1111113331", "3111113311", "1131113311", "1111313311", + "3311111131", "1331111131", "3331111111", "1311311131", "3311311111", + "1331311111", "1311113131", "3311113111", "1331113111", "1313131111", + "1313111311", "1311131311", "1113131311" + }; + + private static final char[] LOOKUP = { + '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D', + 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R', + 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z', '-', '.', ' ', '$', '/', '+', + '%' + }; + + /** + * Ratio of wide bar width to narrow bar width. + */ + private double moduleWidthRatio = 3; + + /** + * Returns the ratio of wide bar width to narrow bar width. + * + * @return the ratio of wide bar width to narrow bar width + */ + public double getModuleWidthRatio() { + return moduleWidthRatio; + } + + /** + * Sets the ratio of wide bar width to narrow bar width. Valid values are usually + * between {@code 2} and {@code 3}. The default value is {@code 3}. + * + * @param moduleWidthRatio the ratio of wide bar width to narrow bar width + */ + public void setModuleWidthRatio(double moduleWidthRatio) { + this.moduleWidthRatio = moduleWidthRatio; + } + + @Override + protected double getModuleWidth(int originalWidth) { + if (originalWidth == 1) { + return 1; + } else { + return moduleWidthRatio; + } + } + + @Override + public boolean encode() { + + if (!(content.matches("[0-9A-Z\\. \\-$/+%]+"))) { + errorMsg.append("Invalid characters in input"); + return false; + } + + StringBuilder p = new StringBuilder(); + int l = content.length(); + int charval, counter = 0; + char thischar; + char checkDigit; + for (int i = 0; i < l; i++) { + thischar = content.charAt(i); + charval = positionOf(thischar, LOOKUP); + counter += charval; + p.append(CODE39LM[charval]); + } + + counter = counter % 43; + checkDigit = LOOKUP[counter]; + encodeInfo.append("Check Digit: ").append(checkDigit).append("\n"); + p.append(CODE39LM[counter]); + + readable = new StringBuilder(content).append(checkDigit); + pattern = new String[]{"1311313111" + p + "131131311"}; + rowCount = 1; + rowHeight = new int[]{-1}; + plotSymbol(); + return true; + } +} diff --git a/barcode/src/main/java/org/xbib/graphics/barcode/MaxiCode.java b/barcode/src/main/java/org/xbib/graphics/barcode/MaxiCode.java new file mode 100755 index 0000000..dc7ecb2 --- /dev/null +++ b/barcode/src/main/java/org/xbib/graphics/barcode/MaxiCode.java @@ -0,0 +1,890 @@ +package org.xbib.graphics.barcode; + +import org.xbib.graphics.barcode.util.Hexagon; +import org.xbib.graphics.barcode.util.ReedSolomon; +import java.awt.geom.Ellipse2D; +import java.util.Arrays; + +/** + * Implements MaxiCode according to ISO 16023:2000. + * MaxiCode employs a pattern of hexagons around a central 'bulls-eye' + * finder pattern. Encoding in several modes is supported, but encoding in + * Mode 2 and 3 require primary messages to be set. Input characters can be + * any from the ISO 8859-1 (Latin-1) character set. + * TODO: Add ECI functionality. + */ +public class MaxiCode extends Symbol { + + /** + * MaxiCode module sequence, from ISO/IEC 16023 Figure 5 (30 x 33 data grid). + */ + private static final int[] MAXICODE_GRID = { + 122, 121, 128, 127, 134, 133, 140, 139, 146, 145, 152, 151, 158, 157, 164, 163, 170, 169, 176, 175, 182, 181, 188, 187, 194, 193, 200, 199, 0, 0, + 124, 123, 130, 129, 136, 135, 142, 141, 148, 147, 154, 153, 160, 159, 166, 165, 172, 171, 178, 177, 184, 183, 190, 189, 196, 195, 202, 201, 817, 0, + 126, 125, 132, 131, 138, 137, 144, 143, 150, 149, 156, 155, 162, 161, 168, 167, 174, 173, 180, 179, 186, 185, 192, 191, 198, 197, 204, 203, 819, 818, + 284, 283, 278, 277, 272, 271, 266, 265, 260, 259, 254, 253, 248, 247, 242, 241, 236, 235, 230, 229, 224, 223, 218, 217, 212, 211, 206, 205, 820, 0, + 286, 285, 280, 279, 274, 273, 268, 267, 262, 261, 256, 255, 250, 249, 244, 243, 238, 237, 232, 231, 226, 225, 220, 219, 214, 213, 208, 207, 822, 821, + 288, 287, 282, 281, 276, 275, 270, 269, 264, 263, 258, 257, 252, 251, 246, 245, 240, 239, 234, 233, 228, 227, 222, 221, 216, 215, 210, 209, 823, 0, + 290, 289, 296, 295, 302, 301, 308, 307, 314, 313, 320, 319, 326, 325, 332, 331, 338, 337, 344, 343, 350, 349, 356, 355, 362, 361, 368, 367, 825, 824, + 292, 291, 298, 297, 304, 303, 310, 309, 316, 315, 322, 321, 328, 327, 334, 333, 340, 339, 346, 345, 352, 351, 358, 357, 364, 363, 370, 369, 826, 0, + 294, 293, 300, 299, 306, 305, 312, 311, 318, 317, 324, 323, 330, 329, 336, 335, 342, 341, 348, 347, 354, 353, 360, 359, 366, 365, 372, 371, 828, 827, + 410, 409, 404, 403, 398, 397, 392, 391, 80, 79, 0, 0, 14, 13, 38, 37, 3, 0, 45, 44, 110, 109, 386, 385, 380, 379, 374, 373, 829, 0, + 412, 411, 406, 405, 400, 399, 394, 393, 82, 81, 41, 0, 16, 15, 40, 39, 4, 0, 0, 46, 112, 111, 388, 387, 382, 381, 376, 375, 831, 830, + 414, 413, 408, 407, 402, 401, 396, 395, 84, 83, 42, 0, 0, 0, 0, 0, 6, 5, 48, 47, 114, 113, 390, 389, 384, 383, 378, 377, 832, 0, + 416, 415, 422, 421, 428, 427, 104, 103, 56, 55, 17, 0, 0, 0, 0, 0, 0, 0, 21, 20, 86, 85, 434, 433, 440, 439, 446, 445, 834, 833, + 418, 417, 424, 423, 430, 429, 106, 105, 58, 57, 0, 0, 0, 0, 0, 0, 0, 0, 23, 22, 88, 87, 436, 435, 442, 441, 448, 447, 835, 0, + 420, 419, 426, 425, 432, 431, 108, 107, 60, 59, 0, 0, 0, 0, 0, 0, 0, 0, 0, 24, 90, 89, 438, 437, 444, 443, 450, 449, 837, 836, + 482, 481, 476, 475, 470, 469, 49, 0, 31, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 54, 53, 464, 463, 458, 457, 452, 451, 838, 0, + 484, 483, 478, 477, 472, 471, 50, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 466, 465, 460, 459, 454, 453, 840, 839, + 486, 485, 480, 479, 474, 473, 52, 51, 32, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 0, 43, 468, 467, 462, 461, 456, 455, 841, 0, + 488, 487, 494, 493, 500, 499, 98, 97, 62, 61, 0, 0, 0, 0, 0, 0, 0, 0, 0, 27, 92, 91, 506, 505, 512, 511, 518, 517, 843, 842, + 490, 489, 496, 495, 502, 501, 100, 99, 64, 63, 0, 0, 0, 0, 0, 0, 0, 0, 29, 28, 94, 93, 508, 507, 514, 513, 520, 519, 844, 0, + 492, 491, 498, 497, 504, 503, 102, 101, 66, 65, 18, 0, 0, 0, 0, 0, 0, 0, 19, 30, 96, 95, 510, 509, 516, 515, 522, 521, 846, 845, + 560, 559, 554, 553, 548, 547, 542, 541, 74, 73, 33, 0, 0, 0, 0, 0, 0, 11, 68, 67, 116, 115, 536, 535, 530, 529, 524, 523, 847, 0, + 562, 561, 556, 555, 550, 549, 544, 543, 76, 75, 0, 0, 8, 7, 36, 35, 12, 0, 70, 69, 118, 117, 538, 537, 532, 531, 526, 525, 849, 848, + 564, 563, 558, 557, 552, 551, 546, 545, 78, 77, 0, 34, 10, 9, 26, 25, 0, 0, 72, 71, 120, 119, 540, 539, 534, 533, 528, 527, 850, 0, + 566, 565, 572, 571, 578, 577, 584, 583, 590, 589, 596, 595, 602, 601, 608, 607, 614, 613, 620, 619, 626, 625, 632, 631, 638, 637, 644, 643, 852, 851, + 568, 567, 574, 573, 580, 579, 586, 585, 592, 591, 598, 597, 604, 603, 610, 609, 616, 615, 622, 621, 628, 627, 634, 633, 640, 639, 646, 645, 853, 0, + 570, 569, 576, 575, 582, 581, 588, 587, 594, 593, 600, 599, 606, 605, 612, 611, 618, 617, 624, 623, 630, 629, 636, 635, 642, 641, 648, 647, 855, 854, + 728, 727, 722, 721, 716, 715, 710, 709, 704, 703, 698, 697, 692, 691, 686, 685, 680, 679, 674, 673, 668, 667, 662, 661, 656, 655, 650, 649, 856, 0, + 730, 729, 724, 723, 718, 717, 712, 711, 706, 705, 700, 699, 694, 693, 688, 687, 682, 681, 676, 675, 670, 669, 664, 663, 658, 657, 652, 651, 858, 857, + 732, 731, 726, 725, 720, 719, 714, 713, 708, 707, 702, 701, 696, 695, 690, 689, 684, 683, 678, 677, 672, 671, 666, 665, 660, 659, 654, 653, 859, 0, + 734, 733, 740, 739, 746, 745, 752, 751, 758, 757, 764, 763, 770, 769, 776, 775, 782, 781, 788, 787, 794, 793, 800, 799, 806, 805, 812, 811, 861, 860, + 736, 735, 742, 741, 748, 747, 754, 753, 760, 759, 766, 765, 772, 771, 778, 777, 784, 783, 790, 789, 796, 795, 802, 801, 808, 807, 814, 813, 862, 0, + 738, 737, 744, 743, 750, 749, 756, 755, 762, 761, 768, 767, 774, 773, 780, 779, 786, 785, 792, 791, 798, 797, 804, 803, 810, 809, 816, 815, 864, 863 + }; + + /** + * ASCII character to Code Set mapping, from ISO/IEC 16023 Appendix A. + * 1 = Set A, 2 = Set B, 3 = Set C, 4 = Set D, 5 = Set E. + * 0 refers to special characters that fit into more than one set (e.g. GS). + */ + private static final int[] MAXICODE_SET = { + 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 0, 5, 5, 5, 5, 5, 5, + 5, 5, 5, 5, 5, 5, 5, 5, 0, 0, 0, 5, 0, 2, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 0, 1, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 2, + 2, 2, 2, 2, 2, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 4, 4, + 4, 4, 4, 4, 4, 4, 4, 4, 4, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, + 5, 4, 5, 5, 5, 5, 5, 5, 4, 5, 3, 4, 3, 5, 5, 4, 4, 3, 3, 3, + 4, 3, 5, 4, 4, 3, 3, 4, 3, 3, 3, 4, 3, 3, 3, 3, 3, 3, 3, 3, + 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, + 3, 3, 3, 3, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, + 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4 + }; + + /** + * ASCII character to symbol value, from ISO/IEC 16023 Appendix A. + */ + private static final int[] MAXICODE_SYMBOL_CHAR = { + 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, + 20, 21, 22, 23, 24, 25, 26, 30, 28, 29, 30, 35, 32, 53, 34, 35, 36, 37, 38, 39, + 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 37, + 38, 39, 40, 41, 52, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, + 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 42, 43, 44, 45, 46, 0, 1, 2, 3, + 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, + 24, 25, 26, 32, 54, 34, 35, 36, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 47, 48, + 49, 50, 51, 52, 53, 54, 55, 56, 57, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 36, + 37, 37, 38, 39, 40, 41, 42, 43, 38, 44, 37, 39, 38, 45, 46, 40, 41, 39, 40, 41, + 42, 42, 47, 43, 44, 43, 44, 45, 45, 46, 47, 46, 0, 1, 2, 3, 4, 5, 6, 7, + 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 32, + 33, 34, 35, 36, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, + 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 32, 33, 34, 35, 36 + }; + + private int mode; + private int structuredAppendPosition = 1; + private int structuredAppendTotal = 1; + private String primaryData = ""; + private int[] codewords; + private int[] source; + private final int[] set = new int[144]; + private final int[] character = new int[144]; + private final boolean[][] grid = new boolean[33][30]; + + /** + * Returns the primary message codewords for mode 2. + * + * @param postcode the postal code + * @param country the country code + * @param service the service code + * @return the primary message, as codewords + */ + private static int[] getMode2PrimaryCodewords(String postcode, int country, int service) { + for (int i = 0; i < postcode.length(); i++) { + if (postcode.charAt(i) < '0' || postcode.charAt(i) > '9') { + postcode = postcode.substring(0, i); + break; + } + } + int postcodeNum = Integer.parseInt(postcode); + int[] primary = new int[10]; + primary[0] = ((postcodeNum & 0x03) << 4) | 2; + primary[1] = ((postcodeNum & 0xfc) >> 2); + primary[2] = ((postcodeNum & 0x3f00) >> 8); + primary[3] = ((postcodeNum & 0xfc000) >> 14); + primary[4] = ((postcodeNum & 0x3f00000) >> 20); + primary[5] = ((postcodeNum & 0x3c000000) >> 26) | ((postcode.length() & 0x3) << 4); + primary[6] = ((postcode.length() & 0x3c) >> 2) | ((country & 0x3) << 4); + primary[7] = (country & 0xfc) >> 2; + primary[8] = ((country & 0x300) >> 8) | ((service & 0xf) << 2); + primary[9] = ((service & 0x3f0) >> 4); + + return primary; + } + + /** + * Returns the primary message codewords for mode 3. + * + * @param postcode the postal code + * @param country the country code + * @param service the service code + * @return the primary message, as codewords + */ + private static int[] getMode3PrimaryCodewords(String postcode, int country, int service) { + + int[] postcodeNums = new int[postcode.length()]; + + postcode = postcode.toUpperCase(); + for (int i = 0; i < postcodeNums.length; i++) { + postcodeNums[i] = postcode.charAt(i); + if (postcode.charAt(i) >= 'A' && postcode.charAt(i) <= 'Z') { + // (Capital) letters shifted to Code Set A values + postcodeNums[i] -= 64; + } + if (postcodeNums[i] == 27 || postcodeNums[i] == 31 || postcodeNums[i] == 33 || postcodeNums[i] >= 59) { + // Not a valid postal code character, use space instead + postcodeNums[i] = 32; + } + // Input characters lower than 27 (NUL - SUB) in postal code are interpreted as capital + // letters in Code Set A (e.g. LF becomes 'J') + } + + int[] primary = new int[10]; + primary[0] = ((postcodeNums[5] & 0x03) << 4) | 3; + primary[1] = ((postcodeNums[4] & 0x03) << 4) | ((postcodeNums[5] & 0x3c) >> 2); + primary[2] = ((postcodeNums[3] & 0x03) << 4) | ((postcodeNums[4] & 0x3c) >> 2); + primary[3] = ((postcodeNums[2] & 0x03) << 4) | ((postcodeNums[3] & 0x3c) >> 2); + primary[4] = ((postcodeNums[1] & 0x03) << 4) | ((postcodeNums[2] & 0x3c) >> 2); + primary[5] = ((postcodeNums[0] & 0x03) << 4) | ((postcodeNums[1] & 0x3c) >> 2); + primary[6] = ((postcodeNums[0] & 0x3c) >> 2) | ((country & 0x3) << 4); + primary[7] = (country & 0xfc) >> 2; + primary[8] = ((country & 0x300) >> 8) | ((service & 0xf) << 2); + primary[9] = ((service & 0x3f0) >> 4); + + return primary; + } + + /** + * Returns the error correction codewords for the specified data codewords. + * + * @param codewords the codewords that we need error correction codewords for + * @param ecclen the number of error correction codewords needed + * @return the error correction codewords for the specified data codewords + */ + private static int[] getErrorCorrection(int[] codewords, int ecclen) { + + ReedSolomon rs = new ReedSolomon(); + rs.init_gf(0x43); + rs.init_code(ecclen, 1); + rs.encode(codewords.length, codewords); + + int[] results = new int[ecclen]; + for (int i = 0; i < ecclen; i++) { + results[i] = rs.getResult(results.length - 1 - i); + } + + return results; + } + + /** + * Returns the MaxiCode mode being used. Only modes 2 to 6 are supported. + * + * @return the MaxiCode mode being used + */ + public int getMode() { + return mode; + } + + /** + * Sets the MaxiCode mode to use. Only modes 2 to 6 are supported. + * + * @param mode the MaxiCode mode to use + */ + public void setMode(int mode) { + if (mode < 2 || mode > 6) { + throw new IllegalArgumentException("Invalid MaxiCode mode: " + mode); + } + this.mode = mode; + } + + /** + * Returns the position of this MaxiCode symbol in a series of symbols using structured append. If this symbol is not part of + * such a series, this method will return 1. + * + * @return the position of this MaxiCode symbol in a series of symbols using structured append + */ + public int getStructuredAppendPosition() { + return structuredAppendPosition; + } + + /** + * If this MaxiCode symbol is part of a series of MaxiCode symbols appended in a structured format, this method sets the + * position of this symbol in the series. Valid values are 1 through 8 inclusive. + * + * @param position the position of this MaxiCode symbol in the structured append series + */ + public void setStructuredAppendPosition(int position) { + if (position < 1 || position > 8) { + throw new IllegalArgumentException("Invalid MaxiCode structured append position: " + position); + } + this.structuredAppendPosition = position; + } + + /** + * Returns the size of the series of MaxiCode symbols using structured append that this symbol is part of. If this symbol is + * not part of a structured append series, this method will return 1. + * + * @return size of the series that this symbol is part of + */ + public int getStructuredAppendTotal() { + return structuredAppendTotal; + } + + /** + * If this MaxiCode symbol is part of a series of MaxiCode symbols appended in a structured format, this method sets the total + * number of symbols in the series. Valid values are 1 through 8 inclusive. A value of 1 indicates that this symbol is not + * part of a structured append series. + * + * @param total the total number of MaxiCode symbols in the structured append series + */ + public void setStructuredAppendTotal(int total) { + if (total < 1 || total > 8) { + throw new IllegalArgumentException("Invalid MaxiCode structured append total: " + total); + } + this.structuredAppendTotal = total; + } + + /** + * Returns the primary data for this MaxiCode symbol. Should only be used for modes 2 and 3. + * + * @return the primary data for this MaxiCode symbol + */ + public String getPrimary() { + return primaryData; + } + + /** + * Sets the primary data. Should only be used for modes 2 and 3. Must conform to the following structure: + * + * + * + * + * + *
Expected primary data structure
CharactersMeaning
1-9Postal code data which can consist of up to 9 digits (for mode 2) or up to 6 + * alphanumeric characters (for mode 3). Remaining unused characters should be + * filled with the SPACE character (ASCII 32).
10-12Three-digit country code according to ISO-3166.
13-15Three digit service code. This depends on your parcel courier.
+ * + * @param primary the primary data + */ + public void setPrimary(String primary) { + primaryData = primary; + } + + @Override + public boolean encode() { + + // copy input data over into source + int sourcelen = content.length(); + source = new int[sourcelen]; + + eciProcess(); + + for (int i = 0; i < sourcelen; i++) { + source[i] = inputBytes[i] & 0xFF; + } + + // mode 2 -> mode 3 if postal code isn't strictly numeric + if (mode == 2) { + for (int i = 0; i < 10 && i < primaryData.length(); i++) { + if ((primaryData.charAt(i) < '0') || (primaryData.charAt(i) > '9')) { + mode = 3; + break; + } + } + } + + // initialize the set and character arrays + if (!processText()) { + errorMsg.append("Input data too long"); + return false; + } + + // start building the codeword array, starting with a copy of the character data + // insert primary message if this is a structured carrier message; insert mode otherwise + codewords = Arrays.copyOf(character, character.length); + if (mode == 2 || mode == 3) { + int[] primary = getPrimaryCodewords(); + if (primary == null) { + return false; + } + codewords = insert(codewords, 0, primary); + } else { + codewords = insert(codewords, 0, new int[]{mode}); + } + + // insert structured append flag if necessary + if (structuredAppendTotal > 1) { + + int[] flag = new int[2]; + flag[0] = 33; // padding + flag[1] = ((structuredAppendPosition - 1) << 3) | (structuredAppendTotal - 1); // position + total + + int index; + if (mode == 2 || mode == 3) { + index = 10; // first two data symbols in the secondary message + } else { + index = 1; // first two data symbols in the primary message (first symbol at index 0 isn't a data symbol) + } + + codewords = insert(codewords, index, flag); + } + + int secondaryMax, secondaryECMax; + if (mode == 5) { + // 68 data codewords, 56 error corrections in secondary message + secondaryMax = 68; + secondaryECMax = 56; + } else { + // 84 data codewords, 40 error corrections in secondary message + secondaryMax = 84; + secondaryECMax = 40; + } + + // truncate data codewords to maximum data space available + int totalMax = secondaryMax + 10; + if (codewords.length > totalMax) { + codewords = Arrays.copyOfRange(codewords, 0, totalMax); + } + + // insert primary error correction between primary message and secondary message (always EEC) + int[] primary = Arrays.copyOfRange(codewords, 0, 10); + int[] primaryCheck = getErrorCorrection(primary, 10); + codewords = insert(codewords, 10, primaryCheck); + + // calculate secondary error correction + int[] secondary = Arrays.copyOfRange(codewords, 20, codewords.length); + int[] secondaryOdd = new int[secondary.length / 2]; + int[] secondaryEven = new int[secondary.length / 2]; + for (int i = 0; i < secondary.length; i++) { + if ((i & 1) != 0) { // odd + secondaryOdd[(i - 1) / 2] = secondary[i]; + } else { // even + secondaryEven[i / 2] = secondary[i]; + } + } + int[] secondaryECOdd = getErrorCorrection(secondaryOdd, secondaryECMax / 2); + int[] secondaryECEven = getErrorCorrection(secondaryEven, secondaryECMax / 2); + + // add secondary error correction after secondary message + codewords = Arrays.copyOf(codewords, codewords.length + secondaryECOdd.length + secondaryECEven.length); + for (int i = 0; i < secondaryECOdd.length; i++) { + codewords[20 + secondaryMax + (2 * i) + 1] = secondaryECOdd[i]; + } + for (int i = 0; i < secondaryECEven.length; i++) { + codewords[20 + secondaryMax + (2 * i)] = secondaryECEven[i]; + } + + encodeInfo.append("Mode: ").append(mode).append("\n"); + encodeInfo.append("ECC Codewords: ").append(secondaryECMax).append("\n"); + encodeInfo.append("Codewords: "); + for (int codeword : codewords) { + encodeInfo.append(Integer.toString(codeword)).append(" "); + } + encodeInfo.append("\n"); + + // copy data into symbol grid + int[] bit_pattern = new int[7]; + for (int i = 0; i < 33; i++) { + for (int j = 0; j < 30; j++) { + + int block = (MAXICODE_GRID[(i * 30) + j] + 5) / 6; + int bit = (MAXICODE_GRID[(i * 30) + j] + 5) % 6; + + if (block != 0) { + + bit_pattern[0] = (codewords[block - 1] & 0x20) >> 5; + bit_pattern[1] = (codewords[block - 1] & 0x10) >> 4; + bit_pattern[2] = (codewords[block - 1] & 0x8) >> 3; + bit_pattern[3] = (codewords[block - 1] & 0x4) >> 2; + bit_pattern[4] = (codewords[block - 1] & 0x2) >> 1; + bit_pattern[5] = (codewords[block - 1] & 0x1); + + grid[i][j] = bit_pattern[bit] != 0; + } + } + } + + // add orientation markings + grid[0][28] = true; // top right filler + grid[0][29] = true; + grid[9][10] = true; // top left marker + grid[9][11] = true; + grid[10][11] = true; + grid[15][7] = true; // left hand marker + grid[16][8] = true; + grid[16][20] = true; // right hand marker + grid[17][20] = true; + grid[22][10] = true; // bottom left marker + grid[23][10] = true; + grid[22][17] = true; // bottom right marker + grid[23][17] = true; + + // the following is provided for compatibility, but the results are not useful + rowCount = 33; + readable = new StringBuilder(); + pattern = new String[33]; + rowHeight = new int[33]; + for (int i = 0; i < 33; i++) { + StringBuilder bin = new StringBuilder(30); + for (int j = 0; j < 30; j++) { + if (grid[i][j]) { + bin.append("1"); + } else { + bin.append("0"); + } + } + pattern[i] = bin2pat(bin.toString()); + rowHeight[i] = 1; + } + symbolHeight = 72; + symbolWidth = 74; + + plotSymbol(); + + return true; + } + + /** + * Extracts the postal code, country code and service code from the primary data and returns the corresponding primary message + * codewords. + * + * @return the primary message codewords + */ + private int[] getPrimaryCodewords() { + + assert mode == 2 || mode == 3; + + if (primaryData.length() != 15) { + errorMsg.append("Invalid Primary String"); + return null; + } + + for (int i = 9; i < 15; i++) { /* check that country code and service are numeric */ + if ((primaryData.charAt(i) < '0') || (primaryData.charAt(i) > '9')) { + errorMsg.append("Invalid Primary String"); + return null; + } + } + + String postcode; + if (mode == 2) { + postcode = primaryData.substring(0, 9); + int index = postcode.indexOf(' '); + if (index != -1) { + postcode = postcode.substring(0, index); + } + } else { + // if (mode == 3) + postcode = primaryData.substring(0, 6); + } + + int country = Integer.parseInt(primaryData.substring(9, 12)); + int service = Integer.parseInt(primaryData.substring(12, 15)); + + if (mode == 2) { + return getMode2PrimaryCodewords(postcode, country, service); + } else { // mode == 3 + return getMode3PrimaryCodewords(postcode, country, service); + } + } + + /** + * Formats text according to Appendix A, populating the {@link #set} and {@link #character} arrays. + * + * @return true if the content fits in this symbol and was formatted; false otherwise + */ + private boolean processText() { + + int length = content.length(); + int i, j, count, current_set; + + if (length > 138) { + return false; + } + + for (i = 0; i < 144; i++) { + set[i] = -1; + character[i] = 0; + } + + for (i = 0; i < length; i++) { + /* Look up characters in table from Appendix A - this gives + value and code set for most characters */ + set[i] = MAXICODE_SET[source[i]]; + character[i] = MAXICODE_SYMBOL_CHAR[source[i]]; + } + + // If a character can be represented in more than one code set, pick which version to use. + if (set[0] == 0) { + if (character[0] == 13) { + character[0] = 0; + } + set[0] = 1; + } + + for (i = 1; i < length; i++) { + if (set[i] == 0) { + /* Special character that can be represented in more than one code set. */ + if (character[i] == 13) { + /* Carriage Return */ + set[i] = bestSurroundingSet(i, length, 1, 5); + if (set[i] == 5) { + character[i] = 13; + } else { + character[i] = 0; + } + } else if (character[i] == 28) { + /* FS */ + set[i] = bestSurroundingSet(i, length, 1, 2, 3, 4, 5); + if (set[i] == 5) { + character[i] = 32; + } + } else if (character[i] == 29) { + /* GS */ + set[i] = bestSurroundingSet(i, length, 1, 2, 3, 4, 5); + if (set[i] == 5) { + character[i] = 33; + } + } else if (character[i] == 30) { + /* RS */ + set[i] = bestSurroundingSet(i, length, 1, 2, 3, 4, 5); + if (set[i] == 5) { + character[i] = 34; + } + } else if (character[i] == 32) { + /* Space */ + set[i] = bestSurroundingSet(i, length, 1, 2, 3, 4, 5); + if (set[i] == 1) { + character[i] = 32; + } else if (set[i] == 2) { + character[i] = 47; + } else { + character[i] = 59; + } + } else if (character[i] == 44) { + /* Comma */ + set[i] = bestSurroundingSet(i, length, 1, 2); + if (set[i] == 2) { + character[i] = 48; + } + } else if (character[i] == 46) { + /* Full Stop */ + set[i] = bestSurroundingSet(i, length, 1, 2); + if (set[i] == 2) { + character[i] = 49; + } + } else if (character[i] == 47) { + /* Slash */ + set[i] = bestSurroundingSet(i, length, 1, 2); + if (set[i] == 2) { + character[i] = 50; + } + } else if (character[i] == 58) { + /* Colon */ + set[i] = bestSurroundingSet(i, length, 1, 2); + if (set[i] == 2) { + character[i] = 51; + } + } + } + } + + for (i = length; i < set.length; i++) { + /* Add the padding */ + if (set[length - 1] == 2) { + set[i] = 2; + } else { + set[i] = 1; + } + character[i] = 33; + } + + /* Find candidates for number compression (not allowed in primary message in modes 2 and 3). */ + if (mode == 2 || mode == 3) { + j = 9; + } else { + j = 0; + } + count = 0; + for (i = j; i < 143; i++) { + if (set[i] == 1 && character[i] >= 48 && character[i] <= 57) { + /* Character is a number */ + count++; + } else { + count = 0; + } + if (count == 9) { + /* Nine digits in a row can be compressed */ + set[i] = 6; + set[i - 1] = 6; + set[i - 2] = 6; + set[i - 3] = 6; + set[i - 4] = 6; + set[i - 5] = 6; + set[i - 6] = 6; + set[i - 7] = 6; + set[i - 8] = 6; + count = 0; + } + } + + /* Add shift and latch characters */ + current_set = 1; + i = 0; + do { + if ((set[i] != current_set) && (set[i] != 6)) { + switch (set[i]) { + case 1: + if (i + 1 < set.length && set[i + 1] == 1) { + if (i + 2 < set.length && set[i + 2] == 1) { + if (i + 3 < set.length && set[i + 3] == 1) { + /* Latch A */ + insert(i, 63); + current_set = 1; + length++; + i += 3; + } else { + /* 3 Shift A */ + insert(i, 57); + length++; + i += 2; + } + } else { + /* 2 Shift A */ + insert(i, 56); + length++; + i++; + } + } else { + /* Shift A */ + insert(i, 59); + length++; + } + break; + case 2: + if (i + 1 < set.length && set[i + 1] == 2) { + /* Latch B */ + insert(i, 63); + current_set = 2; + length++; + i++; + } else { + /* Shift B */ + insert(i, 59); + length++; + } + break; + case 3: + if (i + 3 < set.length && set[i + 1] == 3 && set[i + 2] == 3 && set[i + 3] == 3) { + /* Lock In C */ + insert(i, 60); + insert(i, 60); + current_set = 3; + length++; + i += 3; + } else { + /* Shift C */ + insert(i, 60); + length++; + } + break; + case 4: + if (i + 3 < set.length && set[i + 1] == 4 && set[i + 2] == 4 && set[i + 3] == 4) { + /* Lock In D */ + insert(i, 61); + insert(i, 61); + current_set = 4; + length++; + i += 3; + } else { + /* Shift D */ + insert(i, 61); + length++; + } + break; + case 5: + if (i + 3 < set.length && set[i + 1] == 5 && set[i + 2] == 5 && set[i + 3] == 5) { + /* Lock In E */ + insert(i, 62); + insert(i, 62); + current_set = 5; + length++; + i += 3; + } else { + /* Shift E */ + insert(i, 62); + length++; + } + break; + default: + throw new IllegalStateException("Unexpected set " + set[i] + " at index " + i + "."); + } + i++; + } + i++; + } while (i < set.length); + + /* Number compression has not been forgotten! It's handled below. */ + i = 0; + do { + if (set[i] == 6) { + /* Number compression */ + int value = 0; + for (j = 0; j < 9; j++) { + value *= 10; + value += (character[i + j] - '0'); + } + character[i] = 31; /* NS */ + character[i + 1] = (value & 0x3f000000) >> 24; + character[i + 2] = (value & 0xfc0000) >> 18; + character[i + 3] = (value & 0x3f000) >> 12; + character[i + 4] = (value & 0xfc0) >> 6; + character[i + 5] = (value & 0x3f); + i += 6; + for (j = i; j < 140; j++) { + set[j] = set[j + 3]; + character[j] = character[j + 3]; + } + length -= 3; + } else { + i++; + } + } while (i < set.length); + + /* Inject ECI codes to beginning of data, according to Table 3 */ + if (eciMode != 3) { + insert(0, 27); // ECI + + if ((eciMode >= 0) && (eciMode <= 31)) { + insert(1, eciMode & 0x1F); + length += 2; + } + + if ((eciMode >= 32) && (eciMode <= 1023)) { + insert(1, 0x20 + (eciMode >> 6)); + insert(2, eciMode & 0x3F); + length += 3; + } + + if ((eciMode >= 1024) && (eciMode <= 32767)) { + insert(1, 0x30 + (eciMode >> 12)); + insert(2, (eciMode >> 6) & 0x3F); + insert(3, eciMode & 0x3F); + length += 4; + } + + if ((eciMode >= 32768) && (eciMode <= 999999)) { + insert(1, 0x38 + (eciMode >> 18)); + insert(2, (eciMode >> 12) & 0x3F); + insert(3, (eciMode >> 6) & 0x3F); + insert(4, eciMode & 0x3F); + length += 5; + } + } + + /* Make sure we haven't exceeded the maximum data length. */ + return ((mode != 2 && mode != 3) || length <= 84) && ((mode != 4 && mode != 6) || length <= 93) && (mode != 5 || length <= 77); + } + + /** + * Guesses the best set to use at the specified index by looking at the surrounding sets. In general, characters in + * lower-numbered sets are more common, so we choose them if we can. If no good surrounding sets can be found, the default + * value returned is the first value from the valid set. + * + * @param index the current index + * @param length the maximum length to look at + * @param valid the valid sets for this index + * @return the best set to use at the specified index + */ + private int bestSurroundingSet(int index, int length, int... valid) { + int option1 = set[index - 1]; + if (index + 1 < length) { + // we have two options to check + int option2 = set[index + 1]; + if (contains(valid, option1) && contains(valid, option2)) { + return Math.min(option1, option2); + } else if (contains(valid, option1)) { + return option1; + } else if (contains(valid, option2)) { + return option2; + } else { + return valid[0]; + } + } else { + // we only have one option to check + if (contains(valid, option1)) { + return option1; + } else { + return valid[0]; + } + } + } + + /** + * Moves everything up so that the specified shift or latch character can be inserted. + * + * @param position the position beyond which everything needs to be shifted + * @param c the latch or shift character to insert at the specified position, after everything has been shifted + */ + private void insert(int position, int c) { + for (int i = 143; i > position; i--) { + set[i] = set[i - 1]; + character[i] = character[i - 1]; + } + character[position] = c; + } + + @Override + protected void plotSymbol() { + + // hexagons + for (int row = 0; row < 33; row++) { + for (int col = 0; col < 30; col++) { + if (grid[row][col]) { + double x = (2.46 * col) + 1.23; + if ((row & 1) != 0) { + x += 1.23; + } + double y = (2.135 * row) + 1.43; + getHexagons().add(new Hexagon(x, y)); + } + } + } + + // circles + double[] radii = {10.85, 8.97, 7.10, 5.22, 3.31, 1.43}; + for (double aRadii : radii) { + Ellipse2D.Double circle = new Ellipse2D.Double(); + circle.setFrameFromCenter(35.76, 35.60, 35.76 + aRadii, 35.60 + aRadii); + getTarget().add(circle); + } + } + + @Override + protected int[] getCodewords() { + return codewords; + } +} diff --git a/barcode/src/main/java/org/xbib/graphics/barcode/MicroQrCode.java b/barcode/src/main/java/org/xbib/graphics/barcode/MicroQrCode.java new file mode 100755 index 0000000..a66698c --- /dev/null +++ b/barcode/src/main/java/org/xbib/graphics/barcode/MicroQrCode.java @@ -0,0 +1,1593 @@ +package org.xbib.graphics.barcode; + +import org.xbib.graphics.barcode.util.ReedSolomon; +import java.io.UnsupportedEncodingException; + +/** + * Implements Micro QR Code + * According to ISO/IEC 18004:2006 + * A miniature version of the QR Code symbol for short messages. + * QR Code symbols can encode characters in the Latin-1 set and Kanji + * characters which are members of the Shift-JIS encoding scheme. + */ +public class MicroQrCode extends Symbol { + /* Table 5 - Encoding/Decoding table for Alphanumeric mode */ + private static final char[] RHODIUM = { + '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D', + 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R', + 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z', ' ', '$', '%', '*', '+', '-', + '.', '/', ':' + }; + private static final int[] QR_ANNEX_C1 = { + /* Micro QR Code format information */ + 0x4445, 0x4172, 0x4e2b, 0x4b1c, 0x55ae, 0x5099, 0x5fc0, 0x5af7, 0x6793, + 0x62a4, 0x6dfd, 0x68ca, 0x7678, 0x734f, 0x7c16, 0x7921, 0x06de, 0x03e9, + 0x0cb0, 0x0987, 0x1735, 0x1202, 0x1d5b, 0x186c, 0x2508, 0x203f, 0x2f66, + 0x2a51, 0x34e3, 0x31d4, 0x3e8d, 0x3bba + }; + private static final int[] MICRO_QR_SIZES = { + 11, 13, 15, 17 + }; + private qrMode[] inputMode; + private StringBuilder binary; + private int[] binaryCount = new int[4]; + private int[] grid; + private int[] eval; + private int preferredVersion; + private EccMode preferredEccLevel = EccMode.L; + + /** + * Sets the preferred symbol size. This value may be ignored if the + * data string is too large to fit into the specified symbol. Input + * values correspond to symbol sizes as shown in the following table. + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + *
Range of Micro QR symbol sizes
InputVersionSymbol Size
1M111 x 11
2M213 x 13
3M315 x 15
4M417 x 17
+ * + * @param version Symbol size + */ + public void setPreferredVersion(int version) { + preferredVersion = version; + } + + /** + * Set the amount of symbol space allocated to error correction. + * Levels are predefined according to the following table: + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + *
Micro QR Error correction levels
ECC LevelError Correction CapacityRecovery Capacity
L (default)Approx 20% of symbolApprox 7%
MApprox 37% of symbolApprox 15%
QApprox 55% of symbolApprox 25%
HApprox 65% of symbolApprox 30%
+ * + * @param eccMode Error correction level + */ + public void setEccMode(EccMode eccMode) { + preferredEccLevel = eccMode; + } + + @Override + public boolean encode() { + int i, j, size; + boolean[] version_valid = new boolean[4]; + int n_count, a_count; + EccMode ecc_level; + int version, autoversion; + int bitmask; + int format, format_full; + StringBuilder bin; + boolean byteModeUsed; + boolean alphanumModeUsed; + boolean kanjiModeUsed; + + if (content.length() > 35) { + errorMsg.append("Input data too long"); + return false; + } + + if (!inputCharCheck()) { + errorMsg.append("Invalid characters in input data"); + return false; + } + + for (i = 0; i < 4; i++) { + version_valid[i] = true; + } + + inputMode = new qrMode[40]; + selectEncodingMode(); + + n_count = 0; + a_count = 0; + for (i = 0; i < content.length(); i++) { + if ((content.charAt(i) >= '0') && (content.charAt(i) <= '9')) { + n_count++; + } + if (isAlphanumeric(content.charAt(i))) { + a_count++; + } + } + + if (a_count == content.length()) { + /* All data can be encoded in Alphanumeric mode */ + for (i = 0; i < content.length(); i++) { + inputMode[i] = qrMode.ALPHANUM; + } + } + + if (n_count == content.length()) { + /* All data can be encoded in Numeric mode */ + for (i = 0; i < content.length(); i++) { + inputMode[i] = qrMode.NUMERIC; + } + } + + byteModeUsed = false; + alphanumModeUsed = false; + kanjiModeUsed = false; + + for (i = 0; i < content.length(); i++) { + if (inputMode[i] == qrMode.BINARY) { + byteModeUsed = true; + } + + if (inputMode[i] == qrMode.ALPHANUM) { + alphanumModeUsed = true; + } + + if (inputMode[i] == qrMode.KANJI) { + kanjiModeUsed = true; + } + } + + getBinaryLength(); + + /* Eliminate possivle versions depending on type of content */ + if (byteModeUsed) { + version_valid[0] = false; + version_valid[1] = false; + } + + if (alphanumModeUsed) { + version_valid[0] = false; + } + + if (kanjiModeUsed) { + version_valid[0] = false; + version_valid[1] = false; + } + + /* Eliminate possible versions depending on length of binary data */ + if (binaryCount[0] > 20) { + version_valid[0] = false; + } + if (binaryCount[1] > 40) { + version_valid[1] = false; + } + if (binaryCount[2] > 84) { + version_valid[2] = false; + } + if (binaryCount[3] > 128) { + errorMsg.append("Input data too long"); + return false; + } + + /* Eliminate possible versions depending on error correction level specified */ + ecc_level = preferredEccLevel; + + if (ecc_level == EccMode.H) { + errorMsg.append("Error correction level H not available"); + return false; + } + + if (ecc_level == EccMode.Q) { + version_valid[0] = false; + version_valid[1] = false; + version_valid[2] = false; + if (binaryCount[3] > 80) { + errorMsg.append("Input data too long"); + return false; + } + } + + if (ecc_level == EccMode.M) { + version_valid[0] = false; + if (binaryCount[1] > 32) { + version_valid[1] = false; + } + if (binaryCount[2] > 68) { + version_valid[2] = false; + } + if (binaryCount[3] > 112) { + errorMsg.append("Input data too long"); + return false; + } + } + + autoversion = 3; + if (version_valid[2]) { + autoversion = 2; + } + if (version_valid[1]) { + autoversion = 1; + } + if (version_valid[0]) { + autoversion = 0; + } + + version = autoversion; + /* Get version from user */ + if ((preferredVersion >= 1) && (preferredVersion <= 4)) { + if (preferredVersion >= autoversion) { + version = preferredVersion; + } + } + + /* If there is enough unused space then increase the error correction level */ + if (version == 3) { + if (binaryCount[3] <= 112) { + ecc_level = EccMode.M; + } + if (binaryCount[3] <= 80) { + ecc_level = EccMode.Q; + } + } + + if (version == 2 && binaryCount[2] <= 68) { + ecc_level = EccMode.M; + } + + if (version == 1 && binaryCount[1] <= 32) { + ecc_level = EccMode.M; + } + + binary = new StringBuilder(); + generateBinary(version); + if (binary.length() > 128) { + errorMsg.append("Input data too long"); + return false; + } + + switch (version) { + case 0: + generateM1Symbol(); + encodeInfo.append("Version: M1\n"); + break; + case 1: + generateM2Symbol(ecc_level); + encodeInfo.append("Version: M2\n"); + encodeInfo.append("ECC Level: ").append(levelToLetter(ecc_level)).append("\n"); + break; + case 2: + generateM3Symbol(ecc_level); + encodeInfo.append("Version: M3\n"); + encodeInfo.append("ECC Level: ").append(levelToLetter(ecc_level)).append("\n"); + break; + case 3: + generateM4Symbol(ecc_level); + encodeInfo.append("Version: M4\n"); + encodeInfo.append("ECC Level: ").append(levelToLetter(ecc_level)).append("\n"); + break; + } + + size = MICRO_QR_SIZES[version]; + + grid = new int[size * size]; + + for (i = 0; i < size; i++) { + for (j = 0; j < size; j++) { + grid[(i * size) + j] = 0; + } + } + + setupBitGrid(size); + populateBitGrid(size); + bitmask = applyBitmask(size); + + encodeInfo.append("Mask Pattern: ").append(Integer.toBinaryString(bitmask)).append("\n"); + + /* Add format data */ + format = 0; + switch (version) { + case 1: + switch (ecc_level) { + case L: + format = 1; + break; + case M: + format = 2; + break; + } + break; + case 2: + switch (ecc_level) { + case L: + format = 3; + break; + case M: + format = 4; + break; + } + break; + case 3: + switch (ecc_level) { + case L: + format = 5; + break; + case M: + format = 6; + break; + case Q: + format = 7; + break; + } + break; + } + + format_full = QR_ANNEX_C1[(format << 2) + bitmask]; + + if ((format_full & 0x4000) != 0) { + grid[(8 * size) + 1] += 0x01; + } + if ((format_full & 0x2000) != 0) { + grid[(8 * size) + 2] += 0x01; + } + if ((format_full & 0x1000) != 0) { + grid[(8 * size) + 3] += 0x01; + } + if ((format_full & 0x800) != 0) { + grid[(8 * size) + 4] += 0x01; + } + if ((format_full & 0x400) != 0) { + grid[(8 * size) + 5] += 0x01; + } + if ((format_full & 0x200) != 0) { + grid[(8 * size) + 6] += 0x01; + } + if ((format_full & 0x100) != 0) { + grid[(8 * size) + 7] += 0x01; + } + if ((format_full & 0x80) != 0) { + grid[(8 * size) + 8] += 0x01; + } + if ((format_full & 0x40) != 0) { + grid[(7 * size) + 8] += 0x01; + } + if ((format_full & 0x20) != 0) { + grid[(6 * size) + 8] += 0x01; + } + if ((format_full & 0x10) != 0) { + grid[(5 * size) + 8] += 0x01; + } + if ((format_full & 0x08) != 0) { + grid[(4 * size) + 8] += 0x01; + } + if ((format_full & 0x04) != 0) { + grid[(3 * size) + 8] += 0x01; + } + if ((format_full & 0x02) != 0) { + grid[(2 * size) + 8] += 0x01; + } + if ((format_full & 0x01) != 0) { + grid[(size) + 8] += 0x01; + } + + readable = new StringBuilder(); + pattern = new String[size]; + rowCount = size; + rowHeight = new int[size]; + for (i = 0; i < size; i++) { + bin = new StringBuilder(); + for (j = 0; j < size; j++) { + if ((grid[(i * size) + j] & 0x01) != 0) { + bin.append("1"); + } else { + bin.append("0"); + } + } + pattern[i] = bin2pat(bin.toString()); + rowHeight[i] = 1; + } + + plotSymbol(); + return true; + } + + private boolean inputCharCheck() { + int qmarkBefore, qmarkAfter; + int i; + byte[] temp; + + /* Check that input includes valid characters */ + + if (content.matches("[\u0000-\u00FF]+")) { + /* All characters in ISO 8859-1 */ + return true; + } + + /* Otherwise check for Shift-JIS characters */ + qmarkBefore = 0; + for (i = 0; i < content.length(); i++) { + if (content.charAt(i) == '?') { + qmarkBefore++; + } + } + + try { + temp = content.getBytes("SJIS"); + } catch (UnsupportedEncodingException e) { + errorMsg.append("Character encoding error"); + return false; + } + + qmarkAfter = 0; + for (i = 0; i < temp.length; i++) { + if (temp[i] == '?') { + qmarkAfter++; + } + } + + /* If these values are the same, conversion was sucessful */ + return (qmarkBefore == qmarkAfter); + } + + private char levelToLetter(EccMode ecc_mode) { + switch (ecc_mode) { + case L: + return 'L'; + case M: + return 'M'; + case Q: + return 'Q'; + case H: + return 'H'; + default: + return ' '; + } + } + + private void selectEncodingMode() { + int i, j; + int mlen; + int length = content.length(); + + for (i = 0; i < length; i++) { + if (content.charAt(i) > 0xff) { + inputMode[i] = qrMode.KANJI; + } else { + inputMode[i] = qrMode.BINARY; + if (isAlphanumeric(content.charAt(i))) { + inputMode[i] = qrMode.ALPHANUM; + } + if ((content.charAt(i) >= '0') && (content.charAt(i) <= '9')) { + inputMode[i] = qrMode.NUMERIC; + } + } + } + + /* If less than 6 numeric digits together then don't use numeric mode */ + for (i = 0; i < length; i++) { + if (inputMode[i] == qrMode.NUMERIC) { + if (((i != 0) && (inputMode[i - 1] != qrMode.NUMERIC)) + || (i == 0)) { + mlen = 0; + while (((mlen + i) < length) + && (inputMode[mlen + i] == qrMode.NUMERIC)) { + mlen++; + } + if (mlen < 6) { + for (j = 0; j < mlen; j++) { + inputMode[i + j] = qrMode.ALPHANUM; + } + } + } + } + } + + /* If less than 4 alphanumeric characters together then don't use alphanumeric mode */ + for (i = 0; i < length; i++) { + if (inputMode[i] == qrMode.ALPHANUM) { + if (((i != 0) && (inputMode[i - 1] != qrMode.ALPHANUM)) + || (i == 0)) { + mlen = 0; + while (((mlen + i) < length) + && (inputMode[mlen + i] == qrMode.ALPHANUM)) { + mlen++; + } + if (mlen < 6) { + for (j = 0; j < mlen; j++) { + inputMode[i + j] = qrMode.BINARY; + } + } + } + } + } + } + + private boolean isAlphanumeric(char cglyph) { + /* Returns true if input glyph is in the Alphanumeric set */ + boolean retval = false; + + if ((cglyph >= '0') && (cglyph <= '9')) { + retval = true; + } + if ((cglyph >= 'A') && (cglyph <= 'Z')) { + retval = true; + } + switch (cglyph) { + case ' ': + case '$': + case '%': + case '*': + case '+': + case '-': + case '.': + case '/': + case ':': + retval = true; + break; + } + + return retval; + } + + private String toBinary(int data, int h) { + StringBuilder argument = new StringBuilder(); + for (; + (h != 0); h >>= 1) { + if ((data & h) != 0) { + argument.append("1"); + } else { + argument.append("0"); + } + } + return argument.toString(); + } + + private void getBinaryLength() { + int i; + qrMode currentMode = qrMode.NULL; + int blockLength; + + /* Always include a terminator */ + for (i = 0; i < 4; i++) { + binaryCount[i] = 0; + } + + for (i = 0; i < content.length(); i++) { + if (currentMode != inputMode[i]) { + + blockLength = 0; + do { + blockLength++; + } while (((i + blockLength) < content.length()) + && (inputMode[i + blockLength] == inputMode[i])); + + switch (inputMode[i]) { + case KANJI: + binaryCount[2] += 5 + (blockLength * 13); + binaryCount[3] += 7 + (blockLength * 13); + + break; + case BINARY: + binaryCount[2] += 6 + (blockLength * 8); + binaryCount[3] += 8 + (blockLength * 8); + break; + case ALPHANUM: + int alphaLength; + + if ((blockLength % 2) == 1) { + /* Odd length block */ + alphaLength = ((blockLength - 1) / 2) * 11; + alphaLength += 6; + } else { + /* Even length block */ + alphaLength = (blockLength / 2) * 11; + } + + binaryCount[1] += 4 + alphaLength; + binaryCount[2] += 6 + alphaLength; + binaryCount[3] += 8 + alphaLength; + break; + case NUMERIC: + int numLength; + + switch (blockLength % 3) { + case 1: + /* one digit left over */ + numLength = ((blockLength - 1) / 3) * 10; + numLength += 4; + break; + case 2: + /* two digits left over */ + numLength = ((blockLength - 2) / 3) * 10; + numLength += 7; + break; + default: + /* blockLength is a multiple of 3 */ + numLength = (blockLength / 3) * 10; + break; + } + + binaryCount[0] += 3 + numLength; + binaryCount[1] += 5 + numLength; + binaryCount[2] += 7 + numLength; + binaryCount[3] += 9 + numLength; + break; + } + currentMode = inputMode[i]; + } + } + + /* Add terminator */ + if (binaryCount[1] < 37) { + binaryCount[1] += 5; + } + + if (binaryCount[2] < 81) { + binaryCount[2] += 7; + } + + if (binaryCount[3] < 125) { + binaryCount[3] += 9; + } + } + + private void generateBinary(int version) { + int position = 0; + int blockLength, i; + qrMode data_block; + int msb, lsb, prod, jis; + String oneChar; + byte[] jisBytes; + int count, first, second, third; + + encodeInfo.append("Encoding: "); + + do { + data_block = inputMode[position]; + blockLength = 0; + do { + blockLength++; + } while (((blockLength + position) < content.length()) + && (inputMode[position + blockLength] == data_block)); + + switch (data_block) { + case KANJI: + /* Kanji mode */ + /* Mode indicator */ + switch (version) { + case 2: + binary.append("11"); + break; + case 3: + binary.append("011"); + break; + } + + /* Character count indicator */ + binary.append(toBinary(blockLength, 1 << version)); /* version = 2..3 */ + + encodeInfo.append("KANJ (").append(Integer.toString(blockLength)).append(") "); + + /* Character representation */ + for (i = 0; i < blockLength; i++) { + oneChar = ""; + oneChar += content.charAt(position + i); + + /* Convert Unicode input to Shift-JIS */ + try { + jisBytes = oneChar.getBytes("SJIS"); + } catch (UnsupportedEncodingException e) { + errorMsg.append("Character encoding error"); + return; + } + + jis = ((jisBytes[0] & 0xFF) << 8) + (jisBytes[1] & 0xFF); + + if (jis > 0x9fff) { + jis -= 0xc140; + } else { + jis -= 0x8140; + } + msb = (jis & 0xff00) >> 8; + lsb = (jis & 0xff); + prod = (msb * 0xc0) + lsb; + + binary.append(toBinary(prod, 0x1000)); + + encodeInfo.append(Integer.toString(prod)).append(" "); + } + + break; + case BINARY: + /* Byte mode */ + /* Mode indicator */ + switch (version) { + case 2: + binary.append("10"); + break; + case 3: + binary.append("010"); + break; + } + + /* Character count indicator */ + binary.append(toBinary(blockLength, 2 << version)); /* version = 2..3 */ + + encodeInfo.append("BYTE (").append(Integer.toString(blockLength)).append(") "); + + /* Character representation */ + for (i = 0; i < blockLength; i++) { + int lbyte = content.charAt(position + i); + + binary.append(toBinary(lbyte, 0x80)); + + encodeInfo.append(Integer.toString(lbyte)).append(" "); + } + + break; + case ALPHANUM: + /* Alphanumeric mode */ + /* Mode indicator */ + switch (version) { + case 1: + binary.append("1"); + break; + case 2: + binary.append("01"); + break; + case 3: + binary.append("001"); + break; + } + + /* Character count indicator */ + binary.append(toBinary(blockLength, 2 << version)); /* version = 1..3 */ + + encodeInfo.append("ALPH (").append(Integer.toString(blockLength)).append(") "); + + /* Character representation */ + i = 0; + while (i < blockLength) { + first = positionOf(content.charAt(position + i), RHODIUM); + count = 1; + prod = first; + + if ((i + 1) < blockLength) { + if (inputMode[position + i + 1] == qrMode.ALPHANUM) { + second = positionOf(content.charAt(position + i + 1), RHODIUM); + count = 2; + prod = (first * 45) + second; + } + } + + binary.append(toBinary(prod, 1 << (5 * count))); /* count = 1..2 */ + + encodeInfo.append(Integer.toString(prod)).append(" "); + + i += 2; + } + + break; + case NUMERIC: + /* Numeric mode */ + /* Mode indicator */ + switch (version) { + case 1: + binary.append("0"); + break; + case 2: + binary.append("00"); + break; + case 3: + binary.append("000"); + break; + } + + /* Character count indicator */ + binary.append(toBinary(blockLength, 4 << version)); /* version = 0..3 */ + + encodeInfo.append("NUMB (").append(Integer.toString(blockLength)).append(") "); + + /* Character representation */ + i = 0; + while (i < blockLength) { + first = Character.getNumericValue(content.charAt(position + i)); + count = 1; + prod = first; + + if ((i + 1) < blockLength) { + if (inputMode[position + i + 1] == qrMode.NUMERIC) { + second = Character.getNumericValue(content.charAt(position + i + 1)); + count = 2; + prod = (prod * 10) + second; + } + } + + if ((i + 2) < blockLength) { + if (inputMode[position + i + 2] == qrMode.NUMERIC) { + third = Character.getNumericValue(content.charAt(position + i + 2)); + count = 3; + prod = (prod * 10) + third; + } + } + + binary.append(toBinary(prod, 1 << (3 * count))); /* count = 1..3 */ + + encodeInfo.append(Integer.toString(prod)).append(" "); + + i += 3; + } + break; + } + + position += blockLength; + } while (position < content.length() - 1); + + /* Add terminator */ + switch (version) { + case 0: + binary.append("000"); + break; + case 1: + if (binary.length() < 37) { + binary.append("00000"); + } + break; + case 2: + if (binary.length() < 81) { + binary.append("0000000"); + } + break; + case 3: + if (binary.length() < 125) { + binary.append("000000000"); + } + break; + } + + encodeInfo.append("\n"); + } + + private void generateM1Symbol() { + int i, latch; + int bits_total, bits_left, remainder; + int data_codewords, ecc_codewords; + int[] data_blocks = new int[4]; + int[] ecc_blocks = new int[3]; + ReedSolomon rs = new ReedSolomon(); + + bits_total = 20; + latch = 0; + + /* Manage last (4-bit) block */ + bits_left = bits_total - binary.length(); + if (bits_left <= 4) { + for (i = 0; i < bits_left; i++) { + binary.append("0"); + } + latch = 1; + } + + if (latch == 0) { + /* Complete current byte */ + remainder = 8 - (binary.length() % 8); + if (remainder == 8) { + remainder = 0; + } + for (i = 0; i < remainder; i++) { + binary.append("0"); + } + + /* Add padding */ + bits_left = bits_total - binary.length(); + if (bits_left > 4) { + remainder = (bits_left - 4) / 8; + for (i = 0; i < remainder; i++) { + if ((i & 1) != 0) { + binary.append("00010001"); + } else { + binary.append("11101100"); + } + } + } + binary.append("0000"); + } + + data_codewords = 3; + ecc_codewords = 2; + + /* Copy data into codewords */ + for (i = 0; i < (data_codewords - 1); i++) { + data_blocks[i] = 0; + if (binary.charAt(i * 8) == '1') { + data_blocks[i] += 0x80; + } + if (binary.charAt((i * 8) + 1) == '1') { + data_blocks[i] += 0x40; + } + if (binary.charAt((i * 8) + 2) == '1') { + data_blocks[i] += 0x20; + } + if (binary.charAt((i * 8) + 3) == '1') { + data_blocks[i] += 0x10; + } + if (binary.charAt((i * 8) + 4) == '1') { + data_blocks[i] += 0x08; + } + if (binary.charAt((i * 8) + 5) == '1') { + data_blocks[i] += 0x04; + } + if (binary.charAt((i * 8) + 6) == '1') { + data_blocks[i] += 0x02; + } + if (binary.charAt((i * 8) + 7) == '1') { + data_blocks[i] += 0x01; + } + } + data_blocks[2] = 0; + if (binary.charAt(16) == '1') { + data_blocks[2] += 0x08; + } + if (binary.charAt(17) == '1') { + data_blocks[2] += 0x04; + } + if (binary.charAt(18) == '1') { + data_blocks[2] += 0x02; + } + if (binary.charAt(19) == '1') { + data_blocks[2] += 0x01; + } + + encodeInfo.append("Codewords: "); + + for (i = 0; i < data_codewords; i++) { + encodeInfo.append(Integer.toString(data_blocks[i])).append(" "); + } + encodeInfo.append("\n"); + + /* Calculate Reed-Solomon error codewords */ + rs.init_gf(0x11d); + rs.init_code(ecc_codewords, 0); + rs.encode(data_codewords, data_blocks); + for (i = 0; i < ecc_codewords; i++) { + ecc_blocks[i] = rs.getResult(i); + } + + /* Add Reed-Solomon codewords to binary data */ + for (i = 0; i < ecc_codewords; i++) { + binary.append(toBinary(ecc_blocks[ecc_codewords - i - 1], 0x80)); + } + } + + private void generateM2Symbol(EccMode ecc_mode) { + int i; + int bits_total, bits_left, remainder; + int data_codewords, ecc_codewords; + int[] data_blocks = new int[6]; + int[] ecc_blocks = new int[7]; + ReedSolomon rs = new ReedSolomon(); + + bits_total = 40; // ecc_mode == EccMode.L + if (ecc_mode == EccMode.M) { + bits_total = 32; + } + + /* Complete current byte */ + remainder = 8 - (binary.length() % 8); + if (remainder == 8) { + remainder = 0; + } + for (i = 0; i < remainder; i++) { + binary.append("0"); + } + + /* Add padding */ + bits_left = bits_total - binary.length(); + remainder = bits_left / 8; + for (i = 0; i < remainder; i++) { + if ((i & 1) != 0) { + binary.append("00010001"); + } else { + binary.append("11101100"); + } + } + + data_codewords = 5; + ecc_codewords = 5; // ecc_mode == EccMode.L + if (ecc_mode == EccMode.M) { + data_codewords = 4; + ecc_codewords = 6; + } + + /* Copy data into codewords */ + for (i = 0; i < data_codewords; i++) { + data_blocks[i] = 0; + if (binary.charAt(i * 8) == '1') { + data_blocks[i] += 0x80; + } + if (binary.charAt((i * 8) + 1) == '1') { + data_blocks[i] += 0x40; + } + if (binary.charAt((i * 8) + 2) == '1') { + data_blocks[i] += 0x20; + } + if (binary.charAt((i * 8) + 3) == '1') { + data_blocks[i] += 0x10; + } + if (binary.charAt((i * 8) + 4) == '1') { + data_blocks[i] += 0x08; + } + if (binary.charAt((i * 8) + 5) == '1') { + data_blocks[i] += 0x04; + } + if (binary.charAt((i * 8) + 6) == '1') { + data_blocks[i] += 0x02; + } + if (binary.charAt((i * 8) + 7) == '1') { + data_blocks[i] += 0x01; + } + } + + encodeInfo.append("Codewords: "); + for (i = 0; i < data_codewords; i++) { + encodeInfo.append(Integer.toString(data_blocks[i])).append(" "); + } + encodeInfo.append("\n"); + + /* Calculate Reed-Solomon error codewords */ + rs.init_gf(0x11d); + rs.init_code(ecc_codewords, 0); + rs.encode(data_codewords, data_blocks); + for (i = 0; i < ecc_codewords; i++) { + ecc_blocks[i] = rs.getResult(i); + } + + /* Add Reed-Solomon codewords to binary data */ + for (i = 0; i < ecc_codewords; i++) { + binary.append(toBinary(ecc_blocks[ecc_codewords - i - 1], 0x80)); + } + } + + private void generateM3Symbol(EccMode ecc_mode) { + int i, latch; + int bits_total, bits_left, remainder; + int data_codewords, ecc_codewords; + int[] data_blocks = new int[12]; + int[] ecc_blocks = new int[12]; + ReedSolomon rs = new ReedSolomon(); + + latch = 0; + + bits_total = 84; // ecc_mode == EccMode.L + if (ecc_mode == EccMode.M) { + bits_total = 68; + } + + /* Manage last (4-bit) block */ + bits_left = bits_total - binary.length(); + if (bits_left <= 4) { + for (i = 0; i < bits_left; i++) { + binary.append("0"); + } + latch = 1; + } + + if (latch == 0) { + /* Complete current byte */ + remainder = 8 - (binary.length() % 8); + if (remainder == 8) { + remainder = 0; + } + for (i = 0; i < remainder; i++) { + binary.append("0"); + } + + /* Add padding */ + bits_left = bits_total - binary.length(); + if (bits_left > 4) { + remainder = (bits_left - 4) / 8; + for (i = 0; i < remainder; i++) { + if ((i & 1) != 0) { + binary.append("00010001"); + } else { + binary.append("11101100"); + } + } + } + binary.append("0000"); + } + + data_codewords = 11; + ecc_codewords = 6; // ecc_mode == EccMode.L + if (ecc_mode == EccMode.M) { + data_codewords = 9; + ecc_codewords = 8; + } + + /* Copy data into codewords */ + for (i = 0; i < (data_codewords - 1); i++) { + data_blocks[i] = 0; + if (binary.charAt(i * 8) == '1') { + data_blocks[i] += 0x80; + } + if (binary.charAt((i * 8) + 1) == '1') { + data_blocks[i] += 0x40; + } + if (binary.charAt((i * 8) + 2) == '1') { + data_blocks[i] += 0x20; + } + if (binary.charAt((i * 8) + 3) == '1') { + data_blocks[i] += 0x10; + } + if (binary.charAt((i * 8) + 4) == '1') { + data_blocks[i] += 0x08; + } + if (binary.charAt((i * 8) + 5) == '1') { + data_blocks[i] += 0x04; + } + if (binary.charAt((i * 8) + 6) == '1') { + data_blocks[i] += 0x02; + } + if (binary.charAt((i * 8) + 7) == '1') { + data_blocks[i] += 0x01; + } + } + + if (ecc_mode == EccMode.L) { + data_blocks[10] = 0; + if (binary.charAt(80) == '1') { + data_blocks[10] += 0x08; + } + if (binary.charAt(81) == '1') { + data_blocks[10] += 0x04; + } + if (binary.charAt(82) == '1') { + data_blocks[10] += 0x02; + } + if (binary.charAt(83) == '1') { + data_blocks[10] += 0x01; + } + } + + if (ecc_mode == EccMode.M) { + data_blocks[8] = 0; + if (binary.charAt(64) == '1') { + data_blocks[8] += 0x08; + } + if (binary.charAt(65) == '1') { + data_blocks[8] += 0x04; + } + if (binary.charAt(66) == '1') { + data_blocks[8] += 0x02; + } + if (binary.charAt(67) == '1') { + data_blocks[8] += 0x01; + } + } + + encodeInfo.append("Codewords: "); + for (i = 0; i < data_codewords; i++) { + encodeInfo.append(Integer.toString(data_blocks[i])).append(" "); + } + encodeInfo.append("\n"); + + /* Calculate Reed-Solomon error codewords */ + rs.init_gf(0x11d); + rs.init_code(ecc_codewords, 0); + rs.encode(data_codewords, data_blocks); + for (i = 0; i < ecc_codewords; i++) { + ecc_blocks[i] = rs.getResult(i); + } + + /* Add Reed-Solomon codewords to binary data */ + for (i = 0; i < ecc_codewords; i++) { + binary.append(toBinary(ecc_blocks[ecc_codewords - i - 1], 0x80)); + } + } + + private void generateM4Symbol(EccMode ecc_mode) { + int i; + int bits_total, bits_left, remainder; + int data_codewords, ecc_codewords; + int[] data_blocks = new int[17]; + int[] ecc_blocks = new int[15]; + ReedSolomon rs = new ReedSolomon(); + + bits_total = 128; // ecc_mode == EccMode.L + if (ecc_mode == EccMode.M) { + bits_total = 112; + } + if (ecc_mode == EccMode.Q) { + bits_total = 80; + } + + /* Complete current byte */ + remainder = 8 - (binary.length() % 8); + if (remainder == 8) { + remainder = 0; + } + for (i = 0; i < remainder; i++) { + binary.append("0"); + } + + /* Add padding */ + bits_left = bits_total - binary.length(); + remainder = bits_left / 8; + for (i = 0; i < remainder; i++) { + if ((i & 1) != 0) { + binary.append("00010001"); + } else { + binary.append("11101100"); + } + } + + data_codewords = 16; + ecc_codewords = 8; // ecc_mode == EccMode.L + if (ecc_mode == EccMode.M) { + data_codewords = 14; + ecc_codewords = 10; + } + if (ecc_mode == EccMode.Q) { + data_codewords = 10; + ecc_codewords = 14; + } + + /* Copy data into codewords */ + for (i = 0; i < data_codewords; i++) { + data_blocks[i] = 0; + if (binary.charAt(i * 8) == '1') { + data_blocks[i] += 0x80; + } + if (binary.charAt((i * 8) + 1) == '1') { + data_blocks[i] += 0x40; + } + if (binary.charAt((i * 8) + 2) == '1') { + data_blocks[i] += 0x20; + } + if (binary.charAt((i * 8) + 3) == '1') { + data_blocks[i] += 0x10; + } + if (binary.charAt((i * 8) + 4) == '1') { + data_blocks[i] += 0x08; + } + if (binary.charAt((i * 8) + 5) == '1') { + data_blocks[i] += 0x04; + } + if (binary.charAt((i * 8) + 6) == '1') { + data_blocks[i] += 0x02; + } + if (binary.charAt((i * 8) + 7) == '1') { + data_blocks[i] += 0x01; + } + } + + encodeInfo.append("Codewords: "); + for (i = 0; i < data_codewords; i++) { + encodeInfo.append(Integer.toString(data_blocks[i])).append(" "); + } + encodeInfo.append("\n"); + + /* Calculate Reed-Solomon error codewords */ + rs.init_gf(0x11d); + rs.init_code(ecc_codewords, 0); + rs.encode(data_codewords, data_blocks); + for (i = 0; i < ecc_codewords; i++) { + ecc_blocks[i] = rs.getResult(i); + } + + /* Add Reed-Solomon codewords to binary data */ + for (i = 0; i < ecc_codewords; i++) { + binary.append(toBinary(ecc_blocks[ecc_codewords - i - 1], 0x80)); + } + } + + private void setupBitGrid(int size) { + int i, toggle = 1; + + /* Add timing patterns */ + for (i = 0; i < size; i++) { + if (toggle == 1) { + grid[i] = 0x21; + grid[(i * size)] = 0x21; + toggle = 0; + } else { + grid[i] = 0x20; + grid[(i * size)] = 0x20; + toggle = 1; + } + } + + /* Add finder patterns */ + placeFinderPattern(size, 0, 0); + + /* Add separators */ + for (i = 0; i < 7; i++) { + grid[(7 * size) + i] = 0x10; + grid[(i * size) + 7] = 0x10; + } + grid[(7 * size) + 7] = 0x10; + + + /* Reserve space for format information */ + for (i = 0; i < 8; i++) { + grid[(8 * size) + i] += 0x20; + grid[(i * size) + 8] += 0x20; + } + grid[(8 * size) + 8] += 0x20; + } + + private void placeFinderPattern(int size, int x, int y) { + int xp, yp; + + int[] finder = { + 1, 1, 1, 1, 1, 1, 1, + 1, 0, 0, 0, 0, 0, 1, + 1, 0, 1, 1, 1, 0, 1, + 1, 0, 1, 1, 1, 0, 1, + 1, 0, 1, 1, 1, 0, 1, + 1, 0, 0, 0, 0, 0, 1, + 1, 1, 1, 1, 1, 1, 1 + }; + + for (xp = 0; xp < 7; xp++) { + for (yp = 0; yp < 7; yp++) { + if (finder[xp + (7 * yp)] == 1) { + grid[((yp + y) * size) + (xp + x)] = 0x11; + } else { + grid[((yp + y) * size) + (xp + x)] = 0x10; + } + } + } + } + + private void populateBitGrid(int size) { + boolean goingUp = true; + int row = 0; /* right hand side */ + + int i, n, x, y; + + n = binary.length(); + y = size - 1; + i = 0; + do { + x = (size - 2) - (row * 2); + + if ((grid[(y * size) + (x + 1)] & 0xf0) == 0) { + if (binary.charAt(i) == '1') { + grid[(y * size) + (x + 1)] = 0x01; + } else { + grid[(y * size) + (x + 1)] = 0x00; + } + i++; + } + + if (i < n) { + if ((grid[(y * size) + x] & 0xf0) == 0) { + if (binary.charAt(i) == '1') { + grid[(y * size) + x] = 0x01; + } else { + grid[(y * size) + x] = 0x00; + } + i++; + } + } + + if (goingUp) { + y--; + } else { + y++; + } + if (y == 0) { + /* reached the top */ + row++; + y = 1; + goingUp = false; + } + if (y == size) { + /* reached the bottom */ + row++; + y = size - 1; + goingUp = true; + } + } while (i < n); + } + + private int applyBitmask(int size) { + int x, y; + int p; + int local_pattern; + int[] value = new int[8]; + int best_val, best_pattern; + int bit; + + int[] mask = new int[size * size]; + eval = new int[size * size]; + + /* Perform data masking */ + for (x = 0; x < size; x++) { + for (y = 0; y < size; y++) { + mask[(y * size) + x] = 0x00; + + if ((grid[(y * size) + x] & 0xf0) == 0) { + if ((y & 1) == 0) { + mask[(y * size) + x] += 0x01; + } + + if ((((y / 2) + (x / 3)) & 1) == 0) { + mask[(y * size) + x] += 0x02; + } + + if (((((y * x) & 1) + ((y * x) % 3)) & 1) == 0) { + mask[(y * size) + x] += 0x04; + } + + if (((((y + x) & 1) + ((y * x) % 3)) & 1) == 0) { + mask[(y * size) + x] += 0x08; + } + } + } + } + + + for (x = 0; x < size; x++) { + for (y = 0; y < size; y++) { + if ((grid[(y * size) + x] & 0x01) != 0) { + p = 0xff; + } else { + p = 0x00; + } + + eval[(y * size) + x] = mask[(y * size) + x] ^ p; + } + } + + /* Evaluate result */ + for (local_pattern = 0; local_pattern < 4; local_pattern++) { + value[local_pattern] = evaluateBitmask(size, local_pattern); + } + + best_pattern = 0; + best_val = value[0]; + for (local_pattern = 1; local_pattern < 4; local_pattern++) { + if (value[local_pattern] > best_val) { + best_pattern = local_pattern; + best_val = value[local_pattern]; + } + } + + /* Apply mask */ + for (x = 0; x < size; x++) { + for (y = 0; y < size; y++) { + bit = 0; + switch (best_pattern) { + case 0: + if ((mask[(y * size) + x] & 0x01) != 0) { + bit = 1; + } + break; + case 1: + if ((mask[(y * size) + x] & 0x02) != 0) { + bit = 1; + } + break; + case 2: + if ((mask[(y * size) + x] & 0x04) != 0) { + bit = 1; + } + break; + case 3: + if ((mask[(y * size) + x] & 0x08) != 0) { + bit = 1; + } + break; + } + if (bit == 1) { + if ((grid[(y * size) + x] & 0x01) != 0) { + grid[(y * size) + x] = 0x00; + } else { + grid[(y * size) + x] = 0x01; + } + } + } + } + + return best_pattern; + } + + private int evaluateBitmask(int size, int pattern) { + int sum1, sum2, i, filter = 0, retval; + + switch (pattern) { + case 0: + filter = 0x01; + break; + case 1: + filter = 0x02; + break; + case 2: + filter = 0x04; + break; + case 3: + filter = 0x08; + break; + } + + sum1 = 0; + sum2 = 0; + for (i = 1; i < size; i++) { + if ((eval[(i * size) + size - 1] & filter) != 0) { + sum1++; + } + if ((eval[((size - 1) * size) + i] & filter) != 0) { + sum2++; + } + } + + if (sum1 <= sum2) { + retval = (sum1 * 16) + sum2; + } else { + retval = (sum2 * 16) + sum1; + } + + return retval; + } + + private enum qrMode { + NULL, KANJI, BINARY, ALPHANUM, NUMERIC + } + + public enum EccMode { + L, M, Q, H + } +} diff --git a/barcode/src/main/java/org/xbib/graphics/barcode/MsiPlessey.java b/barcode/src/main/java/org/xbib/graphics/barcode/MsiPlessey.java new file mode 100755 index 0000000..45a6f8d --- /dev/null +++ b/barcode/src/main/java/org/xbib/graphics/barcode/MsiPlessey.java @@ -0,0 +1,188 @@ +package org.xbib.graphics.barcode; + +/** + * Implements the MSI (Modified Plessey) bar code symbology. + * MSI Plessey can encode a string of numeric digits and has a range + * of check digit options. + */ +public class MsiPlessey extends Symbol { + + private final String[] MSI_PlessTable = { + "12121212", "12121221", "12122112", "12122121", "12211212", "12211221", + "12212112", "12212121", "21121212", "21121221" + }; + private CheckDigit checkOption; + + public MsiPlessey() { + checkOption = CheckDigit.NONE; + } + + /** + * Set the check digit scheme to use. Options are: None, Modulo-10, + * 2 x Modulo-10, Modulo-11 and Modulo-11 & 10. + * + * @param checkMode Type of check digit to add to symbol + */ + public void setCheckDigit(CheckDigit checkMode) { + checkOption = checkMode; + } + + @Override + public boolean encode() { + StringBuilder intermediate; + int length = content.length(); + int i; + StringBuilder evenString; + StringBuilder oddString; + String addupString; + int spacer; + int addup; + int weight; + int checkDigit1; + int checkDigit2; + + if (!(content.matches("[0-9]+"))) { + errorMsg.append("Invalid characters in input"); + return false; + } + + intermediate = new StringBuilder("21"); // Start + for (i = 0; i < length; i++) { + intermediate.append(MSI_PlessTable[Character.getNumericValue(content.charAt(i))]); + } + + readable = new StringBuilder(content); + + if ((checkOption == CheckDigit.MOD10) || (checkOption == CheckDigit.MOD10_MOD10)) { + /* Add Modulo-10 check digit */ + evenString = new StringBuilder(); + oddString = new StringBuilder(); + + spacer = content.length() & 1; + + for (i = content.length() - 1; i >= 0; i--) { + if (spacer == 1) { + if ((i & 1) != 0) { + evenString.insert(0, content.charAt(i)); + } else { + oddString.insert(0, content.charAt(i)); + } + } else { + if ((i & 1) != 0) { + oddString.insert(0, content.charAt(i)); + } else { + evenString.insert(0, content.charAt(i)); + } + } + } + + if (oddString.length() == 0) { + addupString = "0"; + } else { + addupString = Integer.toString(Integer.parseInt(oddString.toString()) * 2); + } + + addupString += evenString; + + addup = 0; + for (i = 0; i < addupString.length(); i++) { + addup += addupString.charAt(i) - '0'; + } + + checkDigit1 = 10 - (addup % 10); + if (checkDigit1 == 10) { + checkDigit1 = 0; + } + + intermediate.append(MSI_PlessTable[checkDigit1]); + readable.append(checkDigit1); + } + + if ((checkOption == CheckDigit.MOD11) || (checkOption == CheckDigit.MOD11_MOD10)) { + /* Add a Modulo-11 check digit */ + weight = 2; + addup = 0; + for (i = content.length() - 1; i >= 0; i--) { + addup += (content.charAt(i) - '0') * weight; + weight++; + + if (weight == 8) { + weight = 2; + } + } + + checkDigit1 = 11 - (addup % 11); + + if (checkDigit1 == 11) { + checkDigit1 = 0; + } + + readable.append(checkDigit1); + if (checkDigit1 == 10) { + intermediate.append(MSI_PlessTable[1]); + intermediate.append(MSI_PlessTable[0]); + } else { + intermediate.append(MSI_PlessTable[checkDigit1]); + } + } + + if ((checkOption == CheckDigit.MOD10_MOD10) || (checkOption == CheckDigit.MOD11_MOD10)) { + /* Add a second Modulo-10 check digit */ + evenString = new StringBuilder(); + oddString = new StringBuilder(); + + spacer = readable.length() & 1; + + for (i = readable.length() - 1; i >= 0; i--) { + if (spacer == 1) { + if ((i & 1) != 0) { + evenString.insert(0, readable.charAt(i)); + } else { + oddString.insert(0, readable.charAt(i)); + } + } else { + if ((i & 1) != 0) { + oddString.insert(0, readable.charAt(i)); + } else { + evenString.insert(0, readable.charAt(i)); + } + } + } + + if (oddString.length() == 0) { + addupString = "0"; + } else { + addupString = Integer.toString(Integer.parseInt(oddString.toString()) * 2); + } + + addupString += evenString; + + addup = 0; + for (i = 0; i < addupString.length(); i++) { + addup += addupString.charAt(i) - '0'; + } + + checkDigit2 = 10 - (addup % 10); + if (checkDigit2 == 10) { + checkDigit2 = 0; + } + + intermediate.append(MSI_PlessTable[checkDigit2]); + readable.append(checkDigit2); + } + + intermediate.append("121"); // Stop + + pattern = new String[1]; + pattern[0] = intermediate.toString(); + rowCount = 1; + rowHeight = new int[1]; + rowHeight[0] = -1; + plotSymbol(); + return true; + } + + public enum CheckDigit { + NONE, MOD10, MOD10_MOD10, MOD11, MOD11_MOD10 + } +} diff --git a/barcode/src/main/java/org/xbib/graphics/barcode/Nve18.java b/barcode/src/main/java/org/xbib/graphics/barcode/Nve18.java new file mode 100755 index 0000000..021f732 --- /dev/null +++ b/barcode/src/main/java/org/xbib/graphics/barcode/Nve18.java @@ -0,0 +1,72 @@ +package org.xbib.graphics.barcode; + +import java.util.stream.IntStream; + +/** + * Calculate NVE-18 (Nummer der Versandeinheit) + * Also SSCC-18 (Serial Shipping Container Code) + * Encodes a 17 digit number, adding a Modulo-10 check digit. + */ +public class Nve18 extends Symbol { + + @Override + public boolean encode() { + StringBuilder gs1Equivalent = new StringBuilder(); + int zeroes; + int count = 0; + int c, cdigit; + int p = 0; + Code128 code128 = new Code128(); + + if (content.length() > 17) { + errorMsg.append("Input data too long"); + return false; + } + + if (!(content.matches("[0-9]+"))) { + errorMsg.append("Invalid characters in input"); + return false; + } + + // Add leading zeroes + zeroes = 17 - content.length(); + IntStream.range(0, zeroes).mapToObj(i -> "0").forEach(gs1Equivalent::append); + gs1Equivalent.append(content); + // Add Modulus-10 check digit + for (int i = gs1Equivalent.length() - 1; i >= 0; i--) { + c = Character.getNumericValue(gs1Equivalent.charAt(i)); + if ((p % 2) == 0) { + c = c * 3; + } + count += c; + p++; + } + cdigit = 10 - (count % 10); + if (cdigit == 10) { + cdigit = 0; + } + + encodeInfo.append("NVE Check Digit: ").append(cdigit).append("\n"); + + content = "[00]" + gs1Equivalent + cdigit; + + // Defer to Code 128 + code128.setDataType(DataType.GS1); + code128.setHumanReadableLocation(getHumanReadableLocation()); + + try { + code128.setContent(content); + } catch (Exception e) { + errorMsg.append(e.getMessage()); + return false; + } + getRectangles().clear(); + getRectangles().addAll(code128.getRectangles()); + getTexts().clear(); + getTexts().addAll(code128.getTexts()); + symbolHeight = code128.symbolHeight; + symbolWidth = code128.symbolWidth; + encodeInfo.append(code128.encodeInfo); + return true; + } +} diff --git a/barcode/src/main/java/org/xbib/graphics/barcode/Pdf417.java b/barcode/src/main/java/org/xbib/graphics/barcode/Pdf417.java new file mode 100755 index 0000000..32922ef --- /dev/null +++ b/barcode/src/main/java/org/xbib/graphics/barcode/Pdf417.java @@ -0,0 +1,1704 @@ +package org.xbib.graphics.barcode; + +import java.math.BigInteger; +import java.util.ArrayList; +import java.util.List; + +/** + * Implements PDF417 bar code symbology and MicroPDF417 bar code symbology + * according to ISO/IEC 15438:2006 and ISO/IEC 24728:2006 respectively. + * PDF417 supports encoding up to the ISO standard maximum symbol size of 925 + * codewords which (at error correction level 0) allows a maximum data size + * of 1850 text characters, or 2710 digits. The maximum size MicroPDF417 symbol + * can hold 250 alphanumeric characters or 366 digits. + */ +public class Pdf417 extends Symbol { + + private static final int MAX_NUMERIC_COMPACTION_BLOCK_SIZE = 44; + + private static final int[] COEFRS = { + /* k = 2 */ + 27, 917, + + /* k = 4 */ + 522, 568, 723, 809, + + /* k = 8 */ + 237, 308, 436, 284, 646, 653, 428, 379, + + /* k = 16 */ + 274, 562, 232, 755, 599, 524, 801, 132, 295, 116, 442, 428, 295, 42, 176, 65, + + /* k = 32 */ + 361, 575, 922, 525, 176, 586, 640, 321, 536, 742, 677, 742, 687, 284, 193, 517, + 273, 494, 263, 147, 593, 800, 571, 320, 803, 133, 231, 390, 685, 330, 63, 410, + + /* k = 64 */ + 539, 422, 6, 93, 862, 771, 453, 106, 610, 287, 107, 505, 733, 877, 381, 612, + 723, 476, 462, 172, 430, 609, 858, 822, 543, 376, 511, 400, 672, 762, 283, 184, + 440, 35, 519, 31, 460, 594, 225, 535, 517, 352, 605, 158, 651, 201, 488, 502, + 648, 733, 717, 83, 404, 97, 280, 771, 840, 629, 4, 381, 843, 623, 264, 543, + + /* k = 128 */ + 521, 310, 864, 547, 858, 580, 296, 379, 53, 779, 897, 444, 400, 925, 749, 415, + 822, 93, 217, 208, 928, 244, 583, 620, 246, 148, 447, 631, 292, 908, 490, 704, + 516, 258, 457, 907, 594, 723, 674, 292, 272, 96, 684, 432, 686, 606, 860, 569, + 193, 219, 129, 186, 236, 287, 192, 775, 278, 173, 40, 379, 712, 463, 646, 776, + 171, 491, 297, 763, 156, 732, 95, 270, 447, 90, 507, 48, 228, 821, 808, 898, + 784, 663, 627, 378, 382, 262, 380, 602, 754, 336, 89, 614, 87, 432, 670, 616, + 157, 374, 242, 726, 600, 269, 375, 898, 845, 454, 354, 130, 814, 587, 804, 34, + 211, 330, 539, 297, 827, 865, 37, 517, 834, 315, 550, 86, 801, 4, 108, 539, + + /* k = 256 */ + 524, 894, 75, 766, 882, 857, 74, 204, 82, 586, 708, 250, 905, 786, 138, 720, + 858, 194, 311, 913, 275, 190, 375, 850, 438, 733, 194, 280, 201, 280, 828, 757, + 710, 814, 919, 89, 68, 569, 11, 204, 796, 605, 540, 913, 801, 700, 799, 137, + 439, 418, 592, 668, 353, 859, 370, 694, 325, 240, 216, 257, 284, 549, 209, 884, + 315, 70, 329, 793, 490, 274, 877, 162, 749, 812, 684, 461, 334, 376, 849, 521, + 307, 291, 803, 712, 19, 358, 399, 908, 103, 511, 51, 8, 517, 225, 289, 470, + 637, 731, 66, 255, 917, 269, 463, 830, 730, 433, 848, 585, 136, 538, 906, 90, + 2, 290, 743, 199, 655, 903, 329, 49, 802, 580, 355, 588, 188, 462, 10, 134, + 628, 320, 479, 130, 739, 71, 263, 318, 374, 601, 192, 605, 142, 673, 687, 234, + 722, 384, 177, 752, 607, 640, 455, 193, 689, 707, 805, 641, 48, 60, 732, 621, + 895, 544, 261, 852, 655, 309, 697, 755, 756, 60, 231, 773, 434, 421, 726, 528, + 503, 118, 49, 795, 32, 144, 500, 238, 836, 394, 280, 566, 319, 9, 647, 550, + 73, 914, 342, 126, 32, 681, 331, 792, 620, 60, 609, 441, 180, 791, 893, 754, + 605, 383, 228, 749, 760, 213, 54, 297, 134, 54, 834, 299, 922, 191, 910, 532, + 609, 829, 189, 20, 167, 29, 872, 449, 83, 402, 41, 656, 505, 579, 481, 173, + 404, 251, 688, 95, 497, 555, 642, 543, 307, 159, 924, 558, 648, 55, 497, 10, + + /* k = 512 */ + 352, 77, 373, 504, 35, 599, 428, 207, 409, 574, 118, 498, 285, 380, 350, 492, + 197, 265, 920, 155, 914, 299, 229, 643, 294, 871, 306, 88, 87, 193, 352, 781, + 846, 75, 327, 520, 435, 543, 203, 666, 249, 346, 781, 621, 640, 268, 794, 534, + 539, 781, 408, 390, 644, 102, 476, 499, 290, 632, 545, 37, 858, 916, 552, 41, + 542, 289, 122, 272, 383, 800, 485, 98, 752, 472, 761, 107, 784, 860, 658, 741, + 290, 204, 681, 407, 855, 85, 99, 62, 482, 180, 20, 297, 451, 593, 913, 142, + 808, 684, 287, 536, 561, 76, 653, 899, 729, 567, 744, 390, 513, 192, 516, 258, + 240, 518, 794, 395, 768, 848, 51, 610, 384, 168, 190, 826, 328, 596, 786, 303, + 570, 381, 415, 641, 156, 237, 151, 429, 531, 207, 676, 710, 89, 168, 304, 402, + 40, 708, 575, 162, 864, 229, 65, 861, 841, 512, 164, 477, 221, 92, 358, 785, + 288, 357, 850, 836, 827, 736, 707, 94, 8, 494, 114, 521, 2, 499, 851, 543, + 152, 729, 771, 95, 248, 361, 578, 323, 856, 797, 289, 51, 684, 466, 533, 820, + 669, 45, 902, 452, 167, 342, 244, 173, 35, 463, 651, 51, 699, 591, 452, 578, + 37, 124, 298, 332, 552, 43, 427, 119, 662, 777, 475, 850, 764, 364, 578, 911, + 283, 711, 472, 420, 245, 288, 594, 394, 511, 327, 589, 777, 699, 688, 43, 408, + 842, 383, 721, 521, 560, 644, 714, 559, 62, 145, 873, 663, 713, 159, 672, 729, + 624, 59, 193, 417, 158, 209, 563, 564, 343, 693, 109, 608, 563, 365, 181, 772, + 677, 310, 248, 353, 708, 410, 579, 870, 617, 841, 632, 860, 289, 536, 35, 777, + 618, 586, 424, 833, 77, 597, 346, 269, 757, 632, 695, 751, 331, 247, 184, 45, + 787, 680, 18, 66, 407, 369, 54, 492, 228, 613, 830, 922, 437, 519, 644, 905, + 789, 420, 305, 441, 207, 300, 892, 827, 141, 537, 381, 662, 513, 56, 252, 341, + 242, 797, 838, 837, 720, 224, 307, 631, 61, 87, 560, 310, 756, 665, 397, 808, + 851, 309, 473, 795, 378, 31, 647, 915, 459, 806, 590, 731, 425, 216, 548, 249, + 321, 881, 699, 535, 673, 782, 210, 815, 905, 303, 843, 922, 281, 73, 469, 791, + 660, 162, 498, 308, 155, 422, 907, 817, 187, 62, 16, 425, 535, 336, 286, 437, + 375, 273, 610, 296, 183, 923, 116, 667, 751, 353, 62, 366, 691, 379, 687, 842, + 37, 357, 720, 742, 330, 5, 39, 923, 311, 424, 242, 749, 321, 54, 669, 316, + 342, 299, 534, 105, 667, 488, 640, 672, 576, 540, 316, 486, 721, 610, 46, 656, + 447, 171, 616, 464, 190, 531, 297, 321, 762, 752, 533, 175, 134, 14, 381, 433, + 717, 45, 111, 20, 596, 284, 736, 138, 646, 411, 877, 669, 141, 919, 45, 780, + 407, 164, 332, 899, 165, 726, 600, 325, 498, 655, 357, 752, 768, 223, 849, 647, + 63, 310, 863, 251, 366, 304, 282, 738, 675, 410, 389, 244, 31, 121, 303, 263 + }; + + ; + private static final String[] CODAGEMC = { + "urA", "xfs", "ypy", "unk", "xdw", "yoz", "pDA", "uls", "pBk", "eBA", + "pAs", "eAk", "prA", "uvs", "xhy", "pnk", "utw", "xgz", "fDA", "pls", "fBk", "frA", "pvs", + "uxy", "fnk", "ptw", "uwz", "fls", "psy", "fvs", "pxy", "ftw", "pwz", "fxy", "yrx", "ufk", + "xFw", "ymz", "onA", "uds", "xEy", "olk", "ucw", "dBA", "oks", "uci", "dAk", "okg", "dAc", + "ovk", "uhw", "xaz", "dnA", "ots", "ugy", "dlk", "osw", "ugj", "dks", "osi", "dvk", "oxw", + "uiz", "dts", "owy", "dsw", "owj", "dxw", "oyz", "dwy", "dwj", "ofA", "uFs", "xCy", "odk", + "uEw", "xCj", "clA", "ocs", "uEi", "ckk", "ocg", "ckc", "ckE", "cvA", "ohs", "uay", "ctk", + "ogw", "uaj", "css", "ogi", "csg", "csa", "cxs", "oiy", "cww", "oij", "cwi", "cyy", "oFk", + "uCw", "xBj", "cdA", "oEs", "uCi", "cck", "oEg", "uCb", "ccc", "oEa", "ccE", "oED", "chk", + "oaw", "uDj", "cgs", "oai", "cgg", "oab", "cga", "cgD", "obj", "cib", "cFA", "oCs", "uBi", + "cEk", "oCg", "uBb", "cEc", "oCa", "cEE", "oCD", "cEC", "cas", "cag", "caa", "cCk", "uAr", + "oBa", "oBD", "cCB", "tfk", "wpw", "yez", "mnA", "tds", "woy", "mlk", "tcw", "woj", "FBA", + "mks", "FAk", "mvk", "thw", "wqz", "FnA", "mts", "tgy", "Flk", "msw", "Fks", "Fkg", "Fvk", + "mxw", "tiz", "Fts", "mwy", "Fsw", "Fsi", "Fxw", "myz", "Fwy", "Fyz", "vfA", "xps", "yuy", + "vdk", "xow", "yuj", "qlA", "vcs", "xoi", "qkk", "vcg", "xob", "qkc", "vca", "mfA", "tFs", + "wmy", "qvA", "mdk", "tEw", "wmj", "qtk", "vgw", "xqj", "hlA", "Ekk", "mcg", "tEb", "hkk", + "qsg", "hkc", "EvA", "mhs", "tay", "hvA", "Etk", "mgw", "taj", "htk", "qww", "vij", "hss", + "Esg", "hsg", "Exs", "miy", "hxs", "Eww", "mij", "hww", "qyj", "hwi", "Eyy", "hyy", "Eyj", + "hyj", "vFk", "xmw", "ytj", "qdA", "vEs", "xmi", "qck", "vEg", "xmb", "qcc", "vEa", "qcE", + "qcC", "mFk", "tCw", "wlj", "qhk", "mEs", "tCi", "gtA", "Eck", "vai", "tCb", "gsk", "Ecc", + "mEa", "gsc", "qga", "mED", "EcC", "Ehk", "maw", "tDj", "gxk", "Egs", "mai", "gws", "qii", + "mab", "gwg", "Ega", "EgD", "Eiw", "mbj", "gyw", "Eii", "gyi", "Eib", "gyb", "gzj", "qFA", + "vCs", "xli", "qEk", "vCg", "xlb", "qEc", "vCa", "qEE", "vCD", "qEC", "qEB", "EFA", "mCs", + "tBi", "ghA", "EEk", "mCg", "tBb", "ggk", "qag", "vDb", "ggc", "EEE", "mCD", "ggE", "qaD", + "ggC", "Eas", "mDi", "gis", "Eag", "mDb", "gig", "qbb", "gia", "EaD", "giD", "gji", "gjb", + "qCk", "vBg", "xkr", "qCc", "vBa", "qCE", "vBD", "qCC", "qCB", "ECk", "mBg", "tAr", "gak", + "ECc", "mBa", "gac", "qDa", "mBD", "gaE", "ECC", "gaC", "ECB", "EDg", "gbg", "gba", "gbD", + "vAq", "vAn", "qBB", "mAq", "EBE", "gDE", "gDC", "gDB", "lfA", "sps", "wey", "ldk", "sow", + "ClA", "lcs", "soi", "Ckk", "lcg", "Ckc", "CkE", "CvA", "lhs", "sqy", "Ctk", "lgw", "sqj", + "Css", "lgi", "Csg", "Csa", "Cxs", "liy", "Cww", "lij", "Cwi", "Cyy", "Cyj", "tpk", "wuw", + "yhj", "ndA", "tos", "wui", "nck", "tog", "wub", "ncc", "toa", "ncE", "toD", "lFk", "smw", + "wdj", "nhk", "lEs", "smi", "atA", "Cck", "tqi", "smb", "ask", "ngg", "lEa", "asc", "CcE", + "asE", "Chk", "law", "snj", "axk", "Cgs", "trj", "aws", "nii", "lab", "awg", "Cga", "awa", + "Ciw", "lbj", "ayw", "Cii", "ayi", "Cib", "Cjj", "azj", "vpA", "xus", "yxi", "vok", "xug", + "yxb", "voc", "xua", "voE", "xuD", "voC", "nFA", "tms", "wti", "rhA", "nEk", "xvi", "wtb", + "rgk", "vqg", "xvb", "rgc", "nEE", "tmD", "rgE", "vqD", "nEB", "CFA", "lCs", "sli", "ahA", + "CEk", "lCg", "slb", "ixA", "agk", "nag", "tnb", "iwk", "rig", "vrb", "lCD", "iwc", "agE", + "naD", "iwE", "CEB", "Cas", "lDi", "ais", "Cag", "lDb", "iys", "aig", "nbb", "iyg", "rjb", + "CaD", "aiD", "Cbi", "aji", "Cbb", "izi", "ajb", "vmk", "xtg", "ywr", "vmc", "xta", "vmE", + "xtD", "vmC", "vmB", "nCk", "tlg", "wsr", "rak", "nCc", "xtr", "rac", "vna", "tlD", "raE", + "nCC", "raC", "nCB", "raB", "CCk", "lBg", "skr", "aak", "CCc", "lBa", "iik", "aac", "nDa", + "lBD", "iic", "rba", "CCC", "iiE", "aaC", "CCB", "aaB", "CDg", "lBr", "abg", "CDa", "ijg", + "aba", "CDD", "ija", "abD", "CDr", "ijr", "vlc", "xsq", "vlE", "xsn", "vlC", "vlB", "nBc", + "tkq", "rDc", "nBE", "tkn", "rDE", "vln", "rDC", "nBB", "rDB", "CBc", "lAq", "aDc", "CBE", + "lAn", "ibc", "aDE", "nBn", "ibE", "rDn", "CBB", "ibC", "aDB", "ibB", "aDq", "ibq", "ibn", + "xsf", "vkl", "tkf", "nAm", "nAl", "CAo", "aBo", "iDo", "CAl", "aBl", "kpk", "BdA", "kos", + "Bck", "kog", "seb", "Bcc", "koa", "BcE", "koD", "Bhk", "kqw", "sfj", "Bgs", "kqi", "Bgg", + "kqb", "Bga", "BgD", "Biw", "krj", "Bii", "Bib", "Bjj", "lpA", "sus", "whi", "lok", "sug", + "loc", "sua", "loE", "suD", "loC", "BFA", "kms", "sdi", "DhA", "BEk", "svi", "sdb", "Dgk", + "lqg", "svb", "Dgc", "BEE", "kmD", "DgE", "lqD", "BEB", "Bas", "kni", "Dis", "Bag", "knb", + "Dig", "lrb", "Dia", "BaD", "Bbi", "Dji", "Bbb", "Djb", "tuk", "wxg", "yir", "tuc", "wxa", + "tuE", "wxD", "tuC", "tuB", "lmk", "stg", "nqk", "lmc", "sta", "nqc", "tva", "stD", "nqE", + "lmC", "nqC", "lmB", "nqB", "BCk", "klg", "Dak", "BCc", "str", "bik", "Dac", "lna", "klD", + "bic", "nra", "BCC", "biE", "DaC", "BCB", "DaB", "BDg", "klr", "Dbg", "BDa", "bjg", "Dba", + "BDD", "bja", "DbD", "BDr", "Dbr", "bjr", "xxc", "yyq", "xxE", "yyn", "xxC", "xxB", "ttc", + "wwq", "vvc", "xxq", "wwn", "vvE", "xxn", "vvC", "ttB", "vvB", "llc", "ssq", "nnc", "llE", + "ssn", "rrc", "nnE", "ttn", "rrE", "vvn", "llB", "rrC", "nnB", "rrB", "BBc", "kkq", "DDc", + "BBE", "kkn", "bbc", "DDE", "lln", "jjc", "bbE", "nnn", "BBB", "jjE", "rrn", "DDB", "jjC", + "BBq", "DDq", "BBn", "bbq", "DDn", "jjq", "bbn", "jjn", "xwo", "yyf", "xwm", "xwl", "tso", + "wwf", "vto", "xwv", "vtm", "tsl", "vtl", "lko", "ssf", "nlo", "lkm", "rno", "nlm", "lkl", + "rnm", "nll", "rnl", "BAo", "kkf", "DBo", "lkv", "bDo", "DBm", "BAl", "jbo", "bDm", "DBl", + "jbm", "bDl", "jbl", "DBv", "jbv", "xwd", "vsu", "vst", "nku", "rlu", "rlt", "DAu", "bBu", + "jDu", "jDt", "ApA", "Aok", "keg", "Aoc", "AoE", "AoC", "Aqs", "Aqg", "Aqa", "AqD", "Ari", + "Arb", "kuk", "kuc", "sha", "kuE", "shD", "kuC", "kuB", "Amk", "kdg", "Bqk", "kvg", "kda", + "Bqc", "kva", "BqE", "kvD", "BqC", "AmB", "BqB", "Ang", "kdr", "Brg", "kvr", "Bra", "AnD", + "BrD", "Anr", "Brr", "sxc", "sxE", "sxC", "sxB", "ktc", "lvc", "sxq", "sgn", "lvE", "sxn", + "lvC", "ktB", "lvB", "Alc", "Bnc", "AlE", "kcn", "Drc", "BnE", "AlC", "DrE", "BnC", "AlB", + "DrC", "BnB", "Alq", "Bnq", "Aln", "Drq", "Bnn", "Drn", "wyo", "wym", "wyl", "swo", "txo", + "wyv", "txm", "swl", "txl", "kso", "sgf", "lto", "swv", "nvo", "ltm", "ksl", "nvm", "ltl", + "nvl", "Ako", "kcf", "Blo", "ksv", "Dno", "Blm", "Akl", "bro", "Dnm", "Bll", "brm", "Dnl", + "Akv", "Blv", "Dnv", "brv", "yze", "yzd", "wye", "xyu", "wyd", "xyt", "swe", "twu", "swd", + "vxu", "twt", "vxt", "kse", "lsu", "ksd", "ntu", "lst", "rvu", "ypk", "zew", "xdA", "yos", + "zei", "xck", "yog", "zeb", "xcc", "yoa", "xcE", "yoD", "xcC", "xhk", "yqw", "zfj", "utA", + "xgs", "yqi", "usk", "xgg", "yqb", "usc", "xga", "usE", "xgD", "usC", "uxk", "xiw", "yrj", + "ptA", "uws", "xii", "psk", "uwg", "xib", "psc", "uwa", "psE", "uwD", "psC", "pxk", "uyw", + "xjj", "ftA", "pws", "uyi", "fsk", "pwg", "uyb", "fsc", "pwa", "fsE", "pwD", "fxk", "pyw", + "uzj", "fws", "pyi", "fwg", "pyb", "fwa", "fyw", "pzj", "fyi", "fyb", "xFA", "yms", "zdi", + "xEk", "ymg", "zdb", "xEc", "yma", "xEE", "ymD", "xEC", "xEB", "uhA", "xas", "yni", "ugk", + "xag", "ynb", "ugc", "xaa", "ugE", "xaD", "ugC", "ugB", "oxA", "uis", "xbi", "owk", "uig", + "xbb", "owc", "uia", "owE", "uiD", "owC", "owB", "dxA", "oys", "uji", "dwk", "oyg", "ujb", + "dwc", "oya", "dwE", "oyD", "dwC", "dys", "ozi", "dyg", "ozb", "dya", "dyD", "dzi", "dzb", + "xCk", "ylg", "zcr", "xCc", "yla", "xCE", "ylD", "xCC", "xCB", "uak", "xDg", "ylr", "uac", + "xDa", "uaE", "xDD", "uaC", "uaB", "oik", "ubg", "xDr", "oic", "uba", "oiE", "ubD", "oiC", + "oiB", "cyk", "ojg", "ubr", "cyc", "oja", "cyE", "ojD", "cyC", "cyB", "czg", "ojr", "cza", + "czD", "czr", "xBc", "ykq", "xBE", "ykn", "xBC", "xBB", "uDc", "xBq", "uDE", "xBn", "uDC", + "uDB", "obc", "uDq", "obE", "uDn", "obC", "obB", "cjc", "obq", "cjE", "obn", "cjC", "cjB", + "cjq", "cjn", "xAo", "ykf", "xAm", "xAl", "uBo", "xAv", "uBm", "uBl", "oDo", "uBv", "oDm", + "oDl", "cbo", "oDv", "cbm", "cbl", "xAe", "xAd", "uAu", "uAt", "oBu", "oBt", "wpA", "yes", + "zFi", "wok", "yeg", "zFb", "woc", "yea", "woE", "yeD", "woC", "woB", "thA", "wqs", "yfi", + "tgk", "wqg", "yfb", "tgc", "wqa", "tgE", "wqD", "tgC", "tgB", "mxA", "tis", "wri", "mwk", + "tig", "wrb", "mwc", "tia", "mwE", "tiD", "mwC", "mwB", "FxA", "mys", "tji", "Fwk", "myg", + "tjb", "Fwc", "mya", "FwE", "myD", "FwC", "Fys", "mzi", "Fyg", "mzb", "Fya", "FyD", "Fzi", + "Fzb", "yuk", "zhg", "hjs", "yuc", "zha", "hbw", "yuE", "zhD", "hDy", "yuC", "yuB", "wmk", + "ydg", "zEr", "xqk", "wmc", "zhr", "xqc", "yva", "ydD", "xqE", "wmC", "xqC", "wmB", "xqB", + "tak", "wng", "ydr", "vik", "tac", "wna", "vic", "xra", "wnD", "viE", "taC", "viC", "taB", + "viB", "mik", "tbg", "wnr", "qyk", "mic", "tba", "qyc", "vja", "tbD", "qyE", "miC", "qyC", + "miB", "qyB", "Eyk", "mjg", "tbr", "hyk", "Eyc", "mja", "hyc", "qza", "mjD", "hyE", "EyC", + "hyC", "EyB", "Ezg", "mjr", "hzg", "Eza", "hza", "EzD", "hzD", "Ezr", "ytc", "zgq", "grw", + "ytE", "zgn", "gny", "ytC", "glz", "ytB", "wlc", "ycq", "xnc", "wlE", "ycn", "xnE", "ytn", + "xnC", "wlB", "xnB", "tDc", "wlq", "vbc", "tDE", "wln", "vbE", "xnn", "vbC", "tDB", "vbB", + "mbc", "tDq", "qjc", "mbE", "tDn", "qjE", "vbn", "qjC", "mbB", "qjB", "Ejc", "mbq", "gzc", + "EjE", "mbn", "gzE", "qjn", "gzC", "EjB", "gzB", "Ejq", "gzq", "Ejn", "gzn", "yso", "zgf", + "gfy", "ysm", "gdz", "ysl", "wko", "ycf", "xlo", "ysv", "xlm", "wkl", "xll", "tBo", "wkv", + "vDo", "tBm", "vDm", "tBl", "vDl", "mDo", "tBv", "qbo", "vDv", "qbm", "mDl", "qbl", "Ebo", + "mDv", "gjo", "Ebm", "gjm", "Ebl", "gjl", "Ebv", "gjv", "yse", "gFz", "ysd", "wke", "xku", + "wkd", "xkt", "tAu", "vBu", "tAt", "vBt", "mBu", "qDu", "mBt", "qDt", "EDu", "gbu", "EDt", + "gbt", "ysF", "wkF", "xkh", "tAh", "vAx", "mAx", "qBx", "wek", "yFg", "zCr", "wec", "yFa", + "weE", "yFD", "weC", "weB", "sqk", "wfg", "yFr", "sqc", "wfa", "sqE", "wfD", "sqC", "sqB", + "lik", "srg", "wfr", "lic", "sra", "liE", "srD", "liC", "liB", "Cyk", "ljg", "srr", "Cyc", + "lja", "CyE", "ljD", "CyC", "CyB", "Czg", "ljr", "Cza", "CzD", "Czr", "yhc", "zaq", "arw", + "yhE", "zan", "any", "yhC", "alz", "yhB", "wdc", "yEq", "wvc", "wdE", "yEn", "wvE", "yhn", + "wvC", "wdB", "wvB", "snc", "wdq", "trc", "snE", "wdn", "trE", "wvn", "trC", "snB", "trB", + "lbc", "snq", "njc", "lbE", "snn", "njE", "trn", "njC", "lbB", "njB", "Cjc", "lbq", "azc", + "CjE", "lbn", "azE", "njn", "azC", "CjB", "azB", "Cjq", "azq", "Cjn", "azn", "zio", "irs", + "rfy", "zim", "inw", "rdz", "zil", "ily", "ikz", "ygo", "zaf", "afy", "yxo", "ziv", "ivy", + "adz", "yxm", "ygl", "itz", "yxl", "wco", "yEf", "wto", "wcm", "xvo", "yxv", "wcl", "xvm", + "wtl", "xvl", "slo", "wcv", "tno", "slm", "vro", "tnm", "sll", "vrm", "tnl", "vrl", "lDo", + "slv", "nbo", "lDm", "rjo", "nbm", "lDl", "rjm", "nbl", "rjl", "Cbo", "lDv", "ajo", "Cbm", + "izo", "ajm", "Cbl", "izm", "ajl", "izl", "Cbv", "ajv", "zie", "ifw", "rFz", "zid", "idy", + "icz", "yge", "aFz", "ywu", "ygd", "ihz", "ywt", "wce", "wsu", "wcd", "xtu", "wst", "xtt", + "sku", "tlu", "skt", "vnu", "tlt", "vnt", "lBu", "nDu", "lBt", "rbu", "nDt", "rbt", "CDu", + "abu", "CDt", "iju", "abt", "ijt", "ziF", "iFy", "iEz", "ygF", "ywh", "wcF", "wsh", "xsx", + "skh", "tkx", "vlx", "lAx", "nBx", "rDx", "CBx", "aDx", "ibx", "iCz", "wFc", "yCq", "wFE", + "yCn", "wFC", "wFB", "sfc", "wFq", "sfE", "wFn", "sfC", "sfB", "krc", "sfq", "krE", "sfn", + "krC", "krB", "Bjc", "krq", "BjE", "krn", "BjC", "BjB", "Bjq", "Bjn", "yao", "zDf", "Dfy", + "yam", "Ddz", "yal", "wEo", "yCf", "who", "wEm", "whm", "wEl", "whl", "sdo", "wEv", "svo", + "sdm", "svm", "sdl", "svl", "kno", "sdv", "lro", "knm", "lrm", "knl", "lrl", "Bbo", "knv", + "Djo", "Bbm", "Djm", "Bbl", "Djl", "Bbv", "Djv", "zbe", "bfw", "npz", "zbd", "bdy", "bcz", + "yae", "DFz", "yiu", "yad", "bhz", "yit", "wEe", "wgu", "wEd", "wxu", "wgt", "wxt", "scu", + "stu", "sct", "tvu", "stt", "tvt", "klu", "lnu", "klt", "nru", "lnt", "nrt", "BDu", "Dbu", + "BDt", "bju", "Dbt", "bjt", "jfs", "rpy", "jdw", "roz", "jcy", "jcj", "zbF", "bFy", "zjh", + "jhy", "bEz", "jgz", "yaF", "yih", "yyx", "wEF", "wgh", "wwx", "xxx", "sch", "ssx", "ttx", + "vvx", "kkx", "llx", "nnx", "rrx", "BBx", "DDx", "bbx", "jFw", "rmz", "jEy", "jEj", "bCz", + "jaz", "jCy", "jCj", "jBj", "wCo", "wCm", "wCl", "sFo", "wCv", "sFm", "sFl", "kfo", "sFv", + "kfm", "kfl", "Aro", "kfv", "Arm", "Arl", "Arv", "yDe", "Bpz", "yDd", "wCe", "wau", "wCd", + "wat", "sEu", "shu", "sEt", "sht", "kdu", "kvu", "kdt", "kvt", "Anu", "Bru", "Ant", "Brt", + "zDp", "Dpy", "Doz", "yDF", "ybh", "wCF", "wah", "wix", "sEh", "sgx", "sxx", "kcx", "ktx", + "lvx", "Alx", "Bnx", "Drx", "bpw", "nuz", "boy", "boj", "Dmz", "bqz", "jps", "ruy", "jow", + "ruj", "joi", "job", "bmy", "jqy", "bmj", "jqj", "jmw", "rtj", "jmi", "jmb", "blj", "jnj", + "jli", "jlb", "jkr", "sCu", "sCt", "kFu", "kFt", "Afu", "Aft", "wDh", "sCh", "sax", "kEx", + "khx", "Adx", "Avx", "Buz", "Duy", "Duj", "buw", "nxj", "bui", "bub", "Dtj", "bvj", "jus", + "rxi", "jug", "rxb", "jua", "juD", "bti", "jvi", "btb", "jvb", "jtg", "rwr", "jta", "jtD", + "bsr", "jtr", "jsq", "jsn", "Bxj", "Dxi", "Dxb", "bxg", "nyr", "bxa", "bxD", "Dwr", "bxr", + "bwq", "bwn", "pjk", "urw", "ejA", "pbs", "uny", "ebk", "pDw", "ulz", "eDs", "pBy", "eBw", + "zfc", "fjk", "prw", "zfE", "fbs", "pny", "zfC", "fDw", "plz", "zfB", "fBy", "yrc", "zfq", + "frw", "yrE", "zfn", "fny", "yrC", "flz", "yrB", "xjc", "yrq", "xjE", "yrn", "xjC", "xjB", + "uzc", "xjq", "uzE", "xjn", "uzC", "uzB", "pzc", "uzq", "pzE", "uzn", "pzC", "djA", "ors", + "ufy", "dbk", "onw", "udz", "dDs", "oly", "dBw", "okz", "dAy", "zdo", "drs", "ovy", "zdm", + "dnw", "otz", "zdl", "dly", "dkz", "yno", "zdv", "dvy", "ynm", "dtz", "ynl", "xbo", "ynv", + "xbm", "xbl", "ujo", "xbv", "ujm", "ujl", "ozo", "ujv", "ozm", "ozl", "crk", "ofw", "uFz", + "cns", "ody", "clw", "ocz", "cky", "ckj", "zcu", "cvw", "ohz", "zct", "cty", "csz", "ylu", + "cxz", "ylt", "xDu", "xDt", "ubu", "ubt", "oju", "ojt", "cfs", "oFy", "cdw", "oEz", "ccy", + "ccj", "zch", "chy", "cgz", "ykx", "xBx", "uDx", "cFw", "oCz", "cEy", "cEj", "caz", "cCy", + "cCj", "FjA", "mrs", "tfy", "Fbk", "mnw", "tdz", "FDs", "mly", "FBw", "mkz", "FAy", "zFo", + "Frs", "mvy", "zFm", "Fnw", "mtz", "zFl", "Fly", "Fkz", "yfo", "zFv", "Fvy", "yfm", "Ftz", + "yfl", "wro", "yfv", "wrm", "wrl", "tjo", "wrv", "tjm", "tjl", "mzo", "tjv", "mzm", "mzl", + "qrk", "vfw", "xpz", "hbA", "qns", "vdy", "hDk", "qlw", "vcz", "hBs", "qky", "hAw", "qkj", + "hAi", "Erk", "mfw", "tFz", "hrk", "Ens", "mdy", "hns", "qty", "mcz", "hlw", "Eky", "hky", + "Ekj", "hkj", "zEu", "Evw", "mhz", "zhu", "zEt", "hvw", "Ety", "zht", "hty", "Esz", "hsz", + "ydu", "Exz", "yvu", "ydt", "hxz", "yvt", "wnu", "xru", "wnt", "xrt", "tbu", "vju", "tbt", + "vjt", "mju", "mjt", "grA", "qfs", "vFy", "gnk", "qdw", "vEz", "gls", "qcy", "gkw", "qcj", + "gki", "gkb", "Efs", "mFy", "gvs", "Edw", "mEz", "gtw", "qgz", "gsy", "Ecj", "gsj", "zEh", + "Ehy", "zgx", "gxy", "Egz", "gwz", "ycx", "ytx", "wlx", "xnx", "tDx", "vbx", "mbx", "gfk", + "qFw", "vCz", "gds", "qEy", "gcw", "qEj", "gci", "gcb", "EFw", "mCz", "ghw", "EEy", "ggy", + "EEj", "ggj", "Eaz", "giz", "gFs", "qCy", "gEw", "qCj", "gEi", "gEb", "ECy", "gay", "ECj", + "gaj", "gCw", "qBj", "gCi", "gCb", "EBj", "gDj", "gBi", "gBb", "Crk", "lfw", "spz", "Cns", + "ldy", "Clw", "lcz", "Cky", "Ckj", "zCu", "Cvw", "lhz", "zCt", "Cty", "Csz", "yFu", "Cxz", + "yFt", "wfu", "wft", "sru", "srt", "lju", "ljt", "arA", "nfs", "tpy", "ank", "ndw", "toz", + "als", "ncy", "akw", "ncj", "aki", "akb", "Cfs", "lFy", "avs", "Cdw", "lEz", "atw", "ngz", + "asy", "Ccj", "asj", "zCh", "Chy", "zax", "axy", "Cgz", "awz", "yEx", "yhx", "wdx", "wvx", + "snx", "trx", "lbx", "rfk", "vpw", "xuz", "inA", "rds", "voy", "ilk", "rcw", "voj", "iks", + "rci", "ikg", "rcb", "ika", "afk", "nFw", "tmz", "ivk", "ads", "nEy", "its", "rgy", "nEj", + "isw", "aci", "isi", "acb", "isb", "CFw", "lCz", "ahw", "CEy", "ixw", "agy", "CEj", "iwy", + "agj", "iwj", "Caz", "aiz", "iyz", "ifA", "rFs", "vmy", "idk", "rEw", "vmj", "ics", "rEi", + "icg", "rEb", "ica", "icD", "aFs", "nCy", "ihs", "aEw", "nCj", "igw", "raj", "igi", "aEb", + "igb", "CCy", "aay", "CCj", "iiy", "aaj", "iij", "iFk", "rCw", "vlj", "iEs", "rCi", "iEg", + "rCb", "iEa", "iED", "aCw", "nBj", "iaw", "aCi", "iai", "aCb", "iab", "CBj", "aDj", "ibj", + "iCs", "rBi", "iCg", "rBb", "iCa", "iCD", "aBi", "iDi", "aBb", "iDb", "iBg", "rAr", "iBa", + "iBD", "aAr", "iBr", "iAq", "iAn", "Bfs", "kpy", "Bdw", "koz", "Bcy", "Bcj", "Bhy", "Bgz", + "yCx", "wFx", "sfx", "krx", "Dfk", "lpw", "suz", "Dds", "loy", "Dcw", "loj", "Dci", "Dcb", + "BFw", "kmz", "Dhw", "BEy", "Dgy", "BEj", "Dgj", "Baz", "Diz", "bfA", "nps", "tuy", "bdk", + "now", "tuj", "bcs", "noi", "bcg", "nob", "bca", "bcD", "DFs", "lmy", "bhs", "DEw", "lmj", + "bgw", "DEi", "bgi", "DEb", "bgb", "BCy", "Day", "BCj", "biy", "Daj", "bij", "rpk", "vuw", + "xxj", "jdA", "ros", "vui", "jck", "rog", "vub", "jcc", "roa", "jcE", "roD", "jcC", "bFk", + "nmw", "ttj", "jhk", "bEs", "nmi", "jgs", "rqi", "nmb", "jgg", "bEa", "jga", "bED", "jgD", + "DCw", "llj", "baw", "DCi", "jiw", "bai", "DCb", "jii", "bab", "jib", "BBj", "DDj", "bbj", + "jjj", "jFA", "rms", "vti", "jEk", "rmg", "vtb", "jEc", "rma", "jEE", "rmD", "jEC", "jEB", + "bCs", "nli", "jas", "bCg", "nlb", "jag", "rnb", "jaa", "bCD", "jaD", "DBi", "bDi", "DBb", + "jbi", "bDb", "jbb", "jCk", "rlg", "vsr", "jCc", "rla", "jCE", "rlD", "jCC", "jCB", "bBg", + "nkr", "jDg", "bBa", "jDa", "bBD", "jDD", "DAr", "bBr", "jDr", "jBc", "rkq", "jBE", "rkn", + "jBC", "jBB", "bAq", "jBq", "bAn", "jBn", "jAo", "rkf", "jAm", "jAl", "bAf", "jAv", "Apw", + "kez", "Aoy", "Aoj", "Aqz", "Bps", "kuy", "Bow", "kuj", "Boi", "Bob", "Amy", "Bqy", "Amj", + "Bqj", "Dpk", "luw", "sxj", "Dos", "lui", "Dog", "lub", "Doa", "DoD", "Bmw", "ktj", "Dqw", + "Bmi", "Dqi", "Bmb", "Dqb", "Alj", "Bnj", "Drj", "bpA", "nus", "txi", "bok", "nug", "txb", + "boc", "nua", "boE", "nuD", "boC", "boB", "Dms", "lti", "bqs", "Dmg", "ltb", "bqg", "nvb", + "bqa", "DmD", "bqD", "Bli", "Dni", "Blb", "bri", "Dnb", "brb", "ruk", "vxg", "xyr", "ruc", + "vxa", "ruE", "vxD", "ruC", "ruB", "bmk", "ntg", "twr", "jqk", "bmc", "nta", "jqc", "rva", + "ntD", "jqE", "bmC", "jqC", "bmB", "jqB", "Dlg", "lsr", "bng", "Dla", "jrg", "bna", "DlD", + "jra", "bnD", "jrD", "Bkr", "Dlr", "bnr", "jrr", "rtc", "vwq", "rtE", "vwn", "rtC", "rtB", + "blc", "nsq", "jnc", "blE", "nsn", "jnE", "rtn", "jnC", "blB", "jnB", "Dkq", "blq", "Dkn", + "jnq", "bln", "jnn", "rso", "vwf", "rsm", "rsl", "bko", "nsf", "jlo", "bkm", "jlm", "bkl", + "jll", "Dkf", "bkv", "jlv", "rse", "rsd", "bke", "jku", "bkd", "jkt", "Aey", "Aej", "Auw", + "khj", "Aui", "Aub", "Adj", "Avj", "Bus", "kxi", "Bug", "kxb", "Bua", "BuD", "Ati", "Bvi", + "Atb", "Bvb", "Duk", "lxg", "syr", "Duc", "lxa", "DuE", "lxD", "DuC", "DuB", "Btg", "kwr", + "Dvg", "lxr", "Dva", "BtD", "DvD", "Asr", "Btr", "Dvr", "nxc", "tyq", "nxE", "tyn", "nxC", + "nxB", "Dtc", "lwq", "bvc", "nxq", "lwn", "bvE", "DtC", "bvC", "DtB", "bvB", "Bsq", "Dtq", + "Bsn", "bvq", "Dtn", "bvn", "vyo", "xzf", "vym", "vyl", "nwo", "tyf", "rxo", "nwm", "rxm", + "nwl", "rxl", "Dso", "lwf", "bto", "Dsm", "jvo", "btm", "Dsl", "jvm", "btl", "jvl", "Bsf", + "Dsv", "btv", "jvv", "vye", "vyd", "nwe", "rwu", "nwd", "rwt", "Dse", "bsu", "Dsd", "jtu", + "bst", "jtt", "vyF", "nwF", "rwh", "DsF", "bsh", "jsx", "Ahi", "Ahb", "Axg", "kir", "Axa", + "AxD", "Agr", "Axr", "Bxc", "kyq", "BxE", "kyn", "BxC", "BxB", "Awq", "Bxq", "Awn", "Bxn", + "lyo", "szf", "lym", "lyl", "Bwo", "kyf", "Dxo", "lyv", "Dxm", "Bwl", "Dxl", "Awf", "Bwv", + "Dxv", "tze", "tzd", "lye", "nyu", "lyd", "nyt", "Bwe", "Dwu", "Bwd", "bxu", "Dwt", "bxt", + "tzF", "lyF", "nyh", "BwF", "Dwh", "bwx", "Aiq", "Ain", "Ayo", "kjf", "Aym", "Ayl", "Aif", + "Ayv", "kze", "kzd", "Aye", "Byu", "Ayd", "Byt", "szp" + }; + private static final char[] BR_SET = { + 'A', 'B', 'C', 'D', 'E', 'F', 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', + 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z', '*', '+', '-' + }; + private static final String[] PDF_TTF = { + "00000", "00001", "00010", "00011", "00100", "00101", "00110", "00111", + "01000", "01001", "01010", "01011", "01100", "01101", "01110", "01111", "10000", "10001", + "10010", "10011", "10100", "10101", "10110", "10111", "11000", "11001", "11010", + "11011", "11100", "11101", "11110", "11111", "01", "1111111101010100", "11111101000101001" + }; + private static final int[] ASCII_X = { + 7, 8, 8, 4, 12, 4, 4, 8, 8, 8, 12, 4, 12, 12, 12, 12, 4, 4, 4, 4, 4, 4, 4, 4, + 4, 4, 12, 8, 8, 4, 8, 8, 8, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 8, 8, 8, 4, 8, 8, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 8, 8, 8, 8 + }; + private static final int[] ASCII_Y = { + 26, 10, 20, 15, 18, 21, 10, 28, 23, 24, 22, 20, 13, 16, 17, 19, 0, 1, 2, 3, + 4, 5, 6, 7, 8, 9, 14, 0, 1, 23, 2, 25, 3, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, + 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 4, 5, 6, 24, 7, 8, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, + 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 21, 27, 9 + }; + private static final int[] MICRO_AUTOSIZE = { + 4, 6, 7, 8, 8, 10, 10, 12, 12, 13, 14, 16, 18, 18, 19, 20, 24, 24, 24, 29, 30, 33, 34, 37, 39, 46, 54, 58, 70, 72, 82, 90, 108, 126, // max codeword counts + 1, 14, 2, 7, 24, 3, 15, 25, 4, 8, 16, 5, 17, 26, 9, 6, 10, 18, 27, 11, 28, 12, 19, 13, 29, 20, 30, 21, 22, 31, 23, 32, 33, 34 // corresponding variant + }; + /* Rows, columns, error codewords, k-offset of valid MicroPDF417 sizes from ISO/IEC 24728:2006 */ + private static final int[] MICRO_VARIANTS = { + 1, 1, 1, 1, 1, 1, 2, 2, 2, 2, 2, 2, 2, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, // columns + 11, 14, 17, 20, 24, 28, 8, 11, 14, 17, 20, 23, 26, 6, 8, 10, 12, 15, 20, 26, 32, 38, 44, 4, 6, 8, 10, 12, 15, 20, 26, 32, 38, 44, // rows + 7, 7, 7, 8, 8, 8, 8, 9, 9, 10, 11, 13, 15, 12, 14, 16, 18, 21, 26, 32, 38, 44, 50, 8, 12, 14, 16, 18, 21, 26, 32, 38, 44, 50, // k (EC codewords) + 0, 0, 0, 7, 7, 7, 7, 15, 15, 24, 34, 57, 84, 45, 70, 99, 115, 133, 154, 180, 212, 250, 294, 7, 45, 70, 99, 115, 133, 154, 180, 212, 250, 294 // offset + }; + /* Following is Left RAP, Centre RAP, Right RAP and Start Cluster from ISO/IEC 24728:2006 tables 10, 11 and 12 */ + private static final int[] RAP_TABLE = { + 1, 8, 36, 19, 9, 25, 1, 1, 8, 36, 19, 9, 27, 1, 7, 15, 25, 37, 1, 1, 21, 15, 1, 47, 1, 7, 15, 25, 37, 1, 1, 21, 15, 1, // left RAP + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 7, 15, 25, 37, 17, 9, 29, 31, 25, 19, 1, 7, 15, 25, 37, 17, 9, 29, 31, 25, // centre RAP + 9, 8, 36, 19, 17, 33, 1, 9, 8, 36, 19, 17, 35, 1, 7, 15, 25, 37, 33, 17, 37, 47, 49, 43, 1, 7, 15, 25, 37, 33, 17, 37, 47, 49, // right RAP + 0, 3, 6, 0, 6, 0, 0, 0, 3, 6, 0, 6, 6, 0, 0, 6, 0, 0, 0, 0, 6, 6, 0, 3, 0, 0, 6, 0, 0, 0, 0, 6, 6, 0 // start cluster + }; + /* Left and Right Row Address Pattern from Table 2 */ + private static final String[] RAPLR = { + "", "221311", "311311", "312211", "222211", "213211", "214111", "223111", + "313111", "322111", "412111", "421111", "331111", "241111", "232111", "231211", "321211", + "411211", "411121", "411112", "321112", "312112", "311212", "311221", "311131", "311122", + "311113", "221113", "221122", "221131", "221221", "222121", "312121", "321121", "231121", + "231112", "222112", "213112", "212212", "212221", "212131", "212122", "212113", "211213", + "211123", "211132", "211141", "211231", "211222", "211312", "211321", "211411", "212311" + }; + /* Centre Row Address Pattern from Table 2 */ + private static final String[] RAPC = { + "", "112231", "121231", "122131", "131131", "131221", "132121", "141121", + "141211", "142111", "133111", "132211", "131311", "122311", "123211", "124111", "115111", + "114211", "114121", "123121", "123112", "122212", "122221", "121321", "121411", "112411", + "113311", "113221", "113212", "113122", "122122", "131122", "131113", "122113", "113113", + "112213", "112222", "112312", "112321", "111421", "111331", "111322", "111232", "111223", + "111133", "111124", "111214", "112114", "121114", "121123", "121132", "112132", "112141" + }; + /* MicroPDF417 coefficients from ISO/IEC 24728:2006 Annex F */ + private static final int[] MICRO_COEFFS = { + /* k = 7 */ + 76, 925, 537, 597, 784, 691, 437, + + /* k = 8 */ + 237, 308, 436, 284, 646, 653, 428, 379, + + /* k = 9 */ + 567, 527, 622, 257, 289, 362, 501, 441, 205, + + /* k = 10 */ + 377, 457, 64, 244, 826, 841, 818, 691, 266, 612, + + /* k = 11 */ + 462, 45, 565, 708, 825, 213, 15, 68, 327, 602, 904, + + /* k = 12 */ + 597, 864, 757, 201, 646, 684, 347, 127, 388, 7, 69, 851, + + /* k = 13 */ + 764, 713, 342, 384, 606, 583, 322, 592, 678, 204, 184, 394, 692, + + /* k = 14 */ + 669, 677, 154, 187, 241, 286, 274, 354, 478, 915, 691, 833, 105, 215, + + /* k = 15 */ + 460, 829, 476, 109, 904, 664, 230, 5, 80, 74, 550, 575, 147, 868, 642, + + /* k = 16 */ + 274, 562, 232, 755, 599, 524, 801, 132, 295, 116, 442, 428, 295, 42, 176, 65, + + /* k = 18 */ + 279, 577, 315, 624, 37, 855, 275, 739, 120, 297, 312, 202, 560, 321, 233, 756, + 760, 573, + + /* k = 21 */ + 108, 519, 781, 534, 129, 425, 681, 553, 422, 716, 763, 693, 624, 610, 310, 691, + 347, 165, 193, 259, 568, + + /* k = 26 */ + 443, 284, 887, 544, 788, 93, 477, 760, 331, 608, 269, 121, 159, 830, 446, 893, + 699, 245, 441, 454, 325, 858, 131, 847, 764, 169, + + /* k = 32 */ + 361, 575, 922, 525, 176, 586, 640, 321, 536, 742, 677, 742, 687, 284, 193, 517, + 273, 494, 263, 147, 593, 800, 571, 320, 803, 133, 231, 390, 685, 330, 63, 410, + + /* k = 38 */ + 234, 228, 438, 848, 133, 703, 529, 721, 788, 322, 280, 159, 738, 586, 388, 684, + 445, 680, 245, 595, 614, 233, 812, 32, 284, 658, 745, 229, 95, 689, 920, 771, + 554, 289, 231, 125, 117, 518, + + /* k = 44 */ + 476, 36, 659, 848, 678, 64, 764, 840, 157, 915, 470, 876, 109, 25, 632, 405, + 417, 436, 714, 60, 376, 97, 413, 706, 446, 21, 3, 773, 569, 267, 272, 213, + 31, 560, 231, 758, 103, 271, 572, 436, 339, 730, 82, 285, + + /* k = 50 */ + 923, 797, 576, 875, 156, 706, 63, 81, 257, 874, 411, 416, 778, 50, 205, 303, + 188, 535, 909, 155, 637, 230, 534, 96, 575, 102, 264, 233, 919, 593, 865, 26, + 579, 623, 766, 146, 10, 739, 246, 127, 71, 244, 211, 477, 920, 876, 427, 820, + 718, 435 + }; + private int[] codeWords = new int[2700]; + private int codeWordCount; + private Mode symbolMode = Mode.NORMAL; + private int[] inputData; + private Integer columns; + private Integer rows; + private int preferredEccLevel = -1; + private int structuredAppendFileId = 0; + private int structuredAppendPosition = 1; + private int structuredAppendTotal = 1; + + /** + * Creates a new PDF417 symbol instance. + */ + public Pdf417() { + setBarHeight(3); + } + + private static EncodingMode chooseMode(int codeascii) { + if (codeascii >= '0' && codeascii <= '9') { + return EncodingMode.NUM; + } else if (codeascii == '\t' || codeascii == '\n' || codeascii == '\r' || (codeascii >= ' ' && codeascii <= '~')) { + return EncodingMode.TEX; + } else { + return EncodingMode.BYT; + } + } + + private static int getMicroPdf417Variant(int codeWordCount, Integer columns, Integer rows) { + for (int i = 0; i < 34; i++) { + int maxCodewordCount = MICRO_AUTOSIZE[i]; + if (codeWordCount <= maxCodewordCount) { + int variant = MICRO_AUTOSIZE[i + 34]; + int columnsForThisVariant = MICRO_VARIANTS[variant - 1]; + int rowsForThisVariant = MICRO_VARIANTS[variant - 1 + 34]; + if ((columns == null || columns == columnsForThisVariant) && (rows == null || rows == rowsForThisVariant)) { + return variant; + } + } + } + throw new IllegalStateException("Unable to determine MicroPDF417 variant for " + codeWordCount + " codewords"); + } + + /** + * Determines the encoding block groups for the specified data. + */ + private static List createBlocks(int[] data) { + + List blocks = new ArrayList<>(); + Block current = null; + + for (int aData : data) { + EncodingMode mode = chooseMode(aData); + if ((current != null && current.mode == mode) && + (mode != EncodingMode.NUM || current.length < MAX_NUMERIC_COMPACTION_BLOCK_SIZE)) { + current.length++; + } else { + current = new Block(mode); + blocks.add(current); + } + } + + smoothBlocks(blocks); + + return blocks; + } + + /** + * Combines adjacent blocks of different types in very specific scenarios. + */ + private static void smoothBlocks(List blocks) { + + for (int i = 0; i < blocks.size(); i++) { + Block block = blocks.get(i); + EncodingMode last = (i > 0 ? blocks.get(i - 1).mode : EncodingMode.FALSE); + EncodingMode next = (i < blocks.size() - 1 ? blocks.get(i + 1).mode : EncodingMode.FALSE); + if (block.mode == EncodingMode.NUM) { + if (i == 0) { /* first block */ + if (next == EncodingMode.TEX && block.length < 8) { + block.mode = EncodingMode.TEX; + } else if (next == EncodingMode.BYT && block.length == 1) { + block.mode = EncodingMode.BYT; + } + } else if (i == blocks.size() - 1) { /* last block */ + if (last == EncodingMode.TEX && block.length < 7) { + block.mode = EncodingMode.TEX; + } else if (last == EncodingMode.BYT && block.length == 1) { + block.mode = EncodingMode.BYT; + } + } else { /* not first or last block */ + if (last == EncodingMode.BYT && next == EncodingMode.BYT && block.length < 4) { + block.mode = EncodingMode.BYT; + } else if (last == EncodingMode.BYT && next == EncodingMode.TEX && block.length < 4) { + block.mode = EncodingMode.TEX; + } else if (last == EncodingMode.TEX && next == EncodingMode.BYT && block.length < 5) { + block.mode = EncodingMode.TEX; + } else if (last == EncodingMode.TEX && next == EncodingMode.TEX && block.length < 8) { + block.mode = EncodingMode.TEX; + } else if (last == EncodingMode.NUM && next == EncodingMode.TEX && block.length < 8) { + block.mode = EncodingMode.TEX; + } + } + } + } + + mergeBlocks(blocks); + + for (int i = 0; i < blocks.size(); i++) { + Block block = blocks.get(i); + EncodingMode last = (i > 0 ? blocks.get(i - 1).mode : EncodingMode.FALSE); + EncodingMode next = (i < blocks.size() - 1 ? blocks.get(i + 1).mode : EncodingMode.FALSE); + if (block.mode == EncodingMode.TEX && i > 0) { /* not the first */ + if (i == blocks.size() - 1) { /* the last one */ + if (last == EncodingMode.BYT && block.length == 1) { + block.mode = EncodingMode.BYT; + } + } else { /* not the last one */ + if (last == EncodingMode.BYT && next == EncodingMode.BYT && block.length < 5) { + block.mode = EncodingMode.BYT; + } + if (((last == EncodingMode.BYT && next != EncodingMode.BYT) || + (last != EncodingMode.BYT && next == EncodingMode.BYT)) && (block.length < 3)) { + block.mode = EncodingMode.BYT; + } + } + } + } + + mergeBlocks(blocks); + } + + /** + * Combines adjacent blocks of the same type. + */ + private static void mergeBlocks(List blocks) { + for (int i = 1; i < blocks.size(); i++) { + Block b1 = blocks.get(i - 1); + Block b2 = blocks.get(i); + if ((b1.mode == b2.mode) && + (b1.mode != EncodingMode.NUM || b1.length + b2.length <= MAX_NUMERIC_COMPACTION_BLOCK_SIZE)) { + b1.length += b2.length; + blocks.remove(i); + i--; + } + } + } + + /** + * Sets the default bar height (height of a single row) for this symbol (default value is 3). + * + * @param barHeight the default bar height for this symbol + */ + @Override + public void setBarHeight(int barHeight) { + super.setBarHeight(barHeight); + } + + /** + * Returns the number of data columns used by this symbol, or {@code null} + * if the number of data columns has not been set. + * + * @return the number of data columns used by this symbol + */ + public Integer getDataColumns() { + return columns; + } + + /** + * Sets the width of the symbol by specifying the number of columns + * of data codewords. Valid values are 1-30 for PDF417 and 1-4 + * for MicroPDF417. + * + * @param columns the number of data columns in the symbol + */ + public void setDataColumns(int columns) { + this.columns = columns; + } + + /** + * Returns the number of rows used by this symbol, or {@code null} if + * the number of rows has not been set. + * + * @return the number of rows used by this symbol + */ + public Integer getRows() { + return rows; + } + + /** + * Sets the height of the symbol by specifying the number of rows + * of data codewords. Valid values are 3-90 for PDF417 and 4-44 + * for MicroPDF417. + * + * @param rows the number of rows in the symbol + */ + public void setRows(int rows) { + this.rows = rows; + } + + /** + * Set the amount of the symbol which is dedicated to error correction + * codewords. The number of codewords of error correction data is + * determined by 2(eccLevel + 1). This attribute is ignored + * when using {@link Mode#MICRO micro} mode. + * + * @param eccLevel level of error correction (0-8) + */ + public void setPreferredEccLevel(int eccLevel) { + if (eccLevel < 0 || eccLevel > 8) { + throw new IllegalArgumentException("ECC level must be between 0 and 8."); + } + preferredEccLevel = eccLevel; + } + + /** + * Forces the use of the specified MicroPDF417 variant. Only valid + * when using {@link Mode#MICRO micro} mode. + * + * @param variant the MicroPDF417 variant to use + */ + public void setVariant(int variant) { + if (symbolMode != Mode.MICRO) { + throw new IllegalArgumentException("Can only set variant when using MICRO mode."); + } + if (variant < 1 || variant > 34) { + throw new IllegalArgumentException("Variant must be between 1 and 34."); + } + this.columns = MICRO_VARIANTS[variant - 1]; + this.rows = MICRO_VARIANTS[variant - 1 + 34]; + } + + /** + * Returns the position of this PDF417 symbol in a series of symbols using structured append + * (Macro PDF417). If this symbol is not part of such a series, this method will return 1. + * + * @return the position of this PDF417 symbol in a series of symbols using structured append + */ + public int getStructuredAppendPosition() { + return structuredAppendPosition; + } + + /** + * If this PDF417 symbol is part of a series of PDF417 symbols appended in a structured format + * (Macro PDF417), this method sets the position of this symbol in the series. Valid values are + * 1 through 99,999 inclusive. + * + * @param position the position of this PDF417 symbol in the structured append series + */ + public void setStructuredAppendPosition(int position) { + if (position < 1 || position > 99_999) { + throw new IllegalArgumentException("Invalid PDF417 structured append position: " + position); + } + this.structuredAppendPosition = position; + } + + /** + * Returns the size of the series of PDF417 symbols using structured append (Macro PDF417) that + * this symbol is part of. If this symbol is not part of a structured append series, this method + * will return 1. + * + * @return size of the series that this symbol is part of + */ + public int getStructuredAppendTotal() { + return structuredAppendTotal; + } + + /** + * If this PDF417 symbol is part of a series of PDF417 symbols appended in a structured format + * (Macro PDF417), this method sets the total number of symbols in the series. Valid values are + * 1 through 99,999 inclusive. A value of 1 indicates that this symbol is not part of a structured + * append series. + * + * @param total the total number of PDF417 symbols in the structured append series + */ + public void setStructuredAppendTotal(int total) { + if (total < 1 || total > 99_999) { + throw new IllegalArgumentException("Invalid PDF417 structured append total: " + total); + } + this.structuredAppendTotal = total; + } + + /** + * Returns the unique file ID of the series of PDF417 symbols using structured append (Macro PDF417) + * that this symbol is part of. If this symbol is not part of a structured append series, this method + * will return 0. + * + * @return the unique file ID for the series that this symbol is part of + */ + public int getStructuredAppendFileId() { + return structuredAppendFileId; + } + + /** + * If this PDF417 symbol is part of a series of PDF417 symbols appended in a structured format + * (Macro PDF417), this method sets the unique file ID for the series. Valid values are 0 through + * 899 inclusive. + * + * @param fileId the unique file ID for the series that this symbol is part of + */ + public void setStructuredAppendFileId(int fileId) { + if (fileId < 0 || fileId > 899) { + throw new IllegalArgumentException("Invalid PDF417 structured append file ID: " + fileId); + } + this.structuredAppendFileId = fileId; + } + + public Mode getMode() { + return symbolMode; + } + + public void setMode(Mode mode) { + symbolMode = mode; + } + + @Override + public boolean encode() { + + eciProcess(); + + int sourceLength = inputBytes.length; + inputData = new int[sourceLength]; + for (int i = 0; i < sourceLength; i++) { + inputData[i] = inputBytes[i] & 0xFF; + } + + boolean ok; + switch (symbolMode) { + case MICRO: + ok = processMicroPdf417(); + break; + case NORMAL: + case TRUNCATED: + default: + ok = processPdf417(); + break; + } + + if (ok) { + plotSymbol(); + } + + return ok; + } + + private boolean processPdf417() { + int j, loop, offset; + int[] mccorrection = new int[520]; + int total; + int c1, c2, c3; + int[] dummy = new int[35]; + StringBuilder codebarre; + int selectedECCLevel; + String bin; + + List blocks = createBlocks(inputData); + + /* now compress the data */ + codeWordCount = 0; + + if (readerInit) { + codeWords[codeWordCount] = 921; /* Reader Initialisation */ + codeWordCount++; + } + + if (eciMode != 3) { + /* Encoding ECI assignment number, from ISO/IEC 15438 Table 8 */ + if (eciMode <= 899) { + codeWords[codeWordCount] = 927; + codeWordCount++; + codeWords[codeWordCount] = eciMode; + codeWordCount++; + } + + if ((eciMode >= 900) && (eciMode <= 810899)) { + codeWords[codeWordCount] = 926; + codeWordCount++; + codeWords[codeWordCount] = (eciMode / 900) - 1; + codeWordCount++; + codeWords[codeWordCount] = eciMode % 900; + codeWordCount++; + } + + if ((eciMode >= 810900) && (eciMode <= 811799)) { + codeWords[codeWordCount] = 925; + codeWordCount++; + codeWords[codeWordCount] = eciMode - 810900; + codeWordCount++; + } + } + + int blockCount = 0; + for (int i = 0; i < blocks.size(); i++) { + Block block = blocks.get(i); + switch (block.mode) { + case TEX: + /* text mode */ + boolean firstBlock = (i == 0); + processText(blockCount, block.length, firstBlock); + break; + case BYT: + /* octet stream mode */ + EncodingMode lastMode = (i == 0 ? EncodingMode.TEX : blocks.get(i - 1).mode); + processBytes(blockCount, block.length, lastMode); + break; + case NUM: + /* numeric mode */ + processNumbers(inputData, blockCount, block.length, false); + break; + default: + throw new IllegalStateException("Unknown block type: " + block.mode); + } + blockCount += block.length; + } + + addMacroCodewords(); + + encodeInfo.append("Codewords: "); + for (int i = 0; i < codeWordCount; i++) { + encodeInfo.append(Integer.toString(codeWords[i])).append(" "); + } + encodeInfo.append("\n"); + + /* Now take care of the number of CWs per row */ + + // if we have to default the ECC level, do so per the + // recommendations in the specification (Table E.1) + selectedECCLevel = preferredEccLevel; + if (selectedECCLevel < 0) { + if (codeWordCount <= 40) { + selectedECCLevel = 2; + } else if (codeWordCount <= 160) { + selectedECCLevel = 3; + } else if (codeWordCount <= 320) { + selectedECCLevel = 4; + } else if (codeWordCount <= 863) { + selectedECCLevel = 5; + } else { + selectedECCLevel = 6; + } + } + + int k = 1 << (selectedECCLevel + 1); // error correction codeword count + int dataCodeWordCount = codeWordCount + k + 1; // not including padding + + if (validateRows(3, 90) || validateColumns(1, 30)) { + return false; + } + + if (columns != null) { + if (rows != null) { + // user specified both columns and rows; make sure the data fits + if (columns * rows < dataCodeWordCount) { + errorMsg.append("Too few rows (" + rows + ") and columns (" + columns + ") to hold codewords (" + dataCodeWordCount + ")"); + return false; + } + } else { + // user only specified column count; figure out row count + rows = (int) Math.ceil(dataCodeWordCount / (double) columns); + } + } else { + if (rows != null) { + // user only specified row count; figure out column count + columns = (int) Math.ceil(dataCodeWordCount / (double) rows); + } else { + // user didn't specify columns or rows; figure both out + columns = (int) (0.5 + Math.sqrt((dataCodeWordCount - 1) / 3.0)); + rows = (int) Math.ceil(dataCodeWordCount / (double) columns); + } + } + + if (validateRows(3, 90) || validateColumns(1, 30)) { + return false; + } + + /* add the padding */ + int paddingCount = (columns * rows) - codeWordCount - k - 1; + while (paddingCount > 0) { + codeWords[codeWordCount] = 900; + codeWordCount++; + paddingCount--; + } + + /* add the length descriptor */ + System.arraycopy(codeWords, 0, codeWords, 1, codeWordCount); + codeWordCount++; + codeWords[0] = codeWordCount; + + /* 796 - we now take care of the Reed Solomon codes */ + switch (selectedECCLevel) { + case 1: + offset = 2; + break; + case 2: + offset = 6; + break; + case 3: + offset = 14; + break; + case 4: + offset = 30; + break; + case 5: + offset = 62; + break; + case 6: + offset = 126; + break; + case 7: + offset = 254; + break; + case 8: + offset = 510; + break; + default: + offset = 0; + break; + } + + for (loop = 0; loop < 520; loop++) { + mccorrection[loop] = 0; + } + + for (int i = 0; i < codeWordCount; i++) { + total = (codeWords[i] + mccorrection[k - 1]) % 929; + for (j = k - 1; j > 0; j--) { + mccorrection[j] = (mccorrection[j - 1] + 929 - (total * COEFRS[offset + j]) % 929) % 929; + } + mccorrection[0] = (929 - (total * COEFRS[offset + j]) % 929) % 929; + } + + encodeInfo.append("Data Codewords: ").append(codeWordCount).append("\n"); + encodeInfo.append("ECC Codewords: ").append(k).append("\n"); + + /* we add these codes to the string */ + for (int i = k - 1; i >= 0; i--) { + codeWords[codeWordCount++] = mccorrection[i] != 0 ? 929 - mccorrection[i] : 0; + } + + /* make sure total codeword count isn't too high */ + if (codeWordCount > 929) { + errorMsg.append("Too many codewords required (" + codeWordCount + ", but max is 929)"); + return false; + } + /* 818 - The CW string is finished */ + c1 = (rows - 1) / 3; + c2 = (selectedECCLevel * 3) + (rows - 1) % 3; + c3 = columns - 1; + + readable = new StringBuilder(); + rowCount = rows; + pattern = new String[rows]; + rowHeight = new int[rows]; + encodeInfo.append("Grid Size: ").append(columns).append(" X ").append(rows).append("\n"); + + /* we now encode each row */ + for (int i = 0; i < rows; i++) { + for (j = 0; j < columns; j++) { + dummy[j + 1] = codeWords[i * columns + j]; + } + k = (i / 3) * 30; + switch (i % 3) { + case 0: + offset = 0; // cluster 0 + dummy[0] = k + c1; // left row indicator + dummy[columns + 1] = k + c3; // right row indicator + break; + case 1: + offset = 929; // cluster 3 + dummy[0] = k + c2; // left row indicator + dummy[columns + 1] = k + c1; // right row indicator + break; + case 2: + offset = 1858; // cluster 6 + dummy[0] = k + c3; // left row indicator + dummy[columns + 1] = k + c2; // right row indicator + break; + } + codebarre = new StringBuilder("+*"); + for (j = 0; j <= columns + 1; j++) { + if (!(symbolMode == Mode.TRUNCATED && j > columns)) { + codebarre.append(CODAGEMC[offset + dummy[j]]); + codebarre.append("*"); + } + } + if (symbolMode != Mode.TRUNCATED) { + codebarre.append("-"); + } + bin = ""; + for (j = 0; j < codebarre.length(); j++) { + bin += PDF_TTF[positionOf(codebarre.charAt(j), BR_SET)]; + } + pattern[i] = bin2pat(bin); + rowHeight[i] = defaultHeight; + } + return true; + } + + private boolean processMicroPdf417() { /* like PDF417 only much smaller! */ + + int k, j, longueur, offset; + int total; + int LeftRAPStart, CentreRAPStart, RightRAPStart, StartCluster; + int LeftRAP, CentreRAP, RightRAP, Cluster, flip, loop; + String codebarre; + int[] dummy = new int[5]; + int[] mccorrection = new int[50]; + StringBuilder bin; + + /* Encoding starts out the same as PDF417, so use the same code */ + + List blocks = createBlocks(inputData); + + /* 541 - now compress the data */ + codeWordCount = 0; + if (readerInit) { + codeWords[codeWordCount] = 921; /* Reader Initialisation */ + codeWordCount++; + } + + if (eciMode != 3) { + /* Encoding ECI assignment number, from ISO/IEC 15438 Table 8 */ + if (eciMode <= 899) { + codeWords[codeWordCount] = 927; + codeWordCount++; + codeWords[codeWordCount] = eciMode; + codeWordCount++; + } + + if ((eciMode >= 900) && (eciMode <= 810899)) { + codeWords[codeWordCount] = 926; + codeWordCount++; + codeWords[codeWordCount] = (eciMode / 900) - 1; + codeWordCount++; + codeWords[codeWordCount] = eciMode % 900; + codeWordCount++; + } + + if ((eciMode >= 810900) && (eciMode <= 811799)) { + codeWords[codeWordCount] = 925; + codeWordCount++; + codeWords[codeWordCount] = eciMode - 810900; + codeWordCount++; + } + } + + int blockCount = 0; + for (int i = 0; i < blocks.size(); i++) { + Block block = blocks.get(i); + switch (block.mode) { + case TEX: + /* text mode */ + processText(blockCount, block.length, false); // TODO: this shouldn't always be false? + break; + case BYT: + /* octet stream mode */ + EncodingMode lastMode = (i == 0 ? EncodingMode.TEX : blocks.get(i - 1).mode); + processBytes(blockCount, block.length, lastMode); + break; + case NUM: + /* numeric mode */ + processNumbers(inputData, blockCount, block.length, false); + break; + default: + throw new IllegalStateException("Unknown block type: " + block.mode); + } + blockCount += block.length; + } + + addMacroCodewords(); + + encodeInfo.append("Codewords: "); + for (int i = 0; i < codeWordCount; i++) { + encodeInfo.append(Integer.toString(codeWords[i])).append(" "); + } + encodeInfo.append("\n"); + + /* This is where it all changes! */ + + if (validateRows(4, 44) || validateColumns(1, 4)) { + return false; + } + + if (columns != null) { + int max; + switch (columns) { + case 1: + max = 20; + break; + case 2: + max = 37; + break; + case 3: + max = 82; + break; + case 4: + max = 126; + break; + default: + throw new IllegalStateException("Invalid column count: " + columns); + } + if (codeWordCount > max) { + errorMsg.append("Too few columns (" + columns + ") to hold data codewords (" + codeWordCount + ")"); + return false; + } + } + + /* Now figure out which variant of the symbol to use and load values accordingly */ + + int variant = getMicroPdf417Variant(codeWordCount, columns, rows); + + /* Now we have the variant we can load the data */ + + variant--; + columns = MICRO_VARIANTS[variant]; /* columns */ + rows = MICRO_VARIANTS[variant + 34]; /* rows */ + k = MICRO_VARIANTS[variant + 68]; /* number of EC CWs */ + longueur = (columns * rows) - k; /* number of non-EC CWs */ + int padding = longueur - codeWordCount; /* amount of padding required */ + offset = MICRO_VARIANTS[variant + 102]; /* coefficient offset */ + + encodeInfo.append("Data Codewords: ").append(longueur).append("\n"); + encodeInfo.append("ECC Codewords: ").append(k).append("\n"); + + /* We add the padding */ + while (padding > 0) { + codeWords[codeWordCount] = 900; + codeWordCount++; + padding--; + } + + /* Reed-Solomon error correction */ + longueur = codeWordCount; + for (loop = 0; loop < 50; loop++) { + mccorrection[loop] = 0; + } + + for (int i = 0; i < longueur; i++) { + total = (codeWords[i] + mccorrection[k - 1]) % 929; + for (j = k - 1; j >= 0; j--) { + if (j == 0) { + mccorrection[j] = (929 - (total * MICRO_COEFFS[offset + j]) % 929) % 929; + } else { + mccorrection[j] = (mccorrection[j - 1] + 929 - (total * MICRO_COEFFS[offset + j]) % 929) % 929; + } + } + } + + for (j = 0; j < k; j++) { + if (mccorrection[j] != 0) { + mccorrection[j] = 929 - mccorrection[j]; + } + } + /* we add these codes to the string */ + for (int i = k - 1; i >= 0; i--) { + codeWords[codeWordCount] = mccorrection[i]; + codeWordCount++; + } + + /* Now get the RAP (Row Address Pattern) start values */ + LeftRAPStart = RAP_TABLE[variant]; + CentreRAPStart = RAP_TABLE[variant + 34]; + RightRAPStart = RAP_TABLE[variant + 68]; + StartCluster = RAP_TABLE[variant + 102] / 3; + + /* That's all values loaded, get on with the encoding */ + + LeftRAP = LeftRAPStart; + CentreRAP = CentreRAPStart; + RightRAP = RightRAPStart; + Cluster = StartCluster; /* Cluster can be 0, 1 or 2 for Cluster(0), Cluster(3) and Cluster(6) */ + + readable = new StringBuilder(); + pattern = new String[rows]; + rowCount = rows; + rowHeight = new int[rows]; + + encodeInfo.append("Grid Size: ").append(columns).append(" X ").append(rowCount).append("\n"); + + for (int i = 0; i < rows; i++) { + codebarre = ""; + offset = 929 * Cluster; + for (j = 0; j < 5; j++) { + dummy[j] = 0; + } + for (j = 0; j < columns; j++) { + dummy[j + 1] = codeWords[i * columns + j]; + } + + /* Copy the data into codebarre */ + codebarre += RAPLR[LeftRAP]; + codebarre += "1"; + codebarre += CODAGEMC[offset + dummy[1]]; + codebarre += "1"; + if (columns == 3) { + codebarre += RAPC[CentreRAP]; + } + if (columns >= 2) { + codebarre += "1"; + codebarre += CODAGEMC[offset + dummy[2]]; + codebarre += "1"; + } + if (columns == 4) { + codebarre += RAPC[CentreRAP]; + } + if (columns >= 3) { + codebarre += "1"; + codebarre += CODAGEMC[offset + dummy[3]]; + codebarre += "1"; + } + if (columns == 4) { + codebarre += "1"; + codebarre += CODAGEMC[offset + dummy[4]]; + codebarre += "1"; + } + codebarre += RAPLR[RightRAP]; + codebarre += "1"; /* stop */ + + /* Now codebarre is a mixture of letters and numbers */ + + flip = 1; + bin = new StringBuilder(); + for (loop = 0; loop < codebarre.length(); loop++) { + if ((codebarre.charAt(loop) >= '0') && (codebarre.charAt(loop) <= '9')) { + for (k = 0; k < Character.getNumericValue(codebarre.charAt(loop)); k++) { + if (flip == 0) { + bin.append('0'); + } else { + bin.append('1'); + } + } + if (flip == 0) { + flip = 1; + } else { + flip = 0; + } + } else { + bin.append(PDF_TTF[positionOf(codebarre.charAt(loop), BR_SET)]); + } + } + + /* so now pattern[] holds the string of '1's and '0's. - copy this to the symbol */ + pattern[i] = bin2pat(bin.toString()); + rowHeight[i] = defaultHeight; + + /* Set up RAPs and Cluster for next row */ + LeftRAP++; + CentreRAP++; + RightRAP++; + Cluster++; + + if (LeftRAP == 53) { + LeftRAP = 1; + } + if (CentreRAP == 53) { + CentreRAP = 1; + } + if (RightRAP == 53) { + RightRAP = 1; + } + if (Cluster == 3) { + Cluster = 0; + } + } + return true; + } + + private boolean validateRows(int min, int max) { + if (rows != null) { + if (rows < min) { + errorMsg.append("Too few rows (").append(rows).append(")"); + return true; + } else if (rows > max) { + errorMsg.append("Too many rows (").append(rows).append(")"); + return true; + } + } + return false; + } + + private boolean validateColumns(int min, int max) { + if (columns != null) { + if (columns < min) { + errorMsg.append("Too few columns (").append(columns).append(")"); + return true; + } else if (columns > max) { + errorMsg.append("Too many columns (").append(columns).append(")"); + return true; + } + } + return false; + } + + private void processText(int start, int length, boolean skipLatch) { + int j, blockIndext, curtable, wnet; + int codeascii; + int[] listet0 = new int[5000]; + int[] listet1 = new int[5000]; + int[] chainet = new int[5000]; + + wnet = 0; + + for (j = 0; j < 1000; j++) { + listet0[j] = 0; + } + /* listet will contain the table numbers and the value of each characters */ + for (blockIndext = 0; blockIndext < length; blockIndext++) { + codeascii = inputData[start + blockIndext]; + switch (codeascii) { + case '\t': + listet0[blockIndext] = 12; + listet1[blockIndext] = 12; + break; + case '\n': + listet0[blockIndext] = 8; + listet1[blockIndext] = 15; + break; + case 13: + listet0[blockIndext] = 12; + listet1[blockIndext] = 11; + break; + default: + listet0[blockIndext] = ASCII_X[codeascii - 32]; + listet1[blockIndext] = ASCII_Y[codeascii - 32]; + break; + } + } + + curtable = 1; /* default table */ + for (j = 0; j < length; j++) { + if ((listet0[j] & curtable) != 0) { /* The character is in the current table */ + chainet[wnet] = listet1[j]; + wnet++; + } else { /* Obliged to change table */ + boolean flag = false; /* True if we change table for only one character */ + if (j == (length - 1)) { + flag = true; + } else { + if ((listet0[j] & listet0[j + 1]) == 0) { + flag = true; + } + } + + if (flag) { /* we change only one character - look for temporary switch */ + if (((listet0[j] & 1) != 0) && (curtable == 2)) { /* T_UPP */ + chainet[wnet] = 27; + chainet[wnet + 1] = listet1[j]; + wnet += 2; + } + if ((listet0[j] & 8) != 0) { /* T_PUN */ + chainet[wnet] = 29; + chainet[wnet + 1] = listet1[j]; + wnet += 2; + } + if (!((((listet0[j] & 1) != 0) && (curtable == 2)) || ((listet0[j] & 8) != 0))) { + /* No temporary switch available */ + flag = false; + } + } + + if (!(flag)) { + int newtable; + + if (j == (length - 1)) { + newtable = listet0[j]; + } else { + if ((listet0[j] & listet0[j + 1]) == 0) { + newtable = listet0[j]; + } else { + newtable = listet0[j] & listet0[j + 1]; + } + } + + /* Maintain the first if several tables are possible */ + switch (newtable) { + case 3: + case 5: + case 7: + case 9: + case 11: + case 13: + case 15: + newtable = 1; + break; + case 6: + case 10: + case 14: + newtable = 2; + break; + case 12: + newtable = 4; + break; + } + + /* select the switch */ + switch (curtable) { + case 1: + switch (newtable) { + case 2: + chainet[wnet] = 27; + wnet++; + break; + case 4: + chainet[wnet] = 28; + wnet++; + break; + case 8: + chainet[wnet] = 28; + wnet++; + chainet[wnet] = 25; + wnet++; + break; + } + break; + case 2: + switch (newtable) { + case 1: + chainet[wnet] = 28; + wnet++; + chainet[wnet] = 28; + wnet++; + break; + case 4: + chainet[wnet] = 28; + wnet++; + break; + case 8: + chainet[wnet] = 28; + wnet++; + chainet[wnet] = 25; + wnet++; + break; + } + break; + case 4: + switch (newtable) { + case 1: + chainet[wnet] = 28; + wnet++; + break; + case 2: + chainet[wnet] = 27; + wnet++; + break; + case 8: + chainet[wnet] = 25; + wnet++; + break; + } + break; + case 8: + switch (newtable) { + case 1: + chainet[wnet] = 29; + wnet++; + break; + case 2: + chainet[wnet] = 29; + wnet++; + chainet[wnet] = 27; + wnet++; + break; + case 4: + chainet[wnet] = 29; + wnet++; + chainet[wnet] = 28; + wnet++; + break; + } + break; + } + curtable = newtable; + /* at last we add the character */ + chainet[wnet] = listet1[j]; + wnet++; + } + } + } + + if ((wnet & 1) != 0) { + chainet[wnet] = 29; + wnet++; + } + + /* Now translate the string chainet into codewords */ + + if (!skipLatch) { + // text compaction mode is the default mode for PDF417, + // so no need for an explicit latch if this is the first block + codeWords[codeWordCount] = 900; + codeWordCount++; + } + + for (j = 0; j < wnet; j += 2) { + int cw_number; + + cw_number = (30 * chainet[j]) + chainet[j + 1]; + codeWords[codeWordCount] = cw_number; + codeWordCount++; + } + } + + private void processBytes(int start, int length, EncodingMode lastMode) { + int len = 0; + int chunkLen = 0; + BigInteger mantisa; + BigInteger total; + BigInteger word; + + mantisa = new BigInteger("0"); + total = new BigInteger("0"); + + if (length == 1 && lastMode == EncodingMode.TEX) { + codeWords[codeWordCount++] = 913; + codeWords[codeWordCount++] = inputData[start]; + } else { + /* select the switch for multiple of 6 bytes */ + if (length % 6 == 0) { + codeWords[codeWordCount++] = 924; + } else { + codeWords[codeWordCount++] = 901; + } + + while (len < length) { + chunkLen = length - len; + if (6 <= chunkLen) /* Take groups of 6 */ { + chunkLen = 6; + len += chunkLen; + total = BigInteger.valueOf(0); + + while ((chunkLen--) != 0) { + mantisa = BigInteger.valueOf(inputData[start++]); + total = total.or(mantisa.shiftLeft(chunkLen * 8)); + } + + chunkLen = 5; + + while ((chunkLen--) != 0) { + + word = total.mod(BigInteger.valueOf(900)); + codeWords[codeWordCount + chunkLen] = word.intValue(); + total = total.divide(BigInteger.valueOf(900)); + } + codeWordCount += 5; + } else /* If it remain a group of less than 6 bytes */ { + len += chunkLen; + while ((chunkLen--) != 0) { + codeWords[codeWordCount++] = inputData[start++]; + } + } + } + } + } + + private void processNumbers(int[] data, int start, int length, boolean skipLatch) { + + BigInteger tVal, dVal; + int[] d = new int[16]; + int cw_count; + + if (!skipLatch) { + // we don't need to latch to numeric mode in some cases, e.g. + // during numeric compaction of the Macro PDF417 segment index + codeWords[codeWordCount++] = 902; + } + + StringBuilder t = new StringBuilder(length + 1); + t.append('1'); + for (int i = 0; i < length; i++) { + t.append((char) data[start + i]); + } + + tVal = new BigInteger(t.toString()); + + cw_count = 0; + do { + dVal = tVal.mod(BigInteger.valueOf(900)); + d[cw_count] = dVal.intValue(); + tVal = tVal.divide(BigInteger.valueOf(900)); + cw_count++; + } while (tVal.compareTo(BigInteger.ZERO) == 1); + + for (int i = cw_count - 1; i >= 0; i--) { + codeWords[codeWordCount++] = d[i]; + } + } + + /** + * Adds the Macro PDF417 control block codewords (if any). + */ + private void addMacroCodewords() { + + // if the structured append series size is 1, this isn't + // actually part of a structured append series + if (structuredAppendTotal == 1) { + return; + } + + // add the Macro marker codeword + codeWords[codeWordCount++] = 928; + + // add the segment index, padded with leading zeros to five digits + // use numeric compaction, but no latch + int segmentIndex = structuredAppendPosition - 1; + int[] data = new int[5]; + for (int x = data.length - 1; x >= 0; x--) { + data[x] = '0' + (segmentIndex % 10); + segmentIndex /= 10; + } + processNumbers(data, 0, data.length, true); + + // add the file ID (base 900, which is easy since we limit + // file ID values to the range 0 to 899) + codeWords[codeWordCount++] = structuredAppendFileId; + + // NOTE: we could add the optional segment count field here, but + // it doesn't appear to be necessary... if we do eventually decide + // to add it, it will probably be [923, 001, count1, count2] + + // add the terminator to the last symbol of the series + boolean last = (structuredAppendPosition == structuredAppendTotal); + if (last) { + codeWords[codeWordCount++] = 922; + } + } + + public enum Mode { + /** + * Normal PDF417. + */ + NORMAL, + /** + * Truncated PDF417. + */ + TRUNCATED, + /** + * MicroPDF417. + */ + MICRO + } + + private enum EncodingMode { + FALSE, TEX, BYT, NUM + } + + private static class Block { + + public EncodingMode mode; + public int length; + + public Block(EncodingMode mode) { + this.mode = mode; + this.length = 1; + } + + @Override + public String toString() { + return mode + "x" + length; + } + } +} diff --git a/barcode/src/main/java/org/xbib/graphics/barcode/Pharmacode.java b/barcode/src/main/java/org/xbib/graphics/barcode/Pharmacode.java new file mode 100755 index 0000000..573490a --- /dev/null +++ b/barcode/src/main/java/org/xbib/graphics/barcode/Pharmacode.java @@ -0,0 +1,66 @@ +package org.xbib.graphics.barcode; + +/** + * Implements the Pharmacode + * bar code symbology. + * Pharmacode is used for the identification of pharmaceuticals. The symbology + * is able to encode whole numbers between 3 and 131070. + */ +public class Pharmacode extends Symbol { + + @Override + public boolean encode() { + int tester = 0; + int i; + + StringBuilder inter = new StringBuilder(); + StringBuilder dest = new StringBuilder(); + + if (content.length() > 6) { + errorMsg.append("Input too long"); + return false; + } + + if (!(content.matches("[0-9]+"))) { + errorMsg.append("Invalid characters in data"); + return false; + } + + for (i = 0; i < content.length(); i++) { + tester *= 10; + tester += Character.getNumericValue(content.charAt(i)); + } + + if ((tester < 3) || (tester > 131070)) { + errorMsg.append("Data out of range"); + return false; + } + + do { + if ((tester & 1) == 0) { + inter.append("W"); + tester = (tester - 2) / 2; + } else { + inter.append("N"); + tester = (tester - 1) / 2; + } + } while (tester != 0); + + for (i = inter.length() - 1; i >= 0; i--) { + if (inter.charAt(i) == 'W') { + dest.append("32"); + } else { + dest.append("12"); + } + } + + readable = new StringBuilder(); + pattern = new String[1]; + pattern[0] = dest.toString(); + rowCount = 1; + rowHeight = new int[1]; + rowHeight[0] = -1; + plotSymbol(); + return true; + } +} diff --git a/barcode/src/main/java/org/xbib/graphics/barcode/Pharmacode2Track.java b/barcode/src/main/java/org/xbib/graphics/barcode/Pharmacode2Track.java new file mode 100755 index 0000000..6be57f6 --- /dev/null +++ b/barcode/src/main/java/org/xbib/graphics/barcode/Pharmacode2Track.java @@ -0,0 +1,106 @@ +package org.xbib.graphics.barcode; + +import java.awt.geom.Rectangle2D; + +/** + * Implements the Two-Track Pharmacode bar code symbology. + * Pharmacode Two-Track is an alternative system to Pharmacode One-Track used + * for the identification of pharmaceuticals. The symbology is able to encode + * whole numbers between 4 and 64570080. + */ +public class Pharmacode2Track extends Symbol { + + @Override + public boolean encode() { + int i, tester = 0; + StringBuilder inter; + StringBuilder dest; + + if (content.length() > 8) { + errorMsg.append("Input too long"); + return false; + } + + if (!(content.matches("[0-9]+"))) { + errorMsg.append("Invalid characters in data"); + return false; + } + + for (i = 0; i < content.length(); i++) { + tester *= 10; + tester += Character.getNumericValue(content.charAt(i)); + } + + if ((tester < 4) || (tester > 64570080)) { + errorMsg.append("Data out of range"); + return false; + } + + inter = new StringBuilder(); + do { + switch (tester % 3) { + case 0: + inter.append("F"); + tester = (tester - 3) / 3; + break; + case 1: + inter.append("D"); + tester = (tester - 1) / 3; + break; + case 2: + inter.append("A"); + tester = (tester - 2) / 3; + break; + } + } + while (tester != 0); + + dest = new StringBuilder(); + for (i = (inter.length() - 1); i >= 0; i--) { + dest.append(inter.charAt(i)); + } + + encodeInfo.append("Encoding: ").append(dest).append("\n"); + + readable = new StringBuilder(); + pattern = new String[1]; + pattern[0] = dest.toString(); + rowCount = 1; + rowHeight = new int[1]; + rowHeight[0] = -1; + plotSymbol(); + return true; + } + + @Override + protected void plotSymbol() { + int xBlock; + int x, y, w, h; + getRectangles().clear(); + x = 0; + w = 1; + y = 0; + h = 0; + for (xBlock = 0; xBlock < pattern[0].length(); xBlock++) { + switch (pattern[0].charAt(xBlock)) { + case 'A': + y = 0; + h = defaultHeight / 2; + break; + case 'D': + y = defaultHeight / 2; + h = defaultHeight / 2; + break; + case 'F': + y = 0; + h = defaultHeight; + break; + } + Rectangle2D.Double rect = new Rectangle2D.Double(x, y, w, h); + getRectangles().add(rect); + x += 2; + } + symbolWidth = pattern[0].length() * 2; + symbolHeight = defaultHeight; + } +} diff --git a/barcode/src/main/java/org/xbib/graphics/barcode/Pharmazentralnummer.java b/barcode/src/main/java/org/xbib/graphics/barcode/Pharmazentralnummer.java new file mode 100755 index 0000000..6e3bcad --- /dev/null +++ b/barcode/src/main/java/org/xbib/graphics/barcode/Pharmazentralnummer.java @@ -0,0 +1,69 @@ +package org.xbib.graphics.barcode; + +/** + * PZN8 is a Code 39 based symbology used by the pharmaceutical industry in + * Germany. PZN8 encodes a 7 digit number and includes a modulo-10 check digit. + */ +public class Pharmazentralnummer extends Symbol { + + /* Pharmazentral Nummer is a Code 3 of 9 symbol with an extra + * check digit. Now generates PZN-8. + */ + @Override + public boolean encode() { + int l = content.length(); + StringBuilder localstr; + int zeroes, count = 0, check_digit; + Code3Of9 c = new Code3Of9(); + + if (l > 7) { + errorMsg.append("Input data too long"); + return false; + } + + if (!(content.matches("[0-9]+"))) { + errorMsg.append("Invalid characters in input"); + return false; + } + + localstr = new StringBuilder("-"); + zeroes = 7 - l + 1; + for (int i = 1; i < zeroes; i++) + localstr.append('0'); + + localstr.append(content); + + for (int i = 1; i < 8; i++) { + count += i * Character.getNumericValue(localstr.charAt(i)); + } + + check_digit = count % 11; + if (check_digit == 11) { + check_digit = 0; + } + if (check_digit == 10) { + errorMsg.append("Not a valid PZN identifier"); + return false; + } + + encodeInfo.append("Check Digit: ").append(check_digit).append("\n"); + + localstr.append((char) (check_digit + '0')); + + try { + c.setContent(localstr.toString()); + } catch (Exception e) { + errorMsg.append(e.getMessage()); + return false; + } + + readable = new StringBuilder("PZN").append(localstr); + pattern = new String[1]; + pattern[0] = c.pattern[0]; + rowCount = 1; + rowHeight = new int[1]; + rowHeight[0] = -1; + plotSymbol(); + return true; + } +} diff --git a/barcode/src/main/java/org/xbib/graphics/barcode/Postnet.java b/barcode/src/main/java/org/xbib/graphics/barcode/Postnet.java new file mode 100755 index 0000000..eade347 --- /dev/null +++ b/barcode/src/main/java/org/xbib/graphics/barcode/Postnet.java @@ -0,0 +1,185 @@ +package org.xbib.graphics.barcode; + +import static org.xbib.graphics.barcode.HumanReadableLocation.NONE; +import static org.xbib.graphics.barcode.HumanReadableLocation.TOP; +import org.xbib.graphics.barcode.util.TextBox; +import java.awt.geom.Rectangle2D; + +/** + * Implements POSTNET and + * PLANET + * bar code symbologies. + * POSTNET and PLANET both use numerical input data and include a modulo-10 + * check digit. + */ +public class Postnet extends Symbol { + + private static final String[] PN_TABLE = { + "LLSSS", "SSSLL", "SSLSL", "SSLLS", "SLSSL", "SLSLS", "SLLSS", "LSSSL", "LSSLS", "LSLSS" + }; + + ; + private static final String[] PL_TABLE = { + "SSLLL", "LLLSS", "LLSLS", "LLSSL", "LSLLS", "LSLSL", "LSSLL", "SLLLS", "SLLSL", "SLSLL" + }; + private Mode mode; + + public Postnet() { + this.mode = Mode.POSTNET; + this.defaultHeight = 12; + setHumanReadableLocation(HumanReadableLocation.NONE); + } + + /** + * Returns the barcode mode (PLANET or POSTNET). The default mode is POSTNET. + * + * @return the barcode mode (PLANET or POSTNET) + */ + public Mode getMode() { + return mode; + } + + /** + * Sets the barcode mode (PLANET or POSTNET). The default mode is POSTNET. + * + * @param mode the barcode mode (PLANET or POSTNET) + */ + public void setMode(Mode mode) { + this.mode = mode; + } + + @Override + public boolean encode() { + + boolean retval; + if (mode == Mode.POSTNET) { + retval = makePostnet(); + } else { + retval = makePlanet(); + } + + if (retval) { + plotSymbol(); + } + + return retval; + } + + private boolean makePostnet() { + int i, sum, check_digit; + StringBuilder dest; + + if (content.length() > 38) { + errorMsg.append("Input too long"); + return false; + } + + if (!(content.matches("[0-9]+"))) { + errorMsg.append("Invalid characters in data"); + return false; + } + + sum = 0; + dest = new StringBuilder("L"); + + for (i = 0; i < content.length(); i++) { + dest.append(PN_TABLE[content.charAt(i) - '0']); + sum += content.charAt(i) - '0'; + } + + check_digit = (10 - (sum % 10)) % 10; + encodeInfo.append("Check Digit: ").append(check_digit).append("\n"); + + dest.append(PN_TABLE[check_digit]); + dest.append("L"); + + encodeInfo.append("Encoding: ").append(dest).append("\n"); + readable = new StringBuilder(content); + pattern = new String[]{dest.toString()}; + rowCount = 1; + rowHeight = new int[]{-1}; + + return true; + } + + private boolean makePlanet() { + int i, sum, check_digit; + StringBuilder dest; + + if (content.length() > 38) { + errorMsg.append("Input too long"); + return false; + } + + if (!(content.matches("[0-9]+"))) { + errorMsg.append("Invalid characters in data"); + return false; + } + + sum = 0; + dest = new StringBuilder("L"); + + for (i = 0; i < content.length(); i++) { + dest.append(PL_TABLE[content.charAt(i) - '0']); + sum += content.charAt(i) - '0'; + } + + check_digit = (10 - (sum % 10)) % 10; + encodeInfo.append("Check Digit: ").append(check_digit).append("\n"); + + dest.append(PL_TABLE[check_digit]); + dest.append("L"); + + encodeInfo.append("Encoding: ").append(dest).append("\n"); + readable = new StringBuilder(content); + pattern = new String[]{dest.toString()}; + rowCount = 1; + rowHeight = new int[]{-1}; + + return true; + } + + @Override + protected void plotSymbol() { + int xBlock, shortHeight; + double x, y, w, h; + getRectangles().clear(); + getTexts().clear(); + int baseY; + if (getHumanReadableLocation() == TOP) { + baseY = getTheoreticalHumanReadableHeight(); + } else { + baseY = 0; + } + x = 0; + w = moduleWidth; + shortHeight = (int) (0.4 * defaultHeight); + for (xBlock = 0; xBlock < pattern[0].length(); xBlock++) { + if (pattern[0].charAt(xBlock) == 'L') { + y = baseY; + h = defaultHeight; + } else { + y = baseY + defaultHeight - shortHeight; + h = shortHeight; + } + getRectangles().add(new Rectangle2D.Double(x, y, w, h)); + x += (2.5 * w); + } + symbolWidth = (int) Math.ceil(((pattern[0].length() - 1) * 2.5 * w) + w); // final bar doesn't need extra whitespace + symbolHeight = defaultHeight; + if (getHumanReadableLocation() != NONE && readable.length() > 0) { + double baseline; + if (getHumanReadableLocation() == TOP) { + baseline = fontSize; + } else { + baseline = getHeight() + fontSize; + } + double centerX = getWidth() / 2.0; + getTexts().add(new TextBox(centerX, baseline, readable.toString())); + } + } + + public enum Mode { + PLANET, POSTNET + } +} diff --git a/barcode/src/main/java/org/xbib/graphics/barcode/QrCode.java b/barcode/src/main/java/org/xbib/graphics/barcode/QrCode.java new file mode 100755 index 0000000..a528ddf --- /dev/null +++ b/barcode/src/main/java/org/xbib/graphics/barcode/QrCode.java @@ -0,0 +1,2008 @@ +package org.xbib.graphics.barcode; + +import org.xbib.graphics.barcode.util.ReedSolomon; +import java.io.UnsupportedEncodingException; + +/** + * Implements QR Code bar code symbology According to ISO/IEC 18004:2015 + * The maximum capacity of a (version 40) QR Code symbol is 7089 numeric digits, + * 4296 alphanumeric characters or 2953 bytes of data. QR Code symbols can also + * be used to encode GS1 data. QR Code symbols can encode characters in the + * Latin-1 set and Kanji characters which are members of the Shift-JIS encoding + * scheme. + */ +public class QrCode extends Symbol { + + /* Table 5 - Encoding/Decoding table for Alphanumeric mode */ + private final char[] rhodium = { + '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D', 'E', + 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T', + 'U', 'V', 'W', 'X', 'Y', 'Z', ' ', '$', '%', '*', '+', '-', '.', '/', ':' + }; + private final int[] qr_data_codewords_L = { + 19, 34, 55, 80, 108, 136, 156, 194, 232, 274, 324, 370, 428, 461, 523, 589, 647, + 721, 795, 861, 932, 1006, 1094, 1174, 1276, 1370, 1468, 1531, 1631, + 1735, 1843, 1955, 2071, 2191, 2306, 2434, 2566, 2702, 2812, 2956 + }; + private final int[] qr_data_codewords_M = { + 16, 28, 44, 64, 86, 108, 124, 154, 182, 216, 254, 290, 334, 365, 415, 453, 507, + 563, 627, 669, 714, 782, 860, 914, 1000, 1062, 1128, 1193, 1267, + 1373, 1455, 1541, 1631, 1725, 1812, 1914, 1992, 2102, 2216, 2334 + }; + private final int[] qr_data_codewords_Q = { + 13, 22, 34, 48, 62, 76, 88, 110, 132, 154, 180, 206, 244, 261, 295, 325, 367, + 397, 445, 485, 512, 568, 614, 664, 718, 754, 808, 871, 911, + 985, 1033, 1115, 1171, 1231, 1286, 1354, 1426, 1502, 1582, 1666 + }; + private final int[] qr_data_codewords_H = { + 9, 16, 26, 36, 46, 60, 66, 86, 100, 122, 140, 158, 180, 197, 223, 253, 283, + 313, 341, 385, 406, 442, 464, 514, 538, 596, 628, 661, 701, + 745, 793, 845, 901, 961, 986, 1054, 1096, 1142, 1222, 1276 + }; + private final int[] qr_blocks_L = { + 1, 1, 1, 1, 1, 2, 2, 2, 2, 4, 4, 4, 4, 4, 6, 6, 6, 6, 7, 8, 8, 9, 9, 10, 12, 12, + 12, 13, 14, 15, 16, 17, 18, 19, 19, 20, 21, 22, 24, 25 + }; + private final int[] qr_blocks_M = { + 1, 1, 1, 2, 2, 4, 4, 4, 5, 5, 5, 8, 9, 9, 10, 10, 11, 13, 14, 16, 17, 17, 18, 20, + 21, 23, 25, 26, 28, 29, 31, 33, 35, 37, 38, 40, 43, 45, 47, 49 + }; + private final int[] qr_blocks_Q = { + 1, 1, 2, 2, 4, 4, 6, 6, 8, 8, 8, 10, 12, 16, 12, 17, 16, 18, 21, 20, 23, 23, 25, + 27, 29, 34, 34, 35, 38, 40, 43, 45, 48, 51, 53, 56, 59, 62, 65, 68 + }; + private final int[] qr_blocks_H = { + 1, 1, 2, 4, 4, 4, 5, 6, 8, 8, 11, 11, 16, 16, 18, 16, 19, 21, 25, 25, 25, 34, 30, + 32, 35, 37, 40, 42, 45, 48, 51, 54, 57, 60, 63, 66, 70, 74, 77, 81 + }; + private final int[] qr_total_codewords = { + 26, 44, 70, 100, 134, 172, 196, 242, 292, 346, 404, 466, 532, 581, 655, 733, 815, + 901, 991, 1085, 1156, 1258, 1364, 1474, 1588, 1706, 1828, 1921, 2051, + 2185, 2323, 2465, 2611, 2761, 2876, 3034, 3196, 3362, 3532, 3706 + }; + private final int[] qr_sizes = { + 21, 25, 29, 33, 37, 41, 45, 49, 53, 57, 61, 65, 69, 73, 77, 81, 85, 89, 93, 97, + 101, 105, 109, 113, 117, 121, 125, 129, 133, 137, 141, 145, 149, 153, 157, 161, 165, 169, 173, 177 + }; + private final int[] qr_align_loopsize = { + 0, 2, 2, 2, 2, 2, 3, 3, 3, 3, 3, 3, 3, 4, 4, 4, 4, 4, 4, 4, 5, 5, 5, 5, 5, 5, 5, 6, 6, 6, 6, 6, 6, 6, 7, 7, 7, 7, 7, 7 + }; + private final int[] qr_table_e1 = { + 6, 18, 0, 0, 0, 0, 0, + 6, 22, 0, 0, 0, 0, 0, + 6, 26, 0, 0, 0, 0, 0, + 6, 30, 0, 0, 0, 0, 0, + 6, 34, 0, 0, 0, 0, 0, + 6, 22, 38, 0, 0, 0, 0, + 6, 24, 42, 0, 0, 0, 0, + 6, 26, 46, 0, 0, 0, 0, + 6, 28, 50, 0, 0, 0, 0, + 6, 30, 54, 0, 0, 0, 0, + 6, 32, 58, 0, 0, 0, 0, + 6, 34, 62, 0, 0, 0, 0, + 6, 26, 46, 66, 0, 0, 0, + 6, 26, 48, 70, 0, 0, 0, + 6, 26, 50, 74, 0, 0, 0, + 6, 30, 54, 78, 0, 0, 0, + 6, 30, 56, 82, 0, 0, 0, + 6, 30, 58, 86, 0, 0, 0, + 6, 34, 62, 90, 0, 0, 0, + 6, 28, 50, 72, 94, 0, 0, + 6, 26, 50, 74, 98, 0, 0, + 6, 30, 54, 78, 102, 0, 0, + 6, 28, 54, 80, 106, 0, 0, + 6, 32, 58, 84, 110, 0, 0, + 6, 30, 58, 86, 114, 0, 0, + 6, 34, 62, 90, 118, 0, 0, + 6, 26, 50, 74, 98, 122, 0, + 6, 30, 54, 78, 102, 126, 0, + 6, 26, 52, 78, 104, 130, 0, + 6, 30, 56, 82, 108, 134, 0, + 6, 34, 60, 86, 112, 138, 0, + 6, 30, 58, 86, 114, 142, 0, + 6, 34, 62, 90, 118, 146, 0, + 6, 30, 54, 78, 102, 126, 150, + 6, 24, 50, 76, 102, 128, 154, + 6, 28, 54, 80, 106, 132, 158, + 6, 32, 58, 84, 110, 136, 162, + 6, 26, 54, 82, 110, 138, 166, + 6, 30, 58, 86, 114, 142, 170 + }; + private final int[] qr_annex_c = { + /* Format information bit sequences */ + 0x5412, 0x5125, 0x5e7c, 0x5b4b, 0x45f9, 0x40ce, 0x4f97, 0x4aa0, 0x77c4, 0x72f3, 0x7daa, 0x789d, + 0x662f, 0x6318, 0x6c41, 0x6976, 0x1689, 0x13be, 0x1ce7, 0x19d0, 0x0762, 0x0255, 0x0d0c, 0x083b, + 0x355f, 0x3068, 0x3f31, 0x3a06, 0x24b4, 0x2183, 0x2eda, 0x2bed + }; + private final long[] qr_annex_d = { + /* Version information bit sequences */ + 0x07c94, 0x085bc, 0x09a99, 0x0a4d3, 0x0bbf6, 0x0c762, 0x0d847, 0x0e60d, 0x0f928, 0x10b78, + 0x1145d, 0x12a17, 0x13532, 0x149a6, 0x15683, 0x168c9, 0x177ec, 0x18ec4, 0x191e1, 0x1afab, + 0x1b08e, 0x1cc1a, 0x1d33f, 0x1ed75, 0x1f250, 0x209d5, 0x216f0, 0x228ba, 0x2379f, 0x24b0b, + 0x2542e, 0x26a64, 0x27541, 0x28c69 + }; + private qrMode[] inputMode; + private StringBuilder binary; + private int[] datastream; + private int[] fullstream; + private int[] inputData; + private byte[] grid; + private byte[] eval; + private int preferredVersion = 0; + private int inputLength; + private EccMode preferredEccLevel = EccMode.L; + + /** + * Sets the preferred symbol size. This value may be ignored if the data + * string is too large to fit into the specified symbol. Input values + * correspond to symbol sizes as shown in the following table. + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + *
Available QR Code sizes

+ * Input

+ * Symbol Size

+ * Input

+ * Symbol Size

+ * 1

+ * 21 x 21

+ * 21

+ * 101 x 101

+ * 2

+ * 25 x 25

+ * 22

+ * 105 x 105

+ * 3

+ * 29 x 29

+ * 23

+ * 109 x 109

+ * 4

+ * 33 x 33

+ * 24

+ * 113 x 113

+ * 5

+ * 37 x 37

+ * 25

+ * 117 x 117

+ * 6

+ * 41 x 41

+ * 26

+ * 121 x 121

+ * 7

+ * 45 x 45

+ * 27

+ * 125 x 125

+ * 8

+ * 49 x 49

+ * 28

+ * 129 x 129

+ * 9

+ * 53 x 53

+ * 29

+ * 133 x 133

+ * 10

+ * 57 x 57

+ * 30

+ * 137 x 137

+ * 11

+ * 61 x 61

+ * 31

+ * 141 x 141

+ * 12

+ * 65 x 65

+ * 32

+ * 145 x 145

+ * 13

+ * 69 x 69

+ * 33

+ * 149 x 149

+ * 14

+ * 73 x 73

+ * 34

+ * 153 x 153

+ * 15

+ * 77 x 77

+ * 35

+ * 157 x 157

+ * 16

+ * 81 x 81

+ * 36

+ * 161 x 161

+ * 17

+ * 85 x 85

+ * 37

+ * 165 x 165

+ * 18

+ * 89 x 89

+ * 38

+ * 169 x 169

+ * 19

+ * 93 x 93

+ * 39

+ * 173 x 173

+ * 20

+ * 97 x 97

+ * 40

+ * 177 x 177

+ * + * @param version Symbol version number required + */ + public void setPreferredVersion(int version) { + preferredVersion = version; + } + + /** + * Set the amount of symbol space allocated to error correction. Levels are + * predefined according to the following table: + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + *
QR Code error correction levels
ECC LevelError Correction CapacityRecovery Capacity
L (default)Approx 20% of symbolApprox 7%
MApprox 37% of symbolApprox 15%
QApprox 55% of symbolApprox 25%
HApprox 65% of symbolApprox 30%
+ * + * @param eccMode Error correction level + */ + public void setEccMode(EccMode eccMode) { + preferredEccLevel = eccMode; + } + + @Override + public boolean encode() { + int i, j; + int est_binlen; + EccMode ecc_level; + int max_cw; + int autosize; + int targetCwCount, version, blocks; + int size; + int bitmask; + StringBuilder bin; + boolean canShrink; + + /* This code uses modeFirstFix to make an estimate of the symbol size + needed to contain the data. It then optimises the encoding and + checks to see if the the data will fit in a smaller + symbol, recalculating the binary length if necessary. + */ + inputMode = new qrMode[content.length()]; + modeFirstFix(); + est_binlen = estimate_binary_length(); + + ecc_level = preferredEccLevel; + switch (preferredEccLevel) { + case L: + max_cw = 2956; + break; + case M: + max_cw = 2334; + break; + case Q: + max_cw = 1666; + break; + case H: + max_cw = 1276; + break; + default: + max_cw = 2956; + break; + } + + autosize = 40; + for (i = 39; i >= 0; i--) { + switch (ecc_level) { + case L: + if ((8 * qr_data_codewords_L[i]) >= est_binlen) { + autosize = i + 1; + } + break; + case M: + if ((8 * qr_data_codewords_M[i]) >= est_binlen) { + autosize = i + 1; + } + break; + case Q: + if ((8 * qr_data_codewords_Q[i]) >= est_binlen) { + autosize = i + 1; + } + break; + case H: + if ((8 * qr_data_codewords_H[i]) >= est_binlen) { + autosize = i + 1; + } + break; + } + } + + // The first guess of symbol size is in autosize. Use this to optimise. + est_binlen = getBinaryLength(autosize); + + if (est_binlen > (8 * max_cw)) { + errorMsg.append("Input too long for selected error correction level"); + return false; + } + + // Now see if the optimised binary will fit in a smaller symbol. + canShrink = true; + + do { + if (autosize == 1) { + canShrink = false; + } else { + if (tribus(autosize - 1, 1, 2, 3) != tribus(autosize, 1, 2, 3)) { + // Length of binary needed to encode the data in the smaller symbol is different, recalculate + est_binlen = getBinaryLength(autosize - 1); + } + + switch (ecc_level) { + case L: + if ((8 * qr_data_codewords_L[autosize - 2]) < est_binlen) { + canShrink = false; + } + break; + case M: + if ((8 * qr_data_codewords_M[autosize - 2]) < est_binlen) { + canShrink = false; + } + break; + case Q: + if ((8 * qr_data_codewords_Q[autosize - 2]) < est_binlen) { + canShrink = false; + } + break; + case H: + if ((8 * qr_data_codewords_H[autosize - 2]) < est_binlen) { + canShrink = false; + } + break; + } + + if (canShrink) { + // Optimisation worked - data will fit in a smaller symbol + autosize--; + } else { + // Data did not fit in the smaller symbol, revert to original size + if (tribus(autosize - 1, 1, 2, 3) != tribus(autosize, 1, 2, 3)) { + est_binlen = getBinaryLength(autosize); + } + } + } + } while (canShrink); + + version = autosize; + + if ((preferredVersion >= 1) && (preferredVersion <= 40)) { + /* If the user has selected a larger symbol than the smallest available, + then use the size the user has selected, and re-optimise for this + symbol size. + */ + if (preferredVersion > version) { + version = preferredVersion; + est_binlen = getBinaryLength(preferredVersion); + } + } + + /* Ensure maxium error correction capacity */ + if (est_binlen <= (qr_data_codewords_M[version - 1] * 8)) { + ecc_level = EccMode.M; + } + if (est_binlen <= (qr_data_codewords_Q[version - 1] * 8)) { + ecc_level = EccMode.Q; + } + if (est_binlen <= (qr_data_codewords_H[version - 1] * 8)) { + ecc_level = EccMode.H; + } + + targetCwCount = qr_data_codewords_L[version - 1]; + blocks = qr_blocks_L[version - 1]; + switch (ecc_level) { + case M: + targetCwCount = qr_data_codewords_M[version - 1]; + blocks = qr_blocks_M[version - 1]; + break; + case Q: + targetCwCount = qr_data_codewords_Q[version - 1]; + blocks = qr_blocks_Q[version - 1]; + break; + case H: + targetCwCount = qr_data_codewords_H[version - 1]; + blocks = qr_blocks_H[version - 1]; + break; + } + + datastream = new int[targetCwCount + 1]; + fullstream = new int[qr_total_codewords[version - 1] + 1]; + + if (!(qr_binary(version, targetCwCount))) { + /* Invalid characters used - stop encoding */ + return false; + } + + add_ecc(version, targetCwCount, blocks); + + size = qr_sizes[version - 1]; + + grid = new byte[size * size]; + + encodeInfo.append("Version: ").append(version).append("\n"); + encodeInfo.append("ECC Level: "); + switch (ecc_level) { + case L: + encodeInfo.append("L\n"); + break; + case M: + encodeInfo.append("M\n"); + break; + case Q: + encodeInfo.append("Q\n"); + break; + case H: + default: + encodeInfo.append("H\n"); + break; + } + + for (i = 0; i < size; i++) { + for (j = 0; j < size; j++) { + grid[(i * size) + j] = 0; + } + } + + setup_grid(size, version); + populate_grid(size, qr_total_codewords[version - 1]); + bitmask = apply_bitmask(size, ecc_level); + encodeInfo.append("Mask Pattern: ").append(Integer.toBinaryString(bitmask)).append("\n"); + add_format_info(size, ecc_level, bitmask); + if (version >= 7) { + add_version_info(size, version); + } + + readable = new StringBuilder(); + pattern = new String[size]; + rowCount = size; + rowHeight = new int[size]; + for (i = 0; i < size; i++) { + bin = new StringBuilder(); + for (j = 0; j < size; j++) { + if ((grid[(i * size) + j] & 0x01) != 0) { + bin.append("1"); + } else { + bin.append("0"); + } + } + pattern[i] = bin2pat(bin.toString()); + rowHeight[i] = 1; + } + + plotSymbol(); + return true; + } + + private void modeFirstFix() { + int i; + + eciProcess(); // Get ECI mode + + if (eciMode == 20) { + /* Shift-JIS encoding, use Kanji mode */ + inputLength = content.length(); + inputData = new int[inputLength]; + for (i = 0; i < inputLength; i++) { + inputData[i] = (int) content.charAt(i); + } + } else { + /* Any other encoding method */ + inputLength = inputBytes.length; + inputData = new int[inputLength]; + for (i = 0; i < inputLength; i++) { + inputData[i] = inputBytes[i] & 0xFF; + } + } + + inputMode = new qrMode[inputLength]; + + for (i = 0; i < inputLength; i++) { + if (inputData[i] > 0xff) { + inputMode[i] = qrMode.KANJI; + } else { + inputMode[i] = qrMode.BINARY; + if (isXAlpha((char) (inputData[i] & 0xFF))) { + inputMode[i] = qrMode.ALPHANUM; + } + if ((inputDataType == DataType.GS1) && (inputData[i] == '[')) { + inputMode[i] = qrMode.ALPHANUM; + } + if (isXNumeric((char) (inputData[i] & 0xff))) { + inputMode[i] = qrMode.NUMERIC; + } + } + } + } + + private int getBinaryLength(int version) { + /* Calculate the actial bitlength of the proposed binary string */ + qrMode currentMode; + int i, j; + int count = 0; + + applyOptimisation(version); + + currentMode = qrMode.NULL; + + if (eciMode != 3) { + count += 12; + } + + if (inputDataType == DataType.GS1) { + count += 4; + } + + for (i = 0; i < inputLength; i++) { + if (inputMode[i] != currentMode) { + count += 4; + switch (inputMode[i]) { + case KANJI: + count += tribus(version, 8, 10, 12); + count += (blockLength(i) * 13); + break; + case BINARY: + count += tribus(version, 8, 16, 16); + for (j = i; j < (i + blockLength(i)); j++) { + if (inputData[j] > 0xff) { + count += 16; + } else { + count += 8; + } + } + break; + case ALPHANUM: + count += tribus(version, 9, 11, 13); + switch (blockLength(i) % 2) { + case 0: + count += (blockLength(i) / 2) * 11; + break; + case 1: + count += ((blockLength(i) - 1) / 2) * 11; + count += 6; + break; + } + break; + case NUMERIC: + count += tribus(version, 10, 12, 14); + switch (blockLength(i) % 3) { + case 0: + count += (blockLength(i) / 3) * 10; + break; + case 1: + count += ((blockLength(i) - 1) / 3) * 10; + count += 4; + break; + case 2: + count += ((blockLength(i) - 2) / 3) * 10; + count += 7; + break; + } + break; + } + currentMode = inputMode[i]; + } + } + + return count; + } + + private void applyOptimisation(int version) { + /* Implements a custom optimisation algorithm, because implementation + of the algorithm shown in Annex J.2 created LONGER binary sequences. + */ + + int blockCount = 0; + int i, j; + qrMode currentMode = qrMode.NULL; + + for (i = 0; i < inputLength; i++) { + if (inputMode[i] != currentMode) { + currentMode = inputMode[i]; + blockCount++; + } + } + + int[] blockLength = new int[blockCount]; + qrMode[] blockMode = new qrMode[blockCount]; + + j = -1; + currentMode = qrMode.NULL; + for (i = 0; i < inputLength; i++) { + if (inputMode[i] != currentMode) { + j++; + blockLength[j] = 1; + blockMode[j] = inputMode[i]; + currentMode = inputMode[i]; + } else { + blockLength[j]++; + } + } + + if (blockCount > 1) { + // Search forward + for (i = 0; i <= (blockCount - 2); i++) { + if (blockMode[i] == qrMode.BINARY) { + switch (blockMode[i + 1]) { + case KANJI: + if (blockLength[i + 1] < tribus(version, 4, 5, 6)) { + blockMode[i + 1] = qrMode.BINARY; + } + break; + case ALPHANUM: + if (blockLength[i + 1] < tribus(version, 7, 8, 9)) { + blockMode[i + 1] = qrMode.BINARY; + } + break; + case NUMERIC: + if (blockLength[i + 1] < tribus(version, 3, 4, 5)) { + blockMode[i + 1] = qrMode.BINARY; + } + break; + } + } + + if ((blockMode[i] == qrMode.ALPHANUM) + && (blockMode[i + 1] == qrMode.NUMERIC)) { + if (blockLength[i + 1] < tribus(version, 6, 8, 10)) { + blockMode[i + 1] = qrMode.ALPHANUM; + } + } + } + + // Search backward + for (i = blockCount - 1; i > 0; i--) { + if (blockMode[i] == qrMode.BINARY) { + switch (blockMode[i - 1]) { + case KANJI: + if (blockLength[i - 1] < tribus(version, 4, 5, 6)) { + blockMode[i - 1] = qrMode.BINARY; + } + break; + case ALPHANUM: + if (blockLength[i - 1] < tribus(version, 7, 8, 9)) { + blockMode[i - 1] = qrMode.BINARY; + } + break; + case NUMERIC: + if (blockLength[i - 1] < tribus(version, 3, 4, 5)) { + blockMode[i - 1] = qrMode.BINARY; + } + break; + } + } + + if ((blockMode[i] == qrMode.ALPHANUM) + && (blockMode[i - 1] == qrMode.NUMERIC)) { + if (blockLength[i - 1] < tribus(version, 6, 8, 10)) { + blockMode[i - 1] = qrMode.ALPHANUM; + } + } + } + } + + j = 0; + for (int block = 0; block < blockCount; block++) { + currentMode = blockMode[block]; + for (i = 0; i < blockLength[block]; i++) { + inputMode[j] = currentMode; + j++; + } + } + } + + private int blockLength(int start) { + /* Find the length of the block starting from 'start' */ + int i, count; + qrMode mode = inputMode[start]; + + count = 0; + i = start; + + do { + count++; + } while (((i + count) < inputLength) && (inputMode[i + count] == mode)); + + return count; + } + + private int tribus(int version, int a, int b, int c) { + /* Choose from three numbers based on version */ + int RetVal; + + RetVal = c; + + if (version < 10) { + RetVal = a; + } + + if ((version >= 10) && (version <= 26)) { + RetVal = b; + } + + return RetVal; + } + + private boolean isXAlpha(char cglyph) { + /* Returns true if input is in exclusive Alphanumeric set (Table J.1) */ + boolean retval = false; + + if ((cglyph >= 'A') && (cglyph <= 'Z')) { + retval = true; + } + switch (cglyph) { + case ' ': + case '$': + case '%': + case '*': + case '+': + case '-': + case '.': + case '/': + case ':': + retval = true; + break; + } + + return retval; + } + + private boolean isXNumeric(char cglyph) { + /* Returns true if input is in exclusive Numeric set (Table J.1) */ + boolean retval; + + if ((cglyph >= '0') && (cglyph <= '9')) { + retval = true; + } else { + retval = false; + } + + return retval; + } + + private int estimate_binary_length() { + /* Make an estimate (worst case scenario) of how long the binary string will be */ + int i, count = 0; + qrMode current = qrMode.NULL; + int a_count = 0; + int n_count = 0; + + if (eciMode != 3) { + count += 12; + } + + if (inputDataType == DataType.GS1) { + count += 4; + } + + for (i = 0; i < inputLength; i++) { + if (inputMode[i] != current) { + switch (inputMode[i]) { + case KANJI: + count += 12 + 4; + current = qrMode.KANJI; + break; + case BINARY: + count += 16 + 4; + current = qrMode.BINARY; + break; + case ALPHANUM: + count += 13 + 4; + current = qrMode.ALPHANUM; + a_count = 0; + break; + case NUMERIC: + count += 14 + 4; + current = qrMode.NUMERIC; + n_count = 0; + break; + } + } + + switch (inputMode[i]) { + case KANJI: + count += 13; + break; + case BINARY: + count += 8; + break; + case ALPHANUM: + a_count++; + if ((a_count & 1) == 0) { + count += 5; // 11 in total + a_count = 0; + } else { + count += 6; + } + break; + case NUMERIC: + n_count++; + if ((n_count % 3) == 0) { + count += 3; // 10 in total + n_count = 0; + } else if ((n_count & 1) == 0) { + count += 3; // 7 in total + } else { + count += 4; + } + break; + } + } + + return count; + } + + private boolean qr_binary(int version, int target_binlen) { + /* Convert input data to a binary stream and add padding */ + int position = 0; + int short_data_block_length, i, scheme = 1; + int padbits; + int current_binlen, current_bytes; + int toggle; + boolean alphanumPercent; + String oneChar; + qrMode data_block; + int jis; + byte[] jisBytes; + int msb, lsb, prod; + int count, first, second, third; + int weight; + + binary = new StringBuilder(); + + /* Note: Shift-JIS characters can be encoded in either Kanji + mode or Byte mode. If no ECI code is given, a sequence in Byte + mode could be interpreted in two ways - for example 0xE4 0x6E + could refer to U+E4 and U+6E (än) or U+8205 (舅). To avoid + Mojibake, if using ECI 20 (i.e. if only valid Shift-JIS + characters are found in the input data), this code will insert + an explicit ECI 20 instruction. Symbols without an ECI can then + be assumed to be ECI 3 (ISO 8859-1). + */ + if (eciMode != 3) { + binary.append("0111"); /* ECI */ + + if ((eciMode >= 0) && (eciMode <= 127)) { + binary.append("0"); + qr_bscan(eciMode, 0x40); + } + + if ((eciMode >= 128) && (eciMode <= 16383)) { + binary.append("10"); + qr_bscan(eciMode, 0x1000); + } + + if ((eciMode >= 16384) && (eciMode <= 999999)) { + binary.append("110"); + qr_bscan(eciMode, 0x100000); + } + } + + if (inputDataType == DataType.GS1) { + binary.append("0101"); /* FNC1 */ + + } + + if (version <= 9) { + scheme = 1; + } else if (version <= 26) { + scheme = 2; + } else { + scheme = 3; + } + + encodeInfo.append("Encoding: "); + + alphanumPercent = false; + + do { + data_block = inputMode[position]; + short_data_block_length = 0; + do { + short_data_block_length++; + } while (((short_data_block_length + position) < inputLength) + && (inputMode[position + short_data_block_length] == data_block)); + + switch (data_block) { + case KANJI: + /* Kanji mode */ + /* Mode indicator */ + binary.append("1000"); + + /* Character count indicator */ + qr_bscan(short_data_block_length, 0x20 << (scheme * 2)); /* scheme = 1..3 */ + + encodeInfo.append("KNJI "); + + /* Character representation */ + for (i = 0; i < short_data_block_length; i++) { + oneChar = ""; + oneChar += (char) inputData[position + i]; + + /* Convert Unicode input to Shift-JIS */ + try { + jisBytes = oneChar.getBytes("SJIS"); + } catch (UnsupportedEncodingException e) { + errorMsg.append("Shift-JIS character conversion error"); + return false; + } + + jis = ((jisBytes[0] & 0xFF) << 8) + (jisBytes[1] & 0xFF); + + if (jis > 0x9fff) { + jis -= 0xc140; + } else { + jis -= 0x8140; + } + msb = (jis & 0xff00) >> 8; + lsb = (jis & 0xff); + prod = (msb * 0xc0) + lsb; + + qr_bscan(prod, 0x1000); + + encodeInfo.append(Integer.toString(prod)).append(" "); + } + break; + case BINARY: + /* Byte mode */ + /* Mode indicator */ + binary.append("0100"); + int kanjiModifiedLength = short_data_block_length; + + for (i = 0; i < short_data_block_length; i++) { + if (inputData[position + i] > 0xff) { + kanjiModifiedLength++; + } + } + + /* Character count indicator */ + qr_bscan(kanjiModifiedLength, scheme > 1 ? 0x8000 : 0x80); /* scheme = 1..3 */ + + encodeInfo.append("BYTE "); + + /* Character representation */ + for (i = 0; i < short_data_block_length; i++) { + if (inputData[position + i] > 0xff) { + // Convert Kanji character to Shift-JIS + oneChar = ""; + oneChar += (char) inputData[position + i]; + + /* Convert Unicode input to Shift-JIS */ + try { + jisBytes = oneChar.getBytes("SJIS"); + } catch (UnsupportedEncodingException e) { + errorMsg.append("Shift-JIS character conversion error"); + return false; + } + + qr_bscan((int) jisBytes[0] & 0xff, 0x80); + qr_bscan((int) jisBytes[1] & 0xff, 0x80); + + encodeInfo.append("(").append(Integer.toString((int) jisBytes[0] & 0xff)).append(" "); + encodeInfo.append(Integer.toString((int) jisBytes[1] & 0xff)).append(") "); + } else { + // Process 8-bit byte + int lbyte = (inputData[position + i] & 0xFF); + + if ((inputDataType == DataType.GS1) && (lbyte == '[')) { + lbyte = 0x1d; /* FNC1 */ + + } + + qr_bscan(lbyte, 0x80); + + encodeInfo.append(Integer.toString(lbyte)).append(" "); + } + } + + break; + case ALPHANUM: + /* Alphanumeric mode */ + /* Mode indicator */ + binary.append("0010"); + + /* Character count indicator */ + qr_bscan(short_data_block_length, 0x40 << (2 * scheme)); /* scheme = 1..3 */ + + encodeInfo.append("ALPH "); + + /* Character representation */ + i = 0; + while (i < short_data_block_length) { + + if (!alphanumPercent) { + if ((inputDataType == DataType.GS1) && (inputData[position + i] == '%')) { + first = positionOf('%', rhodium); + second = positionOf('%', rhodium); + count = 2; + prod = (first * 45) + second; + i++; + } else { + if ((inputDataType == DataType.GS1) && (inputData[position + i] == '[')) { + first = positionOf('%', rhodium); /* FNC1 */ + + } else { + first = positionOf((char) (inputData[position + i] & 0xFF), rhodium); + } + count = 1; + i++; + prod = first; + + if (i < short_data_block_length) { + if (inputMode[position + i] == qrMode.ALPHANUM) { + if ((inputDataType == DataType.GS1) && (inputData[position + i] == '%')) { + second = positionOf('%', rhodium); + count = 2; + prod = (first * 45) + second; + alphanumPercent = true; + } else { + if ((inputDataType == DataType.GS1) && (inputData[position + i] == '[')) { + second = positionOf('%', rhodium); /* FNC1 */ + + } else { + second = positionOf((char) (inputData[position + i] & 0xFF), rhodium); + } + count = 2; + i++; + prod = (first * 45) + second; + } + } + } + } + } else { + first = positionOf('%', rhodium); + count = 1; + i++; + prod = first; + alphanumPercent = false; + + if (i < short_data_block_length) { + if (inputMode[position + i] == qrMode.ALPHANUM) { + if ((inputDataType == DataType.GS1) && (inputData[position + i] == '%')) { + second = positionOf('%', rhodium); + count = 2; + prod = (first * 45) + second; + alphanumPercent = true; + } else { + if ((inputDataType == DataType.GS1) && (inputData[position + i] == '[')) { + second = positionOf('%', rhodium); /* FNC1 */ + + } else { + second = positionOf((char) (inputData[position + i] & 0xFF), rhodium); + } + count = 2; + i++; + prod = (first * 45) + second; + } + } + } + } + + qr_bscan(prod, count == 2 ? 0x400 : 0x20); /* count = 1..2 */ + + encodeInfo.append(Integer.toString(prod)).append(" "); + } + ; + break; + case NUMERIC: + /* Numeric mode */ + /* Mode indicator */ + binary.append("0001"); + + /* Character count indicator */ + qr_bscan(short_data_block_length, 0x80 << (2 * scheme)); /* scheme = 1..3 */ + + encodeInfo.append("NUMB "); + + /* Character representation */ + i = 0; + while (i < short_data_block_length) { + + first = Character.getNumericValue(inputData[position + i]); + count = 1; + prod = first; + + if ((i + 1) < short_data_block_length) { + second = Character.getNumericValue(inputData[position + i + 1]); + count = 2; + prod = (prod * 10) + second; + } + + if ((i + 2) < short_data_block_length) { + third = Character.getNumericValue(inputData[position + i + 2]); + count = 3; + prod = (prod * 10) + third; + } + + qr_bscan(prod, 1 << (3 * count)); /* count = 1..3 */ + + encodeInfo.append(Integer.toString(prod)).append(" "); + + i += count; + } + ; + break; + } + position += short_data_block_length; + } while (position < inputLength); + + encodeInfo.append("\n"); + + /* Terminator */ + binary.append("0000"); + + current_binlen = binary.length(); + padbits = 8 - (current_binlen % 8); + if (padbits == 8) { + padbits = 0; + } + current_bytes = (current_binlen + padbits) / 8; + + /* Padding bits */ + for (i = 0; i < padbits; i++) { + binary.append("0"); + } + + /* Put data into 8-bit codewords */ + for (i = 0; i < current_bytes; i++) { + datastream[i] = 0x00; + for (weight = 0; weight < 8; weight++) { + if (binary.charAt((i * 8) + weight) == '1') { + datastream[i] += (0x80 >> weight); + } + } + } + + /* Add pad codewords */ + toggle = 0; + for (i = current_bytes; i < target_binlen; i++) { + if (toggle == 0) { + datastream[i] = 0xec; + toggle = 1; + } else { + datastream[i] = 0x11; + toggle = 0; + } + } + + encodeInfo.append("Codewords: "); + for (i = 0; i < target_binlen; i++) { + encodeInfo.append(Integer.toString(datastream[i])).append(" "); + } + encodeInfo.append("\n"); + + return true; + } + + private void qr_bscan(int data, int h) { + + for (; + (h != 0); h >>= 1) { + if ((data & h) != 0) { + binary.append("1"); + } else { + binary.append("0"); + } + } + + } + + private void add_ecc(int version, int data_cw, int blocks) { + /* Split data into blocks, add error correction and then interleave the blocks and error correction data */ + int ecc_cw = qr_total_codewords[version - 1] - data_cw; + int short_data_block_length = data_cw / blocks; + int qty_long_blocks = data_cw % blocks; + int qty_short_blocks = blocks - qty_long_blocks; + int ecc_block_length = ecc_cw / blocks; + int i, j, k, length_this_block, posn; + + int[] data_block = new int[short_data_block_length + 2]; + int[] ecc_block = new int[ecc_block_length + 2]; + int[] interleaved_data = new int[data_cw + 2]; + int[] interleaved_ecc = new int[ecc_cw + 2]; + + posn = 0; + + for (i = 0; i < blocks; i++) { + ReedSolomon rs = new ReedSolomon(); + if (i < qty_short_blocks) { + length_this_block = short_data_block_length; + } else { + length_this_block = short_data_block_length + 1; + } + + for (j = 0; j < ecc_block_length; j++) { + ecc_block[j] = 0; + } + + for (j = 0; j < length_this_block; j++) { + data_block[j] = datastream[posn + j]; + } + + rs.init_gf(0x11d); + rs.init_code(ecc_block_length, 0); + rs.encode(length_this_block, data_block); + for (k = 0; k < ecc_block_length; k++) { + ecc_block[k] = rs.getResult(k); + } + for (j = 0; j < short_data_block_length; j++) { + interleaved_data[(j * blocks) + i] = data_block[j]; + } + + if (i >= qty_short_blocks) { + interleaved_data[(short_data_block_length * blocks) + (i - qty_short_blocks)] = data_block[short_data_block_length]; + } + + for (j = 0; j < ecc_block_length; j++) { + interleaved_ecc[(j * blocks) + i] = ecc_block[ecc_block_length - j - 1]; + } + + posn += length_this_block; + } + + for (j = 0; j < data_cw; j++) { + fullstream[j] = interleaved_data[j]; + } + for (j = 0; j < ecc_cw; j++) { + fullstream[j + data_cw] = interleaved_ecc[j]; + } + } + + private void setup_grid(int size, int version) { + int i; + int loopsize, x, y, xcoord, ycoord; + boolean toggle = true; + + /* Add timing patterns */ + for (i = 0; i < size; i++) { + if (toggle) { + grid[(6 * size) + i] = 0x21; + grid[(i * size) + 6] = 0x21; + toggle = false; + } else { + grid[(6 * size) + i] = 0x20; + grid[(i * size) + 6] = 0x20; + toggle = true; + } + } + + /* Add finder patterns */ + place_finder(size, 0, 0); + place_finder(size, 0, size - 7); + place_finder(size, size - 7, 0); + + /* Add separators */ + for (i = 0; i < 7; i++) { + grid[(7 * size) + i] = 0x10; + grid[(i * size) + 7] = 0x10; + grid[(7 * size) + (size - 1 - i)] = 0x10; + grid[(i * size) + (size - 8)] = 0x10; + grid[((size - 8) * size) + i] = 0x10; + grid[((size - 1 - i) * size) + 7] = 0x10; + } + grid[(7 * size) + 7] = 0x10; + grid[(7 * size) + (size - 8)] = 0x10; + grid[((size - 8) * size) + 7] = 0x10; + + /* Add alignment patterns */ + if (version != 1) { + /* Version 1 does not have alignment patterns */ + + loopsize = qr_align_loopsize[version - 1]; + for (x = 0; x < loopsize; x++) { + for (y = 0; y < loopsize; y++) { + xcoord = qr_table_e1[((version - 2) * 7) + x]; + ycoord = qr_table_e1[((version - 2) * 7) + y]; + + if ((grid[(ycoord * size) + xcoord] & 0x10) == 0) { + place_align(size, xcoord, ycoord); + } + } + } + } + + /* Reserve space for format information */ + for (i = 0; i < 8; i++) { + grid[(8 * size) + i] += 0x20; + grid[(i * size) + 8] += 0x20; + grid[(8 * size) + (size - 1 - i)] = 0x20; + grid[((size - 1 - i) * size) + 8] = 0x20; + } + grid[(8 * size) + 8] += 0x20; + grid[((size - 1 - 7) * size) + 8] = 0x21; /* Dark Module from Figure 25 */ + + /* Reserve space for version information */ + if (version >= 7) { + for (i = 0; i < 6; i++) { + grid[((size - 9) * size) + i] = 0x20; + grid[((size - 10) * size) + i] = 0x20; + grid[((size - 11) * size) + i] = 0x20; + grid[(i * size) + (size - 9)] = 0x20; + grid[(i * size) + (size - 10)] = 0x20; + grid[(i * size) + (size - 11)] = 0x20; + } + } + } + + private void place_finder(int size, int x, int y) { + int xp, yp; + + int[] finder = { + 1, 1, 1, 1, 1, 1, 1, + 1, 0, 0, 0, 0, 0, 1, + 1, 0, 1, 1, 1, 0, 1, + 1, 0, 1, 1, 1, 0, 1, + 1, 0, 1, 1, 1, 0, 1, + 1, 0, 0, 0, 0, 0, 1, + 1, 1, 1, 1, 1, 1, 1 + }; + + for (xp = 0; xp < 7; xp++) { + for (yp = 0; yp < 7; yp++) { + if (finder[xp + (7 * yp)] == 1) { + grid[((yp + y) * size) + (xp + x)] = 0x11; + } else { + grid[((yp + y) * size) + (xp + x)] = 0x10; + } + } + } + } + + private void place_align(int size, int x, int y) { + int xp, yp; + + int[] alignment = { + 1, 1, 1, 1, 1, + 1, 0, 0, 0, 1, + 1, 0, 1, 0, 1, + 1, 0, 0, 0, 1, + 1, 1, 1, 1, 1 + }; + + x -= 2; + y -= 2; /* Input values represent centre of pattern */ + + for (xp = 0; xp < 5; xp++) { + for (yp = 0; yp < 5; yp++) { + if (alignment[xp + (5 * yp)] == 1) { + grid[((yp + y) * size) + (xp + x)] = 0x11; + } else { + grid[((yp + y) * size) + (xp + x)] = 0x10; + } + } + } + } + + private void populate_grid(int size, int cw) { + boolean goingUp = true; + int row = 0; /* right hand side */ + + int i, n, x, y; + + n = cw * 8; + y = size - 1; + i = 0; + do { + x = (size - 2) - (row * 2); + if (x < 6) { + x--; /* skip over vertical timing pattern */ + + } + + if ((grid[(y * size) + (x + 1)] & 0xf0) == 0) { + if (cwbit(i)) { + grid[(y * size) + (x + 1)] = 0x01; + } else { + grid[(y * size) + (x + 1)] = 0x00; + } + i++; + } + + if (i < n) { + if ((grid[(y * size) + x] & 0xf0) == 0) { + if (cwbit(i)) { + grid[(y * size) + x] = 0x01; + } else { + grid[(y * size) + x] = 0x00; + } + i++; + } + } + + if (goingUp) { + y--; + } else { + y++; + } + if (y == -1) { + /* reached the top */ + row++; + y = 0; + goingUp = false; + } + if (y == size) { + /* reached the bottom */ + row++; + y = size - 1; + goingUp = true; + } + } while (i < n); + } + + private boolean cwbit(int i) { + boolean resultant = false; + + if ((fullstream[i / 8] & (0x80 >> (i % 8))) != 0) { + resultant = true; + } + + return resultant; + } + + private int apply_bitmask(int size, EccMode ecc_level) { + int x, y; + char p; + int local_pattern; + int best_val, best_pattern; + int[] penalty = new int[8]; + byte[] mask = new byte[size * size]; + eval = new byte[size * size]; + + + /* Perform data masking */ + for (x = 0; x < size; x++) { + for (y = 0; y < size; y++) { + mask[(y * size) + x] = 0x00; + + if ((grid[(y * size) + x] & 0xf0) == 0) { + if (((y + x) & 1) == 0) { + mask[(y * size) + x] += 0x01; + } + if ((y & 1) == 0) { + mask[(y * size) + x] += 0x02; + } + if ((x % 3) == 0) { + mask[(y * size) + x] += 0x04; + } + if (((y + x) % 3) == 0) { + mask[(y * size) + x] += 0x08; + } + if ((((y / 2) + (x / 3)) & 1) == 0) { + mask[(y * size) + x] += 0x10; + } + if ((((y * x) & 1) + ((y * x) % 3)) == 0) { + mask[(y * size) + x] += 0x20; + } + if (((((y * x) & 1) + ((y * x) % 3)) & 1) == 0) { + mask[(y * size) + x] += 0x40; + } + if (((((y + x) & 1) + ((y * x) % 3)) & 1) == 0) { + mask[(y * size) + x] += 0x80; + } + } + } + } + + for (x = 0; x < size; x++) { + for (y = 0; y < size; y++) { + if ((grid[(y * size) + x] & 0x01) != 0) { + p = 0xff; + } else { + p = 0x00; + } + + eval[(y * size) + x] = (byte) (mask[(y * size) + x] ^ p); + } + } + + + /* Evaluate result */ + for (local_pattern = 0; local_pattern < 8; local_pattern++) { + add_format_info_eval(size, ecc_level, local_pattern); + penalty[local_pattern] = evaluate(size, local_pattern); + } + + best_pattern = 0; + best_val = penalty[0]; + for (local_pattern = 1; local_pattern < 8; local_pattern++) { + if (penalty[local_pattern] < best_val) { + best_pattern = local_pattern; + best_val = penalty[local_pattern]; + } + } + + /* Apply mask */ + for (x = 0; x < size; x++) { + for (y = 0; y < size; y++) { + if ((mask[(y * size) + x] & (0x01 << best_pattern)) != 0) { + if ((grid[(y * size) + x] & 0x01) != 0) { + grid[(y * size) + x] = 0x00; + } else { + grid[(y * size) + x] = 0x01; + } + } + } + } + + return best_pattern; + } + + private void add_format_info_eval(int size, EccMode ecc_level, int pattern) { + /* Add format information to grid */ + + int format = pattern; + int seq; + int i; + + switch (ecc_level) { + case L: + format += 0x08; + break; + case Q: + format += 0x18; + break; + case H: + format += 0x10; + break; + } + + seq = qr_annex_c[format]; + + for (i = 0; i < 6; i++) { + if (((seq >> i) & 0x01) != 0) { + eval[(i * size) + 8] = (byte) (0x01 >> pattern); + } else { + eval[(i * size) + 8] = 0; + } + } + + for (i = 0; i < 8; i++) { + if (((seq >> i) & 0x01) != 0) { + eval[(8 * size) + (size - i - 1)] = (byte) (0x01 >> pattern); + } else { + eval[(8 * size) + (size - i - 1)] = 0; + } + } + + for (i = 0; i < 6; i++) { + if (((seq >> (i + 9)) & 0x01) != 0) { + eval[(8 * size) + (5 - i)] = (byte) (0x01 >> pattern); + } else { + eval[(8 * size) + (5 - i)] = 0; + } + } + + for (i = 0; i < 7; i++) { + if (((seq >> (i + 8)) & 0x01) != 0) { + eval[(((size - 7) + i) * size) + 8] = (byte) (0x01 >> pattern); + } else { + eval[(((size - 7) + i) * size) + 8] = 0; + } + } + + if (((seq >> 6) & 0x01) != 0) { + eval[(7 * size) + 8] = (byte) (0x01 >> pattern); + } else { + eval[(7 * size) + 8] = 0; + } + + if (((seq >> 7) & 0x01) != 0) { + eval[(8 * size) + 8] = (byte) (0x01 >> pattern); + } else { + eval[(8 * size) + 8] = 0; + } + + if (((seq >> 8) & 0x01) != 0) { + eval[(8 * size) + 7] = (byte) (0x01 >> pattern); + } else { + eval[(8 * size) + 7] = 0; + } + } + + private int evaluate(int size, int pattern) { + int x, y, block; + int result = 0; + int state; + int p; + int weight; + int dark_mods; + int percentage, k; + int a, b, afterCount, beforeCount; + byte[] local = new byte[size * size]; + + for (x = 0; x < size; x++) { + for (y = 0; y < size; y++) { + if ((eval[(y * size) + x] & (0x01 << pattern)) != 0) { + local[(y * size) + x] = '1'; + } else { + local[(y * size) + x] = '0'; + } + } + } + + /* Test 1: Adjacent modules in row/column in same colour */ + /* Vertical */ + for (x = 0; x < size; x++) { + state = local[x]; + block = 0; + for (y = 0; y < size; y++) { + if (local[(y * size) + x] == state) { + block++; + } else { + if (block > 5) { + result += (3 + (block - 5)); + } + block = 0; + state = local[(y * size) + x]; + } + } + if (block > 5) { + result += (3 + (block - 5)); + } + } + + /* Horizontal */ + for (y = 0; y < size; y++) { + state = local[y * size]; + block = 0; + for (x = 0; x < size; x++) { + if (local[(y * size) + x] == state) { + block++; + } else { + if (block > 5) { + result += (3 + (block - 5)); + } + block = 0; + state = local[(y * size) + x]; + } + } + if (block > 5) { + result += (3 + (block - 5)); + } + } + + /* Test 2: Block of modules in same color */ + for (x = 0; x < size - 1; x++) { + for (y = 0; y < size - 1; y++) { + if (((local[(y * size) + x] == local[((y + 1) * size) + x]) + && (local[(y * size) + x] == local[(y * size) + (x + 1)])) + && (local[(y * size) + x] == local[((y + 1) * size) + (x + 1)])) { + result += 3; + } + } + } + + /* Test 3: 1:1:3:1:1 ratio pattern in row/column */ + /* Vertical */ + for (x = 0; x < size; x++) { + for (y = 0; y < (size - 7); y++) { + p = 0; + for (weight = 0; weight < 7; weight++) { + if (local[((y + weight) * size) + x] == '1') { + p += (0x40 >> weight); + } + } + if (p == 0x5d) { + /* Pattern found, check before and after */ + beforeCount = 0; + for (b = (y - 4); b < y; b++) { + if (b < 0) { + beforeCount++; + } else { + if (local[(b * size) + x] == '0') { + beforeCount++; + } else { + beforeCount = 0; + } + } + } + + afterCount = 0; + for (a = (y + 7); a <= (y + 10); a++) { + if (a >= size) { + afterCount++; + } else { + if (local[(a * size) + x] == '0') { + afterCount++; + } else { + afterCount = 0; + } + } + } + + if ((beforeCount == 4) || (afterCount == 4)) { + /* Pattern is preceeded or followed by light area + 4 modules wide */ + result += 40; + } + } + } + } + + /* Horizontal */ + for (y = 0; y < size; y++) { + for (x = 0; x < (size - 7); x++) { + p = 0; + for (weight = 0; weight < 7; weight++) { + if (local[(y * size) + x + weight] == '1') { + p += (0x40 >> weight); + } + } + if (p == 0x5d) { + /* Pattern found, check before and after */ + beforeCount = 0; + for (b = (x - 4); b < x; b++) { + if (b < 0) { + beforeCount++; + } else { + if (local[(y * size) + b] == '0') { + beforeCount++; + } else { + beforeCount = 0; + } + } + } + + afterCount = 0; + for (a = (x + 7); a <= (x + 10); a++) { + if (a >= size) { + afterCount++; + } else { + if (local[(y * size) + a] == '0') { + afterCount++; + } else { + afterCount = 0; + } + } + } + + if ((beforeCount == 4) || (afterCount == 4)) { + /* Pattern is preceeded or followed by light area + 4 modules wide */ + result += 40; + } + } + } + } + + /* Test 4: Proportion of dark modules in entire symbol */ + dark_mods = 0; + for (x = 0; x < size; x++) { + for (y = 0; y < size; y++) { + if (local[(y * size) + x] == '1') { + dark_mods++; + } + } + } + percentage = 100 * (dark_mods / (size * size)); + if (percentage <= 50) { + k = ((100 - percentage) - 50) / 5; + } else { + k = (percentage - 50) / 5; + } + + result += 10 * k; + + return result; + } + + private void add_format_info(int size, EccMode ecc_level, int pattern) { + /* Add format information to grid */ + + int format = pattern; + int seq; + int i; + + switch (ecc_level) { + case L: + format += 0x08; + break; + case Q: + format += 0x18; + break; + case H: + format += 0x10; + break; + } + + seq = qr_annex_c[format]; + + for (i = 0; i < 6; i++) { + grid[(i * size) + 8] += (seq >> i) & 0x01; + } + + for (i = 0; i < 8; i++) { + grid[(8 * size) + (size - i - 1)] += (seq >> i) & 0x01; + } + + for (i = 0; i < 6; i++) { + grid[(8 * size) + (5 - i)] += (seq >> (i + 9)) & 0x01; + } + + for (i = 0; i < 7; i++) { + grid[(((size - 7) + i) * size) + 8] += (seq >> (i + 8)) & 0x01; + } + + grid[(7 * size) + 8] += (seq >> 6) & 0x01; + grid[(8 * size) + 8] += (seq >> 7) & 0x01; + grid[(8 * size) + 7] += (seq >> 8) & 0x01; + } + + private void add_version_info(int size, int version) { + /* Add version information */ + int i; + + long version_data = qr_annex_d[version - 7]; + for (i = 0; i < 6; i++) { + grid[((size - 11) * size) + i] += (version_data >> (i * 3)) & 0x01; + grid[((size - 10) * size) + i] += (version_data >> ((i * 3) + 1)) & 0x01; + grid[((size - 9) * size) + i] += (version_data >> ((i * 3) + 2)) & 0x01; + grid[(i * size) + (size - 11)] += (version_data >> (i * 3)) & 0x01; + grid[(i * size) + (size - 10)] += (version_data >> ((i * 3) + 1)) & 0x01; + grid[(i * size) + (size - 9)] += (version_data >> ((i * 3) + 2)) & 0x01; + } + } + + private enum qrMode { + + NULL, KANJI, BINARY, ALPHANUM, NUMERIC + } + + public enum EccMode { + + L, M, Q, H + } +} diff --git a/barcode/src/main/java/org/xbib/graphics/barcode/RoyalMail4State.java b/barcode/src/main/java/org/xbib/graphics/barcode/RoyalMail4State.java new file mode 100755 index 0000000..9fddf90 --- /dev/null +++ b/barcode/src/main/java/org/xbib/graphics/barcode/RoyalMail4State.java @@ -0,0 +1,112 @@ +package org.xbib.graphics.barcode; + +import java.awt.geom.Rectangle2D; +import java.util.Locale; + +/** + * Encodes data according to the Royal Mail 4-State Country Code + * Data input can consist of numbers 0-9 and letters A-Z and usually includes + * delivery postcode followed by house number. A check digit is calculated + * and added. + */ +public class RoyalMail4State extends Symbol { + /* Handles the 4 State barcodes used in the UK by Royal Mail */ + + private String[] RoyalTable = { + "TTFF", "TDAF", "TDFA", "DTAF", "DTFA", "DDAA", "TADF", "TFTF", "TFDA", + "DATF", "DADA", "DFTA", "TAFD", "TFAD", "TFFT", "DAAD", "DAFT", "DFAT", + "ATDF", "ADTF", "ADDA", "FTTF", "FTDA", "FDTA", "ATFD", "ADAD", "ADFT", + "FTAD", "FTFT", "FDAT", "AADD", "AFTD", "AFDT", "FATD", "FADT", "FFTT" + }; + + private char[] krSet = { + '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D', + 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R', + 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z' + }; + + @Override + public boolean encode() { + StringBuilder dest; + int i, top = 0, bottom = 0; + int row, column; + int index; + + content = content.toUpperCase(Locale.ENGLISH); + if (!(content.matches("[0-9A-Z]+"))) { + errorMsg.append("Invalid characters in data"); + return false; + } + dest = new StringBuilder("A"); + + for (i = 0; i < content.length(); i++) { + index = positionOf(content.charAt(i), krSet); + dest.append(RoyalTable[index]); + top += (index + 1) % 6; + bottom += ((index / 6) + 1) % 6; + } + + /* calculate check digit */ + row = (top % 6) - 1; + column = (bottom % 6) - 1; + if (row == -1) { + row = 5; + } + if (column == -1) { + column = 5; + } + + dest.append(RoyalTable[(6 * row) + column]); + + encodeInfo.append("Check Digit: ").append((6 * row) + column).append("\n"); + + /* Stop character */ + dest.append("F"); + + encodeInfo.append("Encoding: ").append(dest).append("\n"); + readable = new StringBuilder(); + pattern = new String[1]; + pattern[0] = dest.toString(); + rowCount = 1; + rowHeight = new int[1]; + rowHeight[0] = -1; + plotSymbol(); + return true; + } + + @Override + protected void plotSymbol() { + int xBlock; + int x, y, w, h; + getRectangles().clear(); + x = 0; + w = 1; + y = 0; + h = 0; + for (xBlock = 0; xBlock < pattern[0].length(); xBlock++) { + switch (pattern[0].charAt(xBlock)) { + case 'A': + y = 0; + h = 5; + break; + case 'D': + y = 3; + h = 5; + break; + case 'F': + y = 0; + h = 8; + break; + case 'T': + y = 3; + h = 2; + break; + } + Rectangle2D.Double rect = new Rectangle2D.Double(x, y, w, h); + getRectangles().add(rect); + x += 2; + } + symbolWidth = pattern[0].length() * 3; + symbolHeight = 8; + } +} diff --git a/barcode/src/main/java/org/xbib/graphics/barcode/Symbol.java b/barcode/src/main/java/org/xbib/graphics/barcode/Symbol.java new file mode 100755 index 0000000..77b321b --- /dev/null +++ b/barcode/src/main/java/org/xbib/graphics/barcode/Symbol.java @@ -0,0 +1,1058 @@ +package org.xbib.graphics.barcode; + +import static org.xbib.graphics.barcode.HumanReadableLocation.BOTTOM; +import static org.xbib.graphics.barcode.HumanReadableLocation.NONE; +import static org.xbib.graphics.barcode.HumanReadableLocation.TOP; +import org.xbib.graphics.barcode.util.Hexagon; +import org.xbib.graphics.barcode.util.TextBox; +import java.awt.geom.Ellipse2D; +import java.awt.geom.Rectangle2D; +import java.io.UnsupportedEncodingException; +import java.util.ArrayList; +import java.util.List; + +/** + * Generic barcode symbology class. + * TODO: Setting attributes like module width, font size, etc should probably throw + * an exception if set *after* encoding has already been completed. + */ +public abstract class Symbol { + + private final List rectangles = new ArrayList<>(); + + private final List texts = new ArrayList<>(); + + private final List hexagons = new ArrayList<>(); + + private final List target = new ArrayList<>(); + + protected String content; + + protected StringBuilder readable; + + protected String[] pattern; + + protected int rowCount = 0; + + protected int[] rowHeight; + + protected StringBuilder errorMsg = new StringBuilder(); + + protected int symbolHeight = 0; + + protected int symbolWidth = 0; + + protected int defaultHeight = 40; + + private HumanReadableLocation humanReadableLocation = BOTTOM; + + protected StringBuilder encodeInfo = new StringBuilder(); + + protected byte[] inputBytes; + + protected DataType inputDataType = DataType.ECI; + + int moduleWidth = 1; + + double fontSize = 8; + + boolean readerInit; + + int eciMode = 3; + + private int quietZoneHorizontal = 0; + + private int quietZoneVertical = 0; + + private String fontName = "Helvetica"; + + public Symbol() { + unsetReaderInit(); + } + + public List getRectangles() { + return rectangles; + } + + public List getTexts() { + return texts; + } + + public List getHexagons() { + return hexagons; + } + + public List getTarget() { + return target; + } + + /** + * Inserts the specified array into the specified original array at the specified index. + * + * @param original the original array into which we want to insert another array + * @param index the index at which we want to insert the array + * @param inserted the array that we want to insert + * @return the combined array + */ + static int[] insert(int[] original, int index, int[] inserted) { + int[] modified = new int[original.length + inserted.length]; + System.arraycopy(original, 0, modified, 0, index); + System.arraycopy(inserted, 0, modified, index, inserted.length); + System.arraycopy(original, index, modified, index + inserted.length, modified.length - index - inserted.length); + return modified; + } + + /** + * Returns true if the specified array contains the specified value. + * + * @param values the array to check in + * @param value the value to check for + * @return true if the specified array contains the specified value + */ + static boolean contains(int[] values, int value) { + for (int value1 : values) { + if (value1 == value) { + return true; + } + } + return false; + } + + private static boolean roughlyEqual(double d1, double d2) { + return Math.abs(d1 - d2) < 0.0001; + } + + /** + * Sets the type of input data. This setting influences what + * pre-processing is done on data before encoding in the symbol. + * For example: for GS1 mode the AI data will be used to + * calculate the position of 'FNC1' characters. + * Valid values are: + *

+ * + * @param dataType A DataType value which specifies the type of data + */ + public void setDataType(DataType dataType) { + inputDataType = dataType; + } + + /** + * Prefixes symbol data with a "Reader Initialisation" or "Reader + * Programming" instruction. + */ + public final void setReaderInit() { + readerInit = true; + } + + /** + * Removes "Reader Initialisation" or "Reader Programming" instruction + * from symbol data. + */ + private void unsetReaderInit() { + readerInit = false; + } + + /** + * Returns the default bar height for this symbol. + * + * @return the default bar height for this symbol + */ + public int getBarHeight() { + return defaultHeight; + } + + /** + * Sets the default bar height for this symbol (default value is 40). + * + * @param barHeight the default bar height for this symbol + */ + public void setBarHeight(int barHeight) { + this.defaultHeight = barHeight; + } + + /** + * Returns the module width for this symbol. + * + * @return the module width for this symbol + */ + public int getModuleWidth() { + return moduleWidth; + } + + /** + * Sets the module width for this symbol (default value is 1). + * + * @param moduleWidth the module width for this symbol + */ + public void setModuleWidth(int moduleWidth) { + this.moduleWidth = moduleWidth; + } + + /** + * Returns the horizontal quiet zone (white space) added to the left and to the right of this symbol. + * + * @return the horizontal quiet zone (white space) added to the left and to the right of this symbol + */ + public int getQuietZoneHorizontal() { + return quietZoneHorizontal; + } + + /** + * Sets the horizontal quiet zone (white space) added to the left and to the right of this symbol. + * + * @param quietZoneHorizontal the horizontal quiet zone (white space) added to the left and to the right of this symbol + */ + public void setQuietZoneHorizontal(int quietZoneHorizontal) { + this.quietZoneHorizontal = quietZoneHorizontal; + } + + /** + * Returns the vertical quiet zone (white space) added above and below this symbol. + * + * @return the vertical quiet zone (white space) added above and below this symbol + */ + public int getQuietZoneVertical() { + return quietZoneVertical; + } + + /** + * Sets the vertical quiet zone (white space) added above and below this symbol. + * + * @param quietZoneVertical the vertical quiet zone (white space) added above and below this symbol + */ + public void setQuietZoneVertical(int quietZoneVertical) { + this.quietZoneVertical = quietZoneVertical; + } + + /** + * Returns the name of the font to use to render the human-readable text. + * + * @return the name of the font to use to render the human-readable text + */ + public String getFontName() { + return fontName; + } + + /** + * Sets the name of the font to use to render the human-readable text (default value is Helvetica). + * + * @param fontName the name of the font to use to render the human-readable text + */ + public void setFontName(String fontName) { + this.fontName = fontName; + } + + /** + * Returns the size of the font to use to render the human-readable text. + * + * @return the size of the font to use to render the human-readable text + */ + public double getFontSize() { + return fontSize; + } + + /** + * Sets the size of the font to use to render the human-readable text (default value is 8). + * + * @param fontSize the size of the font to use to render the human-readable text + */ + public void setFontSize(double fontSize) { + this.fontSize = fontSize; + } + + /** + * Gets the width of the encoded symbol, including the horizontal quiet zone. + * + * @return the width of the encoded symbol + */ + public int getWidth() { + return symbolWidth + (2 * quietZoneHorizontal); + } + + /** + * Returns the height of the symbol, including the human-readable text, if any, as well as the vertical + * quiet zone. This height is an approximation, since it is calculated without access to a font engine. + * + * @return the height of the symbol, including the human-readable text, if any, as well as the vertical + * quiet zone + */ + public int getHeight() { + return symbolHeight + getHumanReadableHeight() + (2 * quietZoneVertical); + } + + /** + * Returns the height of the human-readable text, including the space between the text and other symbols. + * This height is an approximation, since it is calculated without access to a font engine. + * + * @return the height of the human-readable text + */ + public int getHumanReadableHeight() { + if (texts.isEmpty()) { + return 0; + } else { + return getTheoreticalHumanReadableHeight(); + } + } + + /** + * Returns the height of the human-readable text, assuming this symbol had human-readable text. + * + * @return the height of the human-readable text, assuming this symbol had human-readable text + */ + int getTheoreticalHumanReadableHeight() { + return (int) Math.ceil(fontSize * 1.2); // 0.2 space between bars and text + } + + /** + * Returns a human readable summary of the decisions made by the encoder when creating a symbol. + * + * @return a human readable summary of the decisions made by the encoder when creating a symbol + */ + public String getEncodeInfo() { + return encodeInfo.toString(); + } + + /** + * Returns the location of the human-readable text. + * + * @return the location of the human-readable text + */ + public HumanReadableLocation getHumanReadableLocation() { + return humanReadableLocation; + } + + /** + * Sets the location of the human-readable text (default value is {@link HumanReadableLocation#BOTTOM}). + * + * @param humanReadableLocation the location of the human-readable text + */ + public void setHumanReadableLocation(HumanReadableLocation humanReadableLocation) { + this.humanReadableLocation = humanReadableLocation; + } + + protected int positionOf(char thischar, char[] LookUp) { + int i, outval = 0; + + for (i = 0; i < LookUp.length; i++) { + if (thischar == LookUp[i]) { + outval = i; + } + } + return outval; + } + + protected String bin2pat(String bin) { + boolean black; + int i, l; + StringBuilder pat = new StringBuilder(); + + black = true; + l = 0; + for (i = 0; i < bin.length(); i++) { + if (black) { + if (bin.charAt(i) == '1') { + l++; + } else { + black = false; + pat.append((char) (l + '0')); + l = 1; + } + } else { + if (bin.charAt(i) == '0') { + l++; + } else { + black = true; + pat.append((char) (l + '0')); + l = 1; + } + } + } + pat.append((char) (l + '0')); + + return pat.toString(); + } + + public String getContent() { + return content; + } + + /** + * Set the data to be encoded. Input data will be assumed to be of + * the type set by setDataType. + * + * @param inputData A String containing the data to encode + */ + public void setContent(String inputData) { + int i; + content = inputData; + if (inputDataType == DataType.GS1) { + content = gs1SanityCheck(inputData); + } + if (inputDataType == DataType.GS1) { + readable = new StringBuilder(); + for (i = 0; i < inputData.length(); i++) { + switch (inputData.charAt(i)) { + case '[': + readable.append('('); + break; + case ']': + readable.append(')'); + break; + default: + readable.append(inputData.charAt(i)); + break; + } + } + } + if (inputDataType == DataType.HIBC) { + content = hibcProcess(inputData); + } + if (!content.isEmpty()) { + if (!encode()) { + throw new IllegalStateException(errorMsg.toString()); + } + } else { + throw new IllegalStateException("No input data"); + } + } + + void eciProcess() { + int qmarksBefore, qmarksAfter; + int i; + + qmarksBefore = 0; + for (i = 0; i < content.length(); i++) { + if (content.charAt(i) == '?') { + qmarksBefore++; + } + } + + qmarksAfter = eciEncode("ISO8859_1"); + if (qmarksAfter == qmarksBefore) { + eciMode = 3; + encodeInfo.append("Encoding in ISO 8859-1 character set\n"); + return; + } + + qmarksAfter = eciEncode("ISO8859_2"); + if (qmarksAfter == qmarksBefore) { + eciMode = 4; + encodeInfo.append("Encoding in ISO 8859-2 character set\n"); + return; + } + + qmarksAfter = eciEncode("ISO8859_3"); + if (qmarksAfter == qmarksBefore) { + eciMode = 5; + encodeInfo.append("Encoding in ISO 8859-3 character set\n"); + return; + } + + qmarksAfter = eciEncode("ISO8859_4"); + if (qmarksAfter == qmarksBefore) { + eciMode = 6; + encodeInfo.append("Encoding in ISO 8859-4 character set\n"); + return; + } + + qmarksAfter = eciEncode("ISO8859_5"); + if (qmarksAfter == qmarksBefore) { + eciMode = 7; + encodeInfo.append("Encoding in ISO 8859-5 character set\n"); + return; + } + + qmarksAfter = eciEncode("ISO8859_6"); + if (qmarksAfter == qmarksBefore) { + eciMode = 8; + encodeInfo.append("Encoding in ISO 8859-6 character set\n"); + return; + } + + qmarksAfter = eciEncode("ISO8859_7"); + if (qmarksAfter == qmarksBefore) { + eciMode = 9; + encodeInfo.append("Encoding in ISO 8859-7 character set\n"); + return; + } + + qmarksAfter = eciEncode("ISO8859_8"); + if (qmarksAfter == qmarksBefore) { + eciMode = 10; + encodeInfo.append("Encoding in ISO 8859-8 character set\n"); + return; + } + + qmarksAfter = eciEncode("ISO8859_9"); + if (qmarksAfter == qmarksBefore) { + eciMode = 11; + encodeInfo.append("Encoding in ISO 8859-9 character set\n"); + return; + } + + qmarksAfter = eciEncode("ISO8859_10"); + if (qmarksAfter == qmarksBefore) { + eciMode = 12; + encodeInfo.append("Encoding in ISO 8859-10 character set\n"); + return; + } + + qmarksAfter = eciEncode("ISO8859_11"); + if (qmarksAfter == qmarksBefore) { + eciMode = 13; + encodeInfo.append("Encoding in ISO 8859-11 character set\n"); + return; + } + + qmarksAfter = eciEncode("ISO8859_13"); + if (qmarksAfter == qmarksBefore) { + eciMode = 15; + encodeInfo.append("Encoding in ISO 8859-13 character set\n"); + return; + } + + qmarksAfter = eciEncode("ISO8859_14"); + if (qmarksAfter == qmarksBefore) { + eciMode = 16; + encodeInfo.append("Encoding in ISO 8859-14 character set\n"); + return; + } + + qmarksAfter = eciEncode("ISO8859_15"); + if (qmarksAfter == qmarksBefore) { + eciMode = 17; + encodeInfo.append("Encoding in ISO 8859-15 character set\n"); + return; + } + + qmarksAfter = eciEncode("ISO8859_16"); + if (qmarksAfter == qmarksBefore) { + eciMode = 18; + encodeInfo.append("Encoding in ISO 8859-16 character set\n"); + return; + } + + qmarksAfter = eciEncode("Windows_1250"); + if (qmarksAfter == qmarksBefore) { + eciMode = 21; + encodeInfo.append("Encoding in Windows-1250 character set\n"); + return; + } + + qmarksAfter = eciEncode("Windows_1251"); + if (qmarksAfter == qmarksBefore) { + eciMode = 22; + encodeInfo.append("Encoding in Windows-1251 character set\n"); + return; + } + + qmarksAfter = eciEncode("Windows_1252"); + if (qmarksAfter == qmarksBefore) { + eciMode = 23; + encodeInfo.append("Encoding in Windows-1252 character set\n"); + return; + } + + qmarksAfter = eciEncode("Windows_1256"); + if (qmarksAfter == qmarksBefore) { + eciMode = 24; + encodeInfo.append("Encoding in Windows-1256 character set\n"); + return; + } + + qmarksAfter = eciEncode("SJIS"); + if (qmarksAfter == qmarksBefore) { + eciMode = 20; + encodeInfo.append("Encoding in Shift-JIS character set\n"); + return; + } + + /* default */ + qmarksAfter = eciEncode("UTF8"); + eciMode = 26; + encodeInfo.append("Encoding in UTF-8 character set\n"); + } + + private int eciEncode(String charset) { + /* getBytes replaces unconverted characters to '?', so count + the number of question marks to find if conversion was sucessful. + */ + int i, qmarksAfter; + + try { + inputBytes = content.getBytes(charset); + } catch (UnsupportedEncodingException e) { + return -1; + } + + qmarksAfter = 0; + for (i = 0; i < inputBytes.length; i++) { + if (inputBytes[i] == '?') { + qmarksAfter++; + } + } + + return qmarksAfter; + } + + abstract boolean encode(); + + protected void plotSymbol() { + int xBlock, yBlock; + double x, y, w, h; + boolean black; + rectangles.clear(); + texts.clear(); + int baseY; + if (humanReadableLocation == TOP) { + baseY = getTheoreticalHumanReadableHeight(); + } else { + baseY = 0; + } + h = 0; + y = baseY; + for (yBlock = 0; yBlock < rowCount; yBlock++) { + black = true; + x = 0; + for (xBlock = 0; xBlock < pattern[yBlock].length(); xBlock++) { + char c = pattern[yBlock].charAt(xBlock); + w = getModuleWidth(c - '0') * moduleWidth; + if (black) { + if (rowHeight[yBlock] == -1) { + h = defaultHeight; + } else { + h = rowHeight[yBlock]; + } + if (w != 0 && h != 0) { + Rectangle2D.Double rect = new Rectangle2D.Double(x, y, w, h); + rectangles.add(rect); + } + if (x + w > symbolWidth) { + symbolWidth = (int) Math.ceil(x + w); + } + } + black = !black; + x += w; + } + if ((y - baseY + h) > symbolHeight) { + symbolHeight = (int) Math.ceil(y - baseY + h); + } + y += h; + } + mergeVerticalBlocks(); + if (humanReadableLocation != NONE && readable.length() > 0) { + double baseline; + if (humanReadableLocation == TOP) { + baseline = fontSize; + } else { + baseline = getHeight() + fontSize; + } + double centerX = getWidth() / 2.0; + texts.add(new TextBox(centerX, baseline, readable.toString())); + } + } + + /** + * Returns the module width to use for the specified original module width, taking into account any module width ratio + * customizations. Intended to be overridden by subclasses that support such module width ratio customization. + * + * @param originalWidth the original module width + * @return the module width to use for the specified original module width + */ + protected double getModuleWidth(int originalWidth) { + return originalWidth; + } + + /** + * Search for rectangles which have the same width and x position, and + * which join together vertically and merge them together to reduce the + * number of rectangles needed to describe a symbol. + */ + void mergeVerticalBlocks() { + for (int i = 0; i < rectangles.size() - 1; i++) { + for (int j = i + 1; j < rectangles.size(); j++) { + Rectangle2D.Double firstRect = rectangles.get(i); + Rectangle2D.Double secondRect = rectangles.get(j); + if (roughlyEqual(firstRect.x, secondRect.x) && roughlyEqual(firstRect.width, secondRect.width)) { + if (roughlyEqual(firstRect.y + firstRect.height, secondRect.y)) { + firstRect.height += secondRect.height; + rectangles.set(i, firstRect); + rectangles.remove(j); + } + } + } + } + } + + private String gs1SanityCheck(String source) { + // Enforce compliance with GS1 General Specification + // http://www.gs1.org/docs/gsmp/barcodes/GS1_General_Specifications.pdf + + StringBuilder reduced = new StringBuilder(); + + int i, j, lastAi; + boolean aiLatch; + int bracketLevel, maxBracketLevel, aiLength, maxAiLength, minAiLength; + int[] aiValue = new int[100]; + int[] aiLocation = new int[100]; + int aiCount; + int[] dataLocation = new int[100]; + int[] dataLength = new int[100]; + int srcLen = source.length(); + int errorLatch; + + /* Detect extended ASCII characters */ + for (i = 0; i < srcLen; i++) { + if (source.charAt(i) >= 128) { + errorMsg.append("Extended ASCII characters are not supported by GS1"); + return ""; + } + if (source.charAt(i) < 32) { + errorMsg.append("Control characters are not supported by GS1"); + return ""; + } + } + + if (source.charAt(0) != '[') { + errorMsg.append("Data does not start with an AI"); + return ""; + } + + /* Check the position of the brackets */ + bracketLevel = 0; + maxBracketLevel = 0; + aiLength = 0; + maxAiLength = 0; + minAiLength = 5; + j = 0; + aiLatch = false; + for (i = 0; i < srcLen; i++) { + aiLength += j; + if (((j == 1) && (source.charAt(i) != ']')) + && ((source.charAt(i) < '0') || (source.charAt(i) > '9'))) { + aiLatch = true; + } + if (source.charAt(i) == '[') { + bracketLevel++; + j = 1; + } + if (source.charAt(i) == ']') { + bracketLevel--; + if (aiLength < minAiLength) { + minAiLength = aiLength; + } + j = 0; + aiLength = 0; + } + if (bracketLevel > maxBracketLevel) { + maxBracketLevel = bracketLevel; + } + if (aiLength > maxAiLength) { + maxAiLength = aiLength; + } + } + minAiLength--; + + if (bracketLevel != 0) { + /* Not all brackets are closed */ + errorMsg.append("Malformed AI in input data (brackets don't match)"); + return ""; + } + + if (maxBracketLevel > 1) { + /* Nested brackets */ + errorMsg.append("Found nested brackets in input data"); + return ""; + } + + if (maxAiLength > 4) { + /* AI is too long */ + errorMsg.append("Invalid AI in input data (AI too long)"); + return ""; + } + + if (minAiLength <= 1) { + /* AI is too short */ + errorMsg.append("Invalid AI in input data (AI too short)"); + return ""; + } + + if (aiLatch) { + /* Non-numeric data in AI */ + errorMsg.append("Invalid AI in input data (non-numeric characters in AI)"); + return ""; + } + + aiCount = 0; + for (i = 1; i < srcLen; i++) { + if (source.charAt(i - 1) == '[') { + aiLocation[aiCount] = i; + aiValue[aiCount] = 0; + for (j = 0; source.charAt(i + j) != ']'; j++) { + aiValue[aiCount] *= 10; + aiValue[aiCount] += Character.getNumericValue(source.charAt(i + j)); + } + aiCount++; + } + } + + for (i = 0; i < aiCount; i++) { + dataLocation[i] = aiLocation[i] + 3; + if (aiValue[i] >= 100) { + dataLocation[i]++; + } + if (aiValue[i] >= 1000) { + dataLocation[i]++; + } + dataLength[i] = source.length() - dataLocation[i]; + for (j = source.length() - 1; j >= dataLocation[i]; j--) { + if (source.charAt(j) == '[') { + dataLength[i] = j - dataLocation[i]; + } + } + } + + for (i = 0; i < aiCount; i++) { + if (dataLength[i] == 0) { + /* No data for given AI */ + errorMsg.append("Empty data field in input data"); + return ""; + } + } + + errorLatch = 0; + for (i = 0; i < aiCount; i++) { + switch (aiValue[i]) { + case 0: + if (dataLength[i] != 18) { + errorLatch = 1; + } + break; + case 1: + case 2: + case 3: + if (dataLength[i] != 14) { + errorLatch = 1; + } + break; + case 4: + if (dataLength[i] != 16) { + errorLatch = 1; + } + break; + case 11: + case 12: + case 13: + case 14: + case 15: + case 16: + case 17: + case 18: + case 19: + if (dataLength[i] != 6) { + errorLatch = 1; + } + break; + case 20: + if (dataLength[i] != 2) { + errorLatch = 1; + } + break; + case 23: + case 24: + case 25: + case 39: + case 40: + case 41: + case 42: + case 70: + case 80: + case 81: + errorLatch = 2; + break; + } + if ( + ((aiValue[i] >= 100) && (aiValue[i] <= 179)) + || ((aiValue[i] >= 1000) && (aiValue[i] <= 1799)) + || ((aiValue[i] >= 200) && (aiValue[i] <= 229)) + || ((aiValue[i] >= 2000) && (aiValue[i] <= 2299)) + || ((aiValue[i] >= 300) && (aiValue[i] <= 309)) + || ((aiValue[i] >= 3000) && (aiValue[i] <= 3099)) + || ((aiValue[i] >= 31) && (aiValue[i] <= 36)) + || ((aiValue[i] >= 310) && (aiValue[i] <= 369))) { + errorLatch = 2; + } + if ((aiValue[i] >= 3100) && (aiValue[i] <= 3699) && dataLength[i] != 6) { + errorLatch = 1; + } + if ( + ((aiValue[i] >= 370) && (aiValue[i] <= 379)) + || ((aiValue[i] >= 3700) && (aiValue[i] <= 3799))) { + errorLatch = 2; + } + if ((aiValue[i] >= 410) && (aiValue[i] <= 415)) { + if (dataLength[i] != 13) { + errorLatch = 1; + } + } + if ( + ((aiValue[i] >= 4100) && (aiValue[i] <= 4199)) + || ((aiValue[i] >= 700) && (aiValue[i] <= 703)) + || ((aiValue[i] >= 800) && (aiValue[i] <= 810)) + || ((aiValue[i] >= 900) && (aiValue[i] <= 999)) + || ((aiValue[i] >= 9000) && (aiValue[i] <= 9999))) { + errorLatch = 2; + } + + if (errorLatch == 1) { + errorMsg = new StringBuilder("Invalid data length for AI"); + return ""; + } + + if (errorLatch == 2) { + errorMsg = new StringBuilder("Invalid AI value"); + return ""; + } + } + + /* Resolve AI data - put resulting string in 'reduced' */ + aiLatch = false; + for (i = 0; i < srcLen; i++) { + if ((source.charAt(i) != '[') && (source.charAt(i) != ']')) { + reduced.append(source.charAt(i)); + } + if (source.charAt(i) == '[') { + /* Start of an AI string */ + if (aiLatch) { + reduced.append('['); + } + lastAi = (10 * Character.getNumericValue(source.charAt(i + 1))) + + Character.getNumericValue(source.charAt(i + 2)); + aiLatch = ((lastAi < 0) || (lastAi > 4)) + && ((lastAi < 11) || (lastAi > 20)) + && (lastAi != 23) /* legacy support - see 5.3.8.2.2 */ + && ((lastAi < 31) || (lastAi > 36)) + && (lastAi != 41); + } + /* The ']' character is simply dropped from the input */ + } + + /* the character '[' in the reduced string refers to the FNC1 character */ + return reduced.toString(); + } + + private String hibcProcess(String source) { + char[] hibcCharTable = { + '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', + 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', + 'K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T', + 'U', 'V', 'W', 'X', 'Y', 'Z', '-', '.', ' ', '$', + '/', '+', '%'}; + + int counter, i; + String toProcess; + char checkDigit; + + if (source.length() > 36) { + errorMsg = new StringBuilder("Data too long for HIBC LIC"); + return ""; + } + source = source.toUpperCase(); + if (!(source.matches("[A-Z0-9-\\. \\$/+\\%]+?"))) { + errorMsg = new StringBuilder("Invalid characters in input"); + return ""; + } + + counter = 41; + for (i = 0; i < source.length(); i++) { + counter += positionOf(source.charAt(i), hibcCharTable); + } + counter = counter % 43; + + if (counter < 10) { + checkDigit = (char) (counter + '0'); + } else { + if (counter < 36) { + checkDigit = (char) ((counter - 10) + 'A'); + } else { + switch (counter) { + case 36: + checkDigit = '-'; + break; + case 37: + checkDigit = '.'; + break; + case 38: + checkDigit = ' '; + break; + case 39: + checkDigit = '$'; + break; + case 40: + checkDigit = '/'; + break; + case 41: + checkDigit = '+'; + break; + case 42: + checkDigit = '%'; + break; + default: + checkDigit = ' '; + break; /* Keep compiler happy */ + } + } + } + + encodeInfo.append("HIBC Check Digit: ").append(counter).append(" (").append(checkDigit).append(")\n"); + + toProcess = "+" + source + checkDigit; + return toProcess; + } + + /** + * Returns the intermediate coding of this bar code. Symbol types that use the test + * infrastructure should override this method. + * + * @return the intermediate coding of this bar code + */ + protected int[] getCodewords() { + throw new UnsupportedOperationException(); + } + + /** + * Returns this bar code's pattern, converted into a set of corresponding codewords. + * Useful for bar codes that encode their content as a pattern. + * + * @param size the number of digits in each codeword + * @return this bar code's pattern, converted into a set of corresponding codewords + */ + int[] getPatternAsCodewords(int size) { + if (pattern == null || pattern.length == 0) { + return new int[0]; + } else { + int count = (int) Math.ceil(pattern[0].length() / (double) size); + int[] codewords = new int[pattern.length * count]; + for (int i = 0; i < pattern.length; i++) { + String row = pattern[i]; + for (int j = 0; j < count; j++) { + int substringStart = j * size; + int substringEnd = Math.min((j + 1) * size, row.length()); + codewords[(i * count) + j] = Integer.parseInt(row.substring(substringStart, substringEnd)); + } + } + return codewords; + } + } + + public enum DataType { + UTF8, LATIN1, BINARY, GS1, HIBC, ECI + } +} diff --git a/barcode/src/main/java/org/xbib/graphics/barcode/Telepen.java b/barcode/src/main/java/org/xbib/graphics/barcode/Telepen.java new file mode 100755 index 0000000..39f11a1 --- /dev/null +++ b/barcode/src/main/java/org/xbib/graphics/barcode/Telepen.java @@ -0,0 +1,177 @@ +package org.xbib.graphics.barcode; + +/** + * Telepen (also known as Telepen Alpha) can encode ASCII text input and + * includes a modulo-127 check digit. Telepen Numeric allows compression of + * numeric data into a Telepen symbol. Data can consist of pairs of numbers + * or pairs consisting of a numerical digit followed an X character. + * Telepen Numeric also includes a modulo-127 check digit. + */ +public class Telepen extends Symbol { + + public tp_mode mode; + private String[] TeleTable = { + "1111111111111111", "1131313111", "33313111", "1111313131", + "3111313111", "11333131", "13133131", "111111313111", "31333111", + "1131113131", "33113131", "1111333111", "3111113131", "1113133111", + "1311133111", "111111113131", "3131113111", "11313331", "333331", + "111131113111", "31113331", "1133113111", "1313113111", "1111113331", + "31131331", "113111113111", "3311113111", "1111131331", "311111113111", + "1113111331", "1311111331", "11111111113111", "31313311", "1131311131", + "33311131", "1111313311", "3111311131", "11333311", "13133311", + "111111311131", "31331131", "1131113311", "33113311", "1111331131", + "3111113311", "1113131131", "1311131131", "111111113311", "3131111131", + "1131131311", "33131311", "111131111131", "3111131311", "1133111131", + "1313111131", "111111131311", "3113111311", "113111111131", + "3311111131", "111113111311", "311111111131", "111311111311", + "131111111311", "11111111111131", "3131311111", "11313133", "333133", + "111131311111", "31113133", "1133311111", "1313311111", "1111113133", + "313333", "113111311111", "3311311111", "11113333", "311111311111", + "11131333", "13111333", "11111111311111", "31311133", "1131331111", + "33331111", "1111311133", "3111331111", "11331133", "13131133", + "111111331111", "3113131111", "1131111133", "33111133", "111113131111", + "3111111133", "111311131111", "131111131111", "111111111133", + "31311313", "113131111111", "3331111111", "1111311313", "311131111111", + "11331313", "13131313", "11111131111111", "3133111111", "1131111313", + "33111313", "111133111111", "3111111313", "111313111111", + "131113111111", "111111111313", "313111111111", "1131131113", + "33131113", "11113111111111", "3111131113", "113311111111", + "131311111111", "111111131113", "3113111113", "11311111111111", + "331111111111", "111113111113", "31111111111111", "111311111113", + "131111111113" + }; + + public Telepen() { + mode = tp_mode.NORMAL; + } + + public void setNormalMode() { + mode = tp_mode.NORMAL; + } + + public void setNumericMode() { + mode = tp_mode.NUMERIC; + } + + @Override + public boolean encode() { + if (mode == tp_mode.NORMAL) { + return normal_mode(); + } else { + return numeric_mode(); + } + } + + private boolean normal_mode() { + int count = 0, asciicode, check_digit; + StringBuilder p = new StringBuilder(); + String dest; + + int l = content.length(); + + if (!content.matches("[\u0000-\u007F]+")) { + errorMsg.append("Invalid characters in input data"); + return false; + } + + dest = TeleTable['_']; // Start + for (int i = 0; i < l; i++) { + asciicode = content.charAt(i); + p.append(TeleTable[asciicode]); + count += asciicode; + } + + check_digit = 127 - (count % 127); + if (check_digit == 127) { + check_digit = 0; + } + + p.append(TeleTable[check_digit]); + + encodeInfo.append("Check Digit: ").append(check_digit).append("\n"); + + dest += p; + dest += TeleTable['z']; // Stop + + readable = new StringBuilder(content); + pattern = new String[1]; + pattern[0] = dest; + rowCount = 1; + rowHeight = new int[1]; + rowHeight[0] = -1; + plotSymbol(); + return true; + } + + public boolean numeric_mode() { + int count = 0, check_digit; + StringBuilder p = new StringBuilder(); + String t; + String dest; + int l = content.length(); + int tl, glyph; + char c1, c2; + + //FIXME: Ensure no extended ASCII or Unicode charcters are entered + if (!(content.matches("[0-9X]+"))) { + errorMsg.append("Invalid characters in input"); + return false; + } + + /* If input is an odd length, add a leading zero */ + if ((l & 1) == 1) { + t = "0" + content; + tl = l + 1; + } else { + t = content; + tl = l; + } + + dest = TeleTable['_']; // Start + for (int i = 0; i < tl; i += 2) { + + c1 = t.charAt(i); + c2 = t.charAt(i + 1); + + /* Input nX is allowed, but Xn is not */ + if (c1 == 'X') { + errorMsg.append("Invalid position of X in data"); + return false; + } + + if (c2 == 'X') { + glyph = (c1 - '0') + 17; + count += glyph; + } else { + glyph = ((10 * (c1 - '0')) + (c2 - '0')) + 27; + count += glyph; + } + + p.append(TeleTable[glyph]); + } + + check_digit = 127 - (count % 127); + if (check_digit == 127) { + check_digit = 0; + } + + p.append(TeleTable[check_digit]); + + encodeInfo.append("Check Digit: ").append(check_digit).append("\n"); + + dest += p; + dest += TeleTable['z']; // Stop + readable = new StringBuilder(content); + pattern = new String[1]; + pattern[0] = dest; + rowCount = 1; + rowHeight = new int[1]; + rowHeight[0] = -1; + plotSymbol(); + return true; + } + + public enum tp_mode { + NORMAL, NUMERIC + } +} diff --git a/barcode/src/main/java/org/xbib/graphics/barcode/Upc.java b/barcode/src/main/java/org/xbib/graphics/barcode/Upc.java new file mode 100755 index 0000000..43ca5dc --- /dev/null +++ b/barcode/src/main/java/org/xbib/graphics/barcode/Upc.java @@ -0,0 +1,445 @@ +package org.xbib.graphics.barcode; + +import static org.xbib.graphics.barcode.HumanReadableLocation.NONE; +import static org.xbib.graphics.barcode.HumanReadableLocation.TOP; +import org.xbib.graphics.barcode.util.AddOn; +import org.xbib.graphics.barcode.util.TextBox; +import java.awt.geom.Rectangle2D; + +/** + * Implements UPC bar code symbology. + * According to BS EN 797:1996 + * UPC-A requires an 11 digit article number. The check digit is calculated. + * UPC-E is a zero-compressed version of UPC-A developed for smaller packages. + * The code requires a 6 digit article number (digits 0-9). The check digit + * is calculated. Also supports Number System 1 encoding by entering a 7-digit + * article number stating with the digit 1. In addition EAN-2 and EAN-5 add-on + * symbols can be added using the + character followed by the add-on data. + * + * @author Robert Elliott + */ +public class Upc extends Symbol { + + private boolean useAddOn; + + ; + private String addOnContent; + private Mode mode; + private boolean linkageFlag; + private String[] setAC = { + "3211", "2221", "2122", "1411", "1132", "1231", "1114", "1312", + "1213", "3112" + }; + private String[] setB = { + "1123", "1222", "2212", "1141", "2311", "1321", "4111", "2131", + "3121", "2113" + }; + private String[] UPCParity0 = { + "BBBAAA", "BBABAA", "BBAABA", "BBAAAB", "BABBAA", "BAABBA", "BAAABB", + "BABABA", "BABAAB", "BAABAB" + }; /* Number set for UPC-E symbol (EN Table 4) */ + private String[] UPCParity1 = { + "AAABBB", "AABABB", "AABBAB", "AABBBA", "ABAABB", "ABBAAB", "ABBBAA", + "ABABAB", "ABABBA", "ABBABA" + }; /* Not covered by BS EN 797 */ + + public Upc() { + mode = Mode.UPCA; + linkageFlag = false; + } + + public Mode getMode() { + return mode; + } + + public void setMode(Mode mode) { + this.mode = mode; + } + + public void setLinkageFlag() { + linkageFlag = true; + } + + public void unsetLinkageFlag() { + linkageFlag = false; + } + + @Override + public void setHumanReadableLocation(HumanReadableLocation humanReadableLocation) { + if (humanReadableLocation == TOP) { + throw new IllegalArgumentException("Cannot display human-readable text above UPC bar codes."); + } else { + super.setHumanReadableLocation(humanReadableLocation); + } + } + + @Override + public boolean encode() { + boolean retval; + AddOn addOn = new AddOn(); + String addOnData = ""; + + separateContent(); + if (content.length() == 0) { + errorMsg.append("Missing UPC data"); + retval = false; + } else { + if (mode == Mode.UPCA) { + retval = upca(); + } else { + retval = upce(); + } + } + + if (useAddOn) { + addOnData = addOn.calcAddOn(addOnContent); + if (addOnData.length() == 0) { + errorMsg.append("Invalid Add-On data"); + retval = false; + } else { + pattern[0] = pattern[0] + "9" + addOnData; + + //add leading zeroes to add-on text + if (addOnContent.length() == 1) { + addOnContent = "0" + addOnContent; + } + if (addOnContent.length() == 3) { + addOnContent = "0" + addOnContent; + } + if (addOnContent.length() == 4) { + addOnContent = "0" + addOnContent; + } + } + } + + if (retval) { + plotSymbol(); + } + return retval; + } + + private void separateContent() { + int splitPoint; + + splitPoint = content.indexOf('+'); + if (splitPoint != -1) { + // There is a '+' in the input data, use an add-on EAN2 or EAN5 + useAddOn = true; + addOnContent = content.substring(splitPoint + 1); + content = content.substring(0, splitPoint); + } + } + + private boolean upca() { + StringBuilder accumulator; + StringBuilder dest; + int i; + char check; + + if (!(content.matches("[0-9]+"))) { + errorMsg.append("Invalid characters in input"); + return false; + } + + if (content.length() > 11) { + errorMsg.append("Input data too long"); + return false; + } + + accumulator = new StringBuilder(); + for (i = content.length(); i < 11; i++) { + accumulator.append("0"); + } + accumulator.append(content); + check = calcDigit(accumulator.toString()); + accumulator.append(check); + dest = new StringBuilder("111"); + for (i = 0; i < 12; i++) { + if (i == 6) { + dest.append("11111"); + } + dest.append(setAC[Character.getNumericValue(accumulator.charAt(i))]); + } + dest.append("111"); + + encodeInfo.append("Check Digit: ").append(check).append("\n"); + + readable = accumulator; + pattern = new String[1]; + pattern[0] = dest.toString(); + rowCount = 1; + rowHeight = new int[1]; + rowHeight[0] = -1; + return true; + } + + private boolean upce() { + int i, num_system; + char emode, check; + StringBuilder source; + String parity; + StringBuilder dest; + char[] equivalent = new char[12]; + StringBuilder equiv = new StringBuilder(); + + if (!(content.matches("[0-9]+"))) { + errorMsg.append("Invalid characters in input"); + return false; + } + + if (content.length() > 7) { + errorMsg.append("Input data too long"); + return false; + } + + source = new StringBuilder(); + for (i = content.length(); i < 7; i++) { + source.append("0"); + } + source.append(content); + + /* Two number systems can be used - system 0 and system 1 */ + switch (source.charAt(0)) { + case '0': + num_system = 0; + break; + case '1': + num_system = 1; + break; + default: + errorMsg.append("Invalid input data"); + return false; + } + + /* Expand the zero-compressed UPCE code to make a UPCA equivalent (EN Table 5) */ + emode = source.charAt(6); + for (i = 0; i < 11; i++) { + equivalent[i] = '0'; + } + equivalent[0] = source.charAt(0); + equivalent[1] = source.charAt(1); + equivalent[2] = source.charAt(2); + + switch (emode) { + case '0': + case '1': + case '2': + equivalent[3] = emode; + equivalent[8] = source.charAt(3); + equivalent[9] = source.charAt(4); + equivalent[10] = source.charAt(5); + break; + case '3': + equivalent[3] = source.charAt(3); + equivalent[9] = source.charAt(4); + equivalent[10] = source.charAt(5); + if (((source.charAt(3) == '0') || (source.charAt(3) == '1')) + || (source.charAt(3) == '2')) { + /* Note 1 - "X3 shall not be equal to 0, 1 or 2" */ + errorMsg.append("Invalid UPC-E data"); + return false; + } + break; + case '4': + equivalent[3] = source.charAt(3); + equivalent[4] = source.charAt(4); + equivalent[10] = source.charAt(5); + if (source.charAt(4) == '0') { + /* Note 2 - "X4 shall not be equal to 0" */ + errorMsg.append("Invalid UPC-E data"); + return false; + } + break; + case '5': + case '6': + case '7': + case '8': + case '9': + equivalent[3] = source.charAt(3); + equivalent[4] = source.charAt(4); + equivalent[5] = source.charAt(5); + equivalent[10] = emode; + if (source.charAt(5) == '0') { + /* Note 3 - "X5 shall not be equal to 0" */ + errorMsg.append("Invalid UPC-E data"); + return false; + } + break; + } + + for (i = 0; i < 11; i++) { + equiv.append(equivalent[i]); + } + + /* Get the check digit from the expanded UPCA code */ + check = calcDigit(equiv.toString()); + + encodeInfo.append("Check Digit: ").append(check).append("\n"); + + /* Use the number system and check digit information to choose a parity scheme */ + if (num_system == 1) { + parity = UPCParity1[check - '0']; + } else { + parity = UPCParity0[check - '0']; + } + + /* Take all this information and make the barcode pattern */ + + /* start character */ + dest = new StringBuilder("111"); + + for (i = 0; i <= 5; i++) { + switch (parity.charAt(i)) { + case 'A': + dest.append(setAC[source.charAt(i + 1) - '0']); + break; + case 'B': + dest.append(setB[source.charAt(i + 1) - '0']); + break; + } + } + + /* stop character */ + dest.append("111111"); + + readable = new StringBuilder(source.toString()).append(check); + pattern = new String[1]; + pattern[0] = dest.toString(); + rowCount = 1; + rowHeight = new int[1]; + rowHeight[0] = -1; + return true; + } + + private char calcDigit(String x) { + int count = 0; + int c, cdigit; + for (int i = 0; i < 11; i++) { + c = Character.getNumericValue(x.charAt(i)); + if ((i % 2) == 0) { + c = c * 3; + } + count = count + c; + } + cdigit = 10 - (count % 10); + if (cdigit == 10) { + cdigit = 0; + } + + return (char) (cdigit + '0'); + } + + @Override + protected void plotSymbol() { + int xBlock; + int x, y, w, h; + boolean black; + int compositeOffset = 0; + int shortLongDiff = 5; + getRectangles().clear(); + getTexts().clear(); + black = true; + x = 0; + if (linkageFlag) { + compositeOffset = 6; + } + for (xBlock = 0; xBlock < pattern[0].length(); xBlock++) { + if (black) { + y = 0; + black = false; + w = pattern[0].charAt(xBlock) - '0'; + h = defaultHeight; + /* Add extension to guide bars */ + if (mode == Mode.UPCA) { + if ((x < 10) || (x > 84)) { + h += shortLongDiff; + } + if ((x > 45) && (x < 49)) { + h += shortLongDiff; + } + if (x > 95) { + // Drop add-on + h -= 8; + y = 8; + } + if (linkageFlag && (x == 0) || (x == 94)) { + h += 2; + y -= 2; + } + } else { + if ((x < 4) || (x > 45)) { + h += shortLongDiff; + } + if (x > 52) { + // Drop add-on + h -= 8; + y = 8; + } + if (linkageFlag && (x == 0) || (x == 50)) { + h += 2; + y -= 2; + } + } + Rectangle2D.Double rect = new Rectangle2D.Double(x + 6, y + compositeOffset, w, h); + getRectangles().add(rect); + if ((x + w + 12) > symbolWidth) { + symbolWidth = x + w + 12; + } + } else { + black = true; + } + x += pattern[0].charAt(xBlock) - '0'; + } + + if (linkageFlag) { + // Add separator for composite symbology + if (mode == Mode.UPCA) { + getRectangles().add(new Rectangle2D.Double(6, 0, 1, 2)); + getRectangles().add(new Rectangle2D.Double(94 + 6, 0, 1, 2)); + getRectangles().add(new Rectangle2D.Double(-1 + 6, 2, 1, 2)); + getRectangles().add(new Rectangle2D.Double(95 + 6, 2, 1, 2)); + } else { // UPCE + getRectangles().add(new Rectangle2D.Double(0 + 6, 0, 1, 2)); + getRectangles().add(new Rectangle2D.Double(50 + 6, 0, 1, 2)); + getRectangles().add(new Rectangle2D.Double(-1 + 6, 2, 1, 2)); + getRectangles().add(new Rectangle2D.Double(51 + 6, 2, 1, 2)); + } + } + + symbolHeight = defaultHeight + 5; + + /* Now add the text */ + if (getHumanReadableLocation() != NONE) { + double baseline = getHeight() + fontSize - shortLongDiff + compositeOffset; + double addOnBaseline = 6.0 + compositeOffset; + if (mode == Mode.UPCA) { + getTexts().add(new TextBox(3, baseline, readable.substring(0, 1))); + getTexts().add(new TextBox(34, baseline, readable.substring(1, 6))); + getTexts().add(new TextBox(73, baseline, readable.substring(6, 11))); + getTexts().add(new TextBox(104, baseline, readable.substring(11, 12))); + if (useAddOn) { + if (addOnContent.length() == 2) { + getTexts().add(new TextBox(118, addOnBaseline, addOnContent)); + } else { + getTexts().add(new TextBox(133, addOnBaseline, addOnContent)); + } + } + } else { // UPCE + getTexts().add(new TextBox(3, baseline, readable.substring(0, 1))); + getTexts().add(new TextBox(30, baseline, readable.substring(1, 7))); + getTexts().add(new TextBox(61, baseline, readable.substring(7, 8))); + if (useAddOn) { + if (addOnContent.length() == 2) { + getTexts().add(new TextBox(75, addOnBaseline, addOnContent)); + } else { + getTexts().add(new TextBox(90, addOnBaseline, addOnContent)); + } + } + } + } + } + + public enum Mode { + UPCA, UPCE + } +} diff --git a/barcode/src/main/java/org/xbib/graphics/barcode/UspsOneCode.java b/barcode/src/main/java/org/xbib/graphics/barcode/UspsOneCode.java new file mode 100755 index 0000000..e3461b8 --- /dev/null +++ b/barcode/src/main/java/org/xbib/graphics/barcode/UspsOneCode.java @@ -0,0 +1,451 @@ +package org.xbib.graphics.barcode; + +import static org.xbib.graphics.barcode.HumanReadableLocation.NONE; +import static org.xbib.graphics.barcode.HumanReadableLocation.TOP; +import org.xbib.graphics.barcode.util.TextBox; +import java.awt.geom.Rectangle2D; +import java.math.BigInteger; + +/** + * USPS OneCode (also known as Intelligent Mail Barcode) + * According to USPS-B-3200F + * Specification at https://ribbs.usps.gov/intelligentmail_mailpieces/documents/tech_guides/SPUSPSG.pdf + * OneCode is a fixed length (65-bar) symbol which combines routing and + * customer information in a single symbol. Input data consists of a 20 digit + * tracking code, followed by a dash (-), followed by a delivery point + * zip-code which can be 0, 5, 9 or 11 digits in length. + */ +public class UspsOneCode extends Symbol { + + /* The following lookup tables were generated using the code in Appendix C */ + + private int[] byte_array = new int[13]; + + private int[] AppxD_I = { /* Appendix D Table 1 - 5 of 13 characters */ + 0x001F, 0x1F00, 0x002F, 0x1E80, 0x0037, 0x1D80, 0x003B, 0x1B80, 0x003D, 0x1780, + 0x003E, 0x0F80, 0x004F, 0x1E40, 0x0057, 0x1D40, 0x005B, 0x1B40, 0x005D, 0x1740, + 0x005E, 0x0F40, 0x0067, 0x1CC0, 0x006B, 0x1AC0, 0x006D, 0x16C0, 0x006E, 0x0EC0, + 0x0073, 0x19C0, 0x0075, 0x15C0, 0x0076, 0x0DC0, 0x0079, 0x13C0, 0x007A, 0x0BC0, + 0x007C, 0x07C0, 0x008F, 0x1E20, 0x0097, 0x1D20, 0x009B, 0x1B20, 0x009D, 0x1720, + 0x009E, 0x0F20, 0x00A7, 0x1CA0, 0x00AB, 0x1AA0, 0x00AD, 0x16A0, 0x00AE, 0x0EA0, + 0x00B3, 0x19A0, 0x00B5, 0x15A0, 0x00B6, 0x0DA0, 0x00B9, 0x13A0, 0x00BA, 0x0BA0, + 0x00BC, 0x07A0, 0x00C7, 0x1C60, 0x00CB, 0x1A60, 0x00CD, 0x1660, 0x00CE, 0x0E60, + 0x00D3, 0x1960, 0x00D5, 0x1560, 0x00D6, 0x0D60, 0x00D9, 0x1360, 0x00DA, 0x0B60, + 0x00DC, 0x0760, 0x00E3, 0x18E0, 0x00E5, 0x14E0, 0x00E6, 0x0CE0, 0x00E9, 0x12E0, + 0x00EA, 0x0AE0, 0x00EC, 0x06E0, 0x00F1, 0x11E0, 0x00F2, 0x09E0, 0x00F4, 0x05E0, + 0x00F8, 0x03E0, 0x010F, 0x1E10, 0x0117, 0x1D10, 0x011B, 0x1B10, 0x011D, 0x1710, + 0x011E, 0x0F10, 0x0127, 0x1C90, 0x012B, 0x1A90, 0x012D, 0x1690, 0x012E, 0x0E90, + 0x0133, 0x1990, 0x0135, 0x1590, 0x0136, 0x0D90, 0x0139, 0x1390, 0x013A, 0x0B90, + 0x013C, 0x0790, 0x0147, 0x1C50, 0x014B, 0x1A50, 0x014D, 0x1650, 0x014E, 0x0E50, + 0x0153, 0x1950, 0x0155, 0x1550, 0x0156, 0x0D50, 0x0159, 0x1350, 0x015A, 0x0B50, + 0x015C, 0x0750, 0x0163, 0x18D0, 0x0165, 0x14D0, 0x0166, 0x0CD0, 0x0169, 0x12D0, + 0x016A, 0x0AD0, 0x016C, 0x06D0, 0x0171, 0x11D0, 0x0172, 0x09D0, 0x0174, 0x05D0, + 0x0178, 0x03D0, 0x0187, 0x1C30, 0x018B, 0x1A30, 0x018D, 0x1630, 0x018E, 0x0E30, + 0x0193, 0x1930, 0x0195, 0x1530, 0x0196, 0x0D30, 0x0199, 0x1330, 0x019A, 0x0B30, + 0x019C, 0x0730, 0x01A3, 0x18B0, 0x01A5, 0x14B0, 0x01A6, 0x0CB0, 0x01A9, 0x12B0, + 0x01AA, 0x0AB0, 0x01AC, 0x06B0, 0x01B1, 0x11B0, 0x01B2, 0x09B0, 0x01B4, 0x05B0, + 0x01B8, 0x03B0, 0x01C3, 0x1870, 0x01C5, 0x1470, 0x01C6, 0x0C70, 0x01C9, 0x1270, + 0x01CA, 0x0A70, 0x01CC, 0x0670, 0x01D1, 0x1170, 0x01D2, 0x0970, 0x01D4, 0x0570, + 0x01D8, 0x0370, 0x01E1, 0x10F0, 0x01E2, 0x08F0, 0x01E4, 0x04F0, 0x01E8, 0x02F0, + 0x020F, 0x1E08, 0x0217, 0x1D08, 0x021B, 0x1B08, 0x021D, 0x1708, 0x021E, 0x0F08, + 0x0227, 0x1C88, 0x022B, 0x1A88, 0x022D, 0x1688, 0x022E, 0x0E88, 0x0233, 0x1988, + 0x0235, 0x1588, 0x0236, 0x0D88, 0x0239, 0x1388, 0x023A, 0x0B88, 0x023C, 0x0788, + 0x0247, 0x1C48, 0x024B, 0x1A48, 0x024D, 0x1648, 0x024E, 0x0E48, 0x0253, 0x1948, + 0x0255, 0x1548, 0x0256, 0x0D48, 0x0259, 0x1348, 0x025A, 0x0B48, 0x025C, 0x0748, + 0x0263, 0x18C8, 0x0265, 0x14C8, 0x0266, 0x0CC8, 0x0269, 0x12C8, 0x026A, 0x0AC8, + 0x026C, 0x06C8, 0x0271, 0x11C8, 0x0272, 0x09C8, 0x0274, 0x05C8, 0x0278, 0x03C8, + 0x0287, 0x1C28, 0x028B, 0x1A28, 0x028D, 0x1628, 0x028E, 0x0E28, 0x0293, 0x1928, + 0x0295, 0x1528, 0x0296, 0x0D28, 0x0299, 0x1328, 0x029A, 0x0B28, 0x029C, 0x0728, + 0x02A3, 0x18A8, 0x02A5, 0x14A8, 0x02A6, 0x0CA8, 0x02A9, 0x12A8, 0x02AA, 0x0AA8, + 0x02AC, 0x06A8, 0x02B1, 0x11A8, 0x02B2, 0x09A8, 0x02B4, 0x05A8, 0x02B8, 0x03A8, + 0x02C3, 0x1868, 0x02C5, 0x1468, 0x02C6, 0x0C68, 0x02C9, 0x1268, 0x02CA, 0x0A68, + 0x02CC, 0x0668, 0x02D1, 0x1168, 0x02D2, 0x0968, 0x02D4, 0x0568, 0x02D8, 0x0368, + 0x02E1, 0x10E8, 0x02E2, 0x08E8, 0x02E4, 0x04E8, 0x0307, 0x1C18, 0x030B, 0x1A18, + 0x030D, 0x1618, 0x030E, 0x0E18, 0x0313, 0x1918, 0x0315, 0x1518, 0x0316, 0x0D18, + 0x0319, 0x1318, 0x031A, 0x0B18, 0x031C, 0x0718, 0x0323, 0x1898, 0x0325, 0x1498, + 0x0326, 0x0C98, 0x0329, 0x1298, 0x032A, 0x0A98, 0x032C, 0x0698, 0x0331, 0x1198, + 0x0332, 0x0998, 0x0334, 0x0598, 0x0338, 0x0398, 0x0343, 0x1858, 0x0345, 0x1458, + 0x0346, 0x0C58, 0x0349, 0x1258, 0x034A, 0x0A58, 0x034C, 0x0658, 0x0351, 0x1158, + 0x0352, 0x0958, 0x0354, 0x0558, 0x0361, 0x10D8, 0x0362, 0x08D8, 0x0364, 0x04D8, + 0x0383, 0x1838, 0x0385, 0x1438, 0x0386, 0x0C38, 0x0389, 0x1238, 0x038A, 0x0A38, + 0x038C, 0x0638, 0x0391, 0x1138, 0x0392, 0x0938, 0x0394, 0x0538, 0x03A1, 0x10B8, + 0x03A2, 0x08B8, 0x03A4, 0x04B8, 0x03C1, 0x1078, 0x03C2, 0x0878, 0x03C4, 0x0478, + 0x040F, 0x1E04, 0x0417, 0x1D04, 0x041B, 0x1B04, 0x041D, 0x1704, 0x041E, 0x0F04, + 0x0427, 0x1C84, 0x042B, 0x1A84, 0x042D, 0x1684, 0x042E, 0x0E84, 0x0433, 0x1984, + 0x0435, 0x1584, 0x0436, 0x0D84, 0x0439, 0x1384, 0x043A, 0x0B84, 0x043C, 0x0784, + 0x0447, 0x1C44, 0x044B, 0x1A44, 0x044D, 0x1644, 0x044E, 0x0E44, 0x0453, 0x1944, + 0x0455, 0x1544, 0x0456, 0x0D44, 0x0459, 0x1344, 0x045A, 0x0B44, 0x045C, 0x0744, + 0x0463, 0x18C4, 0x0465, 0x14C4, 0x0466, 0x0CC4, 0x0469, 0x12C4, 0x046A, 0x0AC4, + 0x046C, 0x06C4, 0x0471, 0x11C4, 0x0472, 0x09C4, 0x0474, 0x05C4, 0x0487, 0x1C24, + 0x048B, 0x1A24, 0x048D, 0x1624, 0x048E, 0x0E24, 0x0493, 0x1924, 0x0495, 0x1524, + 0x0496, 0x0D24, 0x0499, 0x1324, 0x049A, 0x0B24, 0x049C, 0x0724, 0x04A3, 0x18A4, + 0x04A5, 0x14A4, 0x04A6, 0x0CA4, 0x04A9, 0x12A4, 0x04AA, 0x0AA4, 0x04AC, 0x06A4, + 0x04B1, 0x11A4, 0x04B2, 0x09A4, 0x04B4, 0x05A4, 0x04C3, 0x1864, 0x04C5, 0x1464, + 0x04C6, 0x0C64, 0x04C9, 0x1264, 0x04CA, 0x0A64, 0x04CC, 0x0664, 0x04D1, 0x1164, + 0x04D2, 0x0964, 0x04D4, 0x0564, 0x04E1, 0x10E4, 0x04E2, 0x08E4, 0x0507, 0x1C14, + 0x050B, 0x1A14, 0x050D, 0x1614, 0x050E, 0x0E14, 0x0513, 0x1914, 0x0515, 0x1514, + 0x0516, 0x0D14, 0x0519, 0x1314, 0x051A, 0x0B14, 0x051C, 0x0714, 0x0523, 0x1894, + 0x0525, 0x1494, 0x0526, 0x0C94, 0x0529, 0x1294, 0x052A, 0x0A94, 0x052C, 0x0694, + 0x0531, 0x1194, 0x0532, 0x0994, 0x0534, 0x0594, 0x0543, 0x1854, 0x0545, 0x1454, + 0x0546, 0x0C54, 0x0549, 0x1254, 0x054A, 0x0A54, 0x054C, 0x0654, 0x0551, 0x1154, + 0x0552, 0x0954, 0x0561, 0x10D4, 0x0562, 0x08D4, 0x0583, 0x1834, 0x0585, 0x1434, + 0x0586, 0x0C34, 0x0589, 0x1234, 0x058A, 0x0A34, 0x058C, 0x0634, 0x0591, 0x1134, + 0x0592, 0x0934, 0x05A1, 0x10B4, 0x05A2, 0x08B4, 0x05C1, 0x1074, 0x05C2, 0x0874, + 0x0607, 0x1C0C, 0x060B, 0x1A0C, 0x060D, 0x160C, 0x060E, 0x0E0C, 0x0613, 0x190C, + 0x0615, 0x150C, 0x0616, 0x0D0C, 0x0619, 0x130C, 0x061A, 0x0B0C, 0x061C, 0x070C, + 0x0623, 0x188C, 0x0625, 0x148C, 0x0626, 0x0C8C, 0x0629, 0x128C, 0x062A, 0x0A8C, + 0x062C, 0x068C, 0x0631, 0x118C, 0x0632, 0x098C, 0x0643, 0x184C, 0x0645, 0x144C, + 0x0646, 0x0C4C, 0x0649, 0x124C, 0x064A, 0x0A4C, 0x0651, 0x114C, 0x0652, 0x094C, + 0x0661, 0x10CC, 0x0662, 0x08CC, 0x0683, 0x182C, 0x0685, 0x142C, 0x0686, 0x0C2C, + 0x0689, 0x122C, 0x068A, 0x0A2C, 0x0691, 0x112C, 0x0692, 0x092C, 0x06A1, 0x10AC, + 0x06A2, 0x08AC, 0x06C1, 0x106C, 0x06C2, 0x086C, 0x0703, 0x181C, 0x0705, 0x141C, + 0x0706, 0x0C1C, 0x0709, 0x121C, 0x070A, 0x0A1C, 0x0711, 0x111C, 0x0712, 0x091C, + 0x0721, 0x109C, 0x0722, 0x089C, 0x0741, 0x105C, 0x0742, 0x085C, 0x0781, 0x103C, + 0x0782, 0x083C, 0x080F, 0x1E02, 0x0817, 0x1D02, 0x081B, 0x1B02, 0x081D, 0x1702, + 0x081E, 0x0F02, 0x0827, 0x1C82, 0x082B, 0x1A82, 0x082D, 0x1682, 0x082E, 0x0E82, + 0x0833, 0x1982, 0x0835, 0x1582, 0x0836, 0x0D82, 0x0839, 0x1382, 0x083A, 0x0B82, + 0x0847, 0x1C42, 0x084B, 0x1A42, 0x084D, 0x1642, 0x084E, 0x0E42, 0x0853, 0x1942, + 0x0855, 0x1542, 0x0856, 0x0D42, 0x0859, 0x1342, 0x085A, 0x0B42, 0x0863, 0x18C2, + 0x0865, 0x14C2, 0x0866, 0x0CC2, 0x0869, 0x12C2, 0x086A, 0x0AC2, 0x0871, 0x11C2, + 0x0872, 0x09C2, 0x0887, 0x1C22, 0x088B, 0x1A22, 0x088D, 0x1622, 0x088E, 0x0E22, + 0x0893, 0x1922, 0x0895, 0x1522, 0x0896, 0x0D22, 0x0899, 0x1322, 0x089A, 0x0B22, + 0x08A3, 0x18A2, 0x08A5, 0x14A2, 0x08A6, 0x0CA2, 0x08A9, 0x12A2, 0x08AA, 0x0AA2, + 0x08B1, 0x11A2, 0x08B2, 0x09A2, 0x08C3, 0x1862, 0x08C5, 0x1462, 0x08C6, 0x0C62, + 0x08C9, 0x1262, 0x08CA, 0x0A62, 0x08D1, 0x1162, 0x08D2, 0x0962, 0x08E1, 0x10E2, + 0x0907, 0x1C12, 0x090B, 0x1A12, 0x090D, 0x1612, 0x090E, 0x0E12, 0x0913, 0x1912, + 0x0915, 0x1512, 0x0916, 0x0D12, 0x0919, 0x1312, 0x091A, 0x0B12, 0x0923, 0x1892, + 0x0925, 0x1492, 0x0926, 0x0C92, 0x0929, 0x1292, 0x092A, 0x0A92, 0x0931, 0x1192, + 0x0932, 0x0992, 0x0943, 0x1852, 0x0945, 0x1452, 0x0946, 0x0C52, 0x0949, 0x1252, + 0x094A, 0x0A52, 0x0951, 0x1152, 0x0961, 0x10D2, 0x0983, 0x1832, 0x0985, 0x1432, + 0x0986, 0x0C32, 0x0989, 0x1232, 0x098A, 0x0A32, 0x0991, 0x1132, 0x09A1, 0x10B2, + 0x09C1, 0x1072, 0x0A07, 0x1C0A, 0x0A0B, 0x1A0A, 0x0A0D, 0x160A, 0x0A0E, 0x0E0A, + 0x0A13, 0x190A, 0x0A15, 0x150A, 0x0A16, 0x0D0A, 0x0A19, 0x130A, 0x0A1A, 0x0B0A, + 0x0A23, 0x188A, 0x0A25, 0x148A, 0x0A26, 0x0C8A, 0x0A29, 0x128A, 0x0A2A, 0x0A8A, + 0x0A31, 0x118A, 0x0A43, 0x184A, 0x0A45, 0x144A, 0x0A46, 0x0C4A, 0x0A49, 0x124A, + 0x0A51, 0x114A, 0x0A61, 0x10CA, 0x0A83, 0x182A, 0x0A85, 0x142A, 0x0A86, 0x0C2A, + 0x0A89, 0x122A, 0x0A91, 0x112A, 0x0AA1, 0x10AA, 0x0AC1, 0x106A, 0x0B03, 0x181A, + 0x0B05, 0x141A, 0x0B06, 0x0C1A, 0x0B09, 0x121A, 0x0B11, 0x111A, 0x0B21, 0x109A, + 0x0B41, 0x105A, 0x0B81, 0x103A, 0x0C07, 0x1C06, 0x0C0B, 0x1A06, 0x0C0D, 0x1606, + 0x0C0E, 0x0E06, 0x0C13, 0x1906, 0x0C15, 0x1506, 0x0C16, 0x0D06, 0x0C19, 0x1306, + 0x0C23, 0x1886, 0x0C25, 0x1486, 0x0C26, 0x0C86, 0x0C29, 0x1286, 0x0C31, 0x1186, + 0x0C43, 0x1846, 0x0C45, 0x1446, 0x0C49, 0x1246, 0x0C51, 0x1146, 0x0C61, 0x10C6, + 0x0C83, 0x1826, 0x0C85, 0x1426, 0x0C89, 0x1226, 0x0C91, 0x1126, 0x0CA1, 0x10A6, + 0x0CC1, 0x1066, 0x0D03, 0x1816, 0x0D05, 0x1416, 0x0D09, 0x1216, 0x0D11, 0x1116, + 0x0D21, 0x1096, 0x0D41, 0x1056, 0x0D81, 0x1036, 0x0E03, 0x180E, 0x0E05, 0x140E, + 0x0E09, 0x120E, 0x0E11, 0x110E, 0x0E21, 0x108E, 0x0E41, 0x104E, 0x0E81, 0x102E, + 0x0F01, 0x101E, 0x100F, 0x1E01, 0x1017, 0x1D01, 0x101B, 0x1B01, 0x101D, 0x1701, + 0x1027, 0x1C81, 0x102B, 0x1A81, 0x102D, 0x1681, 0x1033, 0x1981, 0x1035, 0x1581, + 0x1039, 0x1381, 0x1047, 0x1C41, 0x104B, 0x1A41, 0x104D, 0x1641, 0x1053, 0x1941, + 0x1055, 0x1541, 0x1059, 0x1341, 0x1063, 0x18C1, 0x1065, 0x14C1, 0x1069, 0x12C1, + 0x1071, 0x11C1, 0x1087, 0x1C21, 0x108B, 0x1A21, 0x108D, 0x1621, 0x1093, 0x1921, + 0x1095, 0x1521, 0x1099, 0x1321, 0x10A3, 0x18A1, 0x10A5, 0x14A1, 0x10A9, 0x12A1, + 0x10B1, 0x11A1, 0x10C3, 0x1861, 0x10C5, 0x1461, 0x10C9, 0x1261, 0x10D1, 0x1161, + 0x1107, 0x1C11, 0x110B, 0x1A11, 0x110D, 0x1611, 0x1113, 0x1911, 0x1115, 0x1511, + 0x1119, 0x1311, 0x1123, 0x1891, 0x1125, 0x1491, 0x1129, 0x1291, 0x1131, 0x1191, + 0x1143, 0x1851, 0x1145, 0x1451, 0x1149, 0x1251, 0x1183, 0x1831, 0x1185, 0x1431, + 0x1189, 0x1231, 0x1207, 0x1C09, 0x120B, 0x1A09, 0x120D, 0x1609, 0x1213, 0x1909, + 0x1215, 0x1509, 0x1219, 0x1309, 0x1223, 0x1889, 0x1225, 0x1489, 0x1229, 0x1289, + 0x1243, 0x1849, 0x1245, 0x1449, 0x1283, 0x1829, 0x1285, 0x1429, 0x1303, 0x1819, + 0x1305, 0x1419, 0x1407, 0x1C05, 0x140B, 0x1A05, 0x140D, 0x1605, 0x1413, 0x1905, + 0x1415, 0x1505, 0x1423, 0x1885, 0x1425, 0x1485, 0x1443, 0x1845, 0x1483, 0x1825, + 0x1503, 0x1815, 0x1603, 0x180D, 0x1807, 0x1C03, 0x180B, 0x1A03, 0x1813, 0x1903, + 0x1823, 0x1883, 0x1843, 0x1445, 0x1249, 0x1151, 0x10E1, 0x0C46, 0x0A4A, 0x0952, + 0x08E2, 0x064C, 0x0554, 0x04E4, 0x0358, 0x02E8, 0x01F0 + }; + private int[] AppxD_II = { /* Appendix D Table II - 2 of 13 characters */ + 0x0003, 0x1800, 0x0005, 0x1400, 0x0006, 0x0C00, 0x0009, 0x1200, 0x000A, 0x0A00, + 0x000C, 0x0600, 0x0011, 0x1100, 0x0012, 0x0900, 0x0014, 0x0500, 0x0018, 0x0300, + 0x0021, 0x1080, 0x0022, 0x0880, 0x0024, 0x0480, 0x0028, 0x0280, 0x0030, 0x0180, + 0x0041, 0x1040, 0x0042, 0x0840, 0x0044, 0x0440, 0x0048, 0x0240, 0x0050, 0x0140, + 0x0060, 0x00C0, 0x0081, 0x1020, 0x0082, 0x0820, 0x0084, 0x0420, 0x0088, 0x0220, + 0x0090, 0x0120, 0x0101, 0x1010, 0x0102, 0x0810, 0x0104, 0x0410, 0x0108, 0x0210, + 0x0201, 0x1008, 0x0202, 0x0808, 0x0204, 0x0408, 0x0401, 0x1004, 0x0402, 0x0804, + 0x0801, 0x1002, 0x1001, 0x0802, 0x0404, 0x0208, 0x0110, 0x00A0 + }; + private int[] AppxD_IV = { /* Appendix D Table IV - Bar-to-Character Mapping (reverse lookup) */ + 67, 6, 78, 16, 86, 95, 34, 40, 45, 113, 117, 121, 62, 87, 18, 104, 41, 76, 57, 119, 115, 72, 97, + 2, 127, 26, 105, 35, 122, 52, 114, 7, 24, 82, 68, 63, 94, 44, 77, 112, 70, 100, 39, 30, 107, + 15, 125, 85, 10, 65, 54, 88, 20, 106, 46, 66, 8, 116, 29, 61, 99, 80, 90, 37, 123, 51, 25, 84, + 129, 56, 4, 109, 96, 28, 36, 47, 11, 71, 33, 102, 21, 9, 17, 49, 124, 79, 64, 91, 42, 69, 53, + 60, 14, 1, 27, 103, 126, 75, 89, 50, 120, 19, 32, 110, 92, 111, 130, 59, 31, 12, 81, 43, 55, + 5, 74, 22, 101, 128, 58, 118, 48, 108, 38, 98, 93, 23, 83, 13, 73, 3 + }; + + public UspsOneCode() { + this.defaultHeight = 8; + setHumanReadableLocation(HumanReadableLocation.NONE); + } + + @Override + public boolean encode() { + StringBuilder zip = new StringBuilder(); + String zip_adder; + StringBuilder tracker = new StringBuilder(); + int i, j; + int length = content.length(); + BigInteger accum; + BigInteger x_reg; + BigInteger mask; + int usps_crc; + int[] codeword = new int[10]; + int[] characters = new int[10]; + boolean[] bar_map = new boolean[130]; + char c; + + if (!content.matches("[0-9\u002D]+")) { + errorMsg.append("Invalid characters in input data"); + return false; + } + + if (length > 32) { + errorMsg.append("Input too long"); + return false; + } + + /* separate the tracking code from the routing code */ + j = 0; + for (i = 0; i < length; i++) { + if (content.charAt(i) == '-') { + j = 1; + } else { + if (j == 0) { + /* reading tracker */ + tracker.append(content.charAt(i)); + } else { + /* reading zip code */ + zip.append(content.charAt(i)); + } + } + } + + if (tracker.length() != 20) { + errorMsg.append("Invalid length tracking code"); + return false; + } + + if (zip.length() > 11) { + errorMsg.append("Invalid ZIP code"); + return false; + } + + /* *** Step 1 - Conversion of Data Fields into Binary Data *** */ + /* Routing code first */ + if (zip.length() > 0) { + x_reg = new BigInteger(zip.toString()); + } else { + x_reg = new BigInteger("0"); + } + + /* add weight to routing code */ + if (zip.length() > 9) { + zip_adder = "1000100001"; + } else { + if (zip.length() > 5) { + zip_adder = "100001"; + } else { + if (zip.length() > 0) { + zip_adder = "1"; + } else { + zip_adder = "0"; + } + } + } + + accum = new BigInteger(zip_adder); + accum = accum.add(x_reg); + accum = accum.multiply(BigInteger.valueOf(10)); + accum = accum.add(BigInteger.valueOf(Character.getNumericValue(tracker.charAt(0)))); + accum = accum.multiply(BigInteger.valueOf(5)); + accum = accum.add(BigInteger.valueOf(Character.getNumericValue(tracker.charAt(1)))); + for (i = 2; i < tracker.length(); i++) { + accum = accum.multiply(BigInteger.valueOf(10)); + accum = accum.add(BigInteger.valueOf(Character.getNumericValue(tracker.charAt(i)))); + } + + /* *** Step 2 - Generation of 11-bit CRC on Binary Data *** */ + + for (i = 0; i < 13; i++) { + mask = accum.shiftRight(96 - (8 * i)); + mask = mask.and(new BigInteger("255")); + byte_array[i] = mask.intValue(); + } + + usps_crc = USPS_MSB_Math_CRC11GenerateFrameCheckSequence(); + + /* *** Step 3 - Conversion from Binary Data to Codewords *** */ + /* start with codeword J which is base 636 */ + + x_reg = accum.mod(BigInteger.valueOf(636)); + codeword[9] = x_reg.intValue(); + accum = accum.subtract(x_reg); + accum = accum.divide(BigInteger.valueOf(636)); + + for (i = 8; i >= 0; i--) { + x_reg = accum.mod(BigInteger.valueOf(1365)); + codeword[i] = x_reg.intValue(); + accum = accum.subtract(x_reg); + accum = accum.divide(BigInteger.valueOf(1365)); + } + + for (i = 0; i < 9; i++) { + if (codeword[i] == 1365) { + codeword[i] = 0; + } + } + + /* *** Step 4 - Inserting Additional Information into Codewords *** */ + codeword[9] = codeword[9] * 2; + + if (usps_crc >= 1024) { + codeword[0] += 659; + } + + encodeInfo.append("Codewords: "); + for (i = 0; i < 10; i++) { + encodeInfo.append(Integer.toString(codeword[i])).append(" "); + } + encodeInfo.append("\n"); + + /* *** Step 5 - Conversion from Codewords to Characters *** */ + + for (i = 0; i < 10; i++) { + if (codeword[i] < 1287) { + characters[i] = AppxD_I[codeword[i]]; + } else { + characters[i] = AppxD_II[codeword[i] - 1287]; + } + } + + for (i = 0; i < 10; i++) { + if ((usps_crc & (1 << i)) != 0) { + characters[i] = 0x1FFF - characters[i]; + } + } + + /* *** Step 6 - Conversion from Characters to the Intelligent Mail Barcode *** */ + + for (i = 0; i < 10; i++) { + for (j = 0; j < 13; j++) { + bar_map[AppxD_IV[(13 * i) + j] - 1] = (characters[i] & (1 << j)) != 0; + } + } + + readable = new StringBuilder(content); + pattern = new String[1]; + rowCount = 1; + rowHeight = new int[1]; + rowHeight[0] = -1; + + pattern[0] = ""; + for (i = 0; i < 65; i++) { + c = 'T'; + if (bar_map[i]) { + c = 'D'; + } + if (bar_map[i + 65]) { + c = 'A'; + } + if (bar_map[i] && bar_map[i + 65]) { + c = 'F'; + } + pattern[0] += c; + } + + encodeInfo.append("Encoding: ").append(pattern[0]).append("\n"); + + plotSymbol(); + return true; + } + + private int USPS_MSB_Math_CRC11GenerateFrameCheckSequence() { + int GeneratorPolynomial = 0x0F35; + int FrameCheckSequence = 0x07FF; + int Data; + int ByteIndex, Bit; + int ByteArrayPtr = 0; + + /* Do most significant byte skipping the 2 most significant bits */ + Data = byte_array[ByteArrayPtr] << 5; + ByteArrayPtr++; + for (Bit = 2; Bit < 8; Bit++) { + if (((FrameCheckSequence ^ Data) & 0x400) != 0) + FrameCheckSequence = (FrameCheckSequence << 1) ^ GeneratorPolynomial; + else + FrameCheckSequence = (FrameCheckSequence << 1); + FrameCheckSequence &= 0x7FF; + Data <<= 1; + } + /* Do rest of the bytes */ + for (ByteIndex = 1; ByteIndex < 13; ByteIndex++) { + Data = byte_array[ByteArrayPtr] << 3; + ByteArrayPtr++; + for (Bit = 0; Bit < 8; Bit++) { + if (((FrameCheckSequence ^ Data) & 0x0400) != 0) { + FrameCheckSequence = (FrameCheckSequence << 1) ^ GeneratorPolynomial; + } else { + FrameCheckSequence = (FrameCheckSequence << 1); + } + FrameCheckSequence &= 0x7FF; + Data <<= 1; + } + } + return FrameCheckSequence; + } + + @Override + protected void plotSymbol() { + int xBlock, shortHeight, longHeight; + double x, y, w, h; + getRectangles().clear(); + getTexts().clear(); + int baseY; + if (getHumanReadableLocation() == TOP) { + baseY = getTheoreticalHumanReadableHeight(); + } else { + baseY = 0; + } + + x = 0; + w = moduleWidth; + y = 0; + h = 0; + shortHeight = (int) (0.25 * defaultHeight); + longHeight = (int) (0.625 * defaultHeight); + for (xBlock = 0; xBlock < pattern[0].length(); xBlock++) { + + switch (pattern[0].charAt(xBlock)) { + case 'A': + y = baseY; + h = longHeight; + break; + case 'D': + y = baseY + defaultHeight - longHeight; + h = longHeight; + break; + case 'F': + y = baseY; + h = defaultHeight; + break; + case 'T': + y = baseY + defaultHeight - longHeight; + h = shortHeight; + break; + } + Rectangle2D.Double rect = new Rectangle2D.Double(x, y, w, h); + getRectangles().add(rect); + x += (2.43 * w); + } + symbolWidth = (int) Math.ceil(((pattern[0].length() - 1) * 2.43 * w) + w); // final bar doesn't need extra whitespace + symbolHeight = defaultHeight; + if (getHumanReadableLocation() != NONE && readable.length() > 0) { + double baseline; + if (getHumanReadableLocation() == TOP) { + baseline = fontSize; + } else { + baseline = getHeight() + fontSize; + } + double centerX = getWidth() / 2.0; + getTexts().add(new TextBox(centerX, baseline, readable.toString())); + } + } +} diff --git a/barcode/src/main/java/org/xbib/graphics/barcode/UspsPackage.java b/barcode/src/main/java/org/xbib/graphics/barcode/UspsPackage.java new file mode 100755 index 0000000..0289810 --- /dev/null +++ b/barcode/src/main/java/org/xbib/graphics/barcode/UspsPackage.java @@ -0,0 +1,114 @@ +package org.xbib.graphics.barcode; + +import org.xbib.graphics.barcode.util.TextBox; +import java.awt.geom.Rectangle2D; + +/** + * USPS Intelligent Mail Package Barcode (IMpb)
+ * A linear barcode based on GS1-128. Includes additional data checks. + * Specification at https://ribbs.usps.gov/intelligentmail_package/documents/tech_guides/BarcodePackageIMSpec.pdf + */ +public class UspsPackage extends Symbol { + + @Override + public boolean encode() { + StringBuilder hrt; + StringBuilder spacedHrt; + boolean fourTwenty = false; + int bracketCount = 0; + + if (!(content.matches("[0-9\\[\\]]+"))) { + /* Input must be numeric only */ + errorMsg.append("Invalid IMpd data"); + return false; + } + + if ((content.length() % 2) != 0) { + /* Input must be even length */ + errorMsg.append("Invalid IMpd data"); + return false; + } + + Code128 code128 = new Code128(); + code128.unsetCc(); + code128.setDataType(DataType.GS1); + code128.setContent(content); + + if (content.length() > 4) { + fourTwenty = ((content.charAt(1) == '4') && (content.charAt(2) == '2') && + (content.charAt(3) == '0')); + } + + hrt = new StringBuilder(); + for (int i = 0; i < content.length(); i++) { + if (content.charAt(i) == '[') { + bracketCount++; + } + if (!(fourTwenty && bracketCount < 2)) { + if ((content.charAt(i) >= '0') && (content.charAt(i) <= '9')) { + hrt.append(content.charAt(i)); + } + } + } + + spacedHrt = new StringBuilder(); + for (int i = 0; i < hrt.length(); i++) { + spacedHrt.append(hrt.charAt(i)); + if (i % 4 == 3) { + spacedHrt.append(" "); + } + } + + readable = new StringBuilder(spacedHrt.toString()); + pattern = new String[1]; + pattern[0] = code128.pattern[0]; + rowCount = 1; + rowHeight = new int[1]; + rowHeight[0] = -1; + plotSymbol(); + + return true; + } + + @Override + protected void plotSymbol() { + int xBlock; + int x, y, w, h; + boolean black; + int offset = 20; + int yoffset = 15; + String banner = "USPS TRACKING #"; + getRectangles().clear(); + getTexts().clear(); + y = yoffset; + h = 0; + black = true; + x = 0; + for (xBlock = 0; xBlock < pattern[0].length(); xBlock++) { + w = pattern[0].charAt(xBlock) - '0'; + if (black) { + if (rowHeight[0] == -1) { + h = defaultHeight; + } else { + h = rowHeight[0]; + } + if (w != 0 && h != 0) { + Rectangle2D.Double rect = new Rectangle2D.Double(x + offset, y, w, h); + getRectangles().add(rect); + } + symbolWidth = x + w + (2 * offset); + } + black = !black; + x += w; + } + symbolHeight = h + (2 * yoffset); + // Add boundary bars + Rectangle2D.Double topBar = new Rectangle2D.Double(0, 0, symbolWidth, 2); + Rectangle2D.Double bottomBar = new Rectangle2D.Double(0, symbolHeight - 2, symbolWidth, 2); + getRectangles().add(topBar); + getRectangles().add(bottomBar); + double centerX = getWidth() / 2.0; + getTexts().add(new TextBox(centerX, getHeight() - 6.0, readable.toString())); + getTexts().add(new TextBox(centerX, 12.0, banner)); + } +} diff --git a/barcode/src/main/java/org/xbib/graphics/barcode/render/GraphicsRenderer.java b/barcode/src/main/java/org/xbib/graphics/barcode/render/GraphicsRenderer.java new file mode 100755 index 0000000..59f2609 --- /dev/null +++ b/barcode/src/main/java/org/xbib/graphics/barcode/render/GraphicsRenderer.java @@ -0,0 +1,150 @@ +package org.xbib.graphics.barcode.render; + +import org.xbib.graphics.barcode.HumanReadableLocation; +import org.xbib.graphics.barcode.Symbol; +import org.xbib.graphics.barcode.util.Hexagon; +import org.xbib.graphics.barcode.util.TextBox; +import java.awt.Color; +import java.awt.Font; +import java.awt.FontMetrics; +import java.awt.Graphics2D; +import java.awt.RenderingHints; +import java.awt.font.TextAttribute; +import java.awt.geom.Ellipse2D; +import java.awt.geom.Path2D; +import java.awt.geom.Rectangle2D; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +/** + * Renders symbologies using the Java Graphics API. + */ +public class GraphicsRenderer { + + /** + * The graphics to render to. + */ + private final Graphics2D g2d; + + /** + * The scaling factor. + */ + private final double scalingFactor; + + /** + * The paper (background) color. + */ + private final Color background; + + /** + * The ink (foreground) color. + */ + private final Color foreground; + + private final boolean antialias; + + private final boolean transparentBackground; + + /** + * Creates a new Java 2D renderer. + * + * @param g2d the graphics to render to + * @param scalingFactor the scaling factor to apply + * @param background the paper (background) color + * @param foreground the ink (foreground) color + * @param antialias if true give anti alias hint + */ + public GraphicsRenderer(Graphics2D g2d, + double scalingFactor, + Color background, + Color foreground, + boolean antialias, + boolean transparentBackground) { + this.g2d = g2d; + this.scalingFactor = scalingFactor; + this.background = background; + this.foreground = foreground; + this.antialias = antialias; + this.transparentBackground = transparentBackground; + } + + public void render(Symbol symbol) { + RenderingHints oldRenderingHints = g2d.getRenderingHints(); + Color oldColor = g2d.getColor(); + Color oldBackground = g2d.getBackground(); + if (antialias) { + g2d.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON); + } else { + g2d.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_OFF); + } + double marginX = symbol.getQuietZoneHorizontal() * scalingFactor; + double marginY = symbol.getQuietZoneVertical() * scalingFactor; + g2d.setBackground(background); + if (!transparentBackground) { + g2d.setColor(background); + g2d.fill(g2d.getDeviceConfiguration().getBounds()); + g2d.setColor(foreground); + } + for (Rectangle2D.Double rect : symbol.getRectangles()) { + double x = rect.x * scalingFactor + marginX; + double y = rect.y * scalingFactor + marginY; + double w = rect.width * scalingFactor; + double h = rect.height * scalingFactor; + Path2D path = new Path2D.Double(); + path.moveTo(x, y); + path.lineTo(x + w, y); + path.lineTo(x + w, y + h); + path.lineTo(x, y + h); + path.closePath(); + g2d.fill(path); + } + if (symbol.getHumanReadableLocation() != HumanReadableLocation.NONE) { + Map attributes = new HashMap<>(); + attributes.put(TextAttribute.TRACKING, 0); + Font f = new Font(symbol.getFontName(), Font.PLAIN, (int) (symbol.getFontSize() * scalingFactor)).deriveFont(attributes); + Font oldFont = g2d.getFont(); + g2d.setFont(f); + FontMetrics fm = g2d.getFontMetrics(); + for (TextBox text : symbol.getTexts()) { + Rectangle2D bounds = fm.getStringBounds(text.text, g2d); + double x = (text.x * scalingFactor) - (bounds.getWidth() / 2) + marginX; + double y = (text.y * scalingFactor) + marginY; + g2d.drawString(text.text, (float) x, (float) y); + } + g2d.setFont(oldFont); + } + for (Hexagon hexagon : symbol.getHexagons()) { + Path2D path = new Path2D.Double(); + path.moveTo(hexagon.pointX[0] * scalingFactor + marginX, hexagon.pointY[0] * scalingFactor + marginY); + for(int i = 1; i < 6; ++i) { + double x = hexagon.pointX[i] * scalingFactor + marginX; + double y = hexagon.pointY[i] * scalingFactor + marginY; + path.lineTo(x, y); + } + path.closePath(); + g2d.fill(path); + } + List targets = symbol.getTarget(); + for (int i = 0; i < targets.size(); i++) { + Ellipse2D.Double ellipse = targets.get(i); + double x = ellipse.x * scalingFactor + marginX; + double y = ellipse.y * scalingFactor + marginY; + double w = ellipse.width * scalingFactor; + double h = ellipse.height * scalingFactor; + if ((i & 1) == 0) { + g2d.setColor(foreground); + } else { + g2d.setColor(background); + } + g2d.fill(new Ellipse2D.Double(x, y, w, h)); + } + g2d.setColor(oldColor); + g2d.setBackground(oldBackground); + g2d.setRenderingHints(oldRenderingHints); + } + + public void close() { + g2d.dispose(); + } +} diff --git a/barcode/src/main/java/org/xbib/graphics/barcode/util/AddOn.java b/barcode/src/main/java/org/xbib/graphics/barcode/util/AddOn.java new file mode 100755 index 0000000..4499e80 --- /dev/null +++ b/barcode/src/main/java/org/xbib/graphics/barcode/util/AddOn.java @@ -0,0 +1,112 @@ +package org.xbib.graphics.barcode.util; + +/** + * Encode Add-On barcodes from UPC/EAN. + */ +public class AddOn { + private String content; + private String dest; + + private String[] EANsetA = { + "3211", "2221", "2122", "1411", "1132", "1231", "1114", "1312", "1213", + "3112" + }; + private String[] EANsetB = { + "1123", "1222", "2212", "1141", "2311", "1321", "4111", "2131", "3121", + "2113" + }; + + private String[] EAN2Parity = { + "AA", "AB", "BA", "BB" + }; + private String[] EAN5Parity = { + "BBAAA", "BABAA", "BAABA", "BAAAB", "ABBAA", "AABBA", "AAABB", "ABABA", + "ABAAB", "AABAB" + }; + + public String calcAddOn(String input) { + dest = ""; + content = input; + + if (!(content.matches("[0-9]{1,5}"))) { + return ""; + } + + if (content.length() > 2) { + ean5(); + } else { + ean2(); + } + + return dest; + } + + private void ean2() { + String parity; + String accumulator = ""; + int i, code_value; + + if (!(content.matches("[0-9]+?"))) { + return; + } + + for (i = content.length(); i < 2; i++) { + accumulator += "0"; + } + accumulator += content; + + code_value = ((accumulator.charAt(0) - '0') * 10) + + (accumulator.charAt(1) - '0'); + parity = EAN2Parity[code_value % 4]; + + dest = "112"; /* Start */ + for (i = 0; i < 2; i++) { + if ((parity.charAt(i) == 'B')) { + dest += EANsetB[Character.getNumericValue(accumulator.charAt(i))]; + } else { + dest += EANsetA[Character.getNumericValue(accumulator.charAt(i))]; + } + if (i != 1) { /* Glyph separator */ + dest += "11"; + } + } + } + + private void ean5() { + String parity; + String accumulator = ""; + int i, parity_sum; + + if (!(content.matches("[0-9]+?"))) { + return; + } + + for (i = content.length(); i < 5; i++) { + accumulator += "0"; + } + accumulator += content; + + parity_sum = 0; + for (i = 0; i < 5; i++) { + if ((i % 2) == 0) { + parity_sum += 3 * (accumulator.charAt(i) - '0'); + } else { + parity_sum += 9 * (accumulator.charAt(i) - '0'); + } + } + + parity = EAN5Parity[parity_sum % 10]; + + dest = "112"; /* Start */ + for (i = 0; i < 5; i++) { + if ((parity.charAt(i) == 'B')) { + dest += EANsetB[Character.getNumericValue(accumulator.charAt(i))]; + } else { + dest += EANsetA[Character.getNumericValue(accumulator.charAt(i))]; + } + if (i != 4) { /* Glyph separator */ + dest += "11"; + } + } + } +} diff --git a/barcode/src/main/java/org/xbib/graphics/barcode/util/Hexagon.java b/barcode/src/main/java/org/xbib/graphics/barcode/util/Hexagon.java new file mode 100755 index 0000000..8b27f37 --- /dev/null +++ b/barcode/src/main/java/org/xbib/graphics/barcode/util/Hexagon.java @@ -0,0 +1,23 @@ +package org.xbib.graphics.barcode.util; + +/** + * Calculate a set of points to make a hexagon. + */ +public class Hexagon { + + private static final double INK_SPREAD = 1.25; + + private static final double[] OFFSET_X = {0.0, 0.86, 0.86, 0.0, -0.86, -0.86}; + + private static final double[] OFFSET_Y = {1.0, 0.5, -0.5, -1.0, -0.5, 0.5}; + + public final double[] pointX = new double[6]; + public final double[] pointY = new double[6]; + + public Hexagon(double centreX, double centreY) { + for (int i = 0; i < 6; i++) { + pointX[i] = centreX + (OFFSET_X[i] * INK_SPREAD); + pointY[i] = centreY + (OFFSET_Y[i] * INK_SPREAD); + } + } +} diff --git a/barcode/src/main/java/org/xbib/graphics/barcode/util/ReedSolomon.java b/barcode/src/main/java/org/xbib/graphics/barcode/util/ReedSolomon.java new file mode 100755 index 0000000..30770dc --- /dev/null +++ b/barcode/src/main/java/org/xbib/graphics/barcode/util/ReedSolomon.java @@ -0,0 +1,86 @@ +package org.xbib.graphics.barcode.util; + +/** + */ +public class ReedSolomon { + private int[] res; + private int logmod; + private int rlen; + private int[] logt; + private int[] alog; + private int[] rspoly; + + public int getResult(int count) { + return res[count]; + } + + public void init_gf(int poly) { + int m, b, p, v; + + // Find the top bit, and hence the symbol size + for (b = 1, m = 0; b <= poly; b <<= 1) { + m++; + } + b >>= 1; + m--; + + // Calculate the log/alog tables + logmod = (1 << m) - 1; + logt = new int[logmod + 1]; + alog = new int[logmod]; + + for (p = 1, v = 0; v < logmod; v++) { + alog[v] = p; + logt[p] = v; + p <<= 1; + if ((p & b) != 0) { + p ^= poly; + } + } + } + + public void init_code(int nsym, int index) { + int i, k; + + rspoly = new int[nsym + 1]; + + rlen = nsym; + + rspoly[0] = 1; + for (i = 1; i <= nsym; i++) { + rspoly[i] = 1; + for (k = i - 1; k > 0; k--) { + if (rspoly[k] != 0) { + rspoly[k] = alog[(logt[rspoly[k]] + index) % logmod]; + } + rspoly[k] ^= rspoly[k - 1]; + } + rspoly[0] = alog[(logt[rspoly[0]] + index) % logmod]; + index++; + } + } + + public void encode(int len, int[] data) { + int i, k, m; + + res = new int[rlen]; + for (i = 0; i < rlen; i++) { + res[i] = 0; + } + for (i = 0; i < len; i++) { + m = res[rlen - 1] ^ data[i]; + for (k = rlen - 1; k > 0; k--) { + if ((m != 0) && (rspoly[k] != 0)) { + res[k] = res[k - 1] ^ alog[(logt[m] + logt[rspoly[k]]) % logmod]; + } else { + res[k] = res[k - 1]; + } + } + if ((m != 0) && (rspoly[0] != 0)) { + res[0] = alog[(logt[m] + logt[rspoly[0]]) % logmod]; + } else { + res[0] = 0; + } + } + } +} diff --git a/barcode/src/main/java/org/xbib/graphics/barcode/util/TextBox.java b/barcode/src/main/java/org/xbib/graphics/barcode/util/TextBox.java new file mode 100755 index 0000000..e5a438e --- /dev/null +++ b/barcode/src/main/java/org/xbib/graphics/barcode/util/TextBox.java @@ -0,0 +1,40 @@ +package org.xbib.graphics.barcode.util; + +/** + * A simple text item class. + */ +public class TextBox { + + /** + * X position that the text should be centered on horizontally. + */ + public final double x; + + /** + * Y position of the text baseline. + */ + public final double y; + + /** + * Text value. + */ + public final String text; + + /** + * Creates a new instance. + * + * @param x the X position that the text should be centered on horizontally + * @param y the Y position of the text baseline + * @param text the text value + */ + public TextBox(double x, double y, String text) { + this.x = x; + this.y = y; + this.text = text; + } + + @Override + public String toString() { + return "TextBox[x=" + x + ", y=" + y + ", text=" + text + "]"; + } +} diff --git a/barcode/src/test/java/org/xbib/graphics/barcode/MaxiCodeTest.java b/barcode/src/test/java/org/xbib/graphics/barcode/MaxiCodeTest.java new file mode 100755 index 0000000..b0aecce --- /dev/null +++ b/barcode/src/test/java/org/xbib/graphics/barcode/MaxiCodeTest.java @@ -0,0 +1,19 @@ +package org.xbib.graphics.barcode; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import org.junit.jupiter.api.Test; + +/** + * {@link MaxiCode} tests that can't be run via the {@link SymbolTest}. + */ +public class MaxiCodeTest { + + @Test + public void testHumanReadableHeight() { + MaxiCode maxicode = new MaxiCode(); + maxicode.setMode(4); + maxicode.setContent("ABC"); + assertEquals(0, maxicode.getHumanReadableHeight()); + } + +} diff --git a/barcode/src/test/java/org/xbib/graphics/barcode/ParameterizedExtension.java b/barcode/src/test/java/org/xbib/graphics/barcode/ParameterizedExtension.java new file mode 100644 index 0000000..41a5eac --- /dev/null +++ b/barcode/src/test/java/org/xbib/graphics/barcode/ParameterizedExtension.java @@ -0,0 +1,253 @@ +package org.xbib.graphics.barcode; + +import static java.util.Collections.singletonList; +import static java.util.function.Function.identity; +import static java.util.stream.Collectors.groupingBy; +import static java.util.stream.Collectors.toList; + +import java.lang.reflect.Constructor; +import java.lang.reflect.Executable; +import java.lang.reflect.Field; +import java.lang.reflect.Method; +import java.util.Collection; +import java.util.List; +import java.util.Map; +import java.util.Optional; +import java.util.stream.IntStream; +import java.util.stream.Stream; + +import org.junit.jupiter.api.extension.Extension; +import org.junit.jupiter.api.extension.ExtensionContext; +import org.junit.jupiter.api.extension.ParameterContext; +import org.junit.jupiter.api.extension.ParameterResolutionException; +import org.junit.jupiter.api.extension.ParameterResolver; +import org.junit.jupiter.api.extension.TestInstancePostProcessor; +import org.junit.jupiter.api.extension.TestTemplateInvocationContext; +import org.junit.jupiter.api.extension.TestTemplateInvocationContextProvider; +import org.junit.platform.commons.util.CollectionUtils; +import org.junit.platform.commons.util.ReflectionUtils; +import org.junit.runners.Parameterized; + +public class ParameterizedExtension implements TestTemplateInvocationContextProvider { + + private final static ExtensionContext.Namespace PARAMETERS = ExtensionContext.Namespace.create( + ParameterizedExtension.class); + + /** + * Indicate whether we can provide parameterized support. + * This requires the testClass to either have a static {@code @Parameters} method + * and correct {@code @Parameter} and their corresponding values + * or to have a constructor that could be injected. + */ + public boolean supportsTestTemplate(ExtensionContext context) { + return hasParametersMethod(context) && validInjectionMix(context); + } + + private static boolean validInjectionMix(ExtensionContext context) { + List fields = parametersFields(context); + boolean hasParameterFields = !fields.isEmpty(); + boolean hasCorrectParameterFields = areParametersFormedCorrectly(fields); + boolean hasArgsConstructor = hasArgsConstructor(context); + + if (hasArgsConstructor) { + return !hasParameterFields; + } + else { + return !hasParameterFields || hasCorrectParameterFields; + } + } + + @Override + public Stream provideTestTemplateInvocationContexts(ExtensionContext context) { + // grabbing the parent ensures the PARAMETERS are stored in the same store across multiple TestTemplates. + return context.getParent().flatMap(ParameterizedExtension::parameters).map( + o -> testTemplateContextsFromParameters(o, context)).orElse(Stream.empty()); + } + + private static boolean areParametersFormedCorrectly(List fields) { + List parameterValues = parameterIndexes(fields); + List duplicateIndexes = duplicatedIndexes(parameterValues); + boolean hasAllIndexes = indexRangeComplete(parameterValues); + + return hasAllIndexes && duplicateIndexes.isEmpty(); + } + + private static List parameterIndexes(List fields) { + // @formatter:off + return fields.stream() + .map(f -> f.getAnnotation(Parameterized.Parameter.class)) + .map(Parameterized.Parameter::value) + .collect(toList()); + // @formatter:on + } + + private static List duplicatedIndexes(List parameterValues) { + // @formatter:off + return parameterValues.stream().collect(groupingBy(identity())).entrySet().stream() + .filter(e -> e.getValue().size() > 1) + .map(Map.Entry::getKey) + .collect(toList()); + // @formatter:on + } + + private static Boolean indexRangeComplete(List parameterValues) { + // @formatter:off + return parameterValues.stream() + .max(Integer::compareTo) + .map(i -> parameterValues.containsAll(IntStream.range(0, i).boxed().collect(toList()))) + .orElse(false); + // @formatter:on + } + + private static Optional> parameters(ExtensionContext context) { + return context.getStore(PARAMETERS).getOrComputeIfAbsent("parameterMethod", + k -> new ParameterWrapper(callParameters(context)), ParameterWrapper.class).getValue(); + + } + + private static Optional> callParameters(ExtensionContext context) { + // @formatter:off + return findParametersMethod(context) + .map(m -> ReflectionUtils.invokeMethod(m, null)) + .map(ParameterizedExtension::convertParametersMethodReturnType); + // @formatter:on + } + + private static boolean hasParametersMethod(ExtensionContext context) { + return findParametersMethod(context).isPresent(); + } + + private static Optional findParametersMethod(ExtensionContext extensionContext) { + // @formatter:off + return extensionContext.getTestClass() + .flatMap(ParameterizedExtension::ensureSingleParametersMethod) + .filter(ReflectionUtils::isPublic); + // @formatter:on + } + + private static Optional ensureSingleParametersMethod(Class testClass) { + return ReflectionUtils.findMethods(testClass, + m -> m.isAnnotationPresent(Parameterized.Parameters.class)).stream().findFirst(); + } + + private static Stream testTemplateContextsFromParameters(Collection o, + ExtensionContext context) { + List fields = parametersFields(context); + boolean hasParameterFields = !fields.isEmpty(); + boolean hasCorrectParameterFields = areParametersFormedCorrectly(fields); + + if (!hasParameterFields) { + return o.stream().map(ParameterizedExtension::parameterResolver); + } + else if (hasCorrectParameterFields) { + return o.stream().map(ParameterizedExtension::contextFactory); + } + + return Stream.empty(); + } + + private static TestTemplateInvocationContext parameterResolver(Object[] objects) { + List parameterResolvers = singletonList(new ParameterResolver() { + @Override + public boolean supportsParameter(ParameterContext parameterContext, ExtensionContext extensionContext) + throws ParameterResolutionException { + final Executable declaringExecutable = parameterContext.getDeclaringExecutable(); + return declaringExecutable instanceof Constructor + && declaringExecutable.getParameterCount() == objects.length; + } + + @Override + public Object resolveParameter(ParameterContext parameterContext, ExtensionContext extensionContext) + throws ParameterResolutionException { + return objects[parameterContext.getIndex()]; + } + }); + + return templateWithExtensions(parameterResolvers); + } + + private static TestTemplateInvocationContext contextFactory(Object[] parameters) { + return templateWithExtensions(singletonList(new InjectionExtension(parameters))); + } + + private static class InjectionExtension implements TestInstancePostProcessor { + + private final Object[] parameters; + + public InjectionExtension(Object[] parameters) { + this.parameters = parameters; + } + + @Override + public void postProcessTestInstance(Object testInstance, ExtensionContext context) throws Exception { + List parameters = parametersFields(context); + + if (!parameters.isEmpty() && parameters.size() != this.parameters.length) { + throw unMatchedAmountOfParametersException(); + } + + for (Field param : parameters) { + Parameterized.Parameter annotation = param.getAnnotation(Parameterized.Parameter.class); + int paramIndex = annotation.value(); + param.set(testInstance, this.parameters[paramIndex]); + } + } + + } + + private static TestTemplateInvocationContext templateWithExtensions(List extensions) { + return new TestTemplateInvocationContext() { + @Override + public List getAdditionalExtensions() { + return extensions; + } + }; + } + + private static boolean hasArgsConstructor(ExtensionContext context) { + // @formatter:off + return context.getTestClass() + .map(ReflectionUtils::getDeclaredConstructor) + .filter(c -> c.getParameterCount() > 0) + .isPresent(); + // @formatter:on + } + + private static List parametersFields(ExtensionContext context) { + // @formatter:off + Stream fieldStream = context.getTestClass() + .map(Class::getDeclaredFields) + .map(Stream::of) + .orElse(Stream.empty()); + // @formatter:on + + return fieldStream.filter(f -> f.isAnnotationPresent(Parameterized.Parameter.class)).filter( + ReflectionUtils::isPublic).collect(toList()); + } + + private static ParameterResolutionException unMatchedAmountOfParametersException() { + return new ParameterResolutionException( + "The amount of parametersFields in the constructor doesn't match those in the provided parametersFields"); + } + + private static Collection convertParametersMethodReturnType(Object obj) { + return CollectionUtils.toStream(obj).map(o -> { + if (o instanceof Object[]) { + return (Object[]) o; + } + return new Object[] { o }; + }).collect(toList()); + } + + private static class ParameterWrapper { + private final Optional> value; + + public ParameterWrapper(Optional> value) { + this.value = value; + } + + public Optional> getValue() { + return value; + } + } +} diff --git a/barcode/src/test/java/org/xbib/graphics/barcode/SymbolTest.java b/barcode/src/test/java/org/xbib/graphics/barcode/SymbolTest.java new file mode 100755 index 0000000..ef199be --- /dev/null +++ b/barcode/src/test/java/org/xbib/graphics/barcode/SymbolTest.java @@ -0,0 +1,608 @@ +package org.xbib.graphics.barcode; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertTrue; +import static org.junit.jupiter.api.Assertions.fail; +import com.google.zxing.BinaryBitmap; +import com.google.zxing.DecodeHintType; +import com.google.zxing.LuminanceSource; +import com.google.zxing.Reader; +import com.google.zxing.ReaderException; +import com.google.zxing.Result; +import com.google.zxing.client.j2se.BufferedImageLuminanceSource; +import com.google.zxing.common.HybridBinarizer; +import com.google.zxing.oned.CodaBarReader; +import com.google.zxing.oned.Code39Reader; +import com.google.zxing.oned.Code93Reader; +import com.google.zxing.oned.EAN13Reader; +import com.google.zxing.oned.EAN8Reader; +import com.google.zxing.oned.UPCAReader; +import com.google.zxing.oned.UPCEReader; +import com.google.zxing.pdf417.PDF417Reader; +import com.google.zxing.qrcode.QRCodeReader; +import org.junit.jupiter.api.TestTemplate; +import org.junit.jupiter.api.extension.ExtendWith; +import org.junit.runners.Parameterized; +import org.reflections.Reflections; +import org.xbib.graphics.barcode.render.GraphicsRenderer; +import java.awt.Color; +import java.awt.Font; +import java.awt.FontFormatException; +import java.awt.Graphics2D; +import java.awt.GraphicsEnvironment; +import java.awt.image.BufferedImage; +import java.io.File; +import java.io.FilenameFilter; +import java.io.IOException; +import java.io.InputStream; +import java.io.PrintWriter; +import java.lang.reflect.InvocationTargetException; +import java.lang.reflect.Method; +import java.nio.ByteBuffer; +import java.nio.CharBuffer; +import java.nio.charset.CharacterCodingException; +import java.nio.charset.Charset; +import java.nio.charset.CharsetDecoder; +import java.nio.charset.CodingErrorAction; +import java.nio.charset.StandardCharsets; +import java.nio.file.Files; +import java.util.ArrayList; +import java.util.Collections; +import java.util.LinkedHashMap; +import java.util.List; +import java.util.Map; +import java.util.Objects; +import java.util.Set; +import java.util.logging.Logger; +import javax.imageio.ImageIO; + +/** + * Scans the test resources for file-based bar code tests. + * + * Tests that verify successful behavior will contain the following sets of files: + * + *
+ *   /src/test/resources/uk/org/okapibarcode/backend/[symbol-name]/[test-name].properties (bar code initialization attributes)
+ *   /src/test/resources/uk/org/okapibarcode/backend/[symbol-name]/[test-name].codewords  (expected intermediate coding of the bar code)
+ *   /src/test/resources/uk/org/okapibarcode/backend/[symbol-name]/[test-name].png        (expected final rendering of the bar code)
+ * 
+ * + *

+ * Tests that verify error conditions will contain the following sets of files: + * + *

+ *   /src/test/resources/uk/org/okapibarcode/backend/[symbol-name]/[test-name].properties (bar code initialization attributes)
+ *   /src/test/resources/uk/org/okapibarcode/backend/[symbol-name]/[test-name].error      (expected error message)
+ * 
+ * + * If a properties file is found with no matching expectation files, we assume that it was recently added to the test suite and + * that we need to generate suitable expectation files for it. + * + * A single properties file can contain multiple test configurations (separated by an empty line), as long as the expected output + * is the same for all of those tests. + */ +@ExtendWith(ParameterizedExtension.class) +public class SymbolTest { + + /** The font used to render human-readable text when drawing the symbologies; allows for consistent results across operating systems. */ + private static Font DEJA_VU_SANS; + + /** The type of symbology being tested. */ + private final Class< ? extends Symbol> symbolType; + + /** The test configuration properties. */ + private final Map< String, String > properties; + + /** The file containing the expected intermediate coding of the bar code, if this test verifies successful behavior. */ + private final File codewordsFile; + + /** The file containing the expected final rendering of the bar code, if this test verifies successful behavior. */ + private final File pngFile; + + /** The file containing the expected error message, if this test verifies a failure. */ + private final File errorFile; + + /** + * Finds all test resources and returns the information that JUnit needs to dynamically create the corresponding test cases. + * + * @return the test data needed to dynamically create the test cases + * @throws IOException if there is an error reading a file + */ + @Parameterized.Parameters + public static List< Object[] > data() throws IOException { + String path = "/org/xbib/graphics/barcode/fonts/OkapiDejaVuSans.ttf"; + try { + InputStream is = SymbolTest.class.getResourceAsStream(path); + DEJA_VU_SANS = Font.createFont(Font.TRUETYPE_FONT, is); + GraphicsEnvironment ge = GraphicsEnvironment.getLocalGraphicsEnvironment(); + boolean registered = ge.registerFont(DEJA_VU_SANS); + assertTrue(registered); + } catch (IOException | FontFormatException e) { + throw new IOException(e.getMessage(), e); + } + String backend = "org.xbib.graphics.barcode"; + Reflections reflections = new Reflections(backend); + Set< Class< ? extends Symbol >> symbols = reflections.getSubTypesOf(Symbol.class); + List< Object[] > data = new ArrayList<>(); + for (Class< ? extends Symbol > symbol : symbols) { + String symbolName = symbol.getSimpleName().toLowerCase(); + String dir = "src/test/resources/" + backend.replace('.', '/') + "/" + symbolName; + for (File file : getPropertiesFiles(dir)) { + String fileBaseName = file.getName().replaceAll(".properties", ""); + File codewordsFile = new File(file.getParentFile(), fileBaseName + ".codewords"); + File pngFile = new File(file.getParentFile(), fileBaseName + ".png"); + File errorFile = new File(file.getParentFile(), fileBaseName + ".error"); + for (Map< String, String > properties : readProperties(file)) { + data.add(new Object[] { symbol, properties, codewordsFile, pngFile, errorFile }); + } + } + } + return data; + } + /** + * Creates a new test. + * + * @param symbolType the type of symbol being tested + * @param properties the test configuration properties + * @param codewordsFile the file containing the expected intermediate coding of the bar code, if this test verifies successful behavior + * @param pngFile the file containing the expected final rendering of the bar code, if this test verifies successful behavior + * @param errorFile the file containing the expected error message, if this test verifies a failure + */ + public SymbolTest(Class< ? extends Symbol > symbolType, + Map< String, String > properties, + File codewordsFile, + File pngFile, + File errorFile) { + this.symbolType = symbolType; + this.properties = properties; + this.codewordsFile = codewordsFile; + this.pngFile = pngFile; + this.errorFile = errorFile; + } + + /** + * Runs the test. If there are no expectation files yet, we generate them instead of checking against them. + * + * @throws Exception if any error occurs during the test + */ + @TestTemplate + public void test() throws Exception { + Symbol symbol = symbolType.getDeclaredConstructor().newInstance(); + symbol.setFontName(DEJA_VU_SANS.getFontName()); + try { + setProperties(symbol, properties); + } catch (InvocationTargetException e) { + symbol.errorMsg.append(e.getCause().getMessage()); + } + if (codewordsFile.exists()) { + verifySuccess(symbol); + } else if (errorFile.exists()) { + verifyError(symbol); + } + if (!pngFile.exists()) { + generateExpectationFiles(symbol); + } + } + + /** + * Verifies that the specified symbol was encoded and rendered in a way that matches expectations. + * + * @param symbol the symbol to check + * @throws IOException if there is any I/O error + * @throws ReaderException if ZXing has an issue decoding the barcode image + */ + private void verifySuccess(Symbol symbol) throws IOException, ReaderException { + if (symbol.errorMsg.length() > 0) { + fail("got error message: " + symbol.errorMsg); + } + List< String > expectedList = Files.readAllLines(codewordsFile.toPath(), StandardCharsets.UTF_8); + try { + // try to verify codewords + int[] actualCodewords = symbol.getCodewords(); + assertEquals(expectedList.size(), actualCodewords.length); + for (int i = 0; i < actualCodewords.length; i++) { + int expected = getInt(expectedList.get(i)); + int actual = actualCodewords[i]; + assertEquals(expected, actual, "at codeword index " + i); + } + } catch (UnsupportedOperationException e) { + // codewords aren't supported, try to verify patterns + String[] actualPatterns = symbol.pattern; + if (actualPatterns != null) { + assertEquals(expectedList.size(), actualPatterns.length); + for (int i = 0; i < actualPatterns.length; i++) { + String expected = expectedList.get(i); + String actual = actualPatterns[i]; + assertEquals(expected, actual, "at pattern index " + i); + } + } + } + // make sure the barcode images match + if (pngFile.exists()) { + BufferedImage expected = ImageIO.read(pngFile); + BufferedImage actual = draw(symbol, 10.0d); + if (expected != null && actual != null) { + assertEqualImage(pngFile.getName(), expected, actual); + } + // if possible, ensure an independent third party (ZXing) can read the generated barcode and agrees on what it represents + Reader zxingReader = findReader(symbol); + if (zxingReader != null && expected != null) { + LuminanceSource source = new BufferedImageLuminanceSource(expected); + BinaryBitmap bitmap = new BinaryBitmap(new HybridBinarizer(source)); + Map hints = Collections.singletonMap(DecodeHintType.PURE_BARCODE, Boolean.TRUE); + Result result = zxingReader.decode(bitmap, hints); + String zxingData = removeChecksum(result.getText(), symbol); + String ourData = removeStartStopChars(symbol.getContent(), symbol); + assertEquals(ourData, zxingData, "checking against ZXing results"); + } + } + } + + /** + * Returns a ZXing reader that can read the specified symbol. + * + * @param symbol the symbol to be read + * @return a ZXing reader that can read the specified symbol + */ + private static Reader findReader(Symbol symbol) { + if (symbol instanceof Code93) { + return new Code93Reader(); + } else if (symbol instanceof Code3Of9) { + return new Code39Reader(); + } else if (symbol instanceof Codabar) { + return new CodaBarReader(); + } else if (symbol instanceof QrCode) { + return new QRCodeReader(); + } else if (symbol instanceof Ean) { + Ean ean = (Ean) symbol; + if (ean.getMode() == Ean.Mode.EAN8) { + return new EAN8Reader(); + } else { + return new EAN13Reader(); + } + } else if (symbol instanceof Pdf417) { + Pdf417 pdf417 = (Pdf417) symbol; + if (pdf417.getMode() != Pdf417.Mode.MICRO) { + return new PDF417Reader(); + } + } else if (symbol instanceof Upc) { + Upc upc = (Upc) symbol; + if (upc.getMode() == Upc.Mode.UPCA) { + return new UPCAReader(); + } else { + return new UPCEReader(); + } + } + // no corresponding ZXing reader exists, or it behaves badly so we don't use it for testing + return null; + } + + /** + * Removes the checksum from the specified barcode content, according to the type of symbol that encoded the content. + * + * @param s the barcode content + * @param symbol the symbol which encoded the content + * @return the barcode content, without the checksum + */ + private static String removeChecksum(String s, Symbol symbol) { + if (symbol instanceof Ean || symbol instanceof Upc) { + return s.substring(0, s.length() - 1); + } else { + return s; + } + } + + /** + * Removes the start/stop characters from the specified barcode content, according to the type of symbol that encoded the + * content. + * + * @param s the barcode content + * @param symbol the symbol which encoded the content + * @return the barcode content, without the start/stop characters + */ + private static String removeStartStopChars(String s, Symbol symbol) { + if (symbol instanceof Codabar) { + return s.substring(1, s.length() - 1); + } else { + return s; + } + } + + /** + * Verifies that the specified symbol encountered the expected error during encoding. + * + * @param symbol the symbol to check + * @throws IOException if there is any I/O error + */ + private void verifyError(Symbol symbol) throws IOException { + String expectedError = Files.readAllLines(errorFile.toPath(), StandardCharsets.UTF_8).get(0); + assertTrue(symbol.errorMsg.toString().startsWith(expectedError)); + } + + /** + * Generates the expectation files for the specified symbol. + * + * @param symbol the symbol to generate expectation files for + * @throws IOException if there is any I/O error + */ + private void generateExpectationFiles(Symbol symbol) throws IOException { + if (symbol.errorMsg != null && symbol.errorMsg.length() > 0) { + generateErrorExpectationFile(symbol); + } else { + generateCodewordsExpectationFile(symbol); + generatePngExpectationFile(symbol); + } + } + + /** + * Generates the error expectation file for the specified symbol. + * + * @param symbol the symbol to generate the error expectation file for + * @throws IOException if there is any I/O error + */ + private void generateErrorExpectationFile(Symbol symbol) throws IOException { + if (!errorFile.exists()) { + PrintWriter writer = new PrintWriter(errorFile); + writer.println(symbol.errorMsg); + writer.close(); + } + } + + /** + * Generates the codewords expectation file for the specified symbol. + * + * @param symbol the symbol to generate codewords for + * @throws IOException if there is any I/O error + */ + private void generateCodewordsExpectationFile(Symbol symbol) throws IOException { + if (!codewordsFile.exists()) { + PrintWriter writer = new PrintWriter(codewordsFile); + try { + int[] codewords = symbol.getCodewords(); + for (int codeword : codewords) { + writer.println(codeword); + } + } catch (UnsupportedOperationException e) { + for (String pattern : symbol.pattern) { + writer.println(pattern); + } + } + writer.close(); + } + } + + /** + * Generates the image expectation file for the specified symbol. + * + * @param symbol the symbol to draw + * @throws IOException if there is any I/O error + */ + private void generatePngExpectationFile(Symbol symbol) throws IOException { + BufferedImage img = draw(symbol, 10.0d); + if (img != null) { + ImageIO.write(img, "png", pngFile); + } + } + + /** + * Returns the integer contained in the specified string. If the string contains a tab character, it and everything after it + * is ignored. + * + * @param s the string to extract the integer from + * @return the integer contained in the specified string + */ + private static int getInt(String s) { + int i = s.indexOf('\t'); + if (i != -1) { + s = s.substring(0, i); + } + return Integer.parseInt(s); + } + + /** + * Draws the specified symbol and returns the resultant image. + * + * @param symbol the symbol to draw + * @return the resultant image + */ + private static BufferedImage draw(Symbol symbol, double scalingFactor) { + int width = (int) (symbol.getWidth() * scalingFactor); + int height = (int) (symbol.getHeight() * scalingFactor); + if (width > 0 && height > 0) { + BufferedImage img = new BufferedImage(width, height, BufferedImage.TYPE_BYTE_GRAY); + Graphics2D g2d = img.createGraphics(); + g2d.setPaint(Color.WHITE); + g2d.fillRect(0, 0, width, height); + GraphicsRenderer renderer = new GraphicsRenderer(g2d, scalingFactor, Color.WHITE, Color.BLACK, true, true); + renderer.render(symbol); + g2d.dispose(); + return img; + } + return null; + } + + /** + * Initializes the specified symbol using the specified properties, where keys are attribute names and values are attribute + * values. + * + * @param symbol the symbol to initialize + * @param properties the attribute names and values to set + * @throws ReflectiveOperationException if there is any reflection error + */ + private static void setProperties(Symbol symbol, Map< String, String > properties) throws ReflectiveOperationException { + for (Map.Entry< String, String > entry : properties.entrySet()) { + String name = entry.getKey(); + String value = entry.getValue(); + String setterName = "set" + name.substring(0, 1).toUpperCase() + name.substring(1); + Method setter = getMethod(symbol.getClass(), setterName); + invoke(symbol, setter, value); + } + } + + /** + * Returns the method with the specified name in the specified class, or throws an exception if the specified method cannot be + * found. + * + * @param clazz the class to search in + * @param name the name of the method to search for + * @return the method with the specified name in the specified class + */ + private static Method getMethod(Class< ? > clazz, String name) { + for (Method method : clazz.getMethods()) { + if (method.getName().equals(name)) { + return method; + } + } + throw new IllegalArgumentException("Unable to find method: " + name); + } + + /** + * Invokes the specified method on the specified object with the specified parameter. + * + * @param object the object to invoke the method on + * @param setter the method to invoke + * @param parameter the parameter to pass to the method + * @throws ReflectiveOperationException if there is any reflection error + * @throws IllegalArgumentException if the specified parameter is not valid + */ + @SuppressWarnings("unchecked") + private static < E extends Enum< E >> void invoke(Object object, Method setter, Object parameter) + throws ReflectiveOperationException, IllegalArgumentException { + Class< ? > paramType = setter.getParameterTypes()[0]; + if (String.class.equals(paramType)) { + setter.invoke(object, parameter.toString()); + } else if (boolean.class.equals(paramType)) { + setter.invoke(object, Boolean.valueOf(parameter.toString())); + } else if (int.class.equals(paramType)) { + setter.invoke(object, Integer.parseInt(parameter.toString())); + } else if (double.class.equals(paramType)) { + setter.invoke(object, Double.parseDouble(parameter.toString())); + } else if (Character.class.equals(paramType)) { + setter.invoke(object, parameter.toString().charAt(0)); + } else if (paramType.isEnum()) { + Class< E > e = (Class< E >) paramType; + setter.invoke(object, Enum.valueOf(e, parameter.toString())); + } else { + throw new IllegalArgumentException("Unknown setter type: " + paramType); + } + } + + /** + * Returns all .properties files in the specified directory, or an empty array if none are found. + * + * @param dir the directory to search in + * @return all .properties files in the specified directory, or an empty array if none are found + */ + private static File[] getPropertiesFiles(String dir) { + File[] files = new File(dir).listFiles(new FilenameFilter() { + @Override + public boolean accept(File dir, String name) { + return name.endsWith(".properties"); + } + }); + return Objects.requireNonNullElseGet(files, () -> new File[0]); + } + + /** + * Verifies that the specified images match. + * + * @param expected the expected image to check against + * @param actual the actual image + */ + private static void assertEqualImage(String name, BufferedImage expected, BufferedImage actual) { + int w = expected.getWidth(); + int h = expected.getHeight(); + assertEquals(w, actual.getWidth(), "width"); + assertEquals(h, actual.getHeight(), "height"); + int[] expectedPixels = new int[w * h]; + expected.getRGB(0, 0, w, h, expectedPixels, 0, w); + int[] actualPixels = new int[w * h]; + actual.getRGB(0, 0, w, h, actualPixels, 0, w); + for (int i = 0; i < expectedPixels.length; i++) { + int expectedPixel = expectedPixels[i]; + int actualPixel = actualPixels[i]; + if (expectedPixel != actualPixel) { + int x = i % w; + int y = i / w; + fail(name + ": pixel mismatch at " + x + ", " + y + " " + + Integer.toHexString(expectedPixel) + " " + Integer.toHexString(actualPixel)); + } + } + } + + /** + * Extracts test configuration properties from the specified properties file. A single properties file can contain + * configuration properties for multiple tests. + * + * @param propertiesFile the properties file to read + * @return the test configuration properties in the specified file + * @throws IOException if there is an error reading the properties file + */ + private static List> readProperties(File propertiesFile) throws IOException { + String content; + try { + byte[] bytes = Files.readAllBytes(propertiesFile.toPath()); + content = replacePlaceholders(decode(bytes, StandardCharsets.UTF_8)); + } catch (CharacterCodingException e) { + throw new IOException("Invalid UTF-8 content in file " + propertiesFile.getAbsolutePath(), e); + } + + String eol = System.lineSeparator(); + String[] lines = content.split(eol); + + List< Map< String, String > > allProperties = new ArrayList<>(); + Map< String, String > properties = new LinkedHashMap<>(); + + for (String line : lines) { + if (line.isEmpty()) { + // an empty line signals the start of a new test configuration within this single file + if (!properties.isEmpty()) { + allProperties.add(properties); + properties = new LinkedHashMap<>(); + } + } else if (!line.startsWith("#")) { + int index = line.indexOf('='); + if (index != -1) { + String name = line.substring(0, index); + String value = line.substring(index + 1); + properties.put(name, value); + } else { + throw new IOException(propertiesFile.getAbsolutePath() + ": found line without '=' character; unintentional newline?"); + } + } + } + if (!properties.isEmpty()) { + allProperties.add(properties); + } + return allProperties; + } + + /** + * Equivalent to {@link String#String(byte[], Charset)}, + * except that encoding errors result in exceptions instead of + * silent character replacement. + * + * @param bytes the bytes to decode + * @param charset the character set use to decode the bytes + * @return the specified bytes, as a string + * @throws CharacterCodingException if there is an error decoding the specified bytes + */ + private static String decode(byte[] bytes, Charset charset) throws CharacterCodingException { + CharsetDecoder decoder = charset.newDecoder(); + decoder.onMalformedInput(CodingErrorAction.REPORT); + decoder.onUnmappableCharacter(CodingErrorAction.REPORT); + CharBuffer chars = decoder.decode(ByteBuffer.wrap(bytes)); + return chars.toString(); + } + + /** + * Replaces any special placeholders supported in test properties files with their raw values. + * + * @param s the string to check for placeholders + * @return the specified string, with placeholders replaced + */ + private static String replacePlaceholders(String s) { + return s.replaceAll("\\\\r", "\r") // "\r" -> CR + .replaceAll("\\\\n", "\n"); // "\n" -> LF + } +} diff --git a/barcode/src/test/java/org/xbib/graphics/barcode/output/Code39Test.java b/barcode/src/test/java/org/xbib/graphics/barcode/output/Code39Test.java new file mode 100644 index 0000000..c5e38bb --- /dev/null +++ b/barcode/src/test/java/org/xbib/graphics/barcode/output/Code39Test.java @@ -0,0 +1,59 @@ +package org.xbib.graphics.barcode.output; + +import org.junit.jupiter.api.Test; +import org.xbib.graphics.barcode.Code3Of9; +import org.xbib.graphics.barcode.HumanReadableLocation; +import org.xbib.graphics.barcode.render.GraphicsRenderer; +import java.awt.Color; +import java.awt.Graphics2D; +import java.awt.image.BufferedImage; +import java.io.IOException; +import java.io.OutputStream; +import java.nio.file.Files; +import java.nio.file.Paths; +import javax.imageio.ImageIO; + +public class Code39Test { + + @Test + public void createBarcode1() throws IOException { + Code3Of9 code3Of9 = new Code3Of9(); + code3Of9.setContent("20180123456"); + code3Of9.setHumanReadableLocation(HumanReadableLocation.BOTTOM); + // pixels = (mm * dpi) / 25.4 + double scalingFactor = (1.0d * 72.0d) / 25.4; + int width = (int) (code3Of9.getWidth() * scalingFactor); + int height = (int) (code3Of9.getHeight() * scalingFactor); + BufferedImage bufferedImage = new BufferedImage(width, height, BufferedImage.TYPE_BYTE_GRAY); + GraphicsRenderer renderer = createRenderer(bufferedImage, scalingFactor); + renderer.render(code3Of9); + renderer.close(); + OutputStream outputStream = Files.newOutputStream(Paths.get("build/barcode1.png")); + ImageIO.write(bufferedImage, "png", outputStream); + outputStream.close(); + } + + @Test + public void createBarcode2() throws IOException { + int width = 512; + int height = 150; + Code3Of9 code3Of9 = new Code3Of9(); + //code3Of9.setContent("20180123456"); + code3Of9.setContent("11111111111"); + BufferedImage bufferedImage = new BufferedImage(width, height, BufferedImage.TYPE_BYTE_GRAY); + GraphicsRenderer renderer = createRenderer(bufferedImage, 3.0d); + renderer.render(code3Of9); + renderer.close(); + OutputStream outputStream = Files.newOutputStream(Paths.get("build/barcode2.png")); + ImageIO.write(bufferedImage, "png", outputStream); + outputStream.close(); + } + + private GraphicsRenderer createRenderer(BufferedImage bufferedImage, double scalingFactor) { + Graphics2D g2d = bufferedImage.createGraphics(); + g2d.setPaint(Color.WHITE); + g2d.setBackground(Color.BLACK); + g2d.fillRect(0, 0, bufferedImage.getWidth(), bufferedImage.getHeight()); + return new GraphicsRenderer(g2d, scalingFactor, Color.WHITE, Color.BLACK, false, false); + } +} diff --git a/barcode/src/test/java/org/xbib/graphics/barcode/output/EPSRendererTest.java b/barcode/src/test/java/org/xbib/graphics/barcode/output/EPSRendererTest.java new file mode 100755 index 0000000..55187e5 --- /dev/null +++ b/barcode/src/test/java/org/xbib/graphics/barcode/output/EPSRendererTest.java @@ -0,0 +1,121 @@ +package org.xbib.graphics.barcode.output; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import org.junit.jupiter.api.AfterEach; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.xbib.graphics.barcode.Code93; +import org.xbib.graphics.barcode.render.GraphicsRenderer; +import org.xbib.graphics.barcode.MaxiCode; +import org.xbib.graphics.barcode.Symbol; +import org.xbib.graphics.io.vector.eps.EPSGraphics2D; +import java.awt.Color; +import java.io.BufferedReader; +import java.io.BufferedWriter; +import java.io.IOException; +import java.io.InputStream; +import java.io.StringReader; +import java.nio.charset.StandardCharsets; +import java.nio.file.Files; +import java.nio.file.Paths; +import java.util.Locale; + +public class EPSRendererTest { + + private Locale originalDefaultLocale; + + @BeforeEach + public void before() { + // ensure use of correct decimal separator (period), regardless of default locale + originalDefaultLocale = Locale.getDefault(); + Locale.setDefault(Locale.GERMANY); + } + + @AfterEach + public void after() { + Locale.setDefault(originalDefaultLocale); + } + + @Test + public void testCode93Basic() throws IOException { + Code93 code93 = new Code93(); + code93.setContent("123456789"); + test(code93, 1, Color.WHITE, Color.BLACK, 5, "code93-basic.eps"); + } + + @Test + public void testCode93Margin() throws IOException { + Code93 code93 = new Code93(); + code93.setContent("123456789"); + test(code93, 1, Color.WHITE, Color.BLACK, 20, "code93-margin-size-20.eps"); + } + + @Test + public void testCode93Magnification() throws IOException { + Code93 code93 = new Code93(); + code93.setContent("123456789"); + test(code93, 2, Color.WHITE, Color.BLACK, 5, "code93-magnification-2.eps"); + } + + @Test + public void testCode93Colors() throws IOException { + Code93 code93 = new Code93(); + code93.setContent("123456789"); + test(code93, 1, Color.GREEN, Color.RED, 5, "code93-colors.eps"); + } + + @Test + public void testCode93CustomFont() throws IOException { + Code93 code93 = new Code93(); + code93.setFontName("Arial"); + code93.setFontSize(26); + code93.setContent("123456789"); + test(code93, 1, Color.WHITE, Color.BLACK, 5, "code93-custom-font.eps"); + } + + @Test + public void testMaxiCodeBasic() throws IOException { + MaxiCode maxicode = new MaxiCode(); + maxicode.setMode(4); + maxicode.setContent("123456789"); + test(maxicode, 5, Color.WHITE, Color.BLACK, 5, "maxicode-basic.eps"); + } + + private void test(Symbol symbol, + double magnification, + Color paper, + Color ink, + int margin, + String expectationFile) throws IOException { + symbol.setQuietZoneHorizontal(margin); + symbol.setQuietZoneVertical(margin); + int width = (int) (symbol.getWidth() * magnification); + int height = (int) (symbol.getHeight() * magnification); + EPSGraphics2D epsGraphics2D = new EPSGraphics2D(0, 0, width, height); + GraphicsRenderer graphicsRenderer = new GraphicsRenderer(epsGraphics2D, magnification, paper, ink, false, false); + graphicsRenderer.render(symbol); + graphicsRenderer.close(); + byte[] actualBytes = epsGraphics2D.getBytes(); + String actual = new String(actualBytes, StandardCharsets.UTF_8); + try (BufferedWriter bufferedWriter = Files.newBufferedWriter(Paths.get("build/" + expectationFile))) { + bufferedWriter.write(actual); + } + BufferedReader actualReader = new BufferedReader(new StringReader(actual)); + InputStream is = getClass().getResourceAsStream(expectationFile); + if (is != null) { + byte[] expectedBytes = new byte[is.available()]; + is.read(expectedBytes); + String expected = new String(expectedBytes, StandardCharsets.UTF_8); + BufferedReader expectedReader = new BufferedReader(new StringReader(expected)); + int line = 1; + String actualLine = actualReader.readLine(); + String expectedLine = expectedReader.readLine(); + while (actualLine != null && expectedLine != null) { + assertEquals(expectedLine, actualLine, "Line " + line); + actualLine = actualReader.readLine(); + expectedLine = expectedReader.readLine(); + line++; + } + } + } +} diff --git a/barcode/src/test/java/org/xbib/graphics/barcode/output/PDFRendererTest.java b/barcode/src/test/java/org/xbib/graphics/barcode/output/PDFRendererTest.java new file mode 100755 index 0000000..f0f3833 --- /dev/null +++ b/barcode/src/test/java/org/xbib/graphics/barcode/output/PDFRendererTest.java @@ -0,0 +1,121 @@ +package org.xbib.graphics.barcode.output; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import org.junit.jupiter.api.AfterEach; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.xbib.graphics.barcode.Code93; +import org.xbib.graphics.barcode.MaxiCode; +import org.xbib.graphics.barcode.Symbol; +import org.xbib.graphics.barcode.render.GraphicsRenderer; +import org.xbib.graphics.io.vector.pdf.PDFGraphics2D; +import java.awt.Color; +import java.io.BufferedReader; +import java.io.BufferedWriter; +import java.io.IOException; +import java.io.InputStream; +import java.io.StringReader; +import java.nio.charset.StandardCharsets; +import java.nio.file.Files; +import java.nio.file.Paths; +import java.util.Locale; + +public class PDFRendererTest { + + private Locale originalDefaultLocale; + + @BeforeEach + public void before() { + // ensure use of correct decimal separator (period), regardless of default locale + originalDefaultLocale = Locale.getDefault(); + Locale.setDefault(Locale.GERMANY); + } + + @AfterEach + public void after() { + Locale.setDefault(originalDefaultLocale); + } + + @Test + public void testCode93Basic() throws IOException { + Code93 code93 = new Code93(); + code93.setContent("123456789"); + test(code93, 1, Color.WHITE, Color.BLACK, 5, "code93-basic.pdf"); + } + + @Test + public void testCode93Margin() throws IOException { + Code93 code93 = new Code93(); + code93.setContent("123456789"); + test(code93, 1, Color.WHITE, Color.BLACK, 20, "code93-margin-size-20.pdf"); + } + + @Test + public void testCode93Magnification() throws IOException { + Code93 code93 = new Code93(); + code93.setContent("123456789"); + test(code93, 2, Color.WHITE, Color.BLACK, 5, "code93-magnification-2.pdf"); + } + + @Test + public void testCode93Colors() throws IOException { + Code93 code93 = new Code93(); + code93.setContent("123456789"); + test(code93, 1, Color.GREEN, Color.RED, 5, "code93-colors.pdf"); + } + + @Test + public void testCode93CustomFont() throws IOException { + Code93 code93 = new Code93(); + code93.setFontName("Arial"); + code93.setFontSize(26); + code93.setContent("123456789"); + test(code93, 1, Color.WHITE, Color.BLACK, 5, "code93-custom-font.pdf"); + } + + @Test + public void testMaxiCodeBasic() throws IOException { + MaxiCode maxicode = new MaxiCode(); + maxicode.setMode(4); + maxicode.setContent("123456789"); + test(maxicode, 5, Color.WHITE, Color.BLACK, 5, "maxicode-basic.pdf"); + } + + private void test(Symbol symbol, + double magnification, + Color paper, + Color ink, + int margin, + String expectationFile) throws IOException { + symbol.setQuietZoneHorizontal(margin); + symbol.setQuietZoneVertical(margin); + int width = (int) (symbol.getWidth() * magnification); + int height = (int) (symbol.getHeight() * magnification); + PDFGraphics2D pdfGraphics2D = new PDFGraphics2D(0, 0, width, height); + GraphicsRenderer graphicsRenderer = new GraphicsRenderer(pdfGraphics2D, magnification, paper, ink, false, false); + graphicsRenderer.render(symbol); + graphicsRenderer.close(); + byte[] actualBytes = pdfGraphics2D.getBytes(); + String actual = new String(actualBytes, StandardCharsets.UTF_8); + try (BufferedWriter bufferedWriter = Files.newBufferedWriter(Paths.get("build/" + expectationFile))) { + bufferedWriter.write(actual); + } + BufferedReader actualReader = new BufferedReader(new StringReader(actual)); + InputStream is = getClass().getResourceAsStream(expectationFile); + if (is != null) { + byte[] expectedBytes = new byte[is.available()]; + is.read(expectedBytes); + String expected = new String(expectedBytes, StandardCharsets.UTF_8); + BufferedReader expectedReader = new BufferedReader(new StringReader(expected)); + int line = 1; + String actualLine = actualReader.readLine(); + String expectedLine = expectedReader.readLine(); + while (actualLine != null && expectedLine != null) { + assertEquals(expectedLine, actualLine, "Line " + line); + actualLine = actualReader.readLine(); + expectedLine = expectedReader.readLine(); + line++; + } + } + } +} diff --git a/barcode/src/test/java/org/xbib/graphics/barcode/output/SvgRendererTest.java b/barcode/src/test/java/org/xbib/graphics/barcode/output/SvgRendererTest.java new file mode 100755 index 0000000..d97ee80 --- /dev/null +++ b/barcode/src/test/java/org/xbib/graphics/barcode/output/SvgRendererTest.java @@ -0,0 +1,121 @@ +package org.xbib.graphics.barcode.output; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import org.junit.jupiter.api.AfterEach; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.xbib.graphics.barcode.Code93; +import org.xbib.graphics.barcode.MaxiCode; +import org.xbib.graphics.barcode.Symbol; +import org.xbib.graphics.barcode.render.GraphicsRenderer; +import org.xbib.graphics.io.vector.svg.SVGGraphics2D; +import java.awt.Color; +import java.io.BufferedReader; +import java.io.BufferedWriter; +import java.io.IOException; +import java.io.InputStream; +import java.io.StringReader; +import java.nio.charset.StandardCharsets; +import java.nio.file.Files; +import java.nio.file.Paths; +import java.util.Locale; + +public class SvgRendererTest { + + private Locale originalDefaultLocale; + + @BeforeEach + public void before() { + // ensure use of correct decimal separator (period), regardless of default locale + originalDefaultLocale = Locale.getDefault(); + Locale.setDefault(Locale.GERMANY); + } + + @AfterEach + public void after() { + Locale.setDefault(originalDefaultLocale); + } + + @Test + public void testCode93Basic() throws IOException { + Code93 code93 = new Code93(); + code93.setContent("123456789"); + test(code93, 1, Color.WHITE, Color.BLACK, 5, "code93-basic.svg"); + } + + @Test + public void testCode93Margin() throws IOException { + Code93 code93 = new Code93(); + code93.setContent("123456789"); + test(code93, 1, Color.WHITE, Color.BLACK, 20, "code93-margin-size-20.svg"); + } + + @Test + public void testCode93Magnification() throws IOException { + Code93 code93 = new Code93(); + code93.setContent("123456789"); + test(code93, 2, Color.WHITE, Color.BLACK, 5, "code93-magnification-2.svg"); + } + + @Test + public void testCode93Colors() throws IOException { + Code93 code93 = new Code93(); + code93.setContent("123456789"); + test(code93, 1, Color.GREEN, Color.RED, 5, "code93-colors.svg"); + } + + @Test + public void testCode93CustomFont() throws IOException { + Code93 code93 = new Code93(); + code93.setFontName("Arial"); + code93.setFontSize(26); + code93.setContent("123456789"); + test(code93, 1, Color.WHITE, Color.BLACK, 5, "code93-custom-font.svg"); + } + + @Test + public void testMaxiCodeBasic() throws IOException { + MaxiCode maxicode = new MaxiCode(); + maxicode.setMode(4); + maxicode.setContent("123456789"); + test(maxicode, 1.0, Color.WHITE, Color.BLACK, 5, "maxicode-basic.svg"); + } + + private void test(Symbol symbol, + double magnification, + Color paper, + Color ink, + int margin, + String expectationFile) throws IOException { + symbol.setQuietZoneHorizontal(margin); + symbol.setQuietZoneVertical(margin); + int width = (int) (symbol.getWidth() * magnification); + int height = (int) (symbol.getHeight() * magnification); + SVGGraphics2D svgGraphics2D = new SVGGraphics2D(0, 0, width, height); + GraphicsRenderer graphicsRenderer = new GraphicsRenderer(svgGraphics2D, magnification, paper, ink, false, false); + graphicsRenderer.render(symbol); + graphicsRenderer.close(); + byte[] actualBytes = svgGraphics2D.getBytes(); + String actual = new String(actualBytes, StandardCharsets.UTF_8); + try (BufferedWriter bufferedWriter = Files.newBufferedWriter(Paths.get("build/" + expectationFile))) { + bufferedWriter.write(actual); + } + BufferedReader actualReader = new BufferedReader(new StringReader(actual)); + InputStream is = getClass().getResourceAsStream(expectationFile); + if (is != null) { + byte[] expectedBytes = new byte[is.available()]; + is.read(expectedBytes); + String expected = new String(expectedBytes, StandardCharsets.UTF_8); + BufferedReader expectedReader = new BufferedReader(new StringReader(expected)); + int line = 1; + String actualLine = actualReader.readLine(); + String expectedLine = expectedReader.readLine(); + while (actualLine != null && expectedLine != null) { + assertEquals(expectedLine, actualLine, "Line " + line); + actualLine = actualReader.readLine(); + expectedLine = expectedReader.readLine(); + line++; + } + } + } +} diff --git a/barcode/src/test/resources/org/xbib/graphics/barcode/codabar/basic.codewords b/barcode/src/test/resources/org/xbib/graphics/barcode/codabar/basic.codewords new file mode 100755 index 0000000..7a53df3 --- /dev/null +++ b/barcode/src/test/resources/org/xbib/graphics/barcode/codabar/basic.codewords @@ -0,0 +1,18 @@ +12121121 +11112211 +11122111 +11121121 +21112121 +22111111 +21212111 +11211211 +11221111 +21111211 +21211121 +12111121 +11212121 +12112111 +12211111 +21121111 +11111221 +11121221 diff --git a/barcode/src/test/resources/org/xbib/graphics/barcode/codabar/basic.png b/barcode/src/test/resources/org/xbib/graphics/barcode/codabar/basic.png new file mode 100644 index 0000000000000000000000000000000000000000..ec4e4b1e90f2b951b64cb925c7481775b4515e74 GIT binary patch literal 41561 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 literal 0 HcmV?d00001 diff --git a/barcode/src/test/resources/org/xbib/graphics/barcode/codabar/basic.properties b/barcode/src/test/resources/org/xbib/graphics/barcode/codabar/basic.properties new file mode 100755 index 0000000..e66bd7b --- /dev/null +++ b/barcode/src/test/resources/org/xbib/graphics/barcode/codabar/basic.properties @@ -0,0 +1 @@ +content=B1-2:3.4$5/6+7890C diff --git a/barcode/src/test/resources/org/xbib/graphics/barcode/codabar/module-width-ratio-2.5.codewords b/barcode/src/test/resources/org/xbib/graphics/barcode/codabar/module-width-ratio-2.5.codewords new file mode 100755 index 0000000..7a53df3 --- /dev/null +++ b/barcode/src/test/resources/org/xbib/graphics/barcode/codabar/module-width-ratio-2.5.codewords @@ -0,0 +1,18 @@ +12121121 +11112211 +11122111 +11121121 +21112121 +22111111 +21212111 +11211211 +11221111 +21111211 +21211121 +12111121 +11212121 +12112111 +12211111 +21121111 +11111221 +11121221 diff --git a/barcode/src/test/resources/org/xbib/graphics/barcode/codabar/module-width-ratio-2.5.png b/barcode/src/test/resources/org/xbib/graphics/barcode/codabar/module-width-ratio-2.5.png new file mode 100644 index 0000000000000000000000000000000000000000..27d37a2ccd15bdec7197e9735b7cc2364cd483f0 GIT binary patch literal 47218 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 literal 0 HcmV?d00001 diff --git a/barcode/src/test/resources/org/xbib/graphics/barcode/code11/basic.properties b/barcode/src/test/resources/org/xbib/graphics/barcode/code11/basic.properties new file mode 100755 index 0000000..7ac46f2 --- /dev/null +++ b/barcode/src/test/resources/org/xbib/graphics/barcode/code11/basic.properties @@ -0,0 +1,4 @@ +content=01234-56789 + +checkDigitCount=2 +content=01234-56789 diff --git a/barcode/src/test/resources/org/xbib/graphics/barcode/code11/human-readable-location-none.codewords b/barcode/src/test/resources/org/xbib/graphics/barcode/code11/human-readable-location-none.codewords new file mode 100755 index 0000000..5fbc040 --- /dev/null +++ b/barcode/src/test/resources/org/xbib/graphics/barcode/code11/human-readable-location-none.codewords @@ -0,0 +1,15 @@ +112211 +111121 +211121 +121121 +221111 +112121 +112111 +212111 +122111 +111221 +211211 +211111 +112121 +221111 +112211 diff --git a/barcode/src/test/resources/org/xbib/graphics/barcode/code11/human-readable-location-none.png b/barcode/src/test/resources/org/xbib/graphics/barcode/code11/human-readable-location-none.png new file mode 100644 index 0000000000000000000000000000000000000000..e7bad66d02b8078e62f0637706295fa7ac80e1c5 GIT binary patch literal 19260 zcmeHP-Ahwp96pgmWp>j|w2QjxVnGqf8B${9(p=5_*fCnU1WR#q3^YH|T-HTpFzRNA z%#4z)?bsAQ4y9{WHxgM6oSS+`4MdivgYc`I)82QUzaa>oyWjKPJn!$v;eCG}&wCoO zGSd@wAK1+>OhU$aWe&r{Jz|(`Pj`|9=KES)Cc|9Vo}oOWsn)&zd|dzGG_(EV`lf%^ z+fVgZ_Quz$+-CDebff!4PQ}!i!Ke|k0|S0lpsuh($QIa@n#n9zpUF8NNaJMOROy{u zioT^Zmgf;;USN6Ash=*f&-HhXicYPpHreFVOei!f`c~e;o7jFAMd9azNo~Yf(aJc& z@T*7Sa$CGD3Jo8Y$!iI1T0kM2ilJ5COfyH8Yuy%}FjQ+LuHb;qCzErl305zmZJo1O zOfeJ*l?@TY;tkSW8c(?;E<<5QD8b4{9(>kO$CkeiC}JpDo>WYX0iA*Fl5NS8xP;Ep z#RtUq)SDgDv9+RU8$~l4z0KWHNN5}`9ogavh+{u*rxM>^bX}&7)ph?;Q&hHjZRm;= z5*mkB5|`dMzxC^ zGQ`_1^4tw?yZ*bUb?@_LzQ>(DRDZ8vGe6$E<3`e+KHtSjUbU+B1RcX-&(GWYxY_;& z{bifVD35l{9;9gQPw&YS#3&gRHTqna+Qiu-j(kIIiBVjB*TCPJQVMNqJ8LzMFFm1X zdHvh7CSvp$jB>#>;&{Uk*ZJE8R}Z^TGpXeIOfSWNBgdwCU%Hk~QH?n~bdeaMz*-4| z|Ek2LwN)mYUdksF*$b4`ew~fBQk0xhRqPlei52_K;d* zj~wRnsbfonj=M1w3WdKAgNB6clJ#;Dmrydgc$oOUFVIdMTf6B`iJ`^vsv;>QBP0$8 zKf;f%ABPVPADsQTdU5rVEDMF(a$oo-tkrEGO7ph(~9sY6C;j!-F{`)^F5~GRq Pw^$kHGL`LT3-12~d}|EN literal 0 HcmV?d00001 diff --git a/barcode/src/test/resources/org/xbib/graphics/barcode/code11/human-readable-location-none.properties b/barcode/src/test/resources/org/xbib/graphics/barcode/code11/human-readable-location-none.properties new file mode 100755 index 0000000..7d0abb7 --- /dev/null +++ b/barcode/src/test/resources/org/xbib/graphics/barcode/code11/human-readable-location-none.properties @@ -0,0 +1,2 @@ +humanReadableLocation=NONE +content=01234-56789 diff --git a/barcode/src/test/resources/org/xbib/graphics/barcode/code11/human-readable-location-top.codewords b/barcode/src/test/resources/org/xbib/graphics/barcode/code11/human-readable-location-top.codewords new file mode 100755 index 0000000..5fbc040 --- /dev/null +++ b/barcode/src/test/resources/org/xbib/graphics/barcode/code11/human-readable-location-top.codewords @@ -0,0 +1,15 @@ +112211 +111121 +211121 +121121 +221111 +112121 +112111 +212111 +122111 +111221 +211211 +211111 +112121 +221111 +112211 diff --git a/barcode/src/test/resources/org/xbib/graphics/barcode/code11/human-readable-location-top.png b/barcode/src/test/resources/org/xbib/graphics/barcode/code11/human-readable-location-top.png new file mode 100644 index 0000000000000000000000000000000000000000..737b50091a762aab23304d7a770111dcbe675125 GIT binary patch literal 27606 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 VXqVrc{)b(gHzsbV|8&of{|6@Te!2hv literal 0 HcmV?d00001 diff --git a/barcode/src/test/resources/org/xbib/graphics/barcode/code11/human-readable-location-top.properties b/barcode/src/test/resources/org/xbib/graphics/barcode/code11/human-readable-location-top.properties new file mode 100755 index 0000000..1274d28 --- /dev/null +++ b/barcode/src/test/resources/org/xbib/graphics/barcode/code11/human-readable-location-top.properties @@ -0,0 +1,2 @@ +humanReadableLocation=TOP +content=01234-56789 diff --git a/barcode/src/test/resources/org/xbib/graphics/barcode/code11/invalid-content.error b/barcode/src/test/resources/org/xbib/graphics/barcode/code11/invalid-content.error new file mode 100755 index 0000000..61fd553 --- /dev/null +++ b/barcode/src/test/resources/org/xbib/graphics/barcode/code11/invalid-content.error @@ -0,0 +1 @@ +Invalid characters in input diff --git a/barcode/src/test/resources/org/xbib/graphics/barcode/code11/invalid-content.properties b/barcode/src/test/resources/org/xbib/graphics/barcode/code11/invalid-content.properties new file mode 100755 index 0000000..6e8967c --- /dev/null +++ b/barcode/src/test/resources/org/xbib/graphics/barcode/code11/invalid-content.properties @@ -0,0 +1 @@ +content=01234+56789 diff --git a/barcode/src/test/resources/org/xbib/graphics/barcode/code11/module-width-ratio-2.5.codewords b/barcode/src/test/resources/org/xbib/graphics/barcode/code11/module-width-ratio-2.5.codewords new file mode 100755 index 0000000..5fbc040 --- /dev/null +++ b/barcode/src/test/resources/org/xbib/graphics/barcode/code11/module-width-ratio-2.5.codewords @@ -0,0 +1,15 @@ +112211 +111121 +211121 +121121 +221111 +112121 +112111 +212111 +122111 +111221 +211211 +211111 +112121 +221111 +112211 diff --git a/barcode/src/test/resources/org/xbib/graphics/barcode/code11/module-width-ratio-2.5.png b/barcode/src/test/resources/org/xbib/graphics/barcode/code11/module-width-ratio-2.5.png new file mode 100644 index 0000000000000000000000000000000000000000..b24b7e6a108bd23fc373bf0689853f5e69cf3968 GIT binary patch literal 29246 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 literal 0 HcmV?d00001 diff --git a/barcode/src/test/resources/org/xbib/graphics/barcode/code11/module-width-ratio-2.5.properties b/barcode/src/test/resources/org/xbib/graphics/barcode/code11/module-width-ratio-2.5.properties new file mode 100755 index 0000000..b436aa1 --- /dev/null +++ b/barcode/src/test/resources/org/xbib/graphics/barcode/code11/module-width-ratio-2.5.properties @@ -0,0 +1,2 @@ +moduleWidthRatio=2.5 +content=01234-56789 diff --git a/barcode/src/test/resources/org/xbib/graphics/barcode/code11/module-width-ratio-3.0.codewords b/barcode/src/test/resources/org/xbib/graphics/barcode/code11/module-width-ratio-3.0.codewords new file mode 100755 index 0000000..5fbc040 --- /dev/null +++ b/barcode/src/test/resources/org/xbib/graphics/barcode/code11/module-width-ratio-3.0.codewords @@ -0,0 +1,15 @@ +112211 +111121 +211121 +121121 +221111 +112121 +112111 +212111 +122111 +111221 +211211 +211111 +112121 +221111 +112211 diff --git a/barcode/src/test/resources/org/xbib/graphics/barcode/code11/module-width-ratio-3.0.png b/barcode/src/test/resources/org/xbib/graphics/barcode/code11/module-width-ratio-3.0.png new file mode 100644 index 0000000000000000000000000000000000000000..1b07e5c53b8f9930bb052351d6e30dcbe741c65a GIT binary patch literal 30414 zcmeHw`CC(0*R~xj?PHNzt5^rHrGTh_R)qp4sUso+bpj%dB4R>tqVJ)1%)Z0Sj^P-4ujxN z+&{|liZLZCLn$p+F=EHlWBM;eGANiL2T{~jub+9|94 zzR#4Ozn{qLzgOCr+EyERh{_nmB93X)dRr! z&n^Y_9&oe(Cnpg1fUpi^EkIHR+vieMkiKobGw$fanSkT>+viKy(F&t^m;$Ai4rXSAggW5MAjB zBj6P&;0-X~1vj9D0<=(o77EZp0a_@)o6i7K2E4cafAXsX683mOkBjH1!o)TxfNFpG z(55ea_H6#}%kl3%uCcK2T7CNY+qADA1nnjI?c96o=rQw@)|8{iJ~Q8I6F+dwjamEW z_|delIRExaE)1e6a&*`J;nO$j%qFRIA!_{R-tdGS$*|@tl{} zX=B>E8{3`@lGeq%9nC7_w<0@|hHW9ivS<>PY`OrvCAXF~Yi-yN7%z%s`m{ltw@;3DnO3X8`ad z0I>v+X#jx;kfs33{QniReuejX$Ah8^eq31djt7^kGF!PtJ03Kj5oMLkVZC0TBP$r6>X?S(Kek=uQK(x18Va+n696aq{iJq0D$q&x*Z1d$0duB;0 zJE)kb3oMMF^WnAp0FALf`s3}^@VcA)h73e+&Y0dLM<+;?>k1i6!3%GAxYxa-&iM%JKyH=pQ-sm4_CIPm7^v%YHtY-M*~2EX zFm=s4dAL`8eVHIGVOdr6I?JW>HU6bqK})8JKJA0oooB4>nWeV02Wle;ZY}TU1;M4%p-kVGfKOWJMIz8VdiRzHuR_J zXEaP~sncxU%-{+ex7M+qR=&*+4;v3tJs8Msj4!LCtsa2$=Pr@D(;m--jX}ms59HCK zq9KS^_r_?&f{g3FwE52&S2J2@jTJfy?Kj*W;jvxWKl=AwzUTWIqxnx?Z1ac44S8v_ zlh;EDgW~~@)9B;Zqaw#X8_1(lPy^8Qj9{!d%(bQxH4gILL2}>`ea?XQs#y*F;ZfI<$X^ciw8p_&A z1vjfvF%cx=0_i}Ws7b}^CoVSfA3_G~swg(toGk^F&kOt|&s?iW(sWb5XkJZPPuAeC z_S7l7u=9WiNZ271w`e=+3cVra5lbLDgMEpPl#5e(52xupT#Evshqfw{4*L4)LZ)8~ z{Mw)VeA`;51++g_X9RWmPh;Y8^8il8RJVjn-MloefgUfR2W3Yp(f;fkk(vWUsLnp# z*fT<7wgbV0hvM5_pnmT74A<6L16Pl&A8P@R5>&+*QBNJ3O0}2RYtiFNpRvi~-sr=4 zTs)8A&wW00+G~V^-j>sYIp@BwzbtBDd+gAIt3BA-3#ZWqq#*6dd zpu^VUgu^W(xp-z6*)CBX;1siTSu2HHUYp);VBkXoFNw6uDXP4^L#Dj_(t0ptPV5p_ z_ew`Nkdy;1n9!pgqDTA57R_gZUA(S24RwF@y|Ugp<$HXhk|fmaLiZl{JdiShWV=8_ zI+`tS3l5gCajoX#TLQhPUy5;z{lLCOc-9xI3)PyBZzJ^qw>YtpSwsd+m|@>fV<)t7x78|J&<4# zY7Df0oB9|6m)Vk@>O|)?KAC@9&NB*1+jXMf$2MRwQX*ao7N83VCqY>fFw_vS0V#V;||Yq~6|QE%x>e#!3p z3c(^*asl!B_55`7;zdM2@%2#SxIk3A(olZoCB7J{t_6iCIWKA2nRjgXH+c1_mfpwm zH@3Rtte@oD(S76LMjN?k%~qnwM~}rg(!j zYfRmp=ftfRCD&7o6*9r7M~%T*-$$?rf5eAH9LIUpMqC0`Vy@O?-{|j2hu4%KPv)_` zq&V;bGEU3IrOve%r4CP24JsjhT!HU4d?sv%mKOig-G<$=EtXtY`6P=3;cu(aw_A9h|LW&f(HPIv~I zcd|j27g+dleulW8Qq-)mFYUC>;~*&SbaM#F(mZjA(sEKwap*s`%k#eU%*G^D;_|hG zbkK!N5=M(ae8_%j?#B!pu8gZSILpd{>Cdr{O}e}Ubwzci?Sz<5;;x}s6R#REVf|z` ztuLbX5jwnBSv>8not>p__0R0Kx9g$55G?x79kSL6^0`GDc6BCLrM44xA{+AcP9{LM zX>Wr99c(hPUnZcnXS_B*DKU`T#KQ|j zL9w)ztH}*SrKG3egq{*~9y`FOc3@tRKcP4AcEtqO^bwkEvU}Oj@!&g?`jTt<)oF|t z(nx8`u`ONCxOmy+D!PkN_oRvWDB(l|l6=GIY^=s1}rDVyyr8juKP2_ap5 z3%-4qZz%1*DQ)`>21dOCg}4k2+lVTap{rSOnnr!=ST7-CFj)INnHfD-y1%&C`I!U9R6I!nl8}R@ykdbQ1(=e=`09Lo7C0w)Gi{_ zcY$}S&ypO#1sZ)gcBpc__^mDJE3rQ#sYZSI5r#Z;?c|DWC?ySl4_$%TB5i#D?k~LJ zl`b|kdL?$G7-r8a`tkYmuRT6(lL`b&wi`E&;+~i^Vp)SvwQmE2^U--<%(hc^4KJ8g z!^Bv=z3x|!!rtm~md1=U)IZzX!BhT?tJMiMS8KkN^zZaY$ZH^Z^sq=S`9saj~VoWrF=InK0b!nAi6zl{kQ2alQq~hZceX zIp>Myv~_+vr$Pm@!(zU1_da>93fHXtQF%lp878$YcFW?%Z!FMkU$UWU zntVe1oN|a$H#~no(P^nBY?k7n&#^p*PwB%HSlQ)NU272NRm#zWH(Px^Sn=aWc3DiD z=(zi4Puv~Sr^e}-h0gMf5awPkG&JYD#^A|$4E+7#)9C1kn|I@eH4EQNj^0;qruI!LlW)XjY?q1w=`qcjWI*x0_0tjU=+1detf&mv#(r})Y`bky@KQfqYHjn zY95D_c2)`wv(M<}V)4$d+Gt++Xu{Bg4I@aa)T>z1YeB&gyJpa~IFjEnZC@Rt+EAI| z_gq1HexLb^s&GVxx*^ywi7spALhbOhhsT|WOK>_QwM~1J;I?EZN484?%LBn# zt{5{L_}B$Scp&nBIOvav6=$Ij+i~v=JlT4+Rl~C^(~eFUbt`g=LXfVxa4IuMI=#=* zj}U%oqD;9V|M>IdT>fW#T$8nTOptK;;j&AthV;imr+PmJ&%k8oxo;uuNA}wi1QrbI zJ~ZFJ9qcM@RD;iA#u5a$!Q~?szWQx>iKPcc?5km~fy`hS3zOdJe|!g8KqHWG{4Jt; zieD~PZ8ljNv@$wdYPln^_5css(EX=kR6NC8HVoE`c3VIbn~?SM&k`k@qV`xpzJFMq zp3jo>Er(Yz8j-6LgS|;m;Z)S88O5oBwn0~&-v5_o^su>F*1JO>yDX8#oCkFtllB7 z*_4`vcB_N*!u{$%?ThX4Z>He7@b@Hg%{}Bkf||K_j0ba=*Q0UGN`U#>=>tpKyY1wE znWIN&1cp*(lxs1LOk%1yyGRQ}&SG0#CHPY6y?erLIHRW1h#p0BK^Qvxgnlp2+as_< zAlRdh7ox+o{SbEHCS4}vU1l)G){%fZb%k%7_^BvNd?9Y`o*0eNlq)u3JmR$1!O#(H zG;UhAhEb_eCG+4vB>N5Y`ZeUxS>)jNMuxhd`6ALcM*zHItZ%2Xx%1@7mA%9beQi~i?B zbyY8VUZJ1+8PAPQ+q28+RQdo8le=Erow@;WhiR_FtB#|uXWYy$VE4jaq4!w|aGZ=9 zqf9=e_x(=-wt{zTOoa7H{Fy!=07-9}bS*__3soMoE^|U9l<7}=iM(-D`_reFDPzM+ zLrKF!r6d*w9N(K3$Byqcz-4R87r(mu9GPoR&BqN2^Z-j(Irqgs30`q`BKBmX3yM`K z-c}MHrkDj`Pshm1$4XjD^1$VqiaT4n6^l^!wuKPw^Vvb8Tl7C!7+uonO(<3)H4tbU z(nMaUKFBvL$xXx))?;0MlEwJd2wzmIL}Qswq@b4lIt!Iqhx5d7pLEf&Ir^m1;Ztj7 z*C)?uY;|>oDa@zuRS|j@!MY+Lz#ep9$q33RQU}vOk$4}-@cc0EssH0LJ~rP8RT6>u z;;bwh@dqM!r~HnAeO@fr(L3o`mC>quNbzgRu5x3K;>-~6^om{BhAG32W@UN@DXDw+Hi zQM-fGD?`cKlilKIN^=R+B;lCm=Yh0fvh@UoW@ziZ*QgC!yXgXIaK>35?vg1BQ+0&g zGyA1onoFq9HyuogwGc`In+6b7i`7@+u_ARD4HS)6!>!nO2Q5syon`x1^Qg&wt})!( zNj7FH*>_-cGGnc{Pxrm@3FzX5CjR1b@EuyVWkncaduZcbk6`<@kgkLCetnk%lrbXq zWuxw!>6jk|ZbrZpFA&>Z%OE3vF%|@0^C0ajn|5D^pj_)!DU;>$69i6Gi2n79N!-dlE~+Y01(J=>}tEbq2om zmra`V31CG=iEUP)ecBAFCG|loiN49fo$@SOL|*PO`2kem6b^fCTgV7D4r=s{R(Oo zeTc?%R~wXG{i?;yzC}OVr^@d)_M4HqW-iGCc9SyyP^A7;7Sfoi;y82FPkRj@!C9t; zA>ccYAlLP@MmEfMIiRwYE9OFqRIuilEoKm}O3u~8lEB~3`n5WsBTX(FmC_#-9?{Ns zXM^8;yV-u`sBz^}gMI!TmlK!;g-tE{P@I;%8amaE1)s_%GJ{&0@I1>KX@&N(8VU;H zZW@@}oehDlm=et_wtE|0zWzSr_ua}v58-83==hFQ38!;@*f>>!8Hj5zIEw_aij+3^ z(A-0u;|0@28>9g|*9*HbYS`hWnOST}XQAr*uLOY|gO0BI0ViF$v*PvJv|3o*GtADT{E z{;$h^VH?(?cKz&H-5AEMrkou4)>t85KrrBvE!*OpG*xbTH zU$3O;E2ZBMe>mB1fcjkW*erpckRNPpO9nl)&<%fBlNU%2`p|aX_smR74zVSMkugcKs4{nFpyx=F~lO&}6mL-~hXpc)hLanZc$R?9SZyoRry@ z>c|2<9aE}Cu+J9a)eON>f;`~Cu3^Ud!R5CNixVuIJ6q4IWvKKONI%&E^wDG*nXel( z*<>YbZrW0q(^@u9=?PLKsW*q=@RmDt&9O$*N4VnAM0Z*dytH6kBp49P-qu%I=tBwk zK^=^+>pX&Ec1S1{au~)_`4;|I4@Q#J8IdO1(bl^rapihIFU~|$?pqte`q`fSg`FCsHpo|9)wlu1XoNjxkU64y(5{i;-7t)-7=Wk zxK_E|~#C%IHbTKNbyw@vr z{3+45P`mM!t(}47;ki@j&skI_NwQ~%A1XQDN2h%>9NF1DCk;48Ndt6?kRX{eP zRY8l!B%|Xc>++pubZ;M;9E`v>y`HaJR6t`q!%%2#=y>{AgZ%y$>6UA{EYM!qEB5Lx zqEqixWmm4V0vF$`>_J_gME7^cadmIDcIZC)S?Tdw)e)pGAN73Z#ftqP_#FM9FJ`g^Z;3 z!jr^PiI>5sQj^N2ooKUzn+u)~zZ1*wu42fJMc7Cyv2wu=1#xhLTZE7<`m1quieBUW z$%cH*;VK2;S;7ZvcDHCvo=gvhWPM#X4139Dpcou*)ZDTYdR}16!#~+nSz=#8!SbR9 z6F+K4{8?9Y*!&3X2K+cuk)ajdiSmm$p+S+K51Dv1EU#5=j1^Yf4m5TJ4yN z1ybG8*zl2>R8KT>yr^~pJry7{BNLapTj6a>vu3hv>YP6$v=_cw7Kzv+aFF$EnM-`1 zW9%Ep-!|sEq~(u@y;I5QujB;(`E;o*>1yXP)Mn`wcA8|{>ZTm{ArpQ@ac|(Jo;N(A zPXi-#o+J#RVIKxw88!a%!Ty+RQl8S?a$;{ z%u@*l)uk~qcuk$<8`8<^W(bA)&l+oXpJNBUKd!XXI)KOGP|}e`DxOBax)?^V{@!69i)$iu!J%^!ta%X{uz+D=*hc* zu*QVW7WIQ4!=Z%E`-XU)*Y<0=@*BAb56Ty2-J@j_!978}+0_MF!-IYH^{S4KkjwAm z{_)6D1EKpM=|>-^bn_DS#$|@fVB!^VHc3;u{K9Xv$}*N^?%s#5L*NAHd)Dbo_tVm( z62@P4YHxnvYO%}6+VvjKo=(;Vk9ojDyk!;r*Byd7Yl7Whl=b85Rford-y{FEV&p}5 zDU;{5=HJM&-kfV5#WO?T+j zU8wt&lqsXGC5xLabzMK?mSw~>t1}H6v=7}(7IR!?T?2M0I`A4-b$J~!r9S*qjKU6@2 AC;$Ke literal 0 HcmV?d00001 diff --git a/barcode/src/test/resources/org/xbib/graphics/barcode/code11/module-width-ratio-3.0.properties b/barcode/src/test/resources/org/xbib/graphics/barcode/code11/module-width-ratio-3.0.properties new file mode 100755 index 0000000..f7e1110 --- /dev/null +++ b/barcode/src/test/resources/org/xbib/graphics/barcode/code11/module-width-ratio-3.0.properties @@ -0,0 +1,2 @@ +moduleWidthRatio=3 +content=01234-56789 diff --git a/barcode/src/test/resources/org/xbib/graphics/barcode/code11/start-delimiter.codewords b/barcode/src/test/resources/org/xbib/graphics/barcode/code11/start-delimiter.codewords new file mode 100755 index 0000000..5fbc040 --- /dev/null +++ b/barcode/src/test/resources/org/xbib/graphics/barcode/code11/start-delimiter.codewords @@ -0,0 +1,15 @@ +112211 +111121 +211121 +121121 +221111 +112121 +112111 +212111 +122111 +111221 +211211 +211111 +112121 +221111 +112211 diff --git a/barcode/src/test/resources/org/xbib/graphics/barcode/code11/start-delimiter.png b/barcode/src/test/resources/org/xbib/graphics/barcode/code11/start-delimiter.png new file mode 100644 index 0000000000000000000000000000000000000000..6c89f39afab914b649f179bcbd64df2423b1c107 GIT binary patch literal 27064 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 literal 0 HcmV?d00001 diff --git a/barcode/src/test/resources/org/xbib/graphics/barcode/code11/start-delimiter.properties b/barcode/src/test/resources/org/xbib/graphics/barcode/code11/start-delimiter.properties new file mode 100755 index 0000000..4b140f4 --- /dev/null +++ b/barcode/src/test/resources/org/xbib/graphics/barcode/code11/start-delimiter.properties @@ -0,0 +1,2 @@ +startDelimiter=* +content=01234-56789 diff --git a/barcode/src/test/resources/org/xbib/graphics/barcode/code11/start-stop-delimiter.codewords b/barcode/src/test/resources/org/xbib/graphics/barcode/code11/start-stop-delimiter.codewords new file mode 100755 index 0000000..5fbc040 --- /dev/null +++ b/barcode/src/test/resources/org/xbib/graphics/barcode/code11/start-stop-delimiter.codewords @@ -0,0 +1,15 @@ +112211 +111121 +211121 +121121 +221111 +112121 +112111 +212111 +122111 +111221 +211211 +211111 +112121 +221111 +112211 diff --git a/barcode/src/test/resources/org/xbib/graphics/barcode/code11/start-stop-delimiter.png b/barcode/src/test/resources/org/xbib/graphics/barcode/code11/start-stop-delimiter.png new file mode 100644 index 0000000000000000000000000000000000000000..298b55f17a81722dd9b31bd885af75352dac1d17 GIT binary patch literal 28315 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 literal 0 HcmV?d00001 diff --git a/barcode/src/test/resources/org/xbib/graphics/barcode/code11/start-stop-delimiter.properties b/barcode/src/test/resources/org/xbib/graphics/barcode/code11/start-stop-delimiter.properties new file mode 100755 index 0000000..51c3508 --- /dev/null +++ b/barcode/src/test/resources/org/xbib/graphics/barcode/code11/start-stop-delimiter.properties @@ -0,0 +1,3 @@ +startDelimiter=$ +stopDelimiter=% +content=01234-56789 diff --git a/barcode/src/test/resources/org/xbib/graphics/barcode/code11/stop-delimiter.codewords b/barcode/src/test/resources/org/xbib/graphics/barcode/code11/stop-delimiter.codewords new file mode 100755 index 0000000..5fbc040 --- /dev/null +++ b/barcode/src/test/resources/org/xbib/graphics/barcode/code11/stop-delimiter.codewords @@ -0,0 +1,15 @@ +112211 +111121 +211121 +121121 +221111 +112121 +112111 +212111 +122111 +111221 +211211 +211111 +112121 +221111 +112211 diff --git a/barcode/src/test/resources/org/xbib/graphics/barcode/code11/stop-delimiter.png b/barcode/src/test/resources/org/xbib/graphics/barcode/code11/stop-delimiter.png new file mode 100644 index 0000000000000000000000000000000000000000..62016a860e94fb5fcd94a3018bda7ef0c66f1607 GIT binary patch literal 27184 zcmeHwXH=6}x4(*x?}&h96r`%Cs5I$FNpw(AX-bivC@pk|)IdmHhZhh5QIKAup!6b! zUJ@Cqv<$t25JCtol2Fp~56=AWTKD7qa6jC&^6l(rt>pLYea_ium)}WZADJ2o9TPux z;J^W)2lwxoA2@LM<$(hSxBta^a3BEn;Lw2s9(xb&+_nq_mG!82e!X=-pq0fP{i+FfHH-Z?ImYSu$1RiTC)lim4i(DzcLZ*Y_*3oYk`2^~^6xDDl(86z@x$sNS2U zyuq%WgcT2AtNI_T@`RRLEz6QkiRtP9 zCvn|sPbj5@2b;f|mEiYNViKPZ@CFGjWBYIEqgVb9mX~uSQ7EN^8QlZgS8S)hQ|qV1 z4!8;74LU(y`){e4`{W<2nyPVXG*5Q}*UEjx7;gBieTl$n$VUHTl6^av$^VTtIyJEd zGvMj|;fwXYViD*qlYQwq*I~f+ACv6cq3|CpPoMRj=6yRqnk?N{3^Pe9*q0iRO2jh0 z`Ahib=dXUg^Wi%mzVGKBz5Jt>ANlYDPk!L}Gsob^{rt3xpLX%HUVhPG>>j`9z_0o6 zOK$v<8^7YoFF5lH&ir~mzum%bxBT5%<9ECG%|?E+k>Bg(x7xk(`K@+7(}7Pk;S){x zJRcsVi%+xS)2#R`H$KUbPcr23N%B8VorF_b7q4b^ z(C9_&2Tq-leK2Eksp4f)$C1j0qSz~LCtG5#h#$9KJoNi7hix@3K70D~=|GMJ8^gBb zGN%2;>q`5!aw~|yT%z@C%&J;YW2u*i-EFJ+7vK~Ds}uojs?;N%bnp!8m4FrWy{E*{ zS<6SGA-AHmEglc-kG@+IZdpkdy^1V2W4R+mv%2}n>gErx$%FgSqt;68M`w4DUuQB}}sGOZ^C@d@_B9jmXBg4b!#chea+}zwVh4UwNwztc) z+0jLXg^&)C)*XF)eR)&tg;f&C7RnHMY-WagHSG5?EG+Cv_W#yLZM%gA9Z|rIbOCb9 zMq{{4XdfAluqey%r$PooH$N~ptAl5iL_)XGNG{no(eX!)SqmvD@uq*J)Yzs!Dl(U5 zFN4wCmGR)PrQ`s+2b=s_Hd=P|muw3WOO*bqy5$1wO}s~Pa1R_$VpF4ix{n3acxh8 z+N8fTYu#RaLp7@2vIB{Vu*uflF0;pvXp2hLQ6~;1GLjRZAMZqtPPWF4C1*6ITFXm- zYLqyXGTm}oG$9uWWLnEq)naaC7okbRSUrx&c{DC#crH8wFBIc@rcgdu8`cS|6_=3d zd0y2Y?WzIA45Z-3fDzeoj;GV6{*1N=j<@Jt|IstOc*$XRNgE(0=?}`z9OxUJ{1!K+ z_Io8^wAU3VjfAGcCJ0a^=mxA%vcV!-sSF)wHl-%qc3ZT4>jJc*lg$MPo8d>M%&Ynf zYCm5ZXq&A>O?;MHbXm`=9~l~gh`dzGU;|G>)X0AUN2r?ZtsZ?2IW^KAoV=a*zr0|h zlnzI{U;mp~&P`L{*Z_mY-`c-H$6DkEtlO(}>{}E~y_AZuu5nP!nyAZAM0-$TLcZ*x z`+5?y{ZlIFwBahtG`nJAyIDI8)@}lQq81)B#yD&Ou9oI~bXJC&a-r7Jcz;-8zYH4H{xeEiGO7xBT5qW&Owp<5Y(Oc(= zX=|%im&e`6QCpexkArbz76z3Bs~(g5h>~d__q6pqlsQV2s&mw2!nI75lh?GN$I>Tv zw<-bTA@UrIsR3r=oej-Y#hUe%s?_b=`^}nvM1ULD!?QSEY~noSUAgVYZ@CH9YJjV3 z_^_D0!wKT)w0Vd+6eXHXO_A`!t`+nG_Ld<+)&Y{z390o83W1s)yqSO9Yfi!9pZO)opT$YhZ(6o+~xY59u8&ujku! z=tj?K3+lI>Z^o}9b6#uHxei%eS<~C%6O3Zw*q+VYcIE~(&x=#Me0ubh8Rz>%Vf31r z6#EHTg_%f;8vWr;^youp_qUL@P`AzL{V>b3rXg>UeM1rFljbX=mcP(HdgJcC zw%PR(0c@2Sm0s;0M7_O#iJD_rMo5chU67%%dR2z+=66DMeBDA=r5(CwI5v<%7? zeHoz{pa|Q;51Fscg_?P10m9rZj{vezT!RbQZ`y8KzmD;fOz+*oKO;4=w!%yI;^`p; zLcLQ+iQzx)m7;fe>pdFOTbeYznT9X~X>TT)g~@DP=CDzmP8@e{1xr!3^*R6f@*LA= zwKPM9WeKA7rKbvW+uN~tS0xqfiTdzhm}Epd-((GJLJNM+4d|5X8g*+Ix4z1fbK=1n1;h7mh}YvqDueb77B_eeZ~RJJ6N5mZfBE2%oS{~D>+^tvuq-SYarL9$-z zYWoG!7HL7lLxB%6<;&2m!Ihlbfd;-4x{9p#wp`(Ra0TFejPp6-kWWc*fCLLZoDTUG zH%xr9ff0-A?T3X^znqv$LssCTKSyEveBjE1qjYA4XadQV0?x+9Kg$j-MU&z*on8}d z5)=l+C*I9#a7`5G)?r**rpmCxvm7ZY!0i#*BM`%=%9sEdI<=ta@O0SB+Q7`0LN#&D zy)1)DjV0^B=Q2^8d4tek3Qz*xv2YC#-iNt9*&@e14+pkfRzkXaH|b=xxZl79l>L4d zA_P}u|DmT7{ppio!8S?kkcT%WsP-oLN|>X91U1;hb|g+ zeNRqw0w_c`_spGaQ>9C7wmJuMUFP-NVpz95Erb=MhJu4M!v~+{5^bV*dUP`{0L0sd zhESi>Idwrv>Y7BIa34Fy0~KS!AJy$G&P7z2Hk?Lp*O8A~HxZbdsqk(8*mAt+CIH-r z18mJ*mqCO&UoJFf@)1$BDJ zU_%wUX)`yUUwhq1>tlV8BS!u~OAT`I#+>~oq{1Dmlhy1VvVAY~(Ftkgy>4Vj*PcZwLzABet^hX@D2t$;!C1ZO>7q zF%fg|x8nl?LG(W^A?uV3vvXIm?Kkq+tKO8n(G23vCl>_KzkuE%fCI0eW$YEu>{a5E z3zi)q62n7Y&gnfN!>5>?2j;~vgA$zYKQTbhoptPp&s1njDZq5SuMn=~EAmU>qF(sw zbi3B6GxW;AHK*>t&pHy#?#_Kw$DBCWbN zqfe6J$lH6s1l$&N(ui}>FS-yT~#43Vaui&59OWUIdKVZy2pD)bwE6N{h9*#od>dvIs?^RprAxzZ1%52SN% zu^f*Z86i`K-a2a{ahlEs+p#vOB$uE8_~69=y)I9e$78nusXjeXPR|h*$DKcZppQ)| z(&gV%I)!~FP^TZGb6m?lO@^rzGL6fwS}sR1O743h-FeO5@^t(&8O^cH}sp zNghvnb^s+s~@1dZdGT@J^zTM+-$a@MI@-bXaqL`1u(4n7Z_*I&s(r`^j zjQ|AM@qV9oiv3G?GlcO%=dJ9p>d}rHORm>|tP&^^hFS8X<*9+8CC%utA4xW;jkY;x zaP_b2M4KFi9`Wupsm$j$7qBJ`aLn)GnO->&TFApNkV4WluF*P^Tg-i>SViwH@$1Dl zRJTh4!60-0c4X(mOacJuLS$6Xqd(42#^J0RGnfv9(o&RLtTf=Bl98lbea(_pb``y5{E^dJ-1 zRpQ2*a?D5HI4jA2CAn{zf%VFNn{5 zROLo_UY7Wfv{+r}mc;B4sJ)m)(a3upcVte{?@D!_DLD>JdN+D)eA*$*97O4I&Ik`$ z{vaMDYI4Kj%2br|z3c?b9) zK3RfLdEe|{qTxadOpG9fb4U~kay>iK2oTY;&{j&ZETiAg#!aKDja;Vl2QVuP-Ae4r zdj14>`;GnL?|;A@mY%}XsM_Nw1@dooz1N%)x91D%%fmkSNwWjPJJt7A`wV1XDQ7;d zx2zWI$}4-pDlMzXt&f^ES3(_4jb;VaUPB7$hlUtyP>h|pdubKn*%jw@y@Jxx%9`A~ z=?8ZI#EQ+wA!fhLP+Xcq6O)^jUAl)W#G`i1q$cs_P!BOl+b>k&(F>>BHtrjz=Nb2~gh*mKyLWMeGlI>Zq$6m`JxKu3R|%Nz_Eu zLE-1z+`=T&-;qwv|kBULZ zYn=o6-|Jfyy>}pZ4^m`<-u3@Hph!!>cHZ~(E6dD&an9ZVUzv-DuV@3(h1e^ewB$-E zt5i*7G@X(6AiDQXlmGrEl(-19>>C==zg8LhEM9%70!^hP`&j_g-()Mah_c7( zUWq0wYZ{!E$UDo!O!xp+@Ar@W+uTCHOdruUF^_Hs2r)7Et*OI{M^M~e1&h*T_T7HV z2XIy{HMK$t)aYM0xG@>Mbhpz-{+^b-IClFw^W$Fm{8yiDMS2X~=Mwb>0Q3F2xv{eMs2Y+({s!#*YF#)N!^j|+E^Xv@z zDz{R;4$Y9KmqoV9bXtDjG?U5@wGS|jr?>Y2kLf6%43yAZwik|D6Ux{ z8CxeWqas4SzS}D{s;P5n!WP#mvs1NR3r}X~L4Yz3)y4E8vy?$QMzT$P(lzwWClLDG zHLW|B&l0#n9T*JulFVa$T1&c{e=x?0s>v09$au3=RNp2KwD_z1KFaRTU)S>OO?oT8 z8P>_lBQ=>I1bBy86;C6wG?yV`i81oj%{7hF#G;6hK<;E~tr`96Wv!W@w#zc155r0V zKWwxjc8)YUt=QI)XBA8`;q7cNcC@6eY6@a>d(T)_sa?X)jov zg2%5c$;}xB99!Q>fS;*lSTIV_-S0$wJbFqWT>|`h-HoTB_43E@(!Jie_V9E~{HO=1 zWi@6C9=al&J~NwwQV3(4ofkG)$W}+VI4%eDyJb6E-@I@XJdk){z*XWo?x2SgOKCS2 z-u7Bx(Xd2Oq8L6{ts;%+Sh;wwhP=x=s9cxgYK;-i%l|oQ)6Y}N3lP0zpmO|Mh-MZ` zsqrH;RDFTA1qc_;p!r$u!RBP#?cOFP|DuI(SoP$!|Do+J2hrJ%%_m6M_ zTC4Mh%RSSN9}3piqhpqifb`g2A5`TEj%pV#N8fWaxde3FF4w%XHcXqaJ?S?fa?y0p zuEbG7CtAFY;5TQQ=-Tcz)c&qNbWR+j8SRSrQ48lzyxK4=Q#$%1iv+|${I8ac-W1r$ zGl5`;HbFeUK)cT%q(h5sf@nuSeu!RMUq*FP<@%bW>=<{#{=T%cvIX7UZv1dJjAtugJFepmZAuqO_=`!dcVja3m&bv* z7$#6p(OmbEZpJU4jnDC*yTaG4diS;6V#zv*C7C@`1S?z>(y)ijhu@I~`aYMtXF-8v zv%Qpn>)xJ%{kZ7ur_K_2aPFsb5_wgwhv-?`f09Dz$s6yyBnTw1tY zZj=BYW5Cp^7SKQd-_6RRd1mjDc|Yd z$KuJg^<7!&ibWwy!|y)eR6R!G5^($HL{Nt!bF|&C@7Bq$C;LdnxhL9Q1`M}9v2Hh) zl}Q0O%M5~4jAdnN8LUR*ii10!9u|rE=*&4++MRV1nn@K7oo(QZ->ACaIOfNUMIY%r zXWGvxpQOdXDAz0ocKv#qhViUQu}gH@e7`n*`c=YKFKRZ&H2<%rOlrB-+_|=w@Qw~t z;s?{Yeoa@cp)QNutry~;H)m<)$1~M^DhFVoYTl{SVJm>UkCk4OV8D~Hkd71gozY{H zj`TxCk@Z zF8LT^JsaSZ{^uEz>P$c82?`lBK+U_$J4|_*v0;d$w7}j+TIqh?-KjIZy{)%*iA@L& zz~tb}xNA0-=&*dh)&kamDVR@~F3&w>w44jyS)sIc!BDRO=Xk4FmJ;41>y&;E z!CHab7>_%<@h7i7a-JIzVM9C8ppS}utb6c5^75-SZ|W9o_1AvHNt7g6x zC;h?oQhwzsPTCq>U1!~m3tq5!uipt7onx@r@K z>oq0XTHriH){cq?#ljF6*BUWbDiX~2YbEJ(vbDuj_S`8GY$!+2;NS4QF%p<80`o!? z8C=m^<>pkH=!b?ICn0-8uj%Ng7B9)YM37>B{G75Oaj}gM{ZGN6JImH?_A`EVZ{_kU z--=j^>mioi6suf!KjZE+MA?H>8@nk>pPsA301R7i+ocakO?t!j?%=(kAMay@T8D!%c4k?H>9~AuaCxf=#eLh~PWomZDcGI%UIf;bg5NDs z+wBq2&@Lij3Yu?iql?Z?F-Nu{6zfSX6dVF3y~%7+ptza<`9n%o7X=Nw#fey*)o>c zFWPN%2N>5p1He*ZkbC{)`36#FC5ju4ebf}QIF;CW`MM_CTY6%51+9!(sE|+sFx`F0 zDu@G1V)R3wl@p#NK@z0>KCeL`j)5Ceb>MJbJg3$u^MbXBp)djDav~T(ElAJ;|0q;; zifin8=PY{}_F*Lr@h@dNUOM4a{sKu*>UrTvm}gzFKN|qQ;bWkl5P{Xy1YJ}<#a37~ zlbg_)yPyydpyT>Gbfmp2|0K9u&7xx39@4hraIPQUu8Y$aU=B>&1ek)ZGd>Mj9IpcJ zyO1OH0^sZ~m#2KD%0=+sFD=u)&YaNqUCm8>Kv5mU<#FK2>-0ok0o^3uDCbVDq6quk zbBD}ga4X_^9idI&ve~JSo?H$#U}fbrOqSv_sC*{Z=_U8)Dx4ChEw7Zs21)A#S7z%# zpPveVKlOPT42ug;%x|{j%`*^xxGTl?Xv+mutmc5`sxKtNp3+c;*KH_-;?BWm1iizf z9dw}zRXe4H*ayi=NAkg$jNc5FVS{;&+Gl-*ytOq@$Qw?xrR_}So$Q=;Ntt$`!Eq3f zaZ%?B^V_F{ua{sAD-ad7;lyf@a@WFF82!+m2I2$l*OeR(&%)TsrUL&-wKXjdsjB63 zX&v7#K>fRi3ysa>l+UrvJrerqKb80{Zo_?5C0I%h9%5)e&Ic5$bRC5nR7d&cz!boB zF2S%)!va4U4cOp{#E)_{)J>>JZn-ofjAnPgYRioBDK~n3d*C+*lreP!dZY{6RU}@{+P7`;G6<35jwL2#MIVG6d2<)7}AfVK9J4SnjLuXFoSO@2b0rztpm}{Kea6Hvu1Q7M7{7`()q|6B z#(l#pW2AO;Q5p0U7Tf_gAfFrhBO=76DL95H&1$#KwrC9NKC21dU9dpV-$aCf>w?o- zM07bmqTkk@*AflC-?dj%SlaLA5e?)1w;Fc;lQtoddyspm)1rGRm$m;_P!H~!-l_Qg H=^y_O)Pund literal 0 HcmV?d00001 diff --git a/barcode/src/test/resources/org/xbib/graphics/barcode/code11/stop-delimiter.properties b/barcode/src/test/resources/org/xbib/graphics/barcode/code11/stop-delimiter.properties new file mode 100755 index 0000000..004d4bc --- /dev/null +++ b/barcode/src/test/resources/org/xbib/graphics/barcode/code11/stop-delimiter.properties @@ -0,0 +1,2 @@ +stopDelimiter=+ +content=01234-56789 diff --git a/barcode/src/test/resources/org/xbib/graphics/barcode/code11/three-check-digits.error b/barcode/src/test/resources/org/xbib/graphics/barcode/code11/three-check-digits.error new file mode 100755 index 0000000..6bb2fec --- /dev/null +++ b/barcode/src/test/resources/org/xbib/graphics/barcode/code11/three-check-digits.error @@ -0,0 +1 @@ +Check digit count must be 1 or 2. diff --git a/barcode/src/test/resources/org/xbib/graphics/barcode/code11/three-check-digits.properties b/barcode/src/test/resources/org/xbib/graphics/barcode/code11/three-check-digits.properties new file mode 100755 index 0000000..562d383 --- /dev/null +++ b/barcode/src/test/resources/org/xbib/graphics/barcode/code11/three-check-digits.properties @@ -0,0 +1 @@ +checkDigitCount=3 diff --git a/barcode/src/test/resources/org/xbib/graphics/barcode/code11/zero-check-digits.error b/barcode/src/test/resources/org/xbib/graphics/barcode/code11/zero-check-digits.error new file mode 100755 index 0000000..6bb2fec --- /dev/null +++ b/barcode/src/test/resources/org/xbib/graphics/barcode/code11/zero-check-digits.error @@ -0,0 +1 @@ +Check digit count must be 1 or 2. diff --git a/barcode/src/test/resources/org/xbib/graphics/barcode/code11/zero-check-digits.properties b/barcode/src/test/resources/org/xbib/graphics/barcode/code11/zero-check-digits.properties new file mode 100755 index 0000000..9e66f91 --- /dev/null +++ b/barcode/src/test/resources/org/xbib/graphics/barcode/code11/zero-check-digits.properties @@ -0,0 +1 @@ +checkDigitCount=0 diff --git a/barcode/src/test/resources/org/xbib/graphics/barcode/code2of5/basic.codewords b/barcode/src/test/resources/org/xbib/graphics/barcode/code2of5/basic.codewords new file mode 100755 index 0000000..61126aa --- /dev/null +++ b/barcode/src/test/resources/org/xbib/graphics/barcode/code2of5/basic.codewords @@ -0,0 +1 @@ +31111131113113113133111111313131311113311111133131131113131111331131113113113133111131111 diff --git a/barcode/src/test/resources/org/xbib/graphics/barcode/code2of5/basic.png b/barcode/src/test/resources/org/xbib/graphics/barcode/code2of5/basic.png new file mode 100644 index 0000000000000000000000000000000000000000..f376cd3838e16e650590926f512ab17a33cc42ae GIT binary patch literal 31262 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 C|ETu> literal 0 HcmV?d00001 diff --git a/barcode/src/test/resources/org/xbib/graphics/barcode/code2of5/basic.properties b/barcode/src/test/resources/org/xbib/graphics/barcode/code2of5/basic.properties new file mode 100755 index 0000000..2911d95 --- /dev/null +++ b/barcode/src/test/resources/org/xbib/graphics/barcode/code2of5/basic.properties @@ -0,0 +1 @@ +content=1234567890123 diff --git a/barcode/src/test/resources/org/xbib/graphics/barcode/code2of5/human-readable-location-none.codewords b/barcode/src/test/resources/org/xbib/graphics/barcode/code2of5/human-readable-location-none.codewords new file mode 100755 index 0000000..61126aa --- /dev/null +++ b/barcode/src/test/resources/org/xbib/graphics/barcode/code2of5/human-readable-location-none.codewords @@ -0,0 +1 @@ +31111131113113113133111111313131311113311111133131131113131111331131113113113133111131111 diff --git a/barcode/src/test/resources/org/xbib/graphics/barcode/code2of5/human-readable-location-none.png b/barcode/src/test/resources/org/xbib/graphics/barcode/code2of5/human-readable-location-none.png new file mode 100644 index 0000000000000000000000000000000000000000..0e280925c610a4109be4d42e9e9c6cbd62d64c74 GIT binary patch literal 23160 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 literal 0 HcmV?d00001 diff --git a/barcode/src/test/resources/org/xbib/graphics/barcode/code2of5/human-readable-location-top.properties b/barcode/src/test/resources/org/xbib/graphics/barcode/code2of5/human-readable-location-top.properties new file mode 100755 index 0000000..282ee68 --- /dev/null +++ b/barcode/src/test/resources/org/xbib/graphics/barcode/code2of5/human-readable-location-top.properties @@ -0,0 +1,2 @@ +humanReadableLocation=TOP +content=1234567890123 diff --git a/barcode/src/test/resources/org/xbib/graphics/barcode/code2of5/module-width-ratio-2.5.codewords b/barcode/src/test/resources/org/xbib/graphics/barcode/code2of5/module-width-ratio-2.5.codewords new file mode 100755 index 0000000..61126aa --- /dev/null +++ b/barcode/src/test/resources/org/xbib/graphics/barcode/code2of5/module-width-ratio-2.5.codewords @@ -0,0 +1 @@ +31111131113113113133111111313131311113311111133131131113131111331131113113113133111131111 diff --git a/barcode/src/test/resources/org/xbib/graphics/barcode/code2of5/module-width-ratio-2.5.png b/barcode/src/test/resources/org/xbib/graphics/barcode/code2of5/module-width-ratio-2.5.png new file mode 100644 index 0000000000000000000000000000000000000000..d7e62604d48f768a91375e8469f715d4d54e635d GIT binary patch literal 31094 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 T&Kf`dP}$PL`fTZGw?F<1ZLmO) literal 0 HcmV?d00001 diff --git a/barcode/src/test/resources/org/xbib/graphics/barcode/code2of5/module-width-ratio-2.5.properties b/barcode/src/test/resources/org/xbib/graphics/barcode/code2of5/module-width-ratio-2.5.properties new file mode 100755 index 0000000..687cd75 --- /dev/null +++ b/barcode/src/test/resources/org/xbib/graphics/barcode/code2of5/module-width-ratio-2.5.properties @@ -0,0 +1,2 @@ +moduleWidthRatio=2.5 +content=1234567890123 diff --git a/barcode/src/test/resources/org/xbib/graphics/barcode/code3of9/basic.codewords b/barcode/src/test/resources/org/xbib/graphics/barcode/code3of9/basic.codewords new file mode 100755 index 0000000..8bc811b --- /dev/null +++ b/barcode/src/test/resources/org/xbib/graphics/barcode/code3of9/basic.codewords @@ -0,0 +1,12 @@ +1211212111 +2112111121 +1122111121 +2122111111 +1112211121 +2112211111 +1122211111 +1112112121 +2112112111 +1122112111 +1112212111 +121121211 diff --git a/barcode/src/test/resources/org/xbib/graphics/barcode/code3of9/basic.png b/barcode/src/test/resources/org/xbib/graphics/barcode/code3of9/basic.png new file mode 100644 index 0000000000000000000000000000000000000000..1bcc949b249c9aa83ec9165462d14b45233f8a73 GIT binary patch literal 33955 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 literal 0 HcmV?d00001 diff --git a/barcode/src/test/resources/org/xbib/graphics/barcode/code3of9/basic.properties b/barcode/src/test/resources/org/xbib/graphics/barcode/code3of9/basic.properties new file mode 100755 index 0000000..4507f64 --- /dev/null +++ b/barcode/src/test/resources/org/xbib/graphics/barcode/code3of9/basic.properties @@ -0,0 +1 @@ +content=1234567890 diff --git a/barcode/src/test/resources/org/xbib/graphics/barcode/code3of9/module-width-ratio-3.codewords b/barcode/src/test/resources/org/xbib/graphics/barcode/code3of9/module-width-ratio-3.codewords new file mode 100755 index 0000000..8bc811b --- /dev/null +++ b/barcode/src/test/resources/org/xbib/graphics/barcode/code3of9/module-width-ratio-3.codewords @@ -0,0 +1,12 @@ +1211212111 +2112111121 +1122111121 +2122111111 +1112211121 +2112211111 +1122211111 +1112112121 +2112112111 +1122112111 +1112212111 +121121211 diff --git a/barcode/src/test/resources/org/xbib/graphics/barcode/code3of9/module-width-ratio-3.png b/barcode/src/test/resources/org/xbib/graphics/barcode/code3of9/module-width-ratio-3.png new file mode 100644 index 0000000000000000000000000000000000000000..67a6c03d95c993b18fc36a9d4847e29e5314fa27 GIT binary patch literal 38681 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 Q2Jq5Uy?n7i*(UhE0J^}8JOBUy literal 0 HcmV?d00001 diff --git a/barcode/src/test/resources/org/xbib/graphics/barcode/code93/letters-lowercase-1.properties b/barcode/src/test/resources/org/xbib/graphics/barcode/code93/letters-lowercase-1.properties new file mode 100755 index 0000000..4995fc9 --- /dev/null +++ b/barcode/src/test/resources/org/xbib/graphics/barcode/code93/letters-lowercase-1.properties @@ -0,0 +1 @@ +content=abcdefghi diff --git a/barcode/src/test/resources/org/xbib/graphics/barcode/code93/letters-lowercase-2.codewords b/barcode/src/test/resources/org/xbib/graphics/barcode/code93/letters-lowercase-2.codewords new file mode 100755 index 0000000..8c1038f --- /dev/null +++ b/barcode/src/test/resources/org/xbib/graphics/barcode/code93/letters-lowercase-2.codewords @@ -0,0 +1,23 @@ +111141 +122211 +122112 +122211 +132111 +122211 +111123 +122211 +111222 +122211 +111321 +122211 +121122 +122211 +131121 +122211 +212112 +122211 +212211 +131211 +123111 +111141 +1 diff --git a/barcode/src/test/resources/org/xbib/graphics/barcode/code93/letters-lowercase-2.png b/barcode/src/test/resources/org/xbib/graphics/barcode/code93/letters-lowercase-2.png new file mode 100644 index 0000000000000000000000000000000000000000..bd415da422ec5d57e57c346df1ec063b08a711f3 GIT binary patch literal 50474 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 EfBJ$lkpKVy literal 0 HcmV?d00001 diff --git a/barcode/src/test/resources/org/xbib/graphics/barcode/code93/letters-lowercase-2.properties b/barcode/src/test/resources/org/xbib/graphics/barcode/code93/letters-lowercase-2.properties new file mode 100755 index 0000000..f892b9b --- /dev/null +++ b/barcode/src/test/resources/org/xbib/graphics/barcode/code93/letters-lowercase-2.properties @@ -0,0 +1 @@ +content=jklmnopqr diff --git a/barcode/src/test/resources/org/xbib/graphics/barcode/code93/letters-lowercase-3.codewords b/barcode/src/test/resources/org/xbib/graphics/barcode/code93/letters-lowercase-3.codewords new file mode 100755 index 0000000..e9f6ae4 --- /dev/null +++ b/barcode/src/test/resources/org/xbib/graphics/barcode/code93/letters-lowercase-3.codewords @@ -0,0 +1,21 @@ +111141 +122211 +211122 +122211 +211221 +122211 +221121 +122211 +222111 +122211 +112122 +122211 +112221 +122211 +122121 +122211 +123111 +212211 +122121 +111141 +1 diff --git a/barcode/src/test/resources/org/xbib/graphics/barcode/code93/letters-lowercase-3.png b/barcode/src/test/resources/org/xbib/graphics/barcode/code93/letters-lowercase-3.png new file mode 100644 index 0000000000000000000000000000000000000000..aa911a344ed6d544a6c2e4dfb1cfa046ddd1aa62 GIT binary patch literal 47521 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 J?@cb;{6ABnAU^;A literal 0 HcmV?d00001 diff --git a/barcode/src/test/resources/org/xbib/graphics/barcode/code93/letters-lowercase-3.properties b/barcode/src/test/resources/org/xbib/graphics/barcode/code93/letters-lowercase-3.properties new file mode 100755 index 0000000..e53732a --- /dev/null +++ b/barcode/src/test/resources/org/xbib/graphics/barcode/code93/letters-lowercase-3.properties @@ -0,0 +1 @@ +content=stuvwxyz diff --git a/barcode/src/test/resources/org/xbib/graphics/barcode/code93/letters-uppercase-1.codewords b/barcode/src/test/resources/org/xbib/graphics/barcode/code93/letters-uppercase-1.codewords new file mode 100755 index 0000000..1a37a3f --- /dev/null +++ b/barcode/src/test/resources/org/xbib/graphics/barcode/code93/letters-uppercase-1.codewords @@ -0,0 +1,14 @@ +111141 +211113 +211212 +211311 +221112 +221211 +231111 +112113 +112212 +112311 +121311 +312111 +111141 +1 diff --git a/barcode/src/test/resources/org/xbib/graphics/barcode/code93/letters-uppercase-1.png b/barcode/src/test/resources/org/xbib/graphics/barcode/code93/letters-uppercase-1.png new file mode 100644 index 0000000000000000000000000000000000000000..c2b04673fb07245ebd39b8251132e89a01914fcc GIT binary patch literal 30679 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 JaQyP!{{f4S&V>K~ literal 0 HcmV?d00001 diff --git a/barcode/src/test/resources/org/xbib/graphics/barcode/code93/letters-uppercase-1.properties b/barcode/src/test/resources/org/xbib/graphics/barcode/code93/letters-uppercase-1.properties new file mode 100755 index 0000000..b627ba3 --- /dev/null +++ b/barcode/src/test/resources/org/xbib/graphics/barcode/code93/letters-uppercase-1.properties @@ -0,0 +1 @@ +content=ABCDEFGHI diff --git a/barcode/src/test/resources/org/xbib/graphics/barcode/code93/letters-uppercase-2.codewords b/barcode/src/test/resources/org/xbib/graphics/barcode/code93/letters-uppercase-2.codewords new file mode 100755 index 0000000..624464d --- /dev/null +++ b/barcode/src/test/resources/org/xbib/graphics/barcode/code93/letters-uppercase-2.codewords @@ -0,0 +1,14 @@ +111141 +122112 +132111 +111123 +111222 +111321 +121122 +131121 +212112 +212211 +123111 +211131 +111141 +1 diff --git a/barcode/src/test/resources/org/xbib/graphics/barcode/code93/letters-uppercase-2.png b/barcode/src/test/resources/org/xbib/graphics/barcode/code93/letters-uppercase-2.png new file mode 100644 index 0000000000000000000000000000000000000000..7a28a02e736795f89f1f202710d969fd6d697a04 GIT binary patch literal 32098 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 Ng)4uc&)vBHe*l$6W&HpE literal 0 HcmV?d00001 diff --git a/barcode/src/test/resources/org/xbib/graphics/barcode/code93/letters-uppercase-2.properties b/barcode/src/test/resources/org/xbib/graphics/barcode/code93/letters-uppercase-2.properties new file mode 100755 index 0000000..05f475e --- /dev/null +++ b/barcode/src/test/resources/org/xbib/graphics/barcode/code93/letters-uppercase-2.properties @@ -0,0 +1 @@ +content=JKLMNOPQR diff --git a/barcode/src/test/resources/org/xbib/graphics/barcode/code93/letters-uppercase-3.codewords b/barcode/src/test/resources/org/xbib/graphics/barcode/code93/letters-uppercase-3.codewords new file mode 100755 index 0000000..2bfb512 --- /dev/null +++ b/barcode/src/test/resources/org/xbib/graphics/barcode/code93/letters-uppercase-3.codewords @@ -0,0 +1,13 @@ +111141 +211122 +211221 +221121 +222111 +112122 +112221 +122121 +123111 +211212 +321111 +111141 +1 diff --git a/barcode/src/test/resources/org/xbib/graphics/barcode/code93/letters-uppercase-3.png b/barcode/src/test/resources/org/xbib/graphics/barcode/code93/letters-uppercase-3.png new file mode 100644 index 0000000000000000000000000000000000000000..a37ce08ddcdd8d11169111c46a8293855d44f71a GIT binary patch literal 30660 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 C)i8hn literal 0 HcmV?d00001 diff --git a/barcode/src/test/resources/org/xbib/graphics/barcode/code93/letters-uppercase-3.properties b/barcode/src/test/resources/org/xbib/graphics/barcode/code93/letters-uppercase-3.properties new file mode 100755 index 0000000..1991c87 --- /dev/null +++ b/barcode/src/test/resources/org/xbib/graphics/barcode/code93/letters-uppercase-3.properties @@ -0,0 +1 @@ +content=STUVWXYZ diff --git a/barcode/src/test/resources/org/xbib/graphics/barcode/code93/numbers-with-custom-bar-height.codewords b/barcode/src/test/resources/org/xbib/graphics/barcode/code93/numbers-with-custom-bar-height.codewords new file mode 100755 index 0000000..9532651 --- /dev/null +++ b/barcode/src/test/resources/org/xbib/graphics/barcode/code93/numbers-with-custom-bar-height.codewords @@ -0,0 +1,15 @@ +111141 +131112 +111213 +111312 +111411 +121113 +121212 +121311 +111114 +131211 +141111 +121122 +122211 +111141 +1 diff --git a/barcode/src/test/resources/org/xbib/graphics/barcode/code93/numbers-with-custom-bar-height.png b/barcode/src/test/resources/org/xbib/graphics/barcode/code93/numbers-with-custom-bar-height.png new file mode 100644 index 0000000000000000000000000000000000000000..10894b1fd99ee5a8b423b2fc69b17f2e00024ec3 GIT binary patch literal 65838 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 BVvzs< literal 0 HcmV?d00001 diff --git a/barcode/src/test/resources/org/xbib/graphics/barcode/code93/numbers-with-custom-module-width.properties b/barcode/src/test/resources/org/xbib/graphics/barcode/code93/numbers-with-custom-module-width.properties new file mode 100755 index 0000000..875c664 --- /dev/null +++ b/barcode/src/test/resources/org/xbib/graphics/barcode/code93/numbers-with-custom-module-width.properties @@ -0,0 +1,2 @@ +moduleWidth=5 +content=0123456789 diff --git a/barcode/src/test/resources/org/xbib/graphics/barcode/code93/numbers.codewords b/barcode/src/test/resources/org/xbib/graphics/barcode/code93/numbers.codewords new file mode 100755 index 0000000..9532651 --- /dev/null +++ b/barcode/src/test/resources/org/xbib/graphics/barcode/code93/numbers.codewords @@ -0,0 +1,15 @@ +111141 +131112 +111213 +111312 +111411 +121113 +121212 +121311 +111114 +131211 +141111 +121122 +122211 +111141 +1 diff --git a/barcode/src/test/resources/org/xbib/graphics/barcode/code93/numbers.png b/barcode/src/test/resources/org/xbib/graphics/barcode/code93/numbers.png new file mode 100644 index 0000000000000000000000000000000000000000..851ea8fcaa1d25eb75e1df23ce2bba04b2a384f2 GIT binary patch literal 31691 zcmeHw_fwNw)UM}P5D^eK2vS855ClZ&Ek_X$ks?KU5K!raA|wz777;AT@*lNhsmV(R1(2{R_UCJNfC&WZs#z_Oth1YdxzZ(MAS3%olhroH})i zS@*$x<5Q>3?4LUI$L@LB7pF1=k8Yeg1%0D?|E?(n*w%aZ!tCFtT7S%)3`tFzUJ@7x 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 Q&kg6#TAwLB?f&q;04)EeRsaA1 literal 0 HcmV?d00001 diff --git a/barcode/src/test/resources/org/xbib/graphics/barcode/code93/special-characters-no-ctrl.properties b/barcode/src/test/resources/org/xbib/graphics/barcode/code93/special-characters-no-ctrl.properties new file mode 100755 index 0000000..fcf4d20 --- /dev/null +++ b/barcode/src/test/resources/org/xbib/graphics/barcode/code93/special-characters-no-ctrl.properties @@ -0,0 +1 @@ +content=-.$ /+% diff --git a/barcode/src/test/resources/org/xbib/graphics/barcode/code93/special-characters-with-ctrl-1.codewords b/barcode/src/test/resources/org/xbib/graphics/barcode/code93/special-characters-with-ctrl-1.codewords new file mode 100755 index 0000000..71cbb84 --- /dev/null +++ b/barcode/src/test/resources/org/xbib/graphics/barcode/code93/special-characters-with-ctrl-1.codewords @@ -0,0 +1,23 @@ +111141 +312111 +112113 +121221 +221112 +121221 +112311 +121221 +132111 +312111 +211113 +312111 +221112 +312111 +221211 +312111 +112311 +312111 +122112 +112131 +111312 +111141 +1 diff --git a/barcode/src/test/resources/org/xbib/graphics/barcode/code93/special-characters-with-ctrl-1.png b/barcode/src/test/resources/org/xbib/graphics/barcode/code93/special-characters-with-ctrl-1.png new file mode 100644 index 0000000000000000000000000000000000000000..09824dd996d0e515acd5c891848996586be37824 GIT binary patch literal 42643 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 N54s+Bu? diff --git a/barcode/src/test/resources/org/xbib/graphics/barcode/code93/special-characters-with-ctrl-2.codewords b/barcode/src/test/resources/org/xbib/graphics/barcode/code93/special-characters-with-ctrl-2.codewords new file mode 100755 index 0000000..a4689c5 --- /dev/null +++ b/barcode/src/test/resources/org/xbib/graphics/barcode/code93/special-characters-with-ctrl-2.codewords @@ -0,0 +1,21 @@ +111141 +311121 +211113 +311121 +211212 +311121 +211311 +311121 +231111 +121221 +212112 +121221 +211122 +121221 +112122 +311121 +112113 +231111 +311211 +111141 +1 diff --git a/barcode/src/test/resources/org/xbib/graphics/barcode/code93/special-characters-with-ctrl-2.png b/barcode/src/test/resources/org/xbib/graphics/barcode/code93/special-characters-with-ctrl-2.png new file mode 100644 index 0000000000000000000000000000000000000000..f05d1e7caeb790035cf1e6e5e55221e61f295e85 GIT binary patch literal 36766 zcmeHQc{r4N`yWwCmZDNrqsY=Sj!M={_T?}}mg1089btqwjG-`2n;a*}8k&%7qoP4# z#Oc_wQ)G#eu|pZmVQ_j5nby?vf=EXG(w zNJA`|45ewqj73wr*;8l}9tB zBXowx8g-0I&S?kzt^f zMsl3+;1N-fCJzu`RjthUJARj9G76f`uv1 z^xbtM5cH^y*^E9CQp}}9>vfpF&yzfYLeIJ-=3pXPC0(6&TTwiw-8c7GIGKBtJuV5b zKI=SuU&TL-4&%a?Gy3hQky_OpO>&sW1n6_$kI>fMP>mq7F~o=UQ!;CFXcU zU^R?f_1A!zHqUMK8b9MOh;OP+mg4M{>D@76$L zPQEjlHRbh&sjWLXRz_qL%}?8FB0yPmYCxa z;qCgmNgtT$J9cS(?XcoLBt@vHezHZ8XELvo9nC#>qSjp_J7&X5!~XARKxq!*9UWuR!Sg*XvYo%lLRISOcHoW;3a{V1Q%{_;Rg4T;9e5kOM>tQgf}4K z1`#)ixIq#KB!NI)667U8UJ|6rL8=^7Z#YSydIPFApr8l}ilCqfYTTg44cab1+XZO5 z0G&Xf69_b?f#x*OoaXEc`XxcXBxvCUExe$G7j%__u5vKw00tevpaYn_0kbz?_6CfD zfpIV}p$H}v!Gt0hngv6%V2&HiasQXkaYJ1N`f-!}^DX0RtgdWnNR?T8xz00xt=9{I z7&q}=0V?Cyt(j|$sBM`i1!Ao7rG<}(&<_z^gV9K5-;hXt%ysra5#3?x3a1>2dVENG z#l(B>Y&zM+7 zDma9ctoL!vl;rgx-up)si3`*bI%Ww)qIVfZf+y?$z$x&^YyE{Q&{%to(wjK81a{#1 zkC+cYl|YriD!~c<9QaCjCJ6Y-mB5R@Wd~fr!Oal3ubgLq`$}+&uDUYh8Hjd3;P}6& zdB9f!UkTC~@DK&?m0wrJK#~pQD?z>zq<*JB79GYp1_g`#cKtX~B z1`i4npdj%TG7U(wfg~G9vVleyD(D%4pTPp!uU2|M4oI?rBpXPwfg~G9vhf}!29j(b z$@b+je4vvJbh3d?Hqe~`x-&p`2I$WC?#Zeka0G!P2pmD+2m(hCIKs~_2a~s7J-;0U zj{miRqus87)d+-`y{Un&4X8f;UsWHcce4j;Zz0hki%?>J4C}A=1M_lR)(r0hk##g2 z{*m_q8DdFa`X_59?El?g4{+z95j2~?eMB5ifh4)K3br>?3spnY#OF*{Dz5t|Pv+#| z6t)(P3k}4HHh3&rLNCzs5Z8#vN`Rh2<4}M%SE&{3eHAvS|zdS66@ArN;_9sjcD7=pLYdj^c*|*4so%`XF)F{qjI(mVk>jEaT5}u zVdOn-%eHA}sDL;x!9|xPKsV;6k+tZY&(2<4GDYx|AiR%Fvz#9wnDM!5i)LZNF%TCi z3UxN3S+d+ys}>0@km2IKsWCMD=wj`RB@RkRt{_kR+W7)DgMvk!M)=qo!MiG6on{ZA zKNoP+xM#RipBP7L9^a4TXLWla-yX3rRXKd*#*^E2ZVy+x|8Ok-IL_eMqczA4dvF`G zH!H^857rgRp3(h5QDuV%R_?G;)uvqnk235XR^!^YMyNi(w!L|^$>D9E&Zi4yoaDqq zhbnx}`TI|io+rMY8bw|zXC58=eD+|B%X}|$wV95q+@7$OD5h(-dqYZK`Ue(Gk^v0~ zY@GH=d*-r1wnL4fB1^iMFO7)4RxMmN>-qBh%h!bmQfa$o0~Pk5RimSAeHP9)9RWHN|%B)B7Plq0F(=i3=Lkz-Tk& z=26pK@A&QGx<2n#?Av3#lfO`AzpqKWckVg;dzr&K$s`xK^-3i@CT8V(T~lfW%U0<` z*@pKiUw-9u@H>W389irq%vq!johHSS_PG+TiUq6=lF1dPItye_f2ltky5*{iC@gUY zEb(E|2BLhat^!e9t(+V~u^yhFvO-V%AxPQ#gky*;{V`K}jhpVtk*NLsc1_o{Encs0 z$Gy#qnX*0;Qt?ywRt=7o&b!V3QA$6xBAuV*60BwoWtFU3#*icYr}&UB$yE$V-7!?+ z?AFxPrxW!lK|iTV&mOj5=HMelFUng{FbbR7uPi9f@a2Vy{bGA&J|fjG!8}m7^>TjW zF8lIFNg{zs*9Yk*!lm)Mk@W!<3Y#Ct7UT{518EVG4Gx_xF@vq4;<1L>rFUGcsq7n; z|3OH{Caw1E1FTJf+{^_Rx{wZi@J{23Y?LI4XoxetU#7K@f~qQ26JM+(n2)NouN*@{ z*}jqD4h`sgJ+2eB+v8uc){IaWTZ=fbwqbkp@LO_sRBrbjS(OS)k&yfdF%1oc*DKo1 zi2_~YGZg_rPev#^bw=oS%?rNUrH5`W*mtg6qFjcpgplrlm5{;1 z`}lB;cZU=Ggl8U6sa-=w8A+~ z9;W=q7v?+E7tP87PCC?wnL&5T4GTz~a>(={ z52NE_r|{|~`vTL%ZU08NDp$BbNgJ6d)^^^xQ#X%w`E{+|@eIfaf21&n7?eyPV`Khy zgwn3){z~S6#{Q7^hiP%F=sUzHR}t?x1%3aHhS}enDV#v17HZW?CBtJ0Mrq4JCrRh= z%L#wj7LAYWif$rkbPQ}uY79C-pH~db6D0>3lY}EU%*mLJYW=eK3e=jBO?*yL*M#8x z>A#8dDie+R;i0R^8|^21oEw<~B_#P|Pq_qZ%1e{_5VYA&;Z70lpeMGUNln?tfmYm) z<5>}*sGEALm-JGUDt~O8XRayYS|2xSBkwWYD9QNNeIX;93{3qe%b42g&B1=pbM#LY zqhBOdY+(27PW>?*%d;S=)q(7FQJV0n|EB0nR{8bON}qg^Jf{frG-9m3)wEk=wosiJ zC4A4$yLv09(6;f!Zzr?-+&Aq#8P^r8MqaJTcRUO}xl8RQ+SO)*P9AyXm`;0DC#ID6 z_zLC3kgtsA!se}(H_}xbG>>en)^?T|L1P8{JilM=AnQNw4KI&>yAxeQ4ObtS?=Q(01P0Ty^WhX-hs)oM;n1S#+Ul*QXm8 zv&feTdN+IAGsSX8B^10n2aarf^k#*nA@cs?DbqpfG;#FXqZu*ih;HXj)4Gd+c0pZ{ zM7hC<_KMBjLbu<`3~BSclb6kU+^Pqn@Cj6;JJC)ZzQhTpx*ZhKi5n?q_&#Oo;ea!(sYmB;(O*$IVx%SRAMoX;=IWTB=L zIk*$ls@$!T$s%Trsf}OnxVn7~NoPA+eAqg?+bX_u&N0}Naa?ex6B*w*l!+R8RW5tI<<(DmV9MIDd$BJ(?6#S`fCQ_@j zH!K6$RT8cL*fWWu86Jeqi5-lJ(T#Q zc+I~GzPNp{(J1At1N}iW^o-d3D$zp8?Wbp?b#^aW9({K5HFvl4gIJf}zjj;*wqwt1 z=lge81Ip>9B%3h}i=A5vBAL4eJZGA6_|2R#7$q^!uhql9U0m6(=V1&+sAn$sF8P3U zR`sJGgC62jT<@P@io6Ng>nQ`-o_p5{Z*x3H9U6LJnZzpjTdK6qADnwD1M5#N3Gd+o^Op8lzbb+g|| zQ~ix@_zYPw>})}M*h^G*r65$2zg4dIr}Rzsrr}@99QDx1vqP|oDqj44C{U+cD0e2@$pq?i#)Qw`G>E_V1|m^HJF2i+}gjK6swc^nQKkqJM4j z^S(>aGe#+4j5TXI?mUu0I{r69@5p|1ab1&7`0X366A9AcZ?}B3$Pyjg*;xFWcUZ1{ z+EltnuzmHn>Pvr$o8kOXq^YhM8SRRv7Ze1v-mf>*!o@Yy%^mG~jS?=#f9EHp3T>w( zrgGl5hqLUGRE+)T9na0RJ5LV$j{j`a=1RxmC7m|=+I)MWccebLc+}z`jkPMtHr6rq z%8Q9-X4`Wc8(n&t#-lu76t_h1YPAYvF z@Z@@+JWXlULUFg^@^$Yof0C+l>N7if^;Gh!wl^v+HJ3ji7jjw3nO=NZJc|@WY!63R zi2rM62fj+ANDQGa&X*+jPMiGUhBOX|_?O(O!=`@(+H`JIYu7=%GZAo$4rOLPmi5XG zM*MMmZL1vLsFjm(FP4J{mED+Xx~e(7H1b*6Cq9Jn?qe|mca8BoF6dA4BO(n#<=3g- z8rP&>?^)@9SDVujvK`C6{CP`0;r96QngQdO29uA=)TYzxTplbeABWX8bny5R2MC1B ufQbP73iFLXtc^54AP|>jHzE+Bi^z$QlTcC(;Hhx0>R|TfB6i)90M- zK7IOmlDn4Xr#5fcwLwZsYP0FjC(cPpeeEnI_0{}3=nqn%%LuZR)Gtz|Cyt#DaemTz zXv@$~QjM>cCEe?XqN|&JaxVWd2Z<4L7m-x7uRP_C+Xas2N2@ll#*A&$3XE%0JI&S3 z2Uu7-)RTvc1(suzCsDf=)FhNMmT=$F*f`V1dJg{CfeS*5T*AN)@f}I18@(I~>M<3mIDw*rF#gMtVJQy#3LU=0PE64>QI+62-jkcWcA9-NfG zNeLW1!C4*@M?kd+RGUEg4%9A;c9ekQM+71kh>#PYLjp0Pzbj z=>RnmP!j>y^ZyTdR&1sAR%g?ypEj*pr0e}`^5)1p`NU^|U+Bc%@PY?%m2U?fg`rbS zh<*zb4abamQ(4OC)2IdW8~T|}YC*9O;}`4wcZza|FEL-t)=@{e)sT6g>BPk=)Zyz8 zE13}uk%3bnJ~I>I7DFW9LTo`uF=kM|mlYjUGoTGKevAJNMY)EbB_L7j44yD_`oGYL zZU5}t=4#S5NMkRE&zu5riy_)_2?Thm5xcR^5uKh5Dy87Qxl*gIaDTyzaW0^>G z{KC(+hpWNL5G&cq9Ojild}gR$7)4kQ6-mbhN%ht=^Q%c%r^^t9dc^+k6yt z+2@t;k@e45N05uVCJptGAZyz20ze8G>^JKSY|EMyp^`O5^Lu2Oq z`#7K(JT7&fYv9*^I8TsRZ}U8% zE3xx|W@e(MmD$z2`|zV}a{hL&guC{}`Yq9vt}{JbYkfJvJ%XK?X_|8mbHP356myC! z3-2%KdmY#Y{Z<;Qx{csR*gf}X=kr*>Gw$1DnZtnRpCtUqsO$}h+W$%vRB@o_FU1^EIEH) zh4o2T*GZSJ?w%LXK8uaJe1Lze!Au~eyt%FsDEDyV2AFO~X`Gjiev?AtIQN}lv1KUX3 zVWXU*K=OYhWhbt9O$9P7WH$4nfwQ+Mv&kil)xA&nr!7AFR3&FcPsogko#s1Xl_ATk zz%Gb}707|)1kuLW`8+LS{S_}l7CG&ZX}fd9&#>2S+pn1I`S#l9mQ+J-W$N!KnyAV3 zpM9EBv7#qr#;ueCdtsF!%ip$##X*r&b$DCln_?VfipY~!^I$JRMltn-}r0l+q zb*vRHLPj}&hUCA2{S0*tMe>T-@@!{vjnRv3{nvl>YKh2Ef*lFH1;G!39|Zq@W92uH zPTVggC5tsZaqK+U1;8!ujf8(HD2ekY2@R+Xz7L>ZyL?kDMTOoEhY(Scv<>?u3;#rMvV2B z*Vw_L#}PGloNZvy^%k^+hT=!N56W>yJ|LR0x)%ssVu|?GP7YBQa{}2;3$Pk%**y|_X){N$PEIK?bsc$SMt>XP!Z&caRhuNBnP>()zSLpCm{?vkc zu)5!!wY)}M?c;|Fycur#jE#Qo!qWkzG?OZc^+U;=&K8&ZBVrk5Q`5$RLYf}ZFSmbz z^zcGRsMfx{1Y76oA5p>HL6(Q&d1^1CN(sWOIJxC&K^6L514GUSWn-Vdjh8>i5!y01ly*6INI$D!RwJj>V zY7#ZGrHtRx!xc6K6(N!Ge^hdAy&Wr@ZSY8on2lALt*jryyAJZ@^{=HF(3xLR$sjw)3-U8@E@v*s)R(-!5OfTFg=18D#U?2M8K zqdrEJK5VnEhc)|Ku#3(*pKPBucB2jZnT>|(X-68E*-5?=$99VtwIpI)aS z0yR}t-64}ut2>z#mtsYHhX3%1Zcgg0wiPdYT2oI-k1QNAnoJSO>&VY;n|x!WBVRw| zF2nL4%PVoVCiN~otWBBbOB1kAKvZMQk_dRJ!BOSPyyPW)o9f8}i{rX$ixUs=TO?1qly38dLbb&C!9d`wm2igr%bk7 z);Yshz@PW)>u+(`Vmf)}?F*k-vlqDbD$%ajza&h)3Dc3s2bKqfggpH+jEecXclC(D zx)Ol{AZh!{71J(U;`+Brx&|?X z_RleJX5cJ;{k;;r39F^2Bs*iY-}lHf$r-nLrPK^L=k{=833*M-(4{1|uFW?D-#u-5 zWc5y0Ur0^+qQcAN69;Q4j#XyZZD{fDg&e=H$B6c3_@WKS8Z{qisX>mHlDz#}Y zR5gx%X{m?Isgs%F5VTPvncu2ESSi`0!{4lrSefUF)2OZ+zH_vyhx*)rPI*7957~n| zonbIr&gvLqo3eBM(z}l;3KC#@@0esHr{FQcAs65PdNH5sDn>mwP(?3PhE^@iqq{Fr zyGK9fd^F<)=;@|u`blO#y_8wRYgplP=ps(!yrwp9(X;&OZB;>g3+B|tLD4~K&?EZY zSi|xonj3g;y}7yNyVVU9H+H0ye36l_&YT|7MIV~*4xP7gFqn9JGa#9zyL2LH&p%tG z?jvP6CJ#Yj8jjP;riK~g#DGFE4mW)6x>5A|dyuO6c;>FkY0SP>Il-^FIsM{yJ8wsn&$5as<<98kIl=S6T-%$0QOZa2KSZqCyD|*^DfQii zlErFwQ~N>r&Y@+(JS{^hwVyO>o`yG*kWETz2RF42;e6dP$%cGCV-JH2?$aCxp1hp6 zQVuOCf4wv%*msoL9;4w3&zlNmB)MP9=ly(7g}tT-*`T5_oGf=kNAO$vx6HkhrY?zf z@t8BCLMKAPX7ubNMzwQVG|ar5=-YcUq*_~)H^Gw=O}o#vs!n&$@}*CzLaXoNsU&%$ z2AQ)ljKNo}KM#vCmzSGMrjW1wZx%U<3!eB26vlPaVwOzw2tCYn)vja|C8DjXh=4$_ z+vC^B4l?Tm72!tFR@nCKV#_Em!0R4O zb~c|G6Sv+?b5h4LJdok(WWx?qj|Y`}*i>?I#8lbo#*R#1#}d((PD(nGY7!+p%*ph6 z|L?x-Jv+Lp`WQox)1m0MIK(Kl!HNxcWX;dJFzRP z^6PT{>I;*g9)K(}u@>!3S@m_JkU5 zz8J(sxw*5Fy3EqF58kA@1?k?fmG}kwc=fVhGrPrx2xnL9*ljelPSnGl6-ZW(ZgDtV z^sf9A#g+11ae}wq@G@;%MuJ}^p07z2M!uIA{@Q{{U8={~Z3#6en{iOHZ7)TK?6yQC z%@9ir44dv^Z;}tm-RdO zDDSOhdDQdg$hreaeoH8+usOHZQOuIZv3vm-WOo7knIqaxcQJay|KLP3sltWkP={ zG!4|@X}}36FgQ#M8(jL3NRN{nxuu0VB-&FZC(@d7&rXWubWvw(g-muDr(n5XJQIs~ z$$njsq?2Kb>Gyx9-q}l1ap6ig?}s}|WVQ~ms;=%D?QrIV7aNT5w9=6|ZB2eVLUySy zc|40q>KMwo%3`p?W?m&9vk>2MZ|rfvSrp3SV+?lMM}}2?{9<{E8j9&#YGN()dqB32 z>=?Y&>O=RvC~g&{u)5JcyinohS8F3b^$FhJc-5XaTB5R>M`3nP$!>LU!;TTFxC-~3 zhv*GI_9B0{%CAjAF$X`9!jmhP)4d&>eb=JZ%IV`}QDStRf6iu=MU3#b*CpX$)5H&L zkJl#EK%1bK{^$7Y67w~KZ>y$EA@h-)GTASg3#bgta>lsBgrU8~Ve2v7xB`n=(IZLy z*Aa>x#_x7ke& zhi~rfcu>|f;Yc`6jKx!$gU=gP#dF;{;*$oi6mw#?NCIQ!Suru`a3tzM_Asx6rlIbH zd{xsr8WBy1jU0fl#%0 zhmvSkexA~I!yJh1qBfbR;@0eJ^dWoXS-j@*^RIpqZ!VhlF>>c4-+i*^7wWcP1JKq- z^PmJJ8(vARcpVEg2_Gf-UQk%IqsXDV$mdFcg;@BQy3*!1?xa0whbA$CoXLV>t_HI4 zU4c_%`&jURYv+5<^8D`UpM!$!2+1EUS~qc!5?@^0MS>0cV`k*g@TD4bZf+tYBP8^5 z%ReY8PUGqB+++O8>V-;vK6xZXAG9S2OFvjWpd;IpM}xxH9y0J1rv&xD@d4xZ_V|l+ zJ$6Mi*X<8`r9es5{-uz}a+hJBn*rrZy|1{3b#ctO6`wLrca%SVZD@#*0m%oTkY0%Y{Z znY-Ro9t<{5F1&3GzS#LjE1w-1C?TKq+j;%R=tFRhQ8AvYLKC8$!%382v^drQcAnc1Xe6-wAzE(F* zGG7omCAZ$?AUsdltDxi08l0WE1+7$vJ#uSaHOoYIpkd%gnv8*2zXo6TrA|5jc*|ya zOw%k*-8s0`2^BQdiR#$svJ9QU&kXOG*Aw??6FI`JFZ;_dl^y;9az+rBdW>A*J(k=|EGuy7Ai-! zZAmaff&QJV!)98Eg0CmUdK@`mc;TR+6gv3%`s?{WRyF?o|K1rrq%)*G=BfEn!*PzT zy0vxxB7J>?=M|?%atFiompgT4@+{ic3P{ttjjuI95@eI+NoOtw-qaEeW+^*e^VDKZ zb>|?yLKr9_9S=q<{4q`ospameu?ZfUr&(uYkb)-B++SFq)sy#|K;@(QHlZkpIIlZt zJk&+;Pq@dE<1aJZWw+Pv6bE`LtbT2gC*AC8685OD8K2XoTbNk9qs{%Je{XnAXYLh7 zUs&7EGg+>>4m1~8h3Mo@-3OxQgb|W+^^!{!7^L1!ja}s%lQtRnoS*sKRdL$+(prVp zzhsyz7&tmsUFMcHE+jBr9hSV)q#MpGO@7fMGaEdks9>y?&e6y_UIK<9 zy%UP`5<-<4LLdY}k}t>eJoC-_7rZm?~-f?2iqJ!YNzlc{ZJo;D&pWpq(s(7!S) zF^;l^Mo_8ts7L|XtaQu#-j33&^t4HNuwJ2f=4a#k%mZAeeVXyx_9ke=RFdf}`ZN4>#EF26%#_U-w)fpr_4(TcQh z+BbuNr&4;3vq0{`X4F8Ns<}(Hx}Jj&ccEK_ql1tTQP0>VJCrW}eIvOr#0xUJx?ka4 z=*Fw4_cDL=3s0WZPU`tF%-kwA>mhPjP^dcoym9@cRCPR;U*3J>uqs`C`+R`0DhlZ0 ze*7=`JP{-JXOW{khXcVII2XdMLE_BMSgz@w;6lqSp6Q8uC% zg?{Nr@LQ2W=!>PMdIQRwvw%P^ecf&<@^l)*U#iN}<@`egUB2GdoT*f`-;#7*^e^<# z4HYTwF_{u5#xkF}BhA5KOV=7hjSMw1tTCHmjf_K;ai}tUBf~c`t~iXFD&wZg2sju4 z2P1A|#Ep#bpOM93WN{eDI7Tv#kyB;lR2ivpMume>;b0W17=t}X z|BUKCqltsj#KGvrVf5lKTB;Z=RgBI!MuQ`x!I9CYI?U)(WwaYJ+Km}q+hk2Dbjwpr_@pch+X7G`S;+-kBRkBzu`opw(?J|MAR7=c;Q5TYgcI z$VOAXW9Mam)0d}29zT8eohdHx%OkOVF?Wmw5?zqDv5(A@^YokFrugq1Q9JxI*Cq}} zkdV2J(n7-~^?R}$x-nb{af1Gk7X_c^ZwQTFjT0K4v@6tXe@L~N%way&efo9CIV6qd^Lhrbxe>T#) zd%35+ef$=UPwtzm(jZV9{C$dLEnlq1x~<7_qjV{5hG<$@004SUT5S zN|%2Ks{gyr$8O4Wj8> zr|lNdN&I)6EdRg>!s$M^{3Es9$4%&8O1eGYJ(tZgTK;--+sI%V<0E(H?wz$P7FjxK zI@dw^XAZhAF;ve`J;Ul5R?j%_fB8VhfzR;u3}4T<<}t2$j2k}VhR+D@7{MJQu4lyc zjFb)|rNhYPF|v7#MDG8IL~bBgT(C-3`Ng}bjr7O&|HTIu!L?$?K|7U@hcqBzo=j~~ zI1Do8HO&K7hxO+JQX>;xs>tyekJS{(m!h+zlBEn3TCXqh;oR%BfK0;RC`8+RVWSK{ zi#z3o@fy9II`3K5MO)%7)gpiin;eb%Sb0cIi<p%{$6(L)E*P z+{rr^a2Tp|PzM2Ju6{4XHLdjcK^pOKEAj%nmGPw-hFzXCVyC?#tg9r^rEcW{hwth$ zP}7|O@=(oG)vFuPdu)209}u&T8uZpCH$0Q_*sWI(O}J0;U9plnW&x|Qe!%9|hrIm$ z8~oss9;r;Z6M>%Yg}-r)vL&+c+PVy#Y+0V@h#ZP^5?@Wg2xjjd0Cmgx_DAs?L-&)F z1j@*~k`P7Kxkh&LC>42D;22#r!sotZOk96sqAF$$s7^!QPeAqzMXM z>QL$$$=)8EoCam?Avw%O_%wKR$NdTB+nA2-D50pCvWA5@@%8A{Q#6QTSgunW9VE62x71S^pF-0mjacR8w2z;Rmy3%#K8?!iXjwLWSAlxa!7YXUuG zCuslGUs^3ts4RcQHuQ;4LW*$dkKsL3Lk*n8$nsDi;10?Ww{3ORDfu#)l7lb1b%KXK z;Gx}L9$xD(ao82b2UCtaW$ibk!iYvaDZQdod9Uc6@adL*DSXw3rvS)NbPzi&j^?u74Z!gOT=AEXIatuV5iPxb4KPw3 zC^jLcf9;_!*0ZF6+T|%~DG7T^DN_-lSk(o0m&^nwt!YnFpFYN&%d2prRUf7uB^`sZ z9NL=Y*w`I6zZuL$OzMu@9&+&mziPGQ6ehrYH&7o*>DO#gju_0fvOts~`Kg$bYN|Y; zt^ybLN$id|G7>~>fptC?v?R3D>k`k3T+X`lETCdn9L|OghameWO{KHwrsj&RmU;9?cO2c9zT(#V^F; z$;U37{d+iZN5nVprwVT=m5HTch*qj1yPd4G()7-Q5r=N$1Cb+(k#4T9w}^lbjjgN3 zSdO-&Nzj~%K;u7LrI5N#l-KsLAi#H(*;Qjtt)yjb$%9}BK&!Sssq{-JUoBhJ z(%&Lcon%7PLq2(w6)NMRLDUJ&-LwUo9Ns->pld-^nA~aYeU);a&{eL^0cl3_QlVGb zyuQgr-aKIb#G8?%mMC@`+Bb|n)z`M#WWrXa$_lp4Pl zWS(Gj0)q@vF&x$}XOm!#DG@7*uNH5BhZgzW(+5OwbQsDw*&r*|OwQwY z3>!wt#J)2~4uOzWheo_9!K+6fM0cq=ezv^WuNS8Ig{5;_(W{a)~T{2#@b^L zo4LGm2k_9QD!34zT}sUd_twKlhs=l9+Gy|-S#VKISNEtz_reUe=4eYYKg{nPKl7xD zBb}HBt@~HNVu|vt?RUUK)+99;tXD0caA>c(9Qv!YhR_+bxNtncHi zL)|Srp*IwfbMT?8v$5ZN=Wt9?csWoXk`)Lu+V=(GLb*{)3W{MqT z+rG`tHwm3MFn?=z)S^6%*D3|(JK3+(aQ1;>QL*mb0RrFiV97 z7pW%q^S|TYKzTNFTf@bsUb640ofIjgH{-gQ57Z9{uKopkCkt>5D|@!yGSNn`NexX zmpdr->=MbmuH8d_RyEyrZr7%?WD%}U`u&8`vD;}F4cl*&232|-4Kgbn1}O72s>ryh z{tPYIF98Lt2-aepdRFDPXkVg1LoQxWvI#b8w|bMr&73GrRSTPjm@Td5*;GxOTz7VE z2s<=omkj}hZtM{Ur;c7weL}OF5C{B7&_Uybv;0FVe*YrnF8#P%RzNJG(D%k~8S^UK zc*$|`qKOU?Nz&mGsIdUOm~dHv369t`3@-3x5(}`POkXpq^n$FQzbY!yKiO+VukFn+&3T=qa15*uTspsV~5Op|^ezad5BT2rzrfmCj%}c6{<)OZ;vh(G= zt3JNx=i1LqQ*X0}w2vC8>z6xlY7xl4@c}}Be|!73d*T!2)=?!X+M%gmPHkdV#J*!d zZ0dFBBv%{P*I~*E5>*>NcYE?DkC%=zjrjktBWeZSD7%2Ch(gHWOUJYx{6U?=7v&sr zn5`;YTt-eoUd6Jxu;wOu_j$6pm*e(E2DhnpzQfrn-feCUZ*834@vNb;`*FZ3wSKvy zBn{ch1W>-TwCQT4!%LzOMjHr8Ap3X{tR+ z4=xp_E&`Et+?~} zmHlHPK@*!%u;3dfg(7A|7ExUVeHDAH2+K#)HxpJ5PKAZv7T!34t&bPc$jeFNht;$8 zvnHZeCA(gqWCTaAhqSM{O1UYFZkslTJ9gdLiaELjnvHN$*rB}{Gh9-2Iod-|$2^N% znu)W9R+bp}&ZqUR=+KbQc;)$p5@@N<@?ChboE@lHx4{Ts&+h%kHJn;?V@*c!R^aMr z)80#L3Tmx?Zd)eAG#F=dgmQ4Md>0HFJELuB`SvLfTfJoap=RKfEXhp3pN(9vOUaQR z&uT|?7IEJf0qn?cO-MoW zdBs*d7zuBF-}efwa6@k%d1EOy7$7}sHpsTp`Iu(01iC>&gqe~gGl{zgYTOxe5`2>Z zb>D&&NX9SA+YfKTy4d(@8!ULL&*l1@FqIZ0v89s<==RXEsfDL@m;UPNH@+fe+8XZ8 zemf+Zv}+o0qRtIMke!>AajELtfTY(cws`JQ`0R9W^i`l7DZ#z8acSCJPu47xFehYB z$$^a%J_lHT8K_ZT90*3rutr(){2>gXaGj2O@5iJv;_9T zX~$og001o?)^vaxDaX~Omzvko)zM#$s-lXe5efLePP?cXQaO8%x$5pvhugF7V%T00 zz|Bq=577oZ8i3OdjbRml8*@JlZ_{?~2XlLeOp0<{)M`X9(f(le{tjBwaFUulI%T6Y zb6#D!c_a>6cw3(25YS`1$tsq9S)uO>x+eRRe%ErArkK{rtqZ1I;j^TaV`;Fnw2#yl ze%xE|ZASK^oTl*SWc6z_P$%^=xbjW{uy{Z}aO$k6nK}Pq_~_@(kYI&g2^o$>I}ZJ4 zsh`Yw7dya1HxrJjOnTuUe^rE!qd(~i$JLkBZxZ#{+m`)PWkzfHe-vdq%~Nwk;G==d z>HB>6!oBfWx`e&`%-7SnKJ7%mrK2i6N5f{=iNi%DhN?*u~P31LvIfA7k@Z3c?)`)rCNl89NoZ611utZxCGwY z_iszv0sX#Kpz6o)k%A(~hCC%tsK;C)Y-8Sfmu*h&WUp@idrS#01;(emsepxPsHz_< zwImYNS*7wqWDfqD5D=^3G8+sxwP%};RpU?Ngh*6iMdNNYB>6cUKej#5dcSkC!A9?j z@OyRTqbIS)#9HdhodIYjJ{|m=0D^LF-*_6Y z%&yqE#i3+xjS!GCzZz~>>5b2}tM<2+?w8(+Nm>ES-kr@XCAU|-qN7*!&lAIe?SZnq z5wm1@H;WTF4&M6Mo?e(bYEJzNru9RnYHjobbE%|=$n$1L&K?W>2IK3!_@is?OvJd0 zc}@S_j$b{X)42!s9!p8A{AkfO=jb^7xmNQxWssf^q?+fLr~X36dtW0S-0-kGZC5Jy z$rd-n<1S{M$Pv_8TmGuye#WxmG5&km>xA;{Gf>^p)g*r}*Gx!V#fkRaO`ATL2nU~W zX&B`4^Z&1N^A8IjUM_YQjouBnY2B|KaNlSW^FT$yb*?p=ZJA)ST_aru59YTb+t^R}PzVta8eHytEr~h(AMkwHrc6 z>^1M1-^oXX<~Efp-P6Y@gb8^>9{B-8l>v&z*_wB4X-uV2_K@$_?97WJ0Oe9ZUTg{O zkmvrz<`;D7Pm^bA?$MN#ctFFKMX}T3>?!RB++KWss~QR;C_w?9dcmECwClQdn3PO2 z03poDwY_)zow*dWqZu!s&b~;r>f`frA}&(qo`cLX6Y{(T+C86O;?JteE&h3Th00XQ zTUsvN*VhH;+Ha6l_j{q#Ou)`le+9Nlz-N-nLew3aK1M`O>)Ld4>SKmIS*lJf{1cYH z)ck4L(CE3g6J)4jUorSd)8|coTH;yNMIb(3@?07%mV2v;Viyja+WD)QrX@qRrHPHq zs?A-~zqxm)^=S#1^(}bbOy5@{yIkPzKfEI^%8AjpcJA*duVGmUho|c5twK(YvNLs7 zI5Aq%7{^1b~nrI+{TH@%jJj$#J6%vd-7-rFAx9K#luiUT9qCQds=aK9W6O7b4(SV)8s zI~K|-08XCn?yv}3c5VPNwKHaAZwS)Z6A2tnqovN0#@%L1EuWtFmo8D3Oz2S}jg@Pg zSE|GC3*bj(cc4m5#qY1W)_Xy$(2et(@pMU{p+OUOL?V5jos%yu<|s)6e9v6ECK0GA zG`TUx%#unr!vBo|vzZUFQeM6wez&IFh5TgHSv$&H&0_ODRYi~x}M$s(NvB| zOyQpNZQbjzn4KTTZ@rb{{IPf@YtoVvGLn1lU;08J!4r2fta!`t%Z5MRsuYo#U1uIw z94CmQ#6PubUr5Px?g-tXE9(7u+ooz;1n1Vw3u!ke4G#Ul6j;H=7wwUp#|>e*OI3}| zqt<*-`|aTO%EBaecYq5=AfY5=V67!ql=+9aky?_iA?9%d&x5%Y-<2VeT;S(} z=cjF=Tn77#!}>I9c7UmzW#PQ;^HvRw_*Y=D5bf5%>7O-%2a3JQ@pp7LOvuKZ9H*P> zY)_%1CBO#1?BA;0aDk|QYVOt+FE}RV@Ysq_J?Zy*COH=U5&By^B#(u&*=0k~aZ&Tu zw!4y@9b7;`n3=W{vz>NU3g7yzWcw>EXlR;6na%9-1VA@wl1JTtqdD=cgd^bU7h9Wo zebW;W5nnoU zUnw|3$yRq3qA130cVyep?qq7VZ`~bBkyi*sb z;G65#P-u%^0dp3Z{<$L)auF$6{d?bKyjeMjqj+@=F(Z@q*2p5}U0%^CXLNjkj3Z4OMMUSVUCk0f3EH zf{5GH=a3LR4Z&q~=%pr`(Kk_2c$K;qqOw-i2hbKLp({$kc%nVk$R)FQ2?1{8dGKF2 znv`M6vq*iH@kWssBd#OXq&Cwslfe9U$gIBs{%7ZqDt>i^8L z7~+bIl~>&%=f={KJcZiXNpL}nk{iH|r=@4ZL71jjyAP297MO>WjB?`1j14S$oOPf% zDl^SUGO+ddE8S~6UHf>VRQRCP`Cg~+&DonKhN6osHBKxvq~+fKeiL#1cmV1U)!wo}oe zw6q#RMh{5P@Rly3rKiukniv+#3JX+T-6|xz30Ypj@jSZfL?Mn}As;Utc;HkZv!-J+ z3&KjzAyCsyF(lX3=xj-H@PRH}y?qAM=WS%MRlumNeZcA%c?`(K%U1hxc!PKG$%g$O+oX3K8E`BBXXZ|UpkZA$ zyxvTpFJmhy#YdZFlRsk&2OC`uJl8VZ!dwc*l^W}xTHdD`gT=Hgk70=`Xyx@ZdMLbI zSfva#3s}0X;AcCC+iy6`gyJI|P3BTKA5YwDe&No-Hd8kA*z+~ZRibJ0);M*09UD`t80d(|>r9j7zJY4YSB0Faos z!02QJuWcJJ^2pLP5#15}V8*|k>N4_o>v4`kBbV!|#zknn;tQmfQ=GIqT}S)VTh90< zCC7t~0{U9~Kdxb7#(9e)Ot)&MVsu;Be4JrNcD5|DwJ<`2ZtqaQ)GyXd-p9Gv) za?i1Wr7MQAee^e`7)QC6XhaLeHjlzz1w5x0n9YttVCKdHBf!>~{6DApbL|6ST!@1R zzkm$2UK90i+rxLEA_o#XMGxgj_F!3T_3^oUSaE>pQS1=it)25?<5uLtsEEKjljUc= zcHGeC9o3VeeD-kd7L+sjl3agMvEChkusYg}uD1Hc-y@TTT2I_3;b^m1X&%W`9rW?_ zxvhac6c@+X-|gL3(4<9Di8z4`c6!8-{`3mvONO+_yLg{w|G#}1M zzpQ*SirS`-Cg7bVrc!<1!kb)wR0-6MnrJ6ERkXoHkH5>-IT2x{l)YzY*buP~IB9+4 z#o>Rvsoy9!&;{*zkDhicVo!3Z>XMk3^|~C=N@MP|D>JzJQ0#yAK?Pu$x|6P7{gAEx)MFflu7zB(WMQR2VQBe>@QCdX0G^Ll2V`rqr28fi1Nbg-B zBqAjOLZk}_0YVQDk^t#BU%2Dk`~LCW_YU)}?~l8_{KZ;fo%8HxKl|DBxA%E^$<$bE z^WM!OA|hhv&Yr$3BJ#7Hh{!tmje;LUB*(J+L`3e1oI7n`5o9;V3tKGYAp4f{$H$)} z9{%nA4zUfJe|>l+>sD+2%WqanKZh5~Z+jQB^{i}C`{xlg)onYCi++D~d*;ntyz2i6_P2!)`Hb|)~c3-Iuy zKJv0%-!iW)@Vaja;|94gRMZaP+WyTmVeqn~YFTe)` zPPXuz*i@$>n*}#5w`^M{3WiSAW|#C>R;UJ8PgZn4-s&m%)wOjYraCLCDm_3!$~V$B zJ9(?;jlTHXPOdjJ<3p`Xt!`WP0}f%WIJy$BV8-8$W;`XZ9zgkuuqiZ$feo9lNAoLz zHUc;U@FbBqGXxI8mIL}=tU;{XJWx2ljQeau?Vnam#vb?3$cU>~wk(i@4Tu!rEwnk;(^#TUnQ$NDDM z&Lx69XhvH6OG$7D7`5)}A7>yZ!{gnTvGK@>vIX?hG)93LT&hOJ*Lv3QNkw;ofD?hn zwiZp_FW0<`s%k9gHhof&M>Z)}Ez3)6)l*RrP&MbdI0O=jG#KwGdU0lwz-T727y!R@ zP@K022b=vl<0$Sdnkz+ShY=~c{LbpId@Oh4-AD&W&8>wv&+ zBd0Ga@0qJG!=3V=a_UwN_Vs@AB!qT+@=GgPw6BNBFaH2rMx&Ka4!*gr<^v z{W;a~Ai2j1OG($y=eUeb*OI#|*LX+^h>ZArrQV6z_(IWm|D_ywZF_4Vvbj~kor8@R zbsB<67*lM~*folp@6?G}4_kYB6~KXV1`0tsku?v@GI-uN8s~SgPayb^ePNSCCLQRa zupEf&X51nkU=;E~^;?_!3OI|cU*MQYp52W{{@?|f+Q2Y&%i8V;VJI9jL&q-V@(IMH z4m8DW@dk|JZpRO&0SoZ}5kKR?7llrk4da+KU^$H8vw{XY#es*cIwH;IIsYSvBo6ZK zl_QDQ_2I_oDe8%Sa4)6ei)Z#0sR6-pe2p8D^*5LDHJLY6o!nh>Sx0K1qyXB}^0mb4 zD5ET?th=COYJshWW+o!*EWtV)n`uU$3dQm3aAS#mTo`{53|e-;GH`FXV~r@jdLeh1 zhJ`JT;zW6oeYclwVN~7_6d{wx<22D&+H74Ex|!b>RO#epV*B}P+`!c*6>|$&ku9&S zZwJ0OUimF{p!sW@e^~alCvpD1Rhbj-1!_bu5SS-&2;c%ps|QyZaAiWA zi4Z&vnLo^N!;xzGrfFg*VADJuW5974M)Qhr%%Fw=oVPN%FpWTGCd>iRfooDSgA-{m zVkCv$%8yj%d_=R}Ws)FtJ@;j89FK}2R%jHm*#so8tWM`dPtg6Bg7?8*@(QVIQ!F)z zr76x0;s<#MyARTbbMdqzdxupsS?<&bGrkH1#a}=L;ffL17<~fCr36$TUXyn z^iTkoM#yV(=h4k10kRcl%(528;saxchW{Pj%+Yz!lmcMV3e(%CXUlz79@GzTwJ6>N z0Ey)`Owo)Z^mR-Hb1$smn%1WH=6zOJNJ7^#$~Ll(J((FvxDgr8?E(3mmuNp;uBRCt z`R_2Kl)ymi*#dO}vIF&P5ybMKW>HYI%#6z?gFH0~L%MZ}JWba{%5&hPp|~m){{yG2 zZ(9m(MNQh#BMx5KL+6^l-?F@|aLT%OIo(Ris<&+-CGMp_lve_@I0z!ukN31ZC?5v; zQ4jS#fYIvE8yC&6AS7jyhjd^eWW!SM1V_*vvnHK6eAER^f$G_sfwtpUf$5pC%>2f1 zEGH8SW12&_W}g54%f(@~pt?KN3eNMRUUjFYf<2-S0megc>EGrE3LI@Ug9}cQmKh1+ zt#lFfqCi|fY;g&K{AfXV%#p2q z+!x>|ipMoWw2L#x(0pHD9;AnYn_LMOfNvN7hDNHjjnOAo>hXJ9LDB%Gsgn07^C95t zxsR_2S;9?F(dZQ(5=HC51+wV4F_2wN#H57tixRmqlTmAxC^iB&Im6^&C&lHgc?shD zY+j@!KpfCTkQe}NRnG%K+S82CSHxBnXP&^)e7(BPFKb_ig#h@Xda~d{_m@~+E@*AW z!LguuLoX?;5(bCBOM}RpNeVWlikJbaW6-G8RW|;eRlw=4c^3q4AdgQ&^LaRyB=;k} z6iFi^1A{4=h+|9_Ih1s>$v!6*msnMRj$U17!`jzjBESXG%2b{hs@X)JY6kRGhcG1| z+|nf6XgY8b{O|%ZUJoxtOnTc*5sU;ffE4jvqpMMaV52FCFm7%g)-6AXOM|fUfN@m5 zG(U!+fTn^WiXq=?cT(#LnJ4%zAPra)16FYC71**UFa=xg(r1R=8fGMbP1S%ryWWe& znp&%1u*zUW1+W4?<86~0ud88(?Dpoz`BPsIm+Gb=p<1Cf*rc%O#z+Fl0y8ba@(14h z9B5DyhN%yX#Lo6li`UH5o8fqCwkBpAI`18Rf*DSQoe_sIDPUy*OjL{4ERJ+wx?|@j z7Vk2BR#MSRJwR~ua3}?eYz#N!kg*Ob=*NPi)aNb^u34$F{9r`f(n;<+Sj{zPXr3Ld z5x1ZY&P`C*Z^25tO%Th%N03oXL@#O9vY@AQ@eCwjnK`|7-OW;i5&MvnT|+J)u5Wf1 z3eMjp@LgcHf#pKvC4Qb|7rrfb#+Yfj&k_x`01_cbhv({#?t_G_U6rGUr#QA~&Ie9k zZw`V{i6l{Y(J)HnK24}Tr?N1TG0e$;EFzbK0a_39F6}nk9NA2ogI`aa8WDrdGBMQyfs2f9$m+IA$frpT+T4 zb^YL49mUGlOgf7{LIU!^G0XfOW2Op(Yx&BI$A|~-xbXua<6tetj)s33$4t}Z!3XRx z%qQi+KMya}W5=jPYJW4 z1hR5r<1-8OFlo9}aF;kI0x#~db{2Z`Sy*a4@Px+3?U-9e5=DV7D%;Emxgt6Wa$^^1 zLA`H)S9tiY#VvhfQb8nfPRZX}hRfoYLRcqR3ca^mp`q3MPw}9pB5wZS9FATGzsD`X z*Y|SMxV@P*Q0qvSk!aXl>pob~+PzN9%oL{!HhGtdD(p$Y(N3Wk;QT`hXug>XR7m(dny6e5U_V5+v{rR=~&tfnlnspcT5YC+8M<2nB%t$ijpiYp(%TsLF zzRF3+1PV4J2u^~29%j~K=l+(+C5BhMwTB^GvBL4Okb%7*?z;RtkQ11dx@lv@IsPfzQB2M^#l^AqZ8 z4D9D=@@UE+veSVlj#(k3b6tt-C3nQIfabnGXm04@4}o{**8bj~1zsX=9M4n|U$v(? zDahyXU}z&rlE2Cd?1CG3=ps*81!twz`l*GmuZY+o zW(2Y_2r21C<*YHp9(3Z+!#MGJ88D7I@RpxJWGuCEzr~l}p}Y<|?Y+E*W*m6YZor zIz6n95V94=T?~vDQ0xzjE0BxkVGri9v$Wr7(*!7ep(|jm|fEv z3a2h9@jimL1q;*(7MMVl0$!de4>h?(g&_#q+O^Ca0!v{T@5{wGP#BNU95(JDb4I)n zK^>FKzZm+Zk9!46v+f-qtq(^G!>{K~z15$qz#9|SZg$$IIQV^OU|nV5)lh0Z$y*0W z>_d#r#IxsM*lY9DX?5_GBy0&j1M=E%{84xhcM>?z2FP}Y53T$iU!k9gz%h$igE$5{ zo~U2W%Ld!`B3mkXBs7azj%5(%I`;|Wn8KUIgA^PC+%Po=;|?u0qWU>dYta7+Bw#Qc zg5<0@288ff{2BbXNgxo&8j`)deY1Gm0`@8%C*|retN3il-LxSrb8Pq1dYK7;xfpops)*IAR0O3 z%~L?my1WKPA%GJs$R>{!ngijBgOT7cgcnIL#58~}JA)vf^AlJ2FX3P;U#~bE%H;5)=K;=KGs&+Fwut7k3m2gg zdaNk)qEZ~-G+!0m7CuB0J?=8Mrj%k}Kv=yu7z-|%@yOUk4$w4(;7jsU;QUCS3lj7a z%>7mYTUmNV;C_cJ9s*h{`4NS@Qu0W@*59cW=ui-t7_7%~Z2lT8N4pNBFb!%Q=A^W? zgQ}&sKDM>Tkaite zo01vIPdOlXV#p>(K*;6!q_){xno0o{)~lK7#AI2#lEvCmS~b>srvy(_S6K+OUHX7l zVo#&jwYjW{)l#LVj!uka*4ngZKxN1i!4v+z*@9ZDNx82^ZsbtjH*?R`>aJHnfK1_~ zHHJ-I%UcCckS8y%s`1-X9@ivmZWN~ttX8mw=H^b?rLHmP7@Cz5Jki@GNaEM1^tAWq z9StbGWn8{mwyx>#zl$*xocmhJWHmh}9Oo+X5(QP<0PCFO(i^sO&W$m*l~!w_rKO~x z$7?hOLID&Cpilt+MPXQ|0ficHO=VP}2^N~*HNhu@CRk{Kg(g^Ng8vJYDGWk{QSe$o zVZta_7zM8h5GqW=2-7fYstO6CU||$2jDr6KE?a1Vg(g^Ng8x-Bov;!jEXA#lA!cv^D6!-tO6bBNJJPaQKhU!Dk zoaowvb5o1n6e*!vEOkm**+^{50p&;BpFSUJ`E=O6@sr^zu^WmLmdY7)StNg*7o+5--Q zKHf36ojm?P%X$1j-wh7U&7agyRjv4EefVedy~&@=_Y2GBf&ndzNoK}Gx?o5|SpM*{ zV$Ui~<8r}s{{iN!_yG=__gV>%L0^&@{AY*t&v`F$f6jYhu(Jd+CqjJ@U9{C9CD?1; zpTAXnPZW?m_y^W-@CSA=Kr>V)#w;t!^Pk7Z{1{=u_|Fj*p<$~tPinQWZo#W#Zs56^ zjvm$vZRYt=o_|0gJ%2zeYZ7Z^@d_4A75_95!{f&YmC*kjp;D_an3^*&EJG~AtPbQT z?2#7ik};Wet*PP&|2+xEaL@?h&zHSj?Xri2A%!rc5QY>#2B8WgSz#nAOrQu8C^fCLu#X?Yfxe(MY1hor$DSmt(S=dVsmR>R`tz_hrCh{Eo3rwuIrmp3nRX7H9cYbO|wIUD#Fx(ShNY7gaF zzZne69{QiVN=wj4#B$Bml`d2vvhsg!^PJ0sz$=yA+cCe@Dp(x&Gf+ z`Tu_eW|*r9wopK6fPto3BYECT5`c$uHMyt2CNPLGietv}pW~LHymS~(6J!&)nLHNI zNL(iHrUF5*5}-i9L4FC5s=zyrqnhzg@ZaJ16y6kA5Aqg3;F3UJ5Lty_IT7sR7Qos0 zxEW1;FPv=)v{67yFt1=&>yN+3CKb|rWP>>sKCS>jwQk!}LadUM5q zStu_B2a0o{ft;uC5%@H82?FH)51N8g9s`cxmQlQN90?@fXFqB|-UaCpW+*Zf$81HC z;LA|18Dq|p4FwZ$69lX&atO{u@hce~G;gp2Buv=yO5oJ^u3E5%pGM>i7VywaOYkMw zQClsKd%~HOv;;~^P+SJM1RpB|;YbE-nK+aSpC_^s!4Gi%#TtG;%+HJu=fA{}&A71& zKqrxh9*!(x5T#{>ov7joe? zytWp7s;J=X$lmlsZ~+{(qd~Z_3~nE<8b*d=EO7%6E(cj3Mw3PonV`)aNQK}&;=Yn3 zSt!u>6FRB%Up2ZHkU%b+TMMwfsqr9eUzU**?is+KaP#@S^8_$(fdx(s^ETt=i5NUC z4#%*x85)9ec7;_lxP{;dNXAk4MWMlrc`UoJhnP3F1!SWqpx|v_66fO$dV@ir_<}e4 zA!E^!Db7y{MRJ3|(OOPD|1+NoKKPTRknO;o_J3t6^x^9yPJdpOSPrC@l((_(f@djE2%ds{_Br z?+YhRVPC<6&cWMp^Mn}F?#nVh#=m|A)V!^Sh-7+|2Uw~L)Li2+l!@;BC zc>zoQfR^{^Q^EN|Z)j3!$)+FsuC>(qy_UfBEP!EC7gs_p8yV&B+f7unZxP!PcSVAG z2xF`rGmGac@VNHQxuAfL2fPl~J6|V3dkrHkaP}v2ODO40M^3F$?ss8V>#mC%{r}Or z?2gRanfm5&!=^ZQw+A0~r`|GpW~v;9kQUa$cr?6t`U z1MAoh@pS#_+%F@F`n+@>jrg8V8bEyHSEPXVjT5h2ze)_ay-NEvGuqNMQ{qnY`KtI& zhWonq#3FvajoUpdv07wAu^yf8o9kH~iq?p29QYG7FOjQ#wuMA?Z73 zTl3!W4wb(><&z^I?Buh~Y44F}UlmCZ7?|0bvZw+CE5EPxROQ5S9?2TJ_>G!a{lz);4lRQzvuQ zu4_{Al_N%DZ#ZM;H%sZ{2V}u^$8{L%JuODjGBxtu=I`y2FOd=9y0uTJ_MHs#5Yf%zS2& zLD}(7RdEPta5~At(;~re@)ygbGq(ztUPYgG`blhOO12+T&%dx!=R;&3hPZL(wW3)m zl``tA4@C7x#`tD7*}+Qfjg}zX0Dc>wQ*FK6MATbuw_^tfBco{vC48A9+_JAm@VO zncJs=s`#;t+;Z5j-wPh~m0drlq#4kAhV;GVv2~=L_D?R>v|1S>sSq62n-qb$XgwT}DyMFRQv%_DZ9Vk>K#^ ze0XmlN=8}E2kt(YuzIt$*G1ErQ$F&kyw!DE5Qy9)=Um*R+OO zPsId5-DaHl>ZS)cxU%Q>R>>A9tWkk>P>m2gvG*@HC{+1&rH|DcOx!h0g4V@=>kY4$ zNXs4THRxs;455hsxPlST{1U%c{N!v$R6F^21oQkdD#7Kj=_lS-2Cc%t_o?MW1();c zKUE#X_1@T*2@Gh^%joRS)m`(z4ppK%IC6bFWfwkDK3_*C|v3;P*uljrP5Emq-Mayq19KI7}T z-5oMP004G-wO=P2I97f=vwC3_kMQh{tVPK-D^G5JM=EzHL5wx+1V;nQjPs7lL0 zd(w-P>N4tH1JffBn10~BYT}fd+@3`Ck;FQ8ZfG+?{k!7$ZLU@3jdMx|((Y?&*NEW& zmHdFHrd)$Fe3Vq;Zvl(eK56wb&r$xYhiO{e7!UAgy`@}D9R<`HnoRj~r!B9+#&diY~qSyyj_r^Gmz3-3t z97pEU^SEkL+RP;g?ier;5qw1<`3t@NUD^_!+~Igq6F;hII)yz&OjPq_7hVuq@g8WB za@X4c-{M)sHX8ZTf{Q=57Oqw`z_07ooN2z=;D`HcbHvsuAnZNDWvXM+;@!!HV5_op z#uehrylgd0it`EI$4@en#gA(l=p-8#G#08qOqB3O+pvil4sMAZQS0TEM15PtPagEH zhP8IcIKQZUsj=fula=`X%*PrQyn|<-{KX(j^RC@by>i#Bt|Ul9#8xsYHdJkXhu=VO zej(apSY4%1d`H`O$E?-@FsTBh6yI#5X=dp8@Tb{Y$JGar8Kf%9RLkFL@fY8ydiP)y zUp6&|zD|ytKK(OB`@}4Fkhqf?>(^MD6lllwA95ODHE}qwXZ@kY0ovDhf})y%-51Z6 zUbb*NNOT@J`%%DG^!MEZXO|=Q-(df>xa&_puI+ttX22Ou_f~^~cTHJ?VG1IhQf6>J zb<>&K$IaD7UZEA>1MPY!v$^PZ`XQt1aGY%VOn>ol^5XU6{i6v{ncrl~J2s4MnRydh zHJ2KKUbyqMzPA|_8_TsNCQCOhDlPrGcRY(b>N#ndtt>Z)bC7k(TedN@aut}K{RwNO z^%4A~%iLR&&sV#p*!`laeXck=%ASg|T;#~3g59O~(+-tmfx)>AzYZAWcZ~E^Tpf^? z&$AwDZcEsr?=UN?-re9^!yvVJv6ZdVs>x|lh=AFcdlvRNU&;aw@&`tbYd&^W3R-8^8Z z3h!?k|9)Zkx#xSQ3rstQ!{_P+CynCn9=N{zlj{wPnYdrlO>kyO!?)SE>{AzB=5 zJBh*heG`l8;*MNn+&XHQW6}F!C?k5q>h3>#r;1&J+>2}EEZs@#V~3Ea%!^)M&7<9k zDXcGT3eAoqDGc)O=O^0OKRw{V&03NUXHVWes$jD3yOQ^!F&2F+M|qU2W2zGY^O2Wo z)v|glJx2X#fG289L{GF1zebSnQWJB%X5{oxMSXD?a;45tKsVv|I#E2^_0Uf)dHyig zsxe#tH75wY&x>vahwHXld!8nC#f7Wm_Ovz+EOg6L{8?dgzMJKyihPvL$Jtzk(jOIE zoc5p1d~MY6F{)1INoDbXJjouH`IpeyZFs{^6j|j0XM~zNwf?kIabY?Hn>pHjl3OMDVIxv1x=$e+TeP? z0BT!0fY*2Qm~SYoeA+D7;#t9YnhVYorep<&VM?Un1i68hiN~q0{07ID(t?)>5BFV> zxSf8dr*BcgdMv3yR!bA?{0-b4$!+<7x0x^7(aHjfEoZYhPNC1r9a=&ZwaN$WO@_yY z^<+FkJDs)OtcT_<5hxjN&shB?*x@z74kZLT6p!rf+j4>w_-nuM11CCy_uheb?NM(s zLhrz}(UP(hQPj=kg$PWVb91PlNbAc` z%;X)({+V-QM|bZ0zD}U5r~(OmAdo<$UD@#R7vj^xiKTb+4Ab_gV;mmNzp8P?Hoqv^xD zkJhGp!;4Q|y7Vns+#~g3p}vT;>gryk@&k{X1hqK(c_`TFVlDMC^_i|~ zIXT@PUhy@<5^_&WN+8FCsNR}5MsSLoUm2sn?SAFDyTGA4O{|koF}lW7YlZIP@7eHq z?|iXQkIhn(;MwmZqehP>bLV4__dP)8mq+8TM1=1+0(tAPuTvxWOOoDc#UtbGqLu&k ze(~tunpf!@x)UPf3Oe8W($R;q(Z1~h?mP~>A@lMQ?%b@m#1Xo*52NqQmm_xh!S@5K zwhkXTIYq9ix&In%JG<%Ug0orQr4@Eps`_EfmacjpPR+Wf0~t0QHb_{YBg39L&Rn+Kq}&-q9$C^(tlkJ)$=sHgT2~9nzMMI!-9C4sL7v z25P)YR78VcpKOZHS(fRgwUEbC?cH6kNN^f;Wlu^BWkv7NA6O&}xT-T{jhd9+i+*mG zs@!3kvB6AkBE8JG{@yt1&ED4OrBWs{$pHA1F0%yeKE*L6{Mn z@*lp#)EqcI>Qlaune37xKiM3xs65kqyoo61t>oFT_(bCr9rjVlzI_zv$?xCGhmGdS}gI(j!+Mt^w5r?^JqOfwl(W9s9crRDVvnd;d0$-NGuIT7Vfi!v#Y zDWW25vOjuubSTr+7^yIqXVvaAT+=v|_T|x7-tJ$z?riaN7A*L6RWZ-G)iSKJ9TYybBL($(d62?7!1tMcF^FbS>1seu!%DVTPpN+d+u;3REO*Gxd#7ePnj! z^h(1-@2FM5f?YO&1S~WSFAu1)PC(ZAW(!=?mh~5(zTE!)1myYfVQYi?*S7rb_TjNc zF6gM{w{@@Z+9@gO6uf?f$8Db;_zto zj(}`J%=wAn>PHoW61047FY;sJrXhQz_-5AA0IO#V(o3D?yU|^&-x6I*$De!4&iMkf zVC_Q4!WV|R4kzeEBdfiW=MQ&K2VQJ2zCDym-EEa8P_6RVOoj1HN~p*WOUF|QUc8V% z_QIF<+MEdcq^o5npEm~YGJU(e`9?kT?6zl6R*Xzg&y@G3$djn~LykK=c7w;BzSEJr zd{@qB>o)EajD0=XG|lyMPrE&xVpAtQb^1B~z9lang&EPZbOjcPCque*`+jl6E=nu6 zN#WmwdAP3I)>D1y?c?3K`Xc#Hks)JeQl6f_w71&k<+$DcyoI0v6%?+*ae1~83|eR} zU6~+?%5F`J{hjeqvCM9}d&3lt!Lb~+?ZJ+u<8i!AA!3)(IknnG(JR2zymw z@Yr??xp{hzz2k~sIIgod*6rs67&NT$nAj8J>K`oObG!5PqW@sYlj`{Os&Mc3qiTZp z#+Ib*`(i&hq|R*z0tD8=HfQH9FkHBz#680jIVns#cLhO^go=s zzDsS-zM0)tiWaNx4v&?c7`iw1t(sqzyqF(!eIYfuA$8P3@ak7SpA2OeilPD!n{?8x zWT%TBRT^9{-Q?=jYj@z`gNR|v*P7?Tx9c5k=XY36#4p8Px#;v-?okyO-TKN}>N8ei&(gi;?w>rHW^oq|>bl7P-j6(%N);=` zSltpKNUA7NK_a3cgk*_`h#^Eoh`bVcBry;OBq8stb)wzpoO`}=?g7vD-S6J-e({g_ zhrpO~jydLUjNh1Zj`i!dpupLGo&VPvGiJ=*^!>)4X3UuR-HaLUE&I^=A2Vt*91qQy zadgI}jq7)$hb#JsWh`=dr_SF#+Pme*m)pMcaoTqL%(lk7^Cv6r?OHgi;`sU(-+yy> z<<>7Qo_X}ms^bAycWkzMdh}e?l?`#(7x%}-+2!V)T=)GiryuNo|G+mvc1s?5+ea5+ z#T+3462dJ+oT-*!GJR1d$+R?v3MadCmVJgO{KbrDB{BHY5#{B=V@^qxibKEei*)u` zqu4(>G3)N*R3Bbbhb=J#L@fa zLmC}d)sDSxCJ_wU5c3C=c{P8XENkmi3Xkzjh%3ETiDV#($J+miG?vrVs>Oeyi7GJI;3Pa2f zd$FB#>^XKNrsNkI5`Rph82kc|0_&+?WczZg-hZfz+ zA8G9NV#{4ASDnev-C>!~-=GegU4-0V)xtOji^dt^Ni;PevX>%(8QKrui}2zUn*q~jTq`imW~iSk4H6#ol%b?pL^##l zHAs^q!WeTTHi6@E5y#PTI*{VXQL<66dM5)Ym9IdNL0t_kt4r+6qmR6h)tI*!S*xbB z7o3F%_f{jN*~oA+Ln&90xx=(*<6Y3O0NsT1FJ)r;_ldlUCHH8^b0nbdn5kWdSL)QX zz!sRhTNKK~3MBaFVD6#+e%q$9{S4|pq6}@Mts%a06C=a=1`)Qj$spyaxre-{Mp0TV zl(~}vy`xC-Q%C;LWhk`4=( ztQdf5DY`v=ophFmD9TCbrw7dpoud#Td+K!WV4~H$8iB5)bVSCtj!Qc{V23}Z1jnJX z#<@DA@qaKZbZ8h8Bp9wMt!n>ekDsQB3IY1F_Q>_&|A8=|rWW2=nO+tW9#$9O|{ZZg9II1k5c$*I|+z5C==MNXkh>TPZM2YVygg;ApcqPBvX@Upnd z9!al^ZO$rYcw1M|(9wvj%E=L8_2OX!E(J6G2kLUMS_CGi$U;QZpw;B4~n7v|R!;>i;BD>H)0!-l{ zP2hg7;arxvx?BrQ$fDCEqiD7m8n<^{7UB$3Xg`sj1N1?90}%TwYJKZBQ!ZYM%(Nt= zT3`@$ZGrTI$oL2#^G6GeFPkhL#uq}rd1jY$yxGm3wC8pDeKOJIC}WQhg>2aH~YSCOV5ZR3Nk5sBY|zG9~3|?JBDY zx>AhBY3v*qkSkz^J_f!}uC7g2igg$z^4T_NAjVwAjK||u?Kp=c+bZdg5!GT51^f8LS#Hk`RFed_Pl(Zuwd-fhO

9$30)QS-w!8$R0bz|zwFmg3QCCJtOY`_cO< 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 uwaKM~6|kKE2#;=j}~ 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 MdP51iR3_3usbjC9{I{|@+qW!^|JVB$6 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 literal 0 HcmV?d00001 diff --git a/barcode/src/test/resources/org/xbib/graphics/barcode/japanpost/basic.codewords b/barcode/src/test/resources/org/xbib/graphics/barcode/japanpost/basic.codewords new file mode 100755 index 0000000..659934a --- /dev/null +++ b/barcode/src/test/resources/org/xbib/graphics/barcode/japanpost/basic.codewords @@ -0,0 +1 @@ +FDFFTFTTADFFTTFTTAFDFTFFDATFTFFTDAFTFTDFATDATDATDATDATDATDATDAFFTDF diff --git a/barcode/src/test/resources/org/xbib/graphics/barcode/japanpost/basic.png b/barcode/src/test/resources/org/xbib/graphics/barcode/japanpost/basic.png new file mode 100644 index 0000000000000000000000000000000000000000..e05f54fdf096ca718fcb12ff34c591ab303407e9 GIT binary patch literal 2952 zcmeAS@N?(olHy`uVBq!ia0y~yV7~=q2XFuhhT=3uD+UH`e@_?3kczmsmks%3EJcn4 z-2Z>+b*-lae{<~n)t0yjRUGA=$y!ua-wtAkGg=}? z?~C=mJxsZJvh!Bwu2A*xpI^iL?vxZg>h)W1n%G;dq-p7T3zv`-FI2fFQ5 z*xZ<>*P|^9FQ4o*x7yjY4d@A{=$)sw&5h4rz4}hh@rwDy8~N2&`vKi{Wv$iDQ|q@E zJz6$dIKQMwH20~?+m>xbQ**8N-wO-hd9&}M-SZ@Qzp&8&9u45p6g&)y2z>){ASk~h zasMf>aFINJ76@d`kClUpuw%s_aJ=_pEU2LTcn1UulWV2}ON(@yVXQ4??uuB~_o2Yz zj%#oD+<@@drNAP;Qy*BJ9qBKbLh*2~7YrRQ^B! literal 0 HcmV?d00001 diff --git a/barcode/src/test/resources/org/xbib/graphics/barcode/japanpost/basic.properties b/barcode/src/test/resources/org/xbib/graphics/barcode/japanpost/basic.properties new file mode 100755 index 0000000..82ebd55 --- /dev/null +++ b/barcode/src/test/resources/org/xbib/graphics/barcode/japanpost/basic.properties @@ -0,0 +1 @@ +content=10800752-16-3 diff --git a/barcode/src/test/resources/org/xbib/graphics/barcode/japanpost/bug-50.codewords b/barcode/src/test/resources/org/xbib/graphics/barcode/japanpost/bug-50.codewords new file mode 100755 index 0000000..51359d2 --- /dev/null +++ b/barcode/src/test/resources/org/xbib/graphics/barcode/japanpost/bug-50.codewords @@ -0,0 +1 @@ +FDFFTFTTADFFTTFTTAFDFTFFDATFTFFTDAFTFTFADTDATDATDATDATDATDATDAFTTDF diff --git a/barcode/src/test/resources/org/xbib/graphics/barcode/japanpost/bug-50.png b/barcode/src/test/resources/org/xbib/graphics/barcode/japanpost/bug-50.png new file mode 100644 index 0000000000000000000000000000000000000000..7589003290fda08f26894ac452ab036681104631 GIT binary patch literal 2916 zcmeAS@N?(olHy`uVBq!ia0y~yV7~=q2XFuhhT=3uD+UH`ZBG}+kczmsmkqg8Ed^LE z?)<;Be2?yPLobcL%N81+Q3wiqd4B%?pYP|dm@J&Berc`M&MCK}EjPY=azuD;OxL!e zq{}CBZ|u3A+qbPqI&XjYsr;GUwsY24Sxzidy1Qz!_s+djg=hQu*<3uiG(%h6Z0?=5 zv(WoU1I_^o>BpJ_X996q^f)Zo#e7)=$U8Duz?728*=1_J5uQ`Mkyu`m`0?(PV> zcRJ?H!=Bf#fZ*8FPjSzZ_|s#;fxvv-l=^r}duq+<@u3rj)Gq z^Q&1aeD~Rs+gpThoq~qN9IJ_CnpOIxnzPqls9q`jDrowoysIZW*OxB3J*Q-LnN##m zXb1+YU($1Zzf>=EvnWuxQ|zlCUie#B<`rMcy~UaP6dHtnb3Jy8>;*dPY#2~EXX#3B&+V_C9Jy|FG1qUG zul2&Uuz+7FoH>2b_n_&UZgTCNZnaRaRP$xn+&Mp@)OVfr+@7hvWhykfEJ~H$27QTp z6=W8@@OzN@m8E*Bz-m!A{+vTecNJBYC;slGv_$-m$0Bw1LW7c3oMzB)YWd zQTE&yX6kpB&Aku?OB$nrI2wqfDRBU@^B>j;NgvV`57yoUHJ?3Q{an^LB{Ts5ph7L3 literal 0 HcmV?d00001 diff --git a/barcode/src/test/resources/org/xbib/graphics/barcode/japanpost/bug-50.properties b/barcode/src/test/resources/org/xbib/graphics/barcode/japanpost/bug-50.properties new file mode 100755 index 0000000..fab9d53 --- /dev/null +++ b/barcode/src/test/resources/org/xbib/graphics/barcode/japanpost/bug-50.properties @@ -0,0 +1 @@ +content=10800752-16-4 diff --git a/barcode/src/test/resources/org/xbib/graphics/barcode/logmars/letters1.codewords b/barcode/src/test/resources/org/xbib/graphics/barcode/logmars/letters1.codewords new file mode 100755 index 0000000..348d970 --- /dev/null +++ b/barcode/src/test/resources/org/xbib/graphics/barcode/logmars/letters1.codewords @@ -0,0 +1 @@ +131131311131111311311131131131313113111111113311313111331111113133111111111331313111133111113113311111113331111111133131131131311 diff --git a/barcode/src/test/resources/org/xbib/graphics/barcode/logmars/letters1.png b/barcode/src/test/resources/org/xbib/graphics/barcode/logmars/letters1.png new file mode 100644 index 0000000000000000000000000000000000000000..5d5c0fcb0be65d5037c744bdae4d88a3f8d78f66 GIT binary patch literal 37786 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 au_zynZvRja|>x8}8~ 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 O{)`RH4KSDQJpCW>Y0zH) literal 0 HcmV?d00001 diff --git a/barcode/src/test/resources/org/xbib/graphics/barcode/logmars/letters2.properties b/barcode/src/test/resources/org/xbib/graphics/barcode/logmars/letters2.properties new file mode 100755 index 0000000..fb01a6b --- /dev/null +++ b/barcode/src/test/resources/org/xbib/graphics/barcode/logmars/letters2.properties @@ -0,0 +1 @@ +content=KLMNOPQRST diff --git a/barcode/src/test/resources/org/xbib/graphics/barcode/logmars/letters3.codewords b/barcode/src/test/resources/org/xbib/graphics/barcode/logmars/letters3.codewords new file mode 100755 index 0000000..7747129 --- /dev/null +++ b/barcode/src/test/resources/org/xbib/graphics/barcode/logmars/letters3.codewords @@ -0,0 +1 @@ +13113131113311111131133111113133311111111311311131331131111113313111111111311331131131311 diff --git a/barcode/src/test/resources/org/xbib/graphics/barcode/logmars/letters3.png b/barcode/src/test/resources/org/xbib/graphics/barcode/logmars/letters3.png new file mode 100644 index 0000000000000000000000000000000000000000..53e891f0f8a664a8a4cafad3c5003d2ec1ff6f26 GIT binary patch literal 29551 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 R{!f|#e!)kWU!M8#zX5g7^=beB literal 0 HcmV?d00001 diff --git a/barcode/src/test/resources/org/xbib/graphics/barcode/logmars/letters3.properties b/barcode/src/test/resources/org/xbib/graphics/barcode/logmars/letters3.properties new file mode 100755 index 0000000..943bf42 --- /dev/null +++ b/barcode/src/test/resources/org/xbib/graphics/barcode/logmars/letters3.properties @@ -0,0 +1 @@ +content=UVWXYZ diff --git a/barcode/src/test/resources/org/xbib/graphics/barcode/logmars/module-width-ratio-2.codewords b/barcode/src/test/resources/org/xbib/graphics/barcode/logmars/module-width-ratio-2.codewords new file mode 100755 index 0000000..26b4c5b --- /dev/null +++ b/barcode/src/test/resources/org/xbib/graphics/barcode/logmars/module-width-ratio-2.codewords @@ -0,0 +1 @@ +131131311131131111311133111131313311111111133111313113311111113331111111131131313113113111113311311111133131111133111131131131311 diff --git a/barcode/src/test/resources/org/xbib/graphics/barcode/logmars/module-width-ratio-2.png b/barcode/src/test/resources/org/xbib/graphics/barcode/logmars/module-width-ratio-2.png new file mode 100644 index 0000000000000000000000000000000000000000..b0321bd4f96e493165d62d04e7541e56f3345f33 GIT binary patch literal 35381 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 literal 0 HcmV?d00001 diff --git a/barcode/src/test/resources/org/xbib/graphics/barcode/logmars/module-width-ratio-2.properties b/barcode/src/test/resources/org/xbib/graphics/barcode/logmars/module-width-ratio-2.properties new file mode 100755 index 0000000..090bd8e --- /dev/null +++ b/barcode/src/test/resources/org/xbib/graphics/barcode/logmars/module-width-ratio-2.properties @@ -0,0 +1,2 @@ +moduleWidthRatio=2 +content=1234567890 diff --git a/barcode/src/test/resources/org/xbib/graphics/barcode/logmars/numbers.codewords b/barcode/src/test/resources/org/xbib/graphics/barcode/logmars/numbers.codewords new file mode 100755 index 0000000..26b4c5b --- /dev/null +++ b/barcode/src/test/resources/org/xbib/graphics/barcode/logmars/numbers.codewords @@ -0,0 +1 @@ +131131311131131111311133111131313311111111133111313113311111113331111111131131313113113111113311311111133131111133111131131131311 diff --git a/barcode/src/test/resources/org/xbib/graphics/barcode/logmars/numbers.png b/barcode/src/test/resources/org/xbib/graphics/barcode/logmars/numbers.png new file mode 100644 index 0000000000000000000000000000000000000000..435f2724006de5dfcdc44777f860ab03e1d7c5e3 GIT binary patch literal 40694 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 literal 0 HcmV?d00001 diff --git a/barcode/src/test/resources/org/xbib/graphics/barcode/logmars/special.properties b/barcode/src/test/resources/org/xbib/graphics/barcode/logmars/special.properties new file mode 100755 index 0000000..f5ff26d --- /dev/null +++ b/barcode/src/test/resources/org/xbib/graphics/barcode/logmars/special.properties @@ -0,0 +1 @@ +content=-. $/+% diff --git a/barcode/src/test/resources/org/xbib/graphics/barcode/maxicode/mode-0.error b/barcode/src/test/resources/org/xbib/graphics/barcode/maxicode/mode-0.error new file mode 100755 index 0000000..6bd6b1a --- /dev/null +++ b/barcode/src/test/resources/org/xbib/graphics/barcode/maxicode/mode-0.error @@ -0,0 +1 @@ +Invalid MaxiCode mode: 0 diff --git a/barcode/src/test/resources/org/xbib/graphics/barcode/maxicode/mode-0.properties b/barcode/src/test/resources/org/xbib/graphics/barcode/maxicode/mode-0.properties new file mode 100755 index 0000000..d9e332a --- /dev/null +++ b/barcode/src/test/resources/org/xbib/graphics/barcode/maxicode/mode-0.properties @@ -0,0 +1 @@ +mode=0 diff --git a/barcode/src/test/resources/org/xbib/graphics/barcode/maxicode/mode-1.error b/barcode/src/test/resources/org/xbib/graphics/barcode/maxicode/mode-1.error new file mode 100755 index 0000000..c8ac80f --- /dev/null +++ b/barcode/src/test/resources/org/xbib/graphics/barcode/maxicode/mode-1.error @@ -0,0 +1 @@ +Invalid MaxiCode mode: 1 diff --git a/barcode/src/test/resources/org/xbib/graphics/barcode/maxicode/mode-1.properties b/barcode/src/test/resources/org/xbib/graphics/barcode/maxicode/mode-1.properties new file mode 100755 index 0000000..6999e49 --- /dev/null +++ b/barcode/src/test/resources/org/xbib/graphics/barcode/maxicode/mode-1.properties @@ -0,0 +1 @@ +mode=1 diff --git a/barcode/src/test/resources/org/xbib/graphics/barcode/maxicode/mode-2-invalid-primary.error b/barcode/src/test/resources/org/xbib/graphics/barcode/maxicode/mode-2-invalid-primary.error new file mode 100755 index 0000000..9c1bf4b --- /dev/null +++ b/barcode/src/test/resources/org/xbib/graphics/barcode/maxicode/mode-2-invalid-primary.error @@ -0,0 +1 @@ +Invalid Primary String diff --git a/barcode/src/test/resources/org/xbib/graphics/barcode/maxicode/mode-2-invalid-primary.properties b/barcode/src/test/resources/org/xbib/graphics/barcode/maxicode/mode-2-invalid-primary.properties new file mode 100755 index 0000000..650662d --- /dev/null +++ b/barcode/src/test/resources/org/xbib/graphics/barcode/maxicode/mode-2-invalid-primary.properties @@ -0,0 +1,3 @@ +mode=2 +primary=152382802840X01 +content=[)>01961Z00004951UPSN06X61015912345671/1Y634 ALPHA DRPITTSBURGHPA diff --git a/barcode/src/test/resources/org/xbib/graphics/barcode/maxicode/mode-2-max-data.codewords b/barcode/src/test/resources/org/xbib/graphics/barcode/maxicode/mode-2-max-data.codewords new file mode 100755 index 0000000..50fe863 --- /dev/null +++ b/barcode/src/test/resources/org/xbib/graphics/barcode/maxicode/mode-2-max-data.codewords @@ -0,0 +1,144 @@ +2 +16 +58 +17 +57 +18 +2 +18 +11 +0 +22 +61 +42 +19 +2 +16 +54 +10 +14 +2 +59 +42 +41 +59 +40 +30 +48 +49 +29 +57 +54 +49 +26 +48 +48 +48 +48 +50 +54 +51 +56 +29 +21 +16 +19 +14 +29 +24 +48 +51 +49 +49 +53 +30 +48 +55 +32 +47 +23 +45 +39 +46 +0 +18 +40 +6 +43 +57 +20 +2 +23 +37 +52 +47 +2 +34 +15 +20 +4 +57 +21 +10 +57 +38 +50 +37 +34 +50 +14 +14 +56 +26 +43 +13 +22 +41 +37 +8 +40 +10 +0 +30 +62 +4 +62 +60 +4 +63 +52 +28 +18 +62 +57 +21 +32 +53 +19 +50 +17 +3 +51 +52 +34 +9 +21 +8 +23 +42 +9 +60 +59 +44 +32 +52 +56 +49 +15 +35 +55 +4 +18 +34 +52 +3 diff --git a/barcode/src/test/resources/org/xbib/graphics/barcode/maxicode/mode-2-max-data.png b/barcode/src/test/resources/org/xbib/graphics/barcode/maxicode/mode-2-max-data.png new file mode 100644 index 0000000000000000000000000000000000000000..f7c37d52d59186bff038469d6bb32a5f8cc5dee0 GIT binary patch literal 33472 zcmeEuXH->uxrJKlKb$98O>s#dL9HEYiKec!B?8fpr}7b!2EIdg_s z@xgtqGiS~r!GFCMz%R}mGm4x$b0+wV;(ciyZ?ly;zaLNZb#sk}t6X`n%87?P)Zmuq z%ap$5u3>rYYWnl)tIey#wZQ~sJ$P5u-9EjHntD-qVO;ur8{SPWsb^*H3ArD>7PGsC z|JK>=x61_h-%^=LNlCTl3-1*yQ7xx#2@a){>kXdlPVAgyTD|dl{b{bCs-YTMWzRES zI(xyk^iZ;Rv;k8+jv1Mq3OTBrt3Dh_<`91?hKMI4n#dmUBGg~1+o<#e!J1iD z{wNZf7;*GDLOo4It$zRWO}>vp9Fm-$lyXhKSg5)=KtPYvUbCSlQeD_yJegJHR;f_( zh@&JomX@la&?yo@(@HMUj}ZM4ub--~FC-Zk;=fdhKKnvN1Gc98;G8QWcBev*Xcc;W z`RQuJ(OiVuMz57?lbqnXeD1mgnTdB;^Ui>znqt5f8s$3}=j!&Th$LC+J4oxytMobzpl~w2! z)$MdMG@P6ZDljNrf40owxr#=+C~7SpIpn)5k{%fS@vH~uwhUQjcru+3u^;*tzynDa z95@VvmfN;VwQFK1a?L)IRc~J-Ijm&VUVN*3SB{5h7QrJ;Pi7@WPsVdL*~7+!`K>KS zn+|1Fxe6S=9m+)YSRCRy26^H7?dxrKVx_CqZ!YF-YF#Q&yPJ`*low3tbj3aD{UuY` z$O^)N%FB71#40k8#>=Y=QQYEEgPLdgCukRF<;bhUK*Ky23XK`6ccGLOh*3FO3*X>^ ztaJ9Y;=SUpINgasLyZ}pj21y|B7R3`MTA(>_8DD;^8?;~8GhGH7bro)j23IDNT-LS z_G3m)7W)QwL@Y^a^G(2>q zzr#3zlukhNQ0&oNYP> zOBltwv5I#p5{NEBZsA>oL=*ie;sv`w%t0}y4-R$%vxXzbu+(yDeBfwDLje`KAo1{g zb$ZRhhn33Ec(v*UH=Szk%!+aqu>Y`okl+Svu2N+owLCPw#fgLL=E763mmCh=0{da> zwNzDGFfYMt0SgvjC+7BjmH6hY^k)9dK$t9>dqk1A7dNN(J`e zcbYNdb~O3145?o=T3MoWncOvUb5f#-bW%5171Nmc9eg+kKk1bxJSp0-+6yXZuv|rq>sI$z(N-urf#leWgJ7!6NtvyX;M%s=CH7PndG!qzy z%S}A@rD43v70uU0glbN?uPXCdkBCa@vtt^tXPB8J)l<_NIJ%pEOYHj#qPq3cWHRKa zE0OVbJ%??E7h;CqSg^;4sYEs?`}+^)<#xF=^yOIfOjBT-J<6adVNt9~1d6Z-BIWZ( zTPu-m9m+`6ijO8pF~;Xwp&w=0qy{suXF6!S0=7_jxn#-rp?=m)4Xj~&>U&(0FY6%2 zs=O6^8CWn0^o(w}Ug$^ZyVz~d(^#+^VjB5KN*esotd#X7hXlHr$KHjx#5>LuekAnde|7de_<@`)k|0^6tR<_I?+QHQTIO=^MGcR~atoAi8Gto& z;uTfX{usPNa1H!mR>Vj7WT}@js=ZI_)_*4ZKeMusn*HMa+oZ4N@X=)FvHL~+n$@{k z!>I5l<6;i+(Tly>c6B{tF zR`fl7Qt7eEFw{`VbXBrB+fFT7l8RX~E*h%aM4$mpw&fs077g{tgKeL-LYV~nk@z7{ z^qzG+*Il%x+@OVYY;O&d((#!Or&h4D-6fJ);lblje)WlTEVn|pNTv^ItWWj_577Xa zS*ZyUa-1$dW>FA$zC!tj#R%HRWRSj3F>iQ`HK<+xJu|&=VW^0`&3I;xhZn+L*-0vm zS$LVGD=s=zscKTqG2;>9(p*GJjZ&h!S7X5>?-h5!$+eT-s+C@*BOVdKNlq!Iovam7 zexhL`CLImoWhv|Bal?-BZqos~SlC2%0O>2o?A~ffyP@c3`0U3%cfu8q!z%MD4Pl6S8NX#f`HaP3u6n1D%JdsaMK?={V$qVaobyJ$QFu0?aQ= zv+W6sLgRe}FDmOS?<#fWA-c-w9NZi}=lSh+XBxp|G578w^z*$C$(28CZEAC4?Z?gX zDF&+{yOvKKQ|&ajR#K(QyWJOrnVXH+b~=5@Djesczph{nkerSG>oh?&rNP%fO#P1T z7ltrO^ZfG;LS^<3iJxnQuQCv_J8Be!@KgV?+vi%>*&pR?#v7G{$rchIxbJxU;B;;L z&oJ0C?SFS|bbpsBcg#69NH(@L+BS*G)qwKS_`Xs6gisQPrF}@8cPwV1%auL+Bv4@! zF~L^IRKPE~O&9klAl}#ZY0&&^0&%%9W43LU(nw}hS^0X5UEV4Y5Qx>C}HqXdbHQA#lp8L@1qzq0}!w3 z{l38wZAIja@dJ^LR!u34(!f=Id&Vfu0Xwg_d>*1SYsf_uF=kg^9#bpX@15tTsF=j^ zJ*8L70dmW@&`?m{UQLL>h_D1c>ZIkhijE}eVr(7ol5JwlTz{8DHew4UwOVHE_2T-* zXmRzi+Jkf9mdg*kVk#ybb;fx;#CTeD>dT)nXZovj5v{`Xz*;eD)E7Qw{({#<1Zn&^ zR4wZj)9)p6F~dQF5Z|JdrZs0fCnwxb7cv9(-O04J2yq>Yt3!F9Z zvXmgOl!tz!S2#~nfuO0zD@%QI4){@mFThAIn1<^G7s#^73;`eO07B@S<)q=%*M-K6 zycS4$J^wSw|2r#bwfp<-ih!=@!`9nwDzgEFkWkb;ewH#2mBA5^;GXI#EjJlv=l$}v zVBH?^_s2D=gCsvTr|ww`(0EwLtpBa)Wp>+flYHTjkgug4jOhNfHG$RJ<7Y($7V8IT zaT{0yWUj^?e{A6OSa#Pn0gfm5#%t0u^^-{zJ;FW$*kTvXP7UXSzVswQJTaPQCp*F;J6sCwIy#u@q1oD>_LGW_^Ul4hA*?Y zydX(0Nx494@asE*10w~U?77ftggpXM5Vz5Qhw2$Tcd~4FQl*a}f%MYdstdZSh~i2r z@oxY6*k~5G!wmOtUx=ve$J7*lrf3M_646Hwk3rN6qGHXnHVPn+#d^oS ztAs31@-|l))H8tf<(+}u2A0iV5+=R)`*SjZfql;ymVY^}e+`@rYuK!fzzv7%5f@o# zh)hp~67$!aB8jje^7V(FAOy#GR~i@RrZCD$P82>kGu~X}76ltr7ti75+c!c&7P65b zb3F&*Z-w*kXh6se-1KE71?rTN%odRHh5$>?I@kFIoFapblmywgNz{I5%#6yVN&Vk> zjasJ|!O3w+vXV(6ICj0)@rz!}A$aJ?ZlV*p8Ns28P{0QpWD|}3FuRj>is!b>{S{lR zW!A{e5wak+i^iA7sNPo8$}irz8j|F}Q!iAUEmz;f<(NOYR-g>Wcn(#AWRGm1h^bVI zw+k9|VW6nlhl3N<>BQ2l7$~ZFa^{0lbjV=E{c^2IU$xj?K?g-G#XiC1CB$m3V)+Cf zBP>nQ9lbRIgR=z%3$AhNHsrDe2~?25pU8R?2vmI58WI66;H4N1jlGTOVD&LyKEe-w zC*ph6)<^}sFi@$kX4LSQr~2sZ_PCTM)zalPW7GEnM*3RctChEkm{9I3TEfe(5M8;7 zpC@=%x~mU+t-BpC4C%CDB^Z%66ho^OQq`kqEXJgjc2E7mX7G=&f<35>kzjwnNVX4x z)JG&G%y6$vjm$S1x_3=tc{@|N+o73(E6Y<<@ODKePp{u{iOrA9hQ7y8Cb67B+wqiJ z#_Q7FMZ?3yrbzn=H(pmNd*Eq^tau?L;_>nh9BtJ7uV9X9V6>L#za> zA?q9uo^IhoVqA`h^=sGgF5)u4=Y6yx;fstn2q`TB{xi-0jnzylcF~3}xABBrFK*Sw zVF1u_MmH6B-KhQ|eCWveFoCr|+3_Pz!atnzSs_E4+x#aQ|Ld@B~_tWiw8Ox=V4 zpclTtkP$#MGaw(dCZFDn3#~_V5yif&r&yq`EnLM70cp%JVzYX7nJ+G1r#{n>Idcpw z4exhzN*6c|ggk}%yn!iUAxHN#pdACrP0dj5-I6N^|1PC@Y3cC%6o)my>3YX#q zj!pbNB-C|z4!^5glDLXoXEivU=ra3myt4`O$_vo8v5~+Sw;#SLiTnND2cncNBGSV{ zuDxeDq7P{x8J>*@rj;1b(D}k?yDS?HJ4bN5ziWWU_(=~Y7-HM(vT$=(?mSY~32G9% zwWq-$@a&!ikul56R_JUFKY?$)ng%QZI$Lzzjw!`CmU&y9A*kt4;CR-sK33mPdN#o@ z{+3r`0vS(;1vE=XEiaZwS9GAWnwQ>CQ8PY+nbBBZH}XTgubOI7bc{$|fvfa3_waFg z9fstkQncv9OGN0#B`BIDBRU%Mwc^8Wk1nPNJ;2Cz*OyG_qCDj&hxMCjQ0s2f7nI0gCoIaLy#8kqQClfmG33#{^ST*U$ zBMGBCcGL?PyW(}8g~sbO2%8}|38O4>tJGL9@t?gwm0(n2gvH$@2*-I(HUwdojb z3gptfX_V`R{rnYF>6ZMC&6C!!HbqNz3Mk4iP&P|l-v{1jTGRi+fXkAw?0~n* zDVRu^>2M8CcOW^OSd1KtUxN6_^nAokY>tE_0jBSL$Dy9~p&vCb>u5UB3T&7N{R+$g z`hiX3L;=OAknCQ9oDe+|5A{L3bblN3vDTWV{SB=FQ!Lln}?FJOqKxY$V z-I6fROMP+gZs_M&}KgS1BELJc*a<_98?P!>5bsS`Q}$ z_!@d1Mhu>8bU${4PSLmZ^O;3Vkbwl}Z-m^A4OhTjkyG{K@P)Sp53k|^Hk|9hQCYR_MLMZk zz#{>U&XGpxDL(rQ)+_~bPJ>4PE8=j*yXTWEh;ihe_o6|*NOtfuteYAWc{_5!papa_ zd!m8^CNv73$z8?=Hw<-qS!B-rG zXeOffH*U0oBwOHIg3JGhAL3T=Zacm&=N0X|h-pU1jnTs2MBf73?hlq4WcV;M>zL(b zPl|=QUj6r+P6;K=yS^xrI_O}L^)ukLgJqbxdqo{S!Lj_m$eZ(hG&c#a7|2vxe`U)i ziuG|Q*OYs2BzVi%yaLlG-Of7)Q(J_TgLtl6gqM>p^M^=kc>$v)Y|R10i;G3N4!3LI zPBr_;HY74a8Pi*mxb~|52GMwzS516r^17CcL;0-6dn0`YW8~WkXXAwX&-&=!c8@(-2UpnF7~vc+ z(!pwiVPD!GUi2s1?zS|5`M=DUC18|Dm#O05)1)f{4xx|b_nGrrMdG44QOR5)U$mLy z$T+JZ-{Ft4Hfnlgn*1l7wDO&7U1D!lbSmjAOT>oCE&nF8Jbv#sWGCfAY|80?ea$kqJBB91Cre`EXL*ps))uNZ^=+QhJn6OI z^3Lrj53zxwD;CRVZlHI__uV>T1ISG#`Qad#{(E~y=acfZdm}}H!6JywXqZGO^!B)1 z;Bfx9l|Z}=&XUu30Fv3F3>7>R z)sn$?;f5E$e4amaVfj>ziO&XbY8WM#?)xPyjzW!tTi}=Z&Ft&Wdh`8?AUbJ*=YKhP zT+B{$9?OO1lY%fEB-pr!M9Kz88J39~v+ms?e%1^?oZVXnV~Ryh=aM479N@#Vi8;LSSElc<9HlwOy3FK89^1SV0jv-G_HX3JkDSo)840f282n8 zteQAP=rr)L0`FWtaYl^tc^{ZAK)0?uYfk;PbfTYtMUDU8TI%kg#k!1FJL|zubE;xN z{O*6)^$$BJSQo_LN{9x$&k(T3LPBPDlfT>4ogMh}COl<_{t1oZNm)c)#(DW4XP5a- zH@SPqO3I&`)F~SvBTQw7>oVjj@jO3K6;{XEb9h|K)LSgArZAZ4A}*UWF~P$=Vp2OMv`HL z{!@AJ*KW)w7+=i2ZFCx^I!#rxXvaa+qUK;A44ldaBQG zHZz0{gLxCH)w1mVqmNa9cD+KcuwB%QWR7>zD14aRsqIe=5|fJAE~p{k3B!M*UB1fi zK-=d^8rL30=>XT6PA=z!gvy?rb#Pl&$wq4R=aH;RF-Tv!u;^(gP|C)fnrKpxq59kpW|1-J&(Ups+ zFly6bHHRc98f?C=v}tij&DTlWp=Wd}Qs^$?4KJ&`sA(QHdYNs<%(63dkju%@rWR2& zAi6AjA!_IMB>pRRP1GggsaTEPtvlQQ;FvaU??Y@ws_andBIj7ksgysbX#;rXYmtI% zw^58=Xgf?V`DE@jw*&eD|4jTh_Q_8g!`h{f6q+pvrh zWq2eeAxv3;NWmTv+j;vf#B|ME#^;#iUAcc~QcMstIXgLesfXsaSWiAna{WBAtUOMy z{k+Wb+j_)ZaTx6t06-=6@-A=lDe9$QyC||T#a)W<=a=_uZQ49hDgf+CqkYgbl6zqI zxzL?-X3qslW;xyL%n1Loo-`16$Lly4-X*&uNwF>m(CsdwWl2?l;kJIrE97&JfjvK! z(#+^hM}H~sqoHlU{*2}8aOXMJg|K`Q>bgO6 zX#`$mN&Wx+a3aif0h7t*Zf#77Vl(e)%zVNB)T2%5PR^z3c^{op=RiS(&E~PrH|5*3 z)a@cCEbct8Ul}iCRkC!mcHM)^yXnL)y-BZSwf>}r4zr?Px3$%o|6&KLLc)!K^^S)$3u@<<`_4*&g5H9hKtX7k{PiAR0NVE;f&!z~zSM zsjuA;bxGjH@~qh9$NFPj2=E7vjb(IO0sHz~SAPbR@d(QdxWU41}ljjBX;jupKDnPCH zSYG=km6l+YD+R2?X2_}l7V*3QmPOhq31AnB<&JaEE0UfSy_G3`K0SpufVH_bJ$Pxcv1K4luJplbxr5YC~{H&|8T`d1TS;WuH(HJ|`>MZ38cj z*2MhkC5HKiw86dOQZ3Ya$(3OvJuPUVcdM+JZCA=p?vpXOZr`&kL&?#-eo6cD_2hc7 zQ5sxNO#K55p2q0ck`!3gDs>eN9FR$tI|tJBxZ*WeGPtt_Sh3nq% z85A91Z7Ce+RDwF4L~<=LL*r)xKdv2SzKv&2l+$~ZvB!UjtVJ|uqGw*u0O^sgir_ZO zw$ZZ>XcmW*+N>RE(aKzBQ{{{(dQ0@y9A-rdnx;7!fu`)p(-77nM3u&fhUDbh4zY7! zb4M`#4HxY0`4f!@a_^$vFl%aDeC)u1r8rRq#Tj*19Wh15K8$szA|Gd*^TkD&rv#!y z;`68vqdnvGvp=H_vdx2rNt}Gq-58&f{-A^K?wWa*GGCuYLLcscl4rPc=%4zx8L!Eg zeGS-QgX{RU%UUH%j?7U*^{8}I|@^#lO4>ayT?~-ka-Xjno=!h=l1N*H`ooy?2t;4 z?qy&Rqh8!_Cf#?nCS!KyXQOv~(xTI`ZN>7%skP#zrQ0q$!cPWfhgIO;C990Avay-s zgY$|_-mQF2sjs<2ib~m2ujcQFY5oaFaZt2*{Mbska`f#aLBZLUL-nG!tfl~;`A)1<|t(oEP;pVA0-O$~tBg3&v>GhxP8 zXL+nh-wfG5$|>Q)BaEIN)^2X3q77B*;;UGDZ`AO~Pcn^D@-Qebs+*%HDLsR|CpS$& z^&<`10EZzc@DSeu9#U1P^609?QsdgC&|=mx1x&}Qnp*i8 z)4fMCrf<+RBhOZldNLA-p;bwZ10ME7?n1`T)Pk8tEHK(qc%-a1S?L_2ymtPr+BnIT zWBn0_>6UccWz#oxDZBEIC4RDoIAk>R-3sGG$$E>nftLQ%QBqSsCJpAFpFO!hNjbIa zlvR5+Nxf1DUKEn1^z~*n+^6W&m1B`IqmZ6JvIx#9^ z&QC5IJcj zzFall)7J_m{I`>ip&IC(wltTHuRUhbN24>y>MQ2g%KH}w%{xB`vkcXj(Gl$m;)Gwd z26i`PmA4*X^-L}UU8;s2S2s0(@h(m$p(qN~6f_F8`+l!L-9GJs8?>b6G^lI_-`yxl!5$|O zz@fa%5)6TEyOpTINx`vnqAvd(<%pmzjOL2l#GZW8a_IP}l#NS|vZc3AObZvH;Go#@ zjq{tzQimtOHc0cp;1i{kEs`5=YE6%}4%-vf6T+J2{_V={^WR9`B7GHNoCa$j?V@fy z@wdzBocw)K+aJG~ziHy%^`qPmuB_c%yoRSAh_`yyUv7~sW$2N^&@*DQf@Z|O>$Ft0 zDu!?kG-)uZM>lBD!euYfC1aSa=h>8lFs(13e}1_o8D7P?i)D2#*QeJ@-fcbgUH%b% zuueG8p8vwn?oalDr1B&#YIlxMz}%JM<yc*m#ULoMqVlOD?Jb-}!UpD8~IoWD_`@w1b*vZzDs(Ql-1%9xRw5U9+5j zgpG{18G%I)nlHXKsO^6yX+?Ju+uF|jT0qC z^*Uy_M%ivL@@#Xs^5?G6hrzNCTyq~zm~?I#Uzz%R6tGJt>+~hOAz|tF6bAF)+IIym zi7S3R?Q+)u`kDBO4B)Fkc76O=ok77TCRbfR?ZApRC{4Tgejh$MDJQ&4Yf}3OF?xNg zXa97VScOTs#<7n6o0mUHP0 zcE-PdYM`o?lZdZ-HH&{z{#a6V>64@U=Y+FzZqGiHz>_7<04zF?wIUu2hvvA!ADB9l zGL+yHD>U=NE$yh~NaDS5@yQ^e{*f+5cdiSKDfJVf9gH7QkMk``j)7WBeNV}_%kJvQ zjgythSP}z>FRAL@;ofH8w04Bx4X4=3ul4Kx&1;kE)Pi*2~sQ{(P&TSm9itL^`_8$@)=Z zz_cEc5Qe#R#hqIIR1=CLOt2aOT}}9-08o+vI2%3$Ht36|T5Ed6nYWqY8wcA|al*F0M^Uk%_bXsi z&qQm(*IxI`lOSnp%n5mrq34J|j*27F08Ld^aL4LXJy0A2E|1ay=)nfRvA6}L2Bjrz zUa-GJGA30XW6aea7*qANu5>clOy+6J^$zaaYH)R+0mCt0Sjma59DT$Nwj{^Cw%6b* zB?*+-)f|7E^AM)bmHBy!T)*}M14?O!N{+lY#buddfO+#0y?jb^`hWXrRQ+iRRlm`p zf(}XTyYDSs+7~!6IeOMnzp}au2K}nop{nv)=w3RIq2U-iAf)Lncpu$-05s8nG<;lS z0YLRjL0ykTsSnl5Y>xQ&=e;*~{CM>vs4k2PR?w}@jg{9oRQ=8~_09jke3^u59#u2i z1fmsm;cr|It7rm)`(9VfNiK};*}r1j;&eH?9+WFIGi0~wjFBq=3c=QDieV#yYh6T1 zO$>=cH#;r`Q2aO?iknQTA)U215*P1?*EH&RXr`t3GX#;nPGr{5CkIu2T*`jcb)E*?aVJ0IF< zMS1X+mS4V;M27z;hHH;0KOkt#zhaEESv)O5NyUIhZ)rGEm(k0^(^0>B2kjS%VXqUb z@E5`y`!}tZhPZT&F0O8nfcddp6jLQd8x#`1s$($$huV2EtL16=>J^44QI*+x-Mh`= z?V1HYDvVw}8E@x+2>N-o2P_<`7`5O@s$Ze5J-QnuG-_^;vf(l5_OnA$Ddgy~h|rZ> zf!ljj9+(RZlxVm*l1;oy)-n9V z7N(yvS#>n3w~om^WH#R%+uC%>M*MWM<+W_7AKOc-NdE4Oah zrO3z#6lp*)d~XO>=aP`SlEa0jQW6zQ?|W@&roiF6rA#Xo)GZ#kb8EKo5O(+`@a+i6 zkZpZIz=doc)Kx7fC_(zh**t_RY0bl!iiRv*r;brJ{w$**w{*377o(gO)!e?Mn5wJa zp7qpaIGWEh1_cAJOqy0`c>KB@YY4L`T*U)SFdw!!_k39T zZyaem-o_9?dfFx9uENL}krr5D2~T$jzlPFYOgJaYH%2mPIIeBHkELcs9}~BpRX(M9 zu(SbJ!0u5eeAZ$-6!hFF0ncjle^UPjdCN78fmEzib+!_D* znNBt+D-@qd{~`;opQl>qB+(lA;ttAm_!6FDTR_Sdvr+%ztUGg@sLh1XyhYAGuCh;z zt(k+ZW7n>$2-%LMPL*V1fLMs!>oLzGOn9IB0g;F(vRHa@Xcax!~)@d{m-4&*w zJ@V7(ZkMNRI{2dde2}cg?eqkOGHiro{Yd)cu;ZaLn7P zxHdy~&wW<#J`i^se~GPG_+iou`g_DLl7iE_w7UiuBNsj5C2t9$>=+Jc-iQfj@rgFRW~2Hl?;SOi#>@OCEL@yTuY(hYx9w z&~Gh?Ykxjy$wFOKIyc{wUQLj;L7#_c`!x6FF1kN(5OQy}v+$;Rl z#mC6o14JqNW3$>_8DtWdhmF)8C6>VFM_)VEKSqC5XEAZ#ax~aln@wGuC)!$dQp}!9 zRjjI$xPluq} zRh_xygE7M2Aq_QgZFPO~Z!ufrZ*5Xy(fSbdZ=0iwR$R0lN7@ToIG|%#Qd`WuhQ)-Q z&VeHDetANi^OKldKWQM^klYf;ZTy0l5WN<>qhuGp84c~QI=Nuu7#=_T0D^vls@~o? zwsIYc(jNUe*Lj{dPH~QIwxM_U?gLwq0IJlLeoe*zMxK{0K@H+7p-m}71TwZLM~4t|=O6XufqKe;eI*)3Q2e!o z3K(FC+Yac+%||w9i9PSLaxJJ8{pO`H?!%HWTo{9v5r8o}s@u9!#-I6B)FlPfO9GAG zYv2tpeAj9+qBI~XCr;oZami_3lT+QZh(zRD$;LvFjLj4Nz{8!j%_EPiqCkC-)BV9)QkX=mv7x78;G{Fd5we{z^DC`oA^h(ACD z%OCF=c7VLb+P?_ih`HwjcxJTPe6A(ggo+Q34`1ryHW&E4dM0n`baghx3*+Kb#qXb< z8ql$8ty3D56%?W7dDy((PgEP_>RBi|cWgJeek{9)oRHwk#FYo(bkvOmKs#*;q}m~W zodfG_x$JT1v=$M;a(gXsHR-Kmu4DHm-)zhlPc7Jae5D-?a zi~8nG#8VQuLUen)^n2~7`w3pOy`S2Mg_zYcit$amDp{4nd;cUJBJmGIRSJ1I9OY>^}At7n_naXo8C~jqz`Ne>msXvbFF4kOF_6HHeNnz=m$M z_2jbRe3mr!Q@iZ7F|G6`c+i314eokpu2*p@&6fSI_jE)^ZAAXdERM|kxpv*wMlDOiYhBzPhly}WwgkVbpUlbK>FxVO~BOly%wIk_kj;0 zme9=?WZF(6A>+`^`zE_!-@=7yTouxzQaV&|B#_-^H7Tt`n)8nj9gH~o{+2ne#(JXi zm_M6hQo-R%GSARAE1|_^6_X7%9or*jsGVn`{hFKnS#6p>uVnRH4%Zc`PYtuUee%wRz+Fz155!c}zn@?3S-cYZ|s`*L;QErupyUHtgS8E_WC!XTu0*6NY zQln%zvH61N<-uxSQo5enJ3Rr8vE!thg9mE5#WkYV&h3|WXo6ko=Bz4okrK~iFeo0K z^qfJ8qYE6G@dXb~=A7I+S~PT)M-<0whm4$!=8)rWt4vriJr>>bX3fE#x|ex0ypnR7 zmBgbw4lY1PlH#wmeX_aXGh07+$K$hRVPq%ipqgX%M|)_2{Kst=f_|@k3g(r`Khup- zDgJsGv{>KPk?ALK0v!bLC#uANYLYF|yVN^Z{wYccM_Z2zgLS#_Up z!-1J-BJ(|Q^7^B!Yw(_o=UdM9y!9^;BpA_eK1o&?dwlokiMM|}2R%?9OcZ8-6o>2U zEYbw}M#m2*b3=N^d|NC%wG)mwvuQqJ9vR8o(8<0rmPo-INYKM%EyFEcEAILdLTT1u zR3AlE`HP=g%WM%P#$U5!0{qadpCy5BT5DgZHPem_C=cx&9r4j-X{(aVruZYHo^No>sRgd;GqlI6=8#tCC|-{>;~ zqF)@{sSluu&{z!zs$y(GD3Cjw5cF~=udy9kk_TNC#ljr>oyS}H(@UMnQxtn$CYGXg zXtsRSCVk9b8>(mMo&!TYHKu|qbB=mTEQxJ%9zx^9yIU7I_Zk?Tb04b7R6gZY){U!r z#a`b6%24IkmOdpLcWuzk?hjU)A{?U==!vL7F*6PHTdbmU?5gPj-4iuR9l}J$Crec(WrHf zOFZ@sIDqt|@J`58yo?GGy~J55yH?Y^IPXU28Z|yo7WP+OklCBJ-v+s>rji?!d|2p| zvOVffwXYm-{-9%lva0K7bKZr*^;DdWE5!X33*5*YxdIg4*m<6Afn+l z;dC04jpK5`5k|oynUk+=6jAS=_%rWCO(RPZ-`qPDiJulEW3SfaEz6_W9xZ= z1ia%+^<7TuiPwbvuDkuCvuZt6FFc8hSxw&h*__x1W0ag)UPwRe{3|Q|%DDd3OWsni z&q-$KCqgTnlkaLEfsiX=p>pTU4{VVUM=m~uo2S~h?^HCyc4<{=rBCTNF(Wc;@z zYA%TzM>Q)GsiyTm4bgsZ4|L~x02^7&3R&VM>dvEWxg&NI@Jz2mmY3 zu7vZn+hN1HUc959qrYFa2wX~aS?VO~i+QfVe0d$4yX%UudZ6WQP5UBLAG7q_NnKp- zUHOSZ{TJT*$G zuapE$7kVTl%wjZ#MHF`Be~aTqrE9Mr;dPoW76`3v_xDH~Z@+4oP$w~) zY-FrP^Hr-Mj?c%s17^Nf9bSLad>6H~J4SNchUA>iwny{Lf>kf^YhoHdUI0^m9eN;_ zwq{)Ch&eeTBm!Kyv&I_h(|q@h;K(7C?ec_L;&sEh+Se1$)F;xH9;dLTA7BI;B-fg^ z6RxSnUASZQEZteLn$A>D>#};DDRPXl`k-Ey0IQPUUJFj$%yVo{iJOpi`l1?@D@uAL zref$oXE*gL^2z4iURtSDg-P!C2r-zVdo#EsLdp$Xs(>Ofn6X3)#^uhh$4SoUvS`zW z%%nj^S8c`)Xx>H!pqjhr6+kz>_=uENEbV%FZ2^|A4A+y{Js505j&3I(T$%l7PTi9K z;`*Wa9Z@vh;t1J>2C))VMk!0w0QPn*+>wiAD@mZCqurLTwq_0}c5Obb>CI)L&F$_5 z#+7cxPn*u01%qddoQW6r`G9@GFG(-9jx^i<7Ur`f%KcRcTSLh1nfmGL>~!8wPf|CL zuCM$q&=yMD;pyxmas2%+g)g>3HU8(&JKlMwYj_o6xl5y|7vCJC`zDbMSJPpV&!)|u z5dtakzj~PUe$(@J$dyApAkIXeJz#Trb3|?1GTNLxRy?pCv3Ewy=y@MO-bx#ezCOKH z0ZS~)vb8FF(-Prqc>d@MRalVC$spHY@sZcQg3C;1fYXPn+^L_GZv~{R)lV7zU$SCs zL9QWca!s9?W%tN+imKaEp3Jtsw$^Q}ZkSDvdFk*-9Pi3dM%h_bKNOB=KXruIRqbux z%2G*oYjSw}tdO?-t^L~Z(zN;J;&!TidP^N!eY^PE4ex*R6~qMn#kc0DjSrAty${tt zqHb)5t{-dPV9=jmkCcF<9GxxjyG&d1uUD1Hdy@Q~OLslFmTKi@UwFdq6C3Zwh>LWu zdwjI#n7dl)QKB!z&qV$B*K^h~Nf?jko_UG7v}Y=DGIn@R6~(XKwcfIh@yKO`cbCXLcmc#GoVmj@7j;|QdBurOm$D^2Pw-1z+bXPLZqkif zVkoY{smBGmn_>~4L{{XXE|a*2EaniTJt!_4`W7kKgQ%f4_A%7!FVdG?#g@K%5%Bkm zy=2S`iV<`w8h$6lQj|yp^{M z)W4c20t%fdAfh&)!=JFqIEhM}9VpPX$D(+Re-YfDvA=mA(+Pc=ok zaYKLgQft%ot~fZ;Hn{pEIh_%noL;1nFZ{)5hy~v)H}I=%i|NoYf2Zi$*Mt~bjh;_^ zMWY&friw;;CkB-a`2z|PnU&zqBg;V*x9lAjp`berp^(kz!|C0WFERVgkJefGdp=DU z8Ebg;QWkr-F~w*YA&1iFv^||TSk{GyaGltFc2G_-Z$u^BvIRA-kL|wF9xnRY4~e$e zG1QMMdpCLM9)&q5u4jU17$CbU55;LxW|=PXkX9jxWiB*_v)^81l}Woi+7|s?{rpbe-@o z?BS=$I$OC7uDk{WOEuu)7VpeASmO5-h6zuYw)ddldq!C9k2lie7pP%IGncZVlw)O) z^ef$sH-h1cgg`85ng@Ua4FxQ{c^ZDnDx)%a-zlx_wa%)F69Z+PxUxU0*~vnUc24*- z%{I^HKb>fV#`ufs%$7?Ye5Zv$I5>wd{p7|RGf?pjI~`G0E%u#=ULQZM>m=p=eu`D> zD?tsxHEh_Bc!_fWpagkYSW@#exXsj^%AvDxZC8}kt6bN;J|rf+qVp!@mu3(L5BO&z zP9MfD<3YXzkcWZqrv*>Ad!blSwbS7Ux9KG^sJabFn8}+~gg?bJ_uyZ8{2YgQ z_Yu3Th~R35vKhlUsdvf zbs~GG=VMSV1unnQayTvMx{+CZ$pZi?OOLTPmYkwvk9RiFEOZ^Du`n+0SwcwqPn_~| zTEBpX_PggO_gL2AYpt&6_nT@v~$ACpyR|G|yLdB;iAhQe`Qal?~vL7?%)h+!5bk7eh_VRKKP zBoYpY@wz`e|5rAq-u2>xiwVOQDuNQcY?^zFNF+pm>D$4hfmBi!-YL^jrX zE>a)e)f+i8ZyWd?mUz#W17235Y7E)DZY-$ZNAyCDvm%pZ{7$Vh)}-H7PN$6S~^w;ZC$t5}gD8vT$8Q?X-Cm<6%z$**gqTUhy~l9TnLGqN1{M`T@!M z2DfcbG|{uU!7R0CzVfWJ>wa`vM0A-Gif}`&JOgAQgW0V`ayL-L_;sU?{WOz@A33cbGxtWy081X&+|Ud&*!?&Prtg^r5r8Xjw)ph zA*bd0DtK4OY?yX>F6>@lSf|`jp`U%<-eeD7@p}Cp--o>89nwI0s>MOtaUG789d)d1 z7aARUoR_tvA!!1H59(5RdwPn{4;uv!sRz&U^E}BgtV^!9Q2O~HUARh5=bmfT`gN^W zvg*1;fFX0?^C3mc$|9b=tS0UXd_cZfoQf zoqVVcQ+Lv4edH=PKP&ZN6-e#hsxy=^e(T{m@Db+PpzNpOub{d;)M>?hms_|dtBUKt z4OH~G$kvd6EyFx7zOyn>5cD9Q=P@hFvvhpU@Ojo7cTMfhE7ePXzY;at>9KIQh1ru_ zykc+Qd=2D-Bp@KPJ7-Iz#QAT^UKX|Qem02ek38k`n{o2`YHu`ONG-duZ7LM-0p`^3T?wt|LaR&oc3Ge_M{jOSI+wmDH2Urd+uS0Ro+bZn;98NEMv(W>fw<8L#cCNI zYbp@#bX!;dZ8?e~%JOton9RdRv0Q42a~lmM`cfRzhBHfHrqBkUYg|S-ub&D()~ppCilRThn){L*|%) ziI>oIN-(}Y5ef4iTF^EhP;r+x+VT>i63_dE9(`aJ@9VRg>|$8sw3(3(xiNIwX|o-{ zx?f~VkHYsp@vZTCcy$50+iNag?gp%RRUb!QQgrIldm%)u5-YkVc|J!kRT$`NTnf<} z1QJzeJM<)zdCQL;U2S>uEbPJ+Zy<9TV4R%aoRu)G)!8JNcBI0nLD|9YA+PPT$8m3)vwz7Z?NyLT?uj;c*%-qmr)kbkRDS*~ zYlj1TTf?dV4i1L&7sLejQ^3B4y#GcsP=)64dy8C>18c_(DANLE5SA{>IPU3_qveF{ zH||EQwcmqr&#{%o|BT!lT+zSlZb2cfaSbSe0PMCi=MA>-_g`vfcdPiuvox!KE?oDP zO>q6JF&gCY1$QSg5*{nrIAHaP~q zfZTHpCkNQo*_^x0^%k2C&wS8iV2UyHko{b%kJ7>JRK3Oa`L~aQ8Lp?q#vaEkop``Z zGoCvH9dm#9$Kx0i^zqa-n7r}S_T3e}7SkF<6g}IQ@6_`peN#s?%B(mI-dH_65_-SM zNyJxsApDT`qX2zBz{4Mk6zV~oDL>Y5wfo|`bDOGW1_E1)<4S?akzTmvemD6*GG9TPkK*` z*z*jzuNppn5O@a`J*2gZ1Tt;+dztQ>d(RZ@!)VjZXchE^-3}_7J%>;IYN&f5~dgmM5M7GvldpF7orO;6`{UlXfx?-~} zDE4i}ssm|6*2w?fOk>xzvoq=dg677D@QOeck*&(Ty54uhPY!-R`@v%w)x9(^{-Z9O zsL71uk5$#6&Ybm=TORJ)Q^LWu>#lv{9s#~p^=ZaE{U-`)`qF8dtD|SQ=K@y!j|H8H z!!5lykXaDke^-9kaOfT}i* zrX5f_($h<-vKcSK`@?YD2w@_E20@*jhAA_sda3=@ShTBSn|k=FQg>R(!4^a{qK&%8zJr5(fNEHM~qe##|N{gvP|z(#%PiP zmumvVn(OAdOeZk4UI`bIJD!`)9kz$95`sb?eSYvOXpG^|RM$CD#C1#eBihLhbqTj( zq%J1(;);h?$o`5e5{VacnTCrcibx(`^hnSYkt#Vqqo4;!bHMB+coU{S}$9@6!0ky4aAj z(r!lEaSGz8=cPz{=AFZVROJ_S(6t9i#ZyleV&72wi z+YpJT^$;SPK1$%ZqTrLp5xLRSk z{R8$zM$?jT5)-xT&Brz`(XcgiQIaXNe+EA+_)hFicI|q%<&!yu{S5L#C~* ztzm=>OD&Z#OiNz>J=tq59irsD#+9RwSpR6RYhIGgq|Kt;I!&SN@dHCmYxx(K_I3y_ zuWz~ZgrIb0j`!9oTv1zJ4O)c8_*=d)vbh#_P4i32J~t_Y6~qK0eSh5ZpE~}O9r_pr zqlHBu)9%H*z{cnc?rByil>vb>?kKV42}-l;?q%0TS@ZiO3WE$QCg2E}hW;k%{$m;j zlCux1q7Ca~jT#*5+^emw$sOUgB5=fxw;1V+f3-xWWSGP%D~R{v_uPJ}B+N!xtiEkZS&rH6(r)G#yd3ED*rA@{2x%xn# zrPwCExd;L>+0>y{LK4}}n7q&;pj+IenM}_;_%%n?f60B6^o6@(F>ib9TcyYVyLVFM zKIGZ(v@%H^poHy*lyZYqpGfv2c`Azv+!^Ket8Fi$>H5d`w?cmL`iDA*ixnl;EtYta zF}E%UH9QZPaHXN$DwN|W5!|e+wU=MeA4(he^s|1JUS?;4mHA8=xU2@liblTH@_#!) zh;Z2zboonz3bBU?HTz7a_TEBD6+upQ(`Ip3A;A)}L-S5<^9RUCY15^fNzjJ(kw~Z> z@jdz)O>%ai%H3gY$=~yr%FBS5Zd_c}icSN>w`G8EF{`nH644@q6|8cf7r9tr7(4r8 zzjmiCmU=%%ncfT4i+lzN?mD8&h)^0-<4nEvVTPS)fgg8sy}x&YFr-ON4KtbGFXgeJ z4|PIuVU62Sy<2P9xg7Yxnc5P3j-H4R)ziqYR@qG9xS zTje<1Y4>+ReT@wWoza-LTNZ+mKGinzEg+6 zT)4nj_nz#xP2LTARWp2fbqH274$@3$Lsk!7=Y{Uj9kiJW;@Qp1t~(^%XsDCz5xQ~7 z1p3O-=~D4rOQX!!t*Al6oO->OQIG0lBn6YtEkrwFzQ;vgNN-7+g}B2HxMqn+ zx|#RRgglQ@wjD+%fgEUl*Swu5n$FyBu61t17F=AN{QkQ_u5Hr~l1D&`l(U4D@qr`{VM^)`KvyDSe-QT0qCF zlvIZcy^un9H6s&k^9O&ZV~^NqoJarr%ZF|+p9uF@bDpY7f{xp@H_(<`+mRlZgG@hD zUJc2QpfSh7Z8e#8QdP_3M!(RNm8IHR1*0Y-j`g*{#Mqgo)`L`5w`!EWd>=z9w@eb) zV59u}KI_4*Xv5S|kDs>FA-9%V%cMUERWTto9!|r(@Lx2C!0yMaZr!wz!(+HbazBMY zIFJaZoxEP7B#zN(u5zvLGY(i`UUsJs4*sC8T_49{7Jt5)87^*Qltkz~)-Ys_9qz#` z&iOd_Y5B+WfD394ibNX*uNoCDEic!p7=IA0(&w)VBzIc>qAs>NO*cY(55Ck#_+ccp z5M+2#x#zt2q~TB%_M1CBT}5|&O?@1@cBCq$nBQr=@`4jO-_`sLa~yclO&ZFTg1-ZQ zlf9_Tlvt3Ol{QLqr%M|ykEJtDrI$?BqLf0MN#nsEp}| zNo^2r)NPvt!Gy^IF5H2?5C=j;it@!BfN!wPO!kogsTYd{Lvw9HcwdbdXKW#Z+&y0m_%?h<5~V65Qt4UTzmNFs{FO9V^jf zQG=U7kryfY+V9X;1UL!so6pWrz_AedNZCJ}Z?pM2;O^}8;qR%~<$maY^zxOU^Zh-1 zjcM!lLtBHf%kDc)f)pY8EmAr%R%_R8E!;JkF-(kM6e;pTappM5ny&!6>q$+`Kqd5J zf}NAJTZN&3q#NF4IW=Wo$~3)p8a7Qm&FFu%O8y3@z>}}y*aP_pUsjgDaC1fmt~w?l zVZ)@Z(Wj>tO#OOPMVU@ItAa-jUCkv{-=ooONZ|^4PDt$Ah^z0Sy136uug+ITnm)~_ zYuf$U-e3r(ZQ0e??~wM@gz%V9CodMm& zg?UUlJs7-Il{t2u@grV(jHzpD7)|B|2-(e|3pOH#4ZSFe%?kqyXAqqk)AC7HqH~<7zz|KS z%D}r=Yy#`(?5+*i!sgO|*Z6)I{|4WmraZ!<0gQVy7Hx~*VjX24v?G}Zld@;Wb-Q8r&=djU681$J60qzDqHQpbI_xBUF$a}E#AH2XleYFh|zk<|UT|TZVfBYH2q-`C<&LZzIPYkHl zk<{%5?}7X+CxVIMThF2^U`K7W_=XEMvsFdS&fB(aGQ}V2HI;0$`7{~ zF;{|k10Mc}?yBR>37ns*XMI`b`i%_y%pK?Wx%E#q*t}rs4vwF!W0AU6;QZff=>52bpk_ei33$ppT;4bZ$zak(apQYrcTV_t^wA>#N z5EAY-U{QGQ2%N64@){RAxLM-;F=1WS&f}Y|$cwDIX`|s6pHe*y9L^5Yv5g%p zQ=D1Is-U`a(+!1)u|En?(|Z7h0?vkK=I&$%U%-JgcG}nmzx>k$eI)Db$zP2xa#TdJ zSai57JD3Cb22dD|;~e-a+Zpv@-@GUf1prWx#rZ!y!H#ZVe}QTW-U+z^o37X9D{=L> zH_v~_0%EdyLfWub(=U45cSyniG;rrPUnMAO%n$J8Lo9T^>>ihRoQ*b!dLvL?bvdPG zJfKu0AV6K=2DVHs(T3U((WUK+4n1r2IzlgU-x#8GuoPD5v*c=SS81zOzw*N3MOI@H z^;K4!1NvV>g$BbaR~>~^5Qf@Gbohby?@?H1U*DqaHJ*3r-)UFhQ53rIYt^QS&q4Ya zx$kBX5qYZmVk3$b!QK~cY9dr^`O@sYAmRaRg5xQU-qR!Rvb&H< zfkjstS}(EnQx5o9zw>~ES@`CSnRltcc@NU~OOQ&Rs6@Z|Dd~=3EHoK9?)h=tS}Vh`}Frn4%+*;M;n{n&MSJ)70Esqo<$$cLV#|5=zCsqw#NK@cs&U{ih=b z!3Gn1tM)+cO!atOY~_w=B~~U?J^rvWD$C%1)y(?y{i-cnI0DR%8J}l&S(I@KVQm&= zt8Eu744S0z4_2~vOd*9q(zE-(fBo>kUFAQi3p#S8?o`jrOwXzHPAoHe*p!uY*o*xf ziv!S~963d73C~jbc&q53e(WPE(Yt-U1v%a3r6aUGALm&P!nNG)vN3gYNqk)m+yTw7FGkvyC zG=*6s*X@+XJb<1bCf7|7o4~}6`4ydaoFIU<-&)g+^aE#WS5CAK7zMsQC#O z_Wi0%Jdz4M?MSb$9PEbIsC)tI2uBsVR z?_qd*8WuG7!CQLvso?YF_<_KguaPSwp`(UF+S2-qGF408%LSL3d0ID`t_e~vbX;lN SDjB>5{4+mpbqr_X9P(dSduvhv literal 0 HcmV?d00001 diff --git a/barcode/src/test/resources/org/xbib/graphics/barcode/maxicode/mode-2-max-data.properties b/barcode/src/test/resources/org/xbib/graphics/barcode/maxicode/mode-2-max-data.properties new file mode 100755 index 0000000..aad4dc5 --- /dev/null +++ b/barcode/src/test/resources/org/xbib/graphics/barcode/maxicode/mode-2-max-data.properties @@ -0,0 +1,3 @@ +mode=2 +primary=194280000840002 +content=[)>01961Z00002638UPSNX0311507 /W-'. R(F+9TBW%4/B"OTD9UJ9&2%"2NN8Z+MV)%H(J  diff --git a/barcode/src/test/resources/org/xbib/graphics/barcode/maxicode/mode-2-standard.codewords b/barcode/src/test/resources/org/xbib/graphics/barcode/maxicode/mode-2-standard.codewords new file mode 100755 index 0000000..911cf43 --- /dev/null +++ b/barcode/src/test/resources/org/xbib/graphics/barcode/maxicode/mode-2-standard.codewords @@ -0,0 +1,144 @@ +34 mode 2 + part of the postal code +20 +45 +20 +17 +18 +2 +18 +7 +0 end primary message +61 start primary message error correction +53 +12 +1 +38 +55 +55 +6 +31 +40 end primary message error correction +59 [Shift B] +42 [ +41 ) +59 [Shift B] +40 > +30 RS +48 0 +49 1 +29 GS +57 9 +54 6 +49 1 +26 Z +48 0 +48 0 +48 0 +48 0 +52 4 +57 9 +53 5 +49 1 +29 GS +21 U +16 P +19 S +14 N +29 GS +48 0 +54 6 +24 X +54 6 +49 1 +48 0 +29 GS +49 1 +53 5 +57 9 +29 GS +49 1 +50 2 +51 3 +52 4 +53 5 +54 6 +55 7 +29 GS +49 1 +47 / +49 1 +29 GS +29 GS +25 Y +29 GS +54 6 +51 3 +52 4 +32 +1 A +12 L +16 P +8 H +1 A +32 +4 D +18 R +29 GS +16 P +9 I +20 T +20 T +19 S +2 B +21 U +18 R +7 G +8 H +29 GS +16 P +1 A +30 RS +62 [Shift E] +4 EOT +33 start padding +33 +44 start secondary message error correction +40 +52 +23 +54 +59 +30 +31 +49 +1 +41 +53 +21 +35 +8 +47 +10 +11 +32 +26 +60 +53 +5 +40 +39 +32 +8 +44 +38 +51 +43 +8 +55 +14 +23 +53 +40 +17 +49 +31 end secondary message error correction diff --git a/barcode/src/test/resources/org/xbib/graphics/barcode/maxicode/mode-2-standard.png b/barcode/src/test/resources/org/xbib/graphics/barcode/maxicode/mode-2-standard.png new file mode 100644 index 0000000000000000000000000000000000000000..2baf8d9b5e375199ada3b0be408643c4f0d47e01 GIT binary patch literal 35250 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�jn~4I14{A*t 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/1Y634 ALPHA DRPITTSBURGHPA diff --git a/barcode/src/test/resources/org/xbib/graphics/barcode/maxicode/mode-2-structured-append-1-of-8.codewords b/barcode/src/test/resources/org/xbib/graphics/barcode/maxicode/mode-2-structured-append-1-of-8.codewords new file mode 100755 index 0000000..c5714ed --- /dev/null +++ b/barcode/src/test/resources/org/xbib/graphics/barcode/maxicode/mode-2-structured-append-1-of-8.codewords @@ -0,0 +1,144 @@ +34 mode 2 + part of the postal code +20 +45 +20 +17 +18 +2 +18 +7 +0 end primary message +61 start primary message error correction +53 +12 +1 +38 +55 +55 +6 +31 +40 end primary message error correction +33 padding +7 structured append 1 of 8 = 000 111 = 7 +59 +42 +41 +59 +40 +30 +48 +49 +29 +57 +54 +49 +26 +48 +48 +48 +48 +52 +57 +53 +49 +29 +21 +16 +19 +14 +29 +48 +54 +24 +54 +49 +48 +29 +49 +53 +57 +29 +49 +50 +51 +52 +53 +54 +55 +29 +49 +47 +49 +29 +29 +25 +29 +54 +51 +52 +32 +1 +12 +16 +8 +32 +4 +18 +29 +16 +9 +20 +20 +19 +2 +21 +18 +7 +8 +29 +16 +1 +30 +62 +4 +33 +58 +8 +2 +57 +53 +62 +35 +42 +14 +50 +1 +28 +53 +1 +55 +2 +38 +19 +47 +47 +38 +13 +19 +51 +39 +63 +40 +5 +14 +28 +53 +34 +31 +4 +40 +36 +5 +20 +46 +26 diff --git a/barcode/src/test/resources/org/xbib/graphics/barcode/maxicode/mode-2-structured-append-1-of-8.png b/barcode/src/test/resources/org/xbib/graphics/barcode/maxicode/mode-2-structured-append-1-of-8.png new file mode 100644 index 0000000000000000000000000000000000000000..2b55ec9dea0b7cb2dbfa889c9e3927db64e6ede6 GIT binary patch literal 33954 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 NIc9paK;I$cKL7+y3VZ+n literal 0 HcmV?d00001 diff --git a/barcode/src/test/resources/org/xbib/graphics/barcode/maxicode/mode-2-structured-append-1-of-8.properties b/barcode/src/test/resources/org/xbib/graphics/barcode/maxicode/mode-2-structured-append-1-of-8.properties new file mode 100755 index 0000000..0a1662d --- /dev/null +++ b/barcode/src/test/resources/org/xbib/graphics/barcode/maxicode/mode-2-structured-append-1-of-8.properties @@ -0,0 +1,5 @@ +mode=2 +structuredAppendPosition=1 +structuredAppendTotal=8 +primary=152382802840001 +content=[)>01961Z00004951UPSN06X61015912345671/1Y634 ALPH DRPITTSBURGHPA diff --git a/barcode/src/test/resources/org/xbib/graphics/barcode/maxicode/mode-3-standard.codewords b/barcode/src/test/resources/org/xbib/graphics/barcode/maxicode/mode-3-standard.codewords new file mode 100755 index 0000000..db2e4c2 --- /dev/null +++ b/barcode/src/test/resources/org/xbib/graphics/barcode/maxicode/mode-3-standard.codewords @@ -0,0 +1,144 @@ +19 mode 3 + part of the postal code +28 +16 +28 +16 +28 +0 +31 +4 +0 +59 start primary message error correction +33 +49 +2 +48 +62 +59 +0 +48 +25 +59 start secondary message +42 +41 +59 +40 +30 +48 +49 +29 +57 +54 +49 +26 +48 +48 +48 +48 +52 +57 +53 +49 +29 +21 +16 +19 +14 +29 +48 +54 +24 +54 +49 +48 +29 +49 +53 +57 +29 +49 +50 +51 +52 +53 +54 +55 +29 +49 +47 +49 +29 +29 +25 +29 +54 +51 +52 +32 +1 +12 +16 +8 +1 +32 +4 +18 +29 +16 +9 +20 +20 +19 +2 +21 +18 +7 +8 +29 +16 +1 +30 +62 +4 +33 +33 +44 start secondary message error correction +40 +52 +23 +54 +59 +30 +31 +49 +1 +41 +53 +21 +35 +8 +47 +10 +11 +32 +26 +60 +53 +5 +40 +39 +32 +8 +44 +38 +51 +43 +8 +55 +14 +23 +53 +40 +17 +49 +31 diff --git a/barcode/src/test/resources/org/xbib/graphics/barcode/maxicode/mode-3-standard.png b/barcode/src/test/resources/org/xbib/graphics/barcode/maxicode/mode-3-standard.png new file mode 100644 index 0000000000000000000000000000000000000000..36d9932a8adb96860ca2e3bd40fb4dc8ce4b38df GIT binary patch literal 35039 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

EE%vbfbU^1E9 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 Do`y%7 literal 0 HcmV?d00001 diff --git a/barcode/src/test/resources/org/xbib/graphics/barcode/maxicode/mode-3-standard.properties b/barcode/src/test/resources/org/xbib/graphics/barcode/maxicode/mode-3-standard.properties new file mode 100755 index 0000000..c6cd4cc --- /dev/null +++ b/barcode/src/test/resources/org/xbib/graphics/barcode/maxicode/mode-3-standard.properties @@ -0,0 +1,3 @@ +mode=3 +primary=A1A1A1 124001 +content=[)>01961Z00004951UPSN06X61015912345671/1Y634 ALPHA DRPITTSBURGHPA diff --git a/barcode/src/test/resources/org/xbib/graphics/barcode/maxicode/mode-4-basic-structured-append-total-1.codewords b/barcode/src/test/resources/org/xbib/graphics/barcode/maxicode/mode-4-basic-structured-append-total-1.codewords new file mode 100755 index 0000000..cecd045 --- /dev/null +++ b/barcode/src/test/resources/org/xbib/graphics/barcode/maxicode/mode-4-basic-structured-append-total-1.codewords @@ -0,0 +1,144 @@ +4 mode 4 +1 A +2 B +3 C +33 start padding +33 +33 +33 +33 +33 +13 start primary message error correction +1 +28 +60 +35 +56 +0 +48 +52 +15 end primary message error correction +33 start secondary message +33 +33 +33 +33 +33 +33 +33 +33 +33 +33 +33 +33 +33 +33 +33 +33 +33 +33 +33 +33 +33 +33 +33 +33 +33 +33 +33 +33 +33 +33 +33 +33 +33 +33 +33 +33 +33 +33 +33 +33 +33 +33 +33 +33 +33 +33 +33 +33 +33 +33 +33 +33 +33 +33 +33 +33 +33 +33 +33 +33 +33 +33 +33 +33 +33 +33 +33 +33 +33 +33 +33 +33 +33 +33 +33 +33 +33 +33 +33 +33 +33 +33 +33 +60 start secondary message error correction +60 +40 +40 +9 +9 +43 +43 +14 +14 +50 +50 +12 +12 +53 +53 +57 +57 +58 +58 +36 +36 +28 +28 +10 +10 +53 +53 +37 +37 +30 +30 +14 +14 +5 +5 +31 +31 +40 +40 end secondary message error correction diff --git a/barcode/src/test/resources/org/xbib/graphics/barcode/maxicode/mode-4-basic-structured-append-total-1.png b/barcode/src/test/resources/org/xbib/graphics/barcode/maxicode/mode-4-basic-structured-append-total-1.png new file mode 100644 index 0000000000000000000000000000000000000000..050f89a91166405b819f510c41458badd7a7b39f GIT binary patch literal 31119 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 O*d1~Fq3C-|?0*5?_w!i* literal 0 HcmV?d00001 diff --git a/barcode/src/test/resources/org/xbib/graphics/barcode/maxicode/mode-4-code-set-a-all-symbols.properties b/barcode/src/test/resources/org/xbib/graphics/barcode/maxicode/mode-4-code-set-a-all-symbols.properties new file mode 100755 index 0000000..a90a529 --- /dev/null +++ b/barcode/src/test/resources/org/xbib/graphics/barcode/maxicode/mode-4-code-set-a-all-symbols.properties @@ -0,0 +1,2 @@ +mode=4 +content= ABCDEFGHIJKLMNOPQRSTUVWXYZ "#$%&'()*+,-./01234:56789 diff --git a/barcode/src/test/resources/org/xbib/graphics/barcode/maxicode/mode-4-code-set-b-all-symbols.codewords b/barcode/src/test/resources/org/xbib/graphics/barcode/maxicode/mode-4-code-set-b-all-symbols.codewords new file mode 100755 index 0000000..fe1d3d2 --- /dev/null +++ b/barcode/src/test/resources/org/xbib/graphics/barcode/maxicode/mode-4-code-set-b-all-symbols.codewords @@ -0,0 +1,144 @@ +4 mode 4 +63 [Latch B], since next 2+ chars are in Code Set B +0 +1 +2 +3 +4 +5 +6 +7 +42 start primary message error correction +34 +1 +61 +21 +61 +59 +47 +38 +7 end primary message error correction +8 start secondary message +9 +10 +11 +12 +13 +14 +15 +16 +17 +18 +19 +20 +21 +22 +23 +24 +25 +26 +28 +29 +30 +32 +34 +35 +36 +37 +38 +39 +40 +41 +42 +43 +44 +45 +46 +47 +48 +49 +50 +51 +52 +53 +54 +33 start padding +33 +33 +33 +33 +33 +33 +33 +33 +33 +33 +33 +33 +33 +33 +33 +33 +33 +33 +33 +33 +33 +33 +33 +33 +33 +33 +33 +33 +33 +33 +33 +33 +33 +33 +33 +33 +33 +33 +33 end secondary message +8 start secondary message error correction +25 +22 +26 +53 +57 +54 +49 +52 +5 +34 +13 +11 +7 +21 +42 +61 +11 +4 +9 +32 +29 +3 +15 +26 +24 +24 +3 +53 +12 +22 +26 +36 +45 +34 +13 +48 +40 +9 +10 end secondary message error correction diff --git a/barcode/src/test/resources/org/xbib/graphics/barcode/maxicode/mode-4-code-set-b-all-symbols.png b/barcode/src/test/resources/org/xbib/graphics/barcode/maxicode/mode-4-code-set-b-all-symbols.png new file mode 100644 index 0000000000000000000000000000000000000000..1241727b4073f58a3edbe2182a399db1638bade8 GIT binary patch literal 33314 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[\]^_ ,./:@!| diff --git a/barcode/src/test/resources/org/xbib/graphics/barcode/maxicode/mode-4-code-sets-a-and-b.codewords b/barcode/src/test/resources/org/xbib/graphics/barcode/maxicode/mode-4-code-sets-a-and-b.codewords new file mode 100755 index 0000000..f19a9b2 --- /dev/null +++ b/barcode/src/test/resources/org/xbib/graphics/barcode/maxicode/mode-4-code-sets-a-and-b.codewords @@ -0,0 +1,144 @@ +4 mode 4 +1 A +59 [Shift B], since only next 1 char is in Code Set B +2 b +1 A +1 A +1 A +63 [Latch B], since next 2 chars are in Code Set B +2 b +2 b +11 start primary message error correction +13 +16 +33 +48 +38 +48 +15 +56 +56 end primary message error correction +59 [Shift A], since only next 1 char is in Code Set A +1 A +2 b +2 b +2 b +63 [Latch A], since next 4 chars are in Code Set A +1 A +1 A +1 A +1 A +63 [Latch B], since next 2 chars are in Code Set B +2 b +2 b +57 [3 Shift A], since next 3 chars are in Code Set A +1 A +1 A +1 A +2 b +2 b +56 [2 Shift A], since next 2 chars are in Code Set A +1 A +1 A +2 b +2 b +63 [Latch A], since next 4 chars (including padding) are in Code Set A +1 A +33 start padding +33 +33 +33 +33 +33 +33 +33 +33 +33 +33 +33 +33 +33 +33 +33 +33 +33 +33 +33 +33 +33 +33 +33 +33 +33 +33 +33 +33 +33 +33 +33 +33 +33 +33 +33 +33 +33 +33 +33 +33 +33 +33 +33 +33 +33 +33 +33 +33 +33 +33 +33 +33 +33 +33 +33 +33 +33 +18 start secondary message error correction +45 +20 +58 +41 +11 +12 +58 +20 +31 +11 +7 +63 +62 +33 +52 +46 +4 +50 +49 +26 +59 +25 +37 +25 +36 +15 +9 +22 +49 +13 +9 +53 +5 +47 +63 +52 +57 +59 +53 end secondary message error correction diff --git a/barcode/src/test/resources/org/xbib/graphics/barcode/maxicode/mode-4-code-sets-a-and-b.png b/barcode/src/test/resources/org/xbib/graphics/barcode/maxicode/mode-4-code-sets-a-and-b.png new file mode 100644 index 0000000000000000000000000000000000000000..6c7f1457bcf8ae5b3c9241694ddea068884f8056 GIT binary patch literal 31556 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 literal 0 HcmV?d00001 diff --git a/barcode/src/test/resources/org/xbib/graphics/barcode/maxicode/mode-4-code-sets-a-and-b.properties b/barcode/src/test/resources/org/xbib/graphics/barcode/maxicode/mode-4-code-sets-a-and-b.properties new file mode 100755 index 0000000..8145ca5 --- /dev/null +++ b/barcode/src/test/resources/org/xbib/graphics/barcode/maxicode/mode-4-code-sets-a-and-b.properties @@ -0,0 +1,2 @@ +mode=4 +content=AbAAAbbAbbbAAAAbbAAAbbAAbbA diff --git a/barcode/src/test/resources/org/xbib/graphics/barcode/maxicode/mode-4-code-sets-c-d-e.codewords b/barcode/src/test/resources/org/xbib/graphics/barcode/maxicode/mode-4-code-sets-c-d-e.codewords new file mode 100755 index 0000000..c18a172 --- /dev/null +++ b/barcode/src/test/resources/org/xbib/graphics/barcode/maxicode/mode-4-code-sets-c-d-e.codewords @@ -0,0 +1,144 @@ +4 mode 4 +60 [Shift C] +7 +61 [Shift D] +17 +62 [Shift E] +39 +60 [Shift C] +7 +60 [Shift C] +34 start primary message error correction +18 +16 +28 +24 +58 +1 +47 +51 +8 end primary message error correction +7 start secondary message +61 [Shift D] +17 +61 [Shift D] +17 +62 [Shift E] +39 +62 [Shift E] +39 +60 [Shift C] +7 +60 [Shift C] +7 +60 [Shift C] +7 +61 [Shift D] +17 +61 [Shift D] +17 +61 [Shift D] +17 +62 [Shift E] +39 +62 [Shift E] +39 +62 [Shift E] +39 +60 [Shift C] +60 [Lock In C] +7 +7 +7 +7 +61 [Shift D] +61 [Lock In D] +17 +17 +17 +17 +62 [Shift E] +62 [Lock In E] +39 +39 +39 +39 +60 [Shift C] +60 [Lock In C] +7 +7 +7 +7 +7 +61 [Shift D] +61 [Lock In D] +17 +17 +17 +17 +17 +62 [Shift E] +62 [Lock In E] +39 +39 +39 +39 +39 +63 [Latch B] +33 start padding +33 +33 +33 +33 +33 +33 +33 +33 +33 +33 +33 +33 +33 +33 +33 +33 +5 start secondary message error correction +43 +15 +62 +60 +32 +13 +5 +41 +10 +1 +22 +12 +60 +42 +63 +51 +22 +35 +25 +6 +58 +46 +47 +43 +33 +44 +26 +17 +46 +35 +62 +1 +39 +11 +54 +29 +16 +61 +5 end secondary message error correction diff --git a/barcode/src/test/resources/org/xbib/graphics/barcode/maxicode/mode-4-code-sets-c-d-e.png b/barcode/src/test/resources/org/xbib/graphics/barcode/maxicode/mode-4-code-sets-c-d-e.png new file mode 100644 index 0000000000000000000000000000000000000000..1bacd8da667fc0bee406872762b0eae7bfda412a GIT binary patch literal 35100 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 literal 0 HcmV?d00001 diff --git a/barcode/src/test/resources/org/xbib/graphics/barcode/maxicode/mode-4-max-data.properties b/barcode/src/test/resources/org/xbib/graphics/barcode/maxicode/mode-4-max-data.properties new file mode 100755 index 0000000..d837bda --- /dev/null +++ b/barcode/src/test/resources/org/xbib/graphics/barcode/maxicode/mode-4-max-data.properties @@ -0,0 +1,2 @@ +mode=4 +content=ABCDEFGHIJKLMNOPQRSTUVWXYZABCDEFGHIJKLMNOPQRSTUVWXYZABCDEFGHIJKLMNOPQRSTUVWXYZABCDEFGHIJKLMNO diff --git a/barcode/src/test/resources/org/xbib/graphics/barcode/maxicode/mode-4-numeric-shift.codewords b/barcode/src/test/resources/org/xbib/graphics/barcode/maxicode/mode-4-numeric-shift.codewords new file mode 100755 index 0000000..bad69e1 --- /dev/null +++ b/barcode/src/test/resources/org/xbib/graphics/barcode/maxicode/mode-4-numeric-shift.codewords @@ -0,0 +1,144 @@ +4 mode 4 +31 start numeric shift [NS] +7 numeric compression codeword 1 +22 numeric compression codeword 2 +60 numeric compression codeword 3 +52 numeric compression codeword 4 +21 numeric compression codeword 5 +1 A +1 A +1 A +40 start primary message error correction +60 +12 +24 +63 +32 +21 +40 +5 +55 end primary message error correction +1 A +1 A +31 start numeric shift [NS] +58 numeric compression codeword 1 +55 numeric compression codeword 2 +38 numeric compression codeword 3 +34 numeric compression codeword 4 +49 numeric compression codeword 5 +33 start padding +33 +33 +33 +33 +33 +33 +33 +33 +33 +33 +33 +33 +33 +33 +33 +33 +33 +33 +33 +33 +33 +33 +33 +33 +33 +33 +33 +33 +33 +33 +33 +33 +33 +33 +33 +33 +33 +33 +33 +33 +33 +33 +33 +33 +33 +33 +33 +33 +33 +33 +33 +33 +33 +33 +33 +33 +33 +33 +33 +33 +33 +33 +33 +33 +33 +33 +33 +33 +33 +33 +33 +33 +33 +33 +33 +47 start secondary message error correction +46 +50 +54 +58 +45 +5 +4 +48 +55 +23 +48 +19 +32 +9 +28 +54 +12 +49 +18 +27 +62 +35 +59 +56 +59 +0 +38 +4 +50 +14 +21 +8 +19 +19 +16 +21 +26 +43 +47 end secondary message error correction diff --git a/barcode/src/test/resources/org/xbib/graphics/barcode/maxicode/mode-4-numeric-shift.png b/barcode/src/test/resources/org/xbib/graphics/barcode/maxicode/mode-4-numeric-shift.png new file mode 100644 index 0000000000000000000000000000000000000000..bd503aa2848c8d47a6af14d08578433968fe273d GIT binary patch literal 31472 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�J*rT73ecL`b)-h>s)$ z_d84uWBKM4)|80iP{3j$uuKk=i^2( z(4j6t%6kAvMl>)@L&Z6l%|}yTkd)Q zT|c$3Cz@^D0w~|;$uV{a^XQ8Z9|FAL(krjCb3(SH~HL7o2(b-H&n zJDfhZqeS8+Y?KZPU*mv+Nop$!_Uz?3HLGj6X8!^mB!nGOtS~G*;`T2cYk*Ml` z5vdx*@@562SB(uI#SgxuevMIr=w_t6HC4QS_Y`g7qL9_qVa6Vt&Pi_XtrulC7)-u6 znF@Q%SA(Z8)U{HoNUhUGAtGwXT>)zGL1AM?C8~#BSrw%d9l!1=<2b^Xk=0?Um=;DZ zN(wUbcxUbH>NN0MOy;^*>AS`91A%4Q6*5OIS4f%a5Ls0%5j!Rq~tUiN; z>d7Dh*J6<@bQl(hTYUZDo${xd1)*&X4aafv>8=NfjpL^ zaDrlDQ}5ki=vmJEXph;Ex&hB&DO^^~(+D2e9h%56}Lyq#sy~mkXbr8!_VnvZY z?D7@Jek;AANbC?Jbk~Fi)DMRT+`dP^m%Z-U=SV7VA7tp21`!xg9HNCfV=XYE)4l>~Wt=?>9GrFB<2d0|t({_MldH1bR^jlQDv>q`3$IB&M3A9^P9>_f@zH#|RsN`e|v^A+cJ$fT=0J7Bkk zq*Kr{Wj(gUo%mt>*$-3ji;Dp({f|M;TscFj>B2^$6Bc@=J)zm?E&q3Ip5!kM@T+>x zO7wd!H2W@OhCB?CQ>$Q34==PT&!1u5Jf(w9YhIZ^ z^Ze;Ev6Bv7ak7ry=7*1X?7f)WW~82*wlI84Vsd)92tc!*e>p?OEh`s82MCSO+pFg0xxentMxk8(b zu2I8~Md$5K{z0#dZbTXo&rIM>#Nq~6aQZFnX}ur-CHBGdjVuHuxW%UDk}E{GOBKUrn!>t-$JKN#n+yeTTu^oLqp#8dVK<>r1K2uAS*@0iXz(dFIKGC)Z43+nga{rMI4qF+W zv1`+7_jVYqxsmF=1qsI()OgxcCHEpS`# z7>>y)#fFaLe?`M1a817E2(OI%^*&tEsdhMdc&*)%6r$>8U z&maQZPbjYII$kfv?7fs}>$CdFTrf2ip5C|nCCr?(ns`O7$Jg!R@AHqBIBI-zy0wH7 zDOf+K=;ED9r^yHL)qz3bI#PVbQ%j8kx9`fZ?NcoyWb%4SgNT`LM7wf8G z;Go}B$?GzsUhfP)@0zs)CZ|vxP&|AYfH0Q@Q)RMuyi_T+GSmk6PYU!Q>>eofuW`US zKp78Yw^7;Zv12hCcD9!mk$ajS*`30Ku7s5@*Yc|;Y68HCy1pG+dm(#F@aHQSlAA7DB;vNEV zgpVP7$(#3*h7T?S65q?Phaib|O&S&RMJH z5;VtHl4DyAK83whQzngP&63~j+*A{x=Z_wpMdd2pvLFw#%q~m!_beRv1NeFb@9NSe zL7!F{JpJ5>iGi|<7X#~VC?=ZhE^4+&nMQC~ z5St>R)8s!7!FaJHLoknbzu@FE1g!YUd&+?gvV{dqXj438KX#3viixmAkrn{UW=&U} zzH;F7y{W@F_wrWO%JXDb`|2*y6ickBnYsc)L6t3{zkY0j+Z)D4Q+fj#bGPf9F~Uy>6SJ}KC>iEE0(%2-&G gtnKxG`y!)+X81OnUcsIK|C#Zg-R`@XJA!Ec0ktT4HUIzs literal 0 HcmV?d00001 diff --git a/barcode/src/test/resources/org/xbib/graphics/barcode/maxicode/mode-4-structured-append-1-of-3.properties b/barcode/src/test/resources/org/xbib/graphics/barcode/maxicode/mode-4-structured-append-1-of-3.properties new file mode 100755 index 0000000..d0473d1 --- /dev/null +++ b/barcode/src/test/resources/org/xbib/graphics/barcode/maxicode/mode-4-structured-append-1-of-3.properties @@ -0,0 +1,3 @@ +mode=4 +structuredAppendTotal=3 +content=ABC diff --git a/barcode/src/test/resources/org/xbib/graphics/barcode/maxicode/mode-4-structured-append-2-of-3.codewords b/barcode/src/test/resources/org/xbib/graphics/barcode/maxicode/mode-4-structured-append-2-of-3.codewords new file mode 100755 index 0000000..ab8376d --- /dev/null +++ b/barcode/src/test/resources/org/xbib/graphics/barcode/maxicode/mode-4-structured-append-2-of-3.codewords @@ -0,0 +1,144 @@ +4 mode 4 +33 padding +10 structured append 2 of 3 = 001 010 = 10 +4 +5 +6 +33 +33 +33 +33 +56 +39 +59 +0 +45 +11 +47 +18 +61 +26 +33 +33 +33 +33 +33 +33 +33 +33 +33 +33 +33 +33 +33 +33 +33 +33 +33 +33 +33 +33 +33 +33 +33 +33 +33 +33 +33 +33 +33 +33 +33 +33 +33 +33 +33 +33 +33 +33 +33 +33 +33 +33 +33 +33 +33 +33 +33 +33 +33 +33 +33 +33 +33 +33 +33 +33 +33 +33 +33 +33 +33 +33 +33 +33 +33 +33 +33 +33 +33 +33 +33 +33 +33 +33 +33 +33 +33 +33 +33 +33 +33 +33 +33 +33 +60 +60 +40 +40 +9 +9 +43 +43 +14 +14 +50 +50 +12 +12 +53 +53 +57 +57 +58 +58 +36 +36 +28 +28 +10 +10 +53 +53 +37 +37 +30 +30 +14 +14 +5 +5 +31 +31 +40 +40 diff --git a/barcode/src/test/resources/org/xbib/graphics/barcode/maxicode/mode-4-structured-append-2-of-3.png b/barcode/src/test/resources/org/xbib/graphics/barcode/maxicode/mode-4-structured-append-2-of-3.png new file mode 100644 index 0000000000000000000000000000000000000000..c53022d782b687fb5db7f8e69ff1c427a5644eba GIT binary patch literal 31413 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 literal 0 HcmV?d00001 diff --git a/barcode/src/test/resources/org/xbib/graphics/barcode/maxicode/mode-4-structured-append-3-of-3.properties b/barcode/src/test/resources/org/xbib/graphics/barcode/maxicode/mode-4-structured-append-3-of-3.properties new file mode 100755 index 0000000..11ab9eb --- /dev/null +++ b/barcode/src/test/resources/org/xbib/graphics/barcode/maxicode/mode-4-structured-append-3-of-3.properties @@ -0,0 +1,4 @@ +mode=4 +structuredAppendTotal=3 +structuredAppendPosition=3 +content=GHI diff --git a/barcode/src/test/resources/org/xbib/graphics/barcode/maxicode/mode-4-structured-append-5-of-9.error b/barcode/src/test/resources/org/xbib/graphics/barcode/maxicode/mode-4-structured-append-5-of-9.error new file mode 100755 index 0000000..9fd2ce5 --- /dev/null +++ b/barcode/src/test/resources/org/xbib/graphics/barcode/maxicode/mode-4-structured-append-5-of-9.error @@ -0,0 +1 @@ +Invalid MaxiCode structured append total: 9 diff --git a/barcode/src/test/resources/org/xbib/graphics/barcode/maxicode/mode-4-structured-append-5-of-9.properties b/barcode/src/test/resources/org/xbib/graphics/barcode/maxicode/mode-4-structured-append-5-of-9.properties new file mode 100755 index 0000000..709ef05 --- /dev/null +++ b/barcode/src/test/resources/org/xbib/graphics/barcode/maxicode/mode-4-structured-append-5-of-9.properties @@ -0,0 +1,4 @@ +mode=4 +structuredAppendPosition=5 +structuredAppendTotal=9 +content=ABC diff --git a/barcode/src/test/resources/org/xbib/graphics/barcode/maxicode/mode-4-too-much-data.error b/barcode/src/test/resources/org/xbib/graphics/barcode/maxicode/mode-4-too-much-data.error new file mode 100755 index 0000000..74ec7c8 --- /dev/null +++ b/barcode/src/test/resources/org/xbib/graphics/barcode/maxicode/mode-4-too-much-data.error @@ -0,0 +1 @@ +Input data too long diff --git a/barcode/src/test/resources/org/xbib/graphics/barcode/maxicode/mode-4-too-much-data.properties b/barcode/src/test/resources/org/xbib/graphics/barcode/maxicode/mode-4-too-much-data.properties new file mode 100755 index 0000000..b52076d --- /dev/null +++ b/barcode/src/test/resources/org/xbib/graphics/barcode/maxicode/mode-4-too-much-data.properties @@ -0,0 +1,2 @@ +mode=4 +content=ABCDEFGHIJKLMNOPQRSTUVWXYZABCDEFGHIJKLMNOPQRSTUVWXYZABCDEFGHIJKLMNOPQRSTUVWXYZABCDEFGHIJKLMNOP diff --git a/barcode/src/test/resources/org/xbib/graphics/barcode/maxicode/mode-5-basic.codewords b/barcode/src/test/resources/org/xbib/graphics/barcode/maxicode/mode-5-basic.codewords new file mode 100755 index 0000000..78bfe26 --- /dev/null +++ b/barcode/src/test/resources/org/xbib/graphics/barcode/maxicode/mode-5-basic.codewords @@ -0,0 +1,144 @@ +5 mode 5 +1 A +2 B +3 C +33 start padding +33 +33 +33 +33 +33 +26 start primary message error correction +33 +12 +53 +26 +49 +21 +37 +45 +59 end primary message error correction +33 start secondary message +33 +33 +33 +33 +33 +33 +33 +33 +33 +33 +33 +33 +33 +33 +33 +33 +33 +33 +33 +33 +33 +33 +33 +33 +33 +33 +33 +33 +33 +33 +33 +33 +33 +33 +33 +33 +33 +33 +33 +33 +33 +33 +33 +33 +33 +33 +33 +33 +33 +33 +33 +33 +33 +33 +33 +33 +33 +33 +33 +33 +33 +33 +33 +33 +33 +33 +33 end secondary message +5 start secondary message error correction +5 +39 +39 +9 +9 +62 +62 +6 +6 +31 +31 +53 +53 +56 +56 +6 +6 +39 +39 +36 +36 +3 +3 +63 +63 +18 +18 +9 +9 +28 +28 +53 +53 +59 +59 +37 +37 +38 +38 +40 +40 +28 +28 +31 +31 +41 +41 +20 +20 +24 +24 +44 +44 +34 +34 end secondary message error correction diff --git a/barcode/src/test/resources/org/xbib/graphics/barcode/maxicode/mode-5-basic.png b/barcode/src/test/resources/org/xbib/graphics/barcode/maxicode/mode-5-basic.png new file mode 100644 index 0000000000000000000000000000000000000000..b439a4d88437db1887146cadf97c1af747cad121 GIT binary patch literal 32099 zcmeFZXIN9));27%5h;R#2uia7DplznD)bje%m)M*OogT{}6nxAO_X?&Lvr4%fdIv=epCZnoC_>5K z^5^47>eQmdU&^!c#-I7_7y)t*q&1I;-=TFhZ9(ECxh}v;BP7+I$PLo9vJ0>{5Bes% z^>lX4kqkm`-^jRB0<(v_#nG<%)w)OG+^(Zzsf&)h<=JbK@t9Z)&?qbQxA@8axU8F+ zP~kXNQb;i~Y<<~v0ag{Lpld3JIA|3{$L!h@&pMGJIm|_RjSKTkedwlUyvNlG&Z`-V zvFbDe4}wWB9FekyV)I`}f;mLy)9EFx=IXarDWf5Aq|_H0`UDDI z177Yu0WTj|5ubh(xhy0`ZPyxe~RUY^wbrATL(Sw+t4M}cKVp9ap{ zd6{F(@m@mHFT*%x?+JLh_e6a8GUxXqs#Dpax6Nr=gg|L?Yw*Y${SH{UCXZuJGLfWjU-iyJm9Z(Jx@tX>yNMryDCLaZ*7v4 zLA->pZOLgDP`EFCmT@#yi?Hq!;k(qXu0Mq@>X;Z&q5 zxBAxmZxwi?Wtt&cYcs(5MS281zq<)&&9W>N2Xo10fAo`JUgGP#*uB@SM{fD0#I{vf z-UjrGVwL5iy(XTLo=0{mPkYmmwz7Xt83kW^Aibx2-nh+~)Gvhz9t9M}pH;+P1nZ!P z8U+k#rD_85&DazFUnTvo@^Mh)p`WAJFl3PNKhjPIUB4S+fo(n6BKuF2%+Ibl#DWT> zqrFM~cS`>=ztVJr?z1V{bnsK1$HB&9n{L4zVb7Ao>J>$!qmXwZWk2i+H|mWKxIGn) z5)%oNpyRU!!ThPNJjjI?pbe*drTbw5AI2@-_K4g>L~|qV2hg^3J3x)hpkqN$=(DR5n+?8zxe{)|^nJ&GMJu6`?fVYR#r3^GI_~vXT(1`H zY%Z+Qm<2+6VUq0P)=KErG+>T@>IZK%#t}4UnYaF?n^oL0i4cc^>`FRrE z`E)9iE7HxjVjWP8Y)SMPPV0CQ?iu*k)D(}evoVg`i2Q~ zwe250{a)mMr}pzx{y0JYk9;*c#^7oGTME?QK#4liA9g7R>gjp`5jhKv%_Q$(ci!}^ zw;n2#*B4PoDvXxh6#}1YNtgV9#`esUY-)XV2%ZOI_q*qhvWG@f+ljLAE@4tQsx6F6 z>s@0e`9nPtNfE^LhqRZ|(J6v~}O( zpx+#)o+`oKxXIg5ZN(h9SK71K?mLiK4O(GDZG#<7Vt)hk^22I)kW_G05@Ed6IWTb+HMk z3#Kp+k_ayM4(E^hNQTspgM5%2w)>9293K+b7Mad?-aZ0tfJB_A(vmaV5|#RO1%O|Z z9U~YkwWw|_W9ed+b1XB--;SgLD=)84v{RCj`FtUjv-%}EN$6!t``}SqMMv7%svoO- z$hO)PSW+G?Mf9ZQmpN>D>Hu+5|EI`%sYj zZn)vT$Mm~i_^CQJ0fRD-WbVY@jw>B7EIS0wO8f1*c)w_*gK_l&mgwmtpX7h|R|oFn zPnY9ZvIk(g*7F1Vizax|(e^4+`v z&VDJOiu_S9s)4;9)y#Jlai{{=RHu9Zb6}7oFquzcJ{RT-1M~rExl3Q!aV=mF{A{Up zJ50~bp?k6X?HdbFqbcc{Vw=oY8{h;?VR3#e=M#)f4>?lEapa=PS|7tm&{ocXpDC!} z?i|^mR=hQ-=GIgGSqRmaCQ^^c_tw4$V3R9K%-8!S5W72b_5o=G7iObMELW;7NT?l# z4?3OEtZT_K3U$0T9?E^XC={V{^T-Cz)w;cbM!EqefwUq3jR+a?NS_sJhQFa|M2NMa zTFamdYw&Ah^;B4BbK&nG#=s5vc**8Gajl?ipH}hZBb?TvaWjbJzIe}LkYbo`^#LXk z&mpp^W~#DAnpPN;EwTCd&iuIgplBM4;U_v^x<9-)Iz_9|WLVrJi{XFeo)gaiE86bM z|0Q4nxCF2*f%wP&9TxDvo&5>-`5(WGI=fHP|A?+F6L<2lQtEf49zXELBX+^S@m&Uy zaz1Slj(VCdUEbfdwGTxev5HTIB~DgS7s+kPztRL7%oCxtY{6%O)^_{p5s z`>hYbW=@-2ApSi-L!?>F@+}ciQ02eLD_TumG%|@W%AW$x#PT*yF$fJR0|}G<*M7)8Gd$N*>H(}$$Z2=$3LxP;o*n?lNGB} zm44cIV2$Cz*xZr?gDCY8U2;|1{)|#1fdL6Oj|!EBWBxULHQmF`S8SN5i)j)@|C^?o zR<6|7Y-#Oytid#HdJ{ndgIBkENduE})f}JRAIEl7PSFm0>F)&&`#QD$ap45k2}os0 zGVro%`L%!=a6Vh0u5_;3wy3Q6+`P{u5T(=@kgk`>{{{=;xpD&Yw`k`e_x1hJQj<&1C8na`(`V zze8WzO~slS{s{{Hzh*IjeU!0y0D*h7Q|BEr*!9KsGdTtuPrT~|Tyn8_i>+55$92Li z(TmdB;)WeXg4F4yI+J4t%-^f-m`JH>IUVmM^>|1Pc&zy-HFSEp@;mt|qj2YxRaMyl zttr;BmC4jW=ecy7ug#2X(MED}&fq9i47C|(>5^d4ZKAy_hXnl41f4$NgkJfokKxYm zHrZD_bIF8(qqpSAQ^b&L@k^5SL9JINOn{Az{^-r!P2vIfu;gSQdi2*gNHfFBtuJdW zXt8hJk?mvXQ(a52yspJM3hlPl=#I0W8VY<#B8g^aYZv%{CLYqs(uQj^7vrNjlZY05 zuC1Ku+-NdNdwVAruzF_z&v=8)c_yEabKh6*(*}HXJVH>1GzK2!2$tXUy}%d><#mt~ zQzuUR6qg?MQYPT618p6b=_9+Rx(0+7Y#Dxp7rwT*i+(oGU=t{x9@+4b9gwh~e7}5pu#5*hR}Y^AtnIl51(sX89jwDs7iVft-?=Ro zY*fgi*DDZ1#pT*b6E=F$O;EWIPV=~cO@V_g#^~X52-BU`8jWMgWp)mXOJlRjexlvC zhJzqd0ss5MAC{z~8(46%A>iQ3t%nO$Qra(!Sq2C7L%3FLBvL`DGCR%+4JM_fS0q1p z7Tw*v9!k#AVELq65zXSP)uHCfpO674>!xxKIfR-k`$GLz0=GY1zu$B($ctG&@XQsa z@71V0?Aa%W@?1vbmQtv~%5%ojhoi1*0)U9IK7h@p&r~%E4nBPcmpoq{{IQm#+cOP& zc8GMI{e8fj6smBK%$4*sE*AicmF?>#r3{zAM!nuP9Tji(&)Ysdg_vnfvK9IGl4RFA z7kjpxbROEk*XB0>8<0e-smb2~OfIuaDQ(qD>c@pEZqv(OQW$*CoPl_CoXRg6m}) zi_6G2Mgi}W*|)@8kY#T~z9kvx^pHZ;we3gm?`}G(26L<-B2NLL4WKwy06Zo!zu)5B zkyM;olzHN$UQH35ArTb$frjkCd)7RoXZ{vE5IJP~ufWrN1OymL}x2w^W3!Qvqx-`k-Zi?%QjR3!=v7tYR^Zyw@nl0b_G9&Fv ze-gW8TDf2u$5*QScQZazy(O zb_1I?TR37XboO6Q?rTp_f(v>!2bsIa%+8_8EZBQ^V!gkOf*pKyNl%IAdsSG9BHaelFYB;U3SP+Y$)sa;=b!N7D`LHq|5?#Pa0q z1;!GF`LODGu*KkWd3^$L*UQWwj)%nc<2ONcy;Y(7<$UI>{1Xd$pKle=G9gm!zCIx* zf1;1QZyPl!Il7o89$v1sdcDo5Cl#BIqB+VIOu^>7>}wDVsr2SGYi`I{x>R0Ucssd1 zB~8EJ7?V8GH|Yf*_`U2=#loF%{EkPbcg9Pa$EnE)=|0&Nq}a$TpKMQ|k+D9)<_f~- z6MFrFBq!+bOX&MHfe$mS46Y}=+qb`6@NSEUU!AGeTh_WaFa07qOh-eu4^HBBF&&!^ zqf}YTpCa7Sz#AjSk35%jsNSIB?!Ml-`68=y`#dmAh1ivRnn{(lDS}NKF|KYSS9)P) zMR9q6t?*$J!C_(fW{5zy8kc>l@``p4z(83`oKhl&>`O_IVR4Gfb>|daW<*yw+e}lF zRqnMp6|ygAPw(4&KQT^4w6lUfbk;0Xwe)?!E= zhChyP$~;K?Guw<)_B}hA15K29*W~2PpXf1Vtah_$gHG`q1yT0eDOm}RR$^x#fyO^u z#C84qeha;)cA}l)wle)$g#B-lWAnD4GhAYsr%eBYZg~^MHrYZBc8G!@!`0~jeRls^5$Wv*>uzjT5N5)K1yKq zFim>5V-JG}(wOyI!enyiJ5!~|fimc<`%v9Zf5$7q+)3EU)87l~wdubj)(xI_sdQ?jH$4SD0~P*i zEn6>UFWT+~w|$(-fk3tvV^keRk%67(y_Y=8hRQ2?Z~^O$v;-u}GRoD;pB|?B>JgfTGcDnTBpwRvb$v!#0LH5BeRp!4C9pH{3&vJ@*{06SC^yV!P}luf zcUUZq)D@`<-E6oae#eL~^c{YBU30v`OV9XIDPloJg(+6Mx*Zk60y=M*let4&E2A%a zZ_k)wwm-zniuj<11{hq&KjcF;0fq~F|8poC#2Qav(4-DZOWl6fRGoz zZ3i#7yE5MiSuGnAG!5^7o?U>`j{91mJ#KDgVPwhr#q%mnrICDGj(;4?x#|;Mbl+Rp zzdQuFvFt?0ZJtocmSMrIR}5njk)=&@EPtHr3&i*p<2=(#HtrZ6s7>nP_PY^helW9F z!niRPiK|$A(#byzW+h{6s)*n465ozZpJS;d>U5mzZ?=WP?ir^_D*8?a{9zq^FU;7T zK-X`lP1OyCAVL|uDD6_6ICV1b({%*)m518MvI7J9^LA!d#|D)B zevZ1}asvY{1bZ71TN)dvbuJ9p-(LO(qqv3*%WtY7AmQ!4v8b9d7DCcW18K?bapQt( zyRJ1A=5Ep$3GwEgT;C-CiCra^)6`4va$U8aRp@4@S3o+l1trwDTxWBaj*8iKz(Nke z;wp#1=#~!+aAdC_;AIoWY+tf1z`7Q=+?yFb^R}{|WuMY@Yc^_bC^2WhM=(I&aiQ<; zAdI+8_N=s^p^5|xT>0h)?^iS9sFnHXO!AZ7E3S$g+FX%riIV|ZUsZU)hh(TFBskT! z`Oi(=#d>cgw%CjpAZnbRsZf9gz^v+yjP59V>~Ve+OJa9$+`=Y_B>Om?|MxI#NN zfjQI%U6)71H( zgSH;ev2_}=^1z+I9kcjXPcft-V|5>NX*j?`(&w<$kk&5fr`llJft^_J8ZAhl-FY_C zHIDE$=p=s0tZBK~V!pLmD}QlP)_jy1j|MpfX>~l5KC35D&%qfkT$0z(^C{tKi!8bj zN5Z^f^xYcTUNgG%fRkkDgJ)|oj=A#fY{lYyzU@9zt~2UT1=g=P4L01h;QUpRS{GS zYOWirJ}oto-#K#u<&yK;06<8w;XGgna}L=E0wIaaD96bddYQ&HymtpJ3&Pu{Sr_TG zi3abKdox5!%5R;f@uwoFCWkLkBC^O-rZKipEsZ$nu3dw?>o>=+MfA-axdI7t#34va4TVjDG98 z2x7pk&H11ly7Br}%7xOR^aosQdTVwS2Z%>S4L!ux!|f)z2d4W)O8g8a*wTIQ;HJMS`RPUp9AZjF8`?8@1JfWPTEBPJ2b{Ev&LyEfq? z6HmOzb?L1;+UpMp=0wQIOL?C5dWnM1V(&t?3f)p@ zFk?TE*>qKBy=H^no7>h{tRS3>e+-{U-Cy~OGSQ zyCM7f?73o4>8+ZDW_ctT0}%Jq9_c;*XgQwdQOPcuT2;)u;SL+VsKqhdbns%|r)sh) zr*cnwSGP?i08vc2NmM@4yK`Lb=6WrYX9{1E_{=$N`of&Sol~|8QSR&IP?*NBeukE5 zT5e0(IXoK@TF*^x?+L9BY}ePux#xn#-ITJ++D-xFvO(~0V_R45>P^OnOL5d5HL z6nCBLt)C)oRNM3g_z0Z=ZAK5jjPM47wotD|wxy6syN^Fib}=b}~=)!#Y>Oj?{#In~sePs+_Zq89$-t(J%AW)^V`0O0tNbTcN_a(w3&Gx^4D7Yof; z*k(#Hs8#%|0d{xErakwT$LvE*OC8(aI$hSY1o=N$qqgH#l)w0iHi_+ax7}$jNfWz_ zIqxfnf%l~CjnP>D)5=9vDe`7^mLY%$p$ZNprIYlUo2BTh_cfebkdRgRqzdm*J`AoO-s=S0MoRG{Svf7bBg>mIf;3im{SE@dH?$ymbkNhAU zNy!ko=MWMDskbF-PjqfgcBz{52%+zKF)HEbpcs2i`sG(3)?5`2R&o^_XSeVI_zR;f zEZw|1NICT1w}m!(Tdgy09;yjPIc7H+yx;X#sZ}L|O0nLn>KG^0&cU+YQ7(wsNh$uf z!Vx5?u0eI^`Qi<64p#&WX`)l#9#g=!UcV-T!$f74BSIS?0BEMsL9?9#6EG6 zW{cZ(1kbxeD!2^ncX;k7JJQ4+mzU7w5DPj8`<}eQBThsSh*94Vm**FfmNHVH;mX}o ziS1@i-s3Ux^xez9p)D6Z@OEnfBqg5`VD9``owIZz%8onr{4jHtIci$(Lz`(#5YAAo#!q#%&@JyY9+SDj`X93oi{z#{j?<^Ik5@}0)f z#qa888f2n7Goi+X)AN&{Nl%t2ue4ld`u^IK1Xd*}TF7?0_$$(Xv#;vAz_3r|FvEiR zVJ6{r8xhOugl$2CXOdjtHOy&~ECLH%9@)0|9p!aqtMe?g|2#XcNS~{aRE)Sta%b9L zHkS8}CCpS~mLkrqz2Z?0*7#0xr{$nfyXxm)yAS_nCun2S-d{vuWb9e%?#<@}-wHuPV&>Zd4inyF{;b9-GN2>=zT=oKkW+ zk{uVdoul%cR6>$d5#(s&=_z)$%4uAfGdSuxhs*#{6qvNqbbRBo2h@@6Ep9R=zbAQ< z?@kt-Y!z23ug^qhZAFIMp5ARLzdNTOa|Xyx>l0h`>xoJ%?(;Q4PmLf>(7cG0ad>7R zcKq1U$m)z8d3?p^)32)AQWZd&Z#=wPW9Zo&JGFR9=TyPDJH6jh45A#2#|JUc$3kcD z00g9qq0KU%|t%BqwNi`vwGN;P0~*#U@R+sjvPTQ8l7wurG%k<#n%Ho}M@9q)2gh>_+mw`aHP%t+I^s9;U z4PXZa4P1n>W}q1AF%=nih##O4U^@UI#DejZ{3=GBwCX#cT24%AiB`esSEIEiz19zG zDTL1GBdX*1Rw*Mdz!@o6`n~1fTLR6Ds;=5(R-ARq8UpAB<6Q%|fWG8Teml8&#;>{+ zst$3lx2puNYT;^-)LQWH&Py1zJM2b+<-rObxxw}DhmO}*G>eo~9Aq2<)pW-xn^F8i zAft#~$oO1n%yiER{G88=xq1F)!Ir{w;MdA=VI@zy7ayMoblxybMEr(~O`zH^T2K|w zJL{+vGfglOJmW3B8ug)HKnGo-6g$~^2J|)6d=zFPG}Mq<#!3$a4u)Kcn@_CS3x*ES zE!*|tuTQc^UzBxOp|{cm`-FjIB_iA=KGsWO2qLkmWiQ&zdhjbF6VXSD!+MJgl*3+! z#~StY;a8o=A)T}SRx#62gQ0hppFnUGUFB(lHZhFA{vZ9uiGd@pTg7ntAd)uVJ&v;0 zq{EX(F{P*PSi_;MBY_HQCG|6(6{;1~u;=ebmHJ&;O_j4XhT!zKJ`yXL=KZM>0#+#= z&}_Eh0-O@(fp2FyP$(l&Jb@35oo=?elMH>_E!6Q;@C?#f(J}(8WCuXB)~2y6=h#K- z(sOpWhf4$$d|vHZi!`_L@A|$fjj>;Q5LCaM7MzC;e+(>5n0E>8yL+vP^n0b z7rNx2OK^h6T79c=30~1!(zW|49$>jQ5KatyBpRJ>>V50!mLPFeM8B2@rY9e>4}~JR zn#us{;&4In0a@HRJ`)d80DR{?(stpwu+Vb_Sxkk zx5iap<{3KjR8z=`M%(>nVr}#zRJtIcu>*}bV4|DdyvXOwB%{Is9QI$cc@J`X_0uc} zS`WBM_8Yf;f%MO(0}RBYZ9}zt0|mkW&Y4?aj)MqnXCRZzxWKu8Y$UW^-2>b_&4l}` zV{`2vnrggT8Tw>!EB*0-`sd5WM~_sQl(52b{c#Zu4ljGFrO z(3#H^_MeC*k$@%D0p-3wV~Fss=brC=Mh^4DQnLQ>zw-Bsas+5CIE(<7B3YA-GTdBrTx9_b#WESIbF?&f{cJiNd1Lub** zC9{~c{L?9*D2IxU{4<_Nn$$Om_o@tSdOy}XayIvu;AoOlA2nCbQQD>d^Xw*@D_NBt z!|VBNz;$Nr;RP{nw&TKADY&Y_NV$9zBV7;?!ix6^`ed=>Cxcbki-+R4S-lT$N*C&bz_m z>2A{saNsMY`Z;_qsY=^MR9+l6Se4YMT2BZ?zU{AHKJ?b(8khT{+w zZB8a9QSlmL7{NU4__Pw^$69EaGpzS2^{(JMj25T0xJKp7;H-&|Z7Te&`5x;qReUg) zJ!Vb;xnp<{P!QV(3gQCtx{|ZX8j8aa->!G_y`J~&MMtH>FGtak z1DV6uodACQRzt__ew~t}q^w}!m$^lJI^`*5=JwGDao&tX>S`B8O-mGxe^EyUvp_qG z6LU&5^?LoNwcXUAKIUC`0dopC(1RmcsH(26Nur!T1c$}T-UE9OS?9e zxD>djuK$QXQ;xeZcS#~#k57ScZjJ~s>=t4l++nSZ6yV>nq`0QJmD|I)hExJvNMH+gb;tzlMlNA@LqQR^Po1g6)Z^DM6i z-hd=rBOEz_=piw82A~v~0U`f;3f|YfSdxX=^^8n3M;q--*d+rX6@+HHc13OBnvxHv z!t4NYlIB6azkZ>AvhFoc@$et<)({uFLLd~J@@e-8^a6%q^>W(?MKHP}v6&$lMu<++Ok)w*0A9@69Nafs^R zXh{?UkLg-eF!yL*w%3cPn2`555|{C{`VHs!hH5(Zs_naLc20^W{4+%I2r)LgKj7p~ zQI0*C!(MNCWBLVSw~KK6_iv9zlVD&6mP|AGZ#tGv-waZNu0 zvcM2^fW?Z)RmC9%@F`v=4Nl~XZuWS*(nY)OS_!!zCfVnS$lC)=eU|YgR+gND+Z2eC zDS5Kx!1kxb_LK_Bk+{0Kbv>E6T=jZr{O;9ajoh8Sl>socTU903oz8f@w@3CR#NTpGN9@~=_1LHA04>`zFHTAQ-~#;kA}7VZ z+FP>0T9%ANor9tfwdM7?g8VJ;cpu@fVAB#4_N0EL4`hYnU*mZF#9IE7LAjHW=a#u*H;;qb3^vwZKur=igSnoL z_EwhZkKd~lmWA21s}|fFzx1Ch`()!}RzqTIoA;GXi>Qp{9Yva>-W&F-v=dn>M@{Zk z8#T1ZUQ-+w_!mu~<O;XpUjV=G#>9et6l(e5FUZ`zwdZsrEmos74^M|B{ju_E&{ssIV(1kK&WZ~Qi13(KH+lI~8Dml1eCc*kgw0FBEhDm%Df~ zpl{`_seaHK><7C;m}zHuIt|JvBL=^us0(r+jwXpq`NPH~4@d@!8bP?BdGXFlgVRgJNl$5S#+y1f~%??paL#vdPdTiVvqYoo%Wn4G^^OX zuEYrVa=HXWN?y1pYlV-rt6M;VWF@zb#7UZ!l)9|=G-4<{2V35I-zJr-XiF(>Wn2^oVm(G^z*}JdfHC! zfKFFz1Ua!@a5F6hQd(kZ!oLb!sa0shjso$~D9^{}VMIN0tTJ{mxht{ju|dNCJZGx} zW*vYI1kz+a&#fG#PCg%3ujC58g16LP_cQOZA9O89sMANZ%{54NK~IZD2}q@`jM@R& zH6pBR&-Qq-g9P0X(Jc@2*~lnHymQ*uFi^9%^6v1M`&QF6sp1vXp9;SUa!Av~+zghe zUJ=_86zBG0Gi?v@2RSUCA{clnjp30%c8-;V)Zx1Hqg+?dc=3t73sVJMzq*RTBnm?N zna2}xT6p!9#+%&BM(&|V*70A9=bXp)<5R`+XCR~qZyNcBgZNb3JIBPk)sIs$c0G5u z>Llxd{GI_Px8DNQPN+N{YVNQ@55P)FD#BXX8_qSL(8Y^dtDUpSoz7>_ig@?s_qCpX zs_+WYfH#(O=S%mVb7`*#D{RqiEl9pRB_Kf8ec5CLH zUdQ?l5*j-rD1BsA<_}%mnR6As8m<6ZAO*P(ck411?r|I1z?ppmZ)O}P%h~z1MN5`e zRsL8h*|wfN9rrcClA_;xl{R75zfRJ^R3;v# z8>eL~QPqpGbzYOZdJn2zyWc~n@3`Co+TPLDS^y*7t;D}P-^zZU5UBEIJ_XJm0Dk^x zeS1>gl=mALr{l6T^I4T5Tw;Vc;|A%zXPm2j4uVhx{ZGIJ61wZR=c5wbhOtq}Z-=3m zHko1PS=`q%^s=YOSY97zb1uE`1xFI%nSE1vUipTpS4Agqc@0+d(W@SQ=}ksViB#o` zuDId3T(Jpb=B5 z++n6FGxt5l5-0iRGgh9-qvOWg<7K{HHD{UmOVK{9>O^Bn6;BPgCrK)+#CL+qZ8YvT zc`~u@Q!clDzqP(?BE@TTjf>(pj)b+18UCg4j`uk{ z4PL(@0lO6gH6FoLo67eqcS|R!x+sjB{iQUO7hYUa?DlNi`skW@nCf;H#|~Qr9%9jI z+?|&jVprO_^y2rc!-!dRw`E@Y5O%(@vafCQSEIXYJSV`rUqq$Wkz`O)6*%%$TrxSA zT^Z|%pZD_+GwmwdYPjqoe9+swO;U6HptVMgW$*5JLggBnDyG>HrM_lqHSDigEm&xG z8Q~(>HaQw*>v#D6TfU)AdPKI=X)_w z*n7J39qIW^#U>i4P_?hmY8YWEZ{^5Is$awcNM5Vz_v>0ak`4MMgpuhJ9TN

0t^} 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 literal 0 HcmV?d00001 diff --git a/barcode/src/test/resources/org/xbib/graphics/barcode/maxicode/mode-5-basic.properties b/barcode/src/test/resources/org/xbib/graphics/barcode/maxicode/mode-5-basic.properties new file mode 100755 index 0000000..5302a27 --- /dev/null +++ b/barcode/src/test/resources/org/xbib/graphics/barcode/maxicode/mode-5-basic.properties @@ -0,0 +1,2 @@ +mode=5 +content=ABC diff --git a/barcode/src/test/resources/org/xbib/graphics/barcode/maxicode/mode-5-max-data.codewords b/barcode/src/test/resources/org/xbib/graphics/barcode/maxicode/mode-5-max-data.codewords new file mode 100755 index 0000000..f32c2aa --- /dev/null +++ b/barcode/src/test/resources/org/xbib/graphics/barcode/maxicode/mode-5-max-data.codewords @@ -0,0 +1,144 @@ +5 mode 5 +1 +2 +3 +4 +5 +6 +7 +8 +9 +21 start primary message error correction +3 +43 +45 +54 +49 +63 +4 +45 +7 end primary message error correction +10 start secondary message +11 +12 +13 +14 +15 +16 +17 +18 +19 +20 +21 +22 +23 +24 +25 +26 +1 +2 +3 +4 +5 +6 +7 +8 +9 +10 +11 +12 +13 +14 +15 +16 +17 +18 +19 +20 +21 +22 +23 +24 +25 +26 +1 +2 +3 +4 +5 +6 +7 +8 +9 +10 +11 +12 +13 +14 +15 +16 +17 +18 +19 +20 +21 +22 +23 +24 +25 end secondary message +5 start secondary message error correction +60 +20 +38 +7 +14 +59 +19 +52 +60 +54 +12 +17 +37 +62 +32 +43 +39 +16 +22 +3 +13 +0 +23 +20 +36 +47 +0 +17 +59 +4 +21 +39 +16 +38 +31 +63 +42 +32 +22 +3 +26 +16 +28 +37 +59 +13 +42 +59 +45 +60 +18 +36 +53 +20 +11 end secondary message error correction diff --git a/barcode/src/test/resources/org/xbib/graphics/barcode/maxicode/mode-5-max-data.png b/barcode/src/test/resources/org/xbib/graphics/barcode/maxicode/mode-5-max-data.png new file mode 100644 index 0000000000000000000000000000000000000000..0d33bb5320ee26fafccaf6c76451bf5584176eeb GIT binary patch literal 32958 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

`^7NQ|MwQ059N0tV@e+P$?>Y94 z9$@ji=bZs1$Uutc6Ypnx187UkXVv^Day0WN&F_gK-Jt1eni0_TKVor8>sXdK1dD)l zQ`xCD-~amulE#{YUQKYGU%IHck{TP`s@!1VJ)#6c^#jeTObb23H$%SO5#?*4>$hxo zpW|p-eA*I}ZCtd3v^x}9>I&9{B|VTGeGl9*$wHPVI3ZC&EB#QWB*C?wt|cZ^%WC_{ z<>-69qakj#I$Fa$WY3l|UNnDY!=#>u(FSZF&9e}b^!UojG3wGn!ady#A@;%{+^{ll zCVdU-hFzC*SAraxt}`%>dde*mJc07_jn5`ZB+AcUQw&lGj1|Rlvb+`iB?a6zv%aBj zu5)ZLS(9Oa{hk9m=)ozA+0ll<0T=yHPU`Q$_hb`T zqfNcW-jiT+F^x>`yZLw+yg?Y~s{LT5B3tpbs_;)4*mmChKZt5yC)>_gUe*k|S6RS) z^dmqiEL%=Akv2XnwA%LS=6HWY!>(Fbb-B17$j~sLxhCHe0aiRCXFv{O==dYgaK-ee z-Rb*I(f6c6(MjvzZewyqKkvt2}q?poJ3}qyu2Edms^?*owK#rR*G%q$H~3^o#0-&e^+e+V)4jp!8MQ zU5tu8Cy9uLI>FT~aG5&S?(Ue^bPRihI@&DI@CP$-Aax#UTqs6&Lt_SCMjk3tJ#@p++WwUxCK-R zjnWf$rjcrtqMdh&7^j$9(N_1wUNr4U=u9Yh+?J&>m2OwwkBOf0VH!&A9>2COV?63J zQh60*LGmKCN-=X6qeD1BSkZ}~a5-$WNelB~(ERI`S{3+fuh{bqFTcXFzJkRxBF$%= z(j#?B6W2IQF6?7=X`jyfXAQDi&QypBn1qTR7ZBfUoCa)&f0$G*yTwb{ zF4d&rpeH70J-1aWRu(nww=YjY2hx8-dnG9_-fb#Z=i|b;tw)Pe+c1 zYwAK?pK8>Np8!>2A8q8cX-Js=WK}+aCQZD2jT>*Yrk)uL@P4>Yy7({N><#^$aYgK> z$(Xa$x0)zA>C*xY4gk{puU?A8o9s&R7tu*A2C;2_ed6oBC`Z_j|DAsRK0>Y4wn^}1 zwg0*ptID5eT3t=|uU~DnYmp{7^J$Hlo&>^RW^m$o1!sQ8U8D>tzjFlbeZP`HVDVz~ zQfhfaBx!LIm*y_kqGVr9Z`J9iN@@CqQO%U9syDUh`pY~i(nPW`DRCgMJm=1(;XHwY zsie+f4taM%4aqu-|HF>%M5q=B!PinXQ857Sc9878$y<>|0dbbNjvb4>?^M!cl>DvD zTiwN5lo1aF9PyYJx;gRQ#2ungOGpgt)@Zd_Ko8EUN30WvaAB~q^XjZvef^KNx_D1~FXI@Bzg$reOY^Huf(-7x@p zI6u5|;oP)0FNu$Fw=8phr5y zrQ@NAe2}YM86|2UzLwfeY%in8V}pB6833_Ia-G`33r1N1 zFgW6%om^C_qVICSx^WC(DF;oK@0U&A7+5*(sB9^*Zfd+UJbHwU(noNi%!`!IZ?I6O zjoQ3+RWXjeiRd{;tm@il?x0Kv2M;N94|#uV`F4HRko)o{(?*`gsp)ls%ad2S0^=F@pbm-6amYFgM*aw~$w_~) zCkJ|U+bajDNcQm$_ZDXUHVhd@PMAZsY{j$4lN%%ctaVE^86-R81(Cl!Q!d(*i-b9`-?gOJ2*wbhigsL8we5ma{z)tG`B2GCKZsDQXGAydYi;^sDw$wV6(gVevv8Aiebet|T&$;~y7YbMOocxhE zURE7n(NI6Gq#8(?GmCz=J2l1-A&Oc`|HyRKOMa@}a>W8^ni#UZqLjSj%a3Du5m;Uv zn|jk84l+#_-Uux^?3EDbxBBeK-0SSjST1qRFZp_@q4kq$0jWHg~|xqIi5WXp&Xeu1s6cn>QelfyXT zt#rN3Kq_pSZ|1Dy8=GNq%KgjIEE?ufOJT4sPaZ(wZP1w*nkg-wLfXj@{Sh~Yk>B~h zh8ew*WwBMIUSul2OfiezDR!y$=2O`4>Fg)ORDsd4$b6H;>ysU^Aor)wSN=t!q+#-U zuW<`z!e=V9KOD-}iP+@U0kQ? zk`H=(kfDN|?tYT{pIYdHD4N#cp&zR#%tCR4Mtf+A>C;#ob`)R5<5C?f>>L3T^j-5W z>zUP?u$YhtzXjktD2e^4A7a!9v7r`s-k{4=nz`b7J-&ug2FKZ)diGSsC#Mo0BgH32 zOr$4lRoz>5D8aeUL+Aau2^2HK(>6aVfIKh_RfMw2anG^_wmDL)DW@*jlS(5DMv17c z_w3u1V(xR=t-HROsOK=BCv0{7_9>&Gd_B6NomHtc*oKw?Z#X@Zp2=soovnxH|LT)U(DFRt=#h~#==qTkF>Sx8j@l5~z#JqRN z-s)^huX*(F%gAKp3&zC<4(EUW7iwD%qEbRUl|-Y9*AwFIE-qOwxoWM+&si=yGx7!G zOo`0Xft)-I4uuEkyBV~9U1@vfiykB_n5PTsow))RF?E}%_1 zg6tbdz9tePbB;nDwxSID#>Kn&!@tt8>`B&x2|CmSGL!8QCe-Yx&9U9gEtxt+{6X&G zzJq{yKtqNhB8B*mx`i7GG9fhZ>%T>NFm@^xTh5-2D|$p2z%OKKopr{X)cud6 z!HRpFR9X_){<5|~^&Pz=U9eI>Za(c)6ieBiE}s851GwscTMDI(__9x0Ba2Kzv_-$* zeq`shirs9%kHftzFo4`@}nq)zs8ubPB_eHwF(>gP!bvUsru_O zoTQsH8x>q95;`T(=$XNb3FCU@{1b@l)k-@bi`ILOPujEfN~8OqNlhr}m`^p6tq+Pl z-ZZ1mk^wZjNGimx6h!K1=^Cr8v85AEzv7mzY!G#R?IcRzDRp&tH+XYNd$`kelsKAMnrCwKiXKg6 z#jm_fnl$QpUFLt8lk%%^^v6J{yZpGr)E@bB&XnA_ zDjMk=VCe?@3yUA#Jvl4*5AP_6tZbH`V*hO7*Hsu+!k@^B?2@R>?`?CzPwSEZEoh$h;Lc?%a znFY?PA%fx9Z!|JLAj<%9%SbC{>F4v_pEW>|tnh@0=usojtE5h{CR#A`p~ci!;UJOH z)u9CjG|N(y=kU76=gYkD(GP45yq?nt_5j)kl$XH=3cX^6?ZqI3PL8Uc@=N$I#Bfct z(QC2$$*EZK#(@#<)$1Fq3ML3~+wruZ;4ucd55@el|87ce_c+`3+}J%wEwVPivnJ_1 zwI)W7vFL1GVlR@lm3hn(qQ5(SbqxXVQ^3plm(xiUjVmyq$|8hUT|N&9j7^e^qXQU! zDK)W@9oi@%ZR0%d8CBJh_lXC8zE(ncH$amVviRfhzd0_;NyT=GNJS(BkS4J+gCygh zm^Tepe;T2-eyt^<2W*xf2-u6bAzg@`mkgJFoG7Wb z=?Laz8YvVvvZDMb;C5JTNoOj`?u*x^XsCe|2Z?LuaO~tJWIHloywOp<8kCwrTjcP3I}M^O)L7o7L{#Ivc~Kdvj?uab_-4H$Cn+FM4-S)I6-zV!Jx z;MW2G?hT%v80bMv5v2;OQ^K++zw%S}-g+G>81JyGWanLNWG!Lc)^Em*@AgbCf85@( z9zwVVOlPTju|Hd2b2?D^5X~0Ab;@~i#SIvj`)d4s-X%^g zN;L?Pl(sz{c^19T?X`ETA9EK|gh>WobG&VmwKglO3%EqAkwr~6I@+AQx_#gfVDKGD z7wW@KF2+P7hY}HlxVTx{;hV}T4l+N#o1f3VQvS@zp*f|QvGF(d5ZiV5X%-XN__3{p z*dI2_mlU$F=ITGWKQGiJb_DQUWQ4CpTP=lG&9wFkijY2zZ6+LpOt+DcLC>f)Y*P<`k>Iyh-p};-(fZuS@&QbA1R0i7-5}* z@>J=LnxDuJH?;yT+Th9bfO?fmMF?X?W(^mS8_7?&ezEEJ!F)h!Kt&~@2;SyYUDKGp-J|bM0P==j_*kb%nh_elUDA`i_^j!qOfO ztX^t&H~aqA0ofh$x@c%~1>a;0KA(uHYj@eb044)fgV(YHh=8m!rB1az-d#FFoE?o4 z^qlm`shwS$_s_Vflrelit=R-2I_!@QMuh<#(YErZ#AURl1Wc9*`;pR=9iYBcv$I^^ zZn{oyoaeqt_@s=?lm8GJ_ZI15Acs5WJd#FtSMYpAgXj`vmJUal{P1%xW<2!wbT~e$ z&pE^eu2;C!VXkk9oH}rADj=j+5OGHO6*{-?p1^ib;IBKi&WJ~L+8pt9p#6)+d8)G$ zY|7V+v#rrk`>t}y=Sa6R=6BW!J4~&$ zl+$q%Z+R}!)#BTRzsy+8h89zaiAPo9-soKHhyRLtfnJEM7o?sjExuC`4K+?Zbh4P; z^E&c&9xRWU?BW4Z#%Ni4`tL=?1|z#tx}}bwY5#{)<2iyXR}tBF^j^#0*n_R8hd+zx zU|nnQrmx6~WN~AWGlB@VN-TOIJ2BNYu@q{3*e9kfCjxdt;F`{aBywkS2H!Rl_@>co zW|0XWRBvr)T%%&gP6zhRRU*Cl5-YcVrzqXo*Ipd>*LXwJi1nD+-0&_BJSS(XzBX-d z#_l->d?6yxXWW$M*yPs4G*>#b(*L`&D!iisx%Il(8BW4LL`UV%N5o2j!d%+u3{kIIRi&BtH z3*8On=VD6&t()*OhSv`of8(ROxL*tAbI>NOC{TV3n$~03K6)YX3$+FBvKx%&x|VHd zx@3Kt)riouWdSxu5m=&FJbuPF=n(!pMSc_T)@MF0jRr~)=`?453`ZGW+$r>Ce{4Ly znomLO+Dv)-3t}gzl3@K!gAJadAMY-7;lOw9dU?g$&&;Kn?pqwH>{W{%^e z{ft+etPeV(S^q#Y9(F?@`YVl=+wi9EW&62MF}J1zKt+K0Ur(kU|EvfX6lAOPMSqzo z0k1IIFqzn$8$jZhIwKu|Ufs%mb2>P_3hxp`N>w?b=LuC|(<%pwn7g**iX0jBCuXr0 zgwHoNP1cwBBZs|K{qcu1`QwJ?!pJ*9L?R^tKSA+`fBa8sp5aFzQa^~ng-7&mk24JS z0K1vZo`r|X0|4OvSL^e#QYJK5=;1 z_Vl;Xg+C|8V=s?#bierE+sk%tR)}{!qD3hCg*3)eAedrS<-BP`Kks}#>kL6@i0ZY5 zAAIb*r)?S8S+M0^@Ssm+8Lqi=Z&IYIJC-+7B>jrJUFRQGMCH8YA}-81)neyc@CIaO zQYYq`7di>{eyqb1RMZu57A0iq-Pf`d#}6hB5{i!r<}XK=!Y1el3$KPv(N6SA&LkHr zJ>xtz&9)96=92OkT_RmPZ(EU-(q-W#q?jAR`XOuB5UtWNY)Ds`!=6Gs1*GFp{T65k zvke>0LVwwachg1gNaBwS*QmGv~9DNySo&)lniA&nBZrI@x)Js>%we zyJ@fy08TzkAQcJ14WqNl8xX2(TaQm}9BMy#-)^>oB4CRyE13Q!)^K90y_gGeKHp(d zq4j$5{rl2@bimm+&c4;?x=-{|!q8BimFI$R+LDanK!O;=|a{w~F z0I?fD0qdJb1{;7;k$R38F{Xs>uVb%n3;4Hfh4s(nMB|XY|Gv40p(ufqaOkQ45STbV z>Y@?Zztz{)RnOT1szwzX^`$)0fkr{po2&g+J+oi0`_i|29FR65@M>bEEJ#QC$=>xAy?9?^a? zl%@teuN?RBGXoYWcC$o`5jS6J7KvA%KB(8|w01yVXIbDxo?hEJU} zB1nm}PQ*6`)uR9HJg#NWGVGL84&vfn7qWPht&K;NIi$}K`OGy{c<4xL z*niQCgJvsRaC`$^jEZ{|>*<%4Hoah?PBvT3s+<>WLmLtZYh}RT?i?4mXq*1;=7vc+ zBwsO6(vV`a!{T?@>vKVsSVWRG0n8dmRA#H;`)4^#x7=r>avac6-qp*0usd8=hbJ9-EG(773ixp~m3h4(Kwe?DhK5`uyc%Tfmdl!Soxi z|Kj(mpFGaiwh5D|K~|82;rQq&qn<#_Z^S^Ud@6!Tl)j!$qiiGN-U~y`5{KsNQvpi5 z6Alw>r#%)$lvRed_20~I(ZVJ%hhBJPXl_V0w(2x&N2X~=jCyL$=;&k}5#DChbvC^; z+o-pvEZ4duoLV`8I*Q20zKT1CI)t~8s~o_d4y68kim;L?bG}qzd-n49DirOt;I6Qr zEuA?XDpo@<_4*It-tuGf8mjRX|LJV9e?fV6_wtdOyu+Ieu0zDY8s4>+{=&L|T|=44 zCRYvB!bDQ=douDYSvV%O%lV<_r~Oo?^>M0(PNo)OoFoASc0=XbZpO_PW+QF&PQ!zN7g&|Ti9%CPlQQqyf~?>-1jbUVLH(uLKRqnI zvZ3;o>nJ}p(SzIhkF$8`!jZAh_L0%S$pHCDpg_ovF=yRXh-oleE4W|B!DG^dII#~rHCObAQ?4)h4JD0{=%%4c>Lge*xc&uzg9ULL3KRf& zH7sIIg;IG*YJ{`YGQ5aOk=K#!`8UEPExUEu^_bDKkb~p!=(Hn^VIGx_Yp~aEMMy~G z4_d_S4OF4AANJm=0Jv>U6^WFJG9Y#j$-acu>XjZx+Wmb}0G-|Az_g0gi>uC0OH_(n zR*xz7x~Pq+l%z}&j;-jUW5xW_&i*T<8dh;zj-AGP;~AZN{=@4PC5)YXJGJtC7q@!2 zPKjchq)jC*?`R&fje@8Y61gD101#H`%Zb8uIXei$ye=5hlPT^+!jlkv*^nyEvOi`L3=zPq!C zXBz_%d%kz0TXyRja$x{I!fSJd*v+iLcSyw$8Huf&f@KM*hAA4aQ>u7yj&ywM z3uQNvq;3~u{O4}2$<;p^9e(Xjl2LCmuxf3DoH|vxx#IC6xlfmYu z6>aJzB<`8W>|y$QIqOCBHp`C;zgm5@+p)Hwlo)EHtTvs!_su@6D$4d^C8@Ix3njf5 zA|jQ;wBIF1&{asr$q1r25+%>i3TaE6KvG|*ULho_^cvwFnvWl7CLlx|Z*H@QjDJr6 zv|wj@btyf}G8CLNqtg&&Xz6_bp9yOEthQ9R8oINqB0CZ$^IF*ym%VejAqkeJwu+0F zuC4Ugt_UZUm8PL>2=SyuIhU<9x7Qc$XSkSJf0rklBwIz`1}+m1{Lo% zj%=*x)DYi(rPeAo`RXNh#9q{ui#e{*<7i##*61k&y7Sdg?=Xz5VgDsKHz1`+8@tPW zHPaaWGZ-7aJ!>-mv%%ZMYM&;adS+}K_FVMG>c&WCPL1UxNB`w{vxdbXQEaQH-so-Q ztG1$Pr7t=qh<%Q><6>Ym&b=FQ3QOfb}iU!MP5BVLjS+}<@ z8^#CA;&b}p>_cLXF$2P&Qs-P6(vh^02gv>WfK&z%a#d)2w6ujCO~u3`GnJdNuidF* zc1TX0T&k}@G&Lei1`(tN32_@MpC6Ou8kd{5&3C7rm^%Je{1~x!Y0Iv`)df|l`Z0G*#`W@qjehD zOX_{pj3jI$iYwj(b;z5pl~%U^@wxVk5+G8X&?6M>2QV-i+bxFJvSS}HNK#b3j&;9C zWDr-A2}P_PSXEqFTqo2a;=m;t(Cr=&-TyoP%(RxW{{yIlh1hz+^N6v#5)zf(kEU3g zWlg-Md*>7>ER>rbEA2RELG;+!|He36$(kg^#Bn#A_zJ6u?e)k`J^c}JQtpD}z3Up; z)6=R)pgE-7utTeZfEa_peGs3Ic3T*Y3P>;8Y^GL^^S${!%gswU!5%96e$#A7nzY7d z8}VPi56#h-<*D+ty(AME{1GpMaM^32d#kc07fQLQ{WFPQ=&%ok{PSs58~?s5oSH)s zu$0Y;h%dDuzh6*bf*6;{=9ChU*~r5C3&na)R-P~xYk?Nle%1eXdSUyEJ!ow}v#SQN z>@pk-oHVOz)g~F1VsR@G?zS=OtZg6q+lWWj2rc=iTE>O`8&j*bsiuOjA-a2h<+Ap= zYw|?7XOW#)x>jVa!b&4I+^DN8p=(7zB`*Jdsib?Vp%T_A%t^ss6h`d2{M_kqYjGm8 zp6k!Oj%3WY2Mk8v+LT; z(}-r&xzFmQ)lS<(ue%1joVS#Ra7#vS>O5E2bOhawk81#sY+6P`5VPx}dWp;MGIxo? zI~V;yg)AAOX>q8?U9KB;X8mSp)%K>X$7BwCZDO82MmUVRwJ|oJYJv-pLbZS^OSl@U z(}U@;dd?85s%3&cLQBi(7CjHv_szx>Srro(D=SK3ZvEwW<>*D;o>joA3Xe-3ahJO( zp`z*{$h(~23&_J;>Vs7>R_a;j(vpl)loLkPEQyqNyW#%MZZP=Ku{=b{?cPAK1cfP~ zjUm{rjkqqk=6Elx0r!ytDw}e_CwEbX3xnZeL}WP&H33|!lg^kDXN^Mo%LcFOp-Ky z5tPL|d0&6RsCVyZS#k)qq!EOhu;()c=1f|3E;2}4hC+4kCuY8BfF#!l=8@##6VdIo~5 zpA-jmsnmF3{vXGz>1s_OF8gYakZxwX^}_2+#!;IQy)u*6m{2BGrFHXtkMArj{G&pz z_`0dnva>(C%hqGmI5~T8C9BtZ7aJ|TvnuP@qx*ysv{9?9I>BMt>`1D6LLJl?R9Zh-k8P*tN!C**WO9E=h9 zK)zwTmqA0tTXUi*qU8Des3=UwaBEE&<*?h2dn1pmm;2v7n3BEh^&{)JvpGQPa9r%&6SC!r1dtE(xAtdlTdgVS@K(m`vF`C`0_c zg>;*`SwvUJtZv0-nS5{NMM7(b?R5TvdaiV-;Cs)N^Io!ZwXv zpf)xaw}1SyJExGZoU#To9bpgL26ZON)4){|5UJkp%vm>$sS|vY6ZW#r3Lk$R(MtTx zERhRp6Zh+t6zQ!Y>SHqZZCW!l-RIhIK7jk0cHed_`-v3egC%>s; z{%NulykOUobG}C3INEDwZBC(t8l#bu?pFYqy#BGIVZ*uUqOWh|anoX?yBbkcTv$nB zV_3e?XQ*RvnuE_n^38U9u2YTUK;%GWWcCB1w0f>XaYHxmV#%J0O4#=0iv;2u^1+5y z(d#|2JQ#;LKn;DO2MXVLg`HzmQYlW|Q%uHS4^fh)0R+#$DJlpOY%M;x>@_frMMxdD z;%v*Tj&e`|uVcP+;&PX#suPj^4pF+RXnma{+WlIIwIfKHal&6!zLF;LEHUM*8>ci4 z7Wiy3{UkYh*B-81yfx7;-8F1i?%FGy{R7zIZ5>YG%@uFG7quj55dJ#ORmTA% z67ms2Y5XmS(6tZ7LRZD?^HRH9%nk?8zSB57U~6dW8h|JUTwj+4F}IXK{IGvn%(rWq z_%hGsf)f3QT{ON<*Vdy9E)Zb=f-M&lAOh`U&$R1ezNKT5m@e+KSs|_xSg#h2z92dP z4A-Rpqn+!HYI581_o~PR#P~$OD+=-yutAWnfDy1EgrYQqG*KZSV4;Q*1(BM+cngy>QC4>$!f*^z@kU#=S=6r~ns5 z|MuQLsOU%7QxB>0tjaf4l|;T-`Mgr9Wj%DCNK*IS3Q6zTKm|{^E6hHm&$F6!O0tU= zeqO1k@(L@aNrQ+9l&AhK4(h#x{r)QoJ*$bJcJa-izzFDB+4?^)0j6A5Q4iuBpucwp zD$0U%0)!epLSZd!XOz(e(3)49zfuU7|NX6}dsu0{E;;<$SbG6O zM~P(xTF}N1F$(%4GH_WID&{$mAm7)%eXIlNj~7^P=8Y@@z58$XdhT7|1+cDA%sv=q zG!M6r@2qckYVd&*pFXVM+%~!Fae31EW7M~)?@h5uegtr!Bvlm+EI$gnobd9Id_MRn2 zHO3~XY&UIIsRE@2-MR z9$__mx6;tIOIU2ZBAxrZj23(YYJLV<1_N@|cUMEzKgD~6{Kap=(ssb9Uiz8fqdmAC z^j8B_T;qikHW!4^B;Xl@n!G})Kv+P(qM2Fi<@iAN{fcTi#$n8OvFJw;|GAgv`Xr+(MqHB zePMl&*#l|Wu#d)tkF|c{p{hZ zk-3luE68#NAwp;e*m^<6FG>z0Zx}E>1k>AZn88)2E5s@rzcy@R{dU^IYIxl_PiK^2 zasm(sHOd>w0)mi@IXMiAMOQP4ta^p~aX|;FnB$5nMgY5n1>QfiLc&# zQ3)gGDnz-*mUMDOM_X0gAup{GA?vs^%kXK?a3i|ZjcbmSraKG%qPk;TkRpO}ITF(jBH|N%9$hodtt^BG)DdMN0UN7p~Ar8O$Y-%bm?)8YMj>O0=N1vDF>Zsmso|HD-NsgI#@T7<%N=} z*f+mk%l4b(m58&Cjs1>N?{t|A-u_k0X-D#-WY;-sZK9ZyG-fRhjMAyuYnrM16y3m7 z^sA4(P1@LW%ACx}$93K&iC6!O1fx>vPqJI(T1yzLcwM&>#`dC@cPn>?*&w}*?V|^8 z=89L}YW6#Q=9{Ev?+m6fRee_`VXfw7sVhecbG0)G;NfSZ2Wv-kmG*)e`2-sdBaC2mWpe&1rIMU_H&&}eY^$CNT*7ER=MfaxAEH8H|tX43em`Kh2Y^fnYv^S z(iEDJ+z}3pSx%RFDse|GU)?EuXDs&a8!gx=9L3umV?_8p>Ykj-%q7~Vyt*Q%V*27U z;jn6{PZ2v>|8AJm+h+LU3oj*pM!Cz=oSMpyGFzO~-6*{gKFMn4Hw4+7N}XKU64W_% zWD!ATHX&u8$T-Yn!Er17lhRmZv5TIWmpz`B(KZnI%p?Cf1{dXB9o+TKm$8K~c?OZ`|+UX+~kFp?n&J_`Q?3dgWudF{s_!`)IlJrxK7%0Pz^GMlHN4x_VY|NG@m5t zZB>G4s6$RfoMWXI?zannGm1?dpLRej=1g4MWFi~;qSf5X#?P|07k;Vh4lH;v5Ke|3 zYj_&GxQW~rg4*`}V^yCWr_NNrErjiH3FWYvMK!!dswh2NYL3UScC;Fl;hK8TmZZq^ z2wrp0Ne?uU@qkzVNWH34^C5|)w>k;#gYW>#=g>s^>EiJnLvZy7_3&Vu6pMBd{hvQZ zQ?rUIjxy#^(v^0{33&;*A@9b`v=nAJ4O#E}AFFkXbw@>iZ*y?c+w`uhu)B00$}nB4 zS<}uXixdCy@Y4bjlQodZ?YfnNU2?Z-~C6ZtxVHae3YSwK$aeZpebvH|w2_ zM`dasyw#_=bu(l!Tg5-R2CHvLted%+8-n}GfsLMCb@7nF)}8vi$H*(X^UXTna@SLk zfx7NF5XGpoa#-K67B_-ELDu^**?;=V7Ekpx)CIPB}5B=(6~LLN%>$r4KemQ>Q~aMa1*|gW)|h7!FJ{iM!HW-}C4}c)M5y8IgaT zJX3SzSFP}jQAuKkMX$T=HMa9K!E%s^E~#koWz3n7@@6My($%y!(zWBhhd-4F?EQ}Z z6l{TSI^fCjZ`xazir7U}1);1_5+9?HxrcHy76A;5m-A~p?8ob;X`i}1vD3zqPeFC1IFZNU{| z51iWd!MQ#$N;*(m*rMnBTTFk%$RobzF2`hE+ljcNMFF_6>@ktAeAAXiXYW!)3H-*0 zlKy4{&QB$c7hkdTvND^BFYz!}LBjXIPGRa7FkM#zyDG@DdA;(#s33gjr?#JO|IlKt zotuy=`$TlNAtN-rRh{DP;=lofw75>!_p8C$D^SsD`xG4E;51)FmVHkrw{5Wewyza; z`^&bIx0-Fc&%bYA(02O=xB^_b1t0^U$Li5dxLM|{Zjebh>b?1%_jVB-d$$`qU@`Uy z(Ah8MBmy7`pw%i=kk<5)>Sk_})(}nikgjd%J|Ep2V6fd8xq>zR(w<1@o~zLGTHLd8 zeS=hmc!_f6t9RC}H>7_|`2TK&x9^Z|o+}9SwUyntPJTExVb&^d-+&)z%&a}g8LTAb2)-lc8UAmDd^nwGW|x4AvK&V*X~u_dHSOb zTGc#tg#P=zYOT2fHi~3I*dJ)(7sg$*T*ABYr(+jt_NL6IycbB%a8N{?#J$1NYd}iN zkQbzOPT)+35hG%BiyimCLjH>H?7ykZgCSC0dO5v9%+aWLLpiEM%c&vdnRq6`Dz9y~ zI1|xb%qYRl^-W`Nobj=usIAZdrg9nVHMZcI4D#?M8SJO)t?HyB$@JhZYLmSs_IO{i zbMOg_ZgF_yn59l4aC*!?vz4}H0RODC>mpeT@|}RIwi&o;mK))Sp4VmA!x!Dr^}sLk zb%|sE|H&m3x!!EW)zeW>YQwd%hG%9I(!H1ROn%(%Uux}iu;bxp?L~o=IMbu`ta)6u zH>anSXFAF$hUL-9*1_Qj&UqGd>BD|vYB!HIsOS0p(^PU>Vt9LB6OUH5={?(`p~)G_0iuI(@G-^;s}=Qb@l2EG@3a znBaDGrJ6wHilE4+|F#`GE2N95_xf%_%<{bZ=^>Lh`rKRJhm#lh?z37{no5vivr(y- zAB|TTv6OaM!0Gyc^kiwUB|Ylb+|gBpapF5wmVO@-;dRlp=4c;Vy`*!eai*dQ__C{v zstYncRt$dsO#A}n%GqS+)}jQYrc3n(qMaL;1bci((0B-?SCD*M&L;RLU4!AO4hTVn zfenN$Of|Arip*9=VSr)WC7qledt3CXi6k8Ct)r{a3j|md5(kjEBJb=#2vgXa+8Q8o zS!EaF=YFdO2_%Gd!Vm>Q!KpQ73FuqJ!pKS4-|1G*hg0Cu9W%x&c;pY|FyjHp|yaYFD-XnBqTf6`SW55|HpFEF$CJzgrF1VZpVMFwSz&dQ0#> zkJ7*WFJ+z^F>B%*(irzCIBcQ0i{36Bw#33l^XlX3b?I&Azu_NQ=MrwX?N}NwPeQ}1 zY%S?5{>4@?RP}r3Tq(9Brt6+9(_Lq{EG8@nHCFJUz4JTePd1H7=nQ=h|1=@J)QPppqbJsUl+LiRmzm)O~Z`6%BDc~&?eQC$e!GihU zQ>*eejiaf$3<5olA2gNbXrD{am0-3e75tJq&Z#U*=(%TWKXGZi?X~yYLe@e3A$H92 z(9AbFHXYuPXNll6VDItqh~tKeH0U0Aq;{-=mFcQu@<9UWpQ=6>}g44*W4vFrkE zy8C0t=mbfcIH*+mdNy8#F8DraVl_jk376rs2^#i{JZu{O+l^2DrD_M<`IjmXC?7ag z3wrYwmGG+^MZu__*+N_xs}lQm*`?-Voa$)3pWxh_4B1*Ic3WyBfcjojAA(=@70!@aC*I>a_)-t`>sZKR-SD^@(o{VNA{_+HgB*5l*U@p zhY@drWTLY2)ymaC)FksbJ!HfEwW);K4l{KFz@5NeDW?_@bsTWXwUetM=!R&S@X><@ zHeMmEISa3w0jvb#Gwuqdtk)HNVNJQ?YVf%QHJ|>TtoLJ5|Ff0qW;hy_V|Mz-m1rIE z+-k4TWREXS|F6DhCN%K0+sG+1m`|C+?1@ty52?{(UTV+XxtH2y&!-&W zvme^^eJmL&>77Egc`2vc)!g95Ghg>&c9&l2O>-m0`02+u>Xyjl5*!Pt<~8PCXT~Lw zSv0nW&o>*F%FIqy`pn(6wSv&Wwwg7+@sS+9fMKDRRjuws#5xUZkh5@64zH8uztw#J zo-&`}TRK>JfyRAwxp7gkyd`Qr;gE@}je4hQJ~@`NKD17nwGZfkQ3sY$l(?>)oTy~E9ZdeZvWSkt@Zy@2VqFp?%21qjqFy68 z{ZC5n=vhFy=-A&XNOBDZmaeBufXVI=V)=e(>j;*?o>$6H%^E)kHdnktZtuRX`!lfzDMrw zqdtUgpk2sP?zENTI=dJS71TNAx}?8wEqX{vbKFLAod23e$~Zwlcv3#!<3(GOlM2oe zb&H>)+7`H3+$RGjB!$1S&9P~rj@?*tTMIfiVcd{=>ske78_|MI<3{dh7trO{8k2x& z_PcPd^w~6vhzN?tQ}Kd6kDeL_y_#w$p1ky&(78J_#ouik5@qjWs#*r))Y*GjQ0|pZ zcw36j$r18Wik#n7cp+aTnabw2We3m_JKUGzHTU#5`~=u}TR@%DsH-0FD&@j|P6K@W z-lCZEk-n#KcXIwr4R{c9b)TAs7!GgtKijT#V1;>sAkSMHQ15>*6_cK(KF)PRuVw+3 z&k@EXJoGnlOuLRQi^ijdsAT68_juFF^4>=`v9>(p5q> zwEvlHcxgwKYt62bzb;m>7riY}8RC;JL^k6~af3INzp~=ZT9KwyC?Z}-Ec_#;{vv?1 z#8wvuWUrp05+*LSN29gSPNhn$TERvprOiHJPZb3phAdU?`Is$1^=z;3GV>~4(#L*j zd?&ZGOYW}nq~-b@O>kRF5V6Dt&~O*w32Nq9rp!Xsg1RY1x&lQXvH7ig7@Kec0iW4A zURf)0oZxqdHX4NN%_|;Ep)k*C-Gx*ASz}I`WAi({L_RZRM^2MipB3pDjy^kl2a?Cd zs9wEkcYIPd5Glv@+ZOqKIq#zp-lci~E!fy>LnxTj`BalAjeH@yypwg7GabpN&utVS zI_RQAtVbGO+V!fD;8Xf#E@{DdRAu+FtaAEbfSg%xjmdzQ+C+510*anFHqJwS78y0( z3FFAIL*gS&X!FL(#NW-=8+-%^nZ2axM3ex;qWS}6q+Oi+^(9#%zc0CdOxD6iJy&+* zTws^D?9^4McfeP)(hDzwXp%fyR7^ibr@-Z_!KQaEYwYWios&I!oc4=K5d7+sNuAMa z-Ikp#vrI+nO~IRv8UuFk%8@M_09mM3Hv{={rN(~7T~4mR{%!&GJ~eLJ7AP5IP$MCgG{E$$Y{&rh8P75E@q!ti0{!8+bv4{R4~AdO zz8lgJ-1jYX(|*T%=)&fTN;|DFK42sx2TGyMoj?O5NQ?rX&s=zbUuGaKK|$BB%_55) zH(EiKjvb2*NgODBX1d@3ywnL&065Qagd-u1*u{nEIg?sH{?81Bnhyu0C|tu&hUuyk zBJLPBTtE=fvz?bh>J+K;c1k{xaJjHWepVr3kqJkHb9?Y?g~%EKwPk%hT=2*D5A}~+ z&EZWiK?H}aKv4HB|MC;~2{AopWJ9ZP4I)AZ)iD;wgx?TE596tN)RnssW}1W2cu94U zzkrRc#r#hjyObaYHz&q2Ht8APbu{R#gOzMxRnBcLeUFv06j?!4e59H*=l-BE z&O5i92xZKr)6bvW>Ub3pb)Xk_meYk(Y)CT`D__;3>^mi~ULvtl8W%J)zn&W4GiKOu zIoBc`lD7$J)$DXQ&Q-|HDY7qa7$V)TUou_nlw-!`(2uGWkW!;?SMBc95WBMuevQnw zE%A~yUZ=A%Gr=%?Xh ze@2YE@flsLXBJ*Wbddwb(%gPn8S)JZ zV)FG{pU$L_B&!xj4G{|u!ikgHANB$cZ4gVkXH3h508jt$Kq&CZmT!=6-zq!ba$RHl zBVr+uzExI#yZUdpP@v67II$R#oRBn}91DD`x9X>sm3C%+zWo8|GiGczXRG?}x6ts* zNZTW0md^KcTeiwF_0#ZfeT=prf!ds{>c8JYJBes{shN%VpU3^*`*vSYJd{M8-C+E@ z%?2}S6{TM&uZH)quYH~IAk5=bs~!IE+=k{=-0dt;<;304&sYj z*Eua=!^W{asYUFo8M#E@BNiQcARj)G&GKB}TPf&LS+I;+r|gXGL|CAAoP%Qv4<(RZ8V~slI|D;-Qrci9G;@RA8%Li6z+l8Xf%QX0omRZ>3@zJp*|2@N?AN3C z^W?dzmmx0Qxn@c-UAbvt`l03u$E54!NEg;iptxplM>1}FrE91{V7Ko%<@%iG$*)EO zYzmaJGZ-&rXRyC|#h7CA7@DY*)#Kf!0~(p?tx?K)W!m1fxw4aN_LSnOn-1AwXi2L_ z+;r>e)Ce*DGg231s4QYrr5O#9a&0RFc@fjw2}3Ii4&hJIMAkf>dY|ts({idxVDIlc z8#>TO5D~8*fu!%#t*C8F9I<(d!Q(#cTOKBi($TdN&S-!#D-*WzCJmP(=_UJ?dk7(A7kf4QCbz%8#vaKK+@%wZF$Tv!W3>REe#*K1U070~{azCB ze3v!y!u(EK1=@Gp-(M)+2dV+?=4I=wXq;r5e$jRNJJ1}RXupgG)11(zI)(xeOC96( zclP*X;(6e1lZDtL9)9nWcXn@ozlOZ9-RN>r$>TyTqW^zCgn!vh*#0h7hV-11yV>uy zP-J$<=dLJB^@DZw*phR_SA(SJDR3B9># z*`1_&nw^ol*9m)a79fl@-fHJc*7uW}Csl1XN3oLz_C^Y19W*r1@_jh&c80FxTP1o> zD*=n->{i(kziEPIZS#@wKzlfE`iyo-myorVy}?9JUg7je#+j{3+Xx&*T9+m38_JXP zrf{(KGdQfgG{aqANAw|g{^zoP+9hj; zj`>>_;;*T8{FDgiQvU0h!EVmSMbIMNq^;|G=WII9O;+vF9_-cjdt z+10-$7u{LJ4QH)B$yRb$3DZ(2zIV7#Jj&$C^(as~y~9mTmzMpAt{X~5p;Gnq8quQR z+J-$R(@^rpI?C@*a0_TU>UtxnQK`Sr{w7Bua+yacTcW7!YJ7^tV?nJkOX$eHP*Gn7)AlEkB`T?P+P-+`S{eVVPh_nrlsBlL)wSMW>U;YY&zGFG+ zuK(i(;o32Va=-lYS-0((DV4^gBn7B(9hPF5Lr&NccZnfw3gw}wvWI%^= zZ`-SC=k0Q}IDT_Up2JwlNVNL+a+{`JzGQuSN2^WX8{GL!_E&AV{Z~a|4VI=B?jqZ# z7V30#;Lq26I7r9K9jZP$aFk3@zX8?Hh_yeR4 zd}AER9>C;}qz8*rm#gl|103bCh>{@Z@CLrCM31L78tS7&_Se`}LPmgMNrIe9yGg-2 zKg_(|$%%Kjf9zeYv~mZoA$^QQX)tP`(EBCM`AW3EY)}AdDVNb$@@Q#t4)+2MEkAEi z+nZV<{nRx+)PFAfX`ctW#Ch!#3kTh4}Z zNk~>@9l{Dw6nrmu&ZbUUGCkffcV0@J%$Cn`c1JJ*0|lv+gHhqljPMpMwW5$SQyP)< zOrIt&XdUa_?~{!SDU}$D)GKiPH#XvNib?>|a%qw6IjMKro{z+V@_3FRo`*E{Y8(-s zdC(7RvS7Ar$oz{UZ+EKuhBq`KiaEUSn@1vFrM!tzwO# z1y2n`4?i~$ZG%3e9Z|6=s=KIgR?=b?tn)EayDAC-pdbY$BOHJ=T;v_XSq(c)01|Q~0&EWB6C~qqwfPd;wj<-w^_7g%9EC?qQ5Zy% zMr`efybVQ@9t6-mBA~z;O(o|uYzhDn+AWy%%;*X7vXMR#KrQ+lSK!i*r{su9x6{Hr z(+6|KiEIwx;Q7yr2VHb{l^3przpuD0Nz4f7cW?9G;-WS}LUHs8ohkg;#zn=l39l^J z*uS2a4;_bYAfp!JhKY{IaJ=NJah0@i0K-);i(OMP>f9evoQKeFbqaU7^grN@;JPd_ zMq8NW2a@>>Gxa+*C^I-gFgx*oz&9o7Tv2E3RR0@B>$jv?MoQX~iT?>tWub#@t=R%~ zKY$&TnERCp`z_g)K^Pxe`=9Vz`Z|lw4x4|+pluO)Kal7a3e7t0SyJr#|HSK5wdIPW zDOEw`@bFm>zS6!TG{>Qn$PQq{J1(R@1b~hY?=SR`ptzMXPB~i)p+W^V+BOP}-P!2n>wKBTrdUYdG0XU0u<~o{ucI1UB*h1O*4%X0l?C*uO-Kz@YTr#ankR=-b8PPYx z&?t)Nuz@!6psKW$8$mj;2KhSe@>^J@P5wCuJGGFm10a%&!HFQiYGi8CWMJBrqsq7F{LL0(qQPP>5f-QhFfj5>8kQqSG}T|&%DJFQ!| z;RRY6Gj?4!fE~%-ls*0#I}s=#c=uoFm*}wU<6@|gf8C3WBum2jhR^>iC*(R#kaUb_ zVEb3@ClxtScU{ozfBjA1>VBcX#sl@2Y9W8Nk2bqntmMLIToxTC6#x@QKkJ$>(P8>D=UJPfi*eK%+i#4WBNJLT62 zR=Rv7&ngQvNpO?-T_ch-DpgeFOlqhW#bp&GkMg7F3O^6ZNLsv5L~!+Ayie(LWQLn% z6bE_04aal2sFf&%LQ`TX>ZDQya_<;ZcakAu0p+gP!?UO2>z*;6nT`7Ymiz z13>dHl+KvERIBo|=I@xG|6KMjOMG%H7|p z9_Ft(lQ3)w9**%|=uciyn=H8@IBi|GvY8sd+$^Qt`qbZLmttl>cTL(E@O$e;s5FsV ze5Ya#WCCCdtBEhmdUaw3#Sq566~gv7i3ONV2F6ywNYr_y)caO6=TBQC?bcsb5z zB4FxBIRiqHiug%pQ+2v~2^|zvmyUjJpR^|si0jrja$BPxqixUa3Lt{Rq*h9_qd})m zvJa!%M^qrvGIm{S*wVRlhOo!j8ng1Gn{O)Phj$rz_Bg%)g~9yR+H+4>6Vytdd&oOg zC9|IKeVLc+!P}k7fYAR8Q04Gf)K#-lx}%Vf)PBD=Cz$2Fkuq;JAm7_2PC`_lkpwbA6^?J*%KF^@u6y1Y=#cT58qy;5W!6$fRp7YA%d=8|JImhPmc z?610a@F)K0Va7_<<#vr7r83);()aQ@yE@N)+wC)q-)bMaQ+(}5n2%Z+Ix?O8v}w`T z$lv$!R(SSral1?HPa=aa?Tc3anZ(K%C_1R4Ks&komDl=K9*9R&eSAmL+Py9@4rF-I3fN*7Q?^o{Y zdwCIi&^O$cOsrA4ax|cU2Z$MT#Sst+oM*MGoiF;flU-xrUj{smRr>c&r z2a&%W&hvNl#(`=45Crelhy}lg(!aGa%>{?)V}Hyn8DM646xg{ua0CEvDO^zoILaWU7*v>p*(tgY8g$yV61qt+a&Cj$ z^GbN`0S{ZCJ_7-_1v4Wc#<9&4^%k85f8&WJptw=ROq~nQsl3sAdZtFEN`_)XMKQh=VjPDL}8(VYYA>w>?gNVTwi1#`P03sy>yO?g5pFgI;98<)-iQT&PsHjY{$`Y zo@jOJm_+(FX}K<-Rtu4mBNFl{+qK<;vkMSB$ro+OPogl4#*!hJ+u#42&1O@xfsPKI z<8hsJ<8m&8lGt&+t8Er2t^GE0ngHQWfUVpSH7e=;7Ou!YKpoN4sq!@dQpu@W|ARs@ z72OF8T!7^LzE>8ToqDO}h}gRw82O|~T{Nh(=v&Oh?F{da4HVwJo4S@Lh<)FZ@nNZV ze*{v_f_DH*26Zjk4zrZ(>&tuo6H8@&oVqelhyJ%+Tq-JjoM$C{JNAOHp`k7Sz{>J% z54W$?I{#y9e-ZisnCHOR<2zM0eN*M9Sk3>951)AHH*bw?(Yi=i(*jL8#{#Rz{LC(W|Ppec; zw({51t9Nb;aX?Lz6vwnD*CX8`th%zlUC-1aKtYb27()=`2XdI zfmGxN#3ZhJ%3f}b)yYq zo4t7Gx{${_xiqnwg;|W$1o8O1V7x-k$xAz&9sO^ceH!gpugXP{5&NRGqc)Dr-0ZW^ zZjk5e(&Reex?~L^BjTqRtC)l~GcWeGeV+1*kl%S4BzDMiXMDzi{PAX|WQJQ$!Rm|bf zUB+Y&!3CO(@;=*Nx&9199LtH+`lZ=3zw@|Ok{9vbA@3D-`;oN5?CCRKiF z{SoiIHp8`5{&Yts+trV9dEaU&KBXqHa)aYi459>ebhjcfYC-ZXawUu#PWUm8S^yY> z&Ua-d@=>~kY-?H%IW8hhKFB9g2PRtxFc-mW`@*tQR$rPxyC3hY;6?VmJ0M(_JX-G)At#!O)y zIJqm^ibNp2P^;uoC^L>6=z~9SzKXrh!3@a!*2Wc#8X5c!V_0Y{K;g zu@R}IRqB#jfWmSWS}8_e7TnxXnqTy+m0Un6$}6*Olh^LQe($Vvpco>%q^dn*o?9A| z5x;I12n0Yx5g!v#HFBA}M5W4A>?`tpI;E94H2tk+QKvN%dwaD7PFZ&`mVML-ojGIg z;#dd2`DegG@p@KaIi~RD^h5KgTMHJC;<(|mvMUSg9oKssb_tEmJ{Xnqp0MER_YSnv zQNSq=Y_Mi+^xnh-VB{Kr#qe%RtxGd0pv=-hVl+Bn05T*G)Ni1)U?EEVx3k45T&zO$ zl*x!T4QNv+SM)evT@vES%%>>ZQQe&3>vg*sQz(o$$rwPJu<5pXSQKDpY_5Le@3bqY z0Yr>twO}MmFLC1EqSJOfXMX;kUhPMql|wu9xrUK~41giGXhr}WTjSXkO}IV!Z7pVY zi}_50%6Rz!F`O+^T+Da)2B_CgC71k=!vVOlvr;4kebIha(>V~exgs-pu3~Od&vtfM z&u*jnNy1!lqWD5nZ43LbEaXe*txFf=Exqt&cRK;Hsl!p>wCR^B&ys|GZrJ<_C6$XFNNYVf{lfciPkq zz}f_cW9a3AwUyZ!*JbMwb>rGGLCG=nQtx9fu8JBZfq&SV9f@cm8O$jJOrha@V-EVu z8(cjvO%cUQv^hC4sX)fw>c^QO$?DZZBKvpo9dKAN0?rs~{^4#u424fiWfl}B-{LyQD)A2X?jGa>I0NQRFz`7wV!%;!E+*ZN-`7u_zJ!$I;wnss$YT6(zJkzAoBXSB zuBR5lRn?t9wG~N}g$k#4bYdw!1(&Y3_|2^ii1FBw-wq;X5no!Y{S&W--M{O)z=?Jd zMsO8ZslLyUj~j0AQHDtSbUXB+#$9@n1vES3OC`nBDEP9o1y%JT)F%tCQK_Mk=vm+^ z7edI*UuY=`r>wRQbgMTE6Q#WE$8L%=B3xRY%5~5Z4}{Yng-l2cDo-!_8+(Oq?kzUy zd7_h#TG|%+euA~pma2e?KBr(5g%MH3k?;06I)8`_E94n=g{k>G@xaN5e_r@x2&|u( z%YW!$H*V5{YRux1Tt_|j&*zEyM{sz_3(2NVs7c$A8y>Ig>J1x&F~z&x>5O-DKz)w! zPHlUtSb)$S=Fj4*!%EmSxZy@NhA7GC5krb0#U`}uWHkS@J#;TbeZh=V_%dRNowWN6 z{3&87e27PA#h*($mvWkp9Hf96S*41c75N2D>Xlegyf1g371r0xzRT<%d04(FRi+b5 z8T|+|j~(9SHi8#X+LRsklPdFm_(3bXO4+L?Zn z)hZ^KA0a8L;l8W>`fLT0tbv)H-yK~M*c5C~R95BD=tWtrkXb%@e}K|gHDR<6G_1Lg0!neI5_uNoRPQFtO6HMm95%B3cee1!$X%_3p=BqNia+#N8 z*ZsZnuFu<6xYG9SB5R<}El#|wZPSqN&}U#(-i9y8UnP`ysM8K31zHRArOmvenbfnY zXh#fup+JkyXBQ0LMv98RiXkieM|YM4?42L5ZfhDs znnXO|o!La4Gl4kwAwY)ZuA-%rQ8xPaDKM+%77ah^w}DT1wxOfuuOdwQBGiNIWdE|2 zv?D^|(;$?d{W!c^tFhliZuZ^qdV6ip%=AF#ZS!=~{?0%C{o~x4fSfb3--x}Ch0nVG z4jI&H++R^ns3Jzjq`l=-4A|S@1N!$b2QMXe3hlJ~g3NTcd}+YOVl*sga?_HmekXAO zU6gf*T7{}u<&}fms?zjdC7{K2F%W4h!t|*Aovkq->mnK@~YR8+_J{$-3T&Lsii`cPY?9M?8zKZi84h zkzeGdyWCe>c2 z-MuTVFK2+BuFx3ZVVd^n-l%yV+AHWw&WEOR8=wyVGAQO1jO5SGf4ME6D<%7Ab^TKa z1dlEjfk4n)8vJ}KhEG&Co~+TT!r>E?lcrKuKPIyH#7M2vkr3dV3pbkgm~W1IennNS zJ(1lze$~wW2FJn~(x4yM_p#$0+z^hI2|BCLKb0OCqXaM>U(_!kr0Q*jEKt}xc^(CQ z@AA5&^`YIlw&^1(Pv{D@y1A89W0ZtEK9x+1%cDmfrXmQ?159huN&=)y8`uAEGccYq z;c_AE7CElRUR;F?_)uGNKh@nCL*y};p=~VsgxZd5cnIUXiv|K>a)yCpYb27l5vfORcRYzG8#zJ8 zpx1Y~?_i-;jfL_8K_qZ!Vp48EDnZ>_s$F21hg7{WqRm|7nj@2ei~{%bKjm-lAJkNs zGLtDgNa4NwDxz#72)Dt12OoXCn_G{aO~8_CDhStH(EuCn2xc0HvSO-@^Cb4G$>@|d zaXLnbIpV`tPdiTc`}nlYhx6{LizLp>rYDc)34&81H=JT#WRIb|79xD;8eumW!=mg{ zG1w!W0nM%mP({^UxayFc7G`cmde#88%w(ePV&p{vC$1N->z2DYFG7kf6NDZn-jN8S ztWJcL(4h{(F;a$f#5`bGUxQUNkMg0sSzeRw#d2Hty5zzNcy7LdIXBIGR+f(K@fTY* z;c;lf-j|-Tos&%g3$9PmYj0>%Y?lRADSygvBI;8=BC{`MAEMcGE8f>;CII$Nq6ig+ z0_Kws(!tx558h66o21TpC+<0* zPxKyN2wRGNc-ulenLYb9z?E%Dnk%rht;({c59Z?NhnvDT7d!ffNSj_=G&!Z5toly+ z8j>||K2ftTU?1aY=Tt+3AQa&dm248rX4}TO z=gcN0pmLC!b7zFP`P&80;(+9x+p1}@BI`9ilGMv4k4vx5kV*+bmxdnVkiC^sDG?p+ zEd7^P-{8k`oD@Gu<^=wwW0a!7(z0cQ!S<2f!wl2MjG=Fb^X2Mjqx&WmoX5NcN{+he zSESX~*)wQY{rn#BkLBv+Ae+wQdvgPq3Z{ypI)oFvr#X6tshL-E(0nm}&J9VD@bFW9mxe(g%74P`Rt zKB+nQ1`ho!#^Jf=E_7(oV>|n9Jwqt0iPR=4etUKPP@_EvFfIZ5@sfQTp@QO{)#od2 zzsF!HlMh)CNOKPU0bje(^A0ATYInoXS-4YOI%Xh_px5Hm;V`2qc2v&_ zU3D*s%ep&Bk>Rkq`LMCZ^aG=5jtCHGv~&smYB1nRZcp7S#fWxv_9%8PEgxwlp;z0v zn{vhTTqo?9FAR-m66bbk^BT9EmDLCLRWZmY#UFPI>wkNS%zm1(I z*qD%_d~0DPl=vgW^b4q>NN>wR6L07KN+jb`BF* zHV(9it{rqbpTfCj!YJq9n@OH|zgB10m+$^$tV!KdtS}^JAe7T-`O1u!qST5B4WFzP z{mkq0U8LHWI>-u9&&v-Gj9YkfwJn6G90dbr7(OTbxV3c+POArh<@qSb(g~}u31bJy z8pHHrFa0Sg*@M$kYnE1E!*X#YX{MAmL|N=GXZBP6Q!(ISGb8yaDiI<8rlV9wgZAkn zV<~5Zij}4tC3rP*WL|c}Olv-QvJ}M|*vR@=U7u~%a$63Oh zDlzZ9_Li(NCbHr#*BiCrRPc;aj4zxQqP?@~_mJ)VPtifD5s&AMFp4S@`nfK%Q!f2e zaGXEZfYPsEDd15`b}k-!7@hA>Z>Jfb@7XXgr=eoB((f#6^HV&HYED$vvUH@Xm9~Oz zTMCjvg4B&*H&toB&vatzJXy5OA-I-3tIBh=t%X>Z!!yd(a2&GDWqpmD-grCnr>Gv) zni#GUu_e^nHZ}rwAcP{tdiMFoB^5AiA?~sz+yr^gTgLOa*4!>=Z46%-;c_ly#)M#V zcGf6_(e-bi)56*QJSRBL4W?)bkFCLZIhOb79hgrX;@^O6W*-t;g<0C}cOQb7{2}#I zn35{S|3#ut)wXyFuW?8kt!z4*&oyvjt=x^a#Nt%bCVbDh8oz!AqG}1RKnjlq&vKG>gze2~bGe?&D(LBQ)}8ajYJ)^T=uZJ} zsxwvj^z|)`-0d-JDeONBPY<h~Atq5P zrqNgt=+L+@|9{f32<<=larEnK%U7bgO3rs7bZdKy+vvn-AbS{GEjC2rvr8E`X~F+G zAXuZ^)|6Q_=4n9v^*+t4fK;Q4!0=IyLq|b;ZJ){@KN~%vI_E-@Z-A}{j-{OYV439V zl(Tt0EUne0mR?9Cpsw`PZdU-)rPr2(eT?vSUFu$+KXU(yTocEYDs(=)g2?eMd3O{t zN17k-^te0E9*Mx4X<%5THV(&wWCxs5=X(1#KY0b>$_lwX3kI%P!tvc+xi(9Wlt%is zX{1(es&{pHepq>4NeHyav@g<0E z-gH7Z_Tjm#uZT;=4?F5vqC6I<%+a#&b1BoDo(f7vPr#v@3J*dBCEU4zM+wFU;ys-I z41JFAdaf7QJX1RDnwa_?S_j05X&8~X@C_{IG0F^B3P zJoA~rk4$?wbc_#-sz?qtuZxt$(#_a+SDJ2^^?S#hx?43}hQEISqp}%mcRxSzzQQ6s z9yIcVqm~ZlMrS=P#F$o$=|9!@XU=&~NPM?mdbPE{p6S623tQ==T^O zH7{25*J@ID<7PynZ)AN_ZFg$mkydB?nH_QO(76xmKl)O-C!I0e>*TGu$MESIdfs6M=r5>rdT{z9S z<*(nykZb9^o-{yclO_8j-!aNn-i;m%Q){<7`7K3O@{pnX$>MOz3@~mM#y^>lM2~SP z)W~lcx3=*=#Ki!Ck0|q|a}$p2p6mzaMl|7*xm^k~I;UVL=y~b%5n#gWVfVnK`eM~O z$0rWgxZS{Q0y3YibAT-IJr{=0gM|KcJ^dk!S2k45{aK1TbNYcv?!AJ07d<5haqm`u z)ne_V@mznKn;alz**dWxO0tw@mC3ekr4i}C-GE8doN^(y{JGk4dmin~t3DS*>V?=% zmiTNhNuVRnNE)%CUz!4W1AxfL&mAU2xw~$Dda#**OJ#;utR9yP<3AZ_!0BJ14o_TR zdd<8^Dgz>~Kr(^vtySYgnMu|&{8~+7LiU+tAT`0yJ>tDq<+NvOd)u;kZg+93>SNWv zwjps~pZ=wvZvk`&Y4JVzARw6v*t=!E(JWfpO+GaAY;{K#9!jjJ0@m3O@3*n)7al<2W8AA+ z&WZv(0SFA}0fRR-Px)DTHKNmVY@Z|7C60+*4=Ju{pz{dKtd+FtYO~Z$QRF+U?l_<2 z02KnHn9y=N5j*={jRharL}V^WE$fhtf=FLf_dW^F$H$%5dml@AM<86B~D(GabM&Tgma(& zGk`1n*^IInnmw|+0?)VpQyBSI(soU;)A6B6$xC2^{pJ_{3lZyswB9qI<$W}M{MZJ= zzbW%x@|sCn$~T912%uB`$x|+mBj~>t`mpyq4gD8#G|A3cpjD59?z`OhFAQvpSV;_? zx4fw3CHig0H=p`n7)9V=Ppq=lFBX0|_FtD6pxq$({DM@C$Uo}mZCY|ZAI6%AE>jcOQiX(w@A3=XQ4{GA5;sI&Q z2FVJtP@Hbgwnx*j?$@6KsWS{Dn;eWU=|6`xkj5;xG7r0)z4U&X5eT)2+4K`cYLuG< zoS=cuBZ>`6T*6d=zkt{o5wSx^mYS&a>eeiY<2?uGj;T0>c`}RCy5#K6Ziy^zwOiSA zSba8|zeDc2Ej$x$tH|Wf3@JpeF`8hcyk`L#@%>s}6u-b8wRvygIIf^?dnigPH?vIx zP`LJCrZh8+@3^TcE`Gu{6ulN1)1_Pz`f|L&p?PS$B|dQxYIVe<94s|DxxT;r)&>Xh z%FE<1Nd*;yWkBw2K)%@!84^aTK%US9KN>-Jk*Y3l?zRBz(?ft{K%^5EL7QM;7$FsO zAFI+Q=6z$pVKyaAZUee3zwib9MXA|iDedG_lS-N|x7$X&nX*9SqJ||+fS`;Mmg@i{ zM{*sGsS-mxCP@kO%EK2miItS25|>byoaNCU_trFvzRayGGUG$k^xJ zL##D+JTg>XB&&VwOqCyUV6XTcR_TuLgb2fO(NlYj3x*Q~B!F}GV*$J3!Pu8j`))}S z2Zh|0xP_BY|B5Eq>ocf(v+Sfv)7;+abeQo&1*LLYs)3~T_ogWEqicSvyQ6d5S7fb% zd~(vnW0>NqYeFe%4o&%%F4KDZcU2YHu?tcY>1-#_`K@!PY7f?6SxE?X%v?Gw#=*vZ zb3@bc@Wuh@u>{P0oBN&pQTk&8Yxe=F>UvepcgNB1Z9VQ^q}W`C8s}i^idG*qgIX`o> z*&4p76qIk-gc=kCNVnYJmu%Uvg}<+9bz|sJSIDdq>Du&T@%`lm1Al_X4%>kRimg`_ zH%V=yhGqY-rkC?qUxPUyMzR*JE+ofR|5EPD|y*7t^r|{vBobr|vs8PKkR=zRm0LLL!tXwd2zp_Hb9$VV9gvT!9VRcwrd@9c%RT#tWH582>l^PU_VkFj?!=|y z(4_7GPaX{Y`w@DvwShS25!L1(_y*fbIy?NIgZdRi*NX!3~!|xI{l>ROg z2dsJ%{`2{V_)MUS(-bq~3H1h{;)FH_SMtx@ zPh2go#Nac9Ho)>u#4yhr-)C}jwlqH^Z)Osol9Q0x{Gs`E{-2$(orgdQ$KR(7@(%_% z3FURK+SX!e*11+=gz`>(=@EnMcI_EUXJWXV_+TdNL;gwza)6`eF_@AUwJn&Kg?;%+ zFJZd6IsG=-y!yRxwGc>_8h>F-NbcVXzYKas=5NJc`ks3RZG4`e&BE4dWJ(gRzhi({ z`-#^_mj=ledx{JK`N{IR2X?*di+1iuWhOeM!R(Enu7@86Og`YF;KPSNfO>)j)JbP@ zws7v@$jrDv@hk6Rto9o+OdB1En^kM~*9xO6=1_>znaT*xF9Zv>&v8NzE1bd>n1UpU z4pOzpVjvSqxAA$pe6_{!-Rh1-LW{u3*zLonpb0I8Ykx)6qT@Q9x~41TB5v!UOzj_2W6rn2w3p=Xop+-6)x^UKp(iDYkRDg6<`?c<{OQpISgLIDm=4 zTWX8CfH)U8kBMdOV2e*99kyw+nlEL71 zg_05^$Ozu+nC;%kbLgX5y zNy>s^2s%t1|2aB&{Zr|Pz9LCzYI8O0S^~QXAFOFY?Nu2q$HH5SB7>m-NJ?DSeRb%M z8UQt|WfUN{UHpavS=C=t%^6w#vkRF)+9_z5VkU7x*22U`V_G(?TtC^T>FP28bbV=XJDCK zTFYv#nZcD}*%7Xa<)-~9QWH z2j3Wf;Z9!@UIZnJEJ&fX~c5eZGHS-1SaLhD60EJ_v*&9 zZzreRY@?r@Oa$lkxMd1&JZu6kpHSOK&u62a z_yTmMBXLoOvPeKMm`1b_2L0?O_>5!F>g<+AuEIj}qLlX80ii}AL<#9YxG<0+44~F5M;-W3 zJ@1aX(bWrpfy@Lc@__5$jAC?MsH{~1kN&s7oD#V#Iu~g&N)S;Jl8Y+5$m!C2 z{19$1JR$PEh4Z?l`E!1jw}5_(q&7A8Ja(oHE!UZ(8;*1uj=C9Kz3g9&y)@QQ zGU70t-3>C_xGRs{|F)nWumfhPg(}Iw>nHC5TFaN^I$yPxD2#kaSrrRaNEZh6ecL;}7^`?@OVhsj(MrG|q=o)q9OOV*q^e8^^W|%`^v`Jx>L1K!%MSDN$}0 z14%8%N7AZ7z8J@nLVj1aK-QO)eb^Y*QSm0t&rRko{?H7RlwAh*02anwv_Qy-%E@`+ zY})}#P+-U86P!{izKs=fw{v%Px#_CP6}Q#1F(-Mn%f&P_z7mcr{kn~Sd&$B!&RgzE z+VI1~an!U9OSIgrN$ushIf?h!tLdJ0N#7{InbNEnz)jQ=v+(BtYZhzYh?v};Xe-%; z&y-Ii#}XbCWttCh|E_l4fhB>qOVi7rnDX-d`J1JfbLeX0W>s99s^x!I{drkVljMrw z=d~5dBU8at#j_Po80E9tC|GdtH$7Nz*n7%Ssr7l^)MTe`$mWj3QbOGNGCDP8&VFvZ zWVg-?=+IUwRY1SuQ=~(6GVAjXu$WMcXF)4ILk$Dub911e#FF7ib>E9MxR=&n72dz? z(-=4+Me5U3#msF4DfE7Y6}+gz^9#&)F{F00`lVYo>4NB(?7Y~nM!@q)xAz;*iSEq+ zEMQCm%@a@sFQwvfry9G5FU9}bu-5VNq{dh!coGcpZr^*G0v?Oqu9fB-8y=#Qf80me zar5tHyh9CTgXGUTIxm>@H+_%;p;!w%7EZ!3G9S+a^WdQ9Ln%{Cja!q!`*?#ZLL8Fr z`vJc2k&WqDU+M+d)<}Sm70B}^P?SsuWF-(l@;dT1x-?_KgrWp9-|;79J7|gNBbfS& zM*(*r^@_S7<*+`6kB!KN9L{JENwn>|2%#FAal4Q=7)d{e`*@c=D zHk(cNu)kN;qB%1P#PH!S@;y)FEc=_sD)lqo=;lr(2VBhPDLQRu!eZsjG~@NOf2#M> zc)4C_7O*^(S`yORt6n6(0t#8d-pV+}VY4VVHqB1LXz&oI-`ate*8P;-Y~;WO$c_eV zdpcIeaBY`4^my8)F|~c|vlxfi3}5ZQTzA-VsYq*?;$?pTcTpB)A2VBKvouqNNT(jaE8+4*KCO;d= z)Pa?b!9Zn+I4!{y1r@--KsyY*h zbsiK<*p{Ux5x<0a0$`{BSXTB>^^iVowvqO8-)zhfw>H2doebnZ0N@C)jU%C%51;UT z^+2x`BNKq#_5Zd<1r8V&rY}G-DnMQ^sxybXt6Zsn$s1Zkq8=4yFMQbdx;dig>l<8L zsOa-MT4a;nf6UP4&b?9>lFA`74@CV5azsBY z?|gD>zI!;3##L>4{RUWI1T9zi*)=2zu(wQLP2Cs6W?cLB8Mp>Ai7i50Y6u*pmLoty zR}m`KhobHoPvdp@ZCfpZ*r)0hrHG0%ljxpCr_S2p z5NkD%*fmi9jYU@p_MnKT@;OiCB|?1Z&ic{r5~;GZWJ>yklF&(AAa5S(sL<6{*%@uV zmd6XVfZUT7k8Fw$DDe*yvdkL7fP6<2icdS`B|2d1D3IS zfpmdVV)`kHcAPb~>^^`?`N~<(0zSF=kt>(rmD$cc-Y6{hRestf@>b#fG*Tj4g)z45 zz`1M-8U309TNBAbfqC5z;Z@eM!O9+guv;>aHYs;UK{w`cKFwXxN;<^^PVRR!Jh~4m zcaFwAqn|^zB%aTFd%j{??3ncV^BH_W{2Ow$10qGUXJ#^p$I72r-SKn1>C5!?s~$_S$ExxHkBWGUH^T8baSXjN+6@oN|9 z5-CX*Xi+=U!kB5jb)9JXiNh9w?QWsE1iI(b{!i#tcc=!@>UNrT5(3^|l*+p(^)`F! zx}Kz%n@OCyI1&A1uto`kAlDh%GrWhlZU*kzIaR%LDz}7=dYuLQKaKpK_oTlu7blH} zru!N@k^2RDN2yamG6;@;iwzEy4Z~cR>RWQI;_i|t*^y=}ggEbBdQ#d46p2{`b!{R+ zwx;$&7Bz|x`b0}gb{d#%5@IwiTeuRfq$Iz|;=d`##2kyI+P|r z#i5H*{YPg65op{8tC_6B?xf{qhGNubu;I9Cm1RjX*ayx(&RGDqaj(p-CsO|eP512V zFARHcWL*UkB@q6<_O3lD$+X*7CQf6o*4T^&spIr@nXIhLOPZR)jD9(#^;OuKWCja zXa0TJ`+4@WpMCl5-@Dd}TRHvtM)%&qKJlw4)Pst4YRi~QXXa)Jv#*oE2fTO zLD9U{gC2&cN8p4<%x-ByLvsiw0fvnGSaLcz^SfC+FkNQ#R+fIe*P! z1WXn)mzkM?3mWtc3(o3&V>0N`~1$ix)+3E7IdfSUbu`ydLEIu&gbTC*F zelzM_ZCs3!mum#JVW=y4*Hb!t<=&rN z>gh5p_`V_WvyOh>FPm&bBTWjOycCs$mh`o2>$!ii3z}eP90~XdAAHcMjq#y&qf^BG z&N4_j5@j>LiL>GBV!qjZbc1TLbhX3@{XqM~x&Rpp`?ruRs|=z2wN2I&>d~u%RV>j+ zA{&WPN$Z$#8Ph5xvN;k-xKTj<%3^xZb@wF8N;I@Wo*eT!RAZWJD`XyDaSz$Nb8Ci( zKrA_4RFI5eWqbpxikwuTc|?JJGW(7q zHLz5a6fMTt?6=3J8*+V^qBfXRz2Mk1#gv}(*?55gMS{N zA`U?kMT>kBm^-gA6~1+0ta5&|8Y|)q<969+XY_Ykr)3T0PKtKn)e~3kmp*q|36?Q? z&h*3`egH%1sGa(w>5QDnmHEQwGXQOX5Isfr2UZPMC7%F1qOg)!fWaE#^SA41<4#kD zK;J>v5GtHj9tS7@l-7AFZlq+Hm!!69ioYtlKoz!>HiWYpbPyr$5WU< zTwbygth(+elndJ8(}ljwY6Ys4bj^H)p>eu^`7e?|3XvoCTY9>mgIT(^s5D#HyUEah z!8E6HU8=YP&}y3Ugu6lZTSruZ%Yz+;HV(!z5}lm{K6G;QvI{iGwgiadcdox+XtZEP zK_d!a;KQZ>GHDMe%Pehi4ZqOzkH5ElQ&IPsD|=X1ujm9Kdhv7*ZC)Rvmv(W9dz@8!`dbK3QgW7q0aXlJ!XV~ z+pM3rT|{;CQptSlNaUy$)(j4=>+KKw^rJ2E@?r zPWL6$534z(YpUKAP(x^X+Z#fVXDr_1##V74`n|@1+p0)ua0C(FTbZ+&zQ#5F z4WWS-BR~64BxBP7LUez8PbCDf<0$ef+Q-IZ13)M$dPDl|DX13>X+qGxz0@ATNqnJ_ zq8p392^er(B#h3tcQ;85bdgo@oMv^vW)VVx)yh$ z+vyrejyqo2p1gG!s>I^O=4|gPtSH+*BE_<T9M!fY5h^ib z6rkY1N)*(qM!}BR@UoXbVWSRD!g-_G))!^FCNOUV4eJx|Ho^L?fqeM=ku9;yXyla> zUq;{PE{&W7dKRzBc}15pc^4%CZk6X<+hnalEd+{d2Cc~FWi(D_ z?npjR6_X(n0FBB3A(dq}e>%HP86YtDSVo-46$C*|LGbpAdH%{jAvss>H}gQ`1qP}+ zX2qpX%x4Fw3x0D!K-T~8&UPdGdW&9mqJJ`=6fmAGB7eB`C@ZqSF!}ttdyeE(%Y%`G zPmGxG4Wvr2iui{CI)MWcJmkQa#c$@RgeBYDsF?L>SN??a+UHv@FyOghQCBl1G+^zU zDeZ5Q|BrcD-1&T8aRn*;bHUZ0lYW+}FoaDt=UrrUGw!pBd4X;VvwkUnp$FV*yehbz zGR=oEd5EdsDG>k*C&+Y6e!J%EXE>OGqH82do_J(uSV<^%FR{T0cHH5%THqK)7{a~8 z1!mARlu`#i(ZmmHuNcZ5Z;Y5(43+F(*TMd|*VdC2lDcMaB_wbE(;8N1?OS6`^2Y#8 z==jZBo2v@|Y>r(T&5Tq%`g-yH@@kL6{YrG@KYR~Q@+sd9@!?I&hQlD_ z()({j?5dfE(`hIrRaXVdJR=&10C~jJX5(nmBH#kEvk^HH1VHu9>$cjc{#y|QDQ!NO zjDObq>pf)Hj^ztH1?RZSixLmuL_u67-OTBAm^~8ab(g_0A3dzHizWQ$MX)C_#z64= zs-9OM{=z3?d@$%DVoENKmoXBn>Ca;wzZ3vZyUH6H0|czjXPCN^N>Q~muD&;jH~YyL?8j9#Z~O@-GG+i81?DHr8LA};Q@AYj}O z%ylnhl-S4gW%;MF?IOrSmeU;l%-N}3GkJ)aX=%QcapO|gr2ek0ekxszW)a(qFz}E~ zw_zShA&1-;n>h-X+c57rHAiV}lpYWbN>s9;T)h@sVfomcqtUoe^|Klhai7_-q0L-;iU^PExc}xu_MfRlb*_w!;!djhMQ} zM@mUE3{Ha25~rD0jGitL{-k`Wi4i7(07w5UmUsg+Razcuc6Yn4X6rJg%jJY~6`PA_v5&r2p||e0B@Y z8nfv2%)SSN=;^4Z#uDS&MyJS$XHB?wtZK{~qxFo&xDAyZ{7tjZik0MIr~IVXq2;Z~rSk&Xhu+J84VehL>2XR#xHc-hCVK>AhBO=aahqI7lz6dG$^ zF<6v#xLwmj$&%z9d( zi0Iy{mXxI1{|2bny>_Ouz#s^WR$tCRHg_>&R|5HJdxvW6AJU93sNnZ15j!&2=wpRG zpI=UL&y*kb0AY1q&Qe7(kniWS@L7I$JMTD1BPD9Zl007Yu_m7ZkYRf#_kyZZ)AGGb zu2+A(9GvV>lRnr=h7k%Vj74vj|M@qBPFvm)Vq}q0-a&?KF0<4L1MY1yNikAA?@-M< z{zj3@t4&{F~`jFS}t&R#; zFcN#eVY0hbQ6c!ao^Pvl$WGT2>fSvo+Z^OIEURyC9v9#5{yD7fNvx}l_IQKVS=X3%hHHJ5!Z@Z40I|f@ zPiC!B?>yMT>2d~Co*VPrK3uN(}3`02PhN22ZN^z2tD6-liAli<0JRzw{+s4`C!*BGRD}z~$Hg{NxtGp*3xHM8 ztYEO!=E|)je1uOh1a<~2c)(%{pEb~ zy5#spiS5m|`}Zf=oNESOiLlA?eX3ZL?Aur+L#h}rI;Y#KV>N4IDr54U!8Z%26}lq= zN+;8&^2hWExgP_vES&%pA`*l!_lJItuIh|n1p1~u63%H@s#Q!@DIW8WHjoe1xdqm; zP4~Rk{L{85Gh0ZkCkH1g&2C=ZV<9d(=iQf^tad~bv~l6xXT+i~p-3N|R8W@59WD5j zzIj~on9#MnDETEb5=6Z%OG#;BOJ~Q4*xhj(zF_A71CBn?W&HLHA6eKHEe!UUNxih) zh=uEpd)?*GF;5)&d*9OdZ)g*5`|9Ch5{L=jLSn=CsR~Uv@_^9Q@we<>-49cWWcO*o z9q9}uxX+1mo71wvW>mpBr+9E4;u;QvaJdB(Zoo#h$+Ak=rGtSdj*+3Z=mweYtqu%%rrn zo1^g@&(Wt%;_70NaL<={(wdr8VgCE61x4Mb!IaZ&OFY(igmsC(%65#W*Nr}BvPGFhJiNr<8=-seRuwj(|XOG z4AqMcG;FjrA594OkG<@&y@x^Tc#|BpQXV1=O8Z7DPF@8&@F-dTxZoC@dwk{ zg#X8vq(R&U^&_}$dYmjx8()q*JEI&tScmsd(^w&$Wbx*hQkQERz^Y?^1{Jsk{EC8X zkLC2^$Np&Bv9ovjJ$kK!cqt7|*RLyb5nH`P4xAG>Iy_@k89|zPDSu!w{aA3!D5#%% Z^P}&aV%p>p;IZdy|0eKj`d86e{{=O!9*h70 literal 0 HcmV?d00001 diff --git a/barcode/src/test/resources/org/xbib/graphics/barcode/maxicode/mode-6-max-data.properties b/barcode/src/test/resources/org/xbib/graphics/barcode/maxicode/mode-6-max-data.properties new file mode 100755 index 0000000..ead3ae3 --- /dev/null +++ b/barcode/src/test/resources/org/xbib/graphics/barcode/maxicode/mode-6-max-data.properties @@ -0,0 +1,2 @@ +mode=6 +content=///////////////////////////////////////////////////////////////////////////////////////////// diff --git a/barcode/src/test/resources/org/xbib/graphics/barcode/maxicode/mode-6-too-much-data.error b/barcode/src/test/resources/org/xbib/graphics/barcode/maxicode/mode-6-too-much-data.error new file mode 100755 index 0000000..74ec7c8 --- /dev/null +++ b/barcode/src/test/resources/org/xbib/graphics/barcode/maxicode/mode-6-too-much-data.error @@ -0,0 +1 @@ +Input data too long diff --git a/barcode/src/test/resources/org/xbib/graphics/barcode/maxicode/mode-6-too-much-data.properties b/barcode/src/test/resources/org/xbib/graphics/barcode/maxicode/mode-6-too-much-data.properties new file mode 100755 index 0000000..0dae1b1 --- /dev/null +++ b/barcode/src/test/resources/org/xbib/graphics/barcode/maxicode/mode-6-too-much-data.properties @@ -0,0 +1,2 @@ +mode=6 +content=////////////////////////////////////////////////////////////////////////////////////////////// diff --git a/barcode/src/test/resources/org/xbib/graphics/barcode/maxicode/mode-7.error b/barcode/src/test/resources/org/xbib/graphics/barcode/maxicode/mode-7.error new file mode 100755 index 0000000..8e1e16d --- /dev/null +++ b/barcode/src/test/resources/org/xbib/graphics/barcode/maxicode/mode-7.error @@ -0,0 +1 @@ +Invalid MaxiCode mode: 7 diff --git a/barcode/src/test/resources/org/xbib/graphics/barcode/maxicode/mode-7.properties b/barcode/src/test/resources/org/xbib/graphics/barcode/maxicode/mode-7.properties new file mode 100755 index 0000000..0b770e5 --- /dev/null +++ b/barcode/src/test/resources/org/xbib/graphics/barcode/maxicode/mode-7.properties @@ -0,0 +1 @@ +mode=7 diff --git a/barcode/src/test/resources/org/xbib/graphics/barcode/output/code93-basic.eps b/barcode/src/test/resources/org/xbib/graphics/barcode/output/code93-basic.eps new file mode 100755 index 0000000..62bea42 --- /dev/null +++ b/barcode/src/test/resources/org/xbib/graphics/barcode/output/code93-basic.eps @@ -0,0 +1,71 @@ +%!PS-Adobe-3.0 EPSF-3.0 +%%Creator: OkapiBarcode +%%Title: 123456789 +%%Pages: 0 +%%BoundingBox: 0 0 128 60 +%%EndComments +/TL { setlinewidth moveto lineto stroke } bind def +/TC { moveto 0 360 arc 360 0 arcn fill } bind def +/TH { 0 setlinewidth moveto lineto lineto lineto lineto lineto closepath fill } bind def +/TB { 2 copy } bind def +/TR { newpath 4 1 roll exch moveto 1 index 0 rlineto 0 exch rlineto neg 0 rlineto closepath fill } bind def +/TE { pop pop } bind def +newpath +0.00 0.00 0.00 setrgbcolor +1.00 1.00 1.00 setrgbcolor +60.00 0.00 TB 0.00 128.00 TR +TE +0.00 0.00 0.00 setrgbcolor +40.00 15.00 TB 5.00 1.00 TR +TB 7.00 1.00 TR +TB 9.00 4.00 TR +TB 14.00 1.00 TR +TB 16.00 1.00 TR +TB 19.00 1.00 TR +TB 23.00 1.00 TR +TB 25.00 1.00 TR +TB 29.00 1.00 TR +TB 32.00 1.00 TR +TB 34.00 1.00 TR +TB 39.00 1.00 TR +TB 41.00 1.00 TR +TB 44.00 1.00 TR +TB 46.00 1.00 TR +TB 50.00 1.00 TR +TB 53.00 1.00 TR +TB 56.00 1.00 TR +TB 59.00 1.00 TR +TB 62.00 1.00 TR +TB 66.00 1.00 TR +TB 68.00 1.00 TR +TB 70.00 1.00 TR +TB 72.00 1.00 TR +TB 77.00 1.00 TR +TB 81.00 1.00 TR +TB 84.00 1.00 TR +TB 86.00 1.00 TR +TB 91.00 1.00 TR +TB 93.00 1.00 TR +TB 95.00 1.00 TR +TB 98.00 1.00 TR +TB 100.00 2.00 TR +TB 104.00 1.00 TR +TB 107.00 2.00 TR +TB 111.00 1.00 TR +TB 113.00 1.00 TR +TB 115.00 1.00 TR +TB 117.00 4.00 TR +TB 122.00 1.00 TR +TE +0.00 0.00 0.00 setrgbcolor +matrix currentmatrix +/Helvetica findfont +8.00 scalefont setfont + 0 0 moveto 64.00 7.00 translate 0.00 rotate 0 0 moveto + (123456789Od) stringwidth +pop +-2 div 0 rmoveto + (123456789Od) show +setmatrix + +showpage diff --git a/barcode/src/test/resources/org/xbib/graphics/barcode/output/code93-basic.svg b/barcode/src/test/resources/org/xbib/graphics/barcode/output/code93-basic.svg new file mode 100755 index 0000000..9d52bd0 --- /dev/null +++ b/barcode/src/test/resources/org/xbib/graphics/barcode/output/code93-basic.svg @@ -0,0 +1,53 @@ + + + + 123456789 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 123456789Od + + + diff --git a/barcode/src/test/resources/org/xbib/graphics/barcode/output/code93-colors.eps b/barcode/src/test/resources/org/xbib/graphics/barcode/output/code93-colors.eps new file mode 100755 index 0000000..1853e2f --- /dev/null +++ b/barcode/src/test/resources/org/xbib/graphics/barcode/output/code93-colors.eps @@ -0,0 +1,71 @@ +%!PS-Adobe-3.0 EPSF-3.0 +%%Creator: OkapiBarcode +%%Title: 123456789 +%%Pages: 0 +%%BoundingBox: 0 0 128 60 +%%EndComments +/TL { setlinewidth moveto lineto stroke } bind def +/TC { moveto 0 360 arc 360 0 arcn fill } bind def +/TH { 0 setlinewidth moveto lineto lineto lineto lineto lineto closepath fill } bind def +/TB { 2 copy } bind def +/TR { newpath 4 1 roll exch moveto 1 index 0 rlineto 0 exch rlineto neg 0 rlineto closepath fill } bind def +/TE { pop pop } bind def +newpath +1.00 0.00 0.00 setrgbcolor +0.00 1.00 0.00 setrgbcolor +60.00 0.00 TB 0.00 128.00 TR +TE +1.00 0.00 0.00 setrgbcolor +40.00 15.00 TB 5.00 1.00 TR +TB 7.00 1.00 TR +TB 9.00 4.00 TR +TB 14.00 1.00 TR +TB 16.00 1.00 TR +TB 19.00 1.00 TR +TB 23.00 1.00 TR +TB 25.00 1.00 TR +TB 29.00 1.00 TR +TB 32.00 1.00 TR +TB 34.00 1.00 TR +TB 39.00 1.00 TR +TB 41.00 1.00 TR +TB 44.00 1.00 TR +TB 46.00 1.00 TR +TB 50.00 1.00 TR +TB 53.00 1.00 TR +TB 56.00 1.00 TR +TB 59.00 1.00 TR +TB 62.00 1.00 TR +TB 66.00 1.00 TR +TB 68.00 1.00 TR +TB 70.00 1.00 TR +TB 72.00 1.00 TR +TB 77.00 1.00 TR +TB 81.00 1.00 TR +TB 84.00 1.00 TR +TB 86.00 1.00 TR +TB 91.00 1.00 TR +TB 93.00 1.00 TR +TB 95.00 1.00 TR +TB 98.00 1.00 TR +TB 100.00 2.00 TR +TB 104.00 1.00 TR +TB 107.00 2.00 TR +TB 111.00 1.00 TR +TB 113.00 1.00 TR +TB 115.00 1.00 TR +TB 117.00 4.00 TR +TB 122.00 1.00 TR +TE +1.00 0.00 0.00 setrgbcolor +matrix currentmatrix +/Helvetica findfont +8.00 scalefont setfont + 0 0 moveto 64.00 7.00 translate 0.00 rotate 0 0 moveto + (123456789Od) stringwidth +pop +-2 div 0 rmoveto + (123456789Od) show +setmatrix + +showpage diff --git a/barcode/src/test/resources/org/xbib/graphics/barcode/output/code93-colors.svg b/barcode/src/test/resources/org/xbib/graphics/barcode/output/code93-colors.svg new file mode 100755 index 0000000..cab3f5b --- /dev/null +++ b/barcode/src/test/resources/org/xbib/graphics/barcode/output/code93-colors.svg @@ -0,0 +1,53 @@ + + + + 123456789 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 123456789Od + + + diff --git a/barcode/src/test/resources/org/xbib/graphics/barcode/output/code93-custom-font.eps b/barcode/src/test/resources/org/xbib/graphics/barcode/output/code93-custom-font.eps new file mode 100755 index 0000000..18ba809 --- /dev/null +++ b/barcode/src/test/resources/org/xbib/graphics/barcode/output/code93-custom-font.eps @@ -0,0 +1,71 @@ +%!PS-Adobe-3.0 EPSF-3.0 +%%Creator: OkapiBarcode +%%Title: 123456789 +%%Pages: 0 +%%BoundingBox: 0 0 128 82 +%%EndComments +/TL { setlinewidth moveto lineto stroke } bind def +/TC { moveto 0 360 arc 360 0 arcn fill } bind def +/TH { 0 setlinewidth moveto lineto lineto lineto lineto lineto closepath fill } bind def +/TB { 2 copy } bind def +/TR { newpath 4 1 roll exch moveto 1 index 0 rlineto 0 exch rlineto neg 0 rlineto closepath fill } bind def +/TE { pop pop } bind def +newpath +0.00 0.00 0.00 setrgbcolor +1.00 1.00 1.00 setrgbcolor +82.00 0.00 TB 0.00 128.00 TR +TE +0.00 0.00 0.00 setrgbcolor +40.00 37.00 TB 5.00 1.00 TR +TB 7.00 1.00 TR +TB 9.00 4.00 TR +TB 14.00 1.00 TR +TB 16.00 1.00 TR +TB 19.00 1.00 TR +TB 23.00 1.00 TR +TB 25.00 1.00 TR +TB 29.00 1.00 TR +TB 32.00 1.00 TR +TB 34.00 1.00 TR +TB 39.00 1.00 TR +TB 41.00 1.00 TR +TB 44.00 1.00 TR +TB 46.00 1.00 TR +TB 50.00 1.00 TR +TB 53.00 1.00 TR +TB 56.00 1.00 TR +TB 59.00 1.00 TR +TB 62.00 1.00 TR +TB 66.00 1.00 TR +TB 68.00 1.00 TR +TB 70.00 1.00 TR +TB 72.00 1.00 TR +TB 77.00 1.00 TR +TB 81.00 1.00 TR +TB 84.00 1.00 TR +TB 86.00 1.00 TR +TB 91.00 1.00 TR +TB 93.00 1.00 TR +TB 95.00 1.00 TR +TB 98.00 1.00 TR +TB 100.00 2.00 TR +TB 104.00 1.00 TR +TB 107.00 2.00 TR +TB 111.00 1.00 TR +TB 113.00 1.00 TR +TB 115.00 1.00 TR +TB 117.00 4.00 TR +TB 122.00 1.00 TR +TE +0.00 0.00 0.00 setrgbcolor +matrix currentmatrix +/Arial findfont +26.00 scalefont setfont + 0 0 moveto 64.00 11.00 translate 0.00 rotate 0 0 moveto + (123456789Od) stringwidth +pop +-2 div 0 rmoveto + (123456789Od) show +setmatrix + +showpage diff --git a/barcode/src/test/resources/org/xbib/graphics/barcode/output/code93-custom-font.svg b/barcode/src/test/resources/org/xbib/graphics/barcode/output/code93-custom-font.svg new file mode 100755 index 0000000..5db0be1 --- /dev/null +++ b/barcode/src/test/resources/org/xbib/graphics/barcode/output/code93-custom-font.svg @@ -0,0 +1,53 @@ + + + + 123456789 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 123456789Od + + + diff --git a/barcode/src/test/resources/org/xbib/graphics/barcode/output/code93-magnification-2.eps b/barcode/src/test/resources/org/xbib/graphics/barcode/output/code93-magnification-2.eps new file mode 100755 index 0000000..1d8d66e --- /dev/null +++ b/barcode/src/test/resources/org/xbib/graphics/barcode/output/code93-magnification-2.eps @@ -0,0 +1,71 @@ +%!PS-Adobe-3.0 EPSF-3.0 +%%Creator: OkapiBarcode +%%Title: 123456789 +%%Pages: 0 +%%BoundingBox: 0 0 256 120 +%%EndComments +/TL { setlinewidth moveto lineto stroke } bind def +/TC { moveto 0 360 arc 360 0 arcn fill } bind def +/TH { 0 setlinewidth moveto lineto lineto lineto lineto lineto closepath fill } bind def +/TB { 2 copy } bind def +/TR { newpath 4 1 roll exch moveto 1 index 0 rlineto 0 exch rlineto neg 0 rlineto closepath fill } bind def +/TE { pop pop } bind def +newpath +0.00 0.00 0.00 setrgbcolor +1.00 1.00 1.00 setrgbcolor +120.00 0.00 TB 0.00 256.00 TR +TE +0.00 0.00 0.00 setrgbcolor +80.00 30.00 TB 10.00 2.00 TR +TB 14.00 2.00 TR +TB 18.00 8.00 TR +TB 28.00 2.00 TR +TB 32.00 2.00 TR +TB 38.00 2.00 TR +TB 46.00 2.00 TR +TB 50.00 2.00 TR +TB 58.00 2.00 TR +TB 64.00 2.00 TR +TB 68.00 2.00 TR +TB 78.00 2.00 TR +TB 82.00 2.00 TR +TB 88.00 2.00 TR +TB 92.00 2.00 TR +TB 100.00 2.00 TR +TB 106.00 2.00 TR +TB 112.00 2.00 TR +TB 118.00 2.00 TR +TB 124.00 2.00 TR +TB 132.00 2.00 TR +TB 136.00 2.00 TR +TB 140.00 2.00 TR +TB 144.00 2.00 TR +TB 154.00 2.00 TR +TB 162.00 2.00 TR +TB 168.00 2.00 TR +TB 172.00 2.00 TR +TB 182.00 2.00 TR +TB 186.00 2.00 TR +TB 190.00 2.00 TR +TB 196.00 2.00 TR +TB 200.00 4.00 TR +TB 208.00 2.00 TR +TB 214.00 4.00 TR +TB 222.00 2.00 TR +TB 226.00 2.00 TR +TB 230.00 2.00 TR +TB 234.00 8.00 TR +TB 244.00 2.00 TR +TE +0.00 0.00 0.00 setrgbcolor +matrix currentmatrix +/Helvetica findfont +16.00 scalefont setfont + 0 0 moveto 128.00 14.00 translate 0.00 rotate 0 0 moveto + (123456789Od) stringwidth +pop +-2 div 0 rmoveto + (123456789Od) show +setmatrix + +showpage diff --git a/barcode/src/test/resources/org/xbib/graphics/barcode/output/code93-magnification-2.svg b/barcode/src/test/resources/org/xbib/graphics/barcode/output/code93-magnification-2.svg new file mode 100755 index 0000000..34baea3 --- /dev/null +++ b/barcode/src/test/resources/org/xbib/graphics/barcode/output/code93-magnification-2.svg @@ -0,0 +1,53 @@ + + + + 123456789 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 123456789Od + + + diff --git a/barcode/src/test/resources/org/xbib/graphics/barcode/output/code93-margin-size-20.eps b/barcode/src/test/resources/org/xbib/graphics/barcode/output/code93-margin-size-20.eps new file mode 100755 index 0000000..2dfa31b --- /dev/null +++ b/barcode/src/test/resources/org/xbib/graphics/barcode/output/code93-margin-size-20.eps @@ -0,0 +1,71 @@ +%!PS-Adobe-3.0 EPSF-3.0 +%%Creator: OkapiBarcode +%%Title: 123456789 +%%Pages: 0 +%%BoundingBox: 0 0 158 90 +%%EndComments +/TL { setlinewidth moveto lineto stroke } bind def +/TC { moveto 0 360 arc 360 0 arcn fill } bind def +/TH { 0 setlinewidth moveto lineto lineto lineto lineto lineto closepath fill } bind def +/TB { 2 copy } bind def +/TR { newpath 4 1 roll exch moveto 1 index 0 rlineto 0 exch rlineto neg 0 rlineto closepath fill } bind def +/TE { pop pop } bind def +newpath +0.00 0.00 0.00 setrgbcolor +1.00 1.00 1.00 setrgbcolor +90.00 0.00 TB 0.00 158.00 TR +TE +0.00 0.00 0.00 setrgbcolor +40.00 30.00 TB 20.00 1.00 TR +TB 22.00 1.00 TR +TB 24.00 4.00 TR +TB 29.00 1.00 TR +TB 31.00 1.00 TR +TB 34.00 1.00 TR +TB 38.00 1.00 TR +TB 40.00 1.00 TR +TB 44.00 1.00 TR +TB 47.00 1.00 TR +TB 49.00 1.00 TR +TB 54.00 1.00 TR +TB 56.00 1.00 TR +TB 59.00 1.00 TR +TB 61.00 1.00 TR +TB 65.00 1.00 TR +TB 68.00 1.00 TR +TB 71.00 1.00 TR +TB 74.00 1.00 TR +TB 77.00 1.00 TR +TB 81.00 1.00 TR +TB 83.00 1.00 TR +TB 85.00 1.00 TR +TB 87.00 1.00 TR +TB 92.00 1.00 TR +TB 96.00 1.00 TR +TB 99.00 1.00 TR +TB 101.00 1.00 TR +TB 106.00 1.00 TR +TB 108.00 1.00 TR +TB 110.00 1.00 TR +TB 113.00 1.00 TR +TB 115.00 2.00 TR +TB 119.00 1.00 TR +TB 122.00 2.00 TR +TB 126.00 1.00 TR +TB 128.00 1.00 TR +TB 130.00 1.00 TR +TB 132.00 4.00 TR +TB 137.00 1.00 TR +TE +0.00 0.00 0.00 setrgbcolor +matrix currentmatrix +/Helvetica findfont +8.00 scalefont setfont + 0 0 moveto 79.00 22.00 translate 0.00 rotate 0 0 moveto + (123456789Od) stringwidth +pop +-2 div 0 rmoveto + (123456789Od) show +setmatrix + +showpage diff --git a/barcode/src/test/resources/org/xbib/graphics/barcode/output/code93-margin-size-20.svg b/barcode/src/test/resources/org/xbib/graphics/barcode/output/code93-margin-size-20.svg new file mode 100755 index 0000000..f27da66 --- /dev/null +++ b/barcode/src/test/resources/org/xbib/graphics/barcode/output/code93-margin-size-20.svg @@ -0,0 +1,53 @@ + + + + 123456789 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 123456789Od + + + diff --git a/barcode/src/test/resources/org/xbib/graphics/barcode/output/maxicode-basic.eps b/barcode/src/test/resources/org/xbib/graphics/barcode/output/maxicode-basic.eps new file mode 100755 index 0000000..c256da2 --- /dev/null +++ b/barcode/src/test/resources/org/xbib/graphics/barcode/output/maxicode-basic.eps @@ -0,0 +1,393 @@ +%!PS-Adobe-3.0 EPSF-3.0 +%%Creator: OkapiBarcode +%%Title: 123456789 +%%Pages: 0 +%%BoundingBox: 0 0 420 410 +%%EndComments +/TL { setlinewidth moveto lineto stroke } bind def +/TC { moveto 0 360 arc 360 0 arcn fill } bind def +/TH { 0 setlinewidth moveto lineto lineto lineto lineto lineto closepath fill } bind def +/TB { 2 copy } bind def +/TR { newpath 4 1 roll exch moveto 1 index 0 rlineto 0 exch rlineto neg 0 rlineto closepath fill } bind def +/TE { pop pop } bind def +newpath +0.00 0.00 0.00 setrgbcolor +1.00 1.00 1.00 setrgbcolor +410.00 0.00 TB 0.00 420.00 TR +TE +0.00 0.00 0.00 setrgbcolor +0.00 0.00 0.00 setrgbcolor +60.76 349.40 10.85 60.76 349.40 8.97 69.73 349.40 TC +60.76 349.40 7.10 60.76 349.40 5.22 65.98 349.40 TC +60.76 349.40 3.31 60.76 349.40 1.43 62.19 349.40 TC +28.69 382.32 29.77 382.95 29.77 384.20 28.69 384.82 27.62 384.20 27.62 382.95 TH +33.61 382.32 34.69 382.95 34.69 384.20 33.61 384.82 32.54 384.20 32.54 382.95 TH +38.53 382.32 39.61 382.95 39.61 384.20 38.53 384.82 37.46 384.20 37.46 382.95 TH +43.45 382.32 44.53 382.95 44.53 384.20 43.45 384.82 42.38 384.20 42.38 382.95 TH +48.37 382.32 49.45 382.95 49.45 384.20 48.37 384.82 47.30 384.20 47.30 382.95 TH +53.29 382.32 54.36 382.95 54.36 384.20 53.29 384.82 52.22 384.20 52.22 382.95 TH +58.21 382.32 59.29 382.95 59.29 384.20 58.21 384.82 57.14 384.20 57.14 382.95 TH +63.13 382.32 64.21 382.95 64.21 384.20 63.13 384.82 62.05 384.20 62.05 382.95 TH +68.05 382.32 69.13 382.95 69.13 384.20 68.05 384.82 66.98 384.20 66.98 382.95 TH +72.97 382.32 74.05 382.95 74.05 384.20 72.97 384.82 71.90 384.20 71.90 382.95 TH +77.89 382.32 78.97 382.95 78.97 384.20 77.89 384.82 76.82 384.20 76.82 382.95 TH +82.81 382.32 83.88 382.95 83.88 384.20 82.81 384.82 81.73 384.20 81.73 382.95 TH +87.73 382.32 88.81 382.95 88.81 384.20 87.73 384.82 86.66 384.20 86.66 382.95 TH +92.65 382.32 93.73 382.95 93.73 384.20 92.65 384.82 91.58 384.20 91.58 382.95 TH +95.11 382.32 96.19 382.95 96.19 384.20 95.11 384.82 94.04 384.20 94.04 382.95 TH +97.57 382.32 98.65 382.95 98.65 384.20 97.57 384.82 96.50 384.20 96.50 382.95 TH +26.23 378.05 27.31 378.68 27.31 379.93 26.23 380.55 25.16 379.93 25.16 378.68 TH +31.15 378.05 32.23 378.68 32.23 379.93 31.15 380.55 30.08 379.93 30.08 378.68 TH +36.07 378.05 37.14 378.68 37.14 379.93 36.07 380.55 35.00 379.93 35.00 378.68 TH +40.99 378.05 42.07 378.68 42.07 379.93 40.99 380.55 39.92 379.93 39.92 378.68 TH +45.91 378.05 46.99 378.68 46.99 379.93 45.91 380.55 44.84 379.93 44.84 378.68 TH +50.83 378.05 51.91 378.68 51.91 379.93 50.83 380.55 49.76 379.93 49.76 378.68 TH +55.75 378.05 56.83 378.68 56.83 379.93 55.75 380.55 54.68 379.93 54.68 378.68 TH +60.67 378.05 61.75 378.68 61.75 379.93 60.67 380.55 59.59 379.93 59.59 378.68 TH +65.59 378.05 66.66 378.68 66.66 379.93 65.59 380.55 64.51 379.93 64.51 378.68 TH +70.51 378.05 71.59 378.68 71.59 379.93 70.51 380.55 69.44 379.93 69.44 378.68 TH +75.43 378.05 76.51 378.68 76.51 379.93 75.43 380.55 74.35 379.93 74.35 378.68 TH +80.35 378.05 81.43 378.68 81.43 379.93 80.35 380.55 79.27 379.93 79.27 378.68 TH +85.27 378.05 86.35 378.68 86.35 379.93 85.27 380.55 84.20 379.93 84.20 378.68 TH +90.19 378.05 91.27 378.68 91.27 379.93 90.19 380.55 89.12 379.93 89.12 378.68 TH +95.11 378.05 96.19 378.68 96.19 379.93 95.11 380.55 94.04 379.93 94.04 378.68 TH +29.92 375.92 31.00 376.54 31.00 377.79 29.92 378.42 28.85 377.79 28.85 376.54 TH +34.84 375.92 35.92 376.54 35.92 377.79 34.84 378.42 33.77 377.79 33.77 376.54 TH +39.76 375.92 40.84 376.54 40.84 377.79 39.76 378.42 38.69 377.79 38.69 376.54 TH +44.68 375.92 45.75 376.54 45.75 377.79 44.68 378.42 43.61 377.79 43.61 376.54 TH +49.60 375.92 50.68 376.54 50.68 377.79 49.60 378.42 48.53 377.79 48.53 376.54 TH +54.52 375.92 55.60 376.54 55.60 377.79 54.52 378.42 53.45 377.79 53.45 376.54 TH +59.44 375.92 60.52 376.54 60.52 377.79 59.44 378.42 58.36 377.79 58.36 376.54 TH +64.36 375.92 65.44 376.54 65.44 377.79 64.36 378.42 63.28 377.79 63.28 376.54 TH +69.28 375.92 70.35 376.54 70.35 377.79 69.28 378.42 68.20 377.79 68.20 376.54 TH +74.20 375.92 75.28 376.54 75.28 377.79 74.20 378.42 73.13 377.79 73.13 376.54 TH +79.12 375.92 80.20 376.54 80.20 377.79 79.12 378.42 78.04 377.79 78.04 376.54 TH +84.04 375.92 85.12 376.54 85.12 377.79 84.04 378.42 82.96 377.79 82.96 376.54 TH +88.96 375.92 90.04 376.54 90.04 377.79 88.96 378.42 87.88 377.79 87.88 376.54 TH +93.88 375.92 94.96 376.54 94.96 377.79 93.88 378.42 92.81 377.79 92.81 376.54 TH +96.34 375.92 97.42 376.54 97.42 377.79 96.34 378.42 95.27 377.79 95.27 376.54 TH +97.57 373.78 98.65 374.41 98.65 375.66 97.57 376.28 96.50 375.66 96.50 374.41 TH +27.46 371.65 28.54 372.27 28.54 373.52 27.46 374.15 26.39 373.52 26.39 372.27 TH +32.38 371.65 33.46 372.27 33.46 373.52 32.38 374.15 31.31 373.52 31.31 372.27 TH +37.30 371.65 38.38 372.27 38.38 373.52 37.30 374.15 36.23 373.52 36.23 372.27 TH +42.22 371.65 43.30 372.27 43.30 373.52 42.22 374.15 41.14 373.52 41.14 372.27 TH +47.14 371.65 48.22 372.27 48.22 373.52 47.14 374.15 46.07 373.52 46.07 372.27 TH +52.06 371.65 53.14 372.27 53.14 373.52 52.06 374.15 50.99 373.52 50.99 372.27 TH +56.98 371.65 58.06 372.27 58.06 373.52 56.98 374.15 55.91 373.52 55.91 372.27 TH +61.90 371.65 62.97 372.27 62.97 373.52 61.90 374.15 60.82 373.52 60.82 372.27 TH +66.82 371.65 67.90 372.27 67.90 373.52 66.82 374.15 65.74 373.52 65.74 372.27 TH +71.74 371.65 72.82 372.27 72.82 373.52 71.74 374.15 70.66 373.52 70.66 372.27 TH +76.66 371.65 77.74 372.27 77.74 373.52 76.66 374.15 75.59 373.52 75.59 372.27 TH +81.58 371.65 82.66 372.27 82.66 373.52 81.58 374.15 80.51 373.52 80.51 372.27 TH +86.50 371.65 87.57 372.27 87.57 373.52 86.50 374.15 85.42 373.52 85.42 372.27 TH +91.42 371.65 92.50 372.27 92.50 373.52 91.42 374.15 90.35 373.52 90.35 372.27 TH +28.69 369.51 29.77 370.14 29.77 371.39 28.69 372.01 27.62 371.39 27.62 370.14 TH +33.61 369.51 34.69 370.14 34.69 371.39 33.61 372.01 32.54 371.39 32.54 370.14 TH +38.53 369.51 39.61 370.14 39.61 371.39 38.53 372.01 37.46 371.39 37.46 370.14 TH +43.45 369.51 44.53 370.14 44.53 371.39 43.45 372.01 42.38 371.39 42.38 370.14 TH +48.37 369.51 49.45 370.14 49.45 371.39 48.37 372.01 47.30 371.39 47.30 370.14 TH +53.29 369.51 54.36 370.14 54.36 371.39 53.29 372.01 52.22 371.39 52.22 370.14 TH +58.21 369.51 59.29 370.14 59.29 371.39 58.21 372.01 57.14 371.39 57.14 370.14 TH +63.13 369.51 64.21 370.14 64.21 371.39 63.13 372.01 62.05 371.39 62.05 370.14 TH +68.05 369.51 69.13 370.14 69.13 371.39 68.05 372.01 66.98 371.39 66.98 370.14 TH +72.97 369.51 74.05 370.14 74.05 371.39 72.97 372.01 71.90 371.39 71.90 370.14 TH +77.89 369.51 78.97 370.14 78.97 371.39 77.89 372.01 76.82 371.39 76.82 370.14 TH +82.81 369.51 83.88 370.14 83.88 371.39 82.81 372.01 81.73 371.39 81.73 370.14 TH +87.73 369.51 88.81 370.14 88.81 371.39 87.73 372.01 86.66 371.39 86.66 370.14 TH +92.65 369.51 93.73 370.14 93.73 371.39 92.65 372.01 91.58 371.39 91.58 370.14 TH +95.11 369.51 96.19 370.14 96.19 371.39 95.11 372.01 94.04 371.39 94.04 370.14 TH +96.34 367.38 97.42 368.00 97.42 369.25 96.34 369.88 95.27 369.25 95.27 368.00 TH +26.23 365.24 27.31 365.87 27.31 367.12 26.23 367.74 25.16 367.12 25.16 365.87 TH +31.15 365.24 32.23 365.87 32.23 367.12 31.15 367.74 30.08 367.12 30.08 365.87 TH +36.07 365.24 37.14 365.87 37.14 367.12 36.07 367.74 35.00 367.12 35.00 365.87 TH +40.99 365.24 42.07 365.87 42.07 367.12 40.99 367.74 39.92 367.12 39.92 365.87 TH +45.91 365.24 46.99 365.87 46.99 367.12 45.91 367.74 44.84 367.12 44.84 365.87 TH +50.83 365.24 51.91 365.87 51.91 367.12 50.83 367.74 49.76 367.12 49.76 365.87 TH +55.75 365.24 56.83 365.87 56.83 367.12 55.75 367.74 54.68 367.12 54.68 365.87 TH +60.67 365.24 61.75 365.87 61.75 367.12 60.67 367.74 59.59 367.12 59.59 365.87 TH +65.59 365.24 66.66 365.87 66.66 367.12 65.59 367.74 64.51 367.12 64.51 365.87 TH +70.51 365.24 71.59 365.87 71.59 367.12 70.51 367.74 69.44 367.12 69.44 365.87 TH +75.43 365.24 76.51 365.87 76.51 367.12 75.43 367.74 74.35 367.12 74.35 365.87 TH +80.35 365.24 81.43 365.87 81.43 367.12 80.35 367.74 79.27 367.12 79.27 365.87 TH +85.27 365.24 86.35 365.87 86.35 367.12 85.27 367.74 84.20 367.12 84.20 365.87 TH +90.19 365.24 91.27 365.87 91.27 367.12 90.19 367.74 89.12 367.12 89.12 365.87 TH +97.57 365.24 98.65 365.87 98.65 367.12 97.57 367.74 96.50 367.12 96.50 365.87 TH +29.92 363.11 31.00 363.73 31.00 364.98 29.92 365.61 28.85 364.98 28.85 363.73 TH +34.84 363.11 35.92 363.73 35.92 364.98 34.84 365.61 33.77 364.98 33.77 363.73 TH +39.76 363.11 40.84 363.73 40.84 364.98 39.76 365.61 38.69 364.98 38.69 363.73 TH +44.68 363.11 45.75 363.73 45.75 364.98 44.68 365.61 43.61 364.98 43.61 363.73 TH +47.14 363.11 48.22 363.73 48.22 364.98 47.14 365.61 46.07 364.98 46.07 363.73 TH +49.60 363.11 50.68 363.73 50.68 364.98 49.60 365.61 48.53 364.98 48.53 363.73 TH +52.06 363.11 53.14 363.73 53.14 364.98 52.06 365.61 50.99 364.98 50.99 363.73 TH +54.52 363.11 55.60 363.73 55.60 364.98 54.52 365.61 53.45 364.98 53.45 363.73 TH +61.90 363.11 62.97 363.73 62.97 364.98 61.90 365.61 60.82 364.98 60.82 363.73 TH +76.66 363.11 77.74 363.73 77.74 364.98 76.66 365.61 75.59 364.98 75.59 363.73 TH +79.12 363.11 80.20 363.73 80.20 364.98 79.12 365.61 78.04 364.98 78.04 363.73 TH +84.04 363.11 85.12 363.73 85.12 364.98 84.04 365.61 82.96 364.98 82.96 363.73 TH +88.96 363.11 90.04 363.73 90.04 364.98 88.96 365.61 87.88 364.98 87.88 363.73 TH +93.88 363.11 94.96 363.73 94.96 364.98 93.88 365.61 92.81 364.98 92.81 363.73 TH +53.29 360.97 54.36 361.60 54.36 362.85 53.29 363.47 52.22 362.85 52.22 361.60 TH +55.75 360.97 56.83 361.60 56.83 362.85 55.75 363.47 54.68 362.85 54.68 361.60 TH +60.67 360.97 61.75 361.60 61.75 362.85 60.67 363.47 59.59 362.85 59.59 361.60 TH +65.59 360.97 66.66 361.60 66.66 362.85 65.59 363.47 64.51 362.85 64.51 361.60 TH +75.43 360.97 76.51 361.60 76.51 362.85 75.43 363.47 74.35 362.85 74.35 361.60 TH +77.89 360.97 78.97 361.60 78.97 362.85 77.89 363.47 76.82 362.85 76.82 361.60 TH +27.46 358.84 28.54 359.46 28.54 360.71 27.46 361.34 26.39 360.71 26.39 359.46 TH +32.38 358.84 33.46 359.46 33.46 360.71 32.38 361.34 31.31 360.71 31.31 359.46 TH +37.30 358.84 38.38 359.46 38.38 360.71 37.30 361.34 36.23 360.71 36.23 359.46 TH +42.22 358.84 43.30 359.46 43.30 360.71 42.22 361.34 41.14 360.71 41.14 359.46 TH +52.06 358.84 53.14 359.46 53.14 360.71 52.06 361.34 50.99 360.71 50.99 359.46 TH +71.74 358.84 72.82 359.46 72.82 360.71 71.74 361.34 70.66 360.71 70.66 359.46 TH +76.66 358.84 77.74 359.46 77.74 360.71 76.66 361.34 75.59 360.71 75.59 359.46 TH +81.58 358.84 82.66 359.46 82.66 360.71 81.58 361.34 80.51 360.71 80.51 359.46 TH +86.50 358.84 87.57 359.46 87.57 360.71 86.50 361.34 85.42 360.71 85.42 359.46 TH +91.42 358.84 92.50 359.46 92.50 360.71 91.42 361.34 90.35 360.71 90.35 359.46 TH +96.34 358.84 97.42 359.46 97.42 360.71 96.34 361.34 95.27 360.71 95.27 359.46 TH +28.69 356.70 29.77 357.33 29.77 358.58 28.69 359.20 27.62 358.58 27.62 357.33 TH +33.61 356.70 34.69 357.33 34.69 358.58 33.61 359.20 32.54 358.58 32.54 357.33 TH +38.53 356.70 39.61 357.33 39.61 358.58 38.53 359.20 37.46 358.58 37.46 357.33 TH +43.45 356.70 44.53 357.33 44.53 358.58 43.45 359.20 42.38 358.58 42.38 357.33 TH +48.37 356.70 49.45 357.33 49.45 358.58 48.37 359.20 47.30 358.58 47.30 357.33 TH +50.83 356.70 51.91 357.33 51.91 358.58 50.83 359.20 49.76 358.58 49.76 357.33 TH +72.97 356.70 74.05 357.33 74.05 358.58 72.97 359.20 71.90 358.58 71.90 357.33 TH +75.43 356.70 76.51 357.33 76.51 358.58 75.43 359.20 74.35 358.58 74.35 357.33 TH +82.81 356.70 83.88 357.33 83.88 358.58 82.81 359.20 81.73 358.58 81.73 357.33 TH +87.73 356.70 88.81 357.33 88.81 358.58 87.73 359.20 86.66 358.58 86.66 357.33 TH +92.65 356.70 93.73 357.33 93.73 358.58 92.65 359.20 91.58 358.58 91.58 357.33 TH +95.11 356.70 96.19 357.33 96.19 358.58 95.11 359.20 94.04 358.58 94.04 357.33 TH +42.22 354.57 43.30 355.19 43.30 356.44 42.22 357.07 41.14 356.44 41.14 355.19 TH +71.74 354.57 72.82 355.19 72.82 356.44 71.74 357.07 70.66 356.44 70.66 355.19 TH +74.20 354.57 75.28 355.19 75.28 356.44 74.20 357.07 73.13 356.44 73.13 355.19 TH +76.66 354.57 77.74 355.19 77.74 356.44 76.66 357.07 75.59 356.44 75.59 355.19 TH +79.12 354.57 80.20 355.19 80.20 356.44 79.12 357.07 78.04 356.44 78.04 355.19 TH +26.23 352.43 27.31 353.06 27.31 354.31 26.23 354.93 25.16 354.31 25.16 353.06 TH +31.15 352.43 32.23 353.06 32.23 354.31 31.15 354.93 30.08 354.31 30.08 353.06 TH +36.07 352.43 37.14 353.06 37.14 354.31 36.07 354.93 35.00 354.31 35.00 353.06 TH +40.99 352.43 42.07 353.06 42.07 354.31 40.99 354.93 39.92 354.31 39.92 353.06 TH +45.91 352.43 46.99 353.06 46.99 354.31 45.91 354.93 44.84 354.31 44.84 353.06 TH +75.43 352.43 76.51 353.06 76.51 354.31 75.43 354.93 74.35 354.31 74.35 353.06 TH +80.35 352.43 81.43 353.06 81.43 354.31 80.35 354.93 79.27 354.31 79.27 353.06 TH +85.27 352.43 86.35 353.06 86.35 354.31 85.27 354.93 84.20 354.31 84.20 353.06 TH +90.19 352.43 91.27 353.06 91.27 354.31 90.19 354.93 89.12 354.31 89.12 353.06 TH +29.92 350.30 31.00 350.92 31.00 352.17 29.92 352.80 28.85 352.17 28.85 350.92 TH +34.84 350.30 35.92 350.92 35.92 352.17 34.84 352.80 33.77 352.17 33.77 350.92 TH +39.76 350.30 40.84 350.92 40.84 352.17 39.76 352.80 38.69 352.17 38.69 350.92 TH +42.22 350.30 43.30 350.92 43.30 352.17 42.22 352.80 41.14 352.17 41.14 350.92 TH +44.68 350.30 45.75 350.92 45.75 352.17 44.68 352.80 43.61 352.17 43.61 350.92 TH +47.14 350.30 48.22 350.92 48.22 352.17 47.14 352.80 46.07 352.17 46.07 350.92 TH +76.66 350.30 77.74 350.92 77.74 352.17 76.66 352.80 75.59 352.17 75.59 350.92 TH +84.04 350.30 85.12 350.92 85.12 352.17 84.04 352.80 82.96 352.17 82.96 350.92 TH +88.96 350.30 90.04 350.92 90.04 352.17 88.96 352.80 87.88 352.17 87.88 350.92 TH +93.88 350.30 94.96 350.92 94.96 352.17 93.88 352.80 92.81 352.17 92.81 350.92 TH +96.34 350.30 97.42 350.92 97.42 352.17 96.34 352.80 95.27 352.17 95.27 350.92 TH +45.91 348.16 46.99 348.79 46.99 350.04 45.91 350.66 44.84 350.04 44.84 348.79 TH +75.43 348.16 76.51 348.79 76.51 350.04 75.43 350.66 74.35 350.04 74.35 348.79 TH +95.11 348.16 96.19 348.79 96.19 350.04 95.11 350.66 94.04 350.04 94.04 348.79 TH +27.46 346.03 28.54 346.65 28.54 347.90 27.46 348.53 26.39 347.90 26.39 346.65 TH +32.38 346.03 33.46 346.65 33.46 347.90 32.38 348.53 31.31 347.90 31.31 346.65 TH +37.30 346.03 38.38 346.65 38.38 347.90 37.30 348.53 36.23 347.90 36.23 346.65 TH +47.14 346.03 48.22 346.65 48.22 347.90 47.14 348.53 46.07 347.90 46.07 346.65 TH +76.66 346.03 77.74 346.65 77.74 347.90 76.66 348.53 75.59 347.90 75.59 346.65 TH +79.12 346.03 80.20 346.65 80.20 347.90 79.12 348.53 78.04 347.90 78.04 346.65 TH +81.58 346.03 82.66 346.65 82.66 347.90 81.58 348.53 80.51 347.90 80.51 346.65 TH +86.50 346.03 87.57 346.65 87.57 347.90 86.50 348.53 85.42 347.90 85.42 346.65 TH +91.42 346.03 92.50 346.65 92.50 347.90 91.42 348.53 90.35 347.90 90.35 346.65 TH +28.69 343.89 29.77 344.52 29.77 345.77 28.69 346.39 27.62 345.77 27.62 344.52 TH +33.61 343.89 34.69 344.52 34.69 345.77 33.61 346.39 32.54 345.77 32.54 344.52 TH +38.53 343.89 39.61 344.52 39.61 345.77 38.53 346.39 37.46 345.77 37.46 344.52 TH +43.45 343.89 44.53 344.52 44.53 345.77 43.45 346.39 42.38 345.77 42.38 344.52 TH +48.37 343.89 49.45 344.52 49.45 345.77 48.37 346.39 47.30 345.77 47.30 344.52 TH +72.97 343.89 74.05 344.52 74.05 345.77 72.97 346.39 71.90 345.77 71.90 344.52 TH +75.43 343.89 76.51 344.52 76.51 345.77 75.43 346.39 74.35 345.77 74.35 344.52 TH +77.89 343.89 78.97 344.52 78.97 345.77 77.89 346.39 76.82 345.77 76.82 344.52 TH +82.81 343.89 83.88 344.52 83.88 345.77 82.81 346.39 81.73 345.77 81.73 344.52 TH +87.73 343.89 88.81 344.52 88.81 345.77 87.73 346.39 86.66 345.77 86.66 344.52 TH +92.65 343.89 93.73 344.52 93.73 345.77 92.65 346.39 91.58 345.77 91.58 344.52 TH +95.11 343.89 96.19 344.52 96.19 345.77 95.11 346.39 94.04 345.77 94.04 344.52 TH +97.57 343.89 98.65 344.52 98.65 345.77 97.57 346.39 96.50 345.77 96.50 344.52 TH +42.22 341.76 43.30 342.38 43.30 343.63 42.22 344.26 41.14 343.63 41.14 342.38 TH +47.14 341.76 48.22 342.38 48.22 343.63 47.14 344.26 46.07 343.63 46.07 342.38 TH +49.60 341.76 50.68 342.38 50.68 343.63 49.60 344.26 48.53 343.63 48.53 342.38 TH +74.20 341.76 75.28 342.38 75.28 343.63 74.20 344.26 73.13 343.63 73.13 342.38 TH +96.34 341.76 97.42 342.38 97.42 343.63 96.34 344.26 95.27 343.63 95.27 342.38 TH +26.23 339.62 27.31 340.25 27.31 341.50 26.23 342.12 25.16 341.50 25.16 340.25 TH +31.15 339.62 32.23 340.25 32.23 341.50 31.15 342.12 30.08 341.50 30.08 340.25 TH +36.07 339.62 37.14 340.25 37.14 341.50 36.07 342.12 35.00 341.50 35.00 340.25 TH +43.45 339.62 44.53 340.25 44.53 341.50 43.45 342.12 42.38 341.50 42.38 340.25 TH +48.37 339.62 49.45 340.25 49.45 341.50 48.37 342.12 47.30 341.50 47.30 340.25 TH +50.83 339.62 51.91 340.25 51.91 341.50 50.83 342.12 49.76 341.50 49.76 340.25 TH +75.43 339.62 76.51 340.25 76.51 341.50 75.43 342.12 74.35 341.50 74.35 340.25 TH +77.89 339.62 78.97 340.25 78.97 341.50 77.89 342.12 76.82 341.50 76.82 340.25 TH +80.35 339.62 81.43 340.25 81.43 341.50 80.35 342.12 79.27 341.50 79.27 340.25 TH +85.27 339.62 86.35 340.25 86.35 341.50 85.27 342.12 84.20 341.50 84.20 340.25 TH +90.19 339.62 91.27 340.25 91.27 341.50 90.19 342.12 89.12 341.50 89.12 340.25 TH +95.11 339.62 96.19 340.25 96.19 341.50 95.11 342.12 94.04 341.50 94.04 340.25 TH +97.57 339.62 98.65 340.25 98.65 341.50 97.57 342.12 96.50 341.50 96.50 340.25 TH +29.92 337.49 31.00 338.11 31.00 339.36 29.92 339.99 28.85 339.36 28.85 338.11 TH +34.84 337.49 35.92 338.11 35.92 339.36 34.84 339.99 33.77 339.36 33.77 338.11 TH +39.76 337.49 40.84 338.11 40.84 339.36 39.76 339.99 38.69 339.36 38.69 338.11 TH +44.68 337.49 45.75 338.11 45.75 339.36 44.68 339.99 43.61 339.36 43.61 338.11 TH +49.60 337.49 50.68 338.11 50.68 339.36 49.60 339.99 48.53 339.36 48.53 338.11 TH +69.28 337.49 70.35 338.11 70.35 339.36 69.28 339.99 68.20 339.36 68.20 338.11 TH +71.74 337.49 72.82 338.11 72.82 339.36 71.74 339.99 70.66 339.36 70.66 338.11 TH +74.20 337.49 75.28 338.11 75.28 339.36 74.20 339.99 73.13 339.36 73.13 338.11 TH +76.66 337.49 77.74 338.11 77.74 339.36 76.66 339.99 75.59 339.36 75.59 338.11 TH +79.12 337.49 80.20 338.11 80.20 339.36 79.12 339.99 78.04 339.36 78.04 338.11 TH +84.04 337.49 85.12 338.11 85.12 339.36 84.04 339.99 82.96 339.36 82.96 338.11 TH +88.96 337.49 90.04 338.11 90.04 339.36 88.96 339.99 87.88 339.36 87.88 338.11 TH +93.88 337.49 94.96 338.11 94.96 339.36 93.88 339.99 92.81 339.36 92.81 338.11 TH +45.91 335.35 46.99 335.98 46.99 337.23 45.91 337.85 44.84 337.23 44.84 335.98 TH +48.37 335.35 49.45 335.98 49.45 337.23 48.37 337.85 47.30 337.23 47.30 335.98 TH +50.83 335.35 51.91 335.98 51.91 337.23 50.83 337.85 49.76 337.23 49.76 335.98 TH +55.75 335.35 56.83 335.98 56.83 337.23 55.75 337.85 54.68 337.23 54.68 335.98 TH +65.59 335.35 66.66 335.98 66.66 337.23 65.59 337.85 64.51 337.23 64.51 335.98 TH +68.05 335.35 69.13 335.98 69.13 337.23 68.05 337.85 66.98 337.23 66.98 335.98 TH +70.51 335.35 71.59 335.98 71.59 337.23 70.51 337.85 69.44 337.23 69.44 335.98 TH +75.43 335.35 76.51 335.98 76.51 337.23 75.43 337.85 74.35 337.23 74.35 335.98 TH +95.11 335.35 96.19 335.98 96.19 337.23 95.11 337.85 94.04 337.23 94.04 335.98 TH +97.57 335.35 98.65 335.98 98.65 337.23 97.57 337.85 96.50 337.23 96.50 335.98 TH +27.46 333.22 28.54 333.84 28.54 335.09 27.46 335.72 26.39 335.09 26.39 333.84 TH +32.38 333.22 33.46 333.84 33.46 335.09 32.38 335.72 31.31 335.09 31.31 333.84 TH +37.30 333.22 38.38 333.84 38.38 335.09 37.30 335.72 36.23 335.09 36.23 333.84 TH +42.22 333.22 43.30 333.84 43.30 335.09 42.22 335.72 41.14 335.09 41.14 333.84 TH +47.14 333.22 48.22 333.84 48.22 335.09 47.14 335.72 46.07 335.09 46.07 333.84 TH +52.06 333.22 53.14 333.84 53.14 335.09 52.06 335.72 50.99 335.09 50.99 333.84 TH +54.52 333.22 55.60 333.84 55.60 335.09 54.52 335.72 53.45 335.09 53.45 333.84 TH +56.98 333.22 58.06 333.84 58.06 335.09 56.98 335.72 55.91 335.09 55.91 333.84 TH +59.44 333.22 60.52 333.84 60.52 335.09 59.44 335.72 58.36 335.09 58.36 333.84 TH +61.90 333.22 62.97 333.84 62.97 335.09 61.90 335.72 60.82 335.09 60.82 333.84 TH +64.36 333.22 65.44 333.84 65.44 335.09 64.36 335.72 63.28 335.09 63.28 333.84 TH +69.28 333.22 70.35 333.84 70.35 335.09 69.28 335.72 68.20 335.09 68.20 333.84 TH +71.74 333.22 72.82 333.84 72.82 335.09 71.74 335.72 70.66 335.09 70.66 333.84 TH +81.58 333.22 82.66 333.84 82.66 335.09 81.58 335.72 80.51 335.09 80.51 333.84 TH +86.50 333.22 87.57 333.84 87.57 335.09 86.50 335.72 85.42 335.09 85.42 333.84 TH +91.42 333.22 92.50 333.84 92.50 335.09 91.42 335.72 90.35 335.09 90.35 333.84 TH +96.34 333.22 97.42 333.84 97.42 335.09 96.34 335.72 95.27 335.09 95.27 333.84 TH +28.69 331.08 29.77 331.71 29.77 332.96 28.69 333.58 27.62 332.96 27.62 331.71 TH +33.61 331.08 34.69 331.71 34.69 332.96 33.61 333.58 32.54 332.96 32.54 331.71 TH +38.53 331.08 39.61 331.71 39.61 332.96 38.53 333.58 37.46 332.96 37.46 331.71 TH +43.45 331.08 44.53 331.71 44.53 332.96 43.45 333.58 42.38 332.96 42.38 331.71 TH +48.37 331.08 49.45 331.71 49.45 332.96 48.37 333.58 47.30 332.96 47.30 331.71 TH +53.29 331.08 54.36 331.71 54.36 332.96 53.29 333.58 52.22 332.96 52.22 331.71 TH +58.21 331.08 59.29 331.71 59.29 332.96 58.21 333.58 57.14 332.96 57.14 331.71 TH +63.13 331.08 64.21 331.71 64.21 332.96 63.13 333.58 62.05 332.96 62.05 331.71 TH +68.05 331.08 69.13 331.71 69.13 332.96 68.05 333.58 66.98 332.96 66.98 331.71 TH +72.97 331.08 74.05 331.71 74.05 332.96 72.97 333.58 71.90 332.96 71.90 331.71 TH +75.43 331.08 76.51 331.71 76.51 332.96 75.43 333.58 74.35 332.96 74.35 331.71 TH +77.89 331.08 78.97 331.71 78.97 332.96 77.89 333.58 76.82 332.96 76.82 331.71 TH +80.35 331.08 81.43 331.71 81.43 332.96 80.35 333.58 79.27 332.96 79.27 331.71 TH +82.81 331.08 83.88 331.71 83.88 332.96 82.81 333.58 81.73 332.96 81.73 331.71 TH +87.73 331.08 88.81 331.71 88.81 332.96 87.73 333.58 86.66 332.96 86.66 331.71 TH +92.65 331.08 93.73 331.71 93.73 332.96 92.65 333.58 91.58 332.96 91.58 331.71 TH +95.11 331.08 96.19 331.71 96.19 332.96 95.11 333.58 94.04 332.96 94.04 331.71 TH +97.57 331.08 98.65 331.71 98.65 332.96 97.57 333.58 96.50 332.96 96.50 331.71 TH +76.66 328.95 77.74 329.57 77.74 330.82 76.66 331.45 75.59 330.82 75.59 329.57 TH +79.12 328.95 80.20 329.57 80.20 330.82 79.12 331.45 78.04 330.82 78.04 329.57 TH +81.58 328.95 82.66 329.57 82.66 330.82 81.58 331.45 80.51 330.82 80.51 329.57 TH +84.04 328.95 85.12 329.57 85.12 330.82 84.04 331.45 82.96 330.82 82.96 329.57 TH +88.96 328.95 90.04 329.57 90.04 330.82 88.96 331.45 87.88 330.82 87.88 329.57 TH +93.88 328.95 94.96 329.57 94.96 330.82 93.88 331.45 92.81 330.82 92.81 329.57 TH +96.34 328.95 97.42 329.57 97.42 330.82 96.34 331.45 95.27 330.82 95.27 329.57 TH +26.23 326.81 27.31 327.44 27.31 328.69 26.23 329.31 25.16 328.69 25.16 327.44 TH +31.15 326.81 32.23 327.44 32.23 328.69 31.15 329.31 30.08 328.69 30.08 327.44 TH +36.07 326.81 37.14 327.44 37.14 328.69 36.07 329.31 35.00 328.69 35.00 327.44 TH +40.99 326.81 42.07 327.44 42.07 328.69 40.99 329.31 39.92 328.69 39.92 327.44 TH +45.91 326.81 46.99 327.44 46.99 328.69 45.91 329.31 44.84 328.69 44.84 327.44 TH +50.83 326.81 51.91 327.44 51.91 328.69 50.83 329.31 49.76 328.69 49.76 327.44 TH +55.75 326.81 56.83 327.44 56.83 328.69 55.75 329.31 54.68 328.69 54.68 327.44 TH +60.67 326.81 61.75 327.44 61.75 328.69 60.67 329.31 59.59 328.69 59.59 327.44 TH +65.59 326.81 66.66 327.44 66.66 328.69 65.59 329.31 64.51 328.69 64.51 327.44 TH +70.51 326.81 71.59 327.44 71.59 328.69 70.51 329.31 69.44 328.69 69.44 327.44 TH +95.11 326.81 96.19 327.44 96.19 328.69 95.11 329.31 94.04 328.69 94.04 327.44 TH +27.46 324.68 28.54 325.30 28.54 326.55 27.46 327.18 26.39 326.55 26.39 325.30 TH +29.92 324.68 31.00 325.30 31.00 326.55 29.92 327.18 28.85 326.55 28.85 325.30 TH +32.38 324.68 33.46 325.30 33.46 326.55 32.38 327.18 31.31 326.55 31.31 325.30 TH +34.84 324.68 35.92 325.30 35.92 326.55 34.84 327.18 33.77 326.55 33.77 325.30 TH +37.30 324.68 38.38 325.30 38.38 326.55 37.30 327.18 36.23 326.55 36.23 325.30 TH +39.76 324.68 40.84 325.30 40.84 326.55 39.76 327.18 38.69 326.55 38.69 325.30 TH +42.22 324.68 43.30 325.30 43.30 326.55 42.22 327.18 41.14 326.55 41.14 325.30 TH +44.68 324.68 45.75 325.30 45.75 326.55 44.68 327.18 43.61 326.55 43.61 325.30 TH +56.98 324.68 58.06 325.30 58.06 326.55 56.98 327.18 55.91 326.55 55.91 325.30 TH +59.44 324.68 60.52 325.30 60.52 326.55 59.44 327.18 58.36 326.55 58.36 325.30 TH +61.90 324.68 62.97 325.30 62.97 326.55 61.90 327.18 60.82 326.55 60.82 325.30 TH +64.36 324.68 65.44 325.30 65.44 326.55 64.36 327.18 63.28 326.55 63.28 325.30 TH +79.12 324.68 80.20 325.30 80.20 326.55 79.12 327.18 78.04 326.55 78.04 325.30 TH +84.04 324.68 85.12 325.30 85.12 326.55 84.04 327.18 82.96 326.55 82.96 325.30 TH +28.69 322.54 29.77 323.17 29.77 324.42 28.69 325.04 27.62 324.42 27.62 323.17 TH +33.61 322.54 34.69 323.17 34.69 324.42 33.61 325.04 32.54 324.42 32.54 323.17 TH +36.07 322.54 37.14 323.17 37.14 324.42 36.07 325.04 35.00 324.42 35.00 323.17 TH +40.99 322.54 42.07 323.17 42.07 324.42 40.99 325.04 39.92 324.42 39.92 323.17 TH +45.91 322.54 46.99 323.17 46.99 324.42 45.91 325.04 44.84 324.42 44.84 323.17 TH +48.37 322.54 49.45 323.17 49.45 324.42 48.37 325.04 47.30 324.42 47.30 323.17 TH +50.83 322.54 51.91 323.17 51.91 324.42 50.83 325.04 49.76 324.42 49.76 323.17 TH +53.29 322.54 54.36 323.17 54.36 324.42 53.29 325.04 52.22 324.42 52.22 323.17 TH +65.59 322.54 66.66 323.17 66.66 324.42 65.59 325.04 64.51 324.42 64.51 323.17 TH +68.05 322.54 69.13 323.17 69.13 324.42 68.05 325.04 66.98 324.42 66.98 323.17 TH +70.51 322.54 71.59 323.17 71.59 324.42 70.51 325.04 69.44 324.42 69.44 323.17 TH +72.97 322.54 74.05 323.17 74.05 324.42 72.97 325.04 71.90 324.42 71.90 323.17 TH +77.89 322.54 78.97 323.17 78.97 324.42 77.89 325.04 76.82 324.42 76.82 323.17 TH +82.81 322.54 83.88 323.17 83.88 324.42 82.81 325.04 81.73 324.42 81.73 323.17 TH +87.73 322.54 88.81 323.17 88.81 324.42 87.73 325.04 86.66 324.42 86.66 323.17 TH +92.65 322.54 93.73 323.17 93.73 324.42 92.65 325.04 91.58 324.42 91.58 323.17 TH +27.46 320.41 28.54 321.03 28.54 322.28 27.46 322.91 26.39 322.28 26.39 321.03 TH +32.38 320.41 33.46 321.03 33.46 322.28 32.38 322.91 31.31 322.28 31.31 321.03 TH +37.30 320.41 38.38 321.03 38.38 322.28 37.30 322.91 36.23 322.28 36.23 321.03 TH +42.22 320.41 43.30 321.03 43.30 322.28 42.22 322.91 41.14 322.28 41.14 321.03 TH +59.44 320.41 60.52 321.03 60.52 322.28 59.44 322.91 58.36 322.28 58.36 321.03 TH +64.36 320.41 65.44 321.03 65.44 322.28 64.36 322.91 63.28 322.28 63.28 321.03 TH +69.28 320.41 70.35 321.03 70.35 322.28 69.28 322.91 68.20 322.28 68.20 321.03 TH +74.20 320.41 75.28 321.03 75.28 322.28 74.20 322.91 73.13 322.28 73.13 321.03 TH +76.66 320.41 77.74 321.03 77.74 322.28 76.66 322.91 75.59 322.28 75.59 321.03 TH +79.12 320.41 80.20 321.03 80.20 322.28 79.12 322.91 78.04 322.28 78.04 321.03 TH +81.58 320.41 82.66 321.03 82.66 322.28 81.58 322.91 80.51 322.28 80.51 321.03 TH +84.04 320.41 85.12 321.03 85.12 322.28 84.04 322.91 82.96 322.28 82.96 321.03 TH +86.50 320.41 87.57 321.03 87.57 322.28 86.50 322.91 85.42 322.28 85.42 321.03 TH +91.42 320.41 92.50 321.03 92.50 322.28 91.42 322.91 90.35 322.28 90.35 321.03 TH +96.34 320.41 97.42 321.03 97.42 322.28 96.34 322.91 95.27 322.28 95.27 321.03 TH +26.23 318.27 27.31 318.90 27.31 320.15 26.23 320.77 25.16 320.15 25.16 318.90 TH +28.69 318.27 29.77 318.90 29.77 320.15 28.69 320.77 27.62 320.15 27.62 318.90 TH +31.15 318.27 32.23 318.90 32.23 320.15 31.15 320.77 30.08 320.15 30.08 318.90 TH +33.61 318.27 34.69 318.90 34.69 320.15 33.61 320.77 32.54 320.15 32.54 318.90 TH +38.53 318.27 39.61 318.90 39.61 320.15 38.53 320.77 37.46 320.15 37.46 318.90 TH +43.45 318.27 44.53 318.90 44.53 320.15 43.45 320.77 42.38 320.15 42.38 318.90 TH +45.91 318.27 46.99 318.90 46.99 320.15 45.91 320.77 44.84 320.15 44.84 318.90 TH +50.83 318.27 51.91 318.90 51.91 320.15 50.83 320.77 49.76 320.15 49.76 318.90 TH +65.59 318.27 66.66 318.90 66.66 320.15 65.59 320.77 64.51 320.15 64.51 318.90 TH +68.05 318.27 69.13 318.90 69.13 320.15 68.05 320.77 66.98 320.15 66.98 318.90 TH +70.51 318.27 71.59 318.90 71.59 320.15 70.51 320.77 69.44 320.15 69.44 318.90 TH +72.97 318.27 74.05 318.90 74.05 320.15 72.97 320.77 71.90 320.15 71.90 318.90 TH +77.89 318.27 78.97 318.90 78.97 320.15 77.89 320.77 76.82 320.15 76.82 318.90 TH +82.81 318.27 83.88 318.90 83.88 320.15 82.81 320.77 81.73 320.15 81.73 318.90 TH +85.27 318.27 86.35 318.90 86.35 320.15 85.27 320.77 84.20 320.15 84.20 318.90 TH +90.19 318.27 91.27 318.90 91.27 320.15 90.19 320.77 89.12 320.15 89.12 318.90 TH +95.11 318.27 96.19 318.90 96.19 320.15 95.11 320.77 94.04 320.15 94.04 318.90 TH +29.92 316.14 31.00 316.76 31.00 318.01 29.92 318.64 28.85 318.01 28.85 316.76 TH +34.84 316.14 35.92 316.76 35.92 318.01 34.84 318.64 33.77 318.01 33.77 316.76 TH +37.30 316.14 38.38 316.76 38.38 318.01 37.30 318.64 36.23 318.01 36.23 316.76 TH +42.22 316.14 43.30 316.76 43.30 318.01 42.22 318.64 41.14 318.01 41.14 316.76 TH +47.14 316.14 48.22 316.76 48.22 318.01 47.14 318.64 46.07 318.01 46.07 316.76 TH +49.60 316.14 50.68 316.76 50.68 318.01 49.60 318.64 48.53 318.01 48.53 316.76 TH +52.06 316.14 53.14 316.76 53.14 318.01 52.06 318.64 50.99 318.01 50.99 316.76 TH +54.52 316.14 55.60 316.76 55.60 318.01 54.52 318.64 53.45 318.01 53.45 316.76 TH +59.44 316.14 60.52 316.76 60.52 318.01 59.44 318.64 58.36 318.01 58.36 316.76 TH +64.36 316.14 65.44 316.76 65.44 318.01 64.36 318.64 63.28 318.01 63.28 316.76 TH +66.82 316.14 67.90 316.76 67.90 318.01 66.82 318.64 65.74 318.01 65.74 316.76 TH +71.74 316.14 72.82 316.76 72.82 318.01 71.74 318.64 70.66 318.01 70.66 316.76 TH +76.66 316.14 77.74 316.76 77.74 318.01 76.66 318.64 75.59 318.01 75.59 316.76 TH +81.58 316.14 82.66 316.76 82.66 318.01 81.58 318.64 80.51 318.01 80.51 316.76 TH +86.50 316.14 87.57 316.76 87.57 318.01 86.50 318.64 85.42 318.01 85.42 316.76 TH +88.96 316.14 90.04 316.76 90.04 318.01 88.96 318.64 87.88 318.01 87.88 316.76 TH +91.42 316.14 92.50 316.76 92.50 318.01 91.42 318.64 90.35 318.01 90.35 316.76 TH +93.88 316.14 94.96 316.76 94.96 318.01 93.88 318.64 92.81 318.01 92.81 316.76 TH +28.69 314.00 29.77 314.63 29.77 315.88 28.69 316.50 27.62 315.88 27.62 314.63 TH +33.61 314.00 34.69 314.63 34.69 315.88 33.61 316.50 32.54 315.88 32.54 314.63 TH +58.21 314.00 59.29 314.63 59.29 315.88 58.21 316.50 57.14 315.88 57.14 314.63 TH +63.13 314.00 64.21 314.63 64.21 315.88 63.13 316.50 62.05 315.88 62.05 314.63 TH +65.59 314.00 66.66 314.63 66.66 315.88 65.59 316.50 64.51 315.88 64.51 314.63 TH +70.51 314.00 71.59 314.63 71.59 315.88 70.51 316.50 69.44 315.88 69.44 314.63 TH +75.43 314.00 76.51 314.63 76.51 315.88 75.43 316.50 74.35 315.88 74.35 314.63 TH +80.35 314.00 81.43 314.63 81.43 315.88 80.35 316.50 79.27 315.88 79.27 314.63 TH +87.73 314.00 88.81 314.63 88.81 315.88 87.73 316.50 86.66 315.88 86.66 314.63 TH +92.65 314.00 93.73 314.63 93.73 315.88 92.65 316.50 91.58 315.88 91.58 314.63 TH + +showpage diff --git a/barcode/src/test/resources/org/xbib/graphics/barcode/output/maxicode-basic.svg b/barcode/src/test/resources/org/xbib/graphics/barcode/output/maxicode-basic.svg new file mode 100755 index 0000000..40be49c --- /dev/null +++ b/barcode/src/test/resources/org/xbib/graphics/barcode/output/maxicode-basic.svg @@ -0,0 +1,384 @@ + + + + 123456789 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/barcode/src/test/resources/org/xbib/graphics/barcode/pdf417/micro-basic-2-columns.codewords b/barcode/src/test/resources/org/xbib/graphics/barcode/pdf417/micro-basic-2-columns.codewords new file mode 100755 index 0000000..869cf40 --- /dev/null +++ b/barcode/src/test/resources/org/xbib/graphics/barcode/pdf417/micro-basic-2-columns.codewords @@ -0,0 +1,11 @@ +22131114232212341113313221111 +31131142111413113122614121111 +31221121231143143211234211111 +22221143211132241113233311111 +21321131112612431313112411111 +21411121421124244211212321111 +22311114232212122133232312111 +31311143141112511212233212111 +32211121311531326111124112111 +41211121211316121221264111211 +42111111521133131153214111121 diff --git a/barcode/src/test/resources/org/xbib/graphics/barcode/pdf417/micro-basic-2-columns.png b/barcode/src/test/resources/org/xbib/graphics/barcode/pdf417/micro-basic-2-columns.png new file mode 100644 index 0000000000000000000000000000000000000000..6c7137cbeb2f1348041a54991e341d546df33ed6 GIT binary patch literal 10109 zcmeI2YfMvV8plyqU8fZ$(a@=;A`&1b7G~QRRvSRAF5_@8<3=MDw|nMqCvIi>h8H4?R6FX-_jtK+`xCWCB;_@DH<T2nnime6y^#O8@7|H~`;ROjlZP8xKT&Y;5&I?oITX>`TQ?-nx# z_4ckvb2w^P`=V+o!?b`d^bsA)yq!%Ejtp2m8%jnJE{%Biy)jcolNF=}FML}u$o3042k(wt z{A-lGUVyJhUH+9nBKND@NQreP2w#KBJl?9AN$7#a;CAO3-Qyvt>f0fYnv=1T+u&{P z+hCvb5nCso`Y)>BnYTVPQUMj~q1vI+=}cd3b6s-?0@=WnsZZvGrq(T0mJz^BEtEWz zyQE1_J#lJc0)ZqAap@LE0zL~QEB^x^E9f6;cR*N=$TJ;+QN*_Ujj65<@s}|QCSY$I z^gZ-ESLBRIj!g~$Z8h+*=9^ioG9`yx2P+WJq4}Zl?F=)JtkD>PV#L^RE6fXE3M(K> ztUE&84@8O<7y|1UK-I4mnlAp`b!=FHh?4=Y0eA%%hV^V%pd7(@1mN!cId6ksefgu! zumUp|-UaX;Xh$1jDvpF07a=p;O6nkvh&UpmivCNZJycpE?DbUqz{+veB1zV$x-#gj ztH8QzTCe(RX}UNj)=lmS%u~h_1KH)BvEec?B_#)sVR14Y2gPmW!1iXdQ)dh(M648a7_b zrtt{ZO)jI5W!t&XQQqc%@ia(bF~*e2drSkZ>!bm=Tw5{6f4duhGWmUyQh3qtyCnXc zC0r%3p7L)E_nY2SVm8x*JfCGJ?%I2o*bwoA{+E*5_(o5%jq%JY06d``K;lYOfR6|} zLn`xm%4-2d+tyn}0(OM109ktR6u~b*b4WZNs(&yiZe^FOp&;_*yH8I{cqS3Q0{3PC z?rN{jKC9+)s@&@n^8sE>eH)BMs|g_}pzC?0<2l|(ktRL4e;bUd&Kis#ha$ta&_D9PB-=Tl%nw$Gf0O1JUL(**^4uUv{Ut>i8>tf=}TmfRfm!1c(`&Y3!pfgl+Of5|3(on7NT z(PD+`k*R!Tpj(UJpEwZWK;8!lzw!!M(5C=|(kLR+Nj_)gINb!Ix;Hc}tNVI|(O3e? zRKOhq3fO5X>IGGQH%y*=rS+NT{)G}1u$Dl5i+g>brcv+4(dX`Zjk7frCUsBVOav^| zPJ*0o4h^&^ZeCX;z7|DEU}}NvUYzXW0Z+UeiSLwU%n8a0P>=+U80dZvPYF6+^KPg< zR$@(+?nf~a*lGC`ZH3Y%2arH-tNq4}0lKT&{{;$>z>5RHACtR4!0GP>`^Wup#Ab;n z`?D$ILnlz~$cD_ci!L95kh4^fr$%F=C7{90bE`}lU1leA%`Sv#`YOR8#RLGk+54rBkp@AS8SZi-`yCN@a0(%LD z0p}TM4G*Qd2c36lHzqwqQYo9kf7j^UAuPOj~lyS~Za;myP7nBVmJDA=cMz0vcX2JNH% zI3~__3`%)YT=kvl<0K)u$?8Zi tIn9SRyX40L;(x@s5H;}=`r+-W`@F_i&&qfG1O2XU-`?y!9lP^S{0IJM?v4Ne literal 0 HcmV?d00001 diff --git a/barcode/src/test/resources/org/xbib/graphics/barcode/pdf417/micro-basic-2-columns.properties b/barcode/src/test/resources/org/xbib/graphics/barcode/pdf417/micro-basic-2-columns.properties new file mode 100755 index 0000000..d67a107 --- /dev/null +++ b/barcode/src/test/resources/org/xbib/graphics/barcode/pdf417/micro-basic-2-columns.properties @@ -0,0 +1,3 @@ +mode=MICRO +dataColumns=2 +content=This is just a test. diff --git a/barcode/src/test/resources/org/xbib/graphics/barcode/pdf417/micro-basic-ecc-level-7-ignored.codewords b/barcode/src/test/resources/org/xbib/graphics/barcode/pdf417/micro-basic-ecc-level-7-ignored.codewords new file mode 100755 index 0000000..9dca55e --- /dev/null +++ b/barcode/src/test/resources/org/xbib/graphics/barcode/pdf417/micro-basic-ecc-level-7-ignored.codewords @@ -0,0 +1,6 @@ +221311142322123411133111223112122423231511222213111 +311311221151321414411112123151322112222243113113111 +312211611132212233123112213121421124244211213122111 +222211112141342122242213113121231611212232322222111 +213211331421121213522113122111411342312242212132111 +214111123212241116124113212115411311215311132141111 diff --git a/barcode/src/test/resources/org/xbib/graphics/barcode/pdf417/micro-basic-ecc-level-7-ignored.png b/barcode/src/test/resources/org/xbib/graphics/barcode/pdf417/micro-basic-ecc-level-7-ignored.png new file mode 100644 index 0000000000000000000000000000000000000000..8a60ca842fdb9cdf562a95a7df1cefaa6b0ca759 GIT binary patch literal 10491 zcmeI2e@s(X6vw*|b%NDxj5NTA+5TWZn%O@HHmg~j0%OMDmbp<(>4QR64B`W%uV6ZA zF#Hfv_Q!%o8|QR^WTBMT;V86}vNB*nOTdlxv9?rXu;Ob$=O{`o?A+IX_xEC!o7@oc za^JbP=e~Q+{d~`D{^UqDKQ<|r$K&x2eU$krj~6ow|KEBM{_wKvI&*ovx9o>94}4Zb zwBI>E9^d!M@T6@sr#j>7?7VxQzw=AtrQ@4x*PmQxg!&%$Xq@Wt1BtnrEj4cxi2_Rh z1xg(wG@m{;#&;!nzg()Ga&)r(MJjl~y0A4@74Q9-P^5^n&ROje%gKQc#NCGPbbW>n zRur<6jT3n#V)~};GUcg|PdP4Y1>OO3D@nK}7#UfZC7#L&BseRN*5iS(tMSK$w-XK^ zIAlI=Ng`bL{+v=jzcrT`4?bAGv{RFQ=CpC9rFNJ~cTME`?%vSRZOM64Igkl6#Rco$ z+Y)k=Yy0UpJr^j?P^_$9F8AG4bT5iTdo{-LKEvYgS6G=u72kUb5^FPgQ|DhiY||VY zbL62G+!m_W8C#R{6hc!c^b1TsTNqJ_LVVYB{=TZ(!%d_;H<~L(XzVayzMgJ#PeZ>@ z>Be70J%Vr}TWYx0KiHljRtQo0=Y|~o+XBY`a0oPQ;m2ssbqT$vEat)XVZu>Bl!c0X z6>`UqX)cq>pIR0ThA3bEd%qQs~EYKE2)IAOgvT&VKbGERMIn z%QJKyc{J7h^zI7btOSQZ!;6R)5${C26aS~-l)jE4qOHWnI3{lZefbxsS5H*yQA{5p zj23GWF$5{YyDmrJyLPD>!g3(NQVj84i(kcV!k+}*IXFt-i1_>`j#pm?c(FTcff!b| zc%3Wv%~5!2A&a`{R^5yjpp5J(=$nJ*QWvLGMsbI3)4hTCg)x^io&AH~y4U6T(kMSg zMaVRZ1-R^FC=RHH6F^T>DN*79OgmZ+Jh5H2#@D2~1S*-Wg++`ZitI5rgX~Evs{zd{ z4((hcJSXJYoCeB44`k)@FM$~aI|%%@ewJjbE9Fs&$`Tu;z?yd4L2mib2MF~t#W5P;I+38 zf|>fRaHI7q8)xqFjZh%BatbV`*Y;R|(Y#7q#V|Zak}Y^Eim-4t$T|mRXv#-Rt7;FJ zRxR*da5!~S2SV+*+R#Q$V~5Ya;VfG^7m>5AnPG4maF15 z4V353G9ooy#E!INP6`HZaRURm;l;&^i+3*Gxi~EUdswFUONBylMu{P%<^@^*oJi<5 ztT!R4OsO7=l?8ie;>bowFjEddKcpzOkoTbvynRffxsuXmKz$j3M zt&uoZDe6mpQ1at{34}JgK864KKkdw-B83UOqY)VITZvNyxQ_jyBX!)owkC0 z`6D$LfD#w&x@a1eSQ4Rp2bRCDdh!(%)aa{*c_34CHO6K-qwof{h%=ge-5x`Y1q^^( z%dn%e;%nX=P)Y;KzrFWt42oNMIWB-khd`q+`P?>|44nmUtw>4f05SmOumCmRWt@Ns zXwt#*gV#2Sp<1Z#U=g3SSZVi4upke4dvb0~XcEpM15jTKnIAdfdQL`@HA$loQ+T=X ka^c;Dch~b83^#VgekeF{>yMIc=vL#QtRtC*gN0}R0Rv47H~;_u literal 0 HcmV?d00001 diff --git a/barcode/src/test/resources/org/xbib/graphics/barcode/pdf417/micro-basic-ecc-level-7-ignored.properties b/barcode/src/test/resources/org/xbib/graphics/barcode/pdf417/micro-basic-ecc-level-7-ignored.properties new file mode 100755 index 0000000..bd91f93 --- /dev/null +++ b/barcode/src/test/resources/org/xbib/graphics/barcode/pdf417/micro-basic-ecc-level-7-ignored.properties @@ -0,0 +1,3 @@ +mode=MICRO +preferredEccLevel=7 +content=This is just a test. diff --git a/barcode/src/test/resources/org/xbib/graphics/barcode/pdf417/micro-basic.codewords b/barcode/src/test/resources/org/xbib/graphics/barcode/pdf417/micro-basic.codewords new file mode 100755 index 0000000..9dca55e --- /dev/null +++ b/barcode/src/test/resources/org/xbib/graphics/barcode/pdf417/micro-basic.codewords @@ -0,0 +1,6 @@ +221311142322123411133111223112122423231511222213111 +311311221151321414411112123151322112222243113113111 +312211611132212233123112213121421124244211213122111 +222211112141342122242213113121231611212232322222111 +213211331421121213522113122111411342312242212132111 +214111123212241116124113212115411311215311132141111 diff --git a/barcode/src/test/resources/org/xbib/graphics/barcode/pdf417/micro-basic.png b/barcode/src/test/resources/org/xbib/graphics/barcode/pdf417/micro-basic.png new file mode 100644 index 0000000000000000000000000000000000000000..8a60ca842fdb9cdf562a95a7df1cefaa6b0ca759 GIT binary patch literal 10491 zcmeI2e@s(X6vw*|b%NDxj5NTA+5TWZn%O@HHmg~j0%OMDmbp<(>4QR64B`W%uV6ZA zF#Hfv_Q!%o8|QR^WTBMT;V86}vNB*nOTdlxv9?rXu;Ob$=O{`o?A+IX_xEC!o7@oc za^JbP=e~Q+{d~`D{^UqDKQ<|r$K&x2eU$krj~6ow|KEBM{_wKvI&*ovx9o>94}4Zb zwBI>E9^d!M@T6@sr#j>7?7VxQzw=AtrQ@4x*PmQxg!&%$Xq@Wt1BtnrEj4cxi2_Rh z1xg(wG@m{;#&;!nzg()Ga&)r(MJjl~y0A4@74Q9-P^5^n&ROje%gKQc#NCGPbbW>n zRur<6jT3n#V)~};GUcg|PdP4Y1>OO3D@nK}7#UfZC7#L&BseRN*5iS(tMSK$w-XK^ zIAlI=Ng`bL{+v=jzcrT`4?bAGv{RFQ=CpC9rFNJ~cTME`?%vSRZOM64Igkl6#Rco$ z+Y)k=Yy0UpJr^j?P^_$9F8AG4bT5iTdo{-LKEvYgS6G=u72kUb5^FPgQ|DhiY||VY zbL62G+!m_W8C#R{6hc!c^b1TsTNqJ_LVVYB{=TZ(!%d_;H<~L(XzVayzMgJ#PeZ>@ z>Be70J%Vr}TWYx0KiHljRtQo0=Y|~o+XBY`a0oPQ;m2ssbqT$vEat)XVZu>Bl!c0X z6>`UqX)cq>pIR0ThA3bEd%qQs~EYKE2)IAOgvT&VKbGERMIn z%QJKyc{J7h^zI7btOSQZ!;6R)5${C26aS~-l)jE4qOHWnI3{lZefbxsS5H*yQA{5p zj23GWF$5{YyDmrJyLPD>!g3(NQVj84i(kcV!k+}*IXFt-i1_>`j#pm?c(FTcff!b| zc%3Wv%~5!2A&a`{R^5yjpp5J(=$nJ*QWvLGMsbI3)4hTCg)x^io&AH~y4U6T(kMSg zMaVRZ1-R^FC=RHH6F^T>DN*79OgmZ+Jh5H2#@D2~1S*-Wg++`ZitI5rgX~Evs{zd{ z4((hcJSXJYoCeB44`k)@FM$~aI|%%@ewJjbE9Fs&$`Tu;z?yd4L2mib2MF~t#W5P;I+38 zf|>fRaHI7q8)xqFjZh%BatbV`*Y;R|(Y#7q#V|Zak}Y^Eim-4t$T|mRXv#-Rt7;FJ zRxR*da5!~S2SV+*+R#Q$V~5Ya;VfG^7m>5AnPG4maF15 z4V353G9ooy#E!INP6`HZaRURm;l;&^i+3*Gxi~EUdswFUONBylMu{P%<^@^*oJi<5 ztT!R4OsO7=l?8ie;>bowFjEddKcpzOkoTbvynRffxsuXmKz$j3M zt&uoZDe6mpQ1at{34}JgK864KKkdw-B83UOqY)VITZvNyxQ_jyBX!)owkC0 z`6D$LfD#w&x@a1eSQ4Rp2bRCDdh!(%)aa{*c_34CHO6K-qwof{h%=ge-5x`Y1q^^( z%dn%e;%nX=P)Y;KzrFWt42oNMIWB-khd`q+`P?>|44nmUtw>4f05SmOumCmRWt@Ns zXwt#*gV#2Sp<1Z#U=g3SSZVi4upke4dvb0~XcEpM15jTKnIAdfdQL`@HA$loQ+T=X ka^c;Dch~b83^#VgekeF{>yMIc=vL#QtRtC*gN0}R0Rv47H~;_u literal 0 HcmV?d00001 diff --git a/barcode/src/test/resources/org/xbib/graphics/barcode/pdf417/micro-basic.properties b/barcode/src/test/resources/org/xbib/graphics/barcode/pdf417/micro-basic.properties new file mode 100755 index 0000000..aaaa744 --- /dev/null +++ b/barcode/src/test/resources/org/xbib/graphics/barcode/pdf417/micro-basic.properties @@ -0,0 +1,2 @@ +mode=MICRO +content=This is just a test. diff --git a/barcode/src/test/resources/org/xbib/graphics/barcode/pdf417/micro-data-variety.codewords b/barcode/src/test/resources/org/xbib/graphics/barcode/pdf417/micro-data-variety.codewords new file mode 100755 index 0000000..4165dd4 --- /dev/null +++ b/barcode/src/test/resources/org/xbib/graphics/barcode/pdf417/micro-data-variety.codewords @@ -0,0 +1,17 @@ +22211223521211223312312221121 +21311232241221312222322131121 +21221231162112116112232122121 +21222151214211113113252122211 +21213111252312112323142121311 +21212261211222223151122121221 +21211311212343235212112121131 +21121325111223213332212112131 +21112312221162221143222111231 +21113252123211111422512111321 +21114114222114111611152111411 +21123133132113112312612112311 +21122222211144132312322112221 +21131221143141241221322113121 +21132143111115512213122113211 +21141112241331221113522114111 +21231112112325114243112123111 diff --git a/barcode/src/test/resources/org/xbib/graphics/barcode/pdf417/micro-data-variety.png b/barcode/src/test/resources/org/xbib/graphics/barcode/pdf417/micro-data-variety.png new file mode 100644 index 0000000000000000000000000000000000000000..6bb628a348a37dbda426d25c7da780de459afded GIT binary patch literal 16405 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 F{S90}s(b(d literal 0 HcmV?d00001 diff --git a/barcode/src/test/resources/org/xbib/graphics/barcode/pdf417/micro-structured-append-2-of-3.properties b/barcode/src/test/resources/org/xbib/graphics/barcode/pdf417/micro-structured-append-2-of-3.properties new file mode 100755 index 0000000..4b81899 --- /dev/null +++ b/barcode/src/test/resources/org/xbib/graphics/barcode/pdf417/micro-structured-append-2-of-3.properties @@ -0,0 +1,6 @@ +mode=MICRO +variant=18 +structuredAppendFileId=123 +structuredAppendPosition=2 +structuredAppendTotal=3 +content=is a diff --git a/barcode/src/test/resources/org/xbib/graphics/barcode/pdf417/micro-structured-append-3-of-3.codewords b/barcode/src/test/resources/org/xbib/graphics/barcode/pdf417/micro-structured-append-3-of-3.codewords new file mode 100755 index 0000000..52cf6cb --- /dev/null +++ b/barcode/src/test/resources/org/xbib/graphics/barcode/pdf417/micro-structured-append-3-of-3.codewords @@ -0,0 +1,15 @@ +2131121423221211231214212214111611152131121 +2122122313412111232112621221211244212122121 +2122211113316111142121151151245111122122211 +2121311423221211133114232212142322122121311 +2121221161112411132211611124116111242121221 +2121132352121111123223521211235212112121131 +2112131423221211122314232212142322122112131 +2111231161112411113311611124116111242111231 +2111322141152111112411423132511431112111321 +2111412213213311121412324122141232312111411 +2112313212332111211411235113611113312112311 +2112221123143212111423331131121112452112221 +2113123312133112112315122123212213152113121 +2113213214231112113211322161623112112113211 +2114116111322111213221331232132123412114111 diff --git a/barcode/src/test/resources/org/xbib/graphics/barcode/pdf417/micro-structured-append-3-of-3.png b/barcode/src/test/resources/org/xbib/graphics/barcode/pdf417/micro-structured-append-3-of-3.png new file mode 100644 index 0000000000000000000000000000000000000000..da1bed4f95e1408102fad4f03656d91119004ccb GIT binary patch literal 20470 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 literal 0 HcmV?d00001 diff --git a/barcode/src/test/resources/org/xbib/graphics/barcode/pdf417/micro-structured-append-3-of-3.properties b/barcode/src/test/resources/org/xbib/graphics/barcode/pdf417/micro-structured-append-3-of-3.properties new file mode 100755 index 0000000..8c88464 --- /dev/null +++ b/barcode/src/test/resources/org/xbib/graphics/barcode/pdf417/micro-structured-append-3-of-3.properties @@ -0,0 +1,6 @@ +mode=MICRO +variant=18 +structuredAppendFileId=123 +structuredAppendPosition=3 +structuredAppendTotal=3 +content=test diff --git a/barcode/src/test/resources/org/xbib/graphics/barcode/pdf417/micro-utf-8.codewords b/barcode/src/test/resources/org/xbib/graphics/barcode/pdf417/micro-utf-8.codewords new file mode 100755 index 0000000..4328727 --- /dev/null +++ b/barcode/src/test/resources/org/xbib/graphics/barcode/pdf417/micro-utf-8.codewords @@ -0,0 +1,26 @@ +2213112333221114211131113251124141134112111 +3113111112451213311121611132121244124111211 +3122116111241113221141215112112323414111121 +2222113324112113131112151412212122253211121 +2132111312522112231126113211612112223121121 +2141112122124312321111161241121212443112121 +2231111423221212411115123131151115123112211 +3131111221541111511121143411116112233111311 +3221114121116111421123116112115131233111221 +4121112111213611412114232212151115123111131 +4211111221541112312121143312121142152211131 +3311113111116312311221131153111123532211221 +2411112113314212221233311411112321162211311 +2321115111223212222141123231411113152212211 +2312111434112112132123521211235212112221211 +3212111423221212141111215142211123343121211 +4112115422111111241152211114211152323211211 +4111211122143311331131231151162111322311211 +4111121142512111322113141412413311222311121 +3211126131111311321232112314451212112221121 +3121121231213411312213411511541131112131121 +3112125221114112212221423113111116152122121 +3112214123211313112221214115322131142122211 +3111312422114113111312221531111232612121311 +3111224232112212211312131315231131422121221 +3111135112223111311331223411512311132121131 diff --git a/barcode/src/test/resources/org/xbib/graphics/barcode/pdf417/micro-utf-8.png b/barcode/src/test/resources/org/xbib/graphics/barcode/pdf417/micro-utf-8.png new file mode 100644 index 0000000000000000000000000000000000000000..e6a3e6e8a0a67579980956be918dc1457a76f931 GIT binary patch literal 35457 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 F`!D^@Dpvpi literal 0 HcmV?d00001 diff --git a/barcode/src/test/resources/org/xbib/graphics/barcode/pdf417/micro-utf-8.properties b/barcode/src/test/resources/org/xbib/graphics/barcode/pdf417/micro-utf-8.properties new file mode 100755 index 0000000..8b139ee --- /dev/null +++ b/barcode/src/test/resources/org/xbib/graphics/barcode/pdf417/micro-utf-8.properties @@ -0,0 +1,2 @@ +mode=MICRO +content=12üƒœ˜Ë±‹3asdf23456789012asdf89012asdf89asdfaf2 diff --git a/barcode/src/test/resources/org/xbib/graphics/barcode/pdf417/micro-variant-01.codewords b/barcode/src/test/resources/org/xbib/graphics/barcode/pdf417/micro-variant-01.codewords new file mode 100755 index 0000000..6dd72b5 --- /dev/null +++ b/barcode/src/test/resources/org/xbib/graphics/barcode/pdf417/micro-variant-01.codewords @@ -0,0 +1,11 @@ +221311142322123221111 +311311611111334121111 +312211111222534211111 +222211211511243311111 +213211421231312411111 +214111114311152321111 +223111321412312312111 +313111412132223212111 +322111245111124112111 +412111221222334111211 +421111211231164111121 diff --git a/barcode/src/test/resources/org/xbib/graphics/barcode/pdf417/micro-variant-01.png b/barcode/src/test/resources/org/xbib/graphics/barcode/pdf417/micro-variant-01.png new file mode 100644 index 0000000000000000000000000000000000000000..af7cb2ed3f966faedba659209891eea2066ebbdf GIT binary patch literal 7209 zcmeI1;ZIv-6o*k9bE6w3Q@U!(+!nT&QBWGJp;llW&dD~7GqJQq=*C)-Tc|X0=jaA9 z1)NOAFu8;x2%3>BDc3^jrL_ax5Gi(rqUN@x47LHv?5)$S)U6xfdG${)F*^H!FYis> zoSdAV_xC)Ue0Bfsb!#7A8x<9`ZqG}*UWz-X%ufOwV z*QG2{En6Esk+HJpjgw>R6vMx5JKLwHt`4qzy|PQA5IWM68(Mhot%BJ}z5MhP3*#MN zO~^cBJ@v73O2?wzX`!~k*CDCnQE@PyG8twcw0gB7EuZWfg|9|Bb@-9~>Ohj;l37B* z1*b3!eKnZ(J`=e<(wu){auS&LFL~;jBcXXc2jAw-4_BKBk!MYFH`jZEaisB?z2smp z7USuR8&VGmduF^%n9|cMue{EtrJ;iDh2P+XgQgVsB_rRu{9TH{%v{)SKNYZ7sPf5X zg-W}0HeZ5y-)n8)_|7bi4aoUJV{_r0K_lI>TDX}{ZN1xi+^Vc;9bIgyP;?!R!*?px zyF~Ru|H+$qSO&7@ITwZ+KQdA*cY6lGc9LY`tBL$!#UIIFkQ&?N|7Rv7R}T0 zwYP{(l)Q4(rYIgy2(RH8`jze3B`=mt-y98E+yWm zF^hBy+k1^6eO0`bV?FAKlaYgSmyMJvZt4Aa`c1xTi(sojb4TG=Y&!o09b!5@bzPjq zVx)7*xgU8MYU^_2k)g>pQE-D)qH|`}O`V!LKVQpp=ie-m_)L+a|7V?sw-!dO#$0G5 zm~Tr)Gkx=y3BMB;9blokRq!~5L|cz6)rBAJn=d!zjXx@eIZ{e;Vz5z?Ay~b6)5X?l zwVOZu2n-{KZoDAv*2SGO4dLeI8&wQ~jJ? zxEdQxPkyV|Iq03h@d2-gH?Ni{ z;N_02BD?Mggp*^*<(;G$HxdwD-SoW{Zn26jI^f8=S~1t(#I?6KHAY*iZ&u>P$~Q6-csOi$_=j z*wI>B$z0Qy$v66P@nN3UG+!>kn$zP}Ji}>z%hQyJ`A~+y2Ee5(VY#~wqe~TPmG`ke zyOZ!ke*5|PdxzL=!|X*spdBS=0F3~BemHu2B$><|sqSt){KnK4fsCv*6X$OIdOA;T zs*gP+bdZ*8pr1H^r%0jqS4YuPX=*)O!!5_ctGccWrQaW+8=tDNJC#BQOBkesv2fDc zdBc#19$P0)aPWn$>ufkF+BcJ%)46erpe2yRXI!%HQ{ZGmKVTxzd-|*|SLx~K>&$oQ z2R=_buHrQx{9sB=nJyW{ntg-qX~%bC;V~(|wbiRuH7jbT^%YrIxSkHSnhQVNtTlPZ zQis;}r!AeJgIp*J8;ulte|5B{)_)STN;q3{VLeJ^XuVZvMia>GAYuIM{#8Ph;m|6( z-Jhtlw86e2^MmnB10+7Qwq}1Pn5*Rng^n*PCS*o24*q0bnFNmjz@(R8bC_z!u;@V0#kcTihp4 zBBzK*rR8#n1);40BHZfz4K45nw+C+i;w;g{Kyb4c@VwF6|MZMNFTX4MM9~o55a(2F z0D{3dMYi)gldr9cGR3A!azbCNLGBR|O~1EzJk#f01?;1XlzX3d*RV+jqflD>*Xje8 z;;BBo5D;fjbgJvxByuS-vQJ*w9qVxUYAe(*V#8)+X(+kIx_ppLbo9k7d0-stFr-q{ z)@kk1aB`AZvnkvSqZ{u=lE$m;pAXmNNouBF2p)nl=aESNK)!S=vTa6I|M7(p_(@3R z5HHbI2K$36IOTU;r@LStEflgpgdY?Qb1^j->WCElf9UX)jdTb$V-=13^s(!}v+W`W z8_eAWX{zgS@X7TlfUB%t)t!S9A0LE_6bb6z&H{~Ag_81Fm<~2*CWeIaz}vvSuD1C) z5RC;qPSHnTs7IOrZDS@)$DT0+V9!N&A?1DhJ0RCg9gzZ(5jJgm8YpURe!6XeQ#5{a w7!;!sj1O|&vpIml(Sh87NRj{fPTs9ddFS1lnQrk2`XAt)?ESmQodqZU00ERU&Hw-a literal 0 HcmV?d00001 diff --git a/barcode/src/test/resources/org/xbib/graphics/barcode/pdf417/micro-variant-01.properties b/barcode/src/test/resources/org/xbib/graphics/barcode/pdf417/micro-variant-01.properties new file mode 100755 index 0000000..8ae7d73 --- /dev/null +++ b/barcode/src/test/resources/org/xbib/graphics/barcode/pdf417/micro-variant-01.properties @@ -0,0 +1,16 @@ +mode=MICRO +dataColumns=1 +content=ABCDEF + +mode=MICRO +rows=11 +content=ABCDEF + +mode=MICRO +dataColumns=1 +rows=11 +content=ABCDEF + +mode=MICRO +variant=1 +content=ABCDEF diff --git a/barcode/src/test/resources/org/xbib/graphics/barcode/pdf417/micro-variant-02.codewords b/barcode/src/test/resources/org/xbib/graphics/barcode/pdf417/micro-variant-02.codewords new file mode 100755 index 0000000..f6b90a0 --- /dev/null +++ b/barcode/src/test/resources/org/xbib/graphics/barcode/pdf417/micro-variant-02.codewords @@ -0,0 +1,14 @@ +313111116111243131111 +322111311111633221111 +412111111232344121111 +421111511312224211111 +331111311111633311111 +241111111232342411111 +232111511312222321111 +231211311351122312111 +321211232215113212111 +411211215211414112111 +411121125322114111211 +411112222411144111121 +321112121344113211121 +312112163112213121121 diff --git a/barcode/src/test/resources/org/xbib/graphics/barcode/pdf417/micro-variant-02.png b/barcode/src/test/resources/org/xbib/graphics/barcode/pdf417/micro-variant-02.png new file mode 100644 index 0000000000000000000000000000000000000000..f5060c3d334f4b58e32074e33b11d6789f0bd3ee GIT binary patch literal 9045 zcmeHNZA?>V6lUqfsa1YxC{>F_0?x!Mp`9zHDT4tr2f{MU0xb+$+CtJ&1jl838Zbf- zWCE@Z3J!FUECEuWAL}Y8Lxc_rMVHdj7GxHKqqS-ZC=9oA^#}Y}vL!Ct*iA#4bMwB> zdpPfNbDrm<`BrC`~yF{Zu`go3q4EHT5mrLd|x@ zGMAjDxz1SW?6Pz6xtjEa#=;JwJaMwI$P~XIV|G~IT^{L)bDucmjJ{z%m~Xsn6iW+J zlMWKnFXgl(p%SJj22bZ-QJtHcvS_V}VeEbeNVRjZ?GfTqg@k#s#6lDzG>jon`fW=W z=c=WgHoiy0UV0yEs1hSZL4Z1FCeJ2_6PgJ72X!(7H3fJ;Ns4NZQG2V&2G#-&jThuK zGDNsyoN$uUjsruZ)je8`vqXq>j%TZ2sPsU8hp=J_=}J4QJN zP53s-T+^^?9eE2k%R3TBSG)BRPjeZE`=8gYj%zga`>*Mv)zV#>S^TL6ufr={^^epc zEsNQzSkqB`=q(8zLL`=WyuC~zS?#~jfiKR1?mO44otGIpS6g7Er2&J83V28DZD6fw znAOr)4B|kk-#A#8)%FSv_D6uA_UmnV;9*vq@lhxPKwR^x$1+&cx(9U!Fo+$YwO9tL zjh?JNG6N8GT>PKF#0Y*9e?X_6F3GU5YFLI*O23cZ0x{6FFcqompww|xpz)f*#nD*I zOQ;0j9v7WeC2jSAjvlvip6wibJ;kIX}SuK6KDsI7Di5;gEP5Rj}> zzd*br^lpx!hHS1Tqd-)5mhHh$5FaSL=4|z)DSc>mcoB#oNWX;`Ub3n9NP#FqV1yTq z3^FqoV&FfX@cOSXOnRJf=a{QLn!x}^VSn81n}6@kDd1>*!WWjxNfaE7aXLQ|<~cn; z(Zkqur`rKGMb@_{u@`SI$j}B0vzS@5JTurNPMH${E&6P-rr2PE@sLZ|#isUcn__ov zj0R*vn|6LsH=CZS;+0YW&ZFU8KG3rk=1XJd5wVuY066c!-bG-i-?$I`s;bdiLq-7( zPjsDCRES2o3$=Q46+14{u%g>QZuMn%rBLG{OD9pw53J5d8HWaw5mP;-wwFb9Derq4 zYO+tC3V}Ta`&UFmO|y!kl1W;ZrCVT+QZT@iNr>H!@6IZC)~SRGy{yi|48Jo9-fa2+ z(&{_+;z0@QX)^`DF`*d@F$alXq?2HeNLQszM#y>Q%q>Fg_czpgVGo|#f2V34e7JWx z`}y?C88s50N|H3IJWvtbGr{pmQQ*r7WTe4#mOtw_Ltz7bf;C_60f`4Q>Y8knL(W@H z9*_XV^x~PaSL1J9V`5zCWRMYu#tZT~8M25eF6?tYG0u=VW(pq-)(8fepxJ2Hyf1|$ z$xiqT9i!>nO_`&h`Ylj*7w&tC5e}E=xB(**vlFUUcOKU_V#h)7Bq3vYTQuA*(gK^j!^GyLZr_hF=prH zq>w#wM0!4?NE>titer+UT}MYywEp5V60X?vE+ uK_@yqlfXp>S4>_3PYk@rlUxUt+%rQ{W|q9I_z?TO%(3w3u=e+pOMVB9>)<{B literal 0 HcmV?d00001 diff --git a/barcode/src/test/resources/org/xbib/graphics/barcode/pdf417/micro-variant-02.properties b/barcode/src/test/resources/org/xbib/graphics/barcode/pdf417/micro-variant-02.properties new file mode 100755 index 0000000..7dd4840 --- /dev/null +++ b/barcode/src/test/resources/org/xbib/graphics/barcode/pdf417/micro-variant-02.properties @@ -0,0 +1,3 @@ +mode=MICRO +dataColumns=1 +content=ABCDEFABCDEF diff --git a/barcode/src/test/resources/org/xbib/graphics/barcode/pdf417/micro-variant-03.codewords b/barcode/src/test/resources/org/xbib/graphics/barcode/pdf417/micro-variant-03.codewords new file mode 100755 index 0000000..abc7f2e --- /dev/null +++ b/barcode/src/test/resources/org/xbib/graphics/barcode/pdf417/micro-variant-03.codewords @@ -0,0 +1,17 @@ +222112235212112221121 +213112411111442131121 +212212211161412122121 +212221111513412122211 +212131411111442121311 +212122211161412121221 +212113111513412121131 +211213411111442112131 +211123211161412111231 +211132111513412111321 +211141111133342111411 +211231411213142112311 +211222114132322112221 +211312221611222113121 +211321121251232113211 +211411316111132114111 +212311223125112123111 diff --git a/barcode/src/test/resources/org/xbib/graphics/barcode/pdf417/micro-variant-03.png b/barcode/src/test/resources/org/xbib/graphics/barcode/pdf417/micro-variant-03.png new file mode 100644 index 0000000000000000000000000000000000000000..cf1e1794ea68c6be516d45ac9381f88784ac5e7e GIT binary patch literal 10610 zcmeHN{ZCux6{gFQuEoHlRz@_n7+Ijowi~pd;DJ?0;xLE^FpZEJ8<+2o)POT*wi}3p zN*1ii8goKwW77aOpnb|!(?fEF-}&+NST`VZJLbxU-A z@GHI6Bj4lmo_n76oag-UH>rsMZ@>4ppPygAv81EF_49k<9DKHZ1Yh{IFFot=^V=75 z?C9YWpMIdH0>VSo1Y z1NwdTfssA*F9e(1^S@fyMj>p*a9Vk-q2PK#dEE4xdE>N$gHQ;2`|1}{@ALW&$)?Wr zd&H~X-Zdf=!e+$8n_}?GpHWS#S6mCvyU+3w3SsXE&Tkuu2^RN_Kc1E@IuHtBPfl-; ze~3Jx|2X7aBcg)qbMCyM*!}Hy8J3H4y@&4LO|VYddj2BJ`u$mPh5zKG&rYhDu(q|z z4!XXcc|N~HZTsmCHCqa6S#dBA|J}oB`z%(PxQlHFEKA%qF}xpF{r#^4=0_ZBEk*wc zZ35Ott}63>LcO@zsHKIJjK%6QX~~*@KfV z%&dWf6r8D8XNHaf`%PR^;~Wte4%7K$X;~U~1M%L69JSHi4hPyhG&>grF#(KM*~aGE zgm9>r8XirCLRK8IkJ)q1LHen4wviM0d=Z6wQtD}`2$ieV*~YU^fC z`}zSlhSPj}_F^>8E(uwtx&nQqQguo<-$ALA3Ew2BXSh?(5@**~dfG1#1obqVtujY; zJM!#Z1dT05Vz|AYGzZc5|ATkn)qbDgiH#K5)F0xqAfXUM@ExD+j9B^>vc!FYYN{UX zp*IDMqa5Ppl7BW(O>arQhJ>Rd$l|fSH)W<9vPdmKM_acN%8!I

%8P%|Gav;GMP8 zkdX2TM(czuHzFlX`Cod4om^B}OmM)8Vo;kdDZ!Mw#zHS0LCw{j>S;0*d?QkLiRV*( zAB8a{#V?0UW_l^+J4<+!`$Q=DL$9fiY;uZar`cDJK$%qGFY2!;n8B<7bxDBsO~EP2=4MR41T%EnA2RE1aPje5`xZvWhbMM ziQusW%|Lw*&)l~g!N+~2l`0&bnM#_-c*VUqaEK?65JUk5&-H_*z-T>1A2IHuAe_C6 zUFXVSl0ecuFA)?J23&HsD;_A5qulZah$!+q(g!aa2S8F$-NVkcbWK&A7;;wo>b)8T z;>ViCK2LMji--ld7Yi3J24INyD2zH6G{RFHG#=)9;=h}X1D}P6BF3Qy-f)+Jv+<$e zXuS%kG|GKk6F_iW6xK1EEerNNHyT@02DXC6JYjtAl|}(@b%M z{uX0;D|)Y277Bs=C~nZ}Q`Oe7`x#6D+)63tG^Z^(kfJ~{s#(~ z2tC;Dawk7PF^b!EDqF%3y2vhEzFAyDQ-#9=kK5!3-8vt;J{pCh79M!3Gzpd7#J~Is}&U*3s@Z3!sx>{ zFdYNaWggmGe|ev_4Yn}<@F$qufx)t8jET?fkSJk$B6RdU7~O%HvcHM-KR1VWPr&x8 zxElvyb_d4EzFH{xNCX)Xw&{{29vb0+EA8Wl8n=SNgYB&~IgVy{;0oOFCD#TS6}HDo fLtihCuE)RYJo3wXx8fvdnDSUc>QT*+oa%o8kH#fA literal 0 HcmV?d00001 diff --git a/barcode/src/test/resources/org/xbib/graphics/barcode/pdf417/micro-variant-03.properties b/barcode/src/test/resources/org/xbib/graphics/barcode/pdf417/micro-variant-03.properties new file mode 100755 index 0000000..a646f44 --- /dev/null +++ b/barcode/src/test/resources/org/xbib/graphics/barcode/pdf417/micro-variant-03.properties @@ -0,0 +1,3 @@ +mode=MICRO +dataColumns=1 +content=ABCDEFABCDEFABCDEF diff --git a/barcode/src/test/resources/org/xbib/graphics/barcode/pdf417/micro-variant-04.codewords b/barcode/src/test/resources/org/xbib/graphics/barcode/pdf417/micro-variant-04.codewords new file mode 100755 index 0000000..63e2297 --- /dev/null +++ b/barcode/src/test/resources/org/xbib/graphics/barcode/pdf417/micro-variant-04.codewords @@ -0,0 +1,20 @@ +411112142322124111121 +321112611111333211121 +312112111222533121121 +311212211511243112121 +311221611111333112211 +311131111222533111311 +311122211511243111221 +311113611111333111131 +221113111222532211131 +221122211511242211221 +221131611111332211311 +221221111222532212211 +222121242313112221211 +312121212241143121211 +321121223213313211211 +231121424121212311211 +231112331231222311121 +222112115331212221121 +213112132121162131121 +212212141251212122121 diff --git a/barcode/src/test/resources/org/xbib/graphics/barcode/pdf417/micro-variant-04.png b/barcode/src/test/resources/org/xbib/graphics/barcode/pdf417/micro-variant-04.png new file mode 100644 index 0000000000000000000000000000000000000000..c468ea092510f1c957480dd4df3a219587d8a278 GIT binary patch literal 12930 zcmeHOYfO`A7G@=|J5qHMGGN_dy%5Gktr)sa<8(k6U|7PyY<7oRTSOFENP!MDmf>p9 z5!V43!O&f-An1n43avn)rIx}f4wg%~44Ia;T%v-Yw6MxW0de-6P8mBtHrZq{$r8U` zZ@%{Z-tTbweCM3!IfwIq3Jcn_aoa{aJG)K6e+>Auo!zgp?d�{RTd;3y9uOV`t|T z930^H>E}mO8owg{Ad>yS@b}-o)fgNdP!^kiAtR*Z$*ak$*H3+)uIXpCJMg6wnd5Ah zv3Gx_qk&4FA!l-%eN$zuuZ9b`MtxwPZx;KWw?A)_t6C$RE*7Pw(3Ml<#6D9qXG(m3 zgF?^u=2Imp99M69bD7(7lIkF92aPESO6_Peo33#xy`>K;s^s}jhMTa;Ve!Y5^;e{_n zv8Y(YS-K;f7SGmt0M2udQOF2wwMMOxX2*3eXw$VNd5eCB>xCqE0~q7%@ki0EbrYG> zbZ)2b3SmCs{U5<&Pv63*Mn=Gjj2+O8t&Xz~9 zlXcYBNmXRtN7aou(d&DLB%Iedm}ZP-OO&pHV`cR*eKsjuChcLRdz$Wg9amP^WcXxG znPTS1(D{T8fwxUTpzrlMF`}w-?k+TE+9Y6UdBUSxS#i6CWaENOzGl2oR>=7$P~2qAwjO$&+;}0nC8DjP9^~h_+e}WdqZzUyvaO zzyY**`tw*$=G)}+Xn@}%3cVgrs}q{KQagQ(6C;2)cw9@uZcDYQEs4=XL8@#qCUfv{ za)3FU*3M|ytMsr>`CB@?EAY5EVR;mS*`_im$H$E}IOA!A@o&86|G~*0nfx)IPnX3# z|5sQhdnl)OEg>r_$bu~tyCL=s@AJLY$6+uthYo%E0+&~C@drk zh(Cq+RbJPe{|mr6f09ZsU|NMwz5wVE-w-}{!*I}ZQfKAu7=lZH$K7nJu@)E@;5&gg z>BhpXA^^tR_(!)^rf2t9i9Nn>z{FM;($^FPET)07y;@eTsRURT1GhW18nY%(V2KMP zu(Re*3De(juk)$7gMthiUz#HY(UVVYO^Q7bklMim ziTR^c84pFz)D9W|QLT9q3UpnqS=4~&30KtsS_Qc+N!ju{g}zk}qNjp9*66u)7wsVp zh`Kw?NCVLmu0-%MjI&=>gML5GUSWgiInK@=1oRCTj)A^#VK{lr1fu6~A5(fO@9{AEZDkPs< zfO9b_2m0L7W^GB8ocNG%$S(>8&#l{x#g-whN9DOsMcSbv64X=uxZpI`?0mskV}~Ff ziz!@i>?`Bz*q7A@W$yk^N&NvAoDZX~KA?%~-N;ZzRpEkzWTTH=+mbGr)%{RI;er!q zb75^SLb=I7`A|S5;(}9O>C4~n7{}>eSoqp1Fe-+XxfazZan$5ej25*AM0d z;q**0YHxccu^l1qgfW-K>RCuv#UkvAL`-Bud#&;yOx+Y|=iP8PM~YpM8&`OE*Sj!9 zGjO@}Iu6^shjL))9NwiJhp8CGU^-+$wFbK)HT+h*>oN@K1aX&(aF$1pf@63ubOs$6 zmtg27F?0-)qC3rEM%NPF#f2FlH}PXSii~VDBmB@9-bEPy#(Ul(NfkKrOdJoFmF*Zp!4>P1^7C^CCqpM=8=s% zLffzzfj?uwt{r0DB90uT=--wj@MD;kZGd@{KgiHFe1O1LLfL@wVh!I11&%Y&HXKIa zr>9u90pwBKw(;f0`gcai;-Mpcyf`3GH>Ud`|5JwU&k0Q@T27DN0dgCztHQKi*FH(nPk!#8It$}B zH4=X;D2|@%V>a|X-I(bF13eKj=dez=z4@v<{_4ZG=Ure#NPKH^3Z(zjZa%zDpL=_! zC^*kU7`SddxUiGbniSersd^%BOG--#-~R0bk#^q6P?NhNa?wQ)U#~N_EFPXI?(?s@ T8RLfkToN1@79jcX=;{9eriS?Q literal 0 HcmV?d00001 diff --git a/barcode/src/test/resources/org/xbib/graphics/barcode/pdf417/micro-variant-04.properties b/barcode/src/test/resources/org/xbib/graphics/barcode/pdf417/micro-variant-04.properties new file mode 100755 index 0000000..d0e533a --- /dev/null +++ b/barcode/src/test/resources/org/xbib/graphics/barcode/pdf417/micro-variant-04.properties @@ -0,0 +1,3 @@ +mode=MICRO +dataColumns=1 +content=ABCDEFABCDEFABCDEFABCD diff --git a/barcode/src/test/resources/org/xbib/graphics/barcode/pdf417/micro-variant-05.codewords b/barcode/src/test/resources/org/xbib/graphics/barcode/pdf417/micro-variant-05.codewords new file mode 100755 index 0000000..34ed307 --- /dev/null +++ b/barcode/src/test/resources/org/xbib/graphics/barcode/pdf417/micro-variant-05.codewords @@ -0,0 +1,24 @@ +322111235212114112111 +412111411111444111211 +421111211161414111121 +331111111513413211121 +241111412114313121121 +232111121142153112121 +231211311111633112211 +321211111232343111311 +411211511312223111221 +411121112122443111131 +411112221312242211131 +321112611111332211221 +312112111222532211311 +311212211511242212211 +311221111452212221211 +311131112232513121211 +311122112332233211211 +311113331122142311211 +221113312211522311121 +221122314321122221121 +221131421123222131121 +221221114115132122121 +222121131213152122211 +312121141242122121311 diff --git a/barcode/src/test/resources/org/xbib/graphics/barcode/pdf417/micro-variant-05.png b/barcode/src/test/resources/org/xbib/graphics/barcode/pdf417/micro-variant-05.png new file mode 100644 index 0000000000000000000000000000000000000000..580113a240b1c0a15b717c7af9890881ae3ab68a GIT binary patch literal 15652 zcmeHOZBSEZ7G|YtEvfu)3dRmlrNHPm;F7gbBLY>l4vIT&|m4MxI!tDOqA02m`apnGU z<{I9-Irn+bd7kH-_skzsw!iz%ig*0{{NCO1>DJHu{C;!F&u@YCZFt~E+&&lT=eKEe z$JWF@esNIMnONuil;+>w`pfm~+P$~`{Cm}1uja$mv)<2Vqohowmev^AGcaaol9)E? zKloD14_luoF3~EAY(vxB!sSzp{n^2MwZi2eWwfYmvDQmzr_F(btr4THVw$b8;PG3{ zbQ@<8r)pI4oY$x0Up`RL&Z!Tq`G95Mm=grjSw?)BSD|bOh)qqNELt{{l`5bg*;Uu( zEImF@c~X|RNMPE0thkRRVEC&{6S|qT9&UYCv@xAwUz}1=%~NI8u4{gw2)kd9Y_l(< zDY*EZIWi=$ezA152TIL8U@b}r;^N6fxX2WbNa+XrnSx~=ZU)0Rhd(swi!!OcYQ!!rUYy^)6l&F@z}zqD9Wi#>q~d_CC4DiJ9(U=cO?J-(p&;K} zM;=rIW{M`agkBL&B@^0~%pOrpZ#z^o@5;d4gg1hG)L%bJ1iJtwwX+qaQRHl;NF=eJ zBm-nvAPbF#DtDoYAat(V?uBfuckx3{OtLmUm*mPElf@xebE$2;*3r6I)25MMIXQcm zQ(qqNjKvn15^hOf+>g$)O#}!`8>2f1T*?T4Rmb!9^&|UxVw4f1O>0j#G$?3|Ykrcr zu4Nx+Q!A8%cV{eJoLDQh*u0F{>K?jy+~=G*H;VOzvH!usJo30e zxV=PlOIp}5p(e5sFbi7TP3wqo1F?5QyfY6=Q?l)NQNOHj1$mZ7-mLjn8mDvv3m$Ds zkg&yHyNznm-mcxvp;*-_Po~xCLUi|K{9a(d7Y)@S& z7DRqX@QCPvN)tjS0KwEY-<)W+HYhrK#np~MxoN^Ajtz*JU>1DSS0kN`e40WubP96(e7=tIt*gRQwszhV%zueF@EC)6Rj1t61SI0QVwTt-ag?29?55d9n4o zB=I&epqmD(6^B$oxo7Ajm<8MAePw+$!fZOMwvZhOHJ2+(&YCU{YNj`D0DN0lu>xc} zp*|BXMdx@3L&}t%0D#vfiG!$(&*z8}U{92Y4?%7kRp@2XT;KnR#-|p#4J*Uvt4A zwv^{%f*lXS3<_u9EVckj9G60-Fu{&6FoWh+<%DB*0Z#_C@%b8Y&z8`o=-kD5>n$`k zGCv*zQT%!Cdi2+(T;`9NuARV2OSGVa#0Q;E(46aUh#@=GOdz0iTP5H!_k1b#sq(v9 zg-k;iaLugLWB}msUTSeFEUyYJ6Uzi9G>?%wwmM7q%z+fd%}A835{+ggkEhImH$Xkn z1SZfo*4@eoG^LR~K39aL%;6U#&ed-LuW$^IKt5x{*MNp%DkDJUY>C$>fJ$I89UA5q zu6O#N4a%&v@GygpCj{BAZsu4AC@Hl9#o6o+oLF?Lk^f4bKRM?+QmD_S&uIC0c#r`@ zr+78!DFX*3+Bo$?s5kO!pe6gHL@m0zN0tC%B3TGjIZp=pi>V3VhBvah5}*t8Lvy&TWM z-%#OC^VzZ1Yr2vE>oflEXCYW-x2DaA6Eb_Sr%%&aG-h&;^E@WjJQf zBM-wMEVZTAFiKLU2j{9fs7=O5j~WYk#t6CG|lnJTD1~ zeG^r1opdx9+ejk$ArAH}hu}8q^g?JCWspB!2*TcHv&3|}FcI29y~NMg?_vKGu9emu p!uAirC_Jix)+M#fn-Uws8x|>UkXP(H$iwT;JCahi)_sy*{4X82I5YqN literal 0 HcmV?d00001 diff --git a/barcode/src/test/resources/org/xbib/graphics/barcode/pdf417/micro-variant-05.properties b/barcode/src/test/resources/org/xbib/graphics/barcode/pdf417/micro-variant-05.properties new file mode 100755 index 0000000..743ccf4 --- /dev/null +++ b/barcode/src/test/resources/org/xbib/graphics/barcode/pdf417/micro-variant-05.properties @@ -0,0 +1,3 @@ +mode=MICRO +dataColumns=1 +content=ABCDEFGHIJABCDEFGHIJABCDEFGHIJ diff --git a/barcode/src/test/resources/org/xbib/graphics/barcode/pdf417/micro-variant-06.codewords b/barcode/src/test/resources/org/xbib/graphics/barcode/pdf417/micro-variant-06.codewords new file mode 100755 index 0000000..846384f --- /dev/null +++ b/barcode/src/test/resources/org/xbib/graphics/barcode/pdf417/micro-variant-06.codewords @@ -0,0 +1,28 @@ +311122142322123211211 +311113611111332311211 +221113111222532311121 +221122211511242221121 +221131111452212131121 +221221112232512122121 +222121411111442122211 +312121211161412121311 +321121111513412121221 +231121412114312121131 +231112121142152112131 +222112311111632111231 +213112111232342111321 +212212511312222111411 +212221112122442112311 +212131221312242112221 +212122611111332113121 +212113111222532113211 +211213211511242114111 +211123111452212123111 +211132124215112213111 +211141141331313113111 +211231412124123122111 +211222135222112222111 +211312121224232132111 +211321511114222141111 +211411142211332231111 +212311333112133131111 diff --git a/barcode/src/test/resources/org/xbib/graphics/barcode/pdf417/micro-variant-06.png b/barcode/src/test/resources/org/xbib/graphics/barcode/pdf417/micro-variant-06.png new file mode 100644 index 0000000000000000000000000000000000000000..dbfe35f122f9179d0cecd128ff479ca57190d217 GIT binary patch literal 18187 zcmeI4dr(t%7RT8-y1ONo-EoI3bBz*!`E)aZ_J^^Yws$fT=52#I6kp_}R9AfJrNU0S^MY9|rCW z2>8>@m9b0T-;#2nb*Tm``1Na@RQ|2qpB;BCmwctJ$uEo<$sgxF8F`9N;Zv3m3tS6_ z7vxN{TD%L+gf%D*R3OQ*e^0(UyVy`dF^H)x1rs;Ajc!-MN;eS&Fw6* z!~Q8IwUwvDDXV00h0eEm_Hxn4dt-lXWASbc+;1y$U(*UCZ}GU);*5=&a#2Q3sA;P{ zGz33Qi8BhFeN@YQF14!mSo9iVHrhYTB42q+I+pzQ3cqEKd)_2Y&NS23+9k+E0x>Ny zr*mdl_)=L$$fnMU`)#>Tx=+%4eK3W8$D_4Gro#}GD^}?m2wFqa*dBS~R8^5d9pZo? z-Z?p*l>9CIG`s48^}`F#7+P&GLH`J_&cu!e=2e(q z9b8DPz;;Bct5HXs;h=w1{5`HMKOjcFIS!^PweRFf?x)GIvvEg%qF6q zIMtUahY$mNGaXte@1_WZf;*mi0%92Ou_$)AU{uy}G!Rz^1xXlk{3xPuIgHj|D+Dgg zGJi7GL{9EavR*o!Ex6lgSguSn=D!=OyjNkdHO_a`+eX7RaDtFecF^9eDO>~o&XS!e z@2s%7XRE4q|NNNipi)LAg+t$F@SO>m0xQE7NWzKb>ur^`s4u^~C)I$`@3~Eb$e# zqQJ1`BPZW?uHz!5Qu2k_`Bd3PqdITU6ESwLBG~GjIra~0jpWeS;}-M7>c*^*tBW6H zVfMboPxTSz&6&<8I_B#ysp&qy#E(oO>rM}FNcqOc)ytJB{S9zcunbcDIV}nY`{L%# zicd%1JrlOWz$4GQ^bTL65>Bx%7PGMCRCl{ZW2A_@Zz|xpIvE7PJu}r$-Bvd}bSh4| zF)5zN6!``{>Q?Q#MfoV1hWU0G#M9Z*p~cE(7Vi!-kTK8qg5rb14uQTnkivU{Eu|u3 z>WQ3j$lrav6~HTSD!zjB1QkrZ-*E_1c|crLxYkiDNQy6XZm;ZrROX)7(8sYiO~%R0 zK&)9Spdqc4JiHL*sK2C$RzN-i55!OPt$|G3AtPXGb2o8N_2lyE;m6Imy4$0c^}I$p z>w#$-$9A%6iM>5?o2yhVOf^n7f{P?4(}**ReB~{I@>f0pQw!R?F+X0pjy%D>ZHpU7QK6$uIOw%0ksXOuLM=s zb{f?3KGmY*jRq4G59+c}VQtPCgN(P4n#Iw$q0~@rI!4zZY9x+FX+fJa<+YT$l?sKV z2vjLJj&^qL3Kf>X${Btt>>9EGz`_WS&3YHQmfc<*K%<5$Ot4M`MOFmU(F)s270a!d1wl zr92=}Sr^=0HPx-qWuLWikVo8#;sU=SL0?2Rx;oR0Ht?vu2h4C2qO}dzXAA~|S97Kz zcZZ?{y7+6Fo6AhjnB_;K1uGJnFac4G|nqKFbLLs`z$e=DZ6YFS-iq=b3O*nw8i zy%-)^VlY|8x2h>9_Va*9<{4vy@67>=Cv?;dr5P(g9~w;Va$oyoDu+vbClc~)yn1)BGFwz~!ha|FA zy~@rEV+`_KLO|abOld|z9!b}c{S^t=@~CYh&6t{vjYY|Kj_=G4gV_VPDdRYyPeyeNcZk<_3*EfhMsw)Fy>eCxAP#s_`iTx zmyM)@kXuFtX(0MCA6;6Rr;Ht591jg|kWLDRz8>UTag4fxkYpY*2Kg~zfUCefM|}+H zrlh;pp&40UDS_~yA~^|!jOh5Md*Pw67}Sm6Zlg#oC=cNrf4brP?|K4QJ7ZXnpVrQP zd*H^$bJm4dC-jT0>Cx%Y5m{X;M2>`+U5Y$$^fGJI$+G0V&OOcxw>Qt;C6CVAL=NwF zvXULgSL2ItF78=-qWCw2(|g2iiNm>nmzOQD58K*oZSj>+Yi;LpQ}AtwxlwOdBj-a`AiT&Rhdop?rO;;^j~gPE?*t1`D5z#e*r#P Bg9HEo literal 0 HcmV?d00001 diff --git a/barcode/src/test/resources/org/xbib/graphics/barcode/pdf417/micro-variant-06.properties b/barcode/src/test/resources/org/xbib/graphics/barcode/pdf417/micro-variant-06.properties new file mode 100755 index 0000000..b9ecc98 --- /dev/null +++ b/barcode/src/test/resources/org/xbib/graphics/barcode/pdf417/micro-variant-06.properties @@ -0,0 +1,3 @@ +mode=MICRO +dataColumns=1 +content=ABCDEFGHIJABCDEFGHIJABCDEFGHIJABCDEFGH diff --git a/barcode/src/test/resources/org/xbib/graphics/barcode/pdf417/micro-variant-07.codewords b/barcode/src/test/resources/org/xbib/graphics/barcode/pdf417/micro-variant-07.codewords new file mode 100755 index 0000000..cb0fffb --- /dev/null +++ b/barcode/src/test/resources/org/xbib/graphics/barcode/pdf417/micro-variant-07.codewords @@ -0,0 +1,8 @@ +22131114232212411111442213111 +31131121116141511312223113111 +31221111212244112232513122111 +22221141111144111232342222111 +21321113114214532113112132111 +21411111321621132113332141111 +22311111343113111231352231111 +31311123143112512212133131111 diff --git a/barcode/src/test/resources/org/xbib/graphics/barcode/pdf417/micro-variant-07.png b/barcode/src/test/resources/org/xbib/graphics/barcode/pdf417/micro-variant-07.png new file mode 100644 index 0000000000000000000000000000000000000000..0593a09b6044bb47c8af8a530befe1b6f9fd55fb GIT binary patch literal 7655 zcmeI1aZD3e9LGBe=xjw745eGD&Z@-C1J_2ISP7sYBTQMg8KJajp|qHmBHg$SP%_+L zQy8ugOG;C8BbZF_th5wrDL5+Fu9Shv+McDbkwT1ZsRBh&jC)uA-hYdW$>k-NOD->W z@Atmn-~0T2cka_88SB=*yOuy8tjqj3?K1*l)edm~Xbrd!-r+u~ArPoYW?JeOU*~s> zq#}Veq*dbsFAvLX&By3hH)K2^z4d%5`SGO(kXV>|H&Uvdsq_js?$HD!_ilQNjK*y5 z(NL8IKTHn7igL~tW*(9u8SW1Fr(4T8UsW)rj7p+gzOxRWj)xAH&Fiy_NfSdaWYKd( zsBCKB^uRZsCS`wny5x{#uUk%W%kjr}Sa3P~zczRfuhZy-y?Tqixv#2#%)gv2(Q__L z^cg3Ys8Hwjs`2b%UdbP)O602eiYIpxEGhL9V#6jwh0GLSv2(JPez`O@c)#uM`&*gW z^K<98Kh?w5?|DC&suX0tz4Av!0MXSj>aa`sxEj zQB|F&eqz7X7!RjLvAqIY$YMA8N~XpK**GGkad*6<{ML>q^{KCXH!kcfI0jAz4x5@B+}bg_`@`Gf=SM zXxdq4v0LmKMW?o2zStH#1djGYlDcj_{z-V0a4o%=jzbQ@8VA7k zu14ab<5bzj&3evyk+mUU8Ozgr+s%KtkH1<3G4wQFZ%XMp^08%}Eu>M5xEe{ONEXN; z_;fsUxNKghWk$`(aG9ONT3kAx2%LzSv`%deDSYF3g=EFA3y&^vISg~O$uv#u+|KZy zfRsF;)HGAM=y25*J>0)iNOq2B4wl|{x=Lgv0SgOUN=~U^lW7mkQ|?>;(lxY);U9B< z*ngW%H%B{~DDVy^=&zOj$j!NezZwrXF0+3%7r1LnLeURJ$ z$Uv;ue)3To=!9_@X2fQ+!~9_w@b>8`?#%ZXv)f?>hS~z7NZ=~3=s_DtT%+`{)}Zm) z%5K2>T6B~?w84OiWpuL*N5w*{WDx6(f;w)COck&+1a?JS zU+y>lzMck(1XWDy8_wGYG0a`P=A9m)1WNx`TO?Bl~L*@UHII3q|aJ?u4)!&0U(VEWrGQuuk#B z8BzDIRcF;7XKNS({#{jNNnvj|slB`Pw1~jbfsUa>$fZ0ED@?s@VN?5}IdN`z&C=DW zd($nk{*gqMEuWJeHe~tg1c2TtRezSAe4IsuXIs=oiAa1nliZ3R55xS+riVScryY^3 z1>0yB*%{*oS>Q`g4H5L+$MQpY?3H_G)yqXfd|CKzfdu`ZiST8_^<*?UKW<|p_6IjJ M{YV;e;KbQ~0H2GU>Hq)$ literal 0 HcmV?d00001 diff --git a/barcode/src/test/resources/org/xbib/graphics/barcode/pdf417/micro-variant-07.properties b/barcode/src/test/resources/org/xbib/graphics/barcode/pdf417/micro-variant-07.properties new file mode 100755 index 0000000..8342fe7 --- /dev/null +++ b/barcode/src/test/resources/org/xbib/graphics/barcode/pdf417/micro-variant-07.properties @@ -0,0 +1,16 @@ +mode=MICRO +dataColumns=2 +content=ABCDEFGHIJABCD + +mode=MICRO +rows=8 +content=ABCDEFGHIJABCD + +mode=MICRO +dataColumns=2 +rows=8 +content=ABCDEFGHIJABCD + +mode=MICRO +variant=7 +content=ABCDEFGHIJABCD diff --git a/barcode/src/test/resources/org/xbib/graphics/barcode/pdf417/micro-variant-08.codewords b/barcode/src/test/resources/org/xbib/graphics/barcode/pdf417/micro-variant-08.codewords new file mode 100755 index 0000000..600684e --- /dev/null +++ b/barcode/src/test/resources/org/xbib/graphics/barcode/pdf417/micro-variant-08.codewords @@ -0,0 +1,11 @@ +22131114232212411111443221111 +31131121116141511312224121111 +31221111212244112232514211111 +22221141111144111232343311111 +21321151131222111452212411111 +21411111223251311111632321111 +22311111123234111433312312111 +31311121115331114111443212111 +32211113231331216222114112111 +41211112142223322111254111211 +42111141141213351321114111121 diff --git a/barcode/src/test/resources/org/xbib/graphics/barcode/pdf417/micro-variant-08.png b/barcode/src/test/resources/org/xbib/graphics/barcode/pdf417/micro-variant-08.png new file mode 100644 index 0000000000000000000000000000000000000000..6ce562275bb6ccf02d9da0e545e7516a9b189377 GIT binary patch literal 10436 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 literal 0 HcmV?d00001 diff --git a/barcode/src/test/resources/org/xbib/graphics/barcode/pdf417/micro-variant-10.properties b/barcode/src/test/resources/org/xbib/graphics/barcode/pdf417/micro-variant-10.properties new file mode 100755 index 0000000..7549948 --- /dev/null +++ b/barcode/src/test/resources/org/xbib/graphics/barcode/pdf417/micro-variant-10.properties @@ -0,0 +1,3 @@ +mode=MICRO +dataColumns=2 +content=ABCDEFGHIJABCDEFGHIJABCDEFGHIJABCDEFGHIJABCDEF diff --git a/barcode/src/test/resources/org/xbib/graphics/barcode/pdf417/micro-variant-11.codewords b/barcode/src/test/resources/org/xbib/graphics/barcode/pdf417/micro-variant-11.codewords new file mode 100755 index 0000000..bbf4856 --- /dev/null +++ b/barcode/src/test/resources/org/xbib/graphics/barcode/pdf417/micro-variant-11.codewords @@ -0,0 +1,20 @@ +41111214232212411111444111121 +32111221116141511312223211121 +31211211212244112232513121121 +31121241111144111232343112121 +31122151131222111452213112211 +31113111223251311111633111311 +31112211123234211511243111221 +31111311145221121142153111131 +22111331111163111222532211131 +22112221151124412114312211221 +22113112114215611111332211311 +22122111122253111513412212211 +22212141211431221312242221211 +31212161111133211161413121211 +32112111151341111121553211211 +23112131131134123132142311211 +23111211134115311126122311121 +22211231621211231161122221121 +21311221241511113242222131121 +21221222133411142111612122121 diff --git a/barcode/src/test/resources/org/xbib/graphics/barcode/pdf417/micro-variant-11.png b/barcode/src/test/resources/org/xbib/graphics/barcode/pdf417/micro-variant-11.png new file mode 100644 index 0000000000000000000000000000000000000000..5607dba239008176f757b7405ac1bc564ff28dd7 GIT binary patch literal 18636 zcmeHPeNal%<88?edk#M=&I;`9QG*B`U=h zSOuxTf{Tcv?Y1+RU{4$v=h#uUxTqg@c2`%8>VW|H;8&*+%${{teu4ICMJI)4}2W;*i}t z|9m*Eu4`xYuYAdly#ovPme+-xKDi&uzmpkSq+EE$e>AutVod4}WDT58^-pDBB0dF| z4PEX~dq{d2x@zOW9v!>IDfQ6Zr`j$9RuL<#Ey(klxL^qC2~hLe`~y=~QnX!f^||@( z!zGfME8Oae@E*%+B8|Sb?=+LU+?{VAYX(VTen%RjXoYf89Pa?6G#S#mlD(a z(izh1hSj;M(kxd!OENKEGD|Yh%N{XV!@S{#f3tApNtMS6b6Z#)0VDxkoxX5)&S|W) zbCO0+Wg!1k=zVmJ+7|R!wyM;m&K#LvCJE)u6u34}8@7*RL<^|N@xu?$W6i9NaZ^)j z<|}G<#L60f) zO6l~~f%u!^_74VoH~6}N6R#G~9j$rq{uUl%MGK6Y*{E^vgnI5*6Ul|;h25L|I?|uV zMYUBl7!wNKGk2Ti6CIi(${TpM9Rlc1XBKko!t!(+Z>%i^_nSGX;wnnXxNF0aqV({w z6J&4>6g(%e`eS@L`0wNzL3;w7iqTV_{0^e56BWGcqv~L>S(Gx{&4LYJ)I(o%i{&F^ z4?hes``3_sbQ^3D_6BaAs!(w&kM)Mj+&@)u4Vbp87=>bPWf1c2h9mVFD^t)OFnjr8 zbf~kST_Gwg_c$_r+e;geXN&z$#8Nz6Uj$CPqo$OsFXG@Ee8W+^K9l8&_Lt#@rYUSf z<_^06F%iT>{1PpqIZUe%G1UehjZzUC?aV3$-~Fii;;^Y*0uJRp_8A$+YV$<;gT2P= zq{^+}Roh2qLd+Gy;2hB`(V3Vq!bpAs#|X$XOYA^b&(a^PDe=x9$Tv4jAhe~FR6uyr zSnWVrrjiXMdd9!jK-FYhA+nh_G1oiNGjQV~(2f?ww6#VyZb@Ziknlq286nx_%$me% z7m+F;--0~(QO+=L(iL-qo>#!>>qbTVMWhSJnMPqhYu0v+(uKj$Gt#)6n%s+bFCt|? zxqe>QwWc}68lqX- zrM42&LYTw<&r1;L#ay5J0# zR@T$%?@0U^V{wL%9>5ffSUCr=YOz~w~?$~eTIAm)OwixPN_iZ%N`qM%6n_r@=>K`sgs@9NIAT_J|DB_LvfCO-*%{k%Z=Z3e|@gYSCS zQqZ-9rKGFf9Ytr;!)Kr`jRM*)1W!!23AbkmxYxq}% zjF#?U1VKW1n0m(##NddP`^n5wJV#6BhiS+Cr%_V58R6IR;FD z@gel>5t~Oy9(W9<(j@ggC9#C;$*Erl3fa*(!;Y(qt()%_9;og5{@%GI066K2k`y6j3c1yH{{TN~pvJSlu_M_$^JSD|=yAjsoP*I=x8t=9 zzC)pcB@EijMmds@F9o70ycb+}n0NFqA1?j?jSVF#*8yvNVLSYX;AMw=^64wNw6iJ}W!Li}JnOlg# z6DyBSPcM|&v+jpC*0C2+A!MmSesPktH$9%XPs{_#?SA3xp>;W;{rzp|Z7i|;5-10d zw}bM~eI_FhE(0Y))PvxeAEf(%GGL8tJODZNrDRRS`J13r;I#;Ub$ek{5IfW*-Y9|I zJut<^11S+xHarQLfH@#$h(|~$JHB1VCSFNQ>`P)kbGLLrP@o*Ue5;m>&MD_+X&HiR z0pc*sEf9_~zeg9yVB5#^X+bBihsSfxYrW2t7hAPDII*0D3na(^%h9QT!Y@KSva zY4{4VN-&>OaPmvZ_5qm}iLOq|F zJn=LNPEbH*ILhxn1J>de=pPW=v>52+wdA_#8BaI?!TqerkspAzNRTJx#PTUzHYoKm z&i&Fk7dXD5JfJJ7$APzi60?@-T9T3ug861gt7d)$6vDv$%82bdAf*qfjD1EYqbo@D zP!krHnGN+La9>w-{vvz~5-=@eCxm%y(BK24SDSW0{R#TWi0*Ls7(I(;NR#ZuTIHbr zS8Dp=FG2kZ`p8Y+0#)NIpfez3zlr}#kJL}Iq~Pk`jUs`ECrAwYns3!)AT!rKf5V;3}#ERSdo)V1UZ(j2JC zU6Q`ur4rS=k&{meQjB&iXpp`2XQLuM`!t&24>5%?uk2_wQyP|q#p8P?mm%EcNN zdH*UARI&*1ai}N987-^qT&KN8<-Q~tDp3+~2^4hWkk+@vl`a<9T)_fSDGiRPp`d)K zQxRzATJ1$LFZdLgGD|w&Ug7Zet1UMd7G?~8Nci|pS*rZA*Q4*RWwou8MfZ)v(u$g> ze%vP>gB8NCZ64*{L`6f({+03}=SZ1gdVbjyHb|x^pA}D_*5a;5o-$}r<>(JOdg@iu z%vYp%O3o_d5AErwwf*6w3$$Ee1zN6Uxmfx&O`mA2zJa;I`C&C`&Cn7d zNT&HJ5@Xe{uJv=g(KB5akEWwmVOTr|TB^3z6}%o@Q*8pn!jCgP);Utd)~ z%S%4|h}AJQ)mm7pk2hL&Of`#X-U(Ms=BM#`7fV%!_EcIBOj9KzW}${6u^fJdZJnxK zP*z7r-4BeEdBO@gRkY81u2HbtNQV9O>2qh(TCZ2Vp-hY|1mhU}K%Y65ZSsq|8L8G- znq;2(h^J>>$cJh_;`@F~W-z~%Xt+5M9u{G6Ixw7aeWD|bHPuEE~^#v5geeuMM zx8+n?gg>|S`UWsZU72jbKvJjO_(U#|_GQLI$*%l+y{FyrMHJ zYrDefqEtcR(142FmRd;UD>sJA$0}8X7(T2fgiDg?F+oZav*mLLAp5*GfIVmbotZlA zJ@>%j@Nw^b-}iam-|u;Tzn72Rj@`NZ7r*+&k|j%)zw!Im-&wNc*>&(y_Yd%mC11Vs zX7`dM@5tYHee1jLCsp6ss!ogirC)FV+~-8azvZk*Nn(a|pZV`G+wuEzuZ#0EiW5e= zmnUE4VU^ZeHf6U57CToHzy45@y5^Ow^pELjp~K;aO9R1dU;iYdVV!9$yK=wHZmCfS z_2P~#q{DJLF(JmSm}UK;9RLL86qZ?GDLR4ciPc06qj|RH zV^wbV7J)QMn$bDF%eq|9fx8rphD58S?_}DLJ6|qzXD{OxJI^Zjs3jxfgcv+cn|jGT z^5|CR#%+@5u|on|M9(MtKOQKP<`&D^xOFTd*iUp`#>P^ZS<@A3c+(eP@5!(L~hx>wCxmdg%13|9u7gQHJ1n$E%_ENZV~gje=n;I=26kHb`5ZIL2rY zOdU*^jxoMhTe4;cplWK3qWZzJ&F*V%*X8s}iTc2{{^YvC0@cim(unF{0Nz%M-0~ppD(W7=0q`1TOwcm`FuhKD%Mc27?h5lps1H8WA!U>F(Ob?Y_)gL;512W&Q zOYA=7#?73TRph>9Tsz-B$dkX~@Sby~sM0RYp2=TNj^F7ME1Mq_)Rw1R+AiK9ZgJ%A zQSa70Z{#lPr*A;d0UZZVNiJ|pMppx(J>jTdhaseUagLu3v@otq0!aCU|L$r<2zp~i zS*}UF6c^cATW|p&YJp(R-av3QD#46JC*mD5w*c;*kk>JP1cP^4&HG}O(GR7k_a_=i z(T=PHGGc-BwbX}^cWNSZo6Us`5>f^bRMfpdCa1evgb{6Bc5f;tT*xaK-J{OeL@kh1 zH)p!ez~B@+i`}uKKE2x_zV2B*u9bLx7IoFKzywqD{K0>ZC}fb;)tH# zve&Y0BW)d+LL`!ykCFc5*%K?9XFsg?mLt$I51&<@RZ6D5N#*EqTPr zI(0Ha_Y`=e=IyrL-?G`6GRS^DG5sMuV0wk0x{y8>R177YY$8p`#MYVGxO!{zadT2% zS2zHlKlT<>a8~nq5x=i=e}I6MJ^45dFiH=Qy0Pyis2a1=lkw7&bZJzz#$}m459qfM zP^MVNd{c4M|k)F{Xlw3f5bskn^uFTBeGWxVxfovEd(E? zh&HjIkoBP^iej0d6bg9=b`atb7nqm;6Edsa6wgZ!RHTp;a3IVYzf;4jKcqh(>P7_f zRnQ1Y3hNN+sMGT-p9Jxi39>0>Yd)FQhaZ8{2RSU=h?g zOH;J`cE$Dn3lymZX%MF%5`k4>H-mVYgENd16s@fH*Vl*;$Pox%v-o`~Gd)Cl6;VO* zHw)A#R0=IJ#9N{GzV_|sWk^5MqgZ`}%CJS6$Hr<6uZA`@tp){+i^BE2Zd3v;a5bW~ zKxBNc*I@y<&=`)A66AC{f01FHP88M`7KD0`V5mTOIe==wPZwyS*mY2OZ2!M!vOzM` zpwxVD0Tp74T<=gIS^eO$o&eEMm4>ntoCLQGiwt!&(Z>%8OthW`*^q@2^jbP9Ko@AH zrSIk@=e3~`F$jkODo5#|&({U?s{dUCeEz@wGl)Cf1c8~D4D$rm@WP=$LtPL`orkaD zp8utQ<;U_7#!3G^Rw>kXpdrEqS~-+rAp!Ydk6UTvPCS~`PcL;T?x6ZD0$tU?un}aG z76rl$c^v>(efct1i8ntA#OZdH_vo#1lQ%449L z0!^Ep(Y45cGf!R*1)@}n1)JQIQF0`Oh59E$>yfEtf%>{WK-l<#2^y?k7OOfK^=@Xr zLY|m;;(R*s4CHg91Keu7*3Y1!18tN~FH)A8c}`mEj|$aFu)F0rwGA~2)WR8CfgCvt zj27F9HkEVWmy^sYRvL?-ee|*XATspK^Wj^e(6I%#T$FTFWxIQWi#j>IyD2}=Je%%Y ziWYGXIQCk`dEA5;)J38vgPshqWd4{VBY^XpHr(ywyHFuMv8cp`XJu8n{*yt1Zw+y6s7s&NWp{{g;oBu8_g;bZD%Y@;_8PhK{#HmU@vShFIv^pI z&vRO+Vdp)pwa~=*E(WD#!;mO_p0EPLo%g9iH;O-V7os1uPtrV3$`b?&UR3BgFHXvWi{3}) z_mOXM!ibTspk#0q0Q*5UaA? z#jve;BFavOG}lP5!u5Pv24*1;U5NXVBOKz_Z8+t{L#iiuS<$f)Py?+S%p4srofdM`Nm(BToNT0w5ndRfrMfgTh(N$3aO7nTJ#2cl=YxFC6#m?m6F0XomU@mmEB3zp zPp|!X#XEX=U51=1((*m`_D-uBHG$%$csc*#6Vc0D)iNAou8dYwitK9pC);R6N zjnk$#tHjDcUJFMzb;aGkT+=yqL}U4+c~7!iJ8Vq4_8s@gebMMay=*-@T9Zgr#79P} za9+J^xN0`u((lhs+MsF_B?+e<*#|3e0sCIa!^-=ZU?MhGWBZbq{mm`GXK`9X0n1iC zFmn%c=W!h~4a0ezYJ>bcWn(63_qsYK6YFImx=2+cb??TCIzfH%%;#e3Cf#OYB-i9B ze^VS=<$G`O7p{I+VCxm7c9*(aonw1ByRu06^_Q#vP%;XiLx@e&HzmV^qGD(L2Aj-a zy<#7PhtZFuC-x^5J_f617|*PA2ZpE`sSF952qW%d^K(G|l%5SbHYgYr|CMr~8-dOzPqzzXAUo%w=!xNn(V4~;#@{)nFc+1<`oXI`vZh=Ds@Orf* z-jb9_IEc~a#tI7Fh7}%p=Xj>7$r$qRq2}(F~j%c33LruZa5 z$>=Cxy}LvUT)ODiK)i z2<=1=P-8LDsV`q1#wVn4L zLrB6s_dM@;&Uv15Kl;n|=y@+Je8I=ZXWpAz-uRo3&#YxWKED3HhF|z>?2E|u@kzM& z<{KN{dVg2N%?(wls{(vYozKek=l=ToZ~42JW8V~hHr$J2p>h4!8EY#-eAyLR|Jr38!JIGRyL)YFrKnYGTu=AeuD4xpQ^VgQsN;{qvBJrK zf%5|=L$dyp{qdr{ydo8!&8!G<)UB6r#U{d1Fnx01*KvGsemtl6NT$#0)V7uKYm8)W z>v{$7f`hH|zlc9ZDo7}J6g$(8CTnB|UqzH3@d4eF-8V)ol!aRU9eyhg6HbZ$206i; zo(qJAtq<`fPsuowS9qENzdJHIu%2m{y!>K4Gsjj;TG`BW{YCu)hPilu{Kt(nb-k_M zH9z-y+hfNJLEqM-R#x;}8MaYT;?0trSKbg`P1L0j(wI4&p@ZV_Bia-eqkNEJXGCO9 ze6S=V;%P$&ts%nOr3Ykp!ce7tB}+#ZsZ4Z`f06282ivN&6(6En^{Q+>JMP+%2YM0? z1pCvZ54(;wiM_ppZP-s}TphZ#ncR5N|KaV_fOuF@Otkr zpP#2qQB!GWp%#f7`|^&vR?wYxjbpr}8<^wH#Q{QN68VjnV!lMjQCdT4$B3foy&~1u zum8?me7W0N#dn9laG$OuD26jZW*nxpC5#yM0Ih|AiISBMMk!U#mp3Xd1<aUEB#xn?LusgI}3CXm3JDX(v3WyA|HvSQAFyR7cb=HuKyA zlQGbr`5R_?aDm^5gM%~YhtV;m9B4h1ug0%c4KoSVMZHlE2s;~DAFwK@o(c4j=i#EO z^=f~Zlt4LyvSQ}V;T?ArKEfY>V}SGE`M`pj)d|WUOe?*#bCKR6U@;QFe!z}!7OL>; zI6gQ(o?m>l=ha#$wOJz@Otgn3=~Bv33kLwuw`teinAg187tQy@FU_oUXBd6Hi3M3a$FlutA2J2)~0GWN&MKJ z$`Id5>%iJv$XvBKTtuKLjM#Y?ot-a(S+9G%Qk!x%lD1td1iYAexggfTASOSd5T>ZB z`Z8e_+N1^do^??H3LcT+sNt4mxz^;zMSUENBxzCsxP=sSw679O(ua zrh@SoX26qXu>PHP4J2<-AhOS7XcahdC}B4Hgh8 zx7lr$*=UHo1Kh%mBLp}Ng_Qo-62LAF1`ftAIWsJTNTybh<~=6s5!Adc1}Kg%8_Wf$ zqH4G?$Q?+tobRgk$M2K!nd#e*oxZ*iT3t8}P?>GdGqgzOdKhzQLM&qR2t|@1F=!m9 zye%8%OR8;`L#eoHQE3e2A#*PP{e%Pc!o?o-R=cPAWYA_Y(o@C@ z-X6IN6}8bi&0c}KUTb+fdqF)D1QX!1-{#>j#-YZ^h0L>FIHj{os#!A>f|QWDHv7Yt z-pp9gC(r5Djkoi{lesetm$r9`RfZmgaYPN;=q5?d@Hs84vdz$J%AobQUqQ|Ri89ar zw11P(c+@pRZ%MgOPr3`jmUV#lp*g@XY05JfGwC8P$EMxp>Ag5`KjgA9C(7nmhD1=o zGeqB2>7uOM#*gFY3qc_rc1H*CVg#b-8G=m{QwCd*j6j|3cO6?>tGtQ4^V2M!(owWv z@bSADYbrn_ZKs2>Z~$-y{H!66HxCq33#g^1p$9#ak#$_w%=5Zo;ip@n*H7TTn05!_ zc;Ni_WubxXTUlM|WzfjE2WIkix_}T7P_|Py!M>^}&C7z3>N{oI4W!MMg00r}@b-mt z+%$!TrW*O*XBk{|EmR~Gykljpf9+MNmw*4;Hd-~A?}9@Gf3*My9H-fIi}?fKY7VGh zW%cl7px6{zPC39qdJAlU6}`NOC$KExyfnQJ7Tiq1y2rA?%`ny4Ea7{giynuN;-SmH zuq{-A-PY?_BJXF5qRU0;z^}W`h&*&12ri8lO)f4%Qjg(?2c1YAKJH=53+N&~4O=S5 zWx*gXmFd*163w~PTvo1cB|KT>58DeKjXpvUw(Q?)#@)RnUh( zWbbxTS)-q3aohrDTfaAS&Gsi6Tcl^1DLkI02A|dp!arzW+PtyDLHI>vf-7^6t;A6Y zHeGfVclRN~n6?AuF?5`e${MMmZs6vK8QjHz9jv-b_9vYVl!MT(0(|52yW(Z{Glksn zeF{fFe3B&FHG=X7x?QNe6$xQg+`R@Zi`me!e{Luwq&pJ<0D5IOIm}gzj?#KNI3H~H z8hja$O!74-c>ys{mW3S($E|n?8J#QMV>{&MY`b7sj1mvB9dN64wCqo-(t#sdS2u8J zw)=j^u44!}Q*@zo&Lt+m; zK5Q5}mQV<`MmV;I3j)o>FE0Wy7WKso=^{gqpZoqg5Msd?agA|@%ZrUE7j2otLtENH zQ8bhSi|QGOtl)ouhcXSO!}LnCgqfc2S^@N$1qXeS`iU+npCh4nyTW0zct@O986u0d1~u-Oe!gXt!+ z2UxDQvEOVtk_x3Um(@UK#VEukE)QxVP}It>hDa$N;ewwhCc1B*C!knEDTJqyI27Xi zgjgtqAo9{?U2T7$p)3jtGCXZ${5+A&P(nGzUxfqsGfay(Sb1A7|5jJq7P?v(U)u=9 zK@IdV+jMs$8lW0qD88dVVd`dXLCT`M=@J@n-fyi!^Y%ok z^2^1yk)&u3+YQ6Zk}-_~2-VF~rKw-#AuUlm>W2oCf=1_R;7pHF(@MTyg7iz*u*zD#7V8w+MYvDy6U#+R#WVC=yU$ zTd}hk>#$)8BrID>t-Q2!r6od;c7UVV(iaOQ3b<}+6&cUqDaJZ-s;rHG*;0LZaWA7szE}oi`mGSxM<6Yw! zqMZlvQMX_G{e!Gc9=oI4{l2Sw=dRV2q_tRceQ>wGKV78fd^^?jOoi{YPDrP3Q?&V` zjADYS)-hK#Cm35ys!Wy8s$x9KT@5~RX>tDOI!Vtw%nTU#Hy9$lf1|W~B8`$}(&2mG zbta^;wfk)*rP|JRDGu$)67%R|X_QN9GR(}H6QjtI9fq1kxttz_{4Qm8RF`ehI-&9(c`BL{gyZxH zNAa-G-EKt;iqtxBT5L;~Pn9}Jfd|(rIH#mMV#z03CTy^#S(cF6VbGCUPJRUosVX?z zAyTI3&Y9e3)||lfsDR+q%(nN$jHL@D`(T61mj~W45+Os@O_{Jzk7o8Yh;*NX=4DeS zccgkMUN`j%NQ72yUNq~nUkMw$J~wThfDAD^H@6#ftwkgPid3n7;%%;tGrF~O*GP)L z-xnC7PF{nAmm1f0hO$N3kRj^G+t{nXUIlh5uv_tO1j?d|r54h=KQJZIs+sbsrok^S z2CE$9u@Q?U5Y>qJszGhb+f6z$>8hoTBHN)E`;OFEF{{w@eFcXz->6jc$V5#dd~3vX(ZwJN01Fz+GV-HY#?%E165q*ogO7d>`QPA`>E<>xpoH`saWxY{wfK^Hbt=50x#ih3%+|)4 z88db%vc5M1Dtu&6X_&9_(;?I)#{M>lTc;Wy2*D=+-|HhUEA+_qo+&c$BZDy$*~y-< z53#zFm*0fA&-d{kR?A@a219I@^&-Q24?Z=SXu$QB8NJI22({XN1mXsbr-e)~8%3^? zTRMWs>mG+BMg~tsV~Y+Mgc`t7jHMVm#s3?jfb!KyMCP$#6$B27Tk)V;gI}=Mz$gS=ctPw}Blh>~3I66%kx#JRQ7h zDKi?ZX0~XrN7-WOV2GbsGDRQTOgu6Y-w0dHM)=rUM*@w;9>Fq(9T@DsU~=)FR?9!` zzh&}8dVqYW_64r{sBfo9JTpoF2;8p^J#~UXH>EopDnxEQ~p=HGTd)cV64QNs@K z1>N$UrDLS8`syI?XJ6N?n6ck0pAS<2O55G*@&Tq0rKnQ}#@zd41TeHVXnL8V0j6+~ zo7Z`Fc$?H7Y4=&7sW`41MlCu(7mDQ0P)T{&%=}FN4OK!|uVU+(P7Z|mlr+NuAf+zHY!Mw7_H3lyfwjXXSs=Ssah-g)k3* z%@&jHP;2A(=*dnhHHAVqikWK*AesIq+b^=O;u)|ef^w&o(d4zqarB|l` zgo3A|jwsNT(DRePM4U_9wJi=PbMH`gAH`CNr4&1*uk^(a)Z8b`ENte!_Y?XriX6t_ KEbW1Ux_SMQ|%;^TIFIkcWn@jvSGWyWmaOLLK4CvXf`#|gufs*22=SX#VOZ` zX@ckmEiq7VGgCrfq*3UP*_LhK8hQhSFu<-cfmOQLTujylA}V~Ft$b8TMzZ&?fBRWY znyTGDK9@e9Q(!(h^u%FJm!?bi)AHWbeK2gg?maVfXvMZ%akGC?6r5F- zVYKVsCw<&-uECOHIs5r$`^_H@@rMq@X0j%J1cOH}z)<9L>Af4-cwWO&8X2t+U|s6s z6vaod(^H<&<#;d>jD&FKfd(|>tPSQuI2-px4vP+xUS9tzu+vXfwIQ;tv#s+ZCi4^z4K}K(4kG7R zBqfS6OtiVIT=p~IqCx<$F}?@MTFw6ZXfa87Rsgcga-@Th(O zPM!AWuQx67458CPfJGZ!WhlX&YU8hr`j|8B=oq=4Z$5tJjZrTSIB#5ezpYavDSM-JRPfy&mW- zEhYhQiEqY(5wDX+IjU7X0YTt?xjr_r^=I5^iU~Wuk|~xeHmD7*At0Zc`0d5iAwK08 zQ}|oB8^k^8W+Ib}c1LIYLNY^gp}@Y_v~=a7hJhDF^z%LG(s4yraUo##625q)y{nu} zDw|aHq<$Z+KW9H60+m%pnzm#kA2YjEw-)Y7X7@n}YJK=6l&u62NPNa{WB`VS7`vFw z275Nxo5DiDe*mN6b%OGHN>K@yL*y1jsk83<)h5d= z6i-I3@++GeTgt-SO))4jAdlA(HAAc*T>|O`n@~a z`z$WTq6AV`rj6FW-bOOGUA<9sFaba4$tKqLm9_Juz@34xpR(}v$}==5IFzFU2-lB6 zV}I9Y?>^AbrQSfmnv={tc!Vi-Tm!lEuVmbB5T|ehN=abZcu%Pj7*>pP0YglV~42<>djyzO1v@; zh%~;Na`18WQZsmr!2A?Qa8bMc{s8!jV1A+9Xs1Qs9GWDc4Mr;g8@+2#pzb)IP@#z| zOs;{VgRjB-h)%hPBij&xBEm|Lz8lo-*6ZLd3eM8P$45|yu5p)Nx(4%G8T8+XFnET1 zCKOTEAOJj1de;2VW5wp78KU zD{AVC4ISD_n7%1lsXwJz98#Fad+MA!~}#W1a?9ajq8n) zf(u>3bU`X;gMhSHSDa3P2`{-Zdks{wKwhBN0r_nhB({{QE>@Bbz(b?vJgUJVHeS$pu;`+pY_!gwPjWKHZ(;13}?7@z(r zB;?J=gZuaV{@iiHz@FBeoe_+YhtHdc>%aZm`?otQ$}VVse)IY2y62w-heQd(b?Fn6 zW%F)P*u$Z?SRPJF{okC(&t>OT)mPOM&raQ2unvCeZ;m<2JUnqs5GJ}jOFY}*7Tc3a zS7}ZkMtV7!6~0&oSH)AEso9if+GyIj&|t{^>-8DF%$6)u71nI()5zF)d_q6qRMeXv zm$x#uyo1?k z9kdQ+6umWB;FXJBlZplU`CCJrY?KrW3z$T*^nWW<{Jmy^S#cYgyAho(Mjs%M^|R6>W{)BHm$FN z88JJzj;fa>cZ|AXyd&IR;lo}0uJz=xFp)>oC(1mkvB zEIAT}F-%h1HvXojpLLRNPc9#;7#o``JKCD6NP|bLJ&{KToeny^(tosg!S24|7AFV= zc#bg}xK)yGS8!*8rG{({tI3}?A`Wo4s;^`G08&5K6}s)5(w4<_>Bg%OhT6G=p?Q|& zs6Ly1wd+QTazqVethvLVEVyILW?lQz9P^$bTZ{=1%B{H9(cYRV6gW;xf|XI`l!=io zNtsXPuFTH6t20iOEa{kzYPk!b%O^II?PB}iizM7*{iQQ}Q6Q=?wS4Sd8*#L?Un8I= zOh=er67;6~jwm6+4u#2VZdZ+5>SU8uXVX6yB?x6MVkzFw(u)%wAOuF%wgnV`V~i#?Ux_Y67-N#&a58JEtk^oWoOru4GWY@mek<=J>l!Ylno*~q_k2~9P;BLOyGbL~rwuPfa7(56N_@Kk*aN`S?L$%Ty7rRdx?{&h7 z!p5n*yUQ7RY&0GM3C9X@C)A|)g$-d^J;BmlH?lA~%=5i;n8E#_SMv`I8mI3AVZbK@ z4|6go=x$6bTynqA$i$tWnG?IyHz(aN_Qe&1Jxt`!A=-jT2nJ^Qg&9_7liw`{eICP* z*Qjq8r)t7I(tyL6azB}n_U{5DfZ5 zy`77wR)?%h)ymDx;kqu3(uRA%kIZmibk}9ZU8)}upJeS~`06-Iz;I;9BCK@>!bK0% zU>Q(Wft?r;e{By{@d{+#PjVYGIv#%npanM;mhHWTnxZ|j;G8)IR6p32=aUL#MW$rM z0p-Pv0JBqujqwmG4}KPtusD>ZO3ARiXN zGBX(3GD9rsz|pWJlau4qEz6THnb_t;Y=k%5>Y{^72bW&C-&ZWK&c6k`b1deUi%|gt z!4Z$P;NV+o$BETms3R%lO4n-u!$=^4FJ)td3=+Xzdua7j~D2A3@Pef%~m40Qp^PVymp6AGkG^K2%RdVG#W9S5-s(eb$F5nP01 z2UmfLREVg~O2~X2)ojckWI@@9Fqm`7P|k@MO;4SUI=x`%ZTfvB%*c^uRM&y((6@=6 zM+(YTDimnliuq8e;+&=uyLL`3Rdd(S|3Ia~bd>0&fqGEr{qRB_();1N*bkCz*-m`Q z54o-l%clCCw9t!$(pjP#?28gkf$a954Z2!iMEceA z58T+;mL!WP0i1&2PmK4{tjXo>9xPkifZRyy;5Roz`2okNH8L)bGMD2XbzcUWu*Z8i+#tH12=6a?_c;B|lpK~}-Yf57&JLDvi(&GQ}tC-s1$ zFP`hXZq$YgWwoxk&r59!)AD|3nr)8Z;n6ViXWqXsJEyt%sZwVxn76fC{9eBs}3LCn`oRtH59{AO46T7=JtIWM24jgPL!C-A2y#c3+ zbx^Mx0B@vox}|*`3X`5wE6D{Hn5W_u{qb&uIT6|^>;u@#aWF|GAM6xxXxcbv7i0N(D5I}6j zk_O)wL^FKv6s<>~tnHd@fo=pQ-!m2Hf*U+<78sSes=|8}8n^;rnRo5xfH^{9L~d8uhKo#D|-x?7GPh<*%LuON42s*$+3E zd}Jusrhlv|Y;OQcJ}qm2?*LN69=1H&L6Nv{6bckNHstCvg}DgDcSY#@30|4K?2F}* z4{np9`2@m288r7^;e~U+u0y zN6Ddy1qZZLXu-)Yn1CB>4Dby6etR;Q%?AuGMe^GyT#J__$ls9$TcO+-%ky5UfADk& zu^xONbii#N-9{dh;=~0f{R-$;K<@&27yQ3n&^G>jjU?6a3pM*I4*EpwU`pEl*1cIJ F{{d)p3_1V+ literal 0 HcmV?d00001 diff --git a/barcode/src/test/resources/org/xbib/graphics/barcode/pdf417/micro-variant-16.properties b/barcode/src/test/resources/org/xbib/graphics/barcode/pdf417/micro-variant-16.properties new file mode 100755 index 0000000..8d67ecd --- /dev/null +++ b/barcode/src/test/resources/org/xbib/graphics/barcode/pdf417/micro-variant-16.properties @@ -0,0 +1,3 @@ +mode=MICRO +dataColumns=3 +content=ABCDEFGHIJABCDEFGHIJABCDEF diff --git a/barcode/src/test/resources/org/xbib/graphics/barcode/pdf417/micro-variant-17.codewords b/barcode/src/test/resources/org/xbib/graphics/barcode/pdf417/micro-variant-17.codewords new file mode 100755 index 0000000..d1a6982 --- /dev/null +++ b/barcode/src/test/resources/org/xbib/graphics/barcode/pdf417/micro-variant-17.codewords @@ -0,0 +1,12 @@ +3111221423221211241141111144111232343111221 +3111135113122211331111145221121142153111131 +2211133111116311322111122253111513412211131 +2211224121143111321222131224411111442211221 +2211312111614111312251131222111452212211311 +2212211122325112212231111163111222532212211 +2221214231112313112231231223211214242221211 +3121212212412313111351211511122153123121211 +3211211112134412211312441212611123123211211 +2311212411223211311321312116142133212311211 +2311122124411211221352131122331222132311121 +2221123361111111222222421123122132512221121 diff --git a/barcode/src/test/resources/org/xbib/graphics/barcode/pdf417/micro-variant-17.png b/barcode/src/test/resources/org/xbib/graphics/barcode/pdf417/micro-variant-17.png new file mode 100644 index 0000000000000000000000000000000000000000..6ade8cf9734f3c02c8c930cb05e10d24e28df92f GIT binary patch literal 16943 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 literal 0 HcmV?d00001 diff --git a/barcode/src/test/resources/org/xbib/graphics/barcode/pdf417/micro-variant-20.properties b/barcode/src/test/resources/org/xbib/graphics/barcode/pdf417/micro-variant-20.properties new file mode 100755 index 0000000..de7515a --- /dev/null +++ b/barcode/src/test/resources/org/xbib/graphics/barcode/pdf417/micro-variant-20.properties @@ -0,0 +1,3 @@ +mode=MICRO +dataColumns=3 +content=ABCDEFGHIJABCDEFGHIJABCDEFGHIJABCDEFGHIJABCDEFGHIJABCDEFGHIJABCDEFGHIJABCDEFGHIJABCDEFGHIJ diff --git a/barcode/src/test/resources/org/xbib/graphics/barcode/pdf417/micro-variant-21.codewords b/barcode/src/test/resources/org/xbib/graphics/barcode/pdf417/micro-variant-21.codewords new file mode 100755 index 0000000..ae08d50 --- /dev/null +++ b/barcode/src/test/resources/org/xbib/graphics/barcode/pdf417/micro-variant-21.codewords @@ -0,0 +1,32 @@ +3121122352121111312231111163111222532131121 +3112122115112412212241211431221312242122121 +3112216111113313112221116141511312222122211 +3111311121224413111311223251311111632121311 +3111221112323412211321151124412114312121221 +3111131211421511311361111133211161412121131 +2211131115134111221311212244112232512112131 +2211224111114411222211123234211511242111231 +2211311114522111231212114215611111332111321 +2212211112225311232111151341112122442111411 +2221212213122411142141111144111232342112311 +3121215113122211133111145221121142152112221 +3211213111116311132211122253111513412113121 +2311214121143111123222131224411111442113211 +2311122111614111122351131222111452212114111 +2221121122325111113331111163111222532123111 +2131122115112411112441211431221312242213111 +2122126111113311121421116141511312223113111 +2122211121224411211411223251311111633122111 +2121311112323412111415112421211431412222111 +2121221162213112112351221213412123132132111 +2121131121416112113212341222134114122141111 +2112132513122111213211222414122331232231111 +2111233611211211214144111114111341153131111 +2111321154211211223111333141121412423221111 +2111411513131212123114133131121313154121111 +2112311231125212213143111214142111614211111 +2112223121135113113111111543115232213311111 +2113123321213213122142111143223323112411111 +2113214412131113212123123213115113322321111 +2114111431122314112122431122123321322312111 +2123112111314414121122231412221212253212111 diff --git a/barcode/src/test/resources/org/xbib/graphics/barcode/pdf417/micro-variant-21.png b/barcode/src/test/resources/org/xbib/graphics/barcode/pdf417/micro-variant-21.png new file mode 100644 index 0000000000000000000000000000000000000000..6aef18fef4a526fe959fa337eb910778a82b6e41 GIT binary patch literal 43759 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 a_SMMm{(Vrzx2NErm)C4~QT0N6&c6Y9xl&*N literal 0 HcmV?d00001 diff --git a/barcode/src/test/resources/org/xbib/graphics/barcode/pdf417/micro-variant-21.properties b/barcode/src/test/resources/org/xbib/graphics/barcode/pdf417/micro-variant-21.properties new file mode 100755 index 0000000..9267baa --- /dev/null +++ b/barcode/src/test/resources/org/xbib/graphics/barcode/pdf417/micro-variant-21.properties @@ -0,0 +1,3 @@ +mode=MICRO +dataColumns=3 +content=ABCDEFGHIJABCDEFGHIJABCDEFGHIJABCDEFGHIJABCDEFGHIJABCDEFGHIJABCDEFGHIJABCDEFGHIJABCDEFGHIJABCDEFGHIJABCDEFGHIJABCD diff --git a/barcode/src/test/resources/org/xbib/graphics/barcode/pdf417/micro-variant-22.codewords b/barcode/src/test/resources/org/xbib/graphics/barcode/pdf417/micro-variant-22.codewords new file mode 100755 index 0000000..16326e0 --- /dev/null +++ b/barcode/src/test/resources/org/xbib/graphics/barcode/pdf417/micro-variant-22.codewords @@ -0,0 +1,38 @@ +2312112352121113112231111163111222532112311 +3212112115112413111341211431221312242112221 +4112116111113312211321116141511312222113121 +4111211121224411311311223251311111632113211 +4111121112323411221321151124412114312114111 +3211121211421511222261111133211161412123111 +3121121115134111231211212244112232512213111 +3112124111114411232111123234211511243113111 +3112211114522111142112114215611111333122111 +3111311112225311133111151341112122442222111 +3111222213122411132241111144111232342132111 +3111135113122211123211145221121142152141111 +2211133111116311122311122253111513412231111 +2211224121143111113322131224411111443131111 +2211312111614111112451131222111452213221111 +2212211122325111121431111163111222534121111 +2221212115112411211441211431221312244211111 +3121216111113312111421116141511312223311111 +3211211121224412112311223251311111632411111 +2311211112323412113221151124412114312321111 +2311121211421511213261111133211161412312111 +2221121115134111214111212244112232513212111 +2131124111114411223111123234211511244112111 +2122121114522112123121224213211335114111211 +2122212411115212213113222142214412214111121 +2121314221133113113111143331222414113211121 +2121223211261113122143212212131161313121121 +2121132232133113212111542112144211133112121 +2112131313161114112112112226222512123112211 +2111233112322314121112311252111353213111311 +2111322353111114211112131441112223423111221 +2111411214121513311134121132222322223111131 +2112311111532313221142132122321124132211131 +2112222121154113131111222144135321112211221 +2113121332412112231121332312112331242211311 +2113211142124212321124114221431115112212211 +2114111244111312411121412232111313432221211 +2123112111142511511141431211342311213121211 diff --git a/barcode/src/test/resources/org/xbib/graphics/barcode/pdf417/micro-variant-22.png b/barcode/src/test/resources/org/xbib/graphics/barcode/pdf417/micro-variant-22.png new file mode 100644 index 0000000000000000000000000000000000000000..3b17df860f23715eb87ed2991cfebb9681e402a3 GIT binary patch literal 52887 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 Yf0c4f_|nZu`0pK`^=}ov8NB~L0lG2eO#lD@ literal 0 HcmV?d00001 diff --git a/barcode/src/test/resources/org/xbib/graphics/barcode/pdf417/micro-variant-23.properties b/barcode/src/test/resources/org/xbib/graphics/barcode/pdf417/micro-variant-23.properties new file mode 100755 index 0000000..07f777f --- /dev/null +++ b/barcode/src/test/resources/org/xbib/graphics/barcode/pdf417/micro-variant-23.properties @@ -0,0 +1,3 @@ +mode=MICRO +dataColumns=3 +content=ABCDEFGHIJABCDEFGHIJABCDEFGHIJABCDEFGHIJABCDEFGHIJABCDEFGHIJABCDEFGHIJABCDEFGHIJABCDEFGHIJABCDEFGHIJABCDEFGHIJABCDEFGHIJABCDEFGHIJABCDEFGHIJABCDEFGHIJABCDEFGHIJAB diff --git a/barcode/src/test/resources/org/xbib/graphics/barcode/pdf417/micro-variant-24.codewords b/barcode/src/test/resources/org/xbib/graphics/barcode/pdf417/micro-variant-24.codewords new file mode 100755 index 0000000..c44f734 --- /dev/null +++ b/barcode/src/test/resources/org/xbib/graphics/barcode/pdf417/micro-variant-24.codewords @@ -0,0 +1,4 @@ +211231116111246111113312312121116141511312222112131 +211222112122441122325112311231111163111222532111231 +211312131322231322312312221212212216231314212111321 +211321331331212111533112222123143112512212132111411 diff --git a/barcode/src/test/resources/org/xbib/graphics/barcode/pdf417/micro-variant-24.png b/barcode/src/test/resources/org/xbib/graphics/barcode/pdf417/micro-variant-24.png new file mode 100644 index 0000000000000000000000000000000000000000..a5c360b9f8ed99c8b83f2cf1372879708d19b3fc GIT binary patch literal 6483 zcmeHMT})bM7$#%Zt^V|)p;Qgq)TOG9AP`;PTJ{lG2(c=08-WlNot~QVjf~g%BBhH+ z3nxyy4*yK}eRcTh4^8WvEsM6p{rL9>53%6_b&sRtAeXL{_`QF~?XK{SRf=jAtLSxL z2d#TyHm>NgRBaXtl8~i)rs_(4&j~Io|EoYMkb9j@ZB~=zwBLKY>`8>$yxhEP zUS^a%hng>tHY;~g&V?kM{vT^pwa`~DHCJ4Xc!Mi+Uah)C5%>5E1rX|#v0(d%m= zu!d!gSNhfx=-Oy^U)3zzV8k}+x+g{1X*96^ww7fbVWL&D0?L`!N0>qT@hFgs88D> zygQ(#B>#fWKj|t0s2kwXIUJOslkPQ0Qs+NgDa*fr^v19=tWPdMp1?$HUFPKnJDe&m@28R3#s~wUQY-YV*pD48z(6#OA?}6>&4aXafPdGl|xTOD= zl0Ji)7G1Hx;>^opUn5v&8O#|5P;YrpF8Z5>yI+m)hP=aJP8J%KEI5AVwXk%KNa6Yhq&NyNq#;gs*7RJ0 zJcXv=OBX`CDlD9$`}jmrlF>~Kf~iM%JCVTlc*eZ*#=JDzD>_wA4GfGXnwPiTk%Pk( zy~;Y->@5EACzNNnpLhCWlyX&CDt?97VpMcO|M>u+;ylY(Hb&@@5*8kj7* z^1(L7o!}yuegF>152Hdj*CBSG$@Ik`9a*^_~JfMV15JYpk8`TS%3hlu)k6 u+Q|osi&Me7fHNTauoZnc8(JyZUbeTOT_B literal 0 HcmV?d00001 diff --git a/barcode/src/test/resources/org/xbib/graphics/barcode/pdf417/micro-variant-24.properties b/barcode/src/test/resources/org/xbib/graphics/barcode/pdf417/micro-variant-24.properties new file mode 100755 index 0000000..cdfdb08 --- /dev/null +++ b/barcode/src/test/resources/org/xbib/graphics/barcode/pdf417/micro-variant-24.properties @@ -0,0 +1,3 @@ +mode=MICRO +dataColumns=4 +content=ABCDEFGHIJABCD diff --git a/barcode/src/test/resources/org/xbib/graphics/barcode/pdf417/micro-variant-25.codewords b/barcode/src/test/resources/org/xbib/graphics/barcode/pdf417/micro-variant-25.codewords new file mode 100755 index 0000000..bcbb88f --- /dev/null +++ b/barcode/src/test/resources/org/xbib/graphics/barcode/pdf417/micro-variant-25.codewords @@ -0,0 +1,6 @@ +221311142322124111114411223111123234211511242213111 +311311111452211211421512123161111133211161413113111 +312211111513411121224412213111223251311111633122111 +222211122324123321213213113112122423131241412222111 +213211114113422322411213122112611222122112622132111 +214111234211221434112113212123411123621122122141111 diff --git a/barcode/src/test/resources/org/xbib/graphics/barcode/pdf417/micro-variant-25.png b/barcode/src/test/resources/org/xbib/graphics/barcode/pdf417/micro-variant-25.png new file mode 100644 index 0000000000000000000000000000000000000000..ad5056645ec9b5f9e0aefebf5e4a82338bb7247d GIT binary patch literal 10233 zcmeI2e@qi+7{{9s)Y*zlT-sGre=#&B7G?>%uto#&YX-qZmW8#jf|P=mYqJuL2`fe% zWgEz>7#OQgz^GYsVJcK%!ra!oXF)IYqc0>i7g~8rC98 zd}BD#SQsA>HafO^&DJ)!@tbwe2gyp^TUVEtHZ5=JsR{1&-%3zr5?cvAg?HDX&QC@? z;rwE~p)pvoM$+il8?ub*XR7RUywECd4L(!(6ti4BGK(`Rb>Hu5VEd*nfS68HY;!9F zyi2*~PIHNPkFlsV4y25q4DFiHS>#eIea%?(OsXnXoi7&Mc^%#sE;2k)uz;XkKymyR z2-LML@cFP?Whl!jnXJ_t@`e14C_q8At;&WvFqfwpRk&MnHY$acFO~``0kI0i@GEe| z;DLel0_EaAu9l(KgdO7wDJ8`&3t-1B$9WWmrBR7ivly%aK2;xwMZ71E22Akq$K_rO@v3&-M`q<^;ly zsD-x6BmHA2Zs{p>d1?yXIO)ik7@uWJfPL!bm`TSTS0vJls!^+B;^`e<=wZ3p)D)<# zI>=U9J2+#+66KD84;PL@B2DW=_)Xk;gHJ?CrhBHm*ef2Z;;uhF0NXtZC_wGI9# zxKr@>!J2|X@sCP+7DsOYcHu^0YYNjbBcAZgyIeq$PaW@z04yNugQN-78w@{fA>y^& zSAupsh)h)hllRpQO{^xi!?G&;fF4d?=z{-gjL_X?H3?ce1+TagiCkA>=5ZGf9B0yD zYC60xnMu(^H`XRAd{ftCBj4s+*6d%p8ZM?=aj|&hZ9GM#01jQOF)l~Ng-JyS!wVY zsuGnN?Y+4_nz?}ysk>p=-N2!^1TkpS8y+CyvETh{iBI8EWT#(yb0x(pO-sR{%rG7y zwl4o#EvR5qdOoFJ;TobT4Jpzntv0dFTkqv42j56yQYh*6W0o;Zr3!`L7@XVV`b{P= zCDEP30>YLgHI*FN&0>!qQF(8sQa4pBaU{VNIX$+7aM;w@}SWIVQ)54N8MM>H# z*prSY1_AnFE?mg$NEF;uxT&yG>E~Wpso=5xe=NTbF*3qA-_gw$-8K#ZU0)I5RFx`F z&FM*@orY+{-W?+cOdG&!Eb>-ub1yy8>5Fil|2}w5&=LHoh$q+OHiJb3%LtrGz1Qmw z=U3F(uQ1_7G7?y%6^kZ5HbMG&2A(JYs7 zU?8tE!k9qPaW?l{n{`?&VZ!HYXx_VV zQxnHL^UOs$riJc4iO3@8I%C=I4^$7E{PaL=1+jX=7TfkD^(gHFL3n^oW3Uqie!KY5 z@3=ZZQ1-1veO0FsxuG(SI+|TbDZ7LkYn1G?oK9G}3O>u^atBpn(MI1oq<@Ms=WH z1dLcODWk6+quQ+l(R;J}kGI)O9N-F(lL)|vutIt3ZNd&fmYuF}KQ;i*2cp7&tEpRz yqp#y20wl7)+rh|tyzC$UQ$;N|u)qer0@$m!z literal 0 HcmV?d00001 diff --git a/barcode/src/test/resources/org/xbib/graphics/barcode/pdf417/micro-variant-25.properties b/barcode/src/test/resources/org/xbib/graphics/barcode/pdf417/micro-variant-25.properties new file mode 100755 index 0000000..f5b81f6 --- /dev/null +++ b/barcode/src/test/resources/org/xbib/graphics/barcode/pdf417/micro-variant-25.properties @@ -0,0 +1,3 @@ +mode=MICRO +dataColumns=4 +content=ABCDEFGHIJABCDEFGHIJAB diff --git a/barcode/src/test/resources/org/xbib/graphics/barcode/pdf417/micro-variant-26.codewords b/barcode/src/test/resources/org/xbib/graphics/barcode/pdf417/micro-variant-26.codewords new file mode 100755 index 0000000..216395b --- /dev/null +++ b/barcode/src/test/resources/org/xbib/graphics/barcode/pdf417/micro-variant-26.codewords @@ -0,0 +1,8 @@ +223111142322124111114414112111123234211511242231111 +313111111452211211421514121161111133211161413131111 +322111111513411121224414211111223251311111633221111 +412111111232342115112413311141211431221312244121111 +421111611111332111614113221111511332125113314211111 +331111151211422112135213131121421223142112333311111 +241111321312323115123112231123332211122224132411111 +232111123212511161213212321111145122211133152321111 diff --git a/barcode/src/test/resources/org/xbib/graphics/barcode/pdf417/micro-variant-26.png b/barcode/src/test/resources/org/xbib/graphics/barcode/pdf417/micro-variant-26.png new file mode 100644 index 0000000000000000000000000000000000000000..4f95b031a5ad7a0e28a75458480be34afa6f4dc2 GIT binary patch literal 13123 zcmeHOaZHox9p+?si&VYD#adlY?+6+)3n!iSw87IUat6g^E(Y2n6lv?F{l03QUnd{q&4k?SecT7)zEX!&UI&O+GH!gNqP5Z&*sbDNvVk|qAJ z>`N0~k~iu5e*C`odEVdiJRJXNUe4S(i{~UHB+T9V;pU$uBqWC5_dkChek9!c@WIN2 z1pb_@n>YUam%Gh(Ha4wX%SvoN{4DqGhAW>O|8ee?w*1;NPiGc9JKldz)!R~?93DC| zQe$k!gigPl%MK1mPPg|pR#M?IG-159gsQ~?JkDO{wIvRYV{M=-^X6MHG%2|3PG4GY zieL{-5Y4`I>3?W*Ec@7&m1y=%pb5btfhm=?iZi>3Ljnz7bHLglJMj(-g?HDbpEmAn z%E51u(MXB$pyU^a4{V&R)Xf&v!A5pAsUP9_xuncqdOrFH56iGKeVG(8%!Mp`%E zd5*Uo9GYyY!l?Z{GrlB_DazTq9X68ZxbAUEc>zPPwvzNZYm7Cv2g(}RoC#^lU=n&15yY~-A#yiV&DS$WRXMC`0KMUWy`K^I(F^ezk&rU$be zYX{51auwZ`P8M`zPYT$j7E*D3HEd+<;MDRly{%MCXH1Ey|{9T`d{(9Sh&_HYJLU&UjkHia(#nm!a`ZKV?`vpEwXajlGPm4QTv;JyL1Z7H3#k7bI$vFPz=2)M-jso;MAl0cnk??m=AAZ5qdL;&lJy3T%KT)fZ+j^CMDvEy&e@APfE@ffrpD~P(8HVI zDb$?+$}D|Ni)epetkr2UIG$rO~I&yL^izaw3?;2K>AyKV%1hu&r= zzyCeJ-s=i6EhZWY^YvI&e}6dW%V1jwW97p5<$Q>aQ3M^~7HwXfhc2Kdpd(qAzHOlAqJ3Bhe6oBDpE_zYFb~F5jCmFRp&B!1>?J{) z0gsoo_SXXUi#tG_e_1*yVVCN;AeTguIe;XdJv1KRfmA{Y_r0Oik+)XC)XQRsKdD}N z3^XVm@-UES<0nDn2mL8J4_b&u^M0867lnz~DEGf3aQ8KW)e@Is$Y^{yxigUsTT=cI zre4;JstQkY2+)a0#q;l$oWh*(rXA!62vR4bWMEgD4fz7dfw*8Vwu4uoe(0@cxmHVxK|#BWC`ptF zRyHhkdjfp&=^MwJgKOw-?Z|#uhK!E!g%PkMhIti%nhSP)vPv8tx*DuZ2FnEedRWS_ z9>M@i-(N-2a`6m>8Y1MU=tD<>;C#5iNXQ(g_Em!Nb}mkt9`LYXskB}&$Rmj>ZV|NV z%76%&Daf|z7F+1s^FOygmgVR}i_R6Jv#{MD?hD4q0ol88GU;AWsS5Kgiqr;SUT!}TTpK=xgh zaC~8TPCWl(1CG|5kwE)JSkj5bSe?wrOL2xUc z4sOQm5ePudr3`RmMI?Z?I43zu{Si4 z;Yt#!#E@8uLU7pstsR)xLS?$UR9Zc>G-ptf>BI#{SUu0@(u5c1>3Z9s`>|l#Ax8zt z-;yf>U%d-0k(VWG3370hRDgU{njUgr&lA%sn&-4d3ZH!?wzx?M`BFHFTUF#%L2JX~ zNA3`c)_78Z5?xxvJQRvwFqL9nKjus^K=Ilj{aIq><6ZX?_^F?u|Bl(3owvE^C&fqp E3d>zP`Tzg` literal 0 HcmV?d00001 diff --git a/barcode/src/test/resources/org/xbib/graphics/barcode/pdf417/micro-variant-26.properties b/barcode/src/test/resources/org/xbib/graphics/barcode/pdf417/micro-variant-26.properties new file mode 100755 index 0000000..27a35b7 --- /dev/null +++ b/barcode/src/test/resources/org/xbib/graphics/barcode/pdf417/micro-variant-26.properties @@ -0,0 +1,3 @@ +mode=MICRO +dataColumns=4 +content=ABCDEFGHIJABCDEFGHIJABCDEFGHIJABCD diff --git a/barcode/src/test/resources/org/xbib/graphics/barcode/pdf417/micro-variant-27.codewords b/barcode/src/test/resources/org/xbib/graphics/barcode/pdf417/micro-variant-27.codewords new file mode 100755 index 0000000..902c6f2 --- /dev/null +++ b/barcode/src/test/resources/org/xbib/graphics/barcode/pdf417/micro-variant-27.codewords @@ -0,0 +1,10 @@ +231211235212113111116312411111122253111513412312111 +321211412114312213122411511141111144111232343212111 +411211511312221114522111421112114215611111334112111 +411121111222531115134111412111212244112232514111211 +411112411111441112323412312121151124412114314111121 +321112121142156111113312311221116141511312223211121 +312112412151121151251112221222311233116331113121121 +311212311132512221141412222111215142333311123112121 +311221322231134215121112132154111122412224113112211 +311131114214132531113112141122231241116231123111311 diff --git a/barcode/src/test/resources/org/xbib/graphics/barcode/pdf417/micro-variant-27.png b/barcode/src/test/resources/org/xbib/graphics/barcode/pdf417/micro-variant-27.png new file mode 100644 index 0000000000000000000000000000000000000000..ca6a6254784d6fa91e69171f8aa94b5f8d8dad29 GIT binary patch literal 16531 zcmeI4e@xTq9mkmrGp{|};$l;``imfOp{*;&IPILEhn&G<%iLY0P@xLNr7cw)-zo?W z?!;V*oFG?**c)>7Y}=LJz#9w0_~s-2j7YOD9+8Sq|OjDcL{XUt6~1 zc6-u4nvi^Xetnv?}Z@2BtnB0KZtm)5@&6BG0D{?}joWlT&g9TW4dr0>8VV!rEm zWiTctO|<{D^k1DitQ$<%C+%Ry_MUy7^>NxqXSN!Sv1bl7Ha%Nf`~1zyVlMmG8~Wg! z5!H$H3%r_=-vQ~;N`K(`V zr*j2It%`+u{`KH7q4m_qY3i}Mv9am%mjX%DL)zer#OWRjWdq;e`-#fkSnZyQ@OIaQ zHG`g#R@troy%qOOJESq%9$t zq@=+wjzsE7qdLs&?jPzO5?8(Ibs2O4u2r8ue7VP{Iq>6G@Nn?z;Fk^W9UKb&tw7Ct zwYMV6JY3OJFOlmhou!CLda-nJ*c-&jc*%;h;IG#+d*#5 zD8D1YcXNAXrkZXK9&2dj6X|4;SyM)I4ejLZw01JPw~ofA-pD&UU_Ge6SCK{ZuA*}d z`usy#?h}$o{W@F5#K@M^e1DDHS;F=CkF=KM{f3Yrq-B<5Tb?zlZsffe*7$4I-ZmGC z?VXyA$-WSWTdsy~mh^byyqa;5+k59HzMC`tKEd$iy!XbglrAL%vn?BfTyI<=K=D?U zgqw5k++(3Av<|vCcBYGA;95-`Mzz9yV#Ir{2pF}&vST`uMQclULFX9Rf2SMEmik17 zdd4TJ?int0)AIBPt1y?4&c-!{(SRo;dD84j&ez(EJBXjO$6tgtI(DJEAO+Nl<)%|(s99}TIV0ee&9riU3i7CRZ zZ<`?gWl2#iWV=P~6J76b+*eX-(v;=>N#rbm2pd;Uomh1B$detesR$saS7gAC;VHpy z3Ent3DBw8xFQbEGx))wd6oGg_M{JFR0?jZt3zVr|2S;}}QqL~Ac}aVfe7{F7^C_OpqZ>0=nTC zf9^PZK~15a5h5oSw3S|ItrjW~M}>mnK9Q5Qojq;;?RaAgs%Gp%62UE6`B44SAA(eY zip=B+Ti|A8aX#51DUQ^;J@w4)PPai7%%}MU=0a$@p%uxI^CnI8!j?7g^%+zkZl)=P zB@^Ceh13K8Hie9j<#w3-})OE^6I7lL@z5hN6< zMkrUwKmphLmi^i|RMU~c3X)KDV^`#vsGxo+&`&?PGZg{z)B$A6dZ@qpwQcl9-tXnbY$S*}6@33Y zOv>$X2^^~}7M~gHWMJJ|oR34EAoB z&*oIsk_LGV8q4fGP(O*94X&EU7r89tU4zC-D-BvOZ;|m1($=|FAkRH2bTWlgkMt7{ zwxl{lhS^b8)Mwo37KF$O`}7KA@@n$I5J@Am`z`?kV-b656*75oi{$*+*nBQ%Bk-jy zMQWg3|QEmtc53)gYk`dseYYU6;0~mQ#9iRQc%E6bYr!Ti#V{@YDjl_Mt=>8K3OGb^8alKtyNKTs0S*r-?Cw z7(sRa{74is$az}_sW~|2=1M}O&y3Je!vVt^<;_5@fisVnQ3IaoWR!6$ z{|NX8&S9WfJbV(V1~wt4FVMu28|VPTfE=CBI)m&4{G}*ukiQs^1kJs#h0-|824ce0 zqe7v;p?6wGDE)*80Kp(;6!#RxEaW-({Ms^bz&t&Oa)UEE!&<>`JIn?!J*qIJp0d-a z<#PeTp$g2<0>s1RyO$_5 z+ft&AGYIE@QFX&r8n|#7B@u`hXk&4%fwMn=ua;hK-L!m< z^Ka;~Wm3gGRul;!Cvv2ub+`Y|5QLpjk;*mNAnY&S+ynycRbGf%)(Q7tTmLJ*JM8b#;{xtINT zPy(A6AqWZFeBFdd3lhyGFpe)t5@A3&?i1DYt<8 literal 0 HcmV?d00001 diff --git a/barcode/src/test/resources/org/xbib/graphics/barcode/pdf417/micro-variant-27.properties b/barcode/src/test/resources/org/xbib/graphics/barcode/pdf417/micro-variant-27.properties new file mode 100755 index 0000000..8de0a13 --- /dev/null +++ b/barcode/src/test/resources/org/xbib/graphics/barcode/pdf417/micro-variant-27.properties @@ -0,0 +1,3 @@ +mode=MICRO +dataColumns=4 +content=ABCDEFGHIJABCDEFGHIJABCDEFGHIJABCDEFGHIJABCDEF diff --git a/barcode/src/test/resources/org/xbib/graphics/barcode/pdf417/micro-variant-28.codewords b/barcode/src/test/resources/org/xbib/graphics/barcode/pdf417/micro-variant-28.codewords new file mode 100755 index 0000000..585e723 --- /dev/null +++ b/barcode/src/test/resources/org/xbib/graphics/barcode/pdf417/micro-variant-28.codewords @@ -0,0 +1,12 @@ +311122142322124111114411241111123234211511243111221 +311113111452211211421511331161111133211161413111131 +221113111513411121224411322111223251311111632211131 +221122111232342115112411321241211431221312242211221 +221131611111332111614111312251131222111452212211311 +221221112232513111116312212211122253111513412212211 +222121412114312213122413112241111144111232342221211 +312121511312221114522113111344111114221531123121211 +321121221121622353111112211322412231122113343211211 +231121121411162414122111311312151214421121512311211 +231112431122223111221611221322134122413133112311121 +222112113116222351121211222231131161411241132221121 diff --git a/barcode/src/test/resources/org/xbib/graphics/barcode/pdf417/micro-variant-28.png b/barcode/src/test/resources/org/xbib/graphics/barcode/pdf417/micro-variant-28.png new file mode 100644 index 0000000000000000000000000000000000000000..f6d139e6bfeb57b96752d8046ec07ed919b4dd1e GIT binary patch literal 20407 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 Q|Fz+rq=Rp@@6D?GPn8?6_5c6? literal 0 HcmV?d00001 diff --git a/barcode/src/test/resources/org/xbib/graphics/barcode/pdf417/micro-variant-29.properties b/barcode/src/test/resources/org/xbib/graphics/barcode/pdf417/micro-variant-29.properties new file mode 100755 index 0000000..69382ea --- /dev/null +++ b/barcode/src/test/resources/org/xbib/graphics/barcode/pdf417/micro-variant-29.properties @@ -0,0 +1,3 @@ +mode=MICRO +dataColumns=4 +content=ABCDEFGHIJABCDEFGHIJABCDEFGHIJABCDEFGHIJABCDEFGHIJABCDEFGHIJABCDEFGHIJABCDEF diff --git a/barcode/src/test/resources/org/xbib/graphics/barcode/pdf417/micro-variant-30.codewords b/barcode/src/test/resources/org/xbib/graphics/barcode/pdf417/micro-variant-30.codewords new file mode 100755 index 0000000..906f0f0 --- /dev/null +++ b/barcode/src/test/resources/org/xbib/graphics/barcode/pdf417/micro-variant-30.codewords @@ -0,0 +1,20 @@ +221311142322124111114411421111123234211511243211211 +311311111452211211421511412161111133211161412311211 +312211111513411121224412312111223251311111632311121 +222211111232342115112412311241211431221312242221121 +213211611111332111614112221251131222111452212131121 +214111112232513111116312222111122253111513412122121 +223111412114312213122412132141111144111232342122211 +313111511312221114522112141112114215611111332121311 +322111111222531115134111241111212244112232512121221 +412111411111441112323411331121151124412114312121131 +421111121142156111113311322121116141511312222112131 +331111112122441122325111321231111163111222532111231 +241111211511244121143111312222131224411111442111321 +232111211161415113122212212242151211221234122111411 +231211143211233111531213112211314151122421412112311 +321211213221152131211613111321112235111116152112221 +411211511121331312512212211312311252511114222113121 +411121123116213123115111311323126111411242122113211 +411112422212311316111311221321231413322313212114111 +321112411122242123431111222231152311512112142123111 diff --git a/barcode/src/test/resources/org/xbib/graphics/barcode/pdf417/micro-variant-30.png b/barcode/src/test/resources/org/xbib/graphics/barcode/pdf417/micro-variant-30.png new file mode 100644 index 0000000000000000000000000000000000000000..74e56816646561ea727f93092810a7eca589f0c6 GIT binary patch literal 33125 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 X(YuF4KmGRr{I_M(wvDwvNh$tcxo^Ik literal 0 HcmV?d00001 diff --git a/barcode/src/test/resources/org/xbib/graphics/barcode/pdf417/micro-variant-30.properties b/barcode/src/test/resources/org/xbib/graphics/barcode/pdf417/micro-variant-30.properties new file mode 100755 index 0000000..87be2bd --- /dev/null +++ b/barcode/src/test/resources/org/xbib/graphics/barcode/pdf417/micro-variant-30.properties @@ -0,0 +1,3 @@ +mode=MICRO +dataColumns=4 +content=ABCDEFGHIJABCDEFGHIJABCDEFGHIJABCDEFGHIJABCDEFGHIJABCDEFGHIJABCDEFGHIJABCDEFGHIJABCDEFGHIJABCDEFGHIJABCDEF diff --git a/barcode/src/test/resources/org/xbib/graphics/barcode/pdf417/micro-variant-31.codewords b/barcode/src/test/resources/org/xbib/graphics/barcode/pdf417/micro-variant-31.codewords new file mode 100755 index 0000000..4a51535 --- /dev/null +++ b/barcode/src/test/resources/org/xbib/graphics/barcode/pdf417/micro-variant-31.codewords @@ -0,0 +1,26 @@ +221311142322124111114414211111123234211511244112111 +311311111452211211421513311161111133211161414111211 +312211111513411121224413221111223251311111634111121 +222211111232342115112413131141211431221312243211121 +213211611111332111614112231151131222111452213121121 +214111112232513111116312321111122253111513413112121 +223111412114312213122412411141111144111232343112211 +313111511312221114522111511112114215611111333111311 +322111111222531115134111421111212244112232513111221 +412111411111441112323411412121151124412114313111131 +421111121142156111113312312121116141511312222211131 +331111112122441122325112311231111163111222532211221 +241111211511244121143112221222131224411111442211311 +232111211161415113122212222111145221121142152212211 +231211311111631112225312132111151341112122442221211 +321211221312244111114412141111123234211511243121211 +411211111452211211421511241161111133211161413211211 +411121111513411121224411331111223251311111632311211 +411112141131331322312311322141121242222113152311121 +321112341223112151124111321211144411121161322221121 +312112611121141132324111312263112211111512422131121 +311212411411412411213312212223131421151421212122121 +311221213251122323411113112241114141421314112122211 +311131113521312231143113111311222243122215312121311 +311122151223211314212312211312222413424211122121221 +311113431511111131135211311311125421622111222121131 diff --git a/barcode/src/test/resources/org/xbib/graphics/barcode/pdf417/micro-variant-31.png b/barcode/src/test/resources/org/xbib/graphics/barcode/pdf417/micro-variant-31.png new file mode 100644 index 0000000000000000000000000000000000000000..08177e454ea9f2df5f3e2c218b4f2ae0828d0e14 GIT binary patch literal 42947 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 literal 0 HcmV?d00001 diff --git a/barcode/src/test/resources/org/xbib/graphics/barcode/pdf417/micro-variant-31.properties b/barcode/src/test/resources/org/xbib/graphics/barcode/pdf417/micro-variant-31.properties new file mode 100755 index 0000000..b6aa007 --- /dev/null +++ b/barcode/src/test/resources/org/xbib/graphics/barcode/pdf417/micro-variant-31.properties @@ -0,0 +1,3 @@ +mode=MICRO +dataColumns=4 +content=ABCDEFGHIJABCDEFGHIJABCDEFGHIJABCDEFGHIJABCDEFGHIJABCDEFGHIJABCDEFGHIJABCDEFGHIJABCDEFGHIJABCDEFGHIJABCDEFGHIJABCDEFGHIJABCDEFGHIJABCDEFGHIJAB diff --git a/barcode/src/test/resources/org/xbib/graphics/barcode/pdf417/micro-variant-32.codewords b/barcode/src/test/resources/org/xbib/graphics/barcode/pdf417/micro-variant-32.codewords new file mode 100755 index 0000000..fdd6cb0 --- /dev/null +++ b/barcode/src/test/resources/org/xbib/graphics/barcode/pdf417/micro-variant-32.codewords @@ -0,0 +1,32 @@ +312112235212113111116311312211122253111513412131121 +311212412114312213122412212241111144111232342122121 +311221511312221114522113112212114215611111332122211 +311131111222531115134113111311212244112232512121311 +311122411111441112323412211321151124412114312121221 +311113121142156111113311311321116141511312222121131 +221113112122441122325111221331111163111222532112131 +221122211511244121143111222222131224411111442111231 +221131211161415113122211231211145221121142152111321 +221221311111631112225311232111151341112122442111411 +222121221312244111114411142111123234211511242112311 +312121111452211211421511133161111133211161412112221 +321121111513411121224411132211223251311111632113121 +231121111232342115112411123241211431221312242113211 +231112611111332111614111122351131222111452212114111 +222112112232513111116311113311122253111513412123111 +213112412114312213122411112441111144111232342213111 +212212511312221114522111121412114215611111333113111 +212221111222531115134111211411212244112232513122111 +212131411111441112323412111421151124412114312222111 +212122121142156111113312112321116141511312222132111 +212113112122441122325112113231111163111222532141111 +211213211511244121143111213211242313151411132231111 +211123322231132131511311214131112414131351213131111 +211132313111432141152111223112242141142212323221111 +211141141124224151211212123113324121112423134121111 +211231221234124121251112213113225112441114114211111 +211222113132421114134213113121112361214411223311111 +211312133241214131142113122111113136212213152411111 +211321411232314212212313212124123113521111242321111 +211411312112521122214414112114212142121111462312111 +212311321214313222142114121114132222141414113212111 diff --git a/barcode/src/test/resources/org/xbib/graphics/barcode/pdf417/micro-variant-32.png b/barcode/src/test/resources/org/xbib/graphics/barcode/pdf417/micro-variant-32.png new file mode 100644 index 0000000000000000000000000000000000000000..7d5c608f0becbd2340c4714d6284594720cc674b GIT binary patch literal 52862 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 literal 0 HcmV?d00001 diff --git a/barcode/src/test/resources/org/xbib/graphics/barcode/pdf417/micro-variant-32.properties b/barcode/src/test/resources/org/xbib/graphics/barcode/pdf417/micro-variant-32.properties new file mode 100755 index 0000000..0f93657 --- /dev/null +++ b/barcode/src/test/resources/org/xbib/graphics/barcode/pdf417/micro-variant-32.properties @@ -0,0 +1,3 @@ +mode=MICRO +dataColumns=4 +content=ABCDEFGHIJABCDEFGHIJABCDEFGHIJABCDEFGHIJABCDEFGHIJABCDEFGHIJABCDEFGHIJABCDEFGHIJABCDEFGHIJABCDEFGHIJABCDEFGHIJABCDEFGHIJABCDEFGHIJABCDEFGHIJABCDEFGHIJABCDEFGHIJABCDEFGHIJABCDEFGH diff --git a/barcode/src/test/resources/org/xbib/graphics/barcode/pdf417/micro-variant-33.codewords b/barcode/src/test/resources/org/xbib/graphics/barcode/pdf417/micro-variant-33.codewords new file mode 100755 index 0000000..c29b0d7 --- /dev/null +++ b/barcode/src/test/resources/org/xbib/graphics/barcode/pdf417/micro-variant-33.codewords @@ -0,0 +1,38 @@ +231211235212113111116313112211122253111513412112311 +321211412114312213122413111341111144111232342112221 +411211511312221114522112211312114215611111332113121 +411121111222531115134111311311212244112232512113211 +411112411111441112323411221321151124412114312114111 +321112121142156111113311222221116141511312222123111 +312112112122441122325111231231111163111222532213111 +311212211511244121143111232122131224411111443113111 +311221211161415113122211142111145221121142153122111 +311131311111631112225311133111151341112122442222111 +311122221312244111114411132211123234211511242132111 +311113111452211211421511123261111133211161412141111 +221113111513411121224411122311223251311111632231111 +221122111232342115112411113341211431221312243131111 +221131611111332111614111112451131222111452213221111 +221221112232513111116311121411122253111513414121111 +222121412114312213122411211441111144111232344211111 +312121511312221114522112111412114215611111333311111 +321121111222531115134112112311212244112232512411111 +231121411111441112323412113221151124412114312321111 +231112121142156111113311213221116141511312222312111 +222112112122441122325111214131111163111222533212111 +213112211511244121143111223122131224411111444112111 +212212211161415113122212123111145221121142154111211 +212221311111631112225312213111151341112122444111121 +212131221312244111114413113111123234211511243211121 +212122111452211211421513122161111133211161413121121 +212113125123123131124213212112412124121113443112121 +211213311611312113223314112133311411211321343112211 +211123116111243212312314121111211164431214113111311 +211132221261122252111314211122341131111131633111221 +211141142124124141222113311121161222123232133111131 +211231111441146211123113221125133111231131152211131 +211222315311211441222113131111221136134313112211221 +211312122232231141522112231114123132221421322211311 +211321612211222211422312321142132122412221142212211 +211411111212451132243112411113231232134511112221211 +212311213221152213314111511132112242142133213121211 diff --git a/barcode/src/test/resources/org/xbib/graphics/barcode/pdf417/micro-variant-33.png b/barcode/src/test/resources/org/xbib/graphics/barcode/pdf417/micro-variant-33.png new file mode 100644 index 0000000000000000000000000000000000000000..2fc64ebb25e838747ad5e679cfe63142a3eee219 GIT binary patch literal 63380 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 literal 0 HcmV?d00001 diff --git a/barcode/src/test/resources/org/xbib/graphics/barcode/pdf417/micro-variant-33.properties b/barcode/src/test/resources/org/xbib/graphics/barcode/pdf417/micro-variant-33.properties new file mode 100755 index 0000000..13d9338 --- /dev/null +++ b/barcode/src/test/resources/org/xbib/graphics/barcode/pdf417/micro-variant-33.properties @@ -0,0 +1,3 @@ +mode=MICRO +dataColumns=4 +content=ABCDEFGHIJABCDEFGHIJABCDEFGHIJABCDEFGHIJABCDEFGHIJABCDEFGHIJABCDEFGHIJABCDEFGHIJABCDEFGHIJABCDEFGHIJABCDEFGHIJABCDEFGHIJABCDEFGHIJABCDEFGHIJABCDEFGHIJABCDEFGHIJABCDEFGHIJABCDEFGHIJABCDEFGHIJABCDEFGHIJABCDEFGHIJABCD diff --git a/barcode/src/test/resources/org/xbib/graphics/barcode/pdf417/micro-variant-34.codewords b/barcode/src/test/resources/org/xbib/graphics/barcode/pdf417/micro-variant-34.codewords new file mode 100755 index 0000000..835c6e8 --- /dev/null +++ b/barcode/src/test/resources/org/xbib/graphics/barcode/pdf417/micro-variant-34.codewords @@ -0,0 +1,44 @@ +221311142322124111114411241111123234211511242113121 +311311111452211211421511331161111133211161412113211 +312211111513411121224411322111223251311111632114111 +222211111232342115112411321241211431221312242123111 +213211611111332111614111312251131222111452212213111 +214111112232513111116312212211122253111513413113111 +223111412114312213122413112241111144111232343122111 +313111511312221114522113111312114215611111332222111 +322111111222531115134112211311212244112232512132111 +412111411111441112323411311321151124412114312141111 +421111121142156111113311221321116141511312222231111 +331111112122441122325111222231111163111222533131111 +241111211511244121143111231222131224411111443221111 +232111211161415113122211232111145221121142154121111 +231211311111631112225311142111151341112122444211111 +321211221312244111114411133111123234211511243311111 +411211111452211211421511132261111133211161412411111 +411121111513411121224411123211223251311111632321111 +411112111232342115112411122341211431221312242312111 +321112611111332111614111113351131222111452213212111 +312112112232513111116311112411122253111513414112111 +311212412114312213122411121441111144111232344111211 +311221511312221114522111211412114215611111334111121 +311131111222531115134112111411212244112232513211121 +311122411111441112323412112321151124412114313121121 +311113121142156111113312113221116141511312223112121 +221113112122441122325111213231111163111222533112211 +221122211511244121143111214122131224411111443111311 +221131211161415113122211223111145221121142153111221 +221221311111631112225312123111151341112122443111131 +222121221312244111114412213111123234211511242211131 +312121111452211211421513113152111223421411132211221 +321121311261211314114213122111441411612131122211311 +231121113133144321113213212115121511142131232212211 +231112112211633221341114112112125123612311212221211 +222112123312232131133314121111111246612132113121211 +213112231111261133412214211112333311112431233211211 +212212251331113411312213311114134112321422122311211 +212221212115412154111213221121441122314212312311121 +212131133231131222322313131141331221131421232221121 +212122141143121111633112231112316112331124122131121 +212113111512421521123212321111322431242112412122121 +211213112243312214213212411133112142122521132122211 +211123212142144231321111511134132211321332212121311 diff --git a/barcode/src/test/resources/org/xbib/graphics/barcode/pdf417/micro-variant-34.png b/barcode/src/test/resources/org/xbib/graphics/barcode/pdf417/micro-variant-34.png new file mode 100644 index 0000000000000000000000000000000000000000..0b4cb27ff4b0f2dead1450660a794d37e53372fe GIT binary patch literal 73094 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 literal 0 HcmV?d00001 diff --git a/barcode/src/test/resources/org/xbib/graphics/barcode/pdf417/normal-basic-6-rows-6-columns.properties b/barcode/src/test/resources/org/xbib/graphics/barcode/pdf417/normal-basic-6-rows-6-columns.properties new file mode 100755 index 0000000..e4a3941 --- /dev/null +++ b/barcode/src/test/resources/org/xbib/graphics/barcode/pdf417/normal-basic-6-rows-6-columns.properties @@ -0,0 +1,4 @@ +mode=NORMAL +dataColumns=6 +rows=6 +content=This is just a test. diff --git a/barcode/src/test/resources/org/xbib/graphics/barcode/pdf417/normal-basic-ecc-level-0.codewords b/barcode/src/test/resources/org/xbib/graphics/barcode/pdf417/normal-basic-ecc-level-0.codewords new file mode 100755 index 0000000..0d4a9a2 --- /dev/null +++ b/barcode/src/test/resources/org/xbib/graphics/barcode/pdf417/normal-basic-ecc-level-0.codewords @@ -0,0 +1,7 @@ +8111111351111152211121363411133141111144711311121 +8111111351111125421114131131226141111216711311121 +8111111331111163212311431432112321111155711311121 +8111111321114251432111322411132311114243711311121 +8111111321113216311126124313131141113232711311121 +8111111341114114214211242442112151113411711311121 +8111111331123151322511212321131421123143711311121 diff --git a/barcode/src/test/resources/org/xbib/graphics/barcode/pdf417/normal-basic-ecc-level-0.png b/barcode/src/test/resources/org/xbib/graphics/barcode/pdf417/normal-basic-ecc-level-0.png new file mode 100644 index 0000000000000000000000000000000000000000..260a6a0678aedadedde77d4dc58884ddab12ca1e GIT binary patch literal 11111 zcmeHNZA?>F816)Hvo-#h;Oer*O$}jfVYW`EtTFPEJ^H}}W6 zx%WNqd7tNfpXVO?n3ue4>8hn95@}h=hue0NNQ)!D{k0dth4kpEJdQ-7$EIvc+;vD` zyppI~yonNab@K7Gm(HcM9MgXA`^NdKo!S)yh_mt+rz*k;Im<@L}l!_c_nDpB;O_H=KhsiAqHyRJ)G4^P^df3VyrAgLb zK^bVbtbb|^dt67!R_S*5U?4Cf@MysrfvEL2@H?}mG4Av&<;0kF>a#o!YxF_a8}YCD z`I4(*hbsN2dlklHQHPk?B@j0|+1!F5{U{~7?OsL0WTavD_^H^AWYHXrkBIj^u42wn z0>xGQi|PeIZ!`=k|AE8Wt*qh=8tbnJj9ACpO zt)XjmnzFvA(#rWQee~Q38FCptQH~e;1{2txR!RJop5ixi-x&9H}~92mJgADQK$&fzOmhx6{%QSr%1A?>|4%A)mP^X_KYEhiDJ+=16&PhsHu3Gjw4u)#g4XX zXqhz?O}?I9u(*+uod%u+4ZR(JSa72cUjsJ{o-|lD5Nw`T=%j(pxh7nD-+shQfcY4U zyBv$bHft(`l{D>r#F&6<-jni*9vpXt2r}w+At!2Jq`=Q4H@HBuJca_hspNT%IoDXXJv>SugC8g$8|I^a(ivb z4(v#kMz^px3E}g=dhJ4-7e)x@|s${biSLZvY?DwdBY?}=ntkK7$D3b zJfg4$A;=IC_kRgRsVJc<!=L;A@X}G1bEz4FJDM}l{7-fd4EJOa+$5rZ@-& z4phI|A8ZWR0Niu3YP}iXnN~%q>C`S?5nuahy@4<<>RU`c$$_R8n=Tv+F(4F7296yC zlTKhv{RPm@0N>Pu8AJ|Dl156PQ=Da{7!}Nc5TgUo5#S1h6Xj4R2=(mkpo2T3pP|#VzL>0IB4d}-e&CKvj86|i9w0*8P;*jT-Z2^3)W~uyaGU% zKM+^4Ef8Z~9xxM#yfoO8z_DH}_;8eA&0R(&6h2uu zotWkb-(=95E7ODV3qX!73v_@3{xY%P2Clzm>zjagETCj_8=wRz4wQ zvDmKiC#WpC9tM=Hkmesg_1B-W;BLU(0R0QB6o?!Da}?cLo-UUuW`7tY{-8}s;%!sD Hzvt+mNdttb literal 0 HcmV?d00001 diff --git a/barcode/src/test/resources/org/xbib/graphics/barcode/pdf417/normal-basic-ecc-level-0.properties b/barcode/src/test/resources/org/xbib/graphics/barcode/pdf417/normal-basic-ecc-level-0.properties new file mode 100755 index 0000000..14c709a --- /dev/null +++ b/barcode/src/test/resources/org/xbib/graphics/barcode/pdf417/normal-basic-ecc-level-0.properties @@ -0,0 +1,3 @@ +mode=NORMAL +preferredEccLevel=0 +content=This is just a test. diff --git a/barcode/src/test/resources/org/xbib/graphics/barcode/pdf417/normal-basic-ecc-level-1.codewords b/barcode/src/test/resources/org/xbib/graphics/barcode/pdf417/normal-basic-ecc-level-1.codewords new file mode 100755 index 0000000..9fd5987 --- /dev/null +++ b/barcode/src/test/resources/org/xbib/graphics/barcode/pdf417/normal-basic-ecc-level-1.codewords @@ -0,0 +1,8 @@ +8111111351111152211121363411133141111144711311121 +8111111361111232421114131131226141111216711311121 +8111111331111163212311431432112331111262711311121 +8111111321114251432111322411132311114243711311121 +8111111331113323311126124313131141113232711311121 +8111111341114114214211242442112151114221711311121 +8111111331123151151131322233231121123143711311121 +8111111311116232542111121112542111116133711311121 diff --git a/barcode/src/test/resources/org/xbib/graphics/barcode/pdf417/normal-basic-ecc-level-1.png b/barcode/src/test/resources/org/xbib/graphics/barcode/pdf417/normal-basic-ecc-level-1.png new file mode 100644 index 0000000000000000000000000000000000000000..52aa836cfcfb6fd515870586a238669b7bb2d2be GIT binary patch literal 12961 zcmeHOe@v749cIVmHmNRI=9+3S?wnjWu5fGB#xyWIHB5fpGH&B%QKYn(Vyk7osI@vY zxXd4jfMBhNx8+KfL7@~{N`c;Cz*Z>5BPlJVu$#q6+7~859Yu7X?^`u{@xLXz{mA;e*1Yp&+~bn=hNT)JS}nE+Kp>NLPFN<|JmMOgoJ!M3_kzuJMbq&*RkvSAt9`y z{d;$RdLpmKx?5eWSr_`icXsr<+xLIDH;d^OK4CYY< z(I3>;N$REY0(vb26Lg*DrD!+Pd%BkgCbzP6T3Wl!`TjEX5bEoq-Y%+7C|LQ=0;Efu z5umk4j&0*`PG&T9G;w-bBNQ{G?Plx1iw(xd>;?P>Eziq)5(ULVMp1+I6Tc!~FKk(0 z6s4+@#o~E&rYK8tDxb?5oh!Pgys{FFJrF*2MddAp%dQSNhFK%=!UiAvuGp40!ydVr zTQ@P`;c4t`lM8(qUp4jMJ~mX!NXnCzRF?t~of-{yT~nM% zyfjhlJD|A~zh^4#$^8Qk8Git)E%WbM<~J}fnLnwa%4GP<9ztIXAc5$gMjpDo(UN>`N*AbDyUxGZ|r3H4ele zTl$s7${_)By~e%W^>9PAmv!U{Za$TJV`hEsja<6O0>~?G<296Fu~Q!ymdL|CTc;Gr zx(`Wts%A|@*41#fyDYrgdx(j6v54{i57*TvLmP@qO1Kqv+XubCr^;c(?_| z!@7IXkz3Gj{ap?F^JFQrefkf>8w%(41U^n1EeatgR@ zo9TkCPMv`y`gwac*i}cPZIZPNPI&TiQrJGNQ7*rI*AxUsZJsW>u(P3V9>uB z?s=qNVG@WnNaK1`o<{>QAF_RwO5lp7du!0x-I#lpjICe79G<&{W5Oi{1drJQ$B#H5 zHjtb2+lgC%d2un7FcovB17sjy4`cGfcMMBM&JH3hwzKZ8H$)i>nsD^wM*y(iV`ZQw z+)-nzSfkYrz-qQimU9T;2MfxaYO!A7WuhkoniJQEoJ%vjAXC(NnEpEx#AjSnQp7SH zL#=znNn)|D4?TGoPzSmO=Va;qMKT{ofNFxfKXtvRMU>WDcp*ZXE-fLXl{khIGjmTPr zzQ~%12}e14atSanx`uduw#K`Pv8WPKC8WBL>camwAO&xR?MneaDh)<_@SJ8BTSolz zvkS2x0A8Uf*hp_F675hc8~(p*I>3hdJ2UbBrhe*D!Jr5-BdSubrakKQ3+9bns;6Gt(G>c5Y&&Fc|@zP*2E$q^VgaDL4RTX%f=GVH(k22nWY(Ja6(k(jzDnu#D*AjD#vk z4`4Qut?zY~7seU4zyRO7Blin1e*E)lls%v*CHX=x?hJ~8ukBa-1@H*Fkc3Eit z`W-NtbakaDvMEJD2W#PoO-yKlnqIe-Ob?^EY6g;nO@6ceo+FsinzX+8jdEeJ*)M4q$HcGJyv{ ze?88*y9SIp;6{*8^Rp;h;XiiWb`zQ|N^TUA{5K%nw%fc)&@EFNV-A8TC_9;f#3YKO zw(Xhn`v^&wI^&8Xd3{vr!aY>!sIH^Bu2T8G379a(fb~B!>|i=;+*OJdq|Bm_B0(ym z8aE>>v=Rx%D%E-!Hz$Zx+yXs69ZMGYkrxMUT#XkAi8S5T^C<5i0*O^77hAVAv{^BG z*r5P-Y>^w(&5mxqa%q4=l0M?r9m zAJ%c?<@FYF5Hf(tArCiOqhOcJG#!p+cbuImViYybmm|56rtO)D2Aht|WDs-2=y0S?WWQYnf)x4jcbt z@UWxBlE7MU+(=w>84tiVK_!@Q$>KAEbR@W3!d5$LBrSN@dVy>-!=YG}UqL`6>pnMe z3zo{B<^*pa4@prO_5pENe7j58+c)eX(`A7%9yTNpA6F@-g17fGVB57lJ$Tqg=j6f$ zEOJ$HZ&&z mo>W)8{*lHv1As5S_xClnJ*<7r_t0-o_U}vEtN!@lxqkpZ(F6nl literal 0 HcmV?d00001 diff --git a/barcode/src/test/resources/org/xbib/graphics/barcode/pdf417/normal-basic-ecc-level-1.properties b/barcode/src/test/resources/org/xbib/graphics/barcode/pdf417/normal-basic-ecc-level-1.properties new file mode 100755 index 0000000..17e8082 --- /dev/null +++ b/barcode/src/test/resources/org/xbib/graphics/barcode/pdf417/normal-basic-ecc-level-1.properties @@ -0,0 +1,3 @@ +mode=NORMAL +preferredEccLevel=1 +content=This is just a test. diff --git a/barcode/src/test/resources/org/xbib/graphics/barcode/pdf417/normal-basic-ecc-level-2.codewords b/barcode/src/test/resources/org/xbib/graphics/barcode/pdf417/normal-basic-ecc-level-2.codewords new file mode 100755 index 0000000..7f552e2 --- /dev/null +++ b/barcode/src/test/resources/org/xbib/graphics/barcode/pdf417/normal-basic-ecc-level-2.codewords @@ -0,0 +1,7 @@ +811111135111115231112144341113311212242351111152711311121 +811111135111132311312261221151321414411141111216711311121 +811111131111124611451311223411316111322121111353711311121 +811111132111425121333221322412212332211321114251711311121 +811111132111341411611124331141313511231141113232711311121 +811111135111412212232241133114222224114141114411711311121 +811111133112315123112332133142212331241131123151711311121 diff --git a/barcode/src/test/resources/org/xbib/graphics/barcode/pdf417/normal-basic-ecc-level-2.png b/barcode/src/test/resources/org/xbib/graphics/barcode/pdf417/normal-basic-ecc-level-2.png new file mode 100644 index 0000000000000000000000000000000000000000..3e20ce24e604491a760d5e07dda518d1b8e9e090 GIT binary patch literal 13222 zcmeHOZA@F|6=tK*4ID-41eJqgWkp>S8FvyELj>KVln}<+k#(tNJo6F4wJil3lVrHX zE+8^NwLk>UhbM>Fj8tt@0sgoi7l`9PX+uo-Fr>q9bNO1^6lb=Hwiu&A2|F*)X{LWR zDf=_MvV<&Mbnki3d(U~!^E|Kr@T=VHT`_xNA|fJo<^24}uOlL!v%>fLKY%|GHEFUp zBO-*#oFf@;y;ImZnqht}l@mEJvvL22f6n>%NJ{q8SyhfMba>-k|NMq?h~pHnWp)AW z8XMB++lqEIyz=%9#bH~WwBBUnacL2`n6q?Loy$BOrCCLTfO?V|;u&lv>B4H-+Lwof zL0aJ$rKev?-|rLGSEnT1oH0`CpT_ICnn4XYU8pne?@eIRq-&|$51F6Vl7kw3t#8qP zX>DYjr2;==Pd50kb;!A^lUDDUx9^31B*~L`xjtR7HcM^#vW#539GKsJNKMIO9oHw0 zHn-Z3EeSJK!&@<(rA$(<=#%Tivj^B?ief{Xyd%3l5XV$rbm)x7=^FdiL%zA-%$C7} zdaB0`^}6xG@s8tH9oIN+Zn&#IJ2+FiIRv>#63L%w^L56A^*FPY&v?|Y?_NJOmvm5) z=V7kPy;Fk1%1`DJg&sR6|FFDuMqWRsS88S69|MZ&40B(c_s@7O<#h!^G%eA1MNecC z%m6hOPh|F${rz6TPW)wsnY)=ZanneU2WWk?rjGCb1>H4!KeO*@YQtE(zJm60S1TGs zbJi7C<8o5ul$+VVVrc7@vL>zu!9MxnO3zE_sjK|_ViDCD(DPO&Z;5|LTRZGo?s1Fc z)<5cZ;$~=MJX4+PNfbKo#xcDUMSqQPTvRH%gX5O3Mz7oJq(*_JzIyiaE2(}Wxr7+* zUp^Zes*-t?3@xwcQD-Th;SphWja_&eBbvUVD0O`BUUiBp-Y7`&DJFzLU%cg3@lK#8 zgu!OxrDfL}k56Z*%Yvew0W&FK1w^pD((Y9hi{%ZbwxaHL9`4dMjpVtL%h#_}ooVH2 zBy_;g_8!a^*)mNNpE~<~-$0!DW%fbYqqd^amMnSf@IgtCvwrgqU$t{G!CQ&95?3Xz zO2z+wgDr6?qs;|NWz&3eZ$oFmqahd0`>!D)N6J{AKW-R6O+Hfu5T$$=dqc4iMx+{- zX>O0Y*HLU~u1EsJ^66@!(IOt#Y*1S4q9oM0PuD;n?!NLZz!7l9LGhj#@a638%r*Br%J-5%iZd(j*?pc zH=geG7|quIH&#ah%7WfnRDLIPYU{REzM<`T$8SBhSJl;8drCOA(jR?H{4mKeSsG4tc}F;Ak8Zde0>d#Hahuc-snVI%>gnJJvh zkN>_vAPnAA#gi)_IP`ZRGZ*CW3ieJF_`Kx8DTsm@K#V?3swEdy;nMQQW?0Z{YAmG< zotGj(;c@`P;+uibZ+A;2$>k{!1u}*~38skec-SyH;BCg+jH?+}Gj7`dUDIZ%PH6!m ztVk~=C?WL(G-GshNPPGE40roec`}w-YN?a%1v`@bj?ac~fRl($g5mc7=K#M4Tc;v% zE#O*s2DKpcBu0wt0mJQwz@DPbn>sV-BlbXT6eXlZmjcHhpALZ2iZ04np^6SClIR3* z&sC*6C;-j~B#JpC)du-qgK~FLv(e_fs%X_TmC9q`h!0$3O_e(Z>c(YP|D;0up{f^{ za&7xi=m7hEQHEQgJyGj#3`~-1f6?ZrOPb-VZ*4mh8fobapFiOg?`Bw4!$2H4`M;!( zF+5PqWo%?DDc@At=e~HroEt$GM``(-BFvWN5Zhrrk|4Ugu&IoJOfO`4dHd2!w6L0G z-V_WZn-^)~iL5l7Qf`O!#{3yzkC7-@bavl46BFu($%cj1e6-S|ruTiDdpoRE zJ#c17qoS1}R^x&r^z&dHOc%mxoOiqIQHJdt-(vQJ%Lm(Geb$YXf__NW<-i2k4K!hW zRXvFOfxi)EqX15HZRYr^QBU~!rEkN+TaULM1SVYdxax7k{~tB{jqiQ*!n=pbJ<(n0 P2RJ!dxkt>eojCVTjtsHi literal 0 HcmV?d00001 diff --git a/barcode/src/test/resources/org/xbib/graphics/barcode/pdf417/normal-basic-ecc-level-2.properties b/barcode/src/test/resources/org/xbib/graphics/barcode/pdf417/normal-basic-ecc-level-2.properties new file mode 100755 index 0000000..80c2ab1 --- /dev/null +++ b/barcode/src/test/resources/org/xbib/graphics/barcode/pdf417/normal-basic-ecc-level-2.properties @@ -0,0 +1,3 @@ +mode=NORMAL +preferredEccLevel=2 +content=This is just a test. diff --git a/barcode/src/test/resources/org/xbib/graphics/barcode/pdf417/normal-basic-ecc-level-3.codewords b/barcode/src/test/resources/org/xbib/graphics/barcode/pdf417/normal-basic-ecc-level-3.codewords new file mode 100755 index 0000000..5fc1060 --- /dev/null +++ b/barcode/src/test/resources/org/xbib/graphics/barcode/pdf417/normal-basic-ecc-level-3.codewords @@ -0,0 +1,10 @@ +811111133111123541112152341113311212242351111152711311121 +811111135111142211312261221151321414411151111224711311121 +811111131111124611451311223411316111322121111452711311121 +811111131111515221333221322412212332211321114251711311121 +811111133111352111611124116111243121341221113315711311121 +811111135111412211211533314111336311211231115213711311121 +811111131112323415121511131423212414122131123151711311121 +811111134112121511124116333141111314421121116141711311121 +811111136112221211411216121211451211134461123121711311121 +811111131113252223212421111214163115123111132423711311121 diff --git a/barcode/src/test/resources/org/xbib/graphics/barcode/pdf417/normal-basic-ecc-level-3.png b/barcode/src/test/resources/org/xbib/graphics/barcode/pdf417/normal-basic-ecc-level-3.png new file mode 100644 index 0000000000000000000000000000000000000000..fc614e6204e0aa1597ffadefebc65ef597d61b70 GIT binary patch literal 18533 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 QgMY7Y*z#({D?1PU52K{PjQ{`u literal 0 HcmV?d00001 diff --git a/barcode/src/test/resources/org/xbib/graphics/barcode/pdf417/normal-basic-ecc-level-5.properties b/barcode/src/test/resources/org/xbib/graphics/barcode/pdf417/normal-basic-ecc-level-5.properties new file mode 100755 index 0000000..1b760b0 --- /dev/null +++ b/barcode/src/test/resources/org/xbib/graphics/barcode/pdf417/normal-basic-ecc-level-5.properties @@ -0,0 +1,3 @@ +mode=NORMAL +preferredEccLevel=5 +content=This is just a test. diff --git a/barcode/src/test/resources/org/xbib/graphics/barcode/pdf417/normal-basic-ecc-level-6.codewords b/barcode/src/test/resources/org/xbib/graphics/barcode/pdf417/normal-basic-ecc-level-6.codewords new file mode 100755 index 0000000..d7c9a49 --- /dev/null +++ b/barcode/src/test/resources/org/xbib/graphics/barcode/pdf417/normal-basic-ecc-level-6.codewords @@ -0,0 +1,20 @@ +81111113211113262111213634111331121224232315112211222414132423114321113221111326711311121 +81111113311123152222431131112612431313111222116226123111422322115122111451111323711311121 +81111113211113531312144122411322112215321145212115331121241211511223214221112361711311121 +81111113411211431421211522111424232215111112424214113331332114213322213141121143711311121 +81111113311143312611321122325111112262214321211314215211221152311511421221113414711311121 +81111113411144111151231352133111521131132541112113432121145121121542121121121154711311121 +81111113211233413112224215112223332411211514111321141521314122131114333121123341711311121 +81111113411221242512321131132412124112421222511342111512412132221113613141121116711311121 +81111113111223521232142211132153224113221341111511111246131412412211611341125121711311121 +81111113111332333112234113151114151113141121312641221232211121361211252311133233711311121 +81111113211243226231111232112215251131134212212322325111522211134112313221123314711311121 +81111113111321535213311151214112211113531123153132621111213122421422123221136211711311121 +81111113311511321413222251511211112332233113214223141222112424122142321231151132711311121 +81111113211331153114312221153311514131111211542141131115412321135115112141131313711311121 +81111113111521511222143212142151152211321334112214512112115133213211531112111542711311121 +81111113321121432222323121123341512112411232321342321221131131341312131532112143711311121 +81111113411421221113532144212211121253214113121461412111422223111512411211134511711311121 +81111113521141216113211214311223123322311231314221311135114311153131134141211161711311121 +81111113312114233114123242111341413213211316131111132324333121222122242231211423711311121 +81111113211533113111352132213114211241241122612232123321112261225214112111145122711311121 diff --git a/barcode/src/test/resources/org/xbib/graphics/barcode/pdf417/normal-basic-ecc-level-6.png b/barcode/src/test/resources/org/xbib/graphics/barcode/pdf417/normal-basic-ecc-level-6.png new file mode 100644 index 0000000000000000000000000000000000000000..bb2a8a7cff22de4e8850a3ad086890d8cce63450 GIT binary patch literal 61741 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 literal 0 HcmV?d00001 diff --git a/barcode/src/test/resources/org/xbib/graphics/barcode/pdf417/normal-basic-ecc-level-7.properties b/barcode/src/test/resources/org/xbib/graphics/barcode/pdf417/normal-basic-ecc-level-7.properties new file mode 100755 index 0000000..40bf5d6 --- /dev/null +++ b/barcode/src/test/resources/org/xbib/graphics/barcode/pdf417/normal-basic-ecc-level-7.properties @@ -0,0 +1,3 @@ +mode=NORMAL +preferredEccLevel=7 +content=This is just a test. diff --git a/barcode/src/test/resources/org/xbib/graphics/barcode/pdf417/normal-basic-ecc-level-8.codewords b/barcode/src/test/resources/org/xbib/graphics/barcode/pdf417/normal-basic-ecc-level-8.codewords new file mode 100755 index 0000000..15bd547 --- /dev/null +++ b/barcode/src/test/resources/org/xbib/graphics/barcode/pdf417/normal-basic-ecc-level-8.codewords @@ -0,0 +1,41 @@ +81111113311121441111313634111331121224232315112211222414132423114321113224111323311132512133322132241221233221131423221221112136711311121 +81111113411125211161112411611124116111241161112411611124116111241161112411611124442221112115311312215312442121121121116441112125711311121 +81111113111121553161121211414141331251111113225221141152215412113112126112523121143122311211326161223111113122341126123151113213711311121 +81111113111214161212222523212421211511242222141314222411112222161222342123212223121512144122133113313312123132143332131131121333711311121 +81111113211151331112451212412151221342211421116111412251131351214112242111311352421111165212132122124123121451211134115141114141711311121 +81111113311154115111331211421116111421521113215312613112122122431313215116121141111411442232123221231242141312413241113221121352711311121 +81111113411311422324131114151113121331333231131314112323221422311114222413212413142131232132251133112142131412143231151131131134711311121 +81111113411223225121131353121221131341135312122112312161114411414422211111311451411414111161112441252111422221131262112251121322711311121 +81111113111232613251112223111252254111212111621312242141121115421451221111433131123111262212621111141441221312512232113321126311711311121 +81111113111412161411121622221215314122132222141323211314424112122114142242111143512112411125221341221232222422212323222141141141711311121 +81111113111252231241114322113116111542121211512412145121221144213315211131114133361121123211221541142122241133121111613321124124711311121 +81111113111331611422133132421131133112241112215422441121134122221132223311151143113132423111531213232141144113121232213311141441711311121 +81111113211514211214212413232213212115141132422215113231121411163221152131141133111131361112323411151116132232221423211311151413711311121 +81111113311333211115411312114314313242114213121332114231111351234311151114411141442221112131116221125132411112166131111331132214711311121 +81111113121112451321133311331422223311323311511212321224411144113114511131216112116231121241161122311134231111532132143122112261711311121 +81111113121124244141111432422112112331241212212632321411132132231115141331311314333312111115232212132125231411231211151522112333711311121 +81111113311431223412312151312311121442122211523141222213421111165322111232122511421212143111322411411243331521114121211551141122711311121 +81111113321153114112512112221234114231321143131315211331123115223411511111633111152311311432213122311233112112362162221131211351711311121 +81111113212121261413212323131223232113142122161211113235223125113333121132131331113331142122141412224231242211141223312342121142711311121 +81111113411612114115131112231161211532125122141152141121113111541321612111631122511321311222521223124221111143151113512341152121711311121 +81111113112124421441121311432222335111211312124314512211221162122353111132421131116132121211326114212241115141311422214111213351711311121 +81111113121232331321241314122124121134323131242111243321312211252512213112314222222115131133422121241511122232232321242111214134711311121 +81111113321131243111342252211213332132121212441232233112521221311161122326123111411412131212522241212412511122321142215142112124711311121 +81111113112211364122511122126211124411132351212121211343211261133222115132321141221111542341122221116312224114211163311122131152711311121 +81111113312222324131132222112333341211323221112514112422111611151215111524231113111333322222161124232121112432221321211612131315711311121 +81111113512111151151123321135131331131231112542111531132124211424211141311521133231131154321221214311151311424113315211122114322711311121 +81111113421341111423123142114311142213311212215313422122243211312142122332121161123311241621113231331141163211211223214211231531711311121 +81111113312311242112223423151122311322413141221342412121151215113125122122123241212314131312242251211241122222151323221321231116711311121 +81111113421222223114211413411241322133121152123242212114451311113132421131122611111245124323211121123413412421122411311451212222711311121 +81111113112421424311411212112451214412212112621221521312121122531113154111211236414111412111621312422222125121142431123133111161711311121 +81111113121422231241411312131612131113162214122321112136221311255121124123231114132221151324312114121116211122351142431111233124711311121 +81111113212152223114221334132112411224211213522131133321215112411122511424123311312333112512321152322111112111643111231521214313711311121 +81111113431142114311411211342231234211222124124111442122311162212351212111313143112214332132143111441411531131123213511112211433711311121 +81111113121515114142212111115152212115141412323131241321113151323125112214151113214232121513222114111513222222232311314211242412711311121 +81111113412223124111222411411243211251323114322111125322123161122141611153121221542111124212232111134313211335114212313161221221711311121 +81111113122131521441111413212341125121141421123313431113134214111332223124511211122321421132233214211134122114333161131111311226711311121 +81111113331122413116113111252411221315211221241423213231314124112412112432121431112232242411112521514112333112131514131123112233711311121 +81111113112253125421111233113123161241113611211242112322111352221111522424113312111353214212151113216121321231231253113121224213711311121 +81111113113122342112611341124311111215421131314312413132222112431522123122622111123132411143222251123113541131111341111512222242711311121 +81111113331212321113323334231121321312323211214313131116214232123211224213213124142223121221332331412213311222423323112223121224711311121 +81111113312341216121112323134121131153214322211225113113312331131113411541121512411322221131135231224221421413114111232342141311711311121 diff --git a/barcode/src/test/resources/org/xbib/graphics/barcode/pdf417/normal-basic-ecc-level-8.png b/barcode/src/test/resources/org/xbib/graphics/barcode/pdf417/normal-basic-ecc-level-8.png new file mode 100644 index 0000000000000000000000000000000000000000..d0a33a18e4ed80149786f3ea3824274787979c86 GIT binary patch literal 195438 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%0DZ1&Zg%y{$r1mVlmND(&-K zGQj)C$B)||Umx7RaR1ETAN>8x;{zUF@%Yqz?6a?#$Im?f;Q1TQBfs3w^GlwebNt}X z4}ZyL`|4%Mm-|cPSA;f=FeT6!A6Ax`X72yu{C8X1iL%Fq%0|vIoUbx;bquDzQjF4K%;DUe{I;FH=Zm`*N)1 zGqI7c(5;EF>S*5|8~qEsd%~PiHL3ZR`b+TPGXU0bSL?4k6Xu$$FIi|C-E1nOlm8md ze0TC+r2pcY^V)3Vt%E=go*c#DvLJhVxxc`81LluTt&0(YN?z>?iv4DKQf*PJpQm%CwipC~SX~=F z+yT}B?yCM=U3E~=dAm0@s6kN3S6w_-p0?gHJ0l=zs&uNfwkZ2(bWBi#_1<%3f%d-} zS0oetalZ@RL5pit_bwY8GflRU_Fo41V0;Fv&#ukhVM95h0SuREYM^tu5MrLJahScu zX7AV&1NOf&mA)!Y4*z6ea@pns1~z|#6Ny<6j|aeq3pFe0uKKU^t5*o>gru_k^)jJ? z*aqi`0WoqUVOYb};*$LJeOo*TtQ^}~=*Y(X)@9@$jn;4Jvh+{1xklRGm)BC9UdPJX ze@C0>VdRMEB4L&NiVS7E=bURg%`{v1C(7q%eMj=Z{0;B2+QKmHlj*}L(pTuN;$2zM zbWu_zWkiVxQ)!PPX4OE$F*p6AY9dP7!IvxNY#(on^car#JgcMma0iOn9^5M;Whl6ZB>6} z*uK1&usIto?W#MtpU~qwn3xe}RYsS>@v!!vXW{#|qv3Au&bT|{?u@%L?#_60#-lSH zo$=_5M`t|Q{O^-&E{%Vqcg&dIjCuRbMZe^U=l_|+6EMr?>%VUA>fH|TfrJVGVO9|q z0}x>fKnnUPP#iwBG_L1m_dk%w%~3!&u}&9^q%>)rC(G&pnLt$mD4`JmjS3>Tl9?@AhJ>()#Zgi^H$NNwXAU~fljXdX$!5T#FY;U!lOs=w&dJ+CF5rpng!O_SUhPFwSwtHg3RoKsznzeig4u+}

O)A^-Xco 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 u#yv$a@9_TxcEp|1@7 literal 0 HcmV?d00001 diff --git a/barcode/src/test/resources/org/xbib/graphics/barcode/qrcode/version-06.properties b/barcode/src/test/resources/org/xbib/graphics/barcode/qrcode/version-06.properties new file mode 100755 index 0000000..6b96e12 --- /dev/null +++ b/barcode/src/test/resources/org/xbib/graphics/barcode/qrcode/version-06.properties @@ -0,0 +1,2 @@ +preferredVersion=6 +content=ABC diff --git a/barcode/src/test/resources/org/xbib/graphics/barcode/qrcode/version-07.codewords b/barcode/src/test/resources/org/xbib/graphics/barcode/qrcode/version-07.codewords new file mode 100755 index 0000000..eef57ef --- /dev/null +++ b/barcode/src/test/resources/org/xbib/graphics/barcode/qrcode/version-07.codewords @@ -0,0 +1,45 @@ +712111111:3142117 +151212154243111112151 +113113131113123132211211311 +1131112211392121122111311 +11311221112472143111311 +151811121311211224151 +711111111111111111111111111111117 +0=21532111131218 +021131121224722163121 +01154212311111213311111412 +0411221211211233112171131 +1132111221122121212122222212 +02114241325132141212111 +0112122212213124123111113121 +13112116211121111322132221 +01212121222215114312243 +11113321322243217141 +01122115111211111112111311111412 +24311211414111112171131 +014241224121122122222212 +127111141151321451111 +0413121213332112313323 +212111326111113322311132 +0112131434131233121323 +13731121712331711 +051123121211152512311112 +32213111113312312141161 +01221131332122134221121112 +11313212134432161111111 +021121212114121132212123111111 +1431:1112121121211111141 +0111132211111125211122112211112 +137212231331126241 +1132141111442124123123 +041132223324414113121 +0142231612321132211223 +122132113281131451111 +08121111312321312111132111 +7362311112221211111141 +1511112114332112121123212 +113111123111;43181 +1131171311326211111421 +113111121111114312414121131 +15134111312112111112443 +776122111223112111111 diff --git a/barcode/src/test/resources/org/xbib/graphics/barcode/qrcode/version-07.png b/barcode/src/test/resources/org/xbib/graphics/barcode/qrcode/version-07.png new file mode 100644 index 0000000000000000000000000000000000000000..55415b1372c31cfd3881dc5345460b7f62c6cad0 GIT binary patch literal 10765 zcmd^FeNa-Fwn5xoTe5K*wOXWmYe+8?DaI5PEp@2UbvqRZ1W+K@kf5+8Lii|H z;93+#Z{Z>auxh&<0mD~fJP-=08VV#J8WIj6RX%OL6he?7aGy8OnfuqB>dd{X-8sWQ zAvx!~=REK4>v@iSygOjoD=S}Fv}nQ%eMTJJc^0^IcRc(e#gcnY}`gwiy+zT_TkpKNwiXK=$ZJN1gx@sEZ4i4|^*J-&W z=Ottb(Kn?h!S4W#i#7aNntYni7vr&`oSUW{`TY}kP;Wl&VflbtI;azx#H1Abkf7iK zf5vu3JRokv3QF9Q`7Df+)6eT;4vvODL#MwUI3wpp51$dnd}E30m~xSl)Tf(6J;!SV z-(_g>+f!?5+pXkEU;P_C@=(vq5=9|ZxOq{rB73e$(e6%b z`$*q)cAT8(RpEBDK6*H2a?hNITs#%cD!xw(O_KR@bLyi-h3ySS=Fh9e>%7X08@$p2 z*h;>lLsaOjK3!a-d#Xi*ED41LY0?8d_W;^_+`R*q0eaZ!~q z^Y)Y(x zv;qC5&MGXI&9jE&u1!{wC_(Z7nM!;;H$B%?OXwmQP7BT0YAdBc1>#I~%r~Er_*ONy;ACkB&ULiBNKV zkG!|ESY|M}%VvM_ojn8yS=l-v60^Rsq)Z>X;&&uJ<8?qsb|SsPRD2)nPrqkaUOKQk z_y7BJ)~(a87gv)>VoKupo0fF<2(pyasJr(XU>CJM<_0fXBZj(Ym=>lU9*ni}^7aBo z$!-^t@Pa6Otxu3+^AwbH;S}b-rv*&!VZ_D<|g36dk%tz=Um=b607 z&nMr&?*TDYb50<(BJghT@CpNQHGLUkH(aejlo!d0mvmf9f7sRi)ARDx^XIEsiY~TE%pV#t)$cW9WTk`m%C9%+*ZQn;9Q$$E<4>N! zi!ghgbA+c3hpq7m5)+N5O87W~H(Gh0I6~1T?EW{8Qzh;=fv4!eOca_`ZG}29`nbPF zgpF=;q74q~Y=z_$w*7i?psnQ0mxRILR$CT1ahxk?b7}ORx!2$}-Ldn!Q{s5EpxH&5 zENP6%?LV6igg-NU@!pHl zcJr;?Q_i@nl!UuB?$CGfX0p&mE|y_+8{d?RcW`frcO-Q2>%yDLy+ONvWAUh5)+*u@ zlvH!N7*;Z;hZ+ZH0H7SmewI$(UD&C~hy5%ZcnwMvSR*R8?|47F7^vPlw|B+sKr@aU zae}%AGJn;11o$0w~V+uEFIXAE5o*n1zq0lq98PNEVv zeM@u=jnx!~om?m(ME_8K9Nh6l~+1Z68-uj+} z>N<{?9)pvV(lUam8|vCcUT{|cBC&8F(>Ro3we?L2Qlies8ypIrqi(@2E)j^(MLaKEe4fgBy1HXY!oxn~zJT&awkV#=4Smx5&Nrf}* zt8rwZ(WX_WjqC$;GGe5A%6AVvMg|?h!(qKt6T3k6fAh^i{v08Lj_&~iN1*Iq=Vkmv zS%&QSKmXJxX^4tuvRtLfx2P3viz%Q&7r2<&+g28ch`{ay%JgCcqG|;7^G8EnkzWyF z4=W)Q%B%_57bH~Yk0S@D86Jpo?h}!zNk?p64fsBPJiNu64MRM2i;9q9rX|#~b028g zc}5?&)c9EX1CZOrh)&SAb}+i-*MT$8=J|koBTz7vbPO1AXH)qObaiBpK<+Lmiz3_D z$R81nrf&rae4hy7%N6m^{6RGhsob7&!ST4nzSzc8ZNk>zy@*+t5g9zT*2z0@oc7!K zn3}$ABR_3&CjkHk&<$zVa~1$Su9^8+#p((yC=}pD);57&_i!peUSiVZp8YeCy0lwa z050KGx$w3ii#cN`D8Y(W8kQQ&XIud_E#R`;7DIXiaM#z@cstlHm|s>G$x2KsGN9t< zm;$gH95!FNVQ;=RebEU{fPtrL-2&mt`kr+@e5$v4xmxE=0ld@?rJ!0&k^#_3rGoFO z(?#a@4>T(W?H)Z44RxL!hdWVp=mJkn96kI*ODQtU-LJ0)bX9b`EQl#VD2r;6FRpC| z^q&2;2jUcv>wEG=Gj?df9KgS{X{t8P3iP>#1a4hqaW*?{Z3u!2MBJY_vkNpOJ%rO z%Fg@FQyot-F}A9{b;2@u8KBueK{YWMx2wt`#wv>@|FNqo!#$$0 zmJCo`)r8h5(Qz6Z0yv2?w5gu~vODpl8G0y`r-^GAqJMVA0501Cmb<^yVCjN|a`+6Y z<*y}SbGv0Jz!FwlSjI{4e_RYtNj}*-Xoskah8F;9*ahe@;W~Aj;P(2SrZd0t)-PH9 zz}3Qz*`*gs3OR@J`Wg(9x*I(D(HtTD!zH2J{E^BSM&DW=i=9$g9j+>mVl9VFY}F;R zwXR^Nu=GH7e2-V0Mtc-CY8t;kRD2!ay-I&;B|hQ2l4o1h;7o(y1R@@`G0XV+l?H_z zvJnQD4-yY_=`Q_*TH^#lJh>ou9q4nBZX*aE$b4mn%4$MQQp4EV2haL`v!{tz|Y@K7ip{+PGyQnEc)o zEx>x96KK$ZY2cZhFcjgVuvzUn`ujSFoDmA&1lQDDg+hFkTh}{p`W*$*YXr8)H;D|; zC2&OGl%pcllKa;n2|Af_+7GW>Xf!HiJKtHv0}8adwm`c}Z_& zYSb?|RTM|%QDH{x@61+tb#jLY^3b*|NSPAY0$$W%6^H=TcFg3x9r34MQm$yhcti%^pY{^*$K+LJHoReuM!0qAheN1!g$psS@cJ><}twlEpU;O*3xI$|DWrXB{DFlCa~FB{RY=436X}Mlqhs87yegveEU>|*hA*-p zmot9NA``>=PjFD)1#I&{Zqdgge*k`K#lY7n%Er>SvVjanAN=tZ2>q(_N)LzqJ=sT$ za;;tZu9alwwr0rjcwM#mc^;Mjc zLPE_B)g})>a$Ia60>Q(fu#sQ znaLST*eM|7JG~kwN9=~A_YeqvM~>0T_fPdewU7$uw^=k8P_y-ZJ^?L}iy7>1gP;Qg;@G(0JEhX@Z;`!* zCugcMTmcmfhQ%_5C_TaFRM_&x75#oDV(~;(*>wDbC zDxrkIPeAyC_}KjTp40NPVPy&llsmW&$aMyQ&aaK<5jV#rrmfYlluB#ERnX@?j?{pX z%6wK%4oX5|6thHy6$32``2w2I@P>%BG4g7Rgbw9P22~TH z0Chn^bbg8@UFXGNrI~7tvc@md!b|2$bi(c;?^neJ60~YIOs{yvE#iZj^-T-$k9r=y zNUI5o*VmFAk&Qxt#f~2VNaWT&Ona&*^1|)R1}7RIxx> zLg7L;FKrs+sN4(=3|?t-?>OuMXwFTVK-oHD;DvHc|MU|px}bxKDLr5giv^xo7mKEe zK%x97M-5kDcyO#;3DaU&#}*t}Ho<@9qDJQ?Ow?`_g1hFGWWqoJh=jM6>!B}tm=^`Q z28P8<8y9mNj_|LmWY9)67+XLL!8lqkXGyQao?h6T3SHM8Z8i+6p; zleVgs0Fz`PFpVZ5#bL-oo*a7sJ>75?5oXnDVJ3j<)yYGCcGb|`CtARCz+eF6Q}nKg z^DrfbGzKJ9uoO*8Qm$yeL9p_h_Lvx-xckJF?wJkF1zX6d2O)vle literal 0 HcmV?d00001 diff --git a/barcode/src/test/resources/org/xbib/graphics/barcode/qrcode/version-07.properties b/barcode/src/test/resources/org/xbib/graphics/barcode/qrcode/version-07.properties new file mode 100755 index 0000000..a16b69c --- /dev/null +++ b/barcode/src/test/resources/org/xbib/graphics/barcode/qrcode/version-07.properties @@ -0,0 +1,2 @@ +preferredVersion=7 +content=ABC diff --git a/barcode/src/test/resources/org/xbib/graphics/barcode/qrcode/version-08.codewords b/barcode/src/test/resources/org/xbib/graphics/barcode/qrcode/version-08.codewords new file mode 100755 index 0000000..eb83f45 --- /dev/null +++ b/barcode/src/test/resources/org/xbib/graphics/barcode/qrcode/version-08.codewords @@ -0,0 +1,49 @@ +7212111631111165117 +1511421143123111121141151 +113115111141344311112111311 +11311225111122121422211211311 +11311212111792221411311 +1511112211211213111111121123151 +7111111111111111111111111111111111117 +081322121213211113112: +0448112162111221142311 +0211121221511121211131242111311 +111231232132211121511215121 +01241221221122113111152211132 +11111111131212222113111211234211 +23111123313111121131422111131 +01211121111411111121416316111 +41111161116131111127214 +01222123421111111311111211143111 +16111132331112113372311 +02123111622121413121111113211 +02211121111212517227142 +3313212411113111122211233111 +0412111221342312324211222 +02116342;1212112513 +02331115111313111112242332 +31111111212412111111321211111111221 +02332221341323134113131 +0283428241211251111 +02261511121321211124334 +71112124121231521111222111 +02242221371112111244222 +131111111331312212142111122221 +01221232211422112121243322 +0211322211112141111332111121321 +16151111325512482 +111111211323112264211112111121 +0311126415122112333142 +044111441212111511121121321 +02231235621415264 +01132231311211125121112112112111 +0134223113252114115124 +33211111311121662112531 +0811121221211314112423122 +71242211311141411221111211 +151134134336114332 +1131111114212261232112531 +11311321131121134111241111411 +1131121112111112212361127111 +15124822213411211111122 +7314212331162114151 diff --git a/barcode/src/test/resources/org/xbib/graphics/barcode/qrcode/version-08.png b/barcode/src/test/resources/org/xbib/graphics/barcode/qrcode/version-08.png new file mode 100644 index 0000000000000000000000000000000000000000..2c6545fcd1be56cd2bd47cc94369f1357e087ac3 GIT binary patch literal 13037 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 literal 0 HcmV?d00001 diff --git a/barcode/src/test/resources/org/xbib/graphics/barcode/qrcode/version-08.properties b/barcode/src/test/resources/org/xbib/graphics/barcode/qrcode/version-08.properties new file mode 100755 index 0000000..3e67e39 --- /dev/null +++ b/barcode/src/test/resources/org/xbib/graphics/barcode/qrcode/version-08.properties @@ -0,0 +1,2 @@ +preferredVersion=8 +content=ABC diff --git a/barcode/src/test/resources/org/xbib/graphics/barcode/qrcode/version-09.codewords b/barcode/src/test/resources/org/xbib/graphics/barcode/qrcode/version-09.codewords new file mode 100755 index 0000000..b8f04d1 --- /dev/null +++ b/barcode/src/test/resources/org/xbib/graphics/barcode/qrcode/version-09.codewords @@ -0,0 +1,53 @@ +711131123111131111111341137 +151122282111323542151 +113111111123381223431211311 +113114111131512242121111111111311 +1131142111221111611113411311311 +1511312111134322211433151 +71111111111111111111111111111111111111117 +083121343325521: +02311141111132117113111141323 +12131231611114231161131231 +24121211112322171213411112111 +2311121121111112111443121212116 +3311134144211123111244131 +0232231211211124231521121151 +1111112135131311421111124411111 +0221133111341113218312125 +03125141213231222111212211141 +12253154112122115347 +1311212311111123291211212111112 +0511142114111114413211113214 +02131132312135112121221213111111 +11326211241112231122331331 +1111112112142121822122112312111 +0221153141114221411512125 +04511171:331111121531 +122312213713131117111314 +0411112512111131111611111141112111 +011123131111133113211234112314 +225112118151122121146111 +03312221722124212411221131 +1332121121211121322121211221161 +213113211131121131111121141212114 +1311131121712212132111131112221 +1223132123111918112215 +04414121142111122131111221131111 +01112211321121117111111214121215 +21112521152121121321111311122111 +03322221411221151116112314 +116321111221222221111111122111111111 +6112221113121111115214121314 +113115211241212621111312112111 +111113111121211111122511161155 +2151753311311111111222121111 +012411314212311111121232122115 +031211121613118222237111 +0821112311311312211222121314 +7531123211112311131231112111 +15131135122351111211112314 +113111123112211163121112127111 +11311111412116241124331151 +113111413131121441211211331121 +15123156131112123311216 +7415311112622221431211 diff --git a/barcode/src/test/resources/org/xbib/graphics/barcode/qrcode/version-09.png b/barcode/src/test/resources/org/xbib/graphics/barcode/qrcode/version-09.png new file mode 100644 index 0000000000000000000000000000000000000000..037d618cd3bb31a52dd65c5913395b457a4e8bd6 GIT binary patch literal 14844 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 HrCa|Caf=o! literal 0 HcmV?d00001 diff --git a/barcode/src/test/resources/org/xbib/graphics/barcode/upc/upc-a-basic.properties b/barcode/src/test/resources/org/xbib/graphics/barcode/upc/upc-a-basic.properties new file mode 100755 index 0000000..470bc68 --- /dev/null +++ b/barcode/src/test/resources/org/xbib/graphics/barcode/upc/upc-a-basic.properties @@ -0,0 +1,2 @@ +mode=UPCA +content=12345678901+1234 diff --git a/barcode/src/test/resources/org/xbib/graphics/barcode/upc/upc-a-font.codewords b/barcode/src/test/resources/org/xbib/graphics/barcode/upc/upc-a-font.codewords new file mode 100755 index 0000000..4f37112 --- /dev/null +++ b/barcode/src/test/resources/org/xbib/graphics/barcode/upc/upc-a-font.codewords @@ -0,0 +1 @@ +1112221212214111132123111141111113121213311232112221212211191123211111222112212111411111132 diff --git a/barcode/src/test/resources/org/xbib/graphics/barcode/upc/upc-a-font.png b/barcode/src/test/resources/org/xbib/graphics/barcode/upc/upc-a-font.png new file mode 100644 index 0000000000000000000000000000000000000000..7e99f7d5467d7c7f7d536b8a9b7fddad83b38ed8 GIT binary patch literal 38490 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 Tg0W8af9uwK`*rSDKOXvDk{Q__ literal 0 HcmV?d00001 diff --git a/barcode/src/test/resources/org/xbib/graphics/barcode/upc/upc-a-font.properties b/barcode/src/test/resources/org/xbib/graphics/barcode/upc/upc-a-font.properties new file mode 100755 index 0000000..3a37f4d --- /dev/null +++ b/barcode/src/test/resources/org/xbib/graphics/barcode/upc/upc-a-font.properties @@ -0,0 +1,3 @@ +mode=UPCA +fontSize=4 +content=12345678901+1234 diff --git a/barcode/src/test/resources/org/xbib/graphics/barcode/upc/upc-e-basic.codewords b/barcode/src/test/resources/org/xbib/graphics/barcode/upc/upc-e-basic.codewords new file mode 100755 index 0000000..5f8ccfe --- /dev/null +++ b/barcode/src/test/resources/org/xbib/graphics/barcode/upc/upc-e-basic.codewords @@ -0,0 +1 @@ +11121221411113213214111213111111191123211111222112212111411111132 diff --git a/barcode/src/test/resources/org/xbib/graphics/barcode/upc/upc-e-basic.png b/barcode/src/test/resources/org/xbib/graphics/barcode/upc/upc-e-basic.png new file mode 100644 index 0000000000000000000000000000000000000000..27032bfb5ee3dd05edaeb1bd456a53f351c29035 GIT binary patch literal 32366 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


L6`byQEAGz8M=XxS zLYq!@28-WKE!1R4up((C+^npd0OcjbMb!4#dSrdqWB-JEUH;H}-TuEk+*{+}Pbfff zU^7()%+#Tf=qp#~xbzZ6yAv`2U7^hT7ca{*5~{E4D<+;a6ZgY~4ZKl5QO!da=LK5T z`1&}tms!eJSr}t?Y}vc)@OSq_h6zY=C?|k<4E>nzIZfPfTt6d$b2xUP($FuPe8A&g zRvS_FOi({~!Ykjwt<2>L#xLL${SX5e?1_zt&G7-h*rP<IFER#+b`064lrUWj3q3n7{FMKo}M2X2(%_HzdY% z5^AG+88Sg#U_E%cIThGGaIzO(q=%uTxTbtew{zR3ymtM1_y{>Ke0a#!&*f6TMPlE zoY7NizNCHri{_jiFoHTpIlY>`O_VHJAC9rI894{yH<;A%PY6pfPYUs$g6qDIFj|@b zc&1J1%e$5+KUa<{Cb39Uo;`jZ(D>I~%>4_ak40hrv0u%m7BUTRaFFy&7hCAENmDX8 zfY!;e3Rk%$A(0qW+Pm&<-TUj9;g9j}q{!DE_WAqy`CCchiLYeIfzM+$@8Pj3)#n_% zz3basqx&f_GkSF1s254lOtDbR!AKKp=tzRfmOVPuFeD2X+-%$+*ngr3qkt-TBkGwu zS=c#9pvV(W;%EHP(e!mByHQDDJzpD^E1K006u+>=$9{^iuU5yru@>#R1=t#lF!zf_ z$ayTM$r)~ga3fbuO6{k*kJ*bZdmVH4u&GUKuY^(u8f{q78XCLX90y@Gs=Vm!ogQp43<1Vp3slTag8K0yEDU)OC zD=((sWRzr#^oj680^gIdyETvJ^53x2d!ZEoua0Afl8~9rtfQVRSR(xrVgy^$e-i17 z@~aFwy9v;n!k4vNzaDwW$Axe1z|p2OH~8S<;9so2wdv($C+TSrmui7!(eM-1JQCwzV!;VL0^ zA)?jJGgXX^n?Yw|o>M1^YNFxty9j`~fQTa%WWZzw@EExSWacE2N5aM8`MU)k^)Je~ z8cK{8{9$+TH>5LoTtaSp)scI#z!9%5ASh!Jet&Vhp6!91{&j>tS3eaQqp-?fESWAs zv{b$T+D7`mUk4Wcy7b2{agOryHupv|?T^=|81Dred+oZp;cZX4;@ s@_j@@RL<6vAWFLSk^h?(19ilA%6D#gm^HNYA^F1x-49g!>zAwl1KolR%m4rY literal 0 HcmV?d00001 diff --git a/barcode/src/test/resources/org/xbib/graphics/barcode/upc/upc-e-basic.properties b/barcode/src/test/resources/org/xbib/graphics/barcode/upc/upc-e-basic.properties new file mode 100755 index 0000000..f0d5dcf --- /dev/null +++ b/barcode/src/test/resources/org/xbib/graphics/barcode/upc/upc-e-basic.properties @@ -0,0 +1,2 @@ +mode=UPCE +content=1234567+1234 diff --git a/barcode/src/test/resources/org/xbib/graphics/barcode/uspsonecode/usps-one-code-length-20.codewords b/barcode/src/test/resources/org/xbib/graphics/barcode/uspsonecode/usps-one-code-length-20.codewords new file mode 100755 index 0000000..1534787 --- /dev/null +++ b/barcode/src/test/resources/org/xbib/graphics/barcode/uspsonecode/usps-one-code-length-20.codewords @@ -0,0 +1 @@ +AFDADFAAAFTDDTDDTATADDFAADFDDFTAFTDTAFTFTDTATFTFAFDFDFFFFTFFFATDD diff --git a/barcode/src/test/resources/org/xbib/graphics/barcode/uspsonecode/usps-one-code-length-20.png b/barcode/src/test/resources/org/xbib/graphics/barcode/uspsonecode/usps-one-code-length-20.png new file mode 100644 index 0000000000000000000000000000000000000000..761f00b0c1268cb1518ee8d1b3b28290ca153804 GIT binary patch literal 4234 zcmeAS@N?(olHy`uVBq!ia0y~yU{eCJ12}*L!^UEx=?n}4fu1goAr)~~&pWO<>LzgX z!khZbC(o$yvhREG`rzd=L7}Gqp3iTu|KGl6#pjp_=_~%Vuh<{(yp!*wb%T}vCq>;8 z=N~ZhEI60`iFtGDE6>;mdz`+yaDU?Yw0%#xzWka|Eu$eenqWrr%V=>ibW6+sKN7z3 zTs00kzu^-RRCW}2T^Y(Hp<}C!- z5k>^{kLUe!dH#^|U^}#lpz7Pcf9_{rl@7LJDiM_Z;om=xGlPOC!1nMdBB=Qf|NeR0 z2nwPV^AevDL7o3l_wS>_Xf)uBjnQZzDmSFpU0|$S#QIb6C!_8OVciq;{c-E;7CQqo zclxJ=dlX(Rw7M^@^|Nu;fy!1;aA!E)Q#@qtZt=O952X713H}U6py0piFGXJ^WhbSt zu$#>Plr@n5>d$=vC;6|ip3?^9VqhDPPw<}Qp_%d}4nWm)ZgmdapMZk*7f)Y({>22K zikUSU`W;m(&V6t7s#~B}5C_T!>QNKQJ3gEfJoje-Gf1`ZC+4ULK*9fVdt++d+Mh_S zI{(DE((#IO=|26CPt2u_pSeI8`eM@_=a%xmf@kcqAl36H%3o-*x!CmQ`-`WqjIC78 z2h^&`pW^+8zStZT~F_?*$&tRi5+HksYKu{R#UP1)$)6+p3+vdiYKn zhs;0O{;6q2`>UGx6;Ie-F+CRr6;g{>E898V%RD^8Zv|3quWY}76%+~YFP^@dUg9xt z;V&P%DdH}+A(MswLvC>Z~+cQuD-36;ud QfCd>nUHx3vIVCg!0BMF}jsO4v literal 0 HcmV?d00001 diff --git a/barcode/src/test/resources/org/xbib/graphics/barcode/uspsonecode/usps-one-code-length-20.properties b/barcode/src/test/resources/org/xbib/graphics/barcode/uspsonecode/usps-one-code-length-20.properties new file mode 100755 index 0000000..f48958a --- /dev/null +++ b/barcode/src/test/resources/org/xbib/graphics/barcode/uspsonecode/usps-one-code-length-20.properties @@ -0,0 +1 @@ +content=12345678901234567890 diff --git a/barcode/src/test/resources/org/xbib/graphics/barcode/uspsonecode/usps-one-code-length-25.codewords b/barcode/src/test/resources/org/xbib/graphics/barcode/uspsonecode/usps-one-code-length-25.codewords new file mode 100755 index 0000000..6b3fced --- /dev/null +++ b/barcode/src/test/resources/org/xbib/graphics/barcode/uspsonecode/usps-one-code-length-25.codewords @@ -0,0 +1 @@ +FTTDTTDTTDAFFTTAADDFFFAADAAAADTDFFAFFAATFADDFDDTTDADADDDATTTFDFDF diff --git a/barcode/src/test/resources/org/xbib/graphics/barcode/uspsonecode/usps-one-code-length-25.png b/barcode/src/test/resources/org/xbib/graphics/barcode/uspsonecode/usps-one-code-length-25.png new file mode 100644 index 0000000000000000000000000000000000000000..0386710fadf9310d235031ac4b64cdf741ef73b5 GIT binary patch literal 4039 zcmeH~Urbte6vx>tvxBq}Bd{*rQ0L|pkZtV}FjNbXi7{iMRM9EcE>X8WtU^ttrLeLQ z5t=$@oSV7D7Hgo)SPg9&J1gsiZMqglqg(beIwHG<7Y26*Xp!yQJuj*0<8oi_xeq5f z|G)YCh8kqGdnnISNF>spI%!QKiS$e{{5R}|4{2WW#yE+TccHFEq!>^|?u%Tz1o^vy zOMg#0PQ64EN^_V#$JtW%?qQD8kWCx1$<86YIwB~~4|I)qEeG0Y4%(z7*IQQZp!HlY zv>jkiOzY(r16qAyrxQzltJQN;kAm;a3Tq`{0p~Mi-&@phLD;?d5nmE12%l$oHQf^T z8qb9Z0(l$M{;Y~0xJEWqKU^6@ol@bTB%kf?6(M3B;{}?-iHMP)alk**`}spje-)ZO zr@QPAQPK2`2eI1fg~WdP5IQG4=G(_LH<<(WoN&QdlNmA$d`Z98=yCc{VfClHwe*?a zg&sy`d$Y8~HdP$o�T>Pm%j5@z#@v5xbg#XDD#nTcgbNG=k!#*2}ihDy!^c@(FPpjwr>rE8!pYtFl@VHI- zKdjJiAHV*Ujm3ChlsS8HK}`WvkiT6uV_F5yh&A$hRfs)-v@4i=UQxE{)?)Z3KXB%? z?saUrm}~Y7SG35AfZJRvgEtxkz!@D;@;z&l!ZvD+K8?QY4b_gfz};)}zMDC%S6(5< zlfeie3~<|<={ZI@kkC^7?MqtNK8;%5@nG?j)T7OWB@`CM?tF11kY~MK2r=6QCg3*b zE3BWz5g?&cSD6QSGPzb(;i4krGy|(cg)i!%=iJeB*9ZfT$#&q}w!X34<^&Q-Rcsmm z009ZCNczuV`Qg~qk8HgaTg-j0`A{|Iz7b-ofhU36CUi>b7(icWTJ_T+K&D{9S#d%o zhQ?@ppf1UycU|b9qz#Wbi->{S6r*{^FRKCizFwnFFda@|DRZ6dug4p6EyeX?FP?AR z%FIcrcue3);IwR`4xYm*GXhGv*M%1e*jh=@Kpc+ literal 0 HcmV?d00001 diff --git a/barcode/src/test/resources/org/xbib/graphics/barcode/uspsonecode/usps-one-code-length-25.properties b/barcode/src/test/resources/org/xbib/graphics/barcode/uspsonecode/usps-one-code-length-25.properties new file mode 100755 index 0000000..0814523 --- /dev/null +++ b/barcode/src/test/resources/org/xbib/graphics/barcode/uspsonecode/usps-one-code-length-25.properties @@ -0,0 +1 @@ +content=12345678901234567890-12345 diff --git a/barcode/src/test/resources/org/xbib/graphics/barcode/uspsonecode/usps-one-code-length-29.codewords b/barcode/src/test/resources/org/xbib/graphics/barcode/uspsonecode/usps-one-code-length-29.codewords new file mode 100755 index 0000000..0448f41 --- /dev/null +++ b/barcode/src/test/resources/org/xbib/graphics/barcode/uspsonecode/usps-one-code-length-29.codewords @@ -0,0 +1 @@ +TADDDDATDAAADFDDAFDFDDTFADAADAADFAFFFDAFAFTDTADADFTFFATFFATDFTATD diff --git a/barcode/src/test/resources/org/xbib/graphics/barcode/uspsonecode/usps-one-code-length-29.png b/barcode/src/test/resources/org/xbib/graphics/barcode/uspsonecode/usps-one-code-length-29.png new file mode 100644 index 0000000000000000000000000000000000000000..49c94f6b05a2b6496ed75ecd398775aee863c598 GIT binary patch literal 4413 zcmeAS@N?(olHy`uVBq!ia0y~yU{eCJ12}*L!^UEx=?n~lES@foAr*03&pWO=9Km<= z!ma+yHt$*Sxz@3!&SPH2!YcNF=bxSCs{foI2DJQ% zv6agFhl*{Y&l~^r*aeJwZZzgbljUfh8!b=mdKI;Vst E0COtI#sB~S literal 0 HcmV?d00001 diff --git a/barcode/src/test/resources/org/xbib/graphics/barcode/uspsonecode/usps-one-code-length-29.properties b/barcode/src/test/resources/org/xbib/graphics/barcode/uspsonecode/usps-one-code-length-29.properties new file mode 100755 index 0000000..f95b880 --- /dev/null +++ b/barcode/src/test/resources/org/xbib/graphics/barcode/uspsonecode/usps-one-code-length-29.properties @@ -0,0 +1 @@ +content=12345678901234567890-123456789 diff --git a/barcode/src/test/resources/org/xbib/graphics/barcode/uspsonecode/usps-one-code-length-31.codewords b/barcode/src/test/resources/org/xbib/graphics/barcode/uspsonecode/usps-one-code-length-31.codewords new file mode 100755 index 0000000..5058a4c --- /dev/null +++ b/barcode/src/test/resources/org/xbib/graphics/barcode/uspsonecode/usps-one-code-length-31.codewords @@ -0,0 +1 @@ +AAFDFTDTADDTATTTDTDTDTTDTDDDADTFTTAFFTDFAFAAAFDFATDFTATTFDDFDAAFD diff --git a/barcode/src/test/resources/org/xbib/graphics/barcode/uspsonecode/usps-one-code-length-31.png b/barcode/src/test/resources/org/xbib/graphics/barcode/uspsonecode/usps-one-code-length-31.png new file mode 100644 index 0000000000000000000000000000000000000000..19ed30f8ba3641ca2ba6a41021365d507c5aab65 GIT binary patch literal 3883 zcmeAS@N?(olHy`uVBq!ia0y~yU{eCJ12}*L!^UEx=?n~fpFCY0Ln`9lUOdS4#Da(6 z;?Mu{@9oT$IDA-hf+6q9=Wb)488`%B}eGud&}Yx&F{c*Q@C}JQ7bCpAhCfk^XAYuX%T(mo&Z#pI5Fg zeqypsz|X(T^3})wwLI=#b-rNYp(*JT)TJhzzvBAUcIS2v=Bw*%-pA=pP_JC@={LLG zx1PV8$HhbEKU8YfJg?wyqA-7D_p9on{E2L<;w$g%jZyIbmE@{)U^#WwIOIIA7+RISVji%R2{|8Ndw2?vy|AiH z6UcV{$`f42d;a`%Zjb^bK@j_uIb?(G&&CW%S4w z8=mP#>AYunRei{nXZs2Hk~)Sbwo((y8NSxauXxb_tfYaJvZ(BY@)a2q{}fh4EUaqh z_^y+9HhN3MJ;p2FTe>CkCfdLF!*HV7L;+~ZZ@YktjKCTjR6|=Sybrjj^z-qD4NhPA z1m8s-nzcP+;~wS}?>WT{? { + + protected final ST styler; + + protected final ChartTitle chartTitle; + + protected final Map seriesMap; + + protected AxisPair axisPair; + + protected Plot plot; + + protected Legend legend; + + private int width; + + private int height; + + private String title = ""; + + private String xAxisTitle = ""; + + private String yAxisTitle = ""; + + private final Map yAxisGroupTitleMap; + + protected Chart(int width, int height, ST styler) { + this.styler = styler; + this.width = width; + this.height = height; + this.chartTitle = new ChartTitle<>(this); + this.seriesMap = new LinkedHashMap<>(); + this.yAxisGroupTitleMap = new HashMap<>(); + } + + public abstract void paint(Graphics2D g, int width, int height); + + protected void paintBackground(Graphics2D g) { + g.setRenderingHint(RenderingHints.KEY_ANTIALIASING, styler.getAntiAlias() + ? RenderingHints.VALUE_ANTIALIAS_ON + : RenderingHints.VALUE_ANTIALIAS_OFF); + g.setColor(styler.getChartBackgroundColor()); + Shape rect = new Rectangle2D.Double(0, 0, getWidth(), getHeight()); + g.fill(rect); + } + + public List listFromDoubleArray(double[] data) { + if (data == null) { + return null; + } + List dataNumber; + dataNumber = new ArrayList<>(); + for (double d : data) { + dataNumber.add(d); + } + return dataNumber; + } + + public List listFromFloatArray(float[] data) { + if (data == null) { + return null; + } + List dataNumber; + dataNumber = new ArrayList<>(); + for (float f : data) { + dataNumber.add((double) f); + } + return dataNumber; + } + + public List listFromIntArray(int[] data) { + if (data == null) { + return null; + } + List dataNumber; + dataNumber = new ArrayList<>(); + for (double d : data) { + dataNumber.add(d); + } + return dataNumber; + } + + public List getGeneratedData(int length) { + List generatedData = new ArrayList<>(); + for (int i = 1; i < length + 1; i++) { + generatedData.add((double) i); + } + return generatedData; + } + + public int getWidth() { + return width; + } + + public void setWidth(int width) { + this.width = width; + } + + public int getHeight() { + return height; + } + + public void setHeight(int height) { + this.height = height; + } + + public String getTitle() { + return title; + } + + public void setTitle(String title) { + this.title = title; + } + + public String getXAxisTitle() { + return xAxisTitle; + } + + public void setXAxisTitle(String xAxisTitle) { + this.xAxisTitle = xAxisTitle; + } + + public String getyYAxisTitle() { + return yAxisTitle; + } + + public void setYAxisTitle(String yAxisTitle) { + this.yAxisTitle = yAxisTitle; + } + + public String getYAxisGroupTitle(int yAxisGroup) { + String title = yAxisGroupTitleMap.get(yAxisGroup); + if (title == null) { + return yAxisTitle; + } + return title; + } + + public void setYAxisGroupTitle(int yAxisGroup, String yAxisTitle) { + yAxisGroupTitleMap.put(yAxisGroup, yAxisTitle); + } + + public void setXAxisLabelOverrideMap(Map overrideMap) { + axisPair.getAxisLabelOverrideMap().put("X0", overrideMap); + } + + public void setYAxisLabelOverrideMap(Map overrideMap) { + axisPair.getAxisLabelOverrideMap().put("Y0", overrideMap); + } + + public void setYAxisLabelOverrideMap(Map overrideMap, int yAxisGroup) { + axisPair.getAxisLabelOverrideMap().put(("Y" + yAxisGroup), overrideMap); + } + + public Map getYAxisLabelOverrideMap(Direction direction, int yIndex) { + Map> axisLabelOverrideMap = axisPair.getAxisLabelOverrideMap(); + return axisLabelOverrideMap.get((direction.name() + yIndex)); + } + + public ChartTitle getChartTitle() { + return chartTitle; + } + + public Legend getLegend() { + return legend; + } + + public Plot getPlot() { + return plot; + } + + public Axis getXAxis() { + return axisPair.getXAxis(); + } + + public Axis getYAxis() { + return axisPair.getYAxis(); + } + + public Axis getYAxis(int yIndex) { + return axisPair.getYAxis(yIndex); + } + + public AxisPair getAxisPair() { + return axisPair; + } + + public Map getSeriesMap() { + return seriesMap; + } + + public S removeSeries(String seriesName) { + return seriesMap.remove(seriesName); + } + + public ST getStyler() { + return styler; + } + + public Format getXAxisFormat() { + return axisPair.getXAxis().getAxisTickCalculator().getAxisFormat(); + } + + public Format getYAxisFormat() { + return axisPair.getYAxis().getAxisTickCalculator().getAxisFormat(); + } + + /** + * Save chart as an image file. + * + * @param outputStream output stream + * @param bitmapFormat bitmap format + * @throws IOException if save fails + */ + public void saveBitmap(OutputStream outputStream, BitmapFormat bitmapFormat) throws IOException { + BufferedImage bufferedImage = getBufferedImage(); + ImageIO.write(bufferedImage, bitmapFormat.toString().toLowerCase(), outputStream); + outputStream.close(); + } + + /** + * Save a chart as a PNG with a custom DPI. The default DPI is 72, which is fine for displaying charts on a + * computer + * monitor, but for printing + * charts, a DPI of around 300 is much better. + * + * @param outputStream output stream + * @param dpi dot sper inch + * @throws IOException if save fails + */ + public void saveBitmapWithDPI(OutputStream outputStream, BitmapFormat bitmapFormat, int dpi) throws IOException { + double scaleFactor = dpi / 72.0; + BufferedImage bufferedImage = new BufferedImage((int) (getWidth() * scaleFactor), (int) (getHeight() * scaleFactor), BufferedImage.TYPE_INT_RGB); + Graphics2D graphics2D = bufferedImage.createGraphics(); + AffineTransform at = graphics2D.getTransform(); + at.scale(scaleFactor, scaleFactor); + graphics2D.setTransform(at); + paint(graphics2D, getWidth(), getHeight()); + Iterator writers = ImageIO.getImageWritersByFormatName(bitmapFormat.toString().toLowerCase()); + if (writers.hasNext()) { + ImageWriter writer = writers.next(); + // instantiate an ImageWriteParam object with default compression options + ImageWriteParam iwp = writer.getDefaultWriteParam(); + ImageTypeSpecifier typeSpecifier = ImageTypeSpecifier.createFromBufferedImageType(BufferedImage.TYPE_INT_RGB); + IIOMetadata metadata = writer.getDefaultImageMetadata(typeSpecifier, iwp); + if (metadata.isReadOnly() || !metadata.isStandardMetadataFormatSupported()) { + throw new IllegalArgumentException("It is not possible to set the DPI on a bitmap with " + bitmapFormat + " format!! Try another format."); + } + setDPI(metadata, dpi); + try { + writer.setOutput(outputStream); + IIOImage image = new IIOImage(bufferedImage, null, metadata); + writer.write(null, image, iwp); + } finally { + writer.dispose(); + } + } + } + + /** + * Sets the metadata. + * + * @param metadata metadata + * @param DPI dots per inch + * @throws IIOInvalidTreeException if setting fails + */ + private static void setDPI(IIOMetadata metadata, int DPI) throws IIOInvalidTreeException { + // for PNG, it's dots per millimeter + double dotsPerMilli = 1.0 * DPI / 10 / 2.54; + IIOMetadataNode horiz = new IIOMetadataNode("HorizontalPixelSize"); + horiz.setAttribute("value", Double.toString(dotsPerMilli)); + IIOMetadataNode vert = new IIOMetadataNode("VerticalPixelSize"); + vert.setAttribute("value", Double.toString(dotsPerMilli)); + IIOMetadataNode dim = new IIOMetadataNode("Dimension"); + dim.appendChild(horiz); + dim.appendChild(vert); + IIOMetadataNode root = new IIOMetadataNode("javax_imageio_1.0"); + root.appendChild(dim); + metadata.mergeTree("javax_imageio_1.0", root); + } + + /** + * Save a Chart as a JPEG file + * + * @param outputStream output stream + * @param quality - a float between 0 and 1 (1 = maximum quality) + * @throws IOException if save fails + */ + public void saveJPGWithQuality(OutputStream outputStream, float quality) throws IOException { + BufferedImage bufferedImage = getBufferedImage(); + Iterator iter = ImageIO.getImageWritersByFormatName("jpeg"); + ImageWriter writer = iter.next(); + ImageWriteParam iwp = writer.getDefaultWriteParam(); + iwp.setCompressionMode(ImageWriteParam.MODE_EXPLICIT); + iwp.setCompressionQuality(quality); + try (outputStream) { + writer.setOutput(outputStream); + IIOImage image = new IIOImage(bufferedImage, null, null); + writer.write(null, image, iwp); + writer.dispose(); + } + } + + /** + * Generates a byte[] for a given chart. + * + * @param bitmapFormat bitmap format + * @return a byte[] for a given chart + * @throws IOException if byte array fails + */ + public byte[] getBitmapBytes(BitmapFormat bitmapFormat) throws IOException { + BufferedImage bufferedImage = getBufferedImage(); + ByteArrayOutputStream baos = new ByteArrayOutputStream(); + ImageIO.write(bufferedImage, bitmapFormat.toString().toLowerCase(), baos); + baos.flush(); + byte[] imageInBytes = baos.toByteArray(); + baos.close(); + return imageInBytes; + } + + public BufferedImage getBufferedImage() { + BufferedImage bufferedImage = new BufferedImage(getWidth(), getHeight(), BufferedImage.TYPE_INT_RGB); + Graphics2D graphics2D = bufferedImage.createGraphics(); + paint(graphics2D, getWidth(), getHeight()); + return bufferedImage; + } + + public void write(OutputStream outputStream, VectorGraphicsFormat vectorGraphicsFormat) + throws IOException { + ProcessingPipeline g = null; + switch (vectorGraphicsFormat) { + case EPS: + g = new EPSGraphics2D(0.0, 0.0, getWidth(), getHeight()); + break; + case PDF: + g = new PDFGraphics2D(0.0, 0.0, getWidth(), getHeight()); + break; + case SVG: + g = new SVGGraphics2D(0.0, 0.0, getWidth(), getHeight()); + break; + + default: + break; + } + paint(g, getWidth(), getHeight()); + if (outputStream != null) { + outputStream.write(g.getBytes()); + } + } +} diff --git a/chart/src/main/java/org/xbib/graphics/chart/ChartBuilder.java b/chart/src/main/java/org/xbib/graphics/chart/ChartBuilder.java new file mode 100644 index 0000000..3626c56 --- /dev/null +++ b/chart/src/main/java/org/xbib/graphics/chart/ChartBuilder.java @@ -0,0 +1,66 @@ +package org.xbib.graphics.chart; + +import org.xbib.graphics.chart.theme.DefaultTheme; +import org.xbib.graphics.chart.theme.Theme; + +public abstract class ChartBuilder, C extends Chart> { + + private int width; + + private int height; + + private String title; + + private Theme theme; + + public ChartBuilder() { + this.width = 800; + this.height = 600; + this.title = ""; + this.theme = new DefaultTheme(); + } + + @SuppressWarnings("unchecked") + public T width(int width) { + this.width = width; + return (T) this; + } + + public int getWidth() { + return width; + } + + @SuppressWarnings("unchecked") + public T height(int height) { + this.height = height; + return (T) this; + } + + public int getHeight() { + return height; + } + + @SuppressWarnings("unchecked") + public T title(String title) { + this.title = title; + return (T) this; + } + + public String getTitle() { + return title; + } + + @SuppressWarnings("unchecked") + public T theme(Theme theme) { + if (theme != null) { + this.theme = theme; + } + return (T) this; + } + + public Theme getTheme() { + return theme; + } + + public abstract C build(); +} diff --git a/chart/src/main/java/org/xbib/graphics/chart/ChartComponent.java b/chart/src/main/java/org/xbib/graphics/chart/ChartComponent.java new file mode 100644 index 0000000..e52f790 --- /dev/null +++ b/chart/src/main/java/org/xbib/graphics/chart/ChartComponent.java @@ -0,0 +1,14 @@ +package org.xbib.graphics.chart; + +import java.awt.Graphics2D; +import java.awt.geom.Rectangle2D; + +/** + * A components of a chart that need to be painted. + */ +public interface ChartComponent { + + Rectangle2D getBounds(); + + void paint(Graphics2D g); +} diff --git a/chart/src/main/java/org/xbib/graphics/chart/ChartTitle.java b/chart/src/main/java/org/xbib/graphics/chart/ChartTitle.java new file mode 100644 index 0000000..2789e68 --- /dev/null +++ b/chart/src/main/java/org/xbib/graphics/chart/ChartTitle.java @@ -0,0 +1,87 @@ +package org.xbib.graphics.chart; + +import org.xbib.graphics.chart.series.Series; +import org.xbib.graphics.chart.style.Styler; +import org.xbib.graphics.chart.theme.Theme; + +import java.awt.Graphics2D; +import java.awt.RenderingHints; +import java.awt.Shape; +import java.awt.font.FontRenderContext; +import java.awt.font.TextLayout; +import java.awt.geom.AffineTransform; +import java.awt.geom.Rectangle2D; + +/** + * Chart Title. + */ +public class ChartTitle implements ChartComponent { + + private final Chart chart; + private Rectangle2D bounds; + + public ChartTitle(Chart chart) { + this.chart = chart; + } + + @Override + public Rectangle2D getBounds() { + if (bounds == null) { + bounds = getBoundsHint(); + } + return bounds; + } + + @Override + public void paint(Graphics2D g) { + g.setFont(chart.getStyler().getChartTitleFont()); + if (!chart.getStyler().isChartTitleVisible() || chart.getTitle().length() == 0) { + return; + } + Object oldHint = g.getRenderingHint(RenderingHints.KEY_ANTIALIASING); + g.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON); + FontRenderContext frc = g.getFontRenderContext(); + TextLayout textLayout = new TextLayout(chart.getTitle(), chart.getStyler().getChartTitleFont(), frc); + Rectangle2D textBounds = textLayout.getBounds(); + double xOffset = chart.getPlot().getBounds().getX(); + double yOffset = chart.getStyler().getChartPadding(); + if (chart.getStyler().isChartTitleBoxVisible()) { + double chartTitleBoxWidth = chart.getPlot().getBounds().getWidth(); + double chartTitleBoxHeight = textBounds.getHeight() + 2 * chart.getStyler().getChartTitlePadding(); + g.setStroke(Theme.Strokes.TITLE); + Shape rect = new Rectangle2D.Double(xOffset, yOffset, chartTitleBoxWidth, chartTitleBoxHeight); + g.setColor(chart.getStyler().getChartTitleBoxBackgroundColor()); + g.fill(rect); + g.setColor(chart.getStyler().getChartTitleBoxBorderColor()); + g.draw(rect); + } + xOffset = chart.getPlot().getBounds().getX() + (chart.getPlot().getBounds().getWidth() - textBounds.getWidth()) / 2.0; + yOffset = chart.getStyler().getChartPadding() + textBounds.getHeight() + chart.getStyler().getChartTitlePadding(); + g.setColor(chart.getStyler().getChartFontColor()); + Shape shape = textLayout.getOutline(null); + AffineTransform orig = g.getTransform(); + AffineTransform at = new AffineTransform(); + at.translate(xOffset, yOffset); + g.transform(at); + g.fill(shape); + g.setTransform(orig); + double width = 2 * chart.getStyler().getChartTitlePadding() + textBounds.getWidth(); + double height = 2 * chart.getStyler().getChartTitlePadding() + textBounds.getHeight(); + bounds = new Rectangle2D.Double(xOffset - chart.getStyler().getChartTitlePadding(), + yOffset - textBounds.getHeight() - chart.getStyler().getChartTitlePadding(), width, height); + g.setRenderingHint(RenderingHints.KEY_ANTIALIASING, oldHint); + } + + private Rectangle2D getBoundsHint() { + if (chart.getStyler().isChartTitleVisible() && chart.getTitle().length() > 0) { + TextLayout textLayout = new TextLayout(chart.getTitle(), chart.getStyler().getChartTitleFont(), + new FontRenderContext(null, true, false)); + Rectangle2D rectangle = textLayout.getBounds(); + double width = 2 * chart.getStyler().getChartTitlePadding() + rectangle.getWidth(); + double height = 2 * chart.getStyler().getChartTitlePadding() + rectangle.getHeight(); + return new Rectangle2D.Double(Double.NaN, Double.NaN, width, height); + } else { + return new Rectangle2D.Double(); + } + } +} \ No newline at end of file diff --git a/chart/src/main/java/org/xbib/graphics/chart/Histogram.java b/chart/src/main/java/org/xbib/graphics/chart/Histogram.java new file mode 100644 index 0000000..2a4912e --- /dev/null +++ b/chart/src/main/java/org/xbib/graphics/chart/Histogram.java @@ -0,0 +1,96 @@ +package org.xbib.graphics.chart; + +import java.util.ArrayList; +import java.util.Collection; +import java.util.List; + +public class Histogram { + + private final Collection originalData; + + private final int numBins; + + private final double min; + + private final double max; + + private List xAxisData; + + private List yAxisData; + + public Histogram(Collection data, int numBins) { + this.numBins = numBins; + this.originalData = data; + double tempMax = -Double.MAX_VALUE; + double tempMin = Double.MAX_VALUE; + for (Number number : data) { + double value = number.doubleValue(); + if (value > tempMax) { + tempMax = value; + } + if (value < tempMin) { + tempMin = value; + } + } + max = tempMax; + min = tempMin; + init(); + } + + public Histogram(Collection data, int numBins, double min, double max) { + this.numBins = numBins; + this.originalData = data; + this.min = min; + this.max = max; + init(); + } + + private void init() { + double[] tempYAxisData = new double[numBins]; + final double binSize = (max - min) / numBins; + for (Number anOriginalData : originalData) { + double doubleValue = (anOriginalData).doubleValue(); + int bin = (int) ((doubleValue - min) / binSize); + if (bin >= 0) { + if (doubleValue == max) { + tempYAxisData[bin - 1] += 1; + } else if (bin <= numBins && bin != numBins) { + tempYAxisData[bin] += 1; + } + } + } + yAxisData = new ArrayList<>(numBins); + for (double d : tempYAxisData) { + yAxisData.add(d); + } + xAxisData = new ArrayList<>(numBins); + for (int i = 0; i < numBins; i++) { + xAxisData.add(((i * (max - min)) / numBins + min) + binSize / 2); + } + } + + public List getxAxisData() { + return xAxisData; + } + + public List getyAxisData() { + return yAxisData; + } + + public Collection getOriginalData() { + return originalData; + } + + public int getNumBins() { + return numBins; + } + + public double getMin() { + return min; + } + + public double getMax() { + return max; + } + +} diff --git a/chart/src/main/java/org/xbib/graphics/chart/QuickChart.java b/chart/src/main/java/org/xbib/graphics/chart/QuickChart.java new file mode 100644 index 0000000..bc2ca8d --- /dev/null +++ b/chart/src/main/java/org/xbib/graphics/chart/QuickChart.java @@ -0,0 +1,59 @@ +package org.xbib.graphics.chart; + +import org.xbib.graphics.chart.theme.Theme; +import org.xbib.graphics.chart.xy.XYChart; +import org.xbib.graphics.chart.xy.XYSeries; + +import java.util.List; + +/** + * A convenience class for making Charts with one line of code. + */ +public final class QuickChart { + + private final static int WIDTH = 600; + private final static int HEIGHT = 400; + + private QuickChart() { + } + + public static XYChart getChart(String chartTitle, String xTitle, String yTitle, + String seriesName, double[] xData, double[] yData) { + double[][] yData2d = {yData}; + if (seriesName == null) { + return getChart(chartTitle, xTitle, yTitle, null, xData, yData2d); + } else { + return getChart(chartTitle, xTitle, yTitle, new String[]{seriesName}, xData, yData2d); + } + } + + public static XYChart getChart(String chartTitle, String xTitle, String yTitle, + String[] seriesNames, double[] xData, double[][] yData) { + XYChart chart = new XYChart(WIDTH, HEIGHT); + chart.setTitle(chartTitle); + chart.setXAxisTitle(xTitle); + chart.setYAxisTitle(yTitle); + for (int i = 0; i < yData.length; i++) { + XYSeries series; + if (seriesNames != null) { + series = chart.addSeries(seriesNames[i], xData, yData[i]); + } else { + chart.getStyler().setLegendVisible(false); + series = chart.addSeries(" " + i, xData, yData[i]); + } + series.setMarker(Theme.Series.NONE_MARKER); + } + return chart; + } + + public static XYChart getChart(String chartTitle, String xTitle, String yTitle, + String seriesName, List xData, List yData) { + XYChart chart = new XYChart(WIDTH, HEIGHT); + chart.setTitle(chartTitle); + chart.setXAxisTitle(xTitle); + chart.setYAxisTitle(yTitle); + XYSeries series = chart.addSeries(seriesName, xData, yData); + series.setMarker(Theme.Series.NONE_MARKER); + return chart; + } +} diff --git a/chart/src/main/java/org/xbib/graphics/chart/axis/Axis.java b/chart/src/main/java/org/xbib/graphics/chart/axis/Axis.java new file mode 100644 index 0000000..cec0821 --- /dev/null +++ b/chart/src/main/java/org/xbib/graphics/chart/axis/Axis.java @@ -0,0 +1,293 @@ +package org.xbib.graphics.chart.axis; + +import org.xbib.graphics.chart.category.CategoryStyler; +import org.xbib.graphics.chart.Chart; +import org.xbib.graphics.chart.ChartComponent; +import org.xbib.graphics.chart.series.AxesChartSeries; +import org.xbib.graphics.chart.series.AxesChartSeriesCategory; +import org.xbib.graphics.chart.style.AxesChartStyler; +import org.xbib.graphics.chart.legend.LegendPosition; + +import java.awt.Graphics2D; +import java.awt.RenderingHints; +import java.awt.Shape; +import java.awt.font.FontRenderContext; +import java.awt.font.TextLayout; +import java.awt.geom.AffineTransform; +import java.awt.geom.Rectangle2D; +import java.util.List; +import java.util.Map; + +public class Axis implements ChartComponent { + + private final Chart chart; + private final Rectangle2D.Double bounds; + private final ST axesChartStyler; + private final AxisTitle axisTitle; + private final AxisTick axisTick; + private final Direction direction; + private final int yIndex; + private DataType dataType; + private AxisTickCalculator axisTickCalculator; + private double min; + private double max; + + public Axis(Chart chart, Direction direction, int yIndex) { + this.chart = chart; + this.axesChartStyler = chart.getStyler(); + this.direction = direction; + this.yIndex = yIndex; + bounds = new Rectangle2D.Double(); + axisTitle = new AxisTitle<>(chart, direction, direction == Direction.Y ? this : null, yIndex); + axisTick = new AxisTick<>(chart, direction, direction == Direction.Y ? this : null); + } + + @Override + public Rectangle2D getBounds() { + return bounds; + } + + public void resetMinMax() { + min = Double.MAX_VALUE; + max = -Double.MAX_VALUE; + } + + public void addMinMax(double min, double max) { + if (Double.isNaN(this.min) || min < this.min) { + this.min = min; + } + if (max > this.max) { + this.max = max; + } + } + + public void preparePaint() { + if (direction == Direction.Y) { + double xOffset = 0; + double yOffset = chart.getChartTitle().getBounds().getHeight() + axesChartStyler.getChartPadding(); + int i = 1; + double width = 60; + double height; + do { + double approximateXAxisWidth = chart.getWidth() - width + - (axesChartStyler.getLegendPosition() == LegendPosition.OutsideE + ? chart.getLegend().getBounds().getWidth() + : 0) + - 2 * axesChartStyler.getChartPadding() + - (axesChartStyler.isYAxisTicksVisible() ? (axesChartStyler.getPlotMargin()) : 0) + - (axesChartStyler.getLegendPosition() == LegendPosition.OutsideE + && axesChartStyler.isLegendVisible() + ? axesChartStyler.getChartPadding() + : 0); + + height = chart.getHeight() - yOffset + - chart.getXAxis().getXAxisHeightHint(approximateXAxisWidth) + - axesChartStyler.getPlotMargin() + - axesChartStyler.getChartPadding() + - (axesChartStyler.getLegendPosition() == LegendPosition.OutsideS + ? chart.getLegend().getBounds().getHeight() + : 0); + + width = getYAxisWidthHint(height); + } while (i-- > 0); + bounds.setRect(xOffset, yOffset, width, height); + } else { + Rectangle2D leftYAxisBounds = chart.getAxisPair().getLeftYAxisBounds(); + Rectangle2D rightYAxisBounds = chart.getAxisPair().getRightYAxisBounds(); + double maxYAxisY = Math.max(leftYAxisBounds.getY() + leftYAxisBounds.getHeight(), + rightYAxisBounds.getY() + rightYAxisBounds.getHeight()); + double xOffset = leftYAxisBounds.getWidth() + axesChartStyler.getChartPadding(); + double yOffset = maxYAxisY + axesChartStyler.getPlotMargin() + - (axesChartStyler.getLegendPosition() == LegendPosition.OutsideS + ? chart.getLegend().getBounds().getHeight() + : 0); + double legendWidth = 0; + if (axesChartStyler.getLegendPosition() == LegendPosition.OutsideE + && axesChartStyler.isLegendVisible()) { + legendWidth = chart.getLegend().getBounds().getWidth() + axesChartStyler.getChartPadding(); + } + double width = chart.getWidth() - leftYAxisBounds.getWidth() + - rightYAxisBounds.getWidth() + - 2 * axesChartStyler.getChartPadding() + - legendWidth; + double height = chart.getHeight() - maxYAxisY + - axesChartStyler.getChartPadding() + - axesChartStyler.getPlotMargin(); + bounds.setRect(xOffset, yOffset, width, height); + } + } + + @Override + public void paint(Graphics2D g) { + Object oldHint = g.getRenderingHint(RenderingHints.KEY_ANTIALIASING); + g.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON); + if (direction == Direction.Y) { + boolean onRight = axesChartStyler.getYAxisGroupPosistion(yIndex) == YAxisPosition.Right; + if (onRight) { + axisTick.paint(g); + axisTitle.paint(g); + } else { + axisTitle.paint(g); + axisTick.paint(g); + } + bounds.width = (axesChartStyler.isYAxisTitleVisible() ? axisTitle.getBounds().getWidth() : 0) + + axisTick.getBounds().getWidth(); + + } else { + this.axisTickCalculator = getAxisTickCalculator(bounds.getWidth()); + axisTitle.paint(g); + axisTick.paint(g); + } + g.setRenderingHint(RenderingHints.KEY_ANTIALIASING, oldHint); + } + + private double getXAxisHeightHint(double workingSpace) { + double titleHeight = 0.0; + if (chart.getXAxisTitle() != null && + !chart.getXAxisTitle().trim().equalsIgnoreCase("") && + axesChartStyler.isXAxisTitleVisible()) { + TextLayout textLayout = new TextLayout(chart.getXAxisTitle(), + axesChartStyler.getAxisTitleFont(), new FontRenderContext(null, true, false)); + Rectangle2D rectangle = textLayout.getBounds(); + titleHeight = rectangle.getHeight() + chart.getStyler().getAxisTitlePadding(); + } + this.axisTickCalculator = getAxisTickCalculator(workingSpace); + double axisTickLabelsHeight = 0.0; + if (chart.getStyler().isXAxisTicksVisible()) { + String sampleLabel = ""; + for (int i = 0; i < axisTickCalculator.getTickLabels().size(); i++) { + if (axisTickCalculator.getTickLabels().get(i) != null && + axisTickCalculator.getTickLabels().get(i).length() > sampleLabel.length()) { + sampleLabel = axisTickCalculator.getTickLabels().get(i); + } + } + TextLayout textLayout = new TextLayout(sampleLabel.length() == 0 ? " " : sampleLabel, + axesChartStyler.getAxisTickLabelsFont(), + new FontRenderContext(null, true, false)); + AffineTransform rot = axesChartStyler.getXAxisLabelRotation() == 0 ? null : + AffineTransform.getRotateInstance(-1 * Math.toRadians(axesChartStyler.getXAxisLabelRotation())); + Shape shape = textLayout.getOutline(rot); + Rectangle2D rectangle = shape.getBounds(); + axisTickLabelsHeight = rectangle.getHeight() + axesChartStyler.getAxisTickPadding() + axesChartStyler.getAxisTickMarkLength(); + } + return titleHeight + axisTickLabelsHeight; + } + + private double getYAxisWidthHint(double workingSpace) { + double titleHeight = 0.0; + if (chart.getyYAxisTitle() != null && + !chart.getyYAxisTitle().trim().equalsIgnoreCase("") && + axesChartStyler.isYAxisTitleVisible()) { + TextLayout textLayout = new TextLayout(chart.getyYAxisTitle(), + axesChartStyler.getAxisTitleFont(), new FontRenderContext(null, true, false)); + Rectangle2D rectangle = textLayout.getBounds(); + titleHeight = rectangle.getHeight() + axesChartStyler.getAxisTitlePadding(); + } + double axisTickLabelsHeight = 0.0; + if (axesChartStyler.isYAxisTicksVisible()) { + this.axisTickCalculator = getAxisTickCalculator(workingSpace); + String sampleLabel = ""; + for (int i = 0; i < axisTickCalculator.getTickLabels().size(); i++) { + if (axisTickCalculator.getTickLabels().get(i) != null + && axisTickCalculator.getTickLabels().get(i).length() > sampleLabel.length()) { + sampleLabel = axisTickCalculator.getTickLabels().get(i); + } + } + TextLayout textLayout = new TextLayout(sampleLabel.length() == 0 ? " " : sampleLabel, + axesChartStyler.getAxisTickLabelsFont(), new FontRenderContext(null, true, false)); + Rectangle2D rectangle = textLayout.getBounds(); + axisTickLabelsHeight = rectangle.getWidth() + axesChartStyler.getAxisTickPadding() + axesChartStyler.getAxisTickMarkLength(); + } + return titleHeight + axisTickLabelsHeight; + } + + private AxisTickCalculator getAxisTickCalculator(double workingSpace) { + Map labelOverrideMap = chart.getYAxisLabelOverrideMap(getDirection(), yIndex); + if (labelOverrideMap != null) { + if (getDirection() == Direction.X && axesChartStyler instanceof CategoryStyler) { + AxesChartSeriesCategory axesChartSeries = + (AxesChartSeriesCategory) chart.getSeriesMap().values().iterator().next(); + List categories = (List) axesChartSeries.getXData(); + DataType axisType = chart.getAxisPair().getXAxis().getDataType(); + return new AxisTickCalculatorOverride(getDirection(), + workingSpace, + axesChartStyler, + labelOverrideMap, + axisType, + categories.size()); + } + return new AxisTickCalculatorOverride(getDirection(), workingSpace, min, max, axesChartStyler, labelOverrideMap); + } + if (getDirection() == Direction.X) { + if (axesChartStyler instanceof CategoryStyler) { + AxesChartSeriesCategory axesChartSeries = + (AxesChartSeriesCategory) chart.getSeriesMap().values().iterator().next(); + List categories = (List) axesChartSeries.getXData(); + DataType axisType = chart.getAxisPair().getXAxis().getDataType(); + return new AxisTickCalculatorCategory( + getDirection(), workingSpace, categories, axisType, axesChartStyler); + } else if (getDataType() == DataType.Instant) { + return new AxisTickCalculatorInstant(getDirection(), workingSpace, min, max, axesChartStyler); + } else if (axesChartStyler.isXAxisLogarithmic()) { + return new AxisTickCalculatorLogarithmic( + getDirection(), workingSpace, min, max, axesChartStyler); + } else { + return new AxisTickCalculatorNumber(getDirection(), workingSpace, min, max, axesChartStyler); + } + } else { + if (axesChartStyler.isYAxisLogarithmic() && getDataType() != DataType.Instant) { + return new AxisTickCalculatorLogarithmic(getDirection(), workingSpace, min, max, axesChartStyler); + } else { + return new AxisTickCalculatorNumber(getDirection(), workingSpace, min, max, axesChartStyler); + } + } + } + + public DataType getDataType() { + return dataType; + } + + public void setDataType(DataType dataType) { + + if (dataType != null && this.dataType != null && this.dataType != dataType) { + throw new IllegalArgumentException("Different Axes (e.g. Date, Number, String) cannot be mixed on the same chart!!"); + } + this.dataType = dataType; + } + + public double getMin() { + return min; + } + + public void setMin(double min) { + this.min = min; + } + + public double getMax() { + return max; + } + + public void setMax(double max) { + this.max = max; + } + + public AxisTick getAxisTick() { + return axisTick; + } + + public Direction getDirection() { + return direction; + } + + public AxisTitle getAxisTitle() { + return axisTitle; + } + + public AxisTickCalculator getAxisTickCalculator() { + return this.axisTickCalculator; + } + + public int getYIndex() { + return yIndex; + } +} diff --git a/chart/src/main/java/org/xbib/graphics/chart/axis/AxisPair.java b/chart/src/main/java/org/xbib/graphics/chart/axis/AxisPair.java new file mode 100644 index 0000000..20a2a84 --- /dev/null +++ b/chart/src/main/java/org/xbib/graphics/chart/axis/AxisPair.java @@ -0,0 +1,363 @@ +package org.xbib.graphics.chart.axis; + +import org.xbib.graphics.chart.category.CategorySeriesRenderStyle; +import org.xbib.graphics.chart.category.CategoryStyler; +import org.xbib.graphics.chart.Chart; +import org.xbib.graphics.chart.ChartComponent; +import org.xbib.graphics.chart.series.AxesChartSeries; +import org.xbib.graphics.chart.series.AxesChartSeriesCategory; +import org.xbib.graphics.chart.style.AxesChartStyler; +import org.xbib.graphics.chart.legend.LegendPosition; + +import java.awt.Graphics2D; +import java.awt.geom.Rectangle2D; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.TreeMap; + +public class AxisPair implements ChartComponent { + + private final Chart chart; + + private final Axis xAxis; + + private final Axis yAxis; + + private final TreeMap> yAxisMap; + + private final Rectangle2D.Double leftYAxisBounds; + + private final Rectangle2D.Double rightYAxisBounds; + + private Axis leftMainYAxis; + + private Axis rightMainYAxis; + + private final Map> axisLabelOverrideMap; + + public AxisPair(Chart chart) { + this.chart = chart; + this.xAxis = new Axis<>(chart, Direction.X, 0); + this.yAxis = new Axis<>(chart, Direction.Y, 0); + this.yAxisMap = new TreeMap<>(); + yAxisMap.put(0, yAxis); + leftYAxisBounds = new Rectangle2D.Double(); + rightYAxisBounds = new Rectangle2D.Double(); + axisLabelOverrideMap = new HashMap<>(); + } + + @Override + public Rectangle2D getBounds() { + return null; // should never be called + } + + @Override + public void paint(Graphics2D g) { + prepareForPaint(); + leftMainYAxis = null; + rightMainYAxis = null; + ST styler = chart.getStyler(); + final int chartPadding = styler.getChartPadding(); + int tickMargin = (styler.isYAxisTicksVisible() ? (styler.getPlotMargin()) : 0); + leftYAxisBounds.width = 0; + int leftCount = 0; + double leftStart = chartPadding; + for (Map.Entry> e : yAxisMap.entrySet()) { + Axis ya = e.getValue(); + if (styler.getYAxisGroupPosistion(e.getKey()) == YAxisPosition.Right) { + continue; + } + if (e.getKey() == 0) { + continue; + } + ya.preparePaint(); + Rectangle2D.Double bounds = (Rectangle2D.Double) ya.getBounds(); + bounds.x = leftStart; + ya.paint(g); + double width = bounds.getWidth(); + leftStart += chartPadding + width + tickMargin; + leftYAxisBounds.width += width; + leftCount++; + leftMainYAxis = ya; + } + if (styler.getYAxisGroupPosistion(0) != YAxisPosition.Right) { + yAxis.preparePaint(); + Rectangle2D.Double bounds = (Rectangle2D.Double) yAxis.getBounds(); + bounds.x = leftStart; + yAxis.paint(g); + double width = bounds.getWidth(); + //leftStart += chartPadding + width + tickMargin; + leftYAxisBounds.width += width; + leftCount++; + leftMainYAxis = yAxis; + } + + if (leftCount > 1) { + leftYAxisBounds.width += (leftCount - 1) * chartPadding; + } + leftYAxisBounds.width += leftCount * tickMargin; + rightYAxisBounds.width = 0; + double legendWidth = 0; + if (styler.getLegendPosition() == LegendPosition.OutsideE && styler.isLegendVisible()) { + legendWidth = chart.getLegend().getBounds().getWidth() + styler.getChartPadding(); + } + double rightEnd = chart.getWidth() - legendWidth - chartPadding; + rightYAxisBounds.x = rightEnd; + int rightCount = 0; + for (Map.Entry> e : yAxisMap.descendingMap().entrySet()) { + Axis ya = e.getValue(); + if (styler.getYAxisGroupPosistion(e.getKey()) != YAxisPosition.Right) { + continue; + } + if (e.getKey() == 0) { + continue; + } + ya.preparePaint(); + Rectangle2D.Double bounds = (Rectangle2D.Double) ya.getBounds(); + double aproxWidth = bounds.getWidth(); + double xOffset = rightEnd - aproxWidth; + bounds.x = xOffset; + rightYAxisBounds.x = xOffset; + ya.paint(g); + rightYAxisBounds.width += aproxWidth; + rightEnd -= chartPadding + aproxWidth + tickMargin; + rightCount++; + rightMainYAxis = ya; + } + if (styler.getYAxisGroupPosistion(0) == YAxisPosition.Right) { + yAxis.preparePaint(); + Rectangle2D.Double bounds = (Rectangle2D.Double) yAxis.getBounds(); + double aproxWidth = bounds.getWidth(); + double xOffset = rightEnd - aproxWidth; + bounds.x = xOffset; + rightYAxisBounds.x = xOffset; + yAxis.paint(g); + rightYAxisBounds.width += aproxWidth; + //rightEnd -= chartPadding + aproxWidth + tickMargin; + rightCount++; + rightMainYAxis = yAxis; + } + if (leftMainYAxis == null) { + leftMainYAxis = yAxis; + } + if (rightMainYAxis == null) { + rightMainYAxis = yAxis; + } + + if (rightCount > 1) { + rightYAxisBounds.width += (rightCount - 1) * chartPadding; + } + rightYAxisBounds.width += rightCount * tickMargin; + Rectangle2D.Double bounds = (java.awt.geom.Rectangle2D.Double) yAxis.getBounds(); + leftYAxisBounds.x = chartPadding; + leftYAxisBounds.y = bounds.y; + leftYAxisBounds.height = bounds.height; + rightYAxisBounds.y = bounds.y; + rightYAxisBounds.height = bounds.height; + xAxis.preparePaint(); + xAxis.paint(g); + } + + public Axis getYAxis(int yIndex) { + return yAxisMap.get(yIndex); + } + + public Axis getXAxis() { + return xAxis; + } + + public Axis getYAxis() { + return yAxis; + } + + public Rectangle2D.Double getLeftYAxisBounds() { + return leftYAxisBounds; + } + + public Rectangle2D.Double getRightYAxisBounds() { + return rightYAxisBounds; + } + + public Axis getLeftMainYAxis() { + return leftMainYAxis; + } + + public Axis getRightMainYAxis() { + return rightMainYAxis; + } + + public Map> getAxisLabelOverrideMap() { + return axisLabelOverrideMap; + } + + private void prepareForPaint() { + boolean mainYAxisUsed = false; + if (chart.getSeriesMap() != null) { + for (S series : chart.getSeriesMap().values()) { + int yIndex = series.getYAxisGroup(); + if (!mainYAxisUsed && yIndex == 0) { + mainYAxisUsed = true; + } + if (yAxisMap.containsKey(yIndex)) { + continue; + } + yAxisMap.put(yIndex, new Axis<>(chart, Direction.Y, yIndex)); + } + } + xAxis.setDataType(null); + yAxis.setDataType(null); + for (S series : chart.getSeriesMap().values()) { + xAxis.setDataType(series.getxAxisDataType()); + yAxis.setDataType(series.getyAxisDataType()); + getYAxis(series.getYAxisGroup()).setDataType(series.getyAxisDataType()); + if (!mainYAxisUsed) { + yAxis.setDataType(series.getyAxisDataType()); + } + } + xAxis.resetMinMax(); + for (Axis ya : yAxisMap.values()) { + ya.resetMinMax(); + } + if (chart.getSeriesMap() == null || chart.getSeriesMap().size() < 1) { + xAxis.addMinMax(-1, 1); + for (Axis ya : yAxisMap.values()) { + ya.addMinMax(-1, 1); + } + } else { + int disabledCount = 0; + for (S series : chart.getSeriesMap().values()) { + if (!series.isEnabled()) { + disabledCount++; + continue; + } + xAxis.addMinMax(series.getXMin(), series.getXMax()); + getYAxis(series.getYAxisGroup()).addMinMax(series.getYMin(), series.getYMax()); + if (!mainYAxisUsed) { + yAxis.addMinMax(series.getYMin(), series.getYMax()); + } + } + if (disabledCount == chart.getSeriesMap().values().size()) { + xAxis.addMinMax(-1, 1); + for (Axis ya : yAxisMap.values()) { + ya.addMinMax(-1, 1); + } + } + } + overrideMinMaxForXAxis(); + for (Axis ya : yAxisMap.values()) { + overrideMinMaxForYAxis(ya); + } + if (chart.getStyler().isXAxisLogarithmic() && xAxis.getMin() <= 0.0) { + throw new IllegalArgumentException("Series data (accounting for error bars too) cannot be less or equal to zero for a logarithmic X-Axis"); + } + if (chart.getStyler().isYAxisLogarithmic() && yAxis.getMin() <= 0.0) { + for (Axis ya : yAxisMap.values()) { + if (ya.getMin() <= 0.0) { + throw new IllegalArgumentException("Series data (accounting for error bars too) cannot be less or equal to zero for a logarithmic Y-Axis"); + } + } + } + if (xAxis.getMin() == Double.POSITIVE_INFINITY || xAxis.getMax() == Double.POSITIVE_INFINITY) { + throw new IllegalArgumentException( + "Series data (accounting for error bars too) cannot be equal to Double.POSITIVE_INFINITY!!!"); + } + for (Axis ya : yAxisMap.values()) { + if (ya.getMin() == Double.POSITIVE_INFINITY || ya.getMax() == Double.POSITIVE_INFINITY) { + throw new IllegalArgumentException( + "Series data (accounting for error bars too) cannot be equal to Double.POSITIVE_INFINITY!!!"); + } + if (ya.getMin() == Double.NEGATIVE_INFINITY || ya.getMax() == Double.NEGATIVE_INFINITY) { + throw new IllegalArgumentException( + "Series data (accounting for error bars too) cannot be equal to Double.NEGATIVE_INFINITY!!!"); + } + } + + if (xAxis.getMin() == Double.NEGATIVE_INFINITY || xAxis.getMax() == Double.NEGATIVE_INFINITY) { + throw new IllegalArgumentException( + "Series data (accounting for error bars too) cannot be equal to Double.NEGATIVE_INFINITY!!!"); + } + } + + private void overrideMinMaxForXAxis() { + double overrideXAxisMinValue = xAxis.getMin(); + double overrideXAxisMaxValue = xAxis.getMax(); + if (chart.getStyler().getXAxisMin() != null) { + overrideXAxisMinValue = chart.getStyler().getXAxisMin(); + } + if (chart.getStyler().getXAxisMax() != null) { + overrideXAxisMaxValue = chart.getStyler().getXAxisMax(); + } + xAxis.setMin(overrideXAxisMinValue); + xAxis.setMax(overrideXAxisMaxValue); + } + + private void overrideMinMaxForYAxis(Axis yAxis) { + double overrideYAxisMinValue = yAxis.getMin(); + double overrideYAxisMaxValue = yAxis.getMax(); + if (chart.getStyler() instanceof CategoryStyler) { + CategoryStyler categoryStyler = (CategoryStyler) chart.getStyler(); + if (categoryStyler.getDefaultSeriesRenderStyle() == CategorySeriesRenderStyle.Bar) { + if (categoryStyler.isStacked()) { + AxesChartSeriesCategory axesChartSeries = + (AxesChartSeriesCategory) chart.getSeriesMap().values().iterator().next(); + List categories = (List) axesChartSeries.getXData(); + int numCategories = categories.size(); + double[] accumulatedStackOffsetPos = new double[numCategories]; + double[] accumulatedStackOffsetNeg = new double[numCategories]; + for (S series : chart.getSeriesMap().values()) { + AxesChartSeriesCategory axesChartSeriesCategory = (AxesChartSeriesCategory) series; + if (!series.isEnabled()) { + continue; + } + int categoryCounter = 0; + for (Number next : axesChartSeriesCategory.getYData()) { + if (next == null) { + categoryCounter++; + continue; + } + if (next.doubleValue() > 0) { + accumulatedStackOffsetPos[categoryCounter] += next.doubleValue(); + } else if (next.doubleValue() < 0) { + accumulatedStackOffsetNeg[categoryCounter] += next.doubleValue(); + } + categoryCounter++; + } + } + double max = accumulatedStackOffsetPos[0]; + for (int i = 1; i < accumulatedStackOffsetPos.length; i++) { + if (accumulatedStackOffsetPos[i] > max) { + max = accumulatedStackOffsetPos[i]; + } + } + double min = accumulatedStackOffsetNeg[0]; + for (int i = 1; i < accumulatedStackOffsetNeg.length; i++) { + if (accumulatedStackOffsetNeg[i] < min) { + min = accumulatedStackOffsetNeg[i]; + } + } + overrideYAxisMaxValue = max; + overrideYAxisMinValue = min; + } + if (yAxis.getMin() > 0.0) { + overrideYAxisMinValue = 0.0; + } + if (yAxis.getMax() < 0.0) { + overrideYAxisMaxValue = 0.0; + } + } + } + if (chart.getStyler().getYAxisMin(yAxis.getYIndex()) != null) { + overrideYAxisMinValue = chart.getStyler().getYAxisMin(yAxis.getYIndex()); + } else if (chart.getStyler().getYAxisMin() != null) { + overrideYAxisMinValue = chart.getStyler().getYAxisMin(); + } + if (chart.getStyler().getYAxisMax(yAxis.getYIndex()) != null) { + overrideYAxisMaxValue = chart.getStyler().getYAxisMax(yAxis.getYIndex()); + } else if (chart.getStyler().getYAxisMax() != null) { + overrideYAxisMaxValue = chart.getStyler().getYAxisMax(); + } + yAxis.setMin(overrideYAxisMinValue); + yAxis.setMax(overrideYAxisMaxValue); + } +} diff --git a/chart/src/main/java/org/xbib/graphics/chart/axis/AxisTick.java b/chart/src/main/java/org/xbib/graphics/chart/axis/AxisTick.java new file mode 100644 index 0000000..ac19962 --- /dev/null +++ b/chart/src/main/java/org/xbib/graphics/chart/axis/AxisTick.java @@ -0,0 +1,62 @@ +package org.xbib.graphics.chart.axis; + +import org.xbib.graphics.chart.Chart; +import org.xbib.graphics.chart.ChartComponent; +import org.xbib.graphics.chart.series.AxesChartSeries; +import org.xbib.graphics.chart.style.AxesChartStyler; + +import java.awt.Graphics2D; +import java.awt.geom.Rectangle2D; + +public class AxisTick implements ChartComponent { + + private final Chart chart; + + private final Direction direction; + + private Rectangle2D bounds; + + private final AxisTickLabels axisTickLabels; + + private final AxisTickMarks axisTickMarks; + + protected AxisTick(Chart chart, Direction direction, Axis yAxis) { + this.chart = chart; + this.direction = direction; + axisTickLabels = new AxisTickLabels<>(chart, direction, yAxis); + axisTickMarks = new AxisTickMarks<>(chart, direction, yAxis); + } + + @Override + public Rectangle2D getBounds() { + return bounds; + } + + @Override + public void paint(Graphics2D g) { + if (direction == Direction.Y && chart.getStyler().isYAxisTicksVisible()) { + axisTickLabels.paint(g); + axisTickMarks.paint(g); + bounds = new Rectangle2D.Double(axisTickLabels.getBounds().getX(), + axisTickLabels.getBounds().getY(), + axisTickLabels.getBounds().getWidth() + chart.getStyler().getAxisTickPadding() + axisTickMarks.getBounds().getWidth(), + axisTickMarks.getBounds().getHeight() + ); + } else if (direction == Direction.X && chart.getStyler().isXAxisTicksVisible()) { + axisTickLabels.paint(g); + axisTickMarks.paint(g); + bounds = new Rectangle2D.Double(axisTickMarks.getBounds().getX(), + axisTickMarks.getBounds().getY(), + axisTickLabels.getBounds().getWidth(), + axisTickMarks.getBounds().getHeight() + chart.getStyler().getAxisTickPadding() + axisTickLabels.getBounds().getHeight() + ); + } else { + bounds = new Rectangle2D.Double(); + } + } + + protected AxisTickLabels getAxisTickLabels() { + return axisTickLabels; + } + +} diff --git a/chart/src/main/java/org/xbib/graphics/chart/axis/AxisTickCalculator.java b/chart/src/main/java/org/xbib/graphics/chart/axis/AxisTickCalculator.java new file mode 100644 index 0000000..16a2701 --- /dev/null +++ b/chart/src/main/java/org/xbib/graphics/chart/axis/AxisTickCalculator.java @@ -0,0 +1,87 @@ +package org.xbib.graphics.chart.axis; + +import org.xbib.graphics.chart.style.AxesChartStyler; + +import java.awt.Shape; +import java.awt.font.FontRenderContext; +import java.awt.font.TextLayout; +import java.awt.geom.AffineTransform; +import java.awt.geom.Rectangle2D; +import java.text.Format; +import java.util.LinkedList; +import java.util.List; + +public abstract class AxisTickCalculator { + + protected final Direction axisDirection; + + protected final double workingSpace; + + protected final double minValue; + + protected final double maxValue; + + protected final AxesChartStyler styler; + + protected Format axisFormat; + + protected List tickLocations = new LinkedList<>(); + + protected List tickLabels = new LinkedList<>(); + + public AxisTickCalculator(Direction axisDirection, double workingSpace, double minValue, double maxValue, AxesChartStyler styler) { + this.axisDirection = axisDirection; + this.workingSpace = workingSpace; + this.minValue = minValue; + this.maxValue = maxValue; + this.styler = styler; + } + + /** + * Gets the first position + * + * @param gridStep grid step + * @return first posiition + */ + public double getFirstPosition(double gridStep) { + return minValue - (minValue % gridStep) - gridStep; + } + + public List getTickLocations() { + return tickLocations; + } + + public List getTickLabels() { + return tickLabels; + } + + /** + * Given the generated tickLabels, will they fit side-by-side without overlapping each other and looking bad? + * Sometimes the given tickSpacingHint is simply too small. + * + * @param tickLabels tick lables + * @param tickSpacingHint tick psacing hint + * @return true if it fits + */ + public boolean willLabelsFitInTickSpaceHint(List tickLabels, int tickSpacingHint) { + if (this.axisDirection == Direction.Y) { + return true; + } + String sampleLabel = " "; + for (String tickLabel : tickLabels) { + if (tickLabel != null && tickLabel.length() > sampleLabel.length()) { + sampleLabel = tickLabel; + } + } + TextLayout textLayout = new TextLayout(sampleLabel, styler.getAxisTickLabelsFont(), new FontRenderContext(null, true, false)); + AffineTransform rot = styler.getXAxisLabelRotation() == 0 ? null : AffineTransform.getRotateInstance(-1 * Math.toRadians(styler.getXAxisLabelRotation())); + Shape shape = textLayout.getOutline(rot); + Rectangle2D rectangle = shape.getBounds(); + double largestLabelWidth = rectangle.getWidth(); + return (largestLabelWidth * 1.1 < tickSpacingHint); + } + + public Format getAxisFormat() { + return axisFormat; + } +} diff --git a/chart/src/main/java/org/xbib/graphics/chart/axis/AxisTickCalculatorCategory.java b/chart/src/main/java/org/xbib/graphics/chart/axis/AxisTickCalculatorCategory.java new file mode 100644 index 0000000..92ad620 --- /dev/null +++ b/chart/src/main/java/org/xbib/graphics/chart/axis/AxisTickCalculatorCategory.java @@ -0,0 +1,51 @@ +package org.xbib.graphics.chart.axis; + +import org.xbib.graphics.chart.formatter.DateFormatter; +import org.xbib.graphics.chart.formatter.NumberFormatter; +import org.xbib.graphics.chart.formatter.StringFormatter; +import org.xbib.graphics.chart.style.AxesChartStyler; + +import java.math.BigDecimal; +import java.util.List; + +/** + * This class encapsulates the logic to generate the axis tick mark and axis tick label data for rendering the axis + * ticks for String axes + */ +public class AxisTickCalculatorCategory extends AxisTickCalculator { + + public AxisTickCalculatorCategory(Direction axisDirection, double workingSpace, List categories, DataType axisType, AxesChartStyler styler) { + super(axisDirection, workingSpace, Double.NaN, Double.NaN, styler); + calculate(categories, axisType); + } + + private void calculate(List categories, DataType axisType) { + // tick space - a percentage of the working space available for ticks + double tickSpace = styler.getPlotContentSize() * workingSpace; // in plot space + // where the tick should begin in the working space in pixels + double margin = (workingSpace - tickSpace) / 2.0; + // generate all tickLabels and tickLocations from the first to last position + double gridStep = (tickSpace / (double) categories.size()); + double firstPosition = gridStep / 2.0; + // set up String formatters that may be encountered + if (axisType == DataType.String) { + axisFormat = new StringFormatter(); + } else if (axisType == DataType.Number) { + axisFormat = new NumberFormatter(styler, axisDirection, minValue, maxValue); + } else if (axisType == DataType.Instant) { + axisFormat = new DateFormatter(styler.getDatePattern(), styler.getLocale()); + } + int counter = 0; + for (Object category : categories) { + if (axisType == DataType.String) { + tickLabels.add(category.toString()); + } else if (axisType == DataType.Number) { + tickLabels.add(axisFormat.format(new BigDecimal(category.toString()).doubleValue())); + } else if (axisType == DataType.Instant) { + tickLabels.add(axisFormat.format(category)); + } + double tickLabelPosition = (int) (margin + firstPosition + gridStep * counter++); + tickLocations.add(tickLabelPosition); + } + } +} diff --git a/chart/src/main/java/org/xbib/graphics/chart/axis/AxisTickCalculatorInstant.java b/chart/src/main/java/org/xbib/graphics/chart/axis/AxisTickCalculatorInstant.java new file mode 100644 index 0000000..8b749e2 --- /dev/null +++ b/chart/src/main/java/org/xbib/graphics/chart/axis/AxisTickCalculatorInstant.java @@ -0,0 +1,161 @@ +package org.xbib.graphics.chart.axis; + +import org.xbib.graphics.chart.style.AxesChartStyler; + +import java.time.Instant; +import java.time.format.DateTimeFormatter; +import java.util.ArrayList; +import java.util.List; +import java.util.concurrent.TimeUnit; + +/** + * This class encapsulates the logic to generate the axis tick mark and axis tick label data for rendering the axis + * ticks for date axes + */ +public class AxisTickCalculatorInstant extends AxisTickCalculator { + + private static final long MILLIS_SCALE = TimeUnit.MILLISECONDS.toMillis(1L); + private static final long SEC_SCALE = TimeUnit.SECONDS.toMillis(1L); + private static final long MIN_SCALE = TimeUnit.MINUTES.toMillis(1L); + private static final long HOUR_SCALE = TimeUnit.HOURS.toMillis(1L); + private static final long DAY_SCALE = TimeUnit.DAYS.toMillis(1L); + private static final long MONTH_SCALE = TimeUnit.DAYS.toMillis(1L) * 30; + private static final long YEAR_SCALE = TimeUnit.DAYS.toMillis(1L) * 365; + + private static final List timeSpans = new ArrayList<>(); + + static { + timeSpans.add(new TimeSpan(MILLIS_SCALE, 1, "ss.SSS")); + timeSpans.add(new TimeSpan(MILLIS_SCALE, 2, "ss.SSS")); + timeSpans.add(new TimeSpan(MILLIS_SCALE, 5, "ss.SSS")); + timeSpans.add(new TimeSpan(MILLIS_SCALE, 10, "ss.SSS")); + timeSpans.add(new TimeSpan(MILLIS_SCALE, 50, "ss.SS")); + timeSpans.add(new TimeSpan(MILLIS_SCALE, 100, "ss.SS")); + timeSpans.add(new TimeSpan(MILLIS_SCALE, 200, "ss.SS")); + timeSpans.add(new TimeSpan(MILLIS_SCALE, 500, "ss.SS")); + + timeSpans.add(new TimeSpan(SEC_SCALE, 1, "ss.SS")); + timeSpans.add(new TimeSpan(SEC_SCALE, 2, "ss.S")); + timeSpans.add(new TimeSpan(SEC_SCALE, 5, "ss.S")); + timeSpans.add(new TimeSpan(SEC_SCALE, 10, "HH:mm:ss")); + timeSpans.add(new TimeSpan(SEC_SCALE, 15, "HH:mm:ss")); + timeSpans.add(new TimeSpan(SEC_SCALE, 20, "HH:mm:ss")); + timeSpans.add(new TimeSpan(SEC_SCALE, 30, "HH:mm:ss")); + + timeSpans.add(new TimeSpan(MIN_SCALE, 1, "HH:mm:ss")); + timeSpans.add(new TimeSpan(MIN_SCALE, 2, "HH:mm:ss")); + timeSpans.add(new TimeSpan(MIN_SCALE, 5, "HH:mm:ss")); + timeSpans.add(new TimeSpan(MIN_SCALE, 10, "HH:mm")); + timeSpans.add(new TimeSpan(MIN_SCALE, 15, "HH:mm")); + timeSpans.add(new TimeSpan(MIN_SCALE, 20, "HH:mm")); + timeSpans.add(new TimeSpan(MIN_SCALE, 30, "HH:mm")); + + timeSpans.add(new TimeSpan(HOUR_SCALE, 1, "HH:mm")); + timeSpans.add(new TimeSpan(HOUR_SCALE, 2, "HH:mm")); + timeSpans.add(new TimeSpan(HOUR_SCALE, 4, "HH:mm")); + timeSpans.add(new TimeSpan(HOUR_SCALE, 8, "HH:mm")); + timeSpans.add(new TimeSpan(HOUR_SCALE, 12, "HH:mm")); + + timeSpans.add(new TimeSpan(DAY_SCALE, 1, "EEE HH:mm")); + timeSpans.add(new TimeSpan(DAY_SCALE, 2, "EEE HH:mm")); + timeSpans.add(new TimeSpan(DAY_SCALE, 3, "EEE HH:mm")); + timeSpans.add(new TimeSpan(DAY_SCALE, 5, "MM-dd")); + timeSpans.add(new TimeSpan(DAY_SCALE, 10, "MM-dd")); + timeSpans.add(new TimeSpan(DAY_SCALE, 15, "MM-dd")); + + timeSpans.add(new TimeSpan(MONTH_SCALE, 1, "MM-dd")); + timeSpans.add(new TimeSpan(MONTH_SCALE, 2, "MM-dd")); + timeSpans.add(new TimeSpan(MONTH_SCALE, 3, "MM-dd")); + timeSpans.add(new TimeSpan(MONTH_SCALE, 4, "MM-dd")); + timeSpans.add(new TimeSpan(MONTH_SCALE, 6, "yyyy-MM")); + + timeSpans.add(new TimeSpan(YEAR_SCALE, 1, "yyyy-MM")); + timeSpans.add(new TimeSpan(YEAR_SCALE, 2, "yyyy-MM")); + timeSpans.add(new TimeSpan(YEAR_SCALE, 5, "yyyy")); + timeSpans.add(new TimeSpan(YEAR_SCALE, 10, "yyyy")); + timeSpans.add(new TimeSpan(YEAR_SCALE, 20, "yyyy")); + timeSpans.add(new TimeSpan(YEAR_SCALE, 100, "yyyy")); + timeSpans.add(new TimeSpan(YEAR_SCALE, 500, "yyyy")); + timeSpans.add(new TimeSpan(YEAR_SCALE, 1000, "yyyy")); + } + + public AxisTickCalculatorInstant(Direction axisDirection, double workingSpace, double minValue, double maxValue, AxesChartStyler styler) { + super(axisDirection, workingSpace, minValue, maxValue, styler); + calculate(); + } + + private void calculate() { + double tickSpace = styler.getPlotContentSize() * workingSpace; + if (tickSpace < styler.getXAxisTickMarkSpacingHint()) { + return; + } + double margin = (workingSpace - tickSpace) / 2.0; + long span = (long) Math.abs(maxValue - minValue); + int tickSpacingHint = styler.getXAxisTickMarkSpacingHint(); + int gridStepInChartSpace; + long gridStepHint = (long) (span / tickSpace * tickSpacingHint); + int index = 0; + for (int i = 0; i < timeSpans.size() - 1; i++) { + if (span < ((timeSpans.get(i).getUnitAmount() * timeSpans.get(i).getMagnitude() + timeSpans.get(i + 1).getUnitAmount() * timeSpans.get(i + 1).getMagnitude()) / 2.0)) { + index = i; + break; + } + } + String datePattern = timeSpans.get(index).getDatePattern(); + for (int i = index - 1; i > 0; i--) { + if (gridStepHint > timeSpans.get(i).getUnitAmount() * timeSpans.get(i).getMagnitude()) { + index = i; + break; + } + } + index--; + do { + tickLabels.clear(); + tickLocations.clear(); + double gridStep = timeSpans.get(++index).getUnitAmount() * timeSpans.get(index).getMagnitude(); // in time units (ms) + gridStepInChartSpace = (int) (gridStep / span * tickSpace); + double firstPosition = getFirstPosition(gridStep); + if (styler.getDatePattern() != null) { + datePattern = styler.getDatePattern(); + } + DateTimeFormatter dateTimeFormatter = DateTimeFormatter.ofPattern(datePattern) + .withZone(styler.getZoneId()); + for (double value = firstPosition; value <= maxValue + 2 * gridStep; value = value + gridStep) { + long l = Math.round(value); + tickLabels.add(dateTimeFormatter.format(Instant.ofEpochMilli(l))); + double tickLabelPosition = margin + ((value - minValue) / (maxValue - minValue) * tickSpace); + tickLocations.add(tickLabelPosition); + } + } while (!willLabelsFitInTickSpaceHint(tickLabels, gridStepInChartSpace)); + } + + static class TimeSpan { + + private final long unitAmount; + private final int magnitude; + private final String datePattern; + + TimeSpan(long unitAmount, int magnitude, String datePattern) { + this.unitAmount = unitAmount; + this.magnitude = magnitude; + this.datePattern = datePattern; + } + + long getUnitAmount() { + return unitAmount; + } + + int getMagnitude() { + return magnitude; + } + + String getDatePattern() { + return datePattern; + } + + @Override + public String toString() { + return "TimeSpan [unitAmount=" + unitAmount + ", magnitude=" + magnitude + ", datePattern=" + datePattern + "]"; + } + } +} diff --git a/chart/src/main/java/org/xbib/graphics/chart/axis/AxisTickCalculatorLogarithmic.java b/chart/src/main/java/org/xbib/graphics/chart/axis/AxisTickCalculatorLogarithmic.java new file mode 100644 index 0000000..ae97bf4 --- /dev/null +++ b/chart/src/main/java/org/xbib/graphics/chart/axis/AxisTickCalculatorLogarithmic.java @@ -0,0 +1,62 @@ +package org.xbib.graphics.chart.axis; + +import org.xbib.graphics.chart.formatter.NumberLogFormatter; +import org.xbib.graphics.chart.style.AxesChartStyler; + +import java.math.BigDecimal; + +/** + * This class encapsulates the logic to generate the axis tick mark and axis tick label data for rendering the axis + * ticks for logarithmic axes + */ +public class AxisTickCalculatorLogarithmic extends AxisTickCalculator { + + private final NumberLogFormatter numberLogFormatter; + + public AxisTickCalculatorLogarithmic(Direction axisDirection, double workingSpace, double minValue, double maxValue, AxesChartStyler styler) { + super(axisDirection, workingSpace, minValue, maxValue, styler); + numberLogFormatter = new NumberLogFormatter(styler, axisDirection); + axisFormat = numberLogFormatter; + calculate(); + } + + private void calculate() { + if (minValue == maxValue) { + tickLabels.add(numberLogFormatter.format(BigDecimal.valueOf(maxValue))); + tickLocations.add(workingSpace / 2.0); + return; + } + double tickSpace = styler.getPlotContentSize() * workingSpace; + if (tickSpace < styler.getXAxisTickMarkSpacingHint()) { + return; + } + double margin = (workingSpace - tickSpace) / 2.0; + int logMin = (int) Math.floor(Math.log10(minValue)); + int logMax = (int) Math.ceil(Math.log10(maxValue)); + double firstPosition = pow(logMin); + double tickStep = pow(logMin - 1); + for (int i = logMin; i <= logMax; i++) { + for (double j = firstPosition; j <= pow(i) + .00000001; j = j + tickStep) { + if (j < minValue - tickStep) { + continue; + } + if (j > maxValue + tickStep) { + break; + } + if (Math.abs(Math.log10(j) % 1) < 0.00000001) { + tickLabels.add(numberLogFormatter.format(j)); + } else { + tickLabels.add(null); + } + double tickLabelPosition = (int) (margin + (Math.log10(j) - Math.log10(minValue)) / (Math.log10(maxValue) - Math.log10(minValue)) * tickSpace); + tickLocations.add(tickLabelPosition); + } + tickStep = tickStep * pow(1); + firstPosition = tickStep + pow(i); + } + } + + private static double pow(int exponent) { + return exponent > 0 ? Math.pow(10, exponent) : 1.0 / Math.pow(10, -1 * exponent); + } +} diff --git a/chart/src/main/java/org/xbib/graphics/chart/axis/AxisTickCalculatorNumber.java b/chart/src/main/java/org/xbib/graphics/chart/axis/AxisTickCalculatorNumber.java new file mode 100644 index 0000000..dbd95b3 --- /dev/null +++ b/chart/src/main/java/org/xbib/graphics/chart/axis/AxisTickCalculatorNumber.java @@ -0,0 +1,89 @@ +package org.xbib.graphics.chart.axis; + +import org.xbib.graphics.chart.formatter.NumberFormatter; +import org.xbib.graphics.chart.style.AxesChartStyler; + +import java.math.BigDecimal; +import java.math.RoundingMode; + +/** + * This class encapsulates the logic to generate the axis tick mark and axis tick label data for rendering the axis + * ticks for decimal axes + */ +public class AxisTickCalculatorNumber extends AxisTickCalculator { + + private final NumberFormatter numberFormatter; + + public AxisTickCalculatorNumber(Direction axisDirection, double workingSpace, double minValue, double maxValue, AxesChartStyler styler) { + super(axisDirection, workingSpace, minValue, maxValue, styler); + numberFormatter = new NumberFormatter(styler, axisDirection, minValue, maxValue); + axisFormat = numberFormatter; + calculate(); + } + + private void calculate() { + if (minValue == maxValue) { + tickLabels.add(numberFormatter.format(BigDecimal.valueOf(maxValue))); + tickLocations.add(workingSpace / 2.0); + return; + } + double tickSpace = styler.getPlotContentSize() * workingSpace; + if (tickSpace < styler.getXAxisTickMarkSpacingHint()) { + return; + } + double margin = (workingSpace - tickSpace) / 2.0; + double span = Math.abs(Math.min((maxValue - minValue), Double.MAX_VALUE - 1)); + int tickSpacingHint = (axisDirection == Direction.X ? styler.getXAxisTickMarkSpacingHint() : styler.getYAxisTickMarkSpacingHint()) - 5; + if (axisDirection == Direction.Y && tickSpace < 160) { + tickSpacingHint = 25 - 5; + } + int gridStepInChartSpace; + do { + tickLabels.clear(); + tickLocations.clear(); + tickSpacingHint += 5; + double significand = span / tickSpace * tickSpacingHint; + int exponent = 0; + if (significand == 0) { + exponent = 1; + } else if (significand < 1) { + while (significand < 1) { + significand *= 10.0; + exponent--; + } + } else { + while (significand >= 10 || significand == Double.NEGATIVE_INFINITY) { + significand /= 10.0; + exponent++; + } + } + double gridStep; + if (significand > 7.5) { + gridStep = 10.0 * pow(exponent); + } else if (significand > 3.5) { + gridStep = 5.0 * pow(exponent); + } else if (significand > 1.5) { + gridStep = 2.0 * pow(exponent); + } else { + gridStep = pow(exponent); + } + gridStepInChartSpace = (int) (gridStep / span * tickSpace); + BigDecimal gridStepBigDecimal = BigDecimal.valueOf(gridStep); + BigDecimal cleanedGridStep = gridStepBigDecimal.setScale(10, RoundingMode.HALF_UP).stripTrailingZeros(); + BigDecimal firstPosition = BigDecimal.valueOf(getFirstPosition(cleanedGridStep.doubleValue())); + BigDecimal cleanedFirstPosition = firstPosition.setScale(10, RoundingMode.HALF_UP).stripTrailingZeros(); + for (BigDecimal value = cleanedFirstPosition; + value.compareTo(BigDecimal.valueOf(maxValue + 2 * cleanedGridStep.doubleValue())) < 0; + value = value.add(cleanedGridStep)) { + String tickLabel = numberFormatter.format(value); + tickLabels.add(tickLabel); + double tickLabelPosition = margin + ((value.doubleValue() - minValue) / (maxValue - minValue) * tickSpace); + tickLocations.add(tickLabelPosition); + } + } while (!willLabelsFitInTickSpaceHint(tickLabels, gridStepInChartSpace)); + } + + private static double pow(int exponent) { + return exponent > 0 ? Math.pow(10, exponent) : 1.0 / Math.pow(10, -1 * exponent); + } +} diff --git a/chart/src/main/java/org/xbib/graphics/chart/axis/AxisTickCalculatorOverride.java b/chart/src/main/java/org/xbib/graphics/chart/axis/AxisTickCalculatorOverride.java new file mode 100644 index 0000000..467f4f3 --- /dev/null +++ b/chart/src/main/java/org/xbib/graphics/chart/axis/AxisTickCalculatorOverride.java @@ -0,0 +1,79 @@ +package org.xbib.graphics.chart.axis; + +import org.xbib.graphics.chart.formatter.DateFormatter; +import org.xbib.graphics.chart.formatter.NumberFormatter; +import org.xbib.graphics.chart.formatter.StringFormatter; +import org.xbib.graphics.chart.style.AxesChartStyler; + +import java.util.Map; + +/** + * This class encapsulates the logic to generate the axis tick mark and axis tick label data for + * rendering the axis ticks for given values and labels. + */ +class AxisTickCalculatorOverride extends AxisTickCalculator { + + public AxisTickCalculatorOverride(Direction axisDirection, + double workingSpace, + double minValue, + double maxValue, + AxesChartStyler styler, + Map labelOverrideMap) { + super(axisDirection, workingSpace, minValue, maxValue, styler); + axisFormat = new NumberFormatter(styler, axisDirection, minValue, maxValue); + calculate(labelOverrideMap); + } + + public AxisTickCalculatorOverride(Direction axisDirection, + double workingSpace, + AxesChartStyler styler, + Map markMap, + DataType axisType, + int categoryCount) { + super(axisDirection, workingSpace, Double.NaN, Double.NaN, styler); + if (axisType == DataType.String) { + axisFormat = new StringFormatter(); + } else if (axisType == DataType.Number) { + axisFormat = new NumberFormatter(styler, axisDirection, minValue, maxValue); + } else if (axisType == DataType.Instant) { + axisFormat = new DateFormatter(styler.getDatePattern(), styler.getLocale()); + } + calculateForCategory(markMap, categoryCount); + } + + private void calculate(Map labelOverrideMap) { + if (minValue == maxValue) { + String label = labelOverrideMap.isEmpty() ? " " : labelOverrideMap.values().iterator().next().toString(); + tickLabels.add(label); + tickLocations.add(workingSpace / 2.0); + return; + } + double tickSpace = styler.getPlotContentSize() * workingSpace; + if (tickSpace < styler.getXAxisTickMarkSpacingHint()) { + return; + } + double margin = (workingSpace - tickSpace) / 2.0; + for (Map.Entry entry : labelOverrideMap.entrySet()) { + Object value = entry.getValue(); + String tickLabel = value == null ? " " : value.toString(); + tickLabels.add(tickLabel); + double tickLabelPosition = + margin + ((entry.getKey() - minValue) / (maxValue - minValue) * tickSpace); + tickLocations.add(tickLabelPosition); + } + } + + private void calculateForCategory(Map locationLabelMap, int categoryCount) { + double tickSpace = styler.getPlotContentSize() * workingSpace; + double margin = (workingSpace - tickSpace) / 2.0; + double gridStep = (tickSpace / categoryCount); + double firstPosition = gridStep / 2.0; + for (Map.Entry entry : locationLabelMap.entrySet()) { + Object value = entry.getValue(); + String tickLabel = value == null ? " " : value.toString(); + tickLabels.add(tickLabel); + double tickLabelPosition = margin + firstPosition + gridStep * entry.getKey(); + tickLocations.add(tickLabelPosition); + } + } +} diff --git a/chart/src/main/java/org/xbib/graphics/chart/axis/AxisTickLabels.java b/chart/src/main/java/org/xbib/graphics/chart/axis/AxisTickLabels.java new file mode 100644 index 0000000..352a49e --- /dev/null +++ b/chart/src/main/java/org/xbib/graphics/chart/axis/AxisTickLabels.java @@ -0,0 +1,168 @@ +package org.xbib.graphics.chart.axis; + +import org.xbib.graphics.chart.Chart; +import org.xbib.graphics.chart.ChartComponent; +import org.xbib.graphics.chart.series.AxesChartSeries; +import org.xbib.graphics.chart.style.AxesChartStyler; + +import java.awt.Graphics2D; +import java.awt.Shape; +import java.awt.font.FontRenderContext; +import java.awt.font.TextLayout; +import java.awt.geom.AffineTransform; +import java.awt.geom.Rectangle2D; +import java.util.HashMap; +import java.util.Map; + +/** + * Axis tick labels. + */ +public class AxisTickLabels implements ChartComponent { + + private final Chart chart; + + private final Direction direction; + + private final Axis yAxis; + + private Rectangle2D bounds; + + protected AxisTickLabels(Chart chart, Direction direction, Axis yAxis) { + this.chart = chart; + this.direction = direction; + this.yAxis = yAxis; + } + + @Override + public Rectangle2D getBounds() { + return bounds; + } + + @Override + public void paint(Graphics2D g) { + ST styler = chart.getStyler(); + g.setFont(styler.getAxisTickLabelsFont()); + g.setColor(styler.getAxisTickLabelsColor()); + if (direction == Direction.Y && chart.getStyler().isYAxisTicksVisible()) { + boolean onRight = styler.getYAxisGroupPosistion(yAxis.getYIndex()) == YAxisPosition.Right; + double xOffset; + if (onRight) { + xOffset = yAxis.getBounds().getX() + (styler.isYAxisTicksVisible() ? + styler.getAxisTickMarkLength() + styler.getAxisTickPadding() : 0); + } else { + double xWidth = yAxis.getAxisTitle().getBounds().getWidth(); + xOffset = yAxis.getAxisTitle().getBounds().getX() + xWidth; + } + double yOffset = yAxis.getBounds().getY(); + double height = yAxis.getBounds().getHeight(); + double maxTickLabelWidth = 0; + Map axisLabelTextLayouts = new HashMap<>(); + for (int i = 0; i < chart.getYAxis().getAxisTickCalculator().getTickLabels().size(); i++) { + String tickLabel = chart.getYAxis().getAxisTickCalculator().getTickLabels().get(i); + double tickLocation = chart.getYAxis().getAxisTickCalculator().getTickLocations().get(i); + double flippedTickLocation = yOffset + height - tickLocation; + if (tickLabel != null && !tickLabel.isEmpty() && + flippedTickLocation > yOffset && + flippedTickLocation < yOffset + height) { + FontRenderContext frc = g.getFontRenderContext(); + TextLayout axisLabelTextLayout = new TextLayout(tickLabel, styler.getAxisTickLabelsFont(), frc); + Rectangle2D tickLabelBounds = axisLabelTextLayout.getBounds(); + double boundWidth = tickLabelBounds.getWidth(); + if (boundWidth > maxTickLabelWidth) { + maxTickLabelWidth = boundWidth; + } + axisLabelTextLayouts.put(tickLocation, axisLabelTextLayout); + } + } + for (Map.Entry entry : axisLabelTextLayouts.entrySet()) { + Double tickLocation = entry.getKey(); + TextLayout axisLabelTextLayout = axisLabelTextLayouts.get(tickLocation); + Shape shape = axisLabelTextLayout.getOutline(null); + Rectangle2D tickLabelBounds = shape.getBounds(); + double flippedTickLocation = yOffset + height - tickLocation; + AffineTransform orig = g.getTransform(); + AffineTransform at = new AffineTransform(); + double boundWidth = tickLabelBounds.getWidth(); + double xPos; + switch (chart.getStyler().getYAxisLabelAlignment()) { + case Right: + xPos = xOffset + maxTickLabelWidth - boundWidth; + break; + case Centre: + xPos = xOffset + (maxTickLabelWidth - boundWidth) / 2; + break; + case Left: + default: + xPos = xOffset; + } + at.translate(xPos, flippedTickLocation + tickLabelBounds.getHeight() / 2.0); + g.transform(at); + g.fill(shape); + g.setTransform(orig); + } + bounds = new Rectangle2D.Double(xOffset, yOffset, maxTickLabelWidth, height); + } else if (direction == Direction.X && chart.getStyler().isXAxisTicksVisible()) { + double xOffset = chart.getXAxis().getBounds().getX(); + double yOffset = chart.getXAxis().getAxisTitle().getBounds().getY(); + double width = chart.getXAxis().getBounds().getWidth(); + double maxTickLabelHeight = 0; + int maxTickLabelY = 0; + for (int i = 0; i < chart.getXAxis().getAxisTickCalculator().getTickLabels().size(); i++) { + String tickLabel = chart.getXAxis().getAxisTickCalculator().getTickLabels().get(i); + double tickLocation = chart.getXAxis().getAxisTickCalculator().getTickLocations().get(i); + double shiftedTickLocation = xOffset + tickLocation; + if (tickLabel != null && !tickLabel.isEmpty() && + shiftedTickLocation > xOffset && + shiftedTickLocation < xOffset + width) { + FontRenderContext frc = g.getFontRenderContext(); + TextLayout textLayout = new TextLayout(tickLabel, chart.getStyler().getAxisTickLabelsFont(), frc); + AffineTransform rot = AffineTransform.getRotateInstance(-1 * Math.toRadians(chart.getStyler().getXAxisLabelRotation()), 0, 0); + Shape shape = textLayout.getOutline(rot); + Rectangle2D tickLabelBounds = shape.getBounds2D(); + if (tickLabelBounds.getBounds().height > maxTickLabelY) { + maxTickLabelY = tickLabelBounds.getBounds().height; + } + } + } + for (int i = 0; i < chart.getXAxis().getAxisTickCalculator().getTickLabels().size(); i++) { + String tickLabel = chart.getXAxis().getAxisTickCalculator().getTickLabels().get(i); + double tickLocation = chart.getXAxis().getAxisTickCalculator().getTickLocations().get(i); + double shiftedTickLocation = xOffset + tickLocation; + if (tickLabel != null && shiftedTickLocation > xOffset && shiftedTickLocation < xOffset + width) { + FontRenderContext frc = g.getFontRenderContext(); + TextLayout textLayout = new TextLayout(tickLabel, styler.getAxisTickLabelsFont(), frc); + AffineTransform rot = AffineTransform.getRotateInstance( + -1 * Math.toRadians(styler.getXAxisLabelRotation()), 0, 0); + Shape shape = textLayout.getOutline(rot); + Rectangle2D tickLabelBounds = shape.getBounds2D(); + AffineTransform orig = g.getTransform(); + AffineTransform at = new AffineTransform(); + double xPos; + switch (styler.getXAxisLabelAlignment()) { + case Left: + xPos = shiftedTickLocation; + break; + case Right: + xPos = shiftedTickLocation - tickLabelBounds.getWidth(); + break; + case Centre: + default: + xPos = shiftedTickLocation - tickLabelBounds.getWidth() / 2.0; + } + double shiftX = -1 * tickLabelBounds.getX() * Math.sin(Math.toRadians(chart.getStyler().getXAxisLabelRotation())); + double shiftY = -1 * (tickLabelBounds.getY() + tickLabelBounds.getHeight()); + at.translate(xPos + shiftX, yOffset + shiftY); + g.transform(at); + g.fill(shape); + g.setTransform(orig); + if (tickLabelBounds.getHeight() > maxTickLabelHeight) { + maxTickLabelHeight = tickLabelBounds.getHeight(); + } + } + } + bounds = new Rectangle2D.Double(xOffset, yOffset - maxTickLabelHeight, width, maxTickLabelHeight); + } else { + bounds = new Rectangle2D.Double(); + } + } +} diff --git a/chart/src/main/java/org/xbib/graphics/chart/axis/AxisTickMarks.java b/chart/src/main/java/org/xbib/graphics/chart/axis/AxisTickMarks.java new file mode 100644 index 0000000..d0ce02e --- /dev/null +++ b/chart/src/main/java/org/xbib/graphics/chart/axis/AxisTickMarks.java @@ -0,0 +1,101 @@ +package org.xbib.graphics.chart.axis; + +import org.xbib.graphics.chart.Chart; +import org.xbib.graphics.chart.ChartComponent; +import org.xbib.graphics.chart.series.AxesChartSeries; +import org.xbib.graphics.chart.style.AxesChartStyler; + +import java.awt.Graphics2D; +import java.awt.Shape; +import java.awt.geom.Line2D; +import java.awt.geom.Rectangle2D; + +/** + * Axis tick marks. This includes the little tick marks and the line that hugs the plot area. + */ +public class AxisTickMarks implements ChartComponent { + + private final Chart chart; + + private final Direction direction; + + private final Axis yAxis; + + private Rectangle2D bounds; + + protected AxisTickMarks(Chart chart, Direction direction, Axis yAxis) { + this.chart = chart; + this.direction = direction; + this.yAxis = yAxis; + } + + @Override + public Rectangle2D getBounds() { + return bounds; + } + + @Override + public void paint(Graphics2D g) { + ST styler = chart.getStyler(); + g.setColor(styler.getAxisTickMarksColor()); + g.setStroke(styler.getAxisTickMarksStroke()); + if (direction == Direction.Y && chart.getStyler().isYAxisTicksVisible()) { + int axisTickMarkLength = styler.getAxisTickMarkLength(); + boolean onRight = styler.getYAxisGroupPosistion(yAxis.getYIndex()) == YAxisPosition.Right; + Rectangle2D yAxisBounds = yAxis.getBounds(); + Rectangle2D axisTickLabelBounds = yAxis.getAxisTick().getAxisTickLabels().getBounds(); + double xOffset; + double lineXOffset; + if (onRight) { + xOffset = axisTickLabelBounds.getX() - styler.getAxisTickPadding() - axisTickMarkLength; + lineXOffset = xOffset; + } else { + xOffset = axisTickLabelBounds.getX() + axisTickLabelBounds.getWidth() + styler.getAxisTickPadding(); + lineXOffset = xOffset + axisTickMarkLength; + } + double yOffset = yAxisBounds.getY(); + bounds = new Rectangle2D.Double(xOffset, yOffset, styler.getAxisTickMarkLength(), yAxis.getBounds().getHeight()); + if (styler.isAxisTicksMarksVisible()) { + for (int i = 0; i < chart.getYAxis().getAxisTickCalculator().getTickLabels().size(); i++) { + double tickLocation = yAxis.getAxisTickCalculator().getTickLocations().get(i); + double flippedTickLocation = yOffset + yAxisBounds.getHeight() - tickLocation; + if (flippedTickLocation > bounds.getY() && + flippedTickLocation < bounds.getY() + bounds.getHeight()) { + Shape line = new Line2D.Double(xOffset, flippedTickLocation, xOffset + axisTickMarkLength, flippedTickLocation); + g.draw(line); + } + } + } + if (styler.isAxisTicksLineVisible()) { + Shape line = new Line2D.Double(lineXOffset, yOffset, lineXOffset, yOffset + yAxisBounds.getHeight()); + g.draw(line); + } + } else if (direction == Direction.X && styler.isXAxisTicksVisible()) { + int axisTickMarkLength = styler.getAxisTickMarkLength(); + double xOffset = chart.getXAxis().getBounds().getX(); + double yOffset = chart.getXAxis().getAxisTick().getAxisTickLabels().getBounds().getY() - styler.getAxisTickPadding(); + bounds = new Rectangle2D.Double(xOffset, yOffset - axisTickMarkLength, + chart.getXAxis().getBounds().getWidth(), axisTickMarkLength); + if (styler.isAxisTicksMarksVisible()) { + for (int i = 0; i < chart.getXAxis().getAxisTickCalculator().getTickLabels().size(); i++) { + double tickLocation = chart.getXAxis().getAxisTickCalculator().getTickLocations().get(i); + double shiftedTickLocation = xOffset + tickLocation; + if (shiftedTickLocation > bounds.getX() && + shiftedTickLocation < bounds.getX() + bounds.getWidth()) { + Shape line = new Line2D.Double(shiftedTickLocation, yOffset, xOffset + tickLocation, yOffset - chart.getStyler().getAxisTickMarkLength()); + g.draw(line); + } + } + } + if (styler.isAxisTicksLineVisible()) { + g.setStroke(styler.getAxisTickMarksStroke()); + g.drawLine((int) xOffset, + (int) (yOffset - axisTickMarkLength), + (int) (xOffset + chart.getXAxis().getBounds().getWidth()), + (int) (yOffset - axisTickMarkLength)); + } else { + bounds = new Rectangle2D.Double(); + } + } + } +} diff --git a/chart/src/main/java/org/xbib/graphics/chart/axis/AxisTitle.java b/chart/src/main/java/org/xbib/graphics/chart/axis/AxisTitle.java new file mode 100644 index 0000000..0010256 --- /dev/null +++ b/chart/src/main/java/org/xbib/graphics/chart/axis/AxisTitle.java @@ -0,0 +1,108 @@ +package org.xbib.graphics.chart.axis; + +import org.xbib.graphics.chart.Chart; +import org.xbib.graphics.chart.ChartComponent; +import org.xbib.graphics.chart.series.Series; +import org.xbib.graphics.chart.style.AxesChartStyler; + +import java.awt.Graphics2D; +import java.awt.Shape; +import java.awt.font.FontRenderContext; +import java.awt.font.TextLayout; +import java.awt.geom.AffineTransform; +import java.awt.geom.Rectangle2D; + +/** + * Axis title. + */ +public class AxisTitle implements ChartComponent { + + private final Chart chart; + private final Direction direction; + private final Axis yAxis; + private final int yIndex; + private Rectangle2D bounds; + + public AxisTitle(Chart chart, Direction direction, Axis yAxis, int yIndex) { + this.chart = chart; + this.direction = direction; + this.yAxis = yAxis; + this.yIndex = yIndex; + } + + @Override + public Rectangle2D getBounds() { + return bounds; + } + + @Override + public void paint(Graphics2D g) { + bounds = new Rectangle2D.Double(); + g.setColor(chart.getStyler().getChartFontColor()); + g.setFont(chart.getStyler().getAxisTitleFont()); + if (direction == Direction.Y) { + String yAxisTitle = chart.getYAxisGroupTitle(yIndex); + if (yAxisTitle != null && !yAxisTitle.trim().equalsIgnoreCase("") && + chart.getStyler().isYAxisTitleVisible()) { + FontRenderContext frc = g.getFontRenderContext(); + TextLayout nonRotatedTextLayout = new TextLayout(chart.getyYAxisTitle(), chart.getStyler().getAxisTitleFont(), frc); + Rectangle2D nonRotatedRectangle = nonRotatedTextLayout.getBounds(); + boolean onRight = chart.getStyler().getYAxisGroupPosistion(yAxis.getYIndex()) == YAxisPosition.Right; + int xOffset; + if (onRight) { + xOffset = (int) (yAxis.getAxisTick().getBounds().getX() + + yAxis.getAxisTick().getBounds().getWidth() + + nonRotatedRectangle.getHeight()); + } else { + xOffset = (int) (yAxis.getBounds().getX() + nonRotatedRectangle.getHeight()); + } + int yOffset = (int) ((yAxis.getBounds().getHeight() + nonRotatedRectangle.getWidth()) / 2.0 + + yAxis.getBounds().getY()); + AffineTransform rot = AffineTransform.getRotateInstance(-1 * Math.PI / 2, 0, 0); + Shape shape = nonRotatedTextLayout.getOutline(rot); + AffineTransform orig = g.getTransform(); + AffineTransform at = new AffineTransform(); + at.translate(xOffset, yOffset); + g.transform(at); + g.fill(shape); + g.setTransform(orig); + bounds = new Rectangle2D.Double(xOffset - nonRotatedRectangle.getHeight(), + yOffset - nonRotatedRectangle.getWidth(), + nonRotatedRectangle.getHeight() + chart.getStyler().getAxisTitlePadding(), + nonRotatedRectangle.getWidth()); + } else { + bounds = new Rectangle2D.Double(yAxis.getBounds().getX(), yAxis.getBounds().getY(),0, yAxis.getBounds().getHeight()); + } + } else { + + if (chart.getXAxisTitle() != null && + !chart.getXAxisTitle().trim().equalsIgnoreCase("") && + chart.getStyler().isXAxisTitleVisible()) { + + FontRenderContext frc = g.getFontRenderContext(); + TextLayout textLayout = new TextLayout(chart.getXAxisTitle(), chart.getStyler().getAxisTitleFont(), frc); + Rectangle2D rectangle = textLayout.getBounds(); + + double xOffset = chart.getXAxis().getBounds().getX() + + (chart.getXAxis().getBounds().getWidth() - rectangle.getWidth()) / 2.0; + double yOffset = chart.getXAxis().getBounds().getY() + + chart.getXAxis().getBounds().getHeight() + - rectangle.getHeight(); + + Shape shape = textLayout.getOutline(null); + AffineTransform orig = g.getTransform(); + AffineTransform at = new AffineTransform(); + at.translate((float) xOffset, (float) (yOffset - rectangle.getY())); + g.transform(at); + g.fill(shape); + g.setTransform(orig); + bounds = new Rectangle2D.Double(xOffset, yOffset - chart.getStyler().getAxisTitlePadding(), + rectangle.getWidth(), rectangle.getHeight() + chart.getStyler().getAxisTitlePadding()); + } else { + bounds = new Rectangle2D.Double(chart.getXAxis().getBounds().getX(), + chart.getXAxis().getBounds().getY() + chart.getXAxis().getBounds().getHeight(), + chart.getXAxis().getBounds().getWidth(), 0); + } + } + } +} diff --git a/chart/src/main/java/org/xbib/graphics/chart/axis/DataType.java b/chart/src/main/java/org/xbib/graphics/chart/axis/DataType.java new file mode 100644 index 0000000..e4b0bea --- /dev/null +++ b/chart/src/main/java/org/xbib/graphics/chart/axis/DataType.java @@ -0,0 +1,5 @@ +package org.xbib.graphics.chart.axis; + +public enum DataType { + Number, Instant, String; +} diff --git a/chart/src/main/java/org/xbib/graphics/chart/axis/Direction.java b/chart/src/main/java/org/xbib/graphics/chart/axis/Direction.java new file mode 100644 index 0000000..e886b47 --- /dev/null +++ b/chart/src/main/java/org/xbib/graphics/chart/axis/Direction.java @@ -0,0 +1,5 @@ +package org.xbib.graphics.chart.axis; + +public enum Direction { + X, Y +} diff --git a/chart/src/main/java/org/xbib/graphics/chart/axis/TextAlignment.java b/chart/src/main/java/org/xbib/graphics/chart/axis/TextAlignment.java new file mode 100644 index 0000000..8e3b8a1 --- /dev/null +++ b/chart/src/main/java/org/xbib/graphics/chart/axis/TextAlignment.java @@ -0,0 +1,5 @@ +package org.xbib.graphics.chart.axis; + +public enum TextAlignment { + Left, Centre, Right; +} diff --git a/chart/src/main/java/org/xbib/graphics/chart/axis/YAxisPosition.java b/chart/src/main/java/org/xbib/graphics/chart/axis/YAxisPosition.java new file mode 100644 index 0000000..1e9ed0d --- /dev/null +++ b/chart/src/main/java/org/xbib/graphics/chart/axis/YAxisPosition.java @@ -0,0 +1,5 @@ +package org.xbib.graphics.chart.axis; + +public enum YAxisPosition { + Left, Right +} diff --git a/chart/src/main/java/org/xbib/graphics/chart/bubble/BubbleChart.java b/chart/src/main/java/org/xbib/graphics/chart/bubble/BubbleChart.java new file mode 100644 index 0000000..0af3128 --- /dev/null +++ b/chart/src/main/java/org/xbib/graphics/chart/bubble/BubbleChart.java @@ -0,0 +1,204 @@ +package org.xbib.graphics.chart.bubble; + +import org.xbib.graphics.chart.axis.AxisPair; +import org.xbib.graphics.chart.Chart; +import org.xbib.graphics.chart.plot.AxesChartPlot; +import org.xbib.graphics.chart.plot.ContentPlot; +import org.xbib.graphics.chart.style.SeriesColorMarkerLineStyle; +import org.xbib.graphics.chart.style.SeriesColorMarkerLineStyleCycler; +import org.xbib.graphics.chart.theme.Theme; + +import java.awt.Graphics2D; +import java.awt.Shape; +import java.awt.geom.Ellipse2D; +import java.util.List; +import java.util.Map; + +public class BubbleChart extends Chart { + + public BubbleChart(int width, int height) { + super(width, height, new BubbleStyler()); + axisPair = new AxisPair<>(this); + plot = new BubblePlot<>(this); + legend = new BubbleLegend<>(this); + } + + public BubbleChart(int width, int height, Theme theme) { + this(width, height); + styler.setTheme(theme); + } + + public BubbleChart(BubbleChartBuilder chartBuilder) { + this(chartBuilder.getWidth(), chartBuilder.getHeight(), chartBuilder.getTheme()); + setTitle(chartBuilder.getTitle()); + setXAxisTitle(chartBuilder.xAxisTitle); + setYAxisTitle(chartBuilder.yAxisTitle); + } + + public BubbleSeries addSeries(String seriesName, + List xData, + List yData, + List bubbleData) { + sanityCheck(seriesName, xData, yData, bubbleData); + BubbleSeries series; + if (xData != null) { + if (xData.size() != yData.size()) { + throw new IllegalArgumentException("X and Y-Axis sizes are not the same"); + } + series = new BubbleSeries(seriesName, xData, yData, bubbleData); + } else { // generate xData + series = new BubbleSeries(seriesName, getGeneratedData(yData.size()), yData, bubbleData); + } + seriesMap.put(seriesName, series); + return series; + } + + private void sanityCheck(String seriesName, List xData, + List yData, + List bubbleData) { + if (seriesMap.containsKey(seriesName)) { + throw new IllegalArgumentException("Series name >" + + seriesName + + "< has already been used. Use unique names for each series"); + } + if (yData == null) { + throw new IllegalArgumentException("Y-Axis data cannot be null >" + seriesName); + } + if (yData.size() == 0) { + throw new IllegalArgumentException("Y-Axis data cannot be empty >" + seriesName); + } + if (bubbleData == null) { + throw new IllegalArgumentException("Bubble data cannot be null >" + seriesName); + } + if (bubbleData.size() == 0) { + throw new IllegalArgumentException("Bubble data cannot be empty >" + seriesName); + } + if (xData != null && xData.size() == 0) { + throw new IllegalArgumentException("X-Axis data cannot be empty >" + seriesName); + } + if (bubbleData.size() != yData.size()) { + throw new IllegalArgumentException("Bubble Data and Y-Axis sizes are not the same >" + seriesName); + } + } + + @Override + public void paint(Graphics2D g, int width, int height) { + setWidth(width); + setHeight(height); + for (BubbleSeries bubbleSeries : getSeriesMap().values()) { + BubbleSeriesRenderStyle seriesType = + bubbleSeries.getBubbleSeriesRenderStyle(); + if (seriesType == null) { + bubbleSeries.setBubbleSeriesRenderStyle(getStyler().getDefaultSeriesRenderStyle()); + } + } + setSeriesStyles(); + paintBackground(g); + axisPair.paint(g); + plot.paint(g); + chartTitle.paint(g); + legend.paint(g); + } + + private void setSeriesStyles() { + SeriesColorMarkerLineStyleCycler seriesColorMarkerLineStyleCycler = + new SeriesColorMarkerLineStyleCycler( + getStyler().getSeriesColors(), + getStyler().getSeriesMarkers(), + getStyler().getSeriesLines()); + for (BubbleSeries series : getSeriesMap().values()) { + SeriesColorMarkerLineStyle seriesColorMarkerLineStyle = + seriesColorMarkerLineStyleCycler.getNextSeriesColorMarkerLineStyle(); + if (series.getLineStyle() == null) { + series.setLineStyle(seriesColorMarkerLineStyle.getStroke()); + } + if (series.getLineColor() == null) { + series.setLineColor(seriesColorMarkerLineStyle.getColor()); + } + if (series.getFillColor() == null) { + series.setFillColor(seriesColorMarkerLineStyle.getColor()); + } + } + } + + private static class BubblePlot extends AxesChartPlot { + + private BubblePlot(Chart chart) { + super(chart); + this.contentPlot = new BubbleContentPlot<>(chart); + } + } + + private static class BubbleContentPlot extends ContentPlot { + + private final ST stylerBubble; + + private BubbleContentPlot(Chart chart) { + super(chart); + stylerBubble = chart.getStyler(); + } + + @Override + public void doPaint(Graphics2D g) { + double xTickSpace = stylerBubble.getPlotContentSize() * getBounds().getWidth(); + double xLeftMargin = ((int) getBounds().getWidth() - xTickSpace) / 2.0; + double yTickSpace = stylerBubble.getPlotContentSize() * getBounds().getHeight(); + double yTopMargin = ((int) getBounds().getHeight() - yTickSpace) / 2.0; + double xMin = chart.getXAxis().getMin(); + double xMax = chart.getXAxis().getMax(); + if (stylerBubble.isXAxisLogarithmic()) { + xMin = Math.log10(xMin); + xMax = Math.log10(xMax); + } + Map map = chart.getSeriesMap(); + for (S series : map.values()) { + if (!series.isEnabled()) { + continue; + } + double yMin = chart.getYAxis(series.getYAxisGroup()).getMin(); + double yMax = chart.getYAxis(series.getYAxisGroup()).getMax(); + if (stylerBubble.isYAxisLogarithmic()) { + yMin = Math.log10(yMin); + yMax = Math.log10(yMax); + } + for (int i = 0; i < series.getXData().size(); i++) { + Double x = (Double) series.getXData().get(i); + if (stylerBubble.isXAxisLogarithmic()) { + x = Math.log10(x); + } + if (Double.isNaN((Double)series.getYData().get(i))) { + continue; + } + Double yOrig = (Double) series.getYData().get(i); + double y; + if (stylerBubble.isYAxisLogarithmic()) { + y = Math.log10(yOrig); + } else { + y = yOrig; + } + double xTransform = xLeftMargin + ((x - xMin) / (xMax - xMin) * xTickSpace); + double yTransform = + getBounds().getHeight() - (yTopMargin + (y - yMin) / (yMax - yMin) * yTickSpace); + if (Math.abs(xMax - xMin) / 5 == 0.0) { + xTransform = getBounds().getWidth() / 2.0; + } + if (Math.abs(yMax - yMin) / 5 == 0.0) { + yTransform = getBounds().getHeight() / 2.0; + } + double xOffset = getBounds().getX() + xTransform; + double yOffset = getBounds().getY() + yTransform; + if (series.getExtraValues() != null) { + Double bubbleSize = (Double) series.getExtraValues().get(i); + Shape bubble; + bubble = new Ellipse2D.Double(xOffset - bubbleSize / 2, yOffset - bubbleSize / 2, bubbleSize, bubbleSize); + g.setColor(series.getFillColor()); + g.fill(bubble); + g.setColor(series.getLineColor()); + g.setStroke(series.getLineStyle()); + g.draw(bubble); + } + } + } + } + } +} diff --git a/chart/src/main/java/org/xbib/graphics/chart/bubble/BubbleChartBuilder.java b/chart/src/main/java/org/xbib/graphics/chart/bubble/BubbleChartBuilder.java new file mode 100644 index 0000000..0cee777 --- /dev/null +++ b/chart/src/main/java/org/xbib/graphics/chart/bubble/BubbleChartBuilder.java @@ -0,0 +1,27 @@ +package org.xbib.graphics.chart.bubble; + +import org.xbib.graphics.chart.ChartBuilder; + +public class BubbleChartBuilder extends ChartBuilder { + + String xAxisTitle = ""; + String yAxisTitle = ""; + + public BubbleChartBuilder() { + } + + public BubbleChartBuilder xAxisTitle(String xAxisTitle) { + this.xAxisTitle = xAxisTitle; + return this; + } + + public BubbleChartBuilder yAxisTitle(String yAxisTitle) { + this.yAxisTitle = yAxisTitle; + return this; + } + + @Override + public BubbleChart build() { + return new BubbleChart(this); + } +} diff --git a/chart/src/main/java/org/xbib/graphics/chart/bubble/BubbleLegend.java b/chart/src/main/java/org/xbib/graphics/chart/bubble/BubbleLegend.java new file mode 100644 index 0000000..2074f44 --- /dev/null +++ b/chart/src/main/java/org/xbib/graphics/chart/bubble/BubbleLegend.java @@ -0,0 +1,68 @@ +package org.xbib.graphics.chart.bubble; + +import org.xbib.graphics.chart.Chart; +import org.xbib.graphics.chart.legend.AbstractLegend; +import org.xbib.graphics.chart.series.AxesChartSeries; +import org.xbib.graphics.chart.style.AxesChartStyler; +import org.xbib.graphics.chart.legend.LegendLayout; +import org.xbib.graphics.chart.legend.LegendRenderType; + +import java.awt.Graphics2D; +import java.awt.RenderingHints; +import java.awt.Shape; +import java.awt.geom.Ellipse2D; +import java.awt.geom.Rectangle2D; +import java.util.Map; + +public class BubbleLegend extends AbstractLegend { + + private final ST axesChartStyler; + + public BubbleLegend(Chart chart) { + super(chart); + axesChartStyler = chart.getStyler(); + } + + @Override + public void doPaint(Graphics2D g) { + double startx = xOffset + chart.getStyler().getLegendPadding(); + double starty = yOffset + chart.getStyler().getLegendPadding(); + Object oldHint = g.getRenderingHint(RenderingHints.KEY_ANTIALIASING); + g.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON); + Map map = chart.getSeriesMap(); + for (S series : map.values()) { + if (series.isNotShownInLegend()) { + continue; + } + if (!series.isEnabled()) { + continue; + } + Map seriesTextBounds = getSeriesTextBounds(series); + float legendEntryHeight = getLegendEntryHeight(seriesTextBounds, BOX_SIZE); + Shape rectSmall = new Ellipse2D.Double(startx, starty, BOX_SIZE, BOX_SIZE); + g.setColor(series.getFillColor()); + g.fill(rectSmall); + g.setStroke(series.getLineStyle()); + g.setColor(series.getLineColor()); + g.draw(rectSmall); + final double x = startx + BOX_SIZE + chart.getStyler().getLegendPadding(); + paintSeriesText(g, seriesTextBounds, BOX_SIZE, x, starty); + if (chart.getStyler().getLegendLayout() == LegendLayout.Vertical) { + starty += legendEntryHeight + chart.getStyler().getLegendPadding(); + } else { + int markerWidth = BOX_SIZE; + if (series.getLegendRenderType() == LegendRenderType.Line) { + markerWidth = chart.getStyler().getLegendSeriesLineLength(); + } + float legendEntryWidth = getLegendEntryWidth(seriesTextBounds, markerWidth); + startx += legendEntryWidth + chart.getStyler().getLegendPadding(); + } + } + g.setRenderingHint(RenderingHints.KEY_ANTIALIASING, oldHint); + } + + @Override + public double getSeriesLegendRenderGraphicHeight(S series) { + return series.getLegendRenderType() == LegendRenderType.Box ? BOX_SIZE : axesChartStyler.getMarkerSize(); + } +} diff --git a/chart/src/main/java/org/xbib/graphics/chart/bubble/BubbleSeries.java b/chart/src/main/java/org/xbib/graphics/chart/bubble/BubbleSeries.java new file mode 100644 index 0000000..808a476 --- /dev/null +++ b/chart/src/main/java/org/xbib/graphics/chart/bubble/BubbleSeries.java @@ -0,0 +1,33 @@ +package org.xbib.graphics.chart.bubble; + +import org.xbib.graphics.chart.axis.DataType; +import org.xbib.graphics.chart.series.NoMarkersSeries; +import org.xbib.graphics.chart.legend.LegendRenderType; + +import java.util.List; + +/** + * A Series containing X, Y and bubble size data to be plotted on a Chart. + */ +public class BubbleSeries extends NoMarkersSeries { + + private BubbleSeriesRenderStyle bubbleSeriesRenderStyle = null; + + public BubbleSeries(String name, List xData, List yData, List bubbleSizes) { + super(name, xData, yData, bubbleSizes, DataType.Number); + } + + public BubbleSeriesRenderStyle getBubbleSeriesRenderStyle() { + return bubbleSeriesRenderStyle; + } + + public void setBubbleSeriesRenderStyle(BubbleSeriesRenderStyle bubbleSeriesRenderStyle) { + this.bubbleSeriesRenderStyle = bubbleSeriesRenderStyle; + } + + @Override + public LegendRenderType getLegendRenderType() { + return bubbleSeriesRenderStyle.getLegendRenderType(); + } + +} diff --git a/chart/src/main/java/org/xbib/graphics/chart/bubble/BubbleSeriesRenderStyle.java b/chart/src/main/java/org/xbib/graphics/chart/bubble/BubbleSeriesRenderStyle.java new file mode 100644 index 0000000..359d4ff --- /dev/null +++ b/chart/src/main/java/org/xbib/graphics/chart/bubble/BubbleSeriesRenderStyle.java @@ -0,0 +1,19 @@ +package org.xbib.graphics.chart.bubble; + +import org.xbib.graphics.chart.legend.LegendRenderable; +import org.xbib.graphics.chart.legend.LegendRenderType; + +public enum BubbleSeriesRenderStyle implements LegendRenderable { + Round(LegendRenderType.Box); + + private final LegendRenderType legendRenderType; + + BubbleSeriesRenderStyle(LegendRenderType legendRenderType) { + this.legendRenderType = legendRenderType; + } + + @Override + public LegendRenderType getLegendRenderType() { + return legendRenderType; + } +} diff --git a/chart/src/main/java/org/xbib/graphics/chart/bubble/BubbleStyler.java b/chart/src/main/java/org/xbib/graphics/chart/bubble/BubbleStyler.java new file mode 100644 index 0000000..dbd4bd3 --- /dev/null +++ b/chart/src/main/java/org/xbib/graphics/chart/bubble/BubbleStyler.java @@ -0,0 +1,44 @@ +package org.xbib.graphics.chart.bubble; + +import org.xbib.graphics.chart.style.AxesChartStyler; +import org.xbib.graphics.chart.theme.Theme; + +public class BubbleStyler extends AxesChartStyler { + + private BubbleSeriesRenderStyle bubbleChartSeriesRenderStyle; + + public BubbleStyler() { + this.setAllStyles(); + super.setAllStyles(); + } + + @Override + protected void setAllStyles() { + bubbleChartSeriesRenderStyle = BubbleSeriesRenderStyle.Round; // set default to Round + } + + public BubbleSeriesRenderStyle getDefaultSeriesRenderStyle() { + return bubbleChartSeriesRenderStyle; + } + + /** + * Sets the default series render style for the chart (Round is the only one for now) You can + * override the series render style individually on each Series object. + * + * @param bubbleChartSeriesRenderStyle render style + */ + public BubbleStyler setDefaultSeriesRenderStyle(BubbleSeriesRenderStyle bubbleChartSeriesRenderStyle) { + this.bubbleChartSeriesRenderStyle = bubbleChartSeriesRenderStyle; + return this; + } + + /** + * Set the theme the styler should use + * + * @param theme theme + */ + public void setTheme(Theme theme) { + this.theme = theme; + super.setAllStyles(); + } +} diff --git a/chart/src/main/java/org/xbib/graphics/chart/category/CategoryChart.java b/chart/src/main/java/org/xbib/graphics/chart/category/CategoryChart.java new file mode 100644 index 0000000..113db2d --- /dev/null +++ b/chart/src/main/java/org/xbib/graphics/chart/category/CategoryChart.java @@ -0,0 +1,706 @@ +package org.xbib.graphics.chart.category; + +import org.xbib.graphics.chart.axis.DataType; +import org.xbib.graphics.chart.axis.Axis; +import org.xbib.graphics.chart.axis.AxisPair; +import org.xbib.graphics.chart.Chart; +import org.xbib.graphics.chart.legend.MarkerLegend; +import org.xbib.graphics.chart.plot.AxesChartPlot; +import org.xbib.graphics.chart.plot.ContentPlot; +import org.xbib.graphics.chart.style.SeriesColorMarkerLineStyle; +import org.xbib.graphics.chart.style.SeriesColorMarkerLineStyleCycler; +import org.xbib.graphics.chart.theme.Theme; + +import java.awt.Graphics2D; +import java.awt.Shape; +import java.awt.geom.Line2D; +import java.awt.geom.Path2D; +import java.awt.geom.Point2D; +import java.time.Instant; +import java.util.ArrayList; +import java.util.Collection; +import java.util.Collections; +import java.util.Iterator; +import java.util.List; +import java.util.Map; + +import static org.xbib.graphics.chart.category.CategorySeriesRenderStyle.SteppedBar; + +public class CategoryChart extends Chart { + + public CategoryChart(int width, int height) { + super(width, height, new CategoryStyler()); + axisPair = new AxisPair<>(this); + plot = new CategoryPlot<>(this); + legend = new MarkerLegend<>(this); + } + + public CategoryChart(int width, int height, Theme theme) { + this(width, height); + styler.setTheme(theme); + } + + public CategoryChart(CategoryChartBuilder chartBuilder) { + this(chartBuilder.getWidth(), chartBuilder.getHeight(), chartBuilder.getTheme()); + setTitle(chartBuilder.getTitle()); + setXAxisTitle(chartBuilder.getxAxisTitle()); + setYAxisTitle(chartBuilder.getyAxisTitle()); + } + + /** + * Add a series for a Category type chart using using double arrays + * + * @param seriesName series name + * @param xData the X-Axis data + * @param yData the Y-Axis data + * @return A Series object that you can set properties on + */ + public CategorySeries addSeries(String seriesName, double[] xData, double[] yData) { + return addSeries(seriesName, xData, yData, null); + } + + /** + * Add a series for a Category type chart using using double arrays with error bars + * + * @param seriesName series name + * @param xData the X-Axis data + * @param yData the Y-Axis data + * @param errorBars the error bar data + * @return A Series object that you can set properties on + */ + public CategorySeries addSeries(String seriesName, double[] xData, double[] yData, double[] errorBars) { + return addSeries(seriesName, listFromDoubleArray(xData), listFromDoubleArray(yData), + listFromDoubleArray(errorBars)); + } + + /** + * Add a series for a X-Y type chart using using int arrays + * + * @param seriesName series name + * @param xData the X-Axis data + * @param yData the Y-Axis data + * @return A Series object that you can set properties on + */ + public CategorySeries addSeries(String seriesName, int[] xData, int[] yData) { + return addSeries(seriesName, xData, yData, null); + } + + /** + * Add a series for a X-Y type chart using using int arrays with error bars + * + * @param seriesName series name + * @param xData the X-Axis data + * @param yData the Y-Axis data + * @param errorBars the error bar data + * @return A Series object that you can set properties on + */ + public CategorySeries addSeries(String seriesName, int[] xData, int[] yData, int[] errorBars) { + return addSeries(seriesName, listFromIntArray(xData), listFromIntArray(yData), + listFromIntArray(errorBars)); + } + + /** + * Add a series for a Category type chart using Lists + * + * @param seriesName series name + * @param xData the X-Axis data + * @param yData the Y-Axis data + * @return A Series object that you can set properties on + */ + public CategorySeries addSeries(String seriesName, List xData, List yData) { + return addSeries(seriesName, xData, yData, null); + } + + /** + * Add a series for a Category type chart using Lists with error bars + * + * @param seriesName series name + * @param xData the X-Axis data + * @param yData the Y-Axis data + * @param errorBars the error bar data + * @return A Series object that you can set properties on + */ + public CategorySeries addSeries(String seriesName, + List xData, + List yData, + List errorBars) { + sanityCheck(seriesName, xData, yData, errorBars); + CategorySeries series; + if (xData != null) { + if (xData.size() != yData.size()) { + throw new IllegalArgumentException("X and Y-Axis sizes are not the same"); + } + series = new CategorySeries(seriesName, xData, yData, errorBars, getDataType(xData)); + } else { + series = new CategorySeries(seriesName, getGeneratedData(yData.size()), yData, errorBars, DataType.String); + } + seriesMap.put(seriesName, series); + return series; + } + + private void sanityCheck(String seriesName, List xData, List yData, + List errorBars) { + if (seriesMap.containsKey(seriesName)) { + throw new IllegalArgumentException("Series name >" + seriesName + "< has already been used"); + } + if (yData == null) { + throw new IllegalArgumentException("Y-Axis data cannot be null"); + } + if (yData.size() == 0) { + throw new IllegalArgumentException("Y-Axis data cannot be empty"); + } + if (xData != null && xData.size() == 0) { + throw new IllegalArgumentException("X-Axis data cannot be empty"); + } + if (errorBars != null && errorBars.size() != yData.size()) { + throw new IllegalArgumentException("Error bars and Y-Axis sizes are not the same"); + } + } + + @Override + public void paint(Graphics2D g, int width, int height) { + setWidth(width); + setHeight(height); + for (CategorySeries categorySeries : getSeriesMap().values()) { + CategorySeriesRenderStyle seriesType = categorySeries.getCategorySeriesRenderStyle(); + if (seriesType == null) { + categorySeries.setCategorySeriesRenderStyle(getStyler().getDefaultSeriesRenderStyle()); + } + } + setSeriesStyles(); + paintBackground(g); + axisPair.paint(g); + plot.paint(g); + chartTitle.paint(g); + legend.paint(g); + } + + public void setSeriesStyles() { + SeriesColorMarkerLineStyleCycler seriesColorMarkerLineStyleCycler = + new SeriesColorMarkerLineStyleCycler(getStyler().getSeriesColors(), + getStyler().getSeriesMarkers(), getStyler().getSeriesLines()); + for (CategorySeries series : getSeriesMap().values()) { + SeriesColorMarkerLineStyle seriesColorMarkerLineStyle = + seriesColorMarkerLineStyleCycler.getNextSeriesColorMarkerLineStyle(); + if (series.getLineStyle() == null) { + series.setLineStyle(seriesColorMarkerLineStyle.getStroke()); + } + if (series.getLineColor() == null) { + series.setLineColor(seriesColorMarkerLineStyle.getColor()); + } + if (series.getFillColor() == null) { + series.setFillColor(seriesColorMarkerLineStyle.getColor()); + } + if (series.getMarker() == null) { + series.setMarker(seriesColorMarkerLineStyle.getMarker()); + } + if (series.getMarkerColor() == null) { + series.setMarkerColor(seriesColorMarkerLineStyle.getColor()); + } + } + } + + private DataType getDataType(List data) { + DataType axisType; + Iterator itr = data.iterator(); + Object dataPoint = itr.next(); + if (dataPoint instanceof Number) { + axisType = DataType.Number; + } else if (dataPoint instanceof Instant) { + axisType = DataType.Instant; + } else if (dataPoint instanceof String) { + axisType = DataType.String; + } else { + throw new IllegalArgumentException("Series data must be either Number, Instant or String type"); + } + return axisType; + } + + private static class CategoryPlot extends AxesChartPlot { + + private final ST categoryStyler; + + private CategoryPlot(Chart chart) { + super(chart); + categoryStyler = chart.getStyler(); + } + + @Override + public void paint(Graphics2D g) { + if (CategorySeriesRenderStyle.Bar.equals(categoryStyler.getDefaultSeriesRenderStyle()) || CategorySeriesRenderStyle.Stick.equals(categoryStyler.getDefaultSeriesRenderStyle())) { + this.contentPlot = new ContentPlotCategoryBar<>(chart); + } else { + this.contentPlot = new ContentPlotCategoryLineAreaScatter<>(chart); + } + super.paint(g); + } + } + + private static class ContentPlotCategoryBar extends ContentPlot { + + private final ST stylerCategory; + + ContentPlotCategoryBar(Chart chart) { + super(chart); + this.stylerCategory = chart.getStyler(); + } + + @Override + public void doPaint(Graphics2D g) { + double xTickSpace = stylerCategory.getPlotContentSize() * getBounds().getWidth(); + double xLeftMargin = (getBounds().getWidth() - xTickSpace) / 2.0; + Map seriesMap = chart.getSeriesMap(); + int numCategories = seriesMap.values().iterator().next().getXData().size(); + double gridStep = xTickSpace / numCategories; + double yMin = chart.getYAxis().getMin(); + double yMax = chart.getYAxis().getMax(); + int chartForm; // 1=positive, -1=negative, 0=span + if (yMin > 0.0 && yMax > 0.0) { + chartForm = 1; // positive chart + } else if (yMin < 0.0 && yMax < 0.0) { + chartForm = -1; // negative chart + } else { + chartForm = 0;// span chart + } + double yTickSpace = stylerCategory.getPlotContentSize() * getBounds().getHeight(); + double yTopMargin = (getBounds().getHeight() - yTickSpace) / 2.0; + int seriesCounter = 0; + double[] accumulatedStackOffsetPos = new double[numCategories]; + double[] accumulatedStackOffsetNeg = new double[numCategories]; + for (S series : seriesMap.values()) { + double previousX = -Double.MAX_VALUE; + double previousY = -Double.MAX_VALUE; + Iterator yItr = series.getYData().iterator(); + Iterator ebItr = null; + Collection errorBars = series.getExtraValues(); + if (errorBars != null) { + ebItr = errorBars.iterator(); + } + List steppedPath = null; + List steppedReturnPath = null; + int categoryCounter = 0; + while (yItr.hasNext()) { + Number next = yItr.next(); + if (next == null) { + previousX = -Double.MAX_VALUE; + previousY = -Double.MAX_VALUE; + categoryCounter++; + continue; + } + double y = next.doubleValue(); + double yTop = 0.0; + double yBottom = 0.0; + switch (chartForm) { + case 1: // positive chart + // check for points off the chart draw area due to a custom yMin + if (y < yMin) { + categoryCounter++; + continue; + } + yTop = y; + yBottom = yMin; + break; + case -1: // negative chart + // check for points off the chart draw area due to a custom yMin + if (y > yMax) { + categoryCounter++; + continue; + } + yTop = yMax; + yBottom = y; + break; + case 0: // span chart + if (y >= 0.0) { // positive + yTop = y; + if (series.getCategorySeriesRenderStyle() == CategorySeriesRenderStyle.Bar + || series.getCategorySeriesRenderStyle() == CategorySeriesRenderStyle.Stick + || series.getCategorySeriesRenderStyle() + == SteppedBar) { + yBottom = 0.0; + } else { + yBottom = y; + } + if (stylerCategory.isStacked()) { + yTop += accumulatedStackOffsetPos[categoryCounter]; + yBottom += accumulatedStackOffsetPos[categoryCounter]; + accumulatedStackOffsetPos[categoryCounter] += (yTop - yBottom); + } + } else { + if (series.getCategorySeriesRenderStyle() == CategorySeriesRenderStyle.Bar + || series.getCategorySeriesRenderStyle() == CategorySeriesRenderStyle.Stick + || series.getCategorySeriesRenderStyle() + == SteppedBar) { + yTop = 0.0; + } else { + yTop = y; // yTransform uses yTop, and for non-bars and stick, it's the same as + // yBottom. + } + yBottom = y; + if (stylerCategory.isStacked()) { + yTop -= accumulatedStackOffsetNeg[categoryCounter]; + yBottom -= accumulatedStackOffsetNeg[categoryCounter]; + accumulatedStackOffsetNeg[categoryCounter] += (yTop - yBottom); + } + } + break; + default: + break; + } + + double yTransform = getBounds().getHeight() - (yTopMargin + (yTop - yMin) / (yMax - yMin) * yTickSpace); + double yOffset = getBounds().getY() + yTransform; + + double zeroTransform = getBounds().getHeight() - (yTopMargin + (yBottom - yMin) / (yMax - yMin) * yTickSpace); + double zeroOffset = getBounds().getY() + zeroTransform; + double xOffset; + double barWidth; + + { + double barWidthPercentage = stylerCategory.getAvailableSpaceFill(); + // SteppedBars can not have any space between them + if (series.getCategorySeriesRenderStyle() == CategorySeriesRenderStyle.SteppedBar) + barWidthPercentage = 1; + + if (stylerCategory.isOverlapped() || stylerCategory.isStacked()) { + + barWidth = gridStep * barWidthPercentage; + double barMargin = gridStep * (1 - barWidthPercentage) / 2; + xOffset = getBounds().getX() + xLeftMargin + gridStep * categoryCounter++ + barMargin; + } else { + + barWidth = gridStep / chart.getSeriesMap().size() * barWidthPercentage; + double barMargin = gridStep * (1 - barWidthPercentage) / 2; + xOffset = + getBounds().getX() + + xLeftMargin + + gridStep * categoryCounter++ + + seriesCounter * barWidth + + barMargin; + } + } + + // SteppedBar. Partially drawn in loop, partially after loop. + if (series.getCategorySeriesRenderStyle() == CategorySeriesRenderStyle.SteppedBar) { + + double yCenter = zeroOffset; + double yTip = yOffset; + double stepLength = gridStep; + + // yTip should be the value end, yCenter the center (0) end. + if (y < 0) { + + yTip = zeroOffset; + yCenter = yOffset; + } + + // Init in first iteration + if (steppedPath == null) { + steppedPath = new ArrayList<>(); + steppedReturnPath = new ArrayList<>(); + steppedPath.add(new Point2D.Double(xOffset, yCenter)); + } else if (stylerCategory.isStacked()) { + // If a section of a stacked graph has changed from positive + // to negative or vice-versa, draw what we've stored up so far + // and resume with a blank slate. + if ((previousY > 0 && y < 0) || (previousY < 0 && y > 0)) { + drawStepBar(g, series, steppedPath, steppedReturnPath); + + steppedPath.clear(); + steppedReturnPath.clear(); + steppedPath.add(new Point2D.Double(xOffset, yCenter)); + } + } + + if (!yItr.hasNext()) { + + // Shift the far point of the final bar backwards + // by the same amount its start was shifted forward. + if (!(stylerCategory.isOverlapped() || stylerCategory.isStacked())) { + + double singleBarStep = stepLength / (double) chart.getSeriesMap().size(); + stepLength -= (seriesCounter * singleBarStep); + } + } + + // Draw the vertical line to the new y position, and the horizontal flat of the bar. + steppedPath.add(new Point2D.Double(xOffset, yTip)); + steppedPath.add(new Point2D.Double(xOffset + stepLength, yTip)); + + // Add the corresponding centerline (or equivalent) to the return path + // Could be simplfied and removed for non-stacked graphs + steppedReturnPath.add(new Point2D.Double(xOffset, yCenter)); + steppedReturnPath.add(new Point2D.Double(xOffset + stepLength, yCenter)); + + previousY = y; + } + + // paint series + if (series.getCategorySeriesRenderStyle() == CategorySeriesRenderStyle.Bar) { + + // paint bar + Path2D.Double path = new Path2D.Double(); + path.moveTo(xOffset, yOffset); + path.lineTo(xOffset + barWidth, yOffset); + path.lineTo(xOffset + barWidth, zeroOffset); + path.lineTo(xOffset, zeroOffset); + path.closePath(); + + g.setColor(series.getFillColor()); + g.fill(path); + } else if (CategorySeriesRenderStyle.Stick.equals(series.getCategorySeriesRenderStyle())) { + if (series.getLineStyle() != Theme.Series.NONE_STROKE) { + g.setColor(series.getLineColor()); + g.setStroke(series.getLineStyle()); + Shape line = new Line2D.Double(xOffset, zeroOffset, xOffset, yOffset); + g.draw(line); + } + + // paint marker + if (series.getMarker() != null) { + g.setColor(series.getMarkerColor()); + + if (y <= 0) { + series.getMarker().paint(g, xOffset, zeroOffset, stylerCategory.getMarkerSize()); + } else { + series.getMarker().paint(g, xOffset, yOffset, stylerCategory.getMarkerSize()); + } + } + } else { + if (series.getCategorySeriesRenderStyle() == CategorySeriesRenderStyle.Line) { + if (series.getLineStyle() != Theme.Series.NONE_STROKE) { + if (previousX != -Double.MAX_VALUE && previousY != -Double.MAX_VALUE) { + g.setColor(series.getLineColor()); + g.setStroke(series.getLineStyle()); + Shape line = new Line2D.Double(previousX, previousY, xOffset + barWidth / 2, yOffset); + g.draw(line); + } + } + } + previousX = xOffset + barWidth / 2; + previousY = yOffset; + + // paint marker + if (series.getMarker() != null) { + g.setColor(series.getMarkerColor()); + series.getMarker().paint(g, previousX, previousY, stylerCategory.getMarkerSize()); + } + + } + // paint error bars + if (errorBars != null) { + double eb = ebItr.next().doubleValue(); + // set error bar style + if (stylerCategory.isErrorBarsColorSeriesColor()) { + g.setColor(series.getLineColor()); + } else { + g.setColor(stylerCategory.getErrorBarsColor()); + } + g.setStroke(Theme.Strokes.ERROR_BARS); + + // Top value + double errorBarLength = ((eb) / (yMax - yMin) * yTickSpace); + double topEBOffset = yOffset - errorBarLength; + + // Bottom value + double bottomEBOffset = yOffset + errorBarLength; + + // Draw it + double errorBarOffset = xOffset + barWidth / 2; + Shape line = new Line2D.Double(errorBarOffset, topEBOffset, errorBarOffset, bottomEBOffset); + g.draw(line); + line = new Line2D.Double(errorBarOffset - 3, bottomEBOffset, errorBarOffset + 3, bottomEBOffset); + g.draw(line); + line = new Line2D.Double(errorBarOffset - 3, topEBOffset, errorBarOffset + 3, topEBOffset); + g.draw(line); + } + + } + // Final drawing of a steppedBar is done after the main loop, + // as it continues on null and we may end up missing the final iteration. + if (steppedPath != null && !steppedReturnPath.isEmpty()) { + drawStepBar(g, series, steppedPath, steppedReturnPath); + } + seriesCounter++; + } + } + + private void drawStepBarLine(Graphics2D g, S series, Path2D.Double path) { + if (series.getLineColor() != null) { + g.setColor(series.getLineColor()); + g.setStroke(series.getLineStyle()); + g.draw(path); + } + } + + private void drawStepBarFill(Graphics2D g, S series, Path2D.Double path) { + if (series.getFillColor() != null) { + g.setColor(series.getFillColor()); + g.fill(path); + } + } + + private void drawStepBar(Graphics2D g, S series, List path, List returnPath) { + Collections.reverse(returnPath); + returnPath.remove(returnPath.size() - 1); + path.addAll(returnPath); + Path2D.Double drawPath = new Path2D.Double(); + Point2D.Double startPoint = path.remove(0); + drawPath.moveTo(startPoint.getX(), startPoint.getY()); + for (Point2D.Double currentPoint : path) { + drawPath.lineTo(currentPoint.getX(), currentPoint.getY()); + } + drawStepBarFill(g, series, drawPath); + drawPath.reset(); + drawPath.moveTo(startPoint.getX(), startPoint.getY()); + List linePath = path.subList(0, path.size() - returnPath.size() + 1); + for (Point2D.Double currentPoint : linePath) { + drawPath.lineTo(currentPoint.getX(), currentPoint.getY()); + } + drawStepBarLine(g, series, drawPath); + } + } + + private static class ContentPlotCategoryLineAreaScatter extends ContentPlot { + + private final ST categoryStyler; + + protected ContentPlotCategoryLineAreaScatter(Chart chart) { + super(chart); + this.categoryStyler = chart.getStyler(); + } + + @Override + public void doPaint(Graphics2D g) { + double xTickSpace = categoryStyler.getPlotContentSize() * getBounds().getWidth(); + double xLeftMargin = ((int) getBounds().getWidth() - xTickSpace) / 2.0; + double yTickSpace = categoryStyler.getPlotContentSize() * getBounds().getHeight(); + double yTopMargin =((int) getBounds().getHeight() - yTickSpace) / 2.0; + Map seriesMap = chart.getSeriesMap(); + int numCategories = seriesMap.values().iterator().next().getXData().size(); + double gridStep = xTickSpace / numCategories; + for (S series : seriesMap.values()) { + if (!series.isEnabled()) { + continue; + } + Axis yAxis = chart.getYAxis(series.getYAxisGroup()); + double yMin = yAxis.getMin(); + double yMax = yAxis.getMax(); + if (categoryStyler.isYAxisLogarithmic()) { + yMin = Math.log10(yMin); + yMax = Math.log10(yMax); + } + Collection yData = series.getYData(); + double previousX = -Double.MAX_VALUE; + double previousY = -Double.MAX_VALUE; + Iterator yItr = yData.iterator(); + Iterator ebItr = null; + Collection errorBars = series.getExtraValues(); + if (errorBars != null) { + ebItr = errorBars.iterator(); + } + Path2D.Double path = null; + int categoryCounter = 0; + while (yItr.hasNext()) { + Number next = yItr.next(); + if (next == null) { + // for area charts + closePath(g, path, previousX, yTopMargin); + path = null; + previousX = -Double.MAX_VALUE; + previousY = -Double.MAX_VALUE; + continue; + } + double yOrig = next.doubleValue(); + double y; + if (categoryStyler.isYAxisLogarithmic()) { + y = Math.log10(yOrig); + } else { + y = yOrig; + } + double yTransform = getBounds().getHeight() - (yTopMargin + (y - yMin) / (yMax - yMin) * yTickSpace); + if (Math.abs(yMax - yMin) / 5 == 0.0) { + yTransform = getBounds().getHeight() / 2.0; + } + double xOffset = getBounds().getX() + xLeftMargin + categoryCounter++ * gridStep + gridStep / 2; + double yOffset = getBounds().getY() + yTransform; + if (CategorySeriesRenderStyle.Line.equals(series.getCategorySeriesRenderStyle()) || + CategorySeriesRenderStyle.Area.equals(series.getCategorySeriesRenderStyle())) { + if (series.getLineStyle() != Theme.Series.NONE_STROKE) { + if (previousX != -Double.MAX_VALUE && previousY != -Double.MAX_VALUE) { + g.setColor(series.getLineColor()); + g.setStroke(series.getLineStyle()); + Shape line = new Line2D.Double(previousX, previousY, xOffset, yOffset); + g.draw(line); + } + } + } + if (CategorySeriesRenderStyle.Area.equals(series.getCategorySeriesRenderStyle())) { + if (previousX != -Double.MAX_VALUE && previousY != -Double.MAX_VALUE) { + g.setColor(series.getFillColor()); + double yBottomOfArea = getBounds().getY() + getBounds().getHeight() - yTopMargin; + if (path == null) { + path = new Path2D.Double(); + path.moveTo(previousX, yBottomOfArea); + path.lineTo(previousX, previousY); + } + path.lineTo(xOffset, yOffset); + } + if (xOffset < previousX) { + throw new RuntimeException("X-Data must be in ascending order for Area Charts"); + } + } + if (CategorySeriesRenderStyle.Stick.equals(series.getCategorySeriesRenderStyle())) { + if (series.getLineStyle() != Theme.Series.NONE_STROKE) { + double yBottomOfArea = getBounds().getY() + getBounds().getHeight() - yTopMargin; + g.setColor(series.getLineColor()); + g.setStroke(series.getLineStyle()); + Shape line = new Line2D.Double(xOffset, yBottomOfArea, xOffset, yOffset); + g.draw(line); + } + } + previousX = xOffset; + previousY = yOffset; + if (series.getMarker() != null) { + g.setColor(series.getMarkerColor()); + series.getMarker().paint(g, xOffset, yOffset, categoryStyler.getMarkerSize()); + } + if (errorBars != null) { + double eb = ebItr.next().doubleValue(); + if (categoryStyler.isErrorBarsColorSeriesColor()) { + g.setColor(series.getLineColor()); + } else { + g.setColor(categoryStyler.getErrorBarsColor()); + } + g.setStroke(Theme.Strokes.ERROR_BARS); + double topValue; + if (categoryStyler.isYAxisLogarithmic()) { + topValue = yOrig + eb; + topValue = Math.log10(topValue); + } else { + topValue = y + eb; + } + double topEBTransform = getBounds().getHeight() - (yTopMargin + (topValue - yMin) / (yMax - yMin) * yTickSpace); + double topEBOffset = getBounds().getY() + topEBTransform; + double bottomValue; + if (categoryStyler.isYAxisLogarithmic()) { + bottomValue = yOrig - eb; + bottomValue = Math.log10(bottomValue); + } else { + bottomValue = y - eb; + } + double bottomEBTransform = getBounds().getHeight() - (yTopMargin + (bottomValue - yMin) / (yMax - yMin) * yTickSpace); + double bottomEBOffset = getBounds().getY() + bottomEBTransform; + Shape line = new Line2D.Double(xOffset, topEBOffset, xOffset, bottomEBOffset); + g.draw(line); + line = new Line2D.Double(xOffset - 3, bottomEBOffset, xOffset + 3, bottomEBOffset); + g.draw(line); + line = new Line2D.Double(xOffset - 3, topEBOffset, xOffset + 3, topEBOffset); + g.draw(line); + } + } + g.setColor(series.getFillColor()); + closePath(g, path, previousX, yTopMargin); + } + } + } +} diff --git a/chart/src/main/java/org/xbib/graphics/chart/category/CategoryChartBuilder.java b/chart/src/main/java/org/xbib/graphics/chart/category/CategoryChartBuilder.java new file mode 100644 index 0000000..f36b48c --- /dev/null +++ b/chart/src/main/java/org/xbib/graphics/chart/category/CategoryChartBuilder.java @@ -0,0 +1,40 @@ +package org.xbib.graphics.chart.category; + +import org.xbib.graphics.chart.ChartBuilder; + +public class CategoryChartBuilder extends ChartBuilder { + + private String xAxisTitle = ""; + private String yAxisTitle = ""; + + public CategoryChartBuilder() { + } + + public CategoryChartBuilder xAxisTitle(String xAxisTitle) { + this.xAxisTitle = xAxisTitle; + return this; + } + + public CategoryChartBuilder yAxisTitle(String yAxisTitle) { + this.yAxisTitle = yAxisTitle; + return this; + } + + public String getxAxisTitle() { + return xAxisTitle; + } + + public String getyAxisTitle() { + return yAxisTitle; + } + + /** + * return fully built Chart_Category + * + * @return a Chart_Category + */ + @Override + public CategoryChart build() { + return new CategoryChart(this); + } +} diff --git a/chart/src/main/java/org/xbib/graphics/chart/category/CategorySeries.java b/chart/src/main/java/org/xbib/graphics/chart/category/CategorySeries.java new file mode 100644 index 0000000..71d3bb4 --- /dev/null +++ b/chart/src/main/java/org/xbib/graphics/chart/category/CategorySeries.java @@ -0,0 +1,36 @@ +package org.xbib.graphics.chart.category; + +import org.xbib.graphics.chart.axis.DataType; +import org.xbib.graphics.chart.series.AxesChartSeriesCategory; +import org.xbib.graphics.chart.legend.LegendRenderType; + +import java.util.List; + +/** + * A Series containing category data to be plotted on a Chart. + */ +public class CategorySeries extends AxesChartSeriesCategory { + + private CategorySeriesRenderStyle categorySeriesRenderStyle = null; + + public CategorySeries(String name, List xData, + List yData, + List errorBars, + DataType axisType) { + super(name, xData, yData, errorBars, axisType); + } + + public CategorySeriesRenderStyle getCategorySeriesRenderStyle() { + return categorySeriesRenderStyle; + } + + public void setCategorySeriesRenderStyle(CategorySeriesRenderStyle chartXYSeriesRenderStyle) { + this.categorySeriesRenderStyle = chartXYSeriesRenderStyle; + } + + @Override + public LegendRenderType getLegendRenderType() { + return categorySeriesRenderStyle.getLegendRenderType(); + } + +} diff --git a/chart/src/main/java/org/xbib/graphics/chart/category/CategorySeriesRenderStyle.java b/chart/src/main/java/org/xbib/graphics/chart/category/CategorySeriesRenderStyle.java new file mode 100644 index 0000000..4ef08f6 --- /dev/null +++ b/chart/src/main/java/org/xbib/graphics/chart/category/CategorySeriesRenderStyle.java @@ -0,0 +1,30 @@ +package org.xbib.graphics.chart.category; + +import org.xbib.graphics.chart.legend.LegendRenderable; +import org.xbib.graphics.chart.legend.LegendRenderType; + +public enum CategorySeriesRenderStyle implements LegendRenderable { + + Line(LegendRenderType.Line), + + Area(LegendRenderType.Line), + + Scatter(LegendRenderType.Scatter), + + Bar(LegendRenderType.BoxNoOutline), + + SteppedBar(LegendRenderType.Box), + + Stick(LegendRenderType.Line); + + private final LegendRenderType legendRenderType; + + CategorySeriesRenderStyle(LegendRenderType legendRenderType) { + this.legendRenderType = legendRenderType; + } + + @Override + public LegendRenderType getLegendRenderType() { + return legendRenderType; + } +} diff --git a/chart/src/main/java/org/xbib/graphics/chart/category/CategoryStyler.java b/chart/src/main/java/org/xbib/graphics/chart/category/CategoryStyler.java new file mode 100644 index 0000000..31183f8 --- /dev/null +++ b/chart/src/main/java/org/xbib/graphics/chart/category/CategoryStyler.java @@ -0,0 +1,96 @@ +package org.xbib.graphics.chart.category; + +import org.xbib.graphics.chart.style.AxesChartStyler; +import org.xbib.graphics.chart.theme.Theme; + +public class CategoryStyler extends AxesChartStyler { + + private CategorySeriesRenderStyle categorySeriesRenderStyle; + + private double availableSpaceFill; + private boolean isOverlapped; + private boolean isStacked; + + public CategoryStyler() { + + this.setAllStyles(); + super.setAllStyles(); + } + + @Override + protected void setAllStyles() { + + this.categorySeriesRenderStyle = CategorySeriesRenderStyle.Bar; // set default to bar + + availableSpaceFill = theme.getAvailableSpaceFill(); + isOverlapped = theme.isOverlapped(); + } + + /** + * Sets the available space for rendering each category as a percentage. For a bar chart with one + * series, it will be the width of the bar as a percentage of the maximum space alloted for the + * bar. If there are three series and three bars, the three bars will share the available space. + * This affects all category series render types, not only bar charts. Full width is 100%, i.e. + * 1.0 + * + * @param availableSpaceFill space fill + */ + public void setAvailableSpaceFill(double availableSpaceFill) { + this.availableSpaceFill = availableSpaceFill; + } + + public double getAvailableSpaceFill() { + + return availableSpaceFill; + } + + /** + * Sets the default series render style for the chart (bar, stick, line, scatter, area, etc.) You can override the + * series render style individually on each Series object. + * + * @param categorySeriesRenderStyle render style + */ + public void setDefaultSeriesRenderStyle(CategorySeriesRenderStyle categorySeriesRenderStyle) { + this.categorySeriesRenderStyle = categorySeriesRenderStyle; + } + + public CategorySeriesRenderStyle getDefaultSeriesRenderStyle() { + return categorySeriesRenderStyle; + } + + public boolean isOverlapped() { + return isOverlapped; + } + + /** + * set whether or no bars are overlapped. Otherwise they are places side-by-side + * + * @param isOverlapped overlapped + */ + public void setOverlapped(boolean isOverlapped) { + this.isOverlapped = isOverlapped; + } + + public boolean isStacked() { + return isStacked; + } + + /** + * Set whether or not series renderings (i.e. bars, stick, etc.) are stacked. + * + * @param isStacked stacked + */ + public void setStacked(boolean isStacked) { + this.isStacked = isStacked; + } + + /** + * Set the theme the styler should use + * + * @param theme theme + */ + protected void setTheme(Theme theme) { + this.theme = theme; + super.setAllStyles(); + } +} diff --git a/chart/src/main/java/org/xbib/graphics/chart/formatter/DateFormatter.java b/chart/src/main/java/org/xbib/graphics/chart/formatter/DateFormatter.java new file mode 100644 index 0000000..9308f22 --- /dev/null +++ b/chart/src/main/java/org/xbib/graphics/chart/formatter/DateFormatter.java @@ -0,0 +1,37 @@ +package org.xbib.graphics.chart.formatter; + +import java.text.FieldPosition; +import java.text.Format; +import java.text.ParsePosition; +import java.time.ZonedDateTime; +import java.time.format.DateTimeFormatter; +import java.util.Locale; + +@SuppressWarnings("serial") +public class DateFormatter extends Format { + + private final String pattern; + + private final Locale locale; + + public DateFormatter(String pattern, Locale locale) { + this.pattern = pattern; + this.locale = locale; + } + + @Override + public StringBuffer format(Object obj, StringBuffer toAppendTo, FieldPosition pos) { + if (obj instanceof ZonedDateTime) { + ZonedDateTime zdt = (ZonedDateTime) obj; + DateTimeFormatter dtf = DateTimeFormatter.ofPattern(pattern) + .withLocale(locale); + toAppendTo.append(zdt.format(dtf)); + } + return toAppendTo; + } + + @Override + public Object parseObject(String source, ParsePosition pos) { + return null; + } +} diff --git a/chart/src/main/java/org/xbib/graphics/chart/formatter/NumberFormatter.java b/chart/src/main/java/org/xbib/graphics/chart/formatter/NumberFormatter.java new file mode 100644 index 0000000..96ebf6b --- /dev/null +++ b/chart/src/main/java/org/xbib/graphics/chart/formatter/NumberFormatter.java @@ -0,0 +1,102 @@ +package org.xbib.graphics.chart.formatter; + +import org.xbib.graphics.chart.axis.Direction; +import org.xbib.graphics.chart.style.AxesChartStyler; + +import java.math.BigDecimal; +import java.text.DecimalFormat; +import java.text.FieldPosition; +import java.text.Format; +import java.text.NumberFormat; +import java.text.ParsePosition; + +@SuppressWarnings("serial") +public class NumberFormatter extends Format { + + private final AxesChartStyler styler; + private final Direction axisDirection; + private final double min; + private final double max; + private final NumberFormat numberFormat; + + public NumberFormatter(AxesChartStyler styler, Direction axisDirection, double min, double max) { + this.styler = styler; + this.axisDirection = axisDirection; + this.min = min; + this.max = max; + numberFormat = NumberFormat.getNumberInstance(styler.getLocale()); + } + + public String getFormatPattern(BigDecimal value) { + if (value.compareTo(BigDecimal.ZERO) == 0) { + return "0"; + } + double difference = max - min; + int placeOfDifference; + if (difference == 0.0) { + placeOfDifference = 0; + } else { + placeOfDifference = (int) Math.floor(Math.log(difference) / Math.log(10)); + } + int placeOfValue; + if (value.doubleValue() == 0.0) { + placeOfValue = 0; + } else { + placeOfValue = (int) Math.floor(Math.log(value.doubleValue()) / Math.log(10)); + } + if (placeOfDifference <= 4 && placeOfDifference >= -4) { + return getNormalDecimalPatternPositive(placeOfValue); + } else { + return getScientificDecimalPattern(); + } + } + + private String getNormalDecimalPatternPositive(int placeOfValue) { + int maxNumPlaces = 15; + StringBuilder sb = new StringBuilder(); + for (int i = maxNumPlaces - 1; i >= -1 * maxNumPlaces; i--) { + if (i >= 0 && (i < placeOfValue)) { + sb.append("0"); + } else if (i < 0 && (i > placeOfValue)) { + sb.append("0"); + } else { + sb.append("#"); + } + if (i % 3 == 0 && i > 0) { + sb.append(","); + } + if (i == 0) { + sb.append("."); + } + } + return sb.toString(); + } + + private String getScientificDecimalPattern() { + return "0.###############E0"; + } + + @Override + public StringBuffer format(Object obj, StringBuffer toAppendTo, FieldPosition pos) { + Number value = (Number) obj; + String decimalPattern; + if (axisDirection == Direction.X && styler.getXAxisDecimalPattern() != null) { + decimalPattern = styler.getXAxisDecimalPattern(); + } else if (axisDirection == Direction.Y && styler.getYAxisDecimalPattern() != null) { + decimalPattern = styler.getYAxisDecimalPattern(); + } else if (styler.getDecimalPattern() != null) { + decimalPattern = styler.getDecimalPattern(); + } else { + decimalPattern = getFormatPattern(BigDecimal.valueOf(value.doubleValue())); + } + DecimalFormat normalFormat = (DecimalFormat) numberFormat; + normalFormat.applyPattern(decimalPattern); + toAppendTo.append(normalFormat.format(value)); + return toAppendTo; + } + + @Override + public Object parseObject(String source, ParsePosition pos) { + return null; + } +} diff --git a/chart/src/main/java/org/xbib/graphics/chart/formatter/NumberLogFormatter.java b/chart/src/main/java/org/xbib/graphics/chart/formatter/NumberLogFormatter.java new file mode 100644 index 0000000..827cbd8 --- /dev/null +++ b/chart/src/main/java/org/xbib/graphics/chart/formatter/NumberLogFormatter.java @@ -0,0 +1,54 @@ +package org.xbib.graphics.chart.formatter; + +import org.xbib.graphics.chart.axis.Direction; +import org.xbib.graphics.chart.style.AxesChartStyler; + +import java.text.DecimalFormat; +import java.text.FieldPosition; +import java.text.Format; +import java.text.NumberFormat; +import java.text.ParsePosition; + +@SuppressWarnings("serial") +public class NumberLogFormatter extends Format { + + private final AxesChartStyler styler; + + private final Direction axisDirection; + + private final NumberFormat numberFormat; + + public NumberLogFormatter(AxesChartStyler styler, Direction axisDirection) { + this.styler = styler; + this.axisDirection = axisDirection; + numberFormat = NumberFormat.getNumberInstance(styler.getLocale()); + } + + @Override + public StringBuffer format(Object obj, StringBuffer toAppendTo, FieldPosition pos) { + double number = (Double) obj; + String decimalPattern; + if (axisDirection == Direction.X && styler.getXAxisDecimalPattern() != null) { + decimalPattern = styler.getXAxisDecimalPattern(); + } else if (axisDirection == Direction.Y && styler.getYAxisDecimalPattern() != null) { + decimalPattern = styler.getYAxisDecimalPattern(); + } else if (styler.getDecimalPattern() != null) { + decimalPattern = styler.getDecimalPattern(); + } else { + if (Math.abs(number) > 1000.0 || Math.abs(number) < 0.001) { + decimalPattern = "0E0"; + } else { + decimalPattern = "0.###"; + } + } + DecimalFormat normalFormat = (DecimalFormat) numberFormat; + normalFormat.applyPattern(decimalPattern); + toAppendTo.append(normalFormat.format(number)); + return toAppendTo; + } + + @Override + public Object parseObject(String source, ParsePosition pos) { + return null; + } +} diff --git a/chart/src/main/java/org/xbib/graphics/chart/formatter/StringFormatter.java b/chart/src/main/java/org/xbib/graphics/chart/formatter/StringFormatter.java new file mode 100644 index 0000000..91a6203 --- /dev/null +++ b/chart/src/main/java/org/xbib/graphics/chart/formatter/StringFormatter.java @@ -0,0 +1,21 @@ +package org.xbib.graphics.chart.formatter; + +import java.text.FieldPosition; +import java.text.Format; +import java.text.ParsePosition; + +@SuppressWarnings("serial") +public class StringFormatter extends Format { + + @Override + public StringBuffer format(Object obj, StringBuffer toAppendTo, FieldPosition pos) { + String string = obj.toString(); + toAppendTo.append(string); + return toAppendTo; + } + + @Override + public Object parseObject(String source, ParsePosition pos) { + return null; + } +} diff --git a/chart/src/main/java/org/xbib/graphics/chart/io/BitmapFormat.java b/chart/src/main/java/org/xbib/graphics/chart/io/BitmapFormat.java new file mode 100644 index 0000000..d4481a8 --- /dev/null +++ b/chart/src/main/java/org/xbib/graphics/chart/io/BitmapFormat.java @@ -0,0 +1,5 @@ +package org.xbib.graphics.chart.io; + +public enum BitmapFormat { + PNG, JPG, BMP, GIF +} diff --git a/chart/src/main/java/org/xbib/graphics/chart/io/CSVExporter.java b/chart/src/main/java/org/xbib/graphics/chart/io/CSVExporter.java new file mode 100644 index 0000000..b2c1e06 --- /dev/null +++ b/chart/src/main/java/org/xbib/graphics/chart/io/CSVExporter.java @@ -0,0 +1,91 @@ +package org.xbib.graphics.chart.io; + +import org.xbib.graphics.chart.xy.XYSeries; + +import java.io.BufferedWriter; +import java.io.IOException; +import java.io.OutputStreamWriter; +import java.nio.charset.StandardCharsets; +import java.nio.file.Files; +import java.nio.file.Path; +import java.util.Collection; +import java.util.Iterator; + +/** + * This class is used to export Chart data to a path. + */ +public class CSVExporter { + + private final static String LF = System.getProperty("line.separator"); + + public static void writeCSVRows(XYSeries series, Path path) throws IOException { + try (BufferedWriter bufferedWriter = new BufferedWriter(new OutputStreamWriter(Files.newOutputStream(path), + StandardCharsets.UTF_8))) { + String csv = join(series.getXData()) + LF; + bufferedWriter.write(csv); + csv = join(series.getYData()) + LF; + bufferedWriter.write(csv); + if (series.getExtraValues() != null) { + csv = join(series.getExtraValues()) + LF; + bufferedWriter.write(csv); + } + } + } + + public static void writeCSVColumns(XYSeries series, Path path) throws IOException { + try (BufferedWriter bufferedWriter = new BufferedWriter(new OutputStreamWriter(Files.newOutputStream(path), + StandardCharsets.UTF_8))) { + Collection xData = series.getXData(); + Collection yData = series.getYData(); + Collection errorBarData = series.getExtraValues(); + Iterator itrx = xData.iterator(); + Iterator itry = yData.iterator(); + Iterator itrErrorBar = null; + if (errorBarData != null) { + itrErrorBar = errorBarData.iterator(); + } + while (itrx.hasNext()) { + Number xDataPoint = (Number) itrx.next(); + Number yDataPoint = itry.next(); + Number errorBarValue = null; + if (itrErrorBar != null) { + errorBarValue = itrErrorBar.next(); + } + StringBuilder sb = new StringBuilder(); + sb.append(xDataPoint).append(","); + sb.append(yDataPoint).append(","); + if (errorBarValue != null) { + sb.append(errorBarValue).append(","); + } + sb.append(LF); + bufferedWriter.write(sb.toString()); + } + } + } + + private static String join(Collection collection) { + if (collection == null) { + return null; + } + Iterator iterator = collection.iterator(); + if (!iterator.hasNext()) { + return ""; + } + Object first = iterator.next(); + if (!iterator.hasNext()) { + return first == null ? "" : first.toString(); + } + StringBuilder sb = new StringBuilder(); + if (first != null) { + sb.append(first); + } + while (iterator.hasNext()) { + sb.append(","); + Object obj = iterator.next(); + if (obj != null) { + sb.append(obj); + } + } + return sb.toString(); + } +} diff --git a/chart/src/main/java/org/xbib/graphics/chart/io/CSVImporter.java b/chart/src/main/java/org/xbib/graphics/chart/io/CSVImporter.java new file mode 100644 index 0000000..777b899 --- /dev/null +++ b/chart/src/main/java/org/xbib/graphics/chart/io/CSVImporter.java @@ -0,0 +1,109 @@ +package org.xbib.graphics.chart.io; + +import org.xbib.graphics.chart.theme.Theme; +import org.xbib.graphics.chart.xy.XYChart; + +import java.io.BufferedReader; +import java.io.IOException; +import java.io.InputStream; +import java.io.InputStreamReader; +import java.nio.charset.Charset; +import java.nio.file.FileVisitOption; +import java.nio.file.FileVisitResult; +import java.nio.file.Files; +import java.nio.file.Path; +import java.nio.file.PathMatcher; +import java.nio.file.SimpleFileVisitor; +import java.nio.file.attribute.BasicFileAttributes; +import java.util.ArrayList; +import java.util.EnumSet; +import java.util.List; + +/** + * This class is used to create a Chart object from a folder containing one or more CSV files. The parent folder's name + * becomes the title of the chart. Each CSV file in the folder becomes a series on the chart. + * The CSV file's name becomes the series' name. + */ +public class CSVImporter { + + public static XYChart getChartFromCSVDir(Path path, DataOrientation dataOrientation, int width, int height) + throws IOException { + return getChartFromCSVDir(path, dataOrientation, width, height, null); + } + + public static XYChart getChartFromCSVDir(Path path, DataOrientation dataOrientation, int width, int height, + Theme theme) throws IOException { + XYChart chart = new XYChart(width, height, theme); + final PathMatcher pathMatcher = path.getFileSystem().getPathMatcher("glob:*.csv"); + Files.walkFileTree(path, EnumSet.of(FileVisitOption.FOLLOW_LINKS), Integer.MAX_VALUE, new SimpleFileVisitor() { + @Override + public FileVisitResult visitFile(Path file, BasicFileAttributes attrs) throws IOException { + if (pathMatcher.matches(file.getFileName())) { + String[] xAndYData; + if (dataOrientation == DataOrientation.Rows) { + xAndYData = getSeriesDataFromCSVRows(Files.newInputStream(file)); + } else { + xAndYData = getSeriesDataFromCSVColumns(Files.newInputStream(file)); + } + if (xAndYData[2] == null || xAndYData[2].trim().equalsIgnoreCase("")) { + chart.addSeries(file.toString().substring(0, file.toString().indexOf(".csv")), + getAxisData(xAndYData[0]), getAxisData(xAndYData[1])); + } else { + chart.addSeries(file.toString().substring(0, file.toString().indexOf(".csv")), + getAxisData(xAndYData[0]), getAxisData(xAndYData[1]), getAxisData(xAndYData[2])); + } + } + return FileVisitResult.CONTINUE; + } + }); + return chart; + } + + private static String[] getSeriesDataFromCSVRows(InputStream inputStream) throws IOException { + String[] xAndYData = new String[3]; + try (BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(inputStream, + Charset.forName("UTF-8")))) { + int counter = 0; + String line = null; + while ((line = bufferedReader.readLine()) != null) { + xAndYData[counter++] = line; + } + } + return xAndYData; + } + + private static String[] getSeriesDataFromCSVColumns(InputStream inputStream) throws IOException { + String[] xAndYData = new String[3]; + xAndYData[0] = ""; + xAndYData[1] = ""; + xAndYData[2] = ""; + try (BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(inputStream, + Charset.forName("UTF-8")))) { + String line = null; + while ((line = bufferedReader.readLine()) != null) { + String[] dataArray = line.split(","); + xAndYData[0] += dataArray[0] + ","; + xAndYData[1] += dataArray[1] + ","; + if (dataArray.length > 2) { + xAndYData[2] += dataArray[2] + ","; + } + } + } + return xAndYData; + } + + private static List getAxisData(String stringData) { + List axisData = new ArrayList<>(); + String[] stringDataArray = stringData.split(","); + for (String dataPoint : stringDataArray) { + axisData.add(Double.parseDouble(dataPoint)); + } + return axisData; + } + + public enum DataOrientation { + + Rows, Columns + } + +} diff --git a/chart/src/main/java/org/xbib/graphics/chart/io/VectorGraphicsFormat.java b/chart/src/main/java/org/xbib/graphics/chart/io/VectorGraphicsFormat.java new file mode 100644 index 0000000..c01323f --- /dev/null +++ b/chart/src/main/java/org/xbib/graphics/chart/io/VectorGraphicsFormat.java @@ -0,0 +1,5 @@ +package org.xbib.graphics.chart.io; + +public enum VectorGraphicsFormat { + EPS, PDF, SVG +} diff --git a/chart/src/main/java/org/xbib/graphics/chart/io/vector/Document.java b/chart/src/main/java/org/xbib/graphics/chart/io/vector/Document.java new file mode 100644 index 0000000..49f183f --- /dev/null +++ b/chart/src/main/java/org/xbib/graphics/chart/io/vector/Document.java @@ -0,0 +1,13 @@ +package org.xbib.graphics.chart.io.vector; + +import org.xbib.graphics.chart.io.vector.intermediate.CommandHandler; + +import java.io.IOException; +import java.io.OutputStream; + +public interface Document extends CommandHandler { + void write(OutputStream out) throws IOException; + + void close(); +} + diff --git a/chart/src/main/java/org/xbib/graphics/chart/io/vector/EPSGraphics2D.java b/chart/src/main/java/org/xbib/graphics/chart/io/vector/EPSGraphics2D.java new file mode 100644 index 0000000..8c0eb34 --- /dev/null +++ b/chart/src/main/java/org/xbib/graphics/chart/io/vector/EPSGraphics2D.java @@ -0,0 +1,42 @@ +package org.xbib.graphics.chart.io.vector; + +import org.xbib.graphics.chart.io.vector.eps.EPSProcessor; + +import java.awt.BasicStroke; +import java.awt.Color; + +/** + * {@code Graphics2D} implementation that saves all operations to a string + * in the Encapsulated PostScript® (EPS) format. + */ +public class EPSGraphics2D extends ProcessingPipeline { + + private final Processor processor; + + /** + * Initializes a new VectorGraphics2D pipeline for translating Graphics2D + * commands to EPS data. The document dimensions must be specified as + * parameters. + * + * @param x Left offset. + * @param y Top offset + * @param width Width. + * @param height Height. + */ + public EPSGraphics2D(double x, double y, double width, double height) { + super(x, y, width, height); + processor = new EPSProcessor(); + /* + * The following are the default settings for the graphics state in an EPS file. + * Although they currently appear in the document output, they do not have to be set explicitly. + */ + // TODO: Default graphics state does not need to be printed in the document + setColor(Color.BLACK); + setStroke(new BasicStroke(1f, BasicStroke.CAP_BUTT, BasicStroke.JOIN_MITER, 10f, null, 0f)); + } + + @Override + protected Processor getProcessor() { + return processor; + } +} diff --git a/chart/src/main/java/org/xbib/graphics/chart/io/vector/GraphicsState.java b/chart/src/main/java/org/xbib/graphics/chart/io/vector/GraphicsState.java new file mode 100644 index 0000000..d418b6a --- /dev/null +++ b/chart/src/main/java/org/xbib/graphics/chart/io/vector/GraphicsState.java @@ -0,0 +1,275 @@ +package org.xbib.graphics.chart.io.vector; + +import org.xbib.graphics.chart.io.vector.util.GraphicsUtils; + +import java.awt.AlphaComposite; +import java.awt.BasicStroke; +import java.awt.Color; +import java.awt.Composite; +import java.awt.Font; +import java.awt.Paint; +import java.awt.RenderingHints; +import java.awt.Shape; +import java.awt.Stroke; +import java.awt.geom.AffineTransform; +import java.awt.geom.NoninvertibleTransformException; +import java.awt.geom.Rectangle2D; +import java.util.Objects; + +public class GraphicsState implements Cloneable { + /** + * Default background color. + */ + public static final Color DEFAULT_BACKGROUND = Color.BLACK; + /** + * Default color. + */ + public static final Color DEFAULT_COLOR = Color.WHITE; + /** + * Default clipping shape. + */ + public static final Shape DEFAULT_CLIP = null; + /** + * Default composite mode. + */ + public static final Composite DEFAULT_COMPOSITE = AlphaComposite.SrcOver; + /** + * Default font. + */ + public static final Font DEFAULT_FONT = Font.decode(null); + /** + * Default paint. + */ + public static final Color DEFAULT_PAINT = DEFAULT_COLOR; + /** + * Default stroke. + */ + public static final Stroke DEFAULT_STROKE = new BasicStroke(); + /** + * Default transformation. + */ + public static final AffineTransform DEFAULT_TRANSFORM = + new AffineTransform(); + /** + * Default XOR mode. + */ + public static final Color DEFAULT_XOR_MODE = Color.BLACK; + + /** + * Rendering hints. + */ + private RenderingHints hints; + /** + * Current background color. + */ + private Color background; + /** + * Current foreground color. + */ + private Color color; + /** + * Shape used for clipping paint operations. + */ + private Shape clip; + /** + * Method used for compositing. + */ + private Composite composite; + /** + * Current font. + */ + private Font font; + /** + * Paint used to fill shapes. + */ + private Paint paint; + /** + * Stroke used for drawing shapes. + */ + private Stroke stroke; + /** + * Current transformation matrix. + */ + private AffineTransform transform; + /** + * XOR mode used for rendering. + */ + private Color xorMode; + + public GraphicsState() { + hints = new RenderingHints(null); + background = DEFAULT_BACKGROUND; + color = DEFAULT_COLOR; + clip = DEFAULT_CLIP; + composite = DEFAULT_COMPOSITE; + font = DEFAULT_FONT; + paint = DEFAULT_PAINT; + stroke = DEFAULT_STROKE; + transform = new AffineTransform(DEFAULT_TRANSFORM); + xorMode = DEFAULT_XOR_MODE; + } + + private static Shape transformShape(Shape s, AffineTransform tx) { + if (s == null) { + return null; + } + if (tx == null || tx.isIdentity()) { + return GraphicsUtils.clone(s); + } + boolean isRectangle = s instanceof Rectangle2D; + int nonRectlinearTxMask = AffineTransform.TYPE_GENERAL_TRANSFORM | + AffineTransform.TYPE_GENERAL_ROTATION; + boolean isRectlinearTx = (tx.getType() & nonRectlinearTxMask) == 0; + if (isRectangle && isRectlinearTx) { + Rectangle2D rect = (Rectangle2D) s; + double[] corners = new double[]{ + rect.getMinX(), rect.getMinY(), + rect.getMaxX(), rect.getMaxY() + }; + tx.transform(corners, 0, corners, 0, 2); + rect = new Rectangle2D.Double(); + rect.setFrameFromDiagonal(corners[0], corners[1], corners[2], + corners[3]); + return rect; + } + return tx.createTransformedShape(s); + } + + private static Shape untransformShape(Shape s, AffineTransform tx) { + if (s == null) { + return null; + } + try { + AffineTransform inverse = tx.createInverse(); + return transformShape(s, inverse); + } catch (NoninvertibleTransformException e) { + return null; + } + } + + @Override + public Object clone() throws CloneNotSupportedException { + GraphicsState clone = (GraphicsState) super.clone(); + clone.hints = (RenderingHints) hints.clone(); + clone.clip = GraphicsUtils.clone(clip); + clone.transform = new AffineTransform(transform); + return clone; + } + + public Shape transformShape(Shape shape) { + return transformShape(shape, transform); + } + + public Shape untransformShape(Shape shape) { + return untransformShape(shape, transform); + } + + public RenderingHints getHints() { + return hints; + } + + public Color getBackground() { + return background; + } + + public void setBackground(Color background) { + this.background = background; + } + + public Color getColor() { + return color; + } + + public void setColor(Color color) { + this.color = color; + } + + public Shape getClip() { + return untransformShape(clip); + } + + public void setClip(Shape clip) { + this.clip = transformShape(clip); + } + + public Composite getComposite() { + return composite; + } + + public void setComposite(Composite composite) { + this.composite = composite; + } + + public Font getFont() { + return font; + } + + public void setFont(Font font) { + this.font = font; + } + + public Paint getPaint() { + return paint; + } + + public void setPaint(Paint paint) { + this.paint = paint; + } + + public Stroke getStroke() { + return stroke; + } + + public void setStroke(Stroke stroke) { + this.stroke = stroke; + } + + public AffineTransform getTransform() { + return new AffineTransform(transform); + } + + public void setTransform(AffineTransform tx) { + transform.setTransform(tx); + } + + public Color getXorMode() { + return xorMode; + } + + public void setXorMode(Color xorMode) { + this.xorMode = xorMode; + } + + @Override + public boolean equals(Object obj) { + if (this == obj) { + return true; + } + if (!(obj instanceof GraphicsState)) { + return false; + } + GraphicsState o = (GraphicsState) obj; + return !(!hints.equals(o.hints) || !background.equals(o.background) || + !color.equals(o.color) || !composite.equals(o.composite) || + !font.equals(o.font) || !paint.equals(o.paint) || + !stroke.equals(o.stroke) || !transform.equals(o.transform) || + !xorMode.equals(o.xorMode) || + ((clip == null || o.clip == null) && clip != o.clip) || + (clip != null && !clip.equals(o.clip))); + } + + @Override + public int hashCode() { + return Objects.hash(hints, background, color, composite, font, paint, + stroke, transform, xorMode, clip); + } + + public boolean isDefault() { + return hints.isEmpty() && background.equals(DEFAULT_BACKGROUND) && + color.equals(DEFAULT_COLOR) && composite.equals(DEFAULT_COMPOSITE) && + font.equals(DEFAULT_FONT) && paint.equals(DEFAULT_PAINT) && + stroke.equals(DEFAULT_STROKE) && transform.equals(DEFAULT_TRANSFORM) && + xorMode.equals(DEFAULT_XOR_MODE) && clip == DEFAULT_CLIP; + } +} + diff --git a/chart/src/main/java/org/xbib/graphics/chart/io/vector/PDFGraphics2D.java b/chart/src/main/java/org/xbib/graphics/chart/io/vector/PDFGraphics2D.java new file mode 100644 index 0000000..9a0a8d2 --- /dev/null +++ b/chart/src/main/java/org/xbib/graphics/chart/io/vector/PDFGraphics2D.java @@ -0,0 +1,38 @@ +package org.xbib.graphics.chart.io.vector; + +import org.xbib.graphics.chart.io.vector.pdf.PDFProcessor; + +import java.awt.BasicStroke; +import java.awt.Color; + +/** + * {@code Graphics2D} implementation that saves all operations to a string + * in the Portable Document Format (PDF). + */ +public class PDFGraphics2D extends ProcessingPipeline { + private final Processor processor; + + /** + * Initializes a new VectorGraphics2D pipeline for translating Graphics2D + * commands to PDF data. The document dimensions must be specified as + * parameters. + * + * @param x Left offset. + * @param y Top offset + * @param width Width. + * @param height Height. + */ + public PDFGraphics2D(double x, double y, double width, double height) { + super(x, y, width, height); + processor = new PDFProcessor(); + + // TODO: Default graphics state does not need to be printed in the document + setColor(Color.BLACK); + setStroke(new BasicStroke(1f, BasicStroke.CAP_BUTT, BasicStroke.JOIN_MITER, 10f, null, 0f)); + } + + @Override + protected Processor getProcessor() { + return processor; + } +} diff --git a/chart/src/main/java/org/xbib/graphics/chart/io/vector/ProcessingPipeline.java b/chart/src/main/java/org/xbib/graphics/chart/io/vector/ProcessingPipeline.java new file mode 100644 index 0000000..300b779 --- /dev/null +++ b/chart/src/main/java/org/xbib/graphics/chart/io/vector/ProcessingPipeline.java @@ -0,0 +1,53 @@ +package org.xbib.graphics.chart.io.vector; + +import org.xbib.graphics.chart.io.vector.util.PageSize; + +import java.io.ByteArrayOutputStream; +import java.io.IOException; +import java.io.OutputStream; + +/** + * Base class for convenience implementations of {@code VectorGraphics2D}. + */ +public abstract class ProcessingPipeline extends VectorGraphics2D { + private final PageSize pageSize; + + /** + * Initializes a processing pipeline. + * + * @param x Left offset. + * @param y Top offset + * @param width Width. + * @param height Height. + */ + public ProcessingPipeline(double x, double y, double width, double height) { + pageSize = new PageSize(x, y, width, height); + } + + public PageSize getPageSize() { + return pageSize; + } + + protected abstract Processor getProcessor(); + + public void writeTo(OutputStream out) throws IOException { + Document doc = getProcessor().process(getCommands(), getPageSize()); + doc.write(out); + } + + public byte[] getBytes() { + ByteArrayOutputStream out = new ByteArrayOutputStream(); + try { + writeTo(out); + } catch (IOException e) { + throw new RuntimeException(e); + } finally { + try { + out.close(); + } catch (IOException e) { + e.printStackTrace(); + } + } + return out.toByteArray(); + } +} diff --git a/chart/src/main/java/org/xbib/graphics/chart/io/vector/Processor.java b/chart/src/main/java/org/xbib/graphics/chart/io/vector/Processor.java new file mode 100644 index 0000000..c2e40d9 --- /dev/null +++ b/chart/src/main/java/org/xbib/graphics/chart/io/vector/Processor.java @@ -0,0 +1,9 @@ +package org.xbib.graphics.chart.io.vector; + +import org.xbib.graphics.chart.io.vector.intermediate.commands.Command; +import org.xbib.graphics.chart.io.vector.util.PageSize; + +public interface Processor { + Document process(Iterable> commands, PageSize pageSize); +} + diff --git a/chart/src/main/java/org/xbib/graphics/chart/io/vector/SVGGraphics2D.java b/chart/src/main/java/org/xbib/graphics/chart/io/vector/SVGGraphics2D.java new file mode 100644 index 0000000..bd2084f --- /dev/null +++ b/chart/src/main/java/org/xbib/graphics/chart/io/vector/SVGGraphics2D.java @@ -0,0 +1,36 @@ +package org.xbib.graphics.chart.io.vector; + +import org.xbib.graphics.chart.io.vector.svg.SVGProcessor; + +import java.awt.Color; + +/** + * {@code Graphics2D} implementation that saves all operations to a string + * in the Scaled Vector Graphics (SVG) format. + */ +public class SVGGraphics2D extends ProcessingPipeline { + private final Processor processor; + + /** + * Initializes a new VectorGraphics2D pipeline for translating Graphics2D + * commands to SVG data. The document dimensions must be specified as + * parameters. + * + * @param x Left offset. + * @param y Top offset + * @param width Width. + * @param height Height. + */ + public SVGGraphics2D(double x, double y, double width, double height) { + super(x, y, width, height); + processor = new SVGProcessor(); + + // Make graphics state match default state of Graphics2D + setColor(Color.BLACK); + } + + @Override + protected Processor getProcessor() { + return processor; + } +} diff --git a/chart/src/main/java/org/xbib/graphics/chart/io/vector/SizedDocument.java b/chart/src/main/java/org/xbib/graphics/chart/io/vector/SizedDocument.java new file mode 100644 index 0000000..016cea3 --- /dev/null +++ b/chart/src/main/java/org/xbib/graphics/chart/io/vector/SizedDocument.java @@ -0,0 +1,19 @@ +package org.xbib.graphics.chart.io.vector; + +import org.xbib.graphics.chart.io.vector.util.PageSize; + +public abstract class SizedDocument implements Document { + private final PageSize pageSize; + + public SizedDocument(PageSize pageSize) { + this.pageSize = pageSize; + } + + public PageSize getPageSize() { + return pageSize; + } + + public void close() { + } +} + diff --git a/chart/src/main/java/org/xbib/graphics/chart/io/vector/VectorGraphics2D.java b/chart/src/main/java/org/xbib/graphics/chart/io/vector/VectorGraphics2D.java new file mode 100644 index 0000000..845bba0 --- /dev/null +++ b/chart/src/main/java/org/xbib/graphics/chart/io/vector/VectorGraphics2D.java @@ -0,0 +1,894 @@ +package org.xbib.graphics.chart.io.vector; + +import org.xbib.graphics.chart.io.vector.intermediate.commands.Command; +import org.xbib.graphics.chart.io.vector.intermediate.commands.CreateCommand; +import org.xbib.graphics.chart.io.vector.intermediate.commands.DisposeCommand; +import org.xbib.graphics.chart.io.vector.intermediate.commands.DrawImageCommand; +import org.xbib.graphics.chart.io.vector.intermediate.commands.DrawShapeCommand; +import org.xbib.graphics.chart.io.vector.intermediate.commands.DrawStringCommand; +import org.xbib.graphics.chart.io.vector.intermediate.commands.FillShapeCommand; +import org.xbib.graphics.chart.io.vector.intermediate.commands.RotateCommand; +import org.xbib.graphics.chart.io.vector.intermediate.commands.ScaleCommand; +import org.xbib.graphics.chart.io.vector.intermediate.commands.SetBackgroundCommand; +import org.xbib.graphics.chart.io.vector.intermediate.commands.SetClipCommand; +import org.xbib.graphics.chart.io.vector.intermediate.commands.SetColorCommand; +import org.xbib.graphics.chart.io.vector.intermediate.commands.SetCompositeCommand; +import org.xbib.graphics.chart.io.vector.intermediate.commands.SetFontCommand; +import org.xbib.graphics.chart.io.vector.intermediate.commands.SetHintCommand; +import org.xbib.graphics.chart.io.vector.intermediate.commands.SetPaintCommand; +import org.xbib.graphics.chart.io.vector.intermediate.commands.SetStrokeCommand; +import org.xbib.graphics.chart.io.vector.intermediate.commands.SetTransformCommand; +import org.xbib.graphics.chart.io.vector.intermediate.commands.SetXORModeCommand; +import org.xbib.graphics.chart.io.vector.intermediate.commands.ShearCommand; +import org.xbib.graphics.chart.io.vector.intermediate.commands.TransformCommand; +import org.xbib.graphics.chart.io.vector.intermediate.commands.TranslateCommand; +import org.xbib.graphics.chart.io.vector.util.GraphicsUtils; + +import java.awt.AlphaComposite; +import java.awt.Color; +import java.awt.Composite; +import java.awt.Font; +import java.awt.FontMetrics; +import java.awt.Graphics; +import java.awt.Graphics2D; +import java.awt.GraphicsConfiguration; +import java.awt.Image; +import java.awt.Paint; +import java.awt.Polygon; +import java.awt.Rectangle; +import java.awt.RenderingHints; +import java.awt.RenderingHints.Key; +import java.awt.Shape; +import java.awt.Stroke; +import java.awt.font.FontRenderContext; +import java.awt.font.GlyphVector; +import java.awt.font.TextLayout; +import java.awt.geom.AffineTransform; +import java.awt.geom.Arc2D; +import java.awt.geom.Area; +import java.awt.geom.Ellipse2D; +import java.awt.geom.Line2D; +import java.awt.geom.Path2D; +import java.awt.geom.Rectangle2D; +import java.awt.geom.RoundRectangle2D; +import java.awt.image.AffineTransformOp; +import java.awt.image.BufferedImage; +import java.awt.image.BufferedImageOp; +import java.awt.image.ImageObserver; +import java.awt.image.RenderedImage; +import java.awt.image.renderable.RenderableImage; +import java.text.AttributedCharacterIterator; +import java.util.LinkedList; +import java.util.List; +import java.util.Map; +import java.util.Map.Entry; + +/** + * Base for classes that want to implement vector export. + */ +public class VectorGraphics2D extends Graphics2D implements Cloneable { + /** + * List of operations that were performed on this graphics object and its + * derived objects. + */ + private final List> commands; + /** + * Device configuration settings. + */ + //private final GraphicsConfiguration deviceConfig; + /** + * Context settings used to render fonts. + */ + private final FontRenderContext fontRenderContext; + /** + * Flag that tells whether this graphics object has been disposed. + */ + private boolean disposed; + + private GraphicsState state; + + private Graphics2D _debug_validate_graphics; + + public VectorGraphics2D() { + commands = new LinkedList>(); + emit(new CreateCommand(this)); + fontRenderContext = new FontRenderContext(null, false, true); + + state = new GraphicsState(); + + BufferedImage _debug_validate_bimg = new BufferedImage(200, 250, BufferedImage.TYPE_INT_ARGB); + _debug_validate_graphics = (Graphics2D) _debug_validate_bimg.getGraphics(); + _debug_validate_graphics.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON); + } + + private static Shape intersectShapes(Shape s1, Shape s2) { + if (s1 instanceof Rectangle2D && s2 instanceof Rectangle2D) { + Rectangle2D r1 = (Rectangle2D) s1; + Rectangle2D r2 = (Rectangle2D) s2; + double x1 = Math.max(r1.getMinX(), r2.getMinX()); + double y1 = Math.max(r1.getMinY(), r2.getMinY()); + double x2 = Math.min(r1.getMaxX(), r2.getMaxX()); + double y2 = Math.min(r1.getMaxY(), r2.getMaxY()); + + Rectangle2D intersection = new Rectangle2D.Double(); + if ((x2 < x1) || (y2 < y1)) { + intersection.setFrameFromDiagonal(0, 0, 0, 0); + } else { + intersection.setFrameFromDiagonal(x1, y1, x2, y2); + } + return intersection; + } else { + Area intersection = new Area(s1); + intersection.intersect(new Area(s2)); + return intersection; + } + } + + @Override + public Object clone() throws CloneNotSupportedException { + try { + VectorGraphics2D clone = (VectorGraphics2D) super.clone(); + clone.state = (GraphicsState) state.clone(); + return clone; + } catch (CloneNotSupportedException e) { + return null; + } + } + + @Override + public void addRenderingHints(Map hints) { + if (isDisposed()) { + return; + } + for (Entry entry : hints.entrySet()) { + setRenderingHint((Key) entry.getKey(), entry.getValue()); + } + } + + @Override + public void clip(Shape s) { + _debug_validate_graphics.clip(s); + Shape clipOld = getClip(); + + Shape clip = getClip(); + if ((clip != null) && (s != null)) { + s = intersectShapes(clip, s); + } + setClip(s); + + Shape clipNew = getClip(); + if ((clipNew == null || _debug_validate_graphics.getClip() == null) && clipNew != _debug_validate_graphics.getClip()) { + System.err.println("clip() validation failed: clip(" + clipOld + ", " + s + ") => " + clipNew + " != " + _debug_validate_graphics.getClip()); + } + if (clipNew != null && !GraphicsUtils.equals(clipNew, _debug_validate_graphics.getClip())) { + System.err.println("clip() validation failed: clip(" + clipOld + ", " + s + ") => " + clipNew + " != " + _debug_validate_graphics.getClip()); + } + } + + @Override + public void draw(Shape s) { + if (isDisposed() || s == null) { + return; + } + emit(new DrawShapeCommand(s)); + + _debug_validate_graphics.draw(s); + } + + @Override + public void drawGlyphVector(GlyphVector g, float x, float y) { + Shape s = g.getOutline(x, y); + draw(s); + } + + @Override + public boolean drawImage(Image img, AffineTransform xform, ImageObserver obs) { + BufferedImage bimg = getTransformedImage(img, xform); + return drawImage(bimg, bimg.getMinX(), bimg.getMinY(), + bimg.getWidth(), bimg.getHeight(), null, null); + } + + /** + * Returns a transformed version of an image. + * + * @param image Image to be transformed + * @param xform Affine transform to be applied + * @return Image with transformed content + */ + private BufferedImage getTransformedImage(Image image, + AffineTransform xform) { + Integer interpolationType = + (Integer) getRenderingHint(RenderingHints.KEY_INTERPOLATION); + if (RenderingHints.VALUE_INTERPOLATION_NEAREST_NEIGHBOR + .equals(interpolationType)) { + interpolationType = AffineTransformOp.TYPE_NEAREST_NEIGHBOR; + } else if (RenderingHints.VALUE_INTERPOLATION_BILINEAR + .equals(interpolationType)) { + interpolationType = AffineTransformOp.TYPE_BILINEAR; + } else { + interpolationType = AffineTransformOp.TYPE_BICUBIC; + } + AffineTransformOp op = new AffineTransformOp(xform, interpolationType); + BufferedImage bufferedImage = GraphicsUtils.toBufferedImage(image); + return op.filter(bufferedImage, null); + } + + @Override + public void drawImage(BufferedImage img, BufferedImageOp op, int x, int y) { + if (op != null) { + img = op.filter(img, null); + } + drawImage(img, x, y, img.getWidth(), img.getHeight(), null, null); + } + + @Override + public void drawRenderableImage(RenderableImage img, AffineTransform xform) { + drawRenderedImage(img.createDefaultRendering(), xform); + } + + @Override + public void drawRenderedImage(RenderedImage img, AffineTransform xform) { + BufferedImage bimg = GraphicsUtils.toBufferedImage(img); + drawImage(bimg, xform, null); + } + + @Override + public void drawString(String str, int x, int y) { + drawString(str, (float) x, (float) y); + } + + @Override + public void drawString(String str, float x, float y) { + if (isDisposed() || str == null || str.trim().length() == 0) { + return; + } + boolean isTextAsVectors = false; + if (isTextAsVectors) { + TextLayout layout = new TextLayout(str, getFont(), + getFontRenderContext()); + Shape s = layout.getOutline( + AffineTransform.getTranslateInstance(x, y)); + fill(s); + } else { + emit(new DrawStringCommand(str, x, y)); + + _debug_validate_graphics.drawString(str, x, y); + } + + } + + @Override + public void drawString(AttributedCharacterIterator iterator, int x, int y) { + drawString(iterator, (float) x, (float) y); + } + + @Override + public void drawString(AttributedCharacterIterator iterator, float x, + float y) { + // TODO Draw styled text + StringBuilder buf = new StringBuilder(); + for (char c = iterator.first(); c != AttributedCharacterIterator.DONE; + c = iterator.next()) { + buf.append(c); + } + drawString(buf.toString(), x, y); + } + + @Override + public void fill(Shape s) { + if (isDisposed() || s == null) { + return; + } + emit(new FillShapeCommand(s)); + + _debug_validate_graphics.fill(s); + } + + @Override + public Color getBackground() { + return state.getBackground(); + } + + @Override + public void setBackground(Color color) { + if (isDisposed() || color == null || getColor().equals(color)) { + return; + } + emit(new SetBackgroundCommand(color)); + state.setBackground(color); + + _debug_validate_graphics.setBackground(color); + if (!getBackground().equals(_debug_validate_graphics.getBackground())) { + System.err.println("setBackground() validation failed"); + } + } + + @Override + public Composite getComposite() { + return state.getComposite(); + } + + @Override + public void setComposite(Composite comp) { + if (isDisposed()) { + return; + } + if (comp == null) { + throw new IllegalArgumentException("Cannot set a null composite."); + } + emit(new SetCompositeCommand(comp)); + state.setComposite(comp); + + _debug_validate_graphics.setComposite(comp); + if (!getComposite().equals(_debug_validate_graphics.getComposite())) { + System.err.println("setComposite() validation failed"); + } + } + + @Override + public GraphicsConfiguration getDeviceConfiguration() { + throw new UnsupportedOperationException(); + } + + @Override + public FontRenderContext getFontRenderContext() { + return fontRenderContext; + } + + @Override + public Paint getPaint() { + return state.getPaint(); + } + + @Override + public void setPaint(Paint paint) { + if (isDisposed() || paint == null) { + return; + } + if (paint instanceof Color) { + setColor((Color) paint); + return; + } + if (getPaint().equals(paint)) { + return; + } + emit(new SetPaintCommand(paint)); + state.setPaint(paint); + + _debug_validate_graphics.setPaint(paint); + if (!getPaint().equals(_debug_validate_graphics.getPaint())) { + System.err.println("setPaint() validation failed"); + } + } + + @Override + public Object getRenderingHint(Key hintKey) { + if (RenderingHints.KEY_ANTIALIASING.equals(hintKey)) { + return RenderingHints.VALUE_ANTIALIAS_OFF; + } else if (RenderingHints.KEY_TEXT_ANTIALIASING.equals(hintKey)) { + return RenderingHints.VALUE_TEXT_ANTIALIAS_OFF; + } else if (RenderingHints.KEY_FRACTIONALMETRICS.equals(hintKey)) { + return RenderingHints.VALUE_FRACTIONALMETRICS_ON; + } + return state.getHints().get(hintKey); + } + + @Override + public RenderingHints getRenderingHints() { + return (RenderingHints) state.getHints().clone(); + } + + @Override + public void setRenderingHints(Map hints) { + if (isDisposed()) { + return; + } + state.getHints().clear(); + for (Entry hint : hints.entrySet()) { + setRenderingHint((Key) hint.getKey(), hint.getValue()); + } + } + + @Override + public Stroke getStroke() { + return state.getStroke(); + } + + @Override + public void setStroke(Stroke s) { + if (isDisposed()) { + return; + } + if (s == null) { + throw new IllegalArgumentException("Cannot set a null stroke."); + } + emit(new SetStrokeCommand(s)); + state.setStroke(s); + + _debug_validate_graphics.setStroke(s); + if (!getStroke().equals(_debug_validate_graphics.getStroke())) { + System.err.println("setStroke() validation failed"); + } + } + + @Override + public boolean hit(Rectangle rect, Shape s, boolean onStroke) { + Shape hitShape = s; + if (onStroke) { + hitShape = getStroke().createStrokedShape(hitShape); + } + hitShape = state.transformShape(hitShape); + boolean hit = hitShape.intersects(rect); + + boolean _debug_hit = _debug_validate_graphics.hit(rect, s, onStroke); + if (hit != _debug_hit) { + System.err.println("setClip() validation failed"); + } + + return hit; + } + + @Override + public void setRenderingHint(Key hintKey, Object hintValue) { + if (isDisposed()) { + return; + } + state.getHints().put(hintKey, hintValue); + emit(new SetHintCommand(hintKey, hintValue)); + } + + @Override + public AffineTransform getTransform() { + return new AffineTransform(state.getTransform()); + } + + @Override + public void setTransform(AffineTransform tx) { + if (isDisposed() || tx == null || state.getTransform().equals(tx)) { + return; + } + emit(new SetTransformCommand(tx)); + state.setTransform(tx); + + _debug_validate_graphics.setTransform(tx); + if (!getTransform().equals(_debug_validate_graphics.getTransform())) { + System.err.println("setTransform() validation failed"); + } + } + + @Override + public void shear(double shx, double shy) { + if (shx == 0.0 && shy == 0.0) { + return; + } + AffineTransform txNew = getTransform(); + txNew.shear(shx, shy); + emit(new ShearCommand(shx, shy)); + state.setTransform(txNew); + + _debug_validate_graphics.shear(shx, shy); + if (!getTransform().equals(_debug_validate_graphics.getTransform())) { + System.err.println("shear() validation failed"); + } + } + + @Override + public void transform(AffineTransform tx) { + if (tx.isIdentity()) { + return; + } + AffineTransform txNew = getTransform(); + txNew.concatenate(tx); + emit(new TransformCommand(tx)); + state.setTransform(txNew); + + _debug_validate_graphics.transform(tx); + if (!getTransform().equals(_debug_validate_graphics.getTransform())) { + System.err.println("transform() validation failed"); + } + } + + @Override + public void translate(int x, int y) { + translate((double) x, (double) y); + } + + @Override + public void translate(double tx, double ty) { + if (tx == 0.0 && ty == 0.0) { + return; + } + AffineTransform txNew = getTransform(); + txNew.translate(tx, ty); + emit(new TranslateCommand(tx, ty)); + state.setTransform(txNew); + + _debug_validate_graphics.translate(tx, ty); + if (!getTransform().equals(_debug_validate_graphics.getTransform())) { + System.err.println("translate() validation failed"); + } + } + + @Override + public void rotate(double theta) { + rotate(theta, 0.0, 0.0); + } + + @Override + public void rotate(double theta, double x, double y) { + if (theta == 0.0) { + return; + } + + AffineTransform txNew = getTransform(); + if (x == 0.0 && y == 0.0) { + txNew.rotate(theta); + } else { + txNew.rotate(theta, x, y); + } + + emit(new RotateCommand(theta, x, y)); + state.setTransform(txNew); + + if (x == 0.0 && y == 0.0) { + _debug_validate_graphics.rotate(theta); + if (!getTransform().equals(_debug_validate_graphics.getTransform())) { + System.err.println("rotate(theta) validation failed"); + } + } else { + _debug_validate_graphics.rotate(theta, x, y); + if (!getTransform().equals(_debug_validate_graphics.getTransform())) { + System.err.println("rotate(theta,x,y) validation failed"); + } + } + } + + @Override + public void scale(double sx, double sy) { + if (sx == 1.0 && sy == 1.0) { + return; + } + AffineTransform txNew = getTransform(); + txNew.scale(sx, sy); + emit(new ScaleCommand(sx, sy)); + state.setTransform(txNew); + + _debug_validate_graphics.scale(sx, sy); + if (!getTransform().equals(_debug_validate_graphics.getTransform())) { + System.err.println("scale() validation failed"); + } + } + + @Override + public void clearRect(int x, int y, int width, int height) { + Color colorOld = getColor(); + setColor(getBackground()); + fillRect(x, y, width, height); + setColor(colorOld); + } + + @Override + public void clipRect(int x, int y, int width, int height) { + clip(new Rectangle(x, y, width, height)); + } + + @Override + public void copyArea(int x, int y, int width, int height, int dx, int dy) { + // TODO Implement + //throw new UnsupportedOperationException("copyArea() isn't supported by VectorGraphics2D."); + } + + @Override + public Graphics create() { + if (isDisposed()) { + return null; + } + VectorGraphics2D clone = null; + try { + clone = (VectorGraphics2D) this.clone(); + emit(new CreateCommand(clone)); + } catch (CloneNotSupportedException e) { + e.printStackTrace(); + } + + if (clone != null) { + clone._debug_validate_graphics = (Graphics2D) _debug_validate_graphics.create(); + } + + return clone; + } + + @Override + public void dispose() { + if (isDisposed()) { + return; + } + + emit(new DisposeCommand(this)); + + disposed = true; + + _debug_validate_graphics.dispose(); + } + + @Override + public void drawArc(int x, int y, int width, int height, int startAngle, + int arcAngle) { + draw(new Arc2D.Double(x, y, width, height, + startAngle, arcAngle, Arc2D.OPEN)); + } + + @Override + public boolean drawImage(Image img, int x, int y, ImageObserver observer) { + return drawImage(img, x, y, img.getWidth(observer), + img.getHeight(observer), null, observer); + } + + @Override + public boolean drawImage(Image img, int x, int y, Color bgcolor, + ImageObserver observer) { + return drawImage(img, x, y, img.getWidth(observer), + img.getHeight(observer), bgcolor, observer); + } + + @Override + public boolean drawImage(Image img, int x, int y, int width, int height, + ImageObserver observer) { + return drawImage(img, x, y, width, height, null, observer); + } + + @Override + public boolean drawImage(Image img, int x, int y, int width, int height, + Color bgcolor, ImageObserver observer) { + if (isDisposed() || img == null) { + return true; + } + + int imageWidth = img.getWidth(observer); + int imageHeight = img.getHeight(observer); + Rectangle bounds = new Rectangle(x, y, width, height); + + if (bgcolor != null) { + // Fill rectangle with bgcolor + Color bgcolorOld = getColor(); + setColor(bgcolor); + fill(bounds); + setColor(bgcolorOld); + } + + emit(new DrawImageCommand(img, imageWidth, imageHeight, x, y, width, height)); + + _debug_validate_graphics.drawImage(img, x, y, width, height, bgcolor, observer); + + return true; + } + + @Override + public boolean drawImage(Image img, int dx1, int dy1, int dx2, int dy2, + int sx1, int sy1, int sx2, int sy2, ImageObserver observer) { + return drawImage(img, dx1, dy1, dx2, dy2, sx1, sy1, sx2, sy2, null, + observer); + } + + @Override + public boolean drawImage(Image img, int dx1, int dy1, int dx2, int dy2, + int sx1, int sy1, int sx2, int sy2, Color bgcolor, + ImageObserver observer) { + if (img == null) { + return true; + } + + int sx = Math.min(sx1, sx2); + int sy = Math.min(sy1, sy2); + int sw = Math.abs(sx2 - sx1); + int sh = Math.abs(sy2 - sy1); + int dx = Math.min(dx1, dx2); + int dy = Math.min(dy1, dy2); + int dw = Math.abs(dx2 - dx1); + int dh = Math.abs(dy2 - dy1); + + // Draw image on rectangle + BufferedImage bufferedImg = GraphicsUtils.toBufferedImage(img); + Image cropped = bufferedImg.getSubimage(sx, sy, sw, sh); + return drawImage(cropped, dx, dy, dw, dh, bgcolor, observer); + } + + @Override + public void drawLine(int x1, int y1, int x2, int y2) { + draw(new Line2D.Double(x1, y1, x2, y2)); + } + + @Override + public void drawOval(int x, int y, int width, int height) { + draw(new Ellipse2D.Double(x, y, width, height)); + } + + @Override + public void drawPolygon(Polygon p) { + draw(p); + } + + @Override + public void drawPolygon(int[] xPoints, int[] yPoints, int nPoints) { + draw(new Polygon(xPoints, yPoints, nPoints)); + } + + @Override + public void drawPolyline(int[] xPoints, int[] yPoints, int nPoints) { + Path2D p = new Path2D.Float(); + for (int i = 0; i < nPoints; i++) { + if (i > 0) { + p.lineTo(xPoints[i], yPoints[i]); + } else { + p.moveTo(xPoints[i], yPoints[i]); + } + } + draw(p); + } + + @Override + public void drawRect(int x, int y, int width, int height) { + draw(new Rectangle(x, y, width, height)); + } + + @Override + public void drawRoundRect(int x, int y, int width, int height, + int arcWidth, int arcHeight) { + draw(new RoundRectangle2D.Double(x, y, width, height, + arcWidth, arcHeight)); + } + + @Override + public void fillArc(int x, int y, int width, int height, + int startAngle, int arcAngle) { + fill(new Arc2D.Double(x, y, width, height, + startAngle, arcAngle, Arc2D.PIE)); + } + + @Override + public void fillOval(int x, int y, int width, int height) { + fill(new Ellipse2D.Double(x, y, width, height)); + } + + @Override + public void fillPolygon(Polygon p) { + fill(p); + } + + @Override + public void fillPolygon(int[] xPoints, int[] yPoints, int nPoints) { + fill(new Polygon(xPoints, yPoints, nPoints)); + } + + @Override + public void fillRect(int x, int y, int width, int height) { + fill(new Rectangle(x, y, width, height)); + } + + @Override + public void fillRoundRect(int x, int y, int width, int height, + int arcWidth, int arcHeight) { + fill(new RoundRectangle2D.Double(x, y, width, height, + arcWidth, arcHeight)); + } + + @Override + public Shape getClip() { + return state.getClip(); + } + + @Override + public void setClip(Shape clip) { + if (isDisposed()) { + return; + } + emit(new SetClipCommand(clip)); + state.setClip(clip); + + _debug_validate_graphics.setClip(clip); + if (getClip() == null) { + if (_debug_validate_graphics.getClip() != null) { + System.err.printf("setClip() validation failed: clip=null, validation=%s\n", _debug_validate_graphics.getClip()); + } + } else if (!GraphicsUtils.equals(getClip(), _debug_validate_graphics.getClip())) { + System.err.printf("setClip() validation failed: clip=%s, validation=%s\n", getClip(), _debug_validate_graphics.getClip()); + } + } + + @Override + public Rectangle getClipBounds() { + if (getClip() == null) { + return null; + } + return getClip().getBounds(); + } + + @Override + public Color getColor() { + return state.getColor(); + } + + @Override + public void setColor(Color c) { + if (isDisposed() || c == null || getColor().equals(c)) { + return; + } + emit(new SetColorCommand(c)); + state.setColor(c); + state.setPaint(c); + + _debug_validate_graphics.setColor(c); + if (!getColor().equals(_debug_validate_graphics.getColor())) { + System.err.println("setColor() validation failed"); + } + } + + @Override + public Font getFont() { + return state.getFont(); + } + + @Override + public void setFont(Font font) { + if (isDisposed() || (font != null && getFont().equals(font))) { + return; + } + emit(new SetFontCommand(font)); + state.setFont(font); + + _debug_validate_graphics.setFont(font); + if (!getFont().equals(_debug_validate_graphics.getFont())) { + System.err.println("setFont() validation failed"); + } + } + + @Override + public FontMetrics getFontMetrics(Font f) { + BufferedImage bi = + new BufferedImage(1, 1, BufferedImage.TYPE_INT_ARGB_PRE); + Graphics g = bi.getGraphics(); + FontMetrics fontMetrics = g.getFontMetrics(getFont()); + g.dispose(); + return fontMetrics; + } + + @Override + public void setClip(int x, int y, int width, int height) { + setClip(new Rectangle(x, y, width, height)); + } + + @Override + public void setPaintMode() { + setComposite(AlphaComposite.SrcOver); + + _debug_validate_graphics.setPaintMode(); + } + + public Color getXORMode() { + return state.getXorMode(); + } + + @Override + public void setXORMode(Color c1) { + if (isDisposed() || c1 == null) { + return; + } + emit(new SetXORModeCommand(c1)); + state.setXorMode(c1); + + _debug_validate_graphics.setXORMode(c1); + } + + private void emit(Command command) { + commands.add(command); + } + + protected Iterable> getCommands() { + return commands; + } + + protected boolean isDisposed() { + return disposed; + } +} + diff --git a/chart/src/main/java/org/xbib/graphics/chart/io/vector/VectorHints.java b/chart/src/main/java/org/xbib/graphics/chart/io/vector/VectorHints.java new file mode 100644 index 0000000..8e8ae9f --- /dev/null +++ b/chart/src/main/java/org/xbib/graphics/chart/io/vector/VectorHints.java @@ -0,0 +1,83 @@ +package org.xbib.graphics.chart.io.vector; + +import java.awt.RenderingHints; +import java.util.HashSet; +import java.util.Set; + +public abstract class VectorHints { + public static final Key KEY_EXPORT = new Key(0, "Vector export mode"); + public static final Object VALUE_EXPORT_READABILITY = new Value(KEY_EXPORT, 0, "Maximize readability for humans"); + public static final Object VALUE_EXPORT_QUALITY = new Value(KEY_EXPORT, 1, "Maximize render quality"); + public static final Object VALUE_EXPORT_SIZE = new Value(KEY_EXPORT, 2, "Minimize data size"); + public static final Key KEY_TEXT = new Key(1, "Text export mode"); + public static final Object VALUE_TEXT_DEFAULT = new Value(KEY_TEXT, 0, "Keep text"); + public static final Object VALUE_TEXT_VECTOR = new Value(KEY_TEXT, 1, "Convert text to vector shapes"); + + protected VectorHints() { + throw new UnsupportedOperationException(); + } + + public static class Key extends RenderingHints.Key { + private final String description; + + public Key(int privateKey, String description) { + super(privateKey); + this.description = description; + } + + public int getIndex() { + return intKey(); + } + + @Override + public boolean isCompatibleValue(Object val) { + return val instanceof Value && ((Value) val).isCompatibleKey(this); + } + + @Override + public String toString() { + return description; + } + } + + public static class Value { + private static final Set values = new HashSet(); + private final Key key; + private final int index; + private final String description; + + public Value(Key key, int index, String description) { + this.key = key; + this.index = index; + this.description = description; + register(this); + } + + private synchronized static void register(Value value) { + String id = value.getId(); + if (values.contains(id)) { + throw new ExceptionInInitializerError( + "Duplicate index: " + value.getIndex()); + } + values.add(id); + } + + public boolean isCompatibleKey(RenderingHints.Key key) { + return this.key == key; + } + + public int getIndex() { + return index; + } + + public String getId() { + return key.getIndex() + ":" + getIndex(); + } + + @Override + public String toString() { + return description; + } + } +} + diff --git a/chart/src/main/java/org/xbib/graphics/chart/io/vector/eps/EPSDocument.java b/chart/src/main/java/org/xbib/graphics/chart/io/vector/eps/EPSDocument.java new file mode 100644 index 0000000..1ab1d44 --- /dev/null +++ b/chart/src/main/java/org/xbib/graphics/chart/io/vector/eps/EPSDocument.java @@ -0,0 +1,476 @@ +package org.xbib.graphics.chart.io.vector.eps; + +import org.xbib.graphics.chart.io.vector.GraphicsState; +import org.xbib.graphics.chart.io.vector.SizedDocument; +import org.xbib.graphics.chart.io.vector.intermediate.commands.Command; +import org.xbib.graphics.chart.io.vector.intermediate.commands.CreateCommand; +import org.xbib.graphics.chart.io.vector.intermediate.commands.DisposeCommand; +import org.xbib.graphics.chart.io.vector.intermediate.commands.DrawImageCommand; +import org.xbib.graphics.chart.io.vector.intermediate.commands.DrawShapeCommand; +import org.xbib.graphics.chart.io.vector.intermediate.commands.DrawStringCommand; +import org.xbib.graphics.chart.io.vector.intermediate.commands.FillShapeCommand; +import org.xbib.graphics.chart.io.vector.intermediate.commands.RotateCommand; +import org.xbib.graphics.chart.io.vector.intermediate.commands.ScaleCommand; +import org.xbib.graphics.chart.io.vector.intermediate.commands.SetClipCommand; +import org.xbib.graphics.chart.io.vector.intermediate.commands.SetColorCommand; +import org.xbib.graphics.chart.io.vector.intermediate.commands.SetCompositeCommand; +import org.xbib.graphics.chart.io.vector.intermediate.commands.SetFontCommand; +import org.xbib.graphics.chart.io.vector.intermediate.commands.SetPaintCommand; +import org.xbib.graphics.chart.io.vector.intermediate.commands.SetStrokeCommand; +import org.xbib.graphics.chart.io.vector.intermediate.commands.SetTransformCommand; +import org.xbib.graphics.chart.io.vector.intermediate.commands.ShearCommand; +import org.xbib.graphics.chart.io.vector.intermediate.commands.TransformCommand; +import org.xbib.graphics.chart.io.vector.intermediate.commands.TranslateCommand; +import org.xbib.graphics.chart.io.vector.util.ASCII85EncodeStream; +import org.xbib.graphics.chart.io.vector.util.AlphaToMaskOp; +import org.xbib.graphics.chart.io.vector.util.DataUtils; +import org.xbib.graphics.chart.io.vector.util.FlateEncodeStream; +import org.xbib.graphics.chart.io.vector.util.GraphicsUtils; +import org.xbib.graphics.chart.io.vector.util.ImageDataStream; +import org.xbib.graphics.chart.io.vector.util.ImageDataStream.Interleaving; +import org.xbib.graphics.chart.io.vector.util.LineWrapOutputStream; +import org.xbib.graphics.chart.io.vector.util.PageSize; + +import java.awt.BasicStroke; +import java.awt.Color; +import java.awt.Font; +import java.awt.Image; +import java.awt.Shape; +import java.awt.Stroke; +import java.awt.geom.Arc2D; +import java.awt.geom.Ellipse2D; +import java.awt.geom.Line2D; +import java.awt.geom.PathIterator; +import java.awt.geom.Rectangle2D; +import java.awt.image.BufferedImage; +import java.io.ByteArrayOutputStream; +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; +import java.io.OutputStreamWriter; +import java.util.Arrays; +import java.util.LinkedList; +import java.util.List; +import java.util.Map; +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +public class EPSDocument extends SizedDocument { + /** + * Constant to convert values from millimeters to PostScript® units + * (1/72th inch). + */ + private static final double UNITS_PER_MM = 72.0 / 25.4; + private static final String CHARSET = "ISO-8859-1"; + private static final String EOL = "\n"; + private static final int MAX_LINE_WIDTH = 255; + private static final Pattern ELEMENT_SEPARATION_PATTERN = Pattern.compile("(.{1," + MAX_LINE_WIDTH + "})(\\s+|$)"); + + /** + * Mapping of stroke endcap values from Java to PostScript®. + */ + private static final Map STROKE_ENDCAPS = DataUtils.map( + new Integer[]{BasicStroke.CAP_BUTT, BasicStroke.CAP_ROUND, BasicStroke.CAP_SQUARE}, + new Integer[]{0, 1, 2} + ); + + /** + * Mapping of line join values for path drawing from Java to + * PostScript®. + */ + private static final Map STROKE_LINEJOIN = DataUtils.map( + new Integer[]{BasicStroke.JOIN_MITER, BasicStroke.JOIN_ROUND, BasicStroke.JOIN_BEVEL}, + new Integer[]{0, 1, 2} + ); + + private static final String FONT_LATIN1_SUFFIX = "Lat"; + + private final List elements; + + public EPSDocument(PageSize pageSize) { + super(pageSize); + elements = new LinkedList(); + addHeader(); + } + + private static String getOutput(Color c) { + // TODO Handle transparency + return String.valueOf(c.getRed() / 255.0) + " " + c.getGreen() / 255.0 + " " + c.getBlue() / 255.0 + " rgb"; + } + + private static String getOutput(Shape s) { + StringBuilder out = new StringBuilder(); + out.append("newpath "); + if (s instanceof Line2D) { + Line2D l = (Line2D) s; + out.append(l.getX1()).append(" ").append(l.getY1()).append(" M ") + .append(l.getX2()).append(" ").append(l.getY2()).append(" L"); + } else if (s instanceof Rectangle2D) { + Rectangle2D r = (Rectangle2D) s; + out.append(r.getX()).append(" ").append(r.getY()).append(" ") + .append(r.getWidth()).append(" ").append(r.getHeight()) + .append(" rect Z"); + } else if (s instanceof Ellipse2D) { + Ellipse2D e = (Ellipse2D) s; + double x = e.getX() + e.getWidth() / 2.0; + double y = e.getY() + e.getHeight() / 2.0; + double rx = e.getWidth() / 2.0; + double ry = e.getHeight() / 2.0; + out.append(x).append(" ").append(y).append(" ") + .append(rx).append(" ").append(ry).append(" ") + .append(360.0).append(" ").append(0.0) + .append(" ellipse Z"); + } else if (s instanceof Arc2D) { + Arc2D e = (Arc2D) s; + double x = (e.getX() + e.getWidth() / 2.0); + double y = (e.getY() + e.getHeight() / 2.0); + double rx = e.getWidth() / 2.0; + double ry = e.getHeight() / 2.0; + double startAngle = -e.getAngleStart(); + double endAngle = -(e.getAngleStart() + e.getAngleExtent()); + out.append(x).append(" ").append(y).append(" ") + .append(rx).append(" ").append(ry).append(" ") + .append(startAngle).append(" ").append(endAngle) + .append(" ellipse"); + if (e.getArcType() == Arc2D.CHORD) { + out.append(" Z"); + } else if (e.getArcType() == Arc2D.PIE) { + out.append(" ").append(x).append(" ").append(y).append(" L Z"); + } + } else { + PathIterator segments = s.getPathIterator(null); + double[] coordsCur = new double[6]; + double[] pointPrev = new double[2]; + for (int i = 0; !segments.isDone(); i++, segments.next()) { + if (i > 0) { + out.append(" "); + } + int segmentType = segments.currentSegment(coordsCur); + switch (segmentType) { + case PathIterator.SEG_MOVETO: + out.append(coordsCur[0]).append(" ").append(coordsCur[1]) + .append(" M"); + pointPrev[0] = coordsCur[0]; + pointPrev[1] = coordsCur[1]; + break; + case PathIterator.SEG_LINETO: + out.append(coordsCur[0]).append(" ").append(coordsCur[1]) + .append(" L"); + pointPrev[0] = coordsCur[0]; + pointPrev[1] = coordsCur[1]; + break; + case PathIterator.SEG_CUBICTO: + out.append(coordsCur[0]).append(" ").append(coordsCur[1]) + .append(" ").append(coordsCur[2]).append(" ") + .append(coordsCur[3]).append(" ").append(coordsCur[4]) + .append(" ").append(coordsCur[5]).append(" C"); + pointPrev[0] = coordsCur[4]; + pointPrev[1] = coordsCur[5]; + break; + case PathIterator.SEG_QUADTO: + double x1 = pointPrev[0] + 2.0 / 3.0 * (coordsCur[0] - pointPrev[0]); + double y1 = pointPrev[1] + 2.0 / 3.0 * (coordsCur[1] - pointPrev[1]); + double x2 = coordsCur[0] + 1.0 / 3.0 * (coordsCur[2] - coordsCur[0]); + double y2 = coordsCur[1] + 1.0 / 3.0 * (coordsCur[3] - coordsCur[1]); + double x3 = coordsCur[2]; + double y3 = coordsCur[3]; + out.append(x1).append(" ").append(y1).append(" ") + .append(x2).append(" ").append(y2).append(" ") + .append(x3).append(" ").append(y3).append(" C"); + pointPrev[0] = x3; + pointPrev[1] = y3; + break; + case PathIterator.SEG_CLOSE: + out.append("Z"); + break; + default: + throw new IllegalStateException("Unknown path operation."); + } + } + } + return out.toString(); + } + + private static String getOutput(Image image, int imageWidth, int imageHeight, + double x, double y, double width, double height) { + StringBuilder out = new StringBuilder(); + + BufferedImage bufferedImage = GraphicsUtils.toBufferedImage(image); + int bands = bufferedImage.getSampleModel().getNumBands(); + int bitsPerSample = DataUtils.max(bufferedImage.getSampleModel().getSampleSize()); + bitsPerSample = (int) (Math.ceil(bitsPerSample / 8.0) * 8.0); + if (bands > 3) { + bands = 3; + } + + out.append("gsave").append(EOL); + if (x != 0.0 || y != 0.0) { + out.append(x).append(" ").append(y).append(" translate").append(EOL); + } + if (width != 1.0 || height != 1.0) { + out.append(width).append(" ").append(height).append(" scale").append(EOL); + } + + int decodeScale = 1; + if (bufferedImage.getColorModel().hasAlpha()) { + // TODO Use different InterleaveType (2 or 3) for more efficient compression + out.append("<< /ImageType 3 /InterleaveType 1 ") + .append("/MaskDict ") + .append(imageWidth).append(" ").append(imageHeight).append(" ") + .append(1).append(" ").append(bitsPerSample).append(" ").append(decodeScale).append(" ") + .append(false).append(" ").append(0).append(" imgdict ") + .append("/DataDict ") + .append(imageWidth).append(" ").append(imageHeight).append(" ") + .append(bands).append(" ").append(bitsPerSample).append(" ").append(decodeScale).append(" ") + .append(true).append(" currentfile /ASCII85Decode filter ") + .append("<< /BitsPerComponent ").append(bitsPerSample).append(" >> ") + .append("/FlateDecode filter ") + .append("imgdict ") + .append(">> image").append(EOL); + + // Convert alpha values to binary mask + // FIXME Do alpha conversion in a preprocessing step on commands + bufferedImage = new AlphaToMaskOp(true).filter(bufferedImage, null); + output(bufferedImage, out); + } else { + if (bands == 1) { + out.append("/DeviceGray setcolorspace").append(EOL); + } + if (bufferedImage.getType() == BufferedImage.TYPE_BYTE_BINARY) { + decodeScale = 255; + } + out.append(imageWidth).append(" ").append(imageHeight).append(" ") + .append(bands).append(" ").append(bitsPerSample).append(" ").append(decodeScale).append(" ") + .append(true).append(" currentfile /ASCII85Decode filter ") + .append("<< /BitsPerComponent ").append(bitsPerSample).append(" >> ") + .append("/FlateDecode filter ") + .append("imgdict ") + .append("image").append(EOL); + output(bufferedImage, out); + } + + out.append("grestore"); + return out.toString(); + } + + private static void output(BufferedImage image, StringBuilder out) { + InputStream imageDataStream = + new ImageDataStream(image, Interleaving.SAMPLE); + ByteArrayOutputStream outBytes = new ByteArrayOutputStream(); + OutputStream compressionStream = new FlateEncodeStream( + new ASCII85EncodeStream( + new LineWrapOutputStream(outBytes, 80))); + try { + DataUtils.transfer(imageDataStream, compressionStream, 1024); + compressionStream.close(); + String compressed = outBytes.toString(CHARSET); + out.append(compressed).append(EOL); + } catch (IOException e) { + // TODO Handle exception + e.printStackTrace(); + } + } + + private static String getOutput(String str, double x, double y) { + + return "gsave 1 -1 scale " + x + " " + -y + " M " + getOutput(str) + " show " + "grestore"; + } + + private static StringBuilder getOutput(String str) { + StringBuilder out = new StringBuilder(); + + // Escape text + str = str.replaceAll("\\\\", "\\\\\\\\") + .replaceAll("\t", "\\\\t") + .replaceAll("\b", "\\\\b") + .replaceAll("\f", "\\\\f") + .replaceAll("\\(", "\\\\(") + .replaceAll("\\)", "\\\\)") + .replaceAll("[\r\n]", ""); + + out.append("(").append(str).append(")"); + + return out; + } + + private static String getOutput(Stroke s) { + StringBuilder out = new StringBuilder(); + if (s instanceof BasicStroke) { + BasicStroke bs = (BasicStroke) s; + out.append(bs.getLineWidth()).append(" setlinewidth ") + .append(STROKE_LINEJOIN.get(bs.getLineJoin())).append(" setlinejoin ") + .append(STROKE_ENDCAPS.get(bs.getEndCap())).append(" setlinecap ") + .append("[").append(DataUtils.join(" ", bs.getDashArray())).append("] ") + .append(bs.getDashPhase()).append(" setdash"); + } else { + out.append("% Custom strokes aren't supported at the moment"); + } + return out.toString(); + } + + private static String getOutput(Font font) { + StringBuilder out = new StringBuilder(); + font = GraphicsUtils.getPhysicalFont(font); + String fontName = font.getPSName(); + + // Convert font to ISO-8859-1 encoding + String fontNameLatin1 = fontName + FONT_LATIN1_SUFFIX; + out.append("/").append(fontNameLatin1).append(" ") + .append("/").append(font.getPSName()).append(" latinize "); + + // Use encoded font + out.append("/").append(fontNameLatin1).append(" ") + .append(font.getSize2D()).append(" selectfont"); + + return out.toString(); + } + + private void addHeader() { + double x = getPageSize().x * UNITS_PER_MM, + y = getPageSize().y * UNITS_PER_MM, + width = getPageSize().width * UNITS_PER_MM, + height = getPageSize().height * UNITS_PER_MM; + elements.addAll(Arrays.asList( + "%!PS-Adobe-3.0 EPSF-3.0", + "%%BoundingBox: " + ((int) Math.floor(x)) + " " + ((int) Math.floor(y)) + " " + ((int) Math.ceil(x + width)) + " " + ((int) Math.ceil(y + height)), + "%%HiResBoundingBox: " + x + " " + y + " " + (x + width) + " " + (y + height), + "%%LanguageLevel: 3", + "%%Pages: 1", + "%%EndComments", + "%%Page: 1 1", + "/M /moveto load def", + "/L /lineto load def", + "/C /curveto load def", + "/Z /closepath load def", + "/RL /rlineto load def", + "/rgb /setrgbcolor load def", + "/rect { /height exch def /width exch def /y exch def /x exch def x y M width 0 RL 0 height RL width neg 0 RL } bind def", + "/ellipse { /endangle exch def /startangle exch def /ry exch def /rx exch def /y exch def /x exch def /savematrix matrix currentmatrix def x y translate rx ry scale 0 0 1 startangle endangle arcn savematrix setmatrix } bind def", + "/imgdict { /datastream exch def /hasdata exch def /decodeScale exch def /bits exch def /bands exch def /imgheight exch def /imgwidth exch def << /ImageType 1 /Width imgwidth /Height imgheight /BitsPerComponent bits /Decode [bands {0 decodeScale} repeat] /ImageMatrix [imgwidth 0 0 imgheight 0 0] hasdata { /DataSource datastream } if >> } bind def", + "/latinize { /fontName exch def /fontNameNew exch def fontName findfont 0 dict copy begin /Encoding ISOLatin1Encoding def fontNameNew /FontName def currentdict end dup /FID undef fontNameNew exch definefont pop } bind def", + getOutput(GraphicsState.DEFAULT_FONT), + "gsave", + "clipsave", + "/DeviceRGB setcolorspace", + "0 " + height + " translate", + UNITS_PER_MM + " " + (-UNITS_PER_MM) + " scale", + "/basematrix matrix currentmatrix def" + )); + } + + public void write(OutputStream out) throws IOException { + OutputStreamWriter o = new OutputStreamWriter(out, CHARSET); + for (String element : elements) { + if (element == null) { + continue; + } + + // Write current element in lines of 255 bytes (excluding line terminators) + // Numbers must not be separated by line breaks or errors will occur + // TODO: Integrate functionality into LineWrapOutputStream + Matcher chunkMatcher = ELEMENT_SEPARATION_PATTERN.matcher(element); + + boolean chunkFound = false; + while (chunkMatcher.find()) { + chunkFound = true; + String chunk = chunkMatcher.group(); + o.write(chunk, 0, chunk.length()); + o.append(EOL); + } + if (!chunkFound) { + // TODO: Exception, if no whitespace can be found in the chunk + System.err.println("Unable to divide eps element into lines: " + element); + } + } + o.append("%%EOF"); + o.flush(); + } + + public void handle(Command command) { + if (command instanceof SetClipCommand) { + SetClipCommand c = (SetClipCommand) command; + Shape clip = c.getValue(); + elements.add("cliprestore"); + if (clip != null) { + elements.add(getOutput(clip) + " clip"); + } + } else if (command instanceof SetColorCommand) { + SetColorCommand c = (SetColorCommand) command; + elements.add(getOutput(c.getValue())); + } else if (command instanceof SetCompositeCommand) { + SetCompositeCommand c = (SetCompositeCommand) command; + // TODO Implement composite rendering for EPS + elements.add("% composite not yet implemented: " + c.getValue()); + } else if (command instanceof SetFontCommand) { + SetFontCommand c = (SetFontCommand) command; + elements.add(getOutput(c.getValue())); + } else if (command instanceof SetPaintCommand) { + SetPaintCommand c = (SetPaintCommand) command; + // TODO Implement paint rendering for EPS + elements.add("% paint not yet implemented: " + c.getValue()); + } else if (command instanceof SetStrokeCommand) { + SetStrokeCommand c = (SetStrokeCommand) command; + elements.add(getOutput(c.getValue())); + } else if (command instanceof SetTransformCommand) { + SetTransformCommand c = (SetTransformCommand) command; + StringBuilder e = new StringBuilder(); + double[] matrix = new double[6]; + c.getValue().getMatrix(matrix); + e.append("basematrix setmatrix [") + .append(DataUtils.join(" ", matrix)).append("] concat"); + elements.add(e.toString()); + } else if (command instanceof RotateCommand) { + RotateCommand c = (RotateCommand) command; + StringBuilder e = new StringBuilder(); + double x = c.getCenterX(); + double y = c.getCenterY(); + boolean translated = x != 0.0 || y != 0.0; + if (translated) { + e.append(x).append(" ").append(y).append(" translate "); + } + e.append(Math.toDegrees(c.getTheta())).append(" rotate"); + if (translated) { + e.append(" "); + e.append(-x).append(" ").append(-y).append(" translate"); + } + elements.add(e.toString()); + } else if (command instanceof ScaleCommand) { + ScaleCommand c = (ScaleCommand) command; + elements.add(DataUtils.format(c.getScaleX()) + " " + DataUtils.format(c.getScaleY()) + " scale"); + } else if (command instanceof ShearCommand) { + ShearCommand c = (ShearCommand) command; + elements.add("[1 " + DataUtils.format(c.getShearY()) + " " + DataUtils.format(c.getShearX()) + " 1 0 0] concat"); + } else if (command instanceof TransformCommand) { + TransformCommand c = (TransformCommand) command; + StringBuilder e = new StringBuilder(); + double[] matrix = new double[6]; + c.getValue().getMatrix(matrix); + e.append("[").append(DataUtils.join(" ", matrix)) + .append("] concat"); + elements.add(e.toString()); + } else if (command instanceof TranslateCommand) { + TranslateCommand c = (TranslateCommand) command; + elements.add(String.valueOf(c.getDeltaX()) + " " + c.getDeltaY() + " translate"); + } else if (command instanceof DrawImageCommand) { + DrawImageCommand c = (DrawImageCommand) command; + String e = getOutput(c.getValue(), + c.getImageWidth(), c.getImageHeight(), + c.getX(), c.getY(), c.getWidth(), c.getHeight()); + elements.add(e); + } else if (command instanceof DrawShapeCommand) { + DrawShapeCommand c = (DrawShapeCommand) command; + elements.add(getOutput(c.getValue()) + " stroke"); + } else if (command instanceof DrawStringCommand) { + DrawStringCommand c = (DrawStringCommand) command; + elements.add(getOutput(c.getValue(), c.getX(), c.getY())); + } else if (command instanceof FillShapeCommand) { + FillShapeCommand c = (FillShapeCommand) command; + elements.add(getOutput(c.getValue()) + " fill"); + } else if (command instanceof CreateCommand) { + elements.add("gsave"); + } else if (command instanceof DisposeCommand) { + elements.add("grestore"); + } + } +} + diff --git a/chart/src/main/java/org/xbib/graphics/chart/io/vector/eps/EPSProcessor.java b/chart/src/main/java/org/xbib/graphics/chart/io/vector/eps/EPSProcessor.java new file mode 100644 index 0000000..65fa2b3 --- /dev/null +++ b/chart/src/main/java/org/xbib/graphics/chart/io/vector/eps/EPSProcessor.java @@ -0,0 +1,23 @@ +package org.xbib.graphics.chart.io.vector.eps; + +import org.xbib.graphics.chart.io.vector.Document; +import org.xbib.graphics.chart.io.vector.Processor; +import org.xbib.graphics.chart.io.vector.intermediate.commands.Command; +import org.xbib.graphics.chart.io.vector.intermediate.filters.FillPaintedShapeAsImageFilter; +import org.xbib.graphics.chart.io.vector.util.PageSize; + +public class EPSProcessor implements Processor { + public Document process(Iterable> commands, PageSize pageSize) { + // TODO Apply rotate(theta,x,y) => translate-rotate-translate filter + // TODO Apply image transparency => image mask filter + // TODO Apply optimization filter + FillPaintedShapeAsImageFilter paintedShapeAsImageFilter = new FillPaintedShapeAsImageFilter(commands); + EPSDocument doc = new EPSDocument(pageSize); + for (Command command : paintedShapeAsImageFilter) { + doc.handle(command); + } + doc.close(); + return doc; + } +} + diff --git a/chart/src/main/java/org/xbib/graphics/chart/io/vector/intermediate/CommandHandler.java b/chart/src/main/java/org/xbib/graphics/chart/io/vector/intermediate/CommandHandler.java new file mode 100644 index 0000000..ee0500c --- /dev/null +++ b/chart/src/main/java/org/xbib/graphics/chart/io/vector/intermediate/CommandHandler.java @@ -0,0 +1,8 @@ +package org.xbib.graphics.chart.io.vector.intermediate; + +import org.xbib.graphics.chart.io.vector.intermediate.commands.Command; + +public interface CommandHandler { + void handle(Command command); +} + diff --git a/chart/src/main/java/org/xbib/graphics/chart/io/vector/intermediate/commands/AffineTransformCommand.java b/chart/src/main/java/org/xbib/graphics/chart/io/vector/intermediate/commands/AffineTransformCommand.java new file mode 100644 index 0000000..bcfa6c5 --- /dev/null +++ b/chart/src/main/java/org/xbib/graphics/chart/io/vector/intermediate/commands/AffineTransformCommand.java @@ -0,0 +1,10 @@ +package org.xbib.graphics.chart.io.vector.intermediate.commands; + +import java.awt.geom.AffineTransform; + +public abstract class AffineTransformCommand extends StateCommand { + public AffineTransformCommand(AffineTransform transform) { + super(transform); + } +} + diff --git a/chart/src/main/java/org/xbib/graphics/chart/io/vector/intermediate/commands/Command.java b/chart/src/main/java/org/xbib/graphics/chart/io/vector/intermediate/commands/Command.java new file mode 100644 index 0000000..e1858fd --- /dev/null +++ b/chart/src/main/java/org/xbib/graphics/chart/io/vector/intermediate/commands/Command.java @@ -0,0 +1,31 @@ +package org.xbib.graphics.chart.io.vector.intermediate.commands; + +import java.util.Locale; + +public abstract class Command { + private final T value; + + public Command(T value) { + this.value = value; + } + + public T getValue() { + return value; + } + + @Override + public String toString() { + return String.format((Locale) null, "%s[value=%s]", + getClass().getName(), getValue()); + } + + @Override + public boolean equals(Object obj) { + if (obj == null || !getClass().equals(obj.getClass())) { + return false; + } + Command o = (Command) obj; + return value == o.value || value.equals(o.value); + } +} + diff --git a/chart/src/main/java/org/xbib/graphics/chart/io/vector/intermediate/commands/CreateCommand.java b/chart/src/main/java/org/xbib/graphics/chart/io/vector/intermediate/commands/CreateCommand.java new file mode 100644 index 0000000..a48e9bb --- /dev/null +++ b/chart/src/main/java/org/xbib/graphics/chart/io/vector/intermediate/commands/CreateCommand.java @@ -0,0 +1,10 @@ +package org.xbib.graphics.chart.io.vector.intermediate.commands; + +import org.xbib.graphics.chart.io.vector.VectorGraphics2D; + +public class CreateCommand extends StateCommand { + public CreateCommand(VectorGraphics2D graphics) { + super(graphics); + } +} + diff --git a/chart/src/main/java/org/xbib/graphics/chart/io/vector/intermediate/commands/DisposeCommand.java b/chart/src/main/java/org/xbib/graphics/chart/io/vector/intermediate/commands/DisposeCommand.java new file mode 100644 index 0000000..d9c20b9 --- /dev/null +++ b/chart/src/main/java/org/xbib/graphics/chart/io/vector/intermediate/commands/DisposeCommand.java @@ -0,0 +1,10 @@ +package org.xbib.graphics.chart.io.vector.intermediate.commands; + +import org.xbib.graphics.chart.io.vector.VectorGraphics2D; + +public class DisposeCommand extends StateCommand { + public DisposeCommand(VectorGraphics2D graphics) { + super(graphics); + } +} + diff --git a/chart/src/main/java/org/xbib/graphics/chart/io/vector/intermediate/commands/DrawImageCommand.java b/chart/src/main/java/org/xbib/graphics/chart/io/vector/intermediate/commands/DrawImageCommand.java new file mode 100644 index 0000000..60c8ac1 --- /dev/null +++ b/chart/src/main/java/org/xbib/graphics/chart/io/vector/intermediate/commands/DrawImageCommand.java @@ -0,0 +1,58 @@ +package org.xbib.graphics.chart.io.vector.intermediate.commands; + +import java.awt.Image; +import java.util.Locale; + +public class DrawImageCommand extends Command { + private final int imageWidth; + private final int imageHeight; + private final double x; + private final double y; + private final double width; + private final double height; + + public DrawImageCommand(Image image, int imageWidth, int imageHeight, + double x, double y, double width, double height) { + super(image); + this.imageWidth = imageWidth; + this.imageHeight = imageHeight; + this.x = x; + this.y = y; + this.width = width; + this.height = height; + } + + public int getImageWidth() { + return imageWidth; + } + + public int getImageHeight() { + return imageHeight; + } + + public double getX() { + return x; + } + + public double getY() { + return y; + } + + public double getWidth() { + return width; + } + + public double getHeight() { + return height; + } + + @Override + public String toString() { + return String.format((Locale) null, + "%s[value=%s, imageWidth=%d, imageHeight=%d, x=%f, y=%f, width=%f, height=%f]", + getClass().getName(), getValue(), + getImageWidth(), getImageHeight(), + getX(), getY(), getWidth(), getHeight()); + } +} + diff --git a/chart/src/main/java/org/xbib/graphics/chart/io/vector/intermediate/commands/DrawShapeCommand.java b/chart/src/main/java/org/xbib/graphics/chart/io/vector/intermediate/commands/DrawShapeCommand.java new file mode 100644 index 0000000..7757989 --- /dev/null +++ b/chart/src/main/java/org/xbib/graphics/chart/io/vector/intermediate/commands/DrawShapeCommand.java @@ -0,0 +1,12 @@ +package org.xbib.graphics.chart.io.vector.intermediate.commands; + +import org.xbib.graphics.chart.io.vector.util.GraphicsUtils; + +import java.awt.Shape; + +public class DrawShapeCommand extends Command { + public DrawShapeCommand(Shape shape) { + super(GraphicsUtils.clone(shape)); + } +} + diff --git a/chart/src/main/java/org/xbib/graphics/chart/io/vector/intermediate/commands/DrawStringCommand.java b/chart/src/main/java/org/xbib/graphics/chart/io/vector/intermediate/commands/DrawStringCommand.java new file mode 100644 index 0000000..1bb81a0 --- /dev/null +++ b/chart/src/main/java/org/xbib/graphics/chart/io/vector/intermediate/commands/DrawStringCommand.java @@ -0,0 +1,30 @@ +package org.xbib.graphics.chart.io.vector.intermediate.commands; + +import java.util.Locale; + + +public class DrawStringCommand extends Command { + private final double x; + private final double y; + + public DrawStringCommand(String string, double x, double y) { + super(string); + this.x = x; + this.y = y; + } + + public double getX() { + return x; + } + + public double getY() { + return y; + } + + @Override + public String toString() { + return String.format((Locale) null, "%s[value=%s, x=%f, y=%f]", + getClass().getName(), getValue(), getX(), getY()); + } +} + diff --git a/chart/src/main/java/org/xbib/graphics/chart/io/vector/intermediate/commands/FillShapeCommand.java b/chart/src/main/java/org/xbib/graphics/chart/io/vector/intermediate/commands/FillShapeCommand.java new file mode 100644 index 0000000..d8abbeb --- /dev/null +++ b/chart/src/main/java/org/xbib/graphics/chart/io/vector/intermediate/commands/FillShapeCommand.java @@ -0,0 +1,12 @@ +package org.xbib.graphics.chart.io.vector.intermediate.commands; + +import org.xbib.graphics.chart.io.vector.util.GraphicsUtils; + +import java.awt.Shape; + +public class FillShapeCommand extends Command { + public FillShapeCommand(Shape shape) { + super(GraphicsUtils.clone(shape)); + } +} + diff --git a/chart/src/main/java/org/xbib/graphics/chart/io/vector/intermediate/commands/Group.java b/chart/src/main/java/org/xbib/graphics/chart/io/vector/intermediate/commands/Group.java new file mode 100644 index 0000000..0b67f65 --- /dev/null +++ b/chart/src/main/java/org/xbib/graphics/chart/io/vector/intermediate/commands/Group.java @@ -0,0 +1,16 @@ +package org.xbib.graphics.chart.io.vector.intermediate.commands; + +import java.util.LinkedList; +import java.util.List; + +public class Group extends Command>> { + public Group() { + super(new LinkedList>()); + } + + public void add(Command command) { + List> group = getValue(); + group.add(command); + } +} + diff --git a/chart/src/main/java/org/xbib/graphics/chart/io/vector/intermediate/commands/RotateCommand.java b/chart/src/main/java/org/xbib/graphics/chart/io/vector/intermediate/commands/RotateCommand.java new file mode 100644 index 0000000..e05a203 --- /dev/null +++ b/chart/src/main/java/org/xbib/graphics/chart/io/vector/intermediate/commands/RotateCommand.java @@ -0,0 +1,38 @@ +package org.xbib.graphics.chart.io.vector.intermediate.commands; + +import java.awt.geom.AffineTransform; +import java.util.Locale; + +public class RotateCommand extends AffineTransformCommand { + private final double theta; + private final double centerX; + private final double centerY; + + public RotateCommand(double theta, double centerX, double centerY) { + super(AffineTransform.getRotateInstance(theta, centerX, centerY)); + this.theta = theta; + this.centerX = centerX; + this.centerY = centerY; + } + + public double getTheta() { + return theta; + } + + public double getCenterX() { + return centerX; + } + + public double getCenterY() { + return centerY; + } + + @Override + public String toString() { + return String.format((Locale) null, + "%s[theta=%f, centerX=%f, centerY=%f, value=%s]", + getClass().getName(), getTheta(), getCenterX(), getCenterY(), + getValue()); + } +} + diff --git a/chart/src/main/java/org/xbib/graphics/chart/io/vector/intermediate/commands/ScaleCommand.java b/chart/src/main/java/org/xbib/graphics/chart/io/vector/intermediate/commands/ScaleCommand.java new file mode 100644 index 0000000..2d61a6b --- /dev/null +++ b/chart/src/main/java/org/xbib/graphics/chart/io/vector/intermediate/commands/ScaleCommand.java @@ -0,0 +1,31 @@ +package org.xbib.graphics.chart.io.vector.intermediate.commands; + +import java.awt.geom.AffineTransform; +import java.util.Locale; + +public class ScaleCommand extends AffineTransformCommand { + private final double scaleX; + private final double scaleY; + + public ScaleCommand(double scaleX, double scaleY) { + super(AffineTransform.getScaleInstance(scaleX, scaleY)); + this.scaleX = scaleX; + this.scaleY = scaleY; + } + + public double getScaleX() { + return scaleX; + } + + public double getScaleY() { + return scaleY; + } + + @Override + public String toString() { + return String.format((Locale) null, + "%s[scaleX=%f, scaleY=%f, value=%s]", getClass().getName(), + getScaleX(), getScaleY(), getValue()); + } +} + diff --git a/chart/src/main/java/org/xbib/graphics/chart/io/vector/intermediate/commands/SetBackgroundCommand.java b/chart/src/main/java/org/xbib/graphics/chart/io/vector/intermediate/commands/SetBackgroundCommand.java new file mode 100644 index 0000000..79baa1f --- /dev/null +++ b/chart/src/main/java/org/xbib/graphics/chart/io/vector/intermediate/commands/SetBackgroundCommand.java @@ -0,0 +1,10 @@ +package org.xbib.graphics.chart.io.vector.intermediate.commands; + +import java.awt.Color; + +public class SetBackgroundCommand extends StateCommand { + public SetBackgroundCommand(Color color) { + super(color); + } +} + diff --git a/chart/src/main/java/org/xbib/graphics/chart/io/vector/intermediate/commands/SetClipCommand.java b/chart/src/main/java/org/xbib/graphics/chart/io/vector/intermediate/commands/SetClipCommand.java new file mode 100644 index 0000000..4d865da --- /dev/null +++ b/chart/src/main/java/org/xbib/graphics/chart/io/vector/intermediate/commands/SetClipCommand.java @@ -0,0 +1,10 @@ +package org.xbib.graphics.chart.io.vector.intermediate.commands; + +import java.awt.Shape; + +public class SetClipCommand extends StateCommand { + public SetClipCommand(Shape shape) { + super(shape); + } +} + diff --git a/chart/src/main/java/org/xbib/graphics/chart/io/vector/intermediate/commands/SetColorCommand.java b/chart/src/main/java/org/xbib/graphics/chart/io/vector/intermediate/commands/SetColorCommand.java new file mode 100644 index 0000000..d5d3fc1 --- /dev/null +++ b/chart/src/main/java/org/xbib/graphics/chart/io/vector/intermediate/commands/SetColorCommand.java @@ -0,0 +1,10 @@ +package org.xbib.graphics.chart.io.vector.intermediate.commands; + +import java.awt.Color; + +public class SetColorCommand extends StateCommand { + public SetColorCommand(Color color) { + super(color); + } +} + diff --git a/chart/src/main/java/org/xbib/graphics/chart/io/vector/intermediate/commands/SetCompositeCommand.java b/chart/src/main/java/org/xbib/graphics/chart/io/vector/intermediate/commands/SetCompositeCommand.java new file mode 100644 index 0000000..bddd414 --- /dev/null +++ b/chart/src/main/java/org/xbib/graphics/chart/io/vector/intermediate/commands/SetCompositeCommand.java @@ -0,0 +1,10 @@ +package org.xbib.graphics.chart.io.vector.intermediate.commands; + +import java.awt.Composite; + +public class SetCompositeCommand extends StateCommand { + public SetCompositeCommand(Composite composite) { + super(composite); + } +} + diff --git a/chart/src/main/java/org/xbib/graphics/chart/io/vector/intermediate/commands/SetFontCommand.java b/chart/src/main/java/org/xbib/graphics/chart/io/vector/intermediate/commands/SetFontCommand.java new file mode 100644 index 0000000..4e4d331 --- /dev/null +++ b/chart/src/main/java/org/xbib/graphics/chart/io/vector/intermediate/commands/SetFontCommand.java @@ -0,0 +1,10 @@ +package org.xbib.graphics.chart.io.vector.intermediate.commands; + +import java.awt.Font; + +public class SetFontCommand extends StateCommand { + public SetFontCommand(Font font) { + super(font); + } +} + diff --git a/chart/src/main/java/org/xbib/graphics/chart/io/vector/intermediate/commands/SetHintCommand.java b/chart/src/main/java/org/xbib/graphics/chart/io/vector/intermediate/commands/SetHintCommand.java new file mode 100644 index 0000000..6a50801 --- /dev/null +++ b/chart/src/main/java/org/xbib/graphics/chart/io/vector/intermediate/commands/SetHintCommand.java @@ -0,0 +1,24 @@ +package org.xbib.graphics.chart.io.vector.intermediate.commands; + +import java.util.Locale; + +public class SetHintCommand extends StateCommand { + private final Object key; + + public SetHintCommand(Object hintKey, Object hintValue) { + super(hintValue); + key = hintKey; + } + + public Object getKey() { + return key; + } + + @Override + public String toString() { + return String.format((Locale) null, + "%s[key=%s, value=%s]", getClass().getName(), + getKey(), getValue()); + } +} + diff --git a/chart/src/main/java/org/xbib/graphics/chart/io/vector/intermediate/commands/SetPaintCommand.java b/chart/src/main/java/org/xbib/graphics/chart/io/vector/intermediate/commands/SetPaintCommand.java new file mode 100644 index 0000000..b258680 --- /dev/null +++ b/chart/src/main/java/org/xbib/graphics/chart/io/vector/intermediate/commands/SetPaintCommand.java @@ -0,0 +1,10 @@ +package org.xbib.graphics.chart.io.vector.intermediate.commands; + +import java.awt.Paint; + +public class SetPaintCommand extends StateCommand { + public SetPaintCommand(Paint paint) { + super(paint); + } +} + diff --git a/chart/src/main/java/org/xbib/graphics/chart/io/vector/intermediate/commands/SetStrokeCommand.java b/chart/src/main/java/org/xbib/graphics/chart/io/vector/intermediate/commands/SetStrokeCommand.java new file mode 100644 index 0000000..1489e3f --- /dev/null +++ b/chart/src/main/java/org/xbib/graphics/chart/io/vector/intermediate/commands/SetStrokeCommand.java @@ -0,0 +1,10 @@ +package org.xbib.graphics.chart.io.vector.intermediate.commands; + +import java.awt.Stroke; + +public class SetStrokeCommand extends StateCommand { + public SetStrokeCommand(Stroke stroke) { + super(stroke); + } +} + diff --git a/chart/src/main/java/org/xbib/graphics/chart/io/vector/intermediate/commands/SetTransformCommand.java b/chart/src/main/java/org/xbib/graphics/chart/io/vector/intermediate/commands/SetTransformCommand.java new file mode 100644 index 0000000..6730a8c --- /dev/null +++ b/chart/src/main/java/org/xbib/graphics/chart/io/vector/intermediate/commands/SetTransformCommand.java @@ -0,0 +1,10 @@ +package org.xbib.graphics.chart.io.vector.intermediate.commands; + +import java.awt.geom.AffineTransform; + +public class SetTransformCommand extends StateCommand { + public SetTransformCommand(AffineTransform transform) { + super(new AffineTransform(transform)); + } +} + diff --git a/chart/src/main/java/org/xbib/graphics/chart/io/vector/intermediate/commands/SetXORModeCommand.java b/chart/src/main/java/org/xbib/graphics/chart/io/vector/intermediate/commands/SetXORModeCommand.java new file mode 100644 index 0000000..69239b8 --- /dev/null +++ b/chart/src/main/java/org/xbib/graphics/chart/io/vector/intermediate/commands/SetXORModeCommand.java @@ -0,0 +1,10 @@ +package org.xbib.graphics.chart.io.vector.intermediate.commands; + +import java.awt.Color; + +public class SetXORModeCommand extends StateCommand { + public SetXORModeCommand(Color mode) { + super(mode); + } +} + diff --git a/chart/src/main/java/org/xbib/graphics/chart/io/vector/intermediate/commands/ShearCommand.java b/chart/src/main/java/org/xbib/graphics/chart/io/vector/intermediate/commands/ShearCommand.java new file mode 100644 index 0000000..0efd918 --- /dev/null +++ b/chart/src/main/java/org/xbib/graphics/chart/io/vector/intermediate/commands/ShearCommand.java @@ -0,0 +1,31 @@ +package org.xbib.graphics.chart.io.vector.intermediate.commands; + +import java.awt.geom.AffineTransform; +import java.util.Locale; + +public class ShearCommand extends AffineTransformCommand { + private final double shearX; + private final double shearY; + + public ShearCommand(double shearX, double shearY) { + super(AffineTransform.getShearInstance(shearX, shearY)); + this.shearX = shearX; + this.shearY = shearY; + } + + public double getShearX() { + return shearX; + } + + public double getShearY() { + return shearY; + } + + @Override + public String toString() { + return String.format((Locale) null, + "%s[shearX=%f, shearY=%f, value=%s]", getClass().getName(), + getShearX(), getShearY(), getValue()); + } +} + diff --git a/chart/src/main/java/org/xbib/graphics/chart/io/vector/intermediate/commands/StateCommand.java b/chart/src/main/java/org/xbib/graphics/chart/io/vector/intermediate/commands/StateCommand.java new file mode 100644 index 0000000..fec7cae --- /dev/null +++ b/chart/src/main/java/org/xbib/graphics/chart/io/vector/intermediate/commands/StateCommand.java @@ -0,0 +1,8 @@ +package org.xbib.graphics.chart.io.vector.intermediate.commands; + +public abstract class StateCommand extends Command { + public StateCommand(T value) { + super(value); + } +} + diff --git a/chart/src/main/java/org/xbib/graphics/chart/io/vector/intermediate/commands/TransformCommand.java b/chart/src/main/java/org/xbib/graphics/chart/io/vector/intermediate/commands/TransformCommand.java new file mode 100644 index 0000000..deb6a27 --- /dev/null +++ b/chart/src/main/java/org/xbib/graphics/chart/io/vector/intermediate/commands/TransformCommand.java @@ -0,0 +1,17 @@ +package org.xbib.graphics.chart.io.vector.intermediate.commands; + +import java.awt.geom.AffineTransform; + +public class TransformCommand extends AffineTransformCommand { + private final AffineTransform transform; + + public TransformCommand(AffineTransform transform) { + super(transform); + this.transform = new AffineTransform(transform); + } + + public AffineTransform getTransform() { + return transform; + } +} + diff --git a/chart/src/main/java/org/xbib/graphics/chart/io/vector/intermediate/commands/TranslateCommand.java b/chart/src/main/java/org/xbib/graphics/chart/io/vector/intermediate/commands/TranslateCommand.java new file mode 100644 index 0000000..a58f092 --- /dev/null +++ b/chart/src/main/java/org/xbib/graphics/chart/io/vector/intermediate/commands/TranslateCommand.java @@ -0,0 +1,31 @@ +package org.xbib.graphics.chart.io.vector.intermediate.commands; + +import java.awt.geom.AffineTransform; +import java.util.Locale; + +public class TranslateCommand extends AffineTransformCommand { + private final double deltaX; + private final double deltaY; + + public TranslateCommand(double x, double y) { + super(AffineTransform.getTranslateInstance(x, y)); + this.deltaX = x; + this.deltaY = y; + } + + public double getDeltaX() { + return deltaX; + } + + public double getDeltaY() { + return deltaY; + } + + @Override + public String toString() { + return String.format((Locale) null, + "%s[deltaX=%f, deltaY=%f, value=%s]", getClass().getName(), + getDeltaX(), getDeltaY(), getValue()); + } +} + diff --git a/chart/src/main/java/org/xbib/graphics/chart/io/vector/intermediate/filters/AbsoluteToRelativeTransformsFilter.java b/chart/src/main/java/org/xbib/graphics/chart/io/vector/intermediate/filters/AbsoluteToRelativeTransformsFilter.java new file mode 100644 index 0000000..95a2e63 --- /dev/null +++ b/chart/src/main/java/org/xbib/graphics/chart/io/vector/intermediate/filters/AbsoluteToRelativeTransformsFilter.java @@ -0,0 +1,63 @@ +package org.xbib.graphics.chart.io.vector.intermediate.filters; + +import org.xbib.graphics.chart.io.vector.intermediate.commands.AffineTransformCommand; +import org.xbib.graphics.chart.io.vector.intermediate.commands.Command; +import org.xbib.graphics.chart.io.vector.intermediate.commands.CreateCommand; +import org.xbib.graphics.chart.io.vector.intermediate.commands.DisposeCommand; +import org.xbib.graphics.chart.io.vector.intermediate.commands.SetTransformCommand; +import org.xbib.graphics.chart.io.vector.intermediate.commands.TransformCommand; + +import java.awt.geom.AffineTransform; +import java.awt.geom.NoninvertibleTransformException; +import java.util.Arrays; +import java.util.List; +import java.util.Stack; + +public class AbsoluteToRelativeTransformsFilter extends Filter { + private Stack transforms; + + public AbsoluteToRelativeTransformsFilter(Iterable> stream) { + super(stream); + transforms = new Stack(); + } + + @Override + public Command next() { + Command nextCommand = super.next(); + if (nextCommand instanceof AffineTransformCommand) { + AffineTransformCommand affineTransformCommand = (AffineTransformCommand) nextCommand; + getCurrentTransform().concatenate(affineTransformCommand.getValue()); + } else if (nextCommand instanceof CreateCommand) { + AffineTransform newTransform = transforms.isEmpty() ? new AffineTransform() : new AffineTransform(getCurrentTransform()); + transforms.push(newTransform); + } else if (nextCommand instanceof DisposeCommand) { + transforms.pop(); + } + + return nextCommand; + } + + @Override + protected List> filter(Command command) { + if (command instanceof SetTransformCommand) { + SetTransformCommand setTransformCommand = (SetTransformCommand) command; + AffineTransform absoluteTransform = setTransformCommand.getValue(); + AffineTransform relativeTransform = new AffineTransform(); + try { + AffineTransform invertedOldTransformation = getCurrentTransform().createInverse(); + relativeTransform.concatenate(invertedOldTransformation); + } catch (NoninvertibleTransformException e) { + e.printStackTrace(); + } + relativeTransform.concatenate(absoluteTransform); + TransformCommand transformCommand = new TransformCommand(relativeTransform); + return Arrays.>asList(transformCommand); + } + return Arrays.>asList(command); + } + + private AffineTransform getCurrentTransform() { + return transforms.peek(); + } +} + diff --git a/chart/src/main/java/org/xbib/graphics/chart/io/vector/intermediate/filters/FillPaintedShapeAsImageFilter.java b/chart/src/main/java/org/xbib/graphics/chart/io/vector/intermediate/filters/FillPaintedShapeAsImageFilter.java new file mode 100644 index 0000000..ba43f32 --- /dev/null +++ b/chart/src/main/java/org/xbib/graphics/chart/io/vector/intermediate/filters/FillPaintedShapeAsImageFilter.java @@ -0,0 +1,70 @@ +package org.xbib.graphics.chart.io.vector.intermediate.filters; + +import org.xbib.graphics.chart.io.vector.intermediate.commands.Command; +import org.xbib.graphics.chart.io.vector.intermediate.commands.DisposeCommand; +import org.xbib.graphics.chart.io.vector.intermediate.commands.DrawImageCommand; +import org.xbib.graphics.chart.io.vector.intermediate.commands.FillShapeCommand; +import org.xbib.graphics.chart.io.vector.intermediate.commands.SetPaintCommand; + +import java.awt.Graphics2D; +import java.awt.RenderingHints; +import java.awt.Shape; +import java.awt.geom.Rectangle2D; +import java.awt.image.BufferedImage; +import java.util.Arrays; +import java.util.List; + +public class FillPaintedShapeAsImageFilter extends Filter { + private SetPaintCommand lastSetPaintCommand; + + public FillPaintedShapeAsImageFilter(Iterable> stream) { + super(stream); + } + + @Override + public Command next() { + Command nextCommand = super.next(); + + if (nextCommand instanceof SetPaintCommand) { + lastSetPaintCommand = (SetPaintCommand) nextCommand; + } else if (nextCommand instanceof DisposeCommand) { + lastSetPaintCommand = null; + } + + return nextCommand; + } + + private DrawImageCommand getDrawImageCommand(FillShapeCommand shapeCommand, SetPaintCommand paintCommand) { + Shape shape = shapeCommand.getValue(); + Rectangle2D shapeBounds = shape.getBounds2D(); + double x = shapeBounds.getX(); + double y = shapeBounds.getY(); + double width = shapeBounds.getWidth(); + double height = shapeBounds.getHeight(); + int imageWidth = (int) Math.round(width); + int imageHeight = (int) Math.round(height); + BufferedImage image = new BufferedImage(imageWidth, imageHeight, BufferedImage.TYPE_INT_ARGB); + Graphics2D imageGraphics = (Graphics2D) image.getGraphics(); + imageGraphics.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON); + imageGraphics.scale(imageWidth / width, imageHeight / height); + imageGraphics.translate(-shapeBounds.getX(), -shapeBounds.getY()); + imageGraphics.setPaint(paintCommand.getValue()); + imageGraphics.fill(shape); + imageGraphics.dispose(); + + DrawImageCommand drawImageCommand = new DrawImageCommand(image, imageWidth, imageHeight, x, y, width, height); + return drawImageCommand; + } + + @Override + protected List> filter(Command command) { + if (lastSetPaintCommand != null && command instanceof FillShapeCommand) { + FillShapeCommand fillShapeCommand = (FillShapeCommand) command; + DrawImageCommand drawImageCommand = getDrawImageCommand(fillShapeCommand, lastSetPaintCommand); + return Arrays.>asList(drawImageCommand); + } + + return Arrays.>asList(command); + } +} + diff --git a/chart/src/main/java/org/xbib/graphics/chart/io/vector/intermediate/filters/Filter.java b/chart/src/main/java/org/xbib/graphics/chart/io/vector/intermediate/filters/Filter.java new file mode 100644 index 0000000..c937df7 --- /dev/null +++ b/chart/src/main/java/org/xbib/graphics/chart/io/vector/intermediate/filters/Filter.java @@ -0,0 +1,48 @@ +package org.xbib.graphics.chart.io.vector.intermediate.filters; + +import org.xbib.graphics.chart.io.vector.intermediate.commands.Command; + +import java.util.Iterator; +import java.util.LinkedList; +import java.util.List; +import java.util.Queue; + +public abstract class Filter implements Iterable>, Iterator> { + private final Queue> buffer; + private final Iterator> iterator; + + public Filter(Iterable> stream) { + buffer = new LinkedList>(); + iterator = stream.iterator(); + } + + public Iterator> iterator() { + return this; + } + + public boolean hasNext() { + findNextCommand(); + return !buffer.isEmpty(); + } + + private void findNextCommand() { + while (buffer.isEmpty() && iterator.hasNext()) { + Command command = iterator.next(); + List> commands = filter(command); + if (commands != null) { + buffer.addAll(commands); + } + } + } + + public Command next() { + findNextCommand(); + return buffer.poll(); + } + + public void remove() { + } + + protected abstract List> filter(Command command); +} + diff --git a/chart/src/main/java/org/xbib/graphics/chart/io/vector/intermediate/filters/GroupingFilter.java b/chart/src/main/java/org/xbib/graphics/chart/io/vector/intermediate/filters/GroupingFilter.java new file mode 100644 index 0000000..a0e60dc --- /dev/null +++ b/chart/src/main/java/org/xbib/graphics/chart/io/vector/intermediate/filters/GroupingFilter.java @@ -0,0 +1,47 @@ +package org.xbib.graphics.chart.io.vector.intermediate.filters; + +import org.xbib.graphics.chart.io.vector.intermediate.commands.Command; +import org.xbib.graphics.chart.io.vector.intermediate.commands.Group; + +import java.util.Arrays; +import java.util.List; + + +public abstract class GroupingFilter extends Filter { + private Group group; + + public GroupingFilter(Iterable> stream) { + super(stream); + } + + @Override + public boolean hasNext() { + return group != null || super.hasNext(); + } + + @Override + public Command next() { + if (group == null) { + return super.next(); + } + Group g = group; + group = null; + return g; + } + + @Override + protected List> filter(Command command) { + boolean grouped = isGrouped(command); + if (grouped) { + if (group == null) { + group = new Group(); + } + group.add(command); + return null; + } + return Arrays.>asList(command); + } + + protected abstract boolean isGrouped(Command command); +} + diff --git a/chart/src/main/java/org/xbib/graphics/chart/io/vector/intermediate/filters/OptimizeFilter.java b/chart/src/main/java/org/xbib/graphics/chart/io/vector/intermediate/filters/OptimizeFilter.java new file mode 100644 index 0000000..82cf1c7 --- /dev/null +++ b/chart/src/main/java/org/xbib/graphics/chart/io/vector/intermediate/filters/OptimizeFilter.java @@ -0,0 +1,57 @@ +package org.xbib.graphics.chart.io.vector.intermediate.filters; + +import org.xbib.graphics.chart.io.vector.intermediate.commands.AffineTransformCommand; +import org.xbib.graphics.chart.io.vector.intermediate.commands.Command; +import org.xbib.graphics.chart.io.vector.intermediate.commands.SetHintCommand; +import org.xbib.graphics.chart.io.vector.intermediate.commands.StateCommand; + +import java.util.Arrays; +import java.util.Iterator; +import java.util.LinkedList; +import java.util.List; +import java.util.Queue; + +public class OptimizeFilter extends Filter { + private final Queue> buffer; + + public OptimizeFilter(Iterable> stream) { + super(stream); + buffer = new LinkedList>(); + } + + private static boolean isStateChange(Command command) { + return (command instanceof StateCommand) && + !(command instanceof AffineTransformCommand) && + !(command instanceof SetHintCommand); + } + + @Override + public boolean hasNext() { + return super.hasNext(); + } + + @Override + public Command next() { + if (buffer.isEmpty()) { + return super.next(); + } + return buffer.poll(); + } + + @Override + protected List> filter(Command command) { + if (!isStateChange(command)) { + return Arrays.>asList(command); + } + Iterator> i = buffer.iterator(); + Class cls = command.getClass(); + while (i.hasNext()) { + if (cls.equals(i.next().getClass())) { + i.remove(); + } + } + buffer.add(command); + return null; + } +} + diff --git a/chart/src/main/java/org/xbib/graphics/chart/io/vector/intermediate/filters/StateChangeGroupingFilter.java b/chart/src/main/java/org/xbib/graphics/chart/io/vector/intermediate/filters/StateChangeGroupingFilter.java new file mode 100644 index 0000000..fe64a15 --- /dev/null +++ b/chart/src/main/java/org/xbib/graphics/chart/io/vector/intermediate/filters/StateChangeGroupingFilter.java @@ -0,0 +1,18 @@ +package org.xbib.graphics.chart.io.vector.intermediate.filters; + +import org.xbib.graphics.chart.io.vector.intermediate.commands.Command; +import org.xbib.graphics.chart.io.vector.intermediate.commands.StateCommand; + + +public class StateChangeGroupingFilter extends GroupingFilter { + + public StateChangeGroupingFilter(Iterable> stream) { + super(stream); + } + + @Override + protected boolean isGrouped(Command command) { + return command instanceof StateCommand; + } +} + diff --git a/chart/src/main/java/org/xbib/graphics/chart/io/vector/pdf/GeneratedPayload.java b/chart/src/main/java/org/xbib/graphics/chart/io/vector/pdf/GeneratedPayload.java new file mode 100644 index 0000000..ef063af --- /dev/null +++ b/chart/src/main/java/org/xbib/graphics/chart/io/vector/pdf/GeneratedPayload.java @@ -0,0 +1,30 @@ +package org.xbib.graphics.chart.io.vector.pdf; + +import java.io.IOException; + +public abstract class GeneratedPayload extends Payload { + + public GeneratedPayload(boolean stream) { + super(stream); + } + + @Override + public byte[] getBytes() { + try { + for (byte b : generatePayload()) { + super.write(b); + } + } catch (IOException e) { + throw new RuntimeException(e); + } + return super.getBytes(); + } + + @Override + public void write(int b) throws IOException { + throw new UnsupportedOperationException("Payload will be calculated and is read only."); + } + + protected abstract byte[] generatePayload(); +} + diff --git a/chart/src/main/java/org/xbib/graphics/chart/io/vector/pdf/PDFDocument.java b/chart/src/main/java/org/xbib/graphics/chart/io/vector/pdf/PDFDocument.java new file mode 100644 index 0000000..e5f4cce --- /dev/null +++ b/chart/src/main/java/org/xbib/graphics/chart/io/vector/pdf/PDFDocument.java @@ -0,0 +1,647 @@ +package org.xbib.graphics.chart.io.vector.pdf; + +import org.xbib.graphics.chart.io.vector.GraphicsState; +import org.xbib.graphics.chart.io.vector.SizedDocument; +import org.xbib.graphics.chart.io.vector.intermediate.commands.AffineTransformCommand; +import org.xbib.graphics.chart.io.vector.intermediate.commands.Command; +import org.xbib.graphics.chart.io.vector.intermediate.commands.CreateCommand; +import org.xbib.graphics.chart.io.vector.intermediate.commands.DisposeCommand; +import org.xbib.graphics.chart.io.vector.intermediate.commands.DrawImageCommand; +import org.xbib.graphics.chart.io.vector.intermediate.commands.DrawShapeCommand; +import org.xbib.graphics.chart.io.vector.intermediate.commands.DrawStringCommand; +import org.xbib.graphics.chart.io.vector.intermediate.commands.FillShapeCommand; +import org.xbib.graphics.chart.io.vector.intermediate.commands.Group; +import org.xbib.graphics.chart.io.vector.intermediate.commands.SetBackgroundCommand; +import org.xbib.graphics.chart.io.vector.intermediate.commands.SetClipCommand; +import org.xbib.graphics.chart.io.vector.intermediate.commands.SetColorCommand; +import org.xbib.graphics.chart.io.vector.intermediate.commands.SetFontCommand; +import org.xbib.graphics.chart.io.vector.intermediate.commands.SetHintCommand; +import org.xbib.graphics.chart.io.vector.intermediate.commands.SetPaintCommand; +import org.xbib.graphics.chart.io.vector.intermediate.commands.SetStrokeCommand; +import org.xbib.graphics.chart.io.vector.intermediate.commands.SetTransformCommand; +import org.xbib.graphics.chart.io.vector.util.DataUtils; +import org.xbib.graphics.chart.io.vector.util.FlateEncodeStream; +import org.xbib.graphics.chart.io.vector.util.FormattingWriter; +import org.xbib.graphics.chart.io.vector.util.GraphicsUtils; +import org.xbib.graphics.chart.io.vector.util.ImageDataStream; +import org.xbib.graphics.chart.io.vector.util.ImageDataStream.Interleaving; +import org.xbib.graphics.chart.io.vector.util.PageSize; + +import java.awt.BasicStroke; +import java.awt.Color; +import java.awt.Font; +import java.awt.Image; +import java.awt.Shape; +import java.awt.Stroke; +import java.awt.geom.AffineTransform; +import java.awt.geom.PathIterator; +import java.awt.image.BufferedImage; +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; +import java.io.UnsupportedEncodingException; +import java.lang.reflect.InvocationTargetException; +import java.util.Arrays; +import java.util.HashMap; +import java.util.LinkedList; +import java.util.List; +import java.util.Map; +import java.util.Stack; + +public class PDFDocument extends SizedDocument { + private static final String EOL = "\n"; + private static final String CHARSET = "ISO-8859-1"; + private static final String HEADER = "%PDF-1.4"; + private static final String FOOTER = "%%EOF"; + + /** + * Constant to convert values from millimeters to PDF units (1/72th inch). + */ + private static final double MM_IN_UNITS = 72.0 / 25.4; + + /** + * Mapping of stroke endcap values from Java to PDF. + */ + private static final Map STROKE_ENDCAPS = DataUtils.map( + new Integer[]{BasicStroke.CAP_BUTT, BasicStroke.CAP_ROUND, BasicStroke.CAP_SQUARE}, + new Integer[]{0, 1, 2} + ); + + /** + * Mapping of line join values for path drawing from Java to PDF. + */ + private static final Map STROKE_LINEJOIN = DataUtils.map( + new Integer[]{BasicStroke.JOIN_MITER, BasicStroke.JOIN_ROUND, BasicStroke.JOIN_BEVEL}, + new Integer[]{0, 1, 2} + ); + + private final List objects; + private final Map xref; + private final Map images; + private final Stack states; + private int objectIdCounter; + private PDFObject contents; + private Resources resources; + private boolean transformed; + + private boolean compressed; + + public PDFDocument(PageSize pageSize) { + super(pageSize); + states = new Stack<>(); + states.push(new GraphicsState()); + objects = new LinkedList<>(); + objectIdCounter = 1; + xref = new HashMap<>(); + images = new HashMap<>(); + initPage(); + } + + public static String toString(PDFObject obj) { + StringBuilder out = new StringBuilder(); + out.append(obj.id).append(" ").append(obj.version).append(" obj") + .append(EOL); + if (!obj.dict.isEmpty()) { + out.append(serialize(obj.dict)).append(EOL); + } + if (obj.payload != null) { + String content; + try { + content = new String(obj.payload.getBytes(), CHARSET); + } catch (UnsupportedEncodingException e) { + content = ""; + } + if (content.length() > 0) { + if (obj.payload.isStream()) { + out.append("stream").append(EOL); + } + out.append(content); + if (obj.payload.isStream()) { + out.append("endstream"); + } + out.append(EOL); + } + } + out.append("endobj"); + return out.toString(); + } + + private static String serialize(Object obj) { + if (obj instanceof String) { + return "/" + obj.toString(); + } else if (obj instanceof float[]) { + return serialize(DataUtils.asList((float[]) obj)); + } else if (obj instanceof double[]) { + return serialize(DataUtils.asList((double[]) obj)); + } else if (obj instanceof Object[]) { + return serialize(Arrays.asList((Object[]) obj)); + } else if (obj instanceof List) { + List list = (List) obj; + StringBuilder out = new StringBuilder(); + out.append("["); + int i = 0; + for (Object elem : list) { + if (i++ > 0) { + out.append(" "); + } + out.append(serialize(elem)); + } + out.append("]"); + return out.toString(); + } else if (obj instanceof Map) { + Map dict = (Map) obj; + StringBuilder out = new StringBuilder(); + out.append("<<").append(EOL); + for (Map.Entry entry : dict.entrySet()) { + String key = entry.getKey().toString(); + out.append(serialize(key)).append(" "); + + Object value = entry.getValue(); + out.append(serialize(value)).append(EOL); + } + out.append(">>"); + return out.toString(); + } else if (obj instanceof PDFObject) { + PDFObject pdfObj = (PDFObject) obj; + return String.valueOf(pdfObj.id) + " " + pdfObj.version + " R"; + } else { + return DataUtils.format(obj); + } + } + + private static String getOutput(Color c) { + StringBuilder out = new StringBuilder(); + String r = serialize(c.getRed() / 255.0); + String g = serialize(c.getGreen() / 255.0); + String b = serialize(c.getBlue() / 255.0); + out.append(r).append(" ").append(g).append(" ").append(b).append(" rg ") + .append(r).append(" ").append(g).append(" ").append(b).append(" RG"); + return out.toString(); + } + + private static String getOutput(Shape s) { + StringBuilder out = new StringBuilder(); + PathIterator segments = s.getPathIterator(null); + double[] coordsCur = new double[6]; + double[] pointPrev = new double[2]; + for (int i = 0; !segments.isDone(); i++, segments.next()) { + if (i > 0) { + out.append(" "); + } + int segmentType = segments.currentSegment(coordsCur); + switch (segmentType) { + case PathIterator.SEG_MOVETO: + out.append(serialize(coordsCur[0])).append(" ") + .append(serialize(coordsCur[1])).append(" m"); + pointPrev[0] = coordsCur[0]; + pointPrev[1] = coordsCur[1]; + break; + case PathIterator.SEG_LINETO: + out.append(serialize(coordsCur[0])).append(" ") + .append(serialize(coordsCur[1])).append(" l"); + pointPrev[0] = coordsCur[0]; + pointPrev[1] = coordsCur[1]; + break; + case PathIterator.SEG_CUBICTO: + out.append(serialize(coordsCur[0])).append(" ") + .append(serialize(coordsCur[1])).append(" ") + .append(serialize(coordsCur[2])).append(" ") + .append(serialize(coordsCur[3])).append(" ") + .append(serialize(coordsCur[4])).append(" ") + .append(serialize(coordsCur[5])).append(" c"); + pointPrev[0] = coordsCur[4]; + pointPrev[1] = coordsCur[5]; + break; + case PathIterator.SEG_QUADTO: + double x1 = pointPrev[0] + 2.0 / 3.0 * (coordsCur[0] - pointPrev[0]); + double y1 = pointPrev[1] + 2.0 / 3.0 * (coordsCur[1] - pointPrev[1]); + double x2 = coordsCur[0] + 1.0 / 3.0 * (coordsCur[2] - coordsCur[0]); + double y2 = coordsCur[1] + 1.0 / 3.0 * (coordsCur[3] - coordsCur[1]); + double x3 = coordsCur[2]; + double y3 = coordsCur[3]; + out.append(serialize(x1)).append(" ") + .append(serialize(y1)).append(" ") + .append(serialize(x2)).append(" ") + .append(serialize(y2)).append(" ") + .append(serialize(x3)).append(" ") + .append(serialize(y3)).append(" c"); + pointPrev[0] = x3; + pointPrev[1] = y3; + break; + case PathIterator.SEG_CLOSE: + out.append("h"); + break; + default: + throw new IllegalStateException("Unknown path operation."); + } + } + + return out.toString(); + } + + private static String getOutput(GraphicsState state, Resources resources, boolean first) { + StringBuilder out = new StringBuilder(); + + if (!first) { + out.append("Q").append(EOL); + } + out.append("q").append(EOL); + + if (!state.getColor().equals(GraphicsState.DEFAULT_COLOR)) { + if (state.getColor().getAlpha() != GraphicsState.DEFAULT_COLOR.getAlpha()) { + double a = state.getColor().getAlpha() / 255.0; + String resourceId = resources.getId(a); + out.append("/").append(resourceId).append(" gs").append(EOL); + } + out.append(getOutput(state.getColor())).append(EOL); + } + if (!state.getTransform().equals(GraphicsState.DEFAULT_TRANSFORM)) { + out.append(getOutput(state.getTransform())).append(" cm").append(EOL); + } + if (!state.getStroke().equals(GraphicsState.DEFAULT_STROKE)) { + out.append(getOutput(state.getStroke())).append(EOL); + } + if (state.getClip() != GraphicsState.DEFAULT_CLIP) { + out.append(getOutput(state.getClip())).append(" W n").append(EOL); + } + if (!state.getFont().equals(GraphicsState.DEFAULT_FONT)) { + Font font = state.getFont(); + String fontResourceId = resources.getId(font); + float fontSize = font.getSize2D(); + out.append("/").append(fontResourceId).append(" ").append(fontSize) + .append(" Tf").append(EOL); + } + + return DataUtils.stripTrailing(out.toString(), EOL); + } + + private static String getOutput(Stroke s) { + StringBuilder out = new StringBuilder(); + if (s instanceof BasicStroke) { + BasicStroke strokeDefault = (BasicStroke) GraphicsState.DEFAULT_STROKE; + BasicStroke strokeNew = (BasicStroke) s; + if (strokeNew.getLineWidth() != strokeDefault.getLineWidth()) { + out.append(serialize(strokeNew.getLineWidth())) + .append(" w").append(EOL); + } + if (strokeNew.getLineJoin() == BasicStroke.JOIN_MITER && strokeNew.getMiterLimit() != strokeDefault.getMiterLimit()) { + out.append(serialize(strokeNew.getMiterLimit())) + .append(" M").append(EOL); + } + if (strokeNew.getLineJoin() != strokeDefault.getLineJoin()) { + out.append(serialize(STROKE_LINEJOIN.get(strokeNew.getLineJoin()))) + .append(" j").append(EOL); + } + if (strokeNew.getEndCap() != strokeDefault.getEndCap()) { + out.append(serialize(STROKE_ENDCAPS.get(strokeNew.getEndCap()))) + .append(" J").append(EOL); + } + if (strokeNew.getDashArray() != strokeDefault.getDashArray()) { + if (strokeNew.getDashArray() != null) { + out.append(serialize(strokeNew.getDashArray())).append(" ") + .append(serialize(strokeNew.getDashPhase())) + .append(" d").append(EOL); + } else { + out.append(EOL).append("[] 0 d").append(EOL); + } + } + } + return out.toString(); + } + + private static String getOutput(AffineTransform transform) { + double[] matrix = new double[6]; + transform.getMatrix(matrix); + return DataUtils.join(" ", matrix); + } + + private static String getOutput(String str, double x, double y) { + + // Save current graphics state + // Undo swapping of y axis + // Render text + // Restore previous graphics state + + return "q " + "1 0 0 -1 " + x + " " + y + " cm " + "BT " + getOutput(str) + " Tj ET " + "Q"; + } + + private static StringBuilder getOutput(String str) { + StringBuilder out = new StringBuilder(); + + // Escape string + str = str.replaceAll("\\\\", "\\\\\\\\") + .replaceAll("\t", "\\\\t") + .replaceAll("\b", "\\\\b") + .replaceAll("\f", "\\\\f") + .replaceAll("\\(", "\\\\(") + .replaceAll("\\)", "\\\\)") + .replaceAll("[\r\n]", ""); + + out.append("(").append(str).append(")"); + + return out; + } + + private static String getOutput(PDFObject image, double x, double y, + double width, double height, Resources resources) { + // Query image resource id + String resourceId = resources.getId(image); + + // Save graphics state + // Move image to correct position and scale it to (width, height) + // Swap y axis + // Draw image + // Restore old graphics state + + return "q " + width + " 0 0 " + height + " " + x + " " + y + " cm " + "1 0 0 -1 0 1 cm " + "/" + resourceId + " Do " + "Q"; + } + + private GraphicsState getCurrentState() { + return states.peek(); + } + + private void initPage() { + Map dict; + + // Catalog + dict = DataUtils.map( + new String[]{"Type"}, + new Object[]{"Catalog"} + ); + PDFObject catalog = addObject(dict, null); + + // Pages + List pagesKids = new LinkedList<>(); + dict = DataUtils.map( + new String[]{"Type", "Kids", "Count"}, + new Object[]{"Pages", pagesKids, 1} + ); + PDFObject pages = addObject(dict, null); + catalog.dict.put("Pages", pages); + + // Page + double x = getPageSize().x * MM_IN_UNITS; + double y = getPageSize().y * MM_IN_UNITS; + double width = getPageSize().width * MM_IN_UNITS; + double height = getPageSize().height * MM_IN_UNITS; + dict = DataUtils.map( + new String[]{"Type", "Parent", "MediaBox"}, + new Object[]{"Page", pages, new double[]{x, y, width, height}} + ); + PDFObject page = addObject(dict, null); + pagesKids.add(page); + + // Contents + Payload contentsPayload = new Payload(true); + contents = addObject(null, contentsPayload); + page.dict.put("Contents", contents); + + // Compression + if (compressed) { + try { + contentsPayload.addFilter(FlateEncodeStream.class); + contents.dict.put("Filter", new Object[]{"FlateDecode"}); + } catch (NoSuchMethodException | InstantiationException | InvocationTargetException | IllegalAccessException e) { + // ignore + } + } + + // Initial content + try { + contentsPayload.write(DataUtils.join("", new Object[]{ + "q", EOL, + getOutput(getCurrentState().getColor()), EOL, + MM_IN_UNITS, " 0 0 ", -MM_IN_UNITS, " 0 ", height, " cm", EOL + }).getBytes(CHARSET)); + } catch (IOException e) { + throw new RuntimeException(e); + } + + // Content length + Payload contentLengthPayload = new SizePayload(contents, CHARSET, false); + PDFObject contentLength = addObject(null, contentLengthPayload); + contents.dict.put("Length", contentLength); + + // Resources + resources = new Resources(objectIdCounter++, 0); + objects.add(resources); + page.dict.put("Resources", resources); + + // Create initial font + Font font = getCurrentState().getFont(); + String fontResourceId = resources.getId(font); + float fontSize = font.getSize2D(); + StringBuilder out = new StringBuilder(); + out.append("/").append(fontResourceId).append(" ").append(fontSize).append(" Tf").append(EOL); + try { + contentsPayload.write(out.toString().getBytes(CHARSET)); + } catch (IOException e) { + throw new RuntimeException(e); + } + } + + private PDFObject addObject(Map dict, Payload payload) { + final int id = objectIdCounter++; + final int version = 0; + PDFObject object = new PDFObject(id, version, dict, payload); + objects.add(object); + return object; + } + + private PDFObject addObject(Image image) { + BufferedImage bufferedImage = GraphicsUtils.toBufferedImage(image); + + int width = bufferedImage.getWidth(); + int height = bufferedImage.getHeight(); + int bitsPerSample = DataUtils.max(bufferedImage.getSampleModel().getSampleSize()); + int bands = bufferedImage.getSampleModel().getNumBands(); + String colorSpaceName = (bands == 1) ? "DeviceGray" : "DeviceRGB"; + + Payload imagePayload = new Payload(true); + + // Compression + String[] imageFilters = {}; + if (compressed) { + try { + imagePayload.addFilter(FlateEncodeStream.class); + imageFilters = new String[]{"FlateDecode"}; + } catch (NoSuchMethodException | InstantiationException | InvocationTargetException | IllegalAccessException e) { + // ignore + } + } + + InputStream imageDataStream = + new ImageDataStream(bufferedImage, Interleaving.WITHOUT_ALPHA); + + try { + DataUtils.transfer(imageDataStream, imagePayload, 1024); + imagePayload.close(); + } catch (IOException e) { + throw new RuntimeException(e); + } + + int length = imagePayload.getBytes().length; + + Map imageDict = DataUtils.map( + new String[]{"Type", "Subtype", "Width", "Height", "ColorSpace", + "BitsPerComponent", "Length", "Filter"}, + new Object[]{"XObject", "Image", width, height, colorSpaceName, + bitsPerSample, length, imageFilters} + ); + + PDFObject imageObject = addObject(imageDict, imagePayload); + + boolean hasAlpha = bufferedImage.getColorModel().hasAlpha(); + if (hasAlpha) { + BufferedImage mask = GraphicsUtils.getAlphaImage(bufferedImage); + + PDFObject maskObject = addObject(mask); + + boolean isBitmask = mask.getSampleModel().getSampleSize(0) == 1; + if (isBitmask) { + maskObject.dict.put("ImageMask", true); + maskObject.dict.remove("ColorSpace"); + imageObject.dict.put("Mask", maskObject); + } else { + imageObject.dict.put("SMask", maskObject); + } + } + + return imageObject; + } + + public void write(OutputStream out) throws IOException { + FormattingWriter o = new FormattingWriter(out, CHARSET, EOL); + + o.writeln(HEADER); + + for (PDFObject obj : objects) { + xref.put(obj, o.tell()); + o.writeln(toString(obj)); + o.flush(); + } + + long xrefPos = o.tell(); + o.writeln("xref"); + o.write(0).write(" ").writeln(objects.size() + 1); + o.format("%010d %05d f ", 0, 65535).writeln(); + for (PDFObject obj : objects) { + o.format("%010d %05d n ", xref.get(obj), 0).writeln(); + } + o.flush(); + + o.writeln("trailer"); + o.writeln(serialize(DataUtils.map( + new String[]{"Size", "Root"}, + new Object[]{objects.size() + 1, objects.get(0)} + ))); + + o.writeln("startxref"); + o.writeln(xrefPos); + + o.writeln(FOOTER); + o.flush(); + } + + public void handle(Command command) { + String s = ""; + if (command instanceof Group) { + Group c = (Group) command; + applyStateCommands(c.getValue()); + s = getOutput(getCurrentState(), resources, !transformed); + transformed = true; + } else if (command instanceof DrawShapeCommand) { + DrawShapeCommand c = (DrawShapeCommand) command; + s = getOutput(c.getValue()) + " S"; + } else if (command instanceof FillShapeCommand) { + FillShapeCommand c = (FillShapeCommand) command; + s = getOutput(c.getValue()) + " f"; + } else if (command instanceof DrawStringCommand) { + DrawStringCommand c = (DrawStringCommand) command; + s = getOutput(c.getValue(), c.getX(), c.getY()); + } else if (command instanceof DrawImageCommand) { + DrawImageCommand c = (DrawImageCommand) command; + // Create object for image data + Image image = c.getValue(); + PDFObject imageObject = images.get(image.hashCode()); + if (imageObject == null) { + imageObject = addObject(image); + images.put(image.hashCode(), imageObject); + } + s = getOutput(imageObject, c.getX(), c.getY(), + c.getWidth(), c.getHeight(), resources); + } + + try { + Payload contentsPayload = contents.payload; + contentsPayload.write(s.getBytes(CHARSET)); + contentsPayload.write(EOL.getBytes(CHARSET)); + } catch (IOException e) { + throw new RuntimeException(e); + } + } + + private void applyStateCommands(List> commands) { + for (Command command : commands) { + if (command instanceof SetHintCommand) { + SetHintCommand c = (SetHintCommand) command; + getCurrentState().getHints().put(c.getKey(), c.getValue()); + } else if (command instanceof SetBackgroundCommand) { + SetBackgroundCommand c = (SetBackgroundCommand) command; + getCurrentState().setBackground(c.getValue()); + } else if (command instanceof SetColorCommand) { + SetColorCommand c = (SetColorCommand) command; + getCurrentState().setColor(c.getValue()); + } else if (command instanceof SetPaintCommand) { + SetPaintCommand c = (SetPaintCommand) command; + getCurrentState().setPaint(c.getValue()); + } else if (command instanceof SetStrokeCommand) { + SetStrokeCommand c = (SetStrokeCommand) command; + getCurrentState().setStroke(c.getValue()); + } else if (command instanceof SetFontCommand) { + SetFontCommand c = (SetFontCommand) command; + getCurrentState().setFont(c.getValue()); + } else if (command instanceof SetTransformCommand) { + throw new UnsupportedOperationException("The PDF format has no means of setting the transformation matrix."); + } else if (command instanceof AffineTransformCommand) { + AffineTransformCommand c = (AffineTransformCommand) command; + AffineTransform stateTransform = getCurrentState().getTransform(); + AffineTransform transformToBeApplied = c.getValue(); + stateTransform.concatenate(transformToBeApplied); + getCurrentState().setTransform(stateTransform); + } else if (command instanceof SetClipCommand) { + SetClipCommand c = (SetClipCommand) command; + getCurrentState().setClip(c.getValue()); + } else if (command instanceof CreateCommand) { + try { + states.push((GraphicsState) getCurrentState().clone()); + } catch (CloneNotSupportedException e) { + e.printStackTrace(); + } + } else if (command instanceof DisposeCommand) { + states.pop(); + } + } + } + + @Override + public void close() { + try { + String footer = "Q" + EOL; + if (transformed) { + footer += "Q" + EOL; + } + Payload contentsPayload = contents.payload; + contentsPayload.write(footer.getBytes(CHARSET)); + contentsPayload.close(); + } catch (IOException e) { + throw new RuntimeException(e); + } + super.close(); + } + + public void setCompressed(boolean compressed) { + this.compressed = compressed; + } +} + diff --git a/chart/src/main/java/org/xbib/graphics/chart/io/vector/pdf/PDFObject.java b/chart/src/main/java/org/xbib/graphics/chart/io/vector/pdf/PDFObject.java new file mode 100644 index 0000000..6b49f3f --- /dev/null +++ b/chart/src/main/java/org/xbib/graphics/chart/io/vector/pdf/PDFObject.java @@ -0,0 +1,22 @@ +package org.xbib.graphics.chart.io.vector.pdf; + +import java.util.LinkedHashMap; +import java.util.Map; + +public class PDFObject { + public final int id; + public final int version; + public final Map dict; + public final Payload payload; + + public PDFObject(int id, int version, Map dict, Payload payload) { + this.dict = new LinkedHashMap<>(); + this.id = id; + this.version = version; + this.payload = payload; + if (dict != null) { + this.dict.putAll(dict); + } + } +} + diff --git a/chart/src/main/java/org/xbib/graphics/chart/io/vector/pdf/PDFProcessor.java b/chart/src/main/java/org/xbib/graphics/chart/io/vector/pdf/PDFProcessor.java new file mode 100644 index 0000000..e259855 --- /dev/null +++ b/chart/src/main/java/org/xbib/graphics/chart/io/vector/pdf/PDFProcessor.java @@ -0,0 +1,25 @@ +package org.xbib.graphics.chart.io.vector.pdf; + +import org.xbib.graphics.chart.io.vector.Document; +import org.xbib.graphics.chart.io.vector.Processor; +import org.xbib.graphics.chart.io.vector.intermediate.commands.Command; +import org.xbib.graphics.chart.io.vector.intermediate.filters.AbsoluteToRelativeTransformsFilter; +import org.xbib.graphics.chart.io.vector.intermediate.filters.FillPaintedShapeAsImageFilter; +import org.xbib.graphics.chart.io.vector.intermediate.filters.StateChangeGroupingFilter; +import org.xbib.graphics.chart.io.vector.util.PageSize; + +public class PDFProcessor implements Processor { + + public Document process(Iterable> commands, PageSize pageSize) { + AbsoluteToRelativeTransformsFilter absoluteToRelativeTransformsFilter = new AbsoluteToRelativeTransformsFilter(commands); + FillPaintedShapeAsImageFilter paintedShapeAsImageFilter = new FillPaintedShapeAsImageFilter(absoluteToRelativeTransformsFilter); + Iterable> filtered = new StateChangeGroupingFilter(paintedShapeAsImageFilter); + PDFDocument doc = new PDFDocument(pageSize); + for (Command command : filtered) { + doc.handle(command); + } + doc.close(); + return doc; + } +} + diff --git a/chart/src/main/java/org/xbib/graphics/chart/io/vector/pdf/Payload.java b/chart/src/main/java/org/xbib/graphics/chart/io/vector/pdf/Payload.java new file mode 100644 index 0000000..13f9d20 --- /dev/null +++ b/chart/src/main/java/org/xbib/graphics/chart/io/vector/pdf/Payload.java @@ -0,0 +1,50 @@ +package org.xbib.graphics.chart.io.vector.pdf; + +import org.xbib.graphics.chart.io.vector.util.FlateEncodeStream; + +import java.io.ByteArrayOutputStream; +import java.io.IOException; +import java.io.OutputStream; +import java.lang.reflect.InvocationTargetException; + +public class Payload extends OutputStream { + private final ByteArrayOutputStream byteStream; + private final boolean stream; + private OutputStream filteredStream; + private boolean empty; + + public Payload(boolean stream) { + byteStream = new ByteArrayOutputStream(); + filteredStream = byteStream; + this.stream = stream; + empty = true; + } + + public byte[] getBytes() { + return byteStream.toByteArray(); + } + + public boolean isStream() { + return stream; + } + + @Override + public void write(int b) throws IOException { + filteredStream.write(b); + empty = false; + } + + @Override + public void close() throws IOException { + super.close(); + filteredStream.close(); + } + + public void addFilter(Class filterClass) throws NoSuchMethodException, IllegalAccessException, InvocationTargetException, InstantiationException { + if (!empty) { + throw new IllegalStateException("unable to add filter after writing to payload"); + } + filteredStream = filterClass.getConstructor(OutputStream.class).newInstance(filteredStream); + } +} + diff --git a/chart/src/main/java/org/xbib/graphics/chart/io/vector/pdf/Resources.java b/chart/src/main/java/org/xbib/graphics/chart/io/vector/pdf/Resources.java new file mode 100644 index 0000000..7532871 --- /dev/null +++ b/chart/src/main/java/org/xbib/graphics/chart/io/vector/pdf/Resources.java @@ -0,0 +1,99 @@ +package org.xbib.graphics.chart.io.vector.pdf; + +import org.xbib.graphics.chart.io.vector.util.DataUtils; +import org.xbib.graphics.chart.io.vector.util.GraphicsUtils; + +import java.awt.Font; +import java.util.HashMap; +import java.util.LinkedHashMap; +import java.util.Map; +import java.util.concurrent.atomic.AtomicInteger; + +public class Resources extends PDFObject { + + private static final String KEY_PROC_SET = "ProcSet"; + private static final String KEY_TRANSPARENCY = "ExtGState"; + private static final String KEY_FONT = "Font"; + private static final String KEY_IMAGE = "XObject"; + + private static final String[] VALUE_PROC_SET = {"PDF", "Text", "ImageB", "ImageC", "ImageI"}; + + private static final String PREFIX_FONT = "Fnt"; + private static final String PREFIX_IMAGE = "Img"; + private static final String PREFIX_TRANSPARENCY = "Trp"; + + private final Map fonts; + private final Map images; + private final Map transparencies; + + private final AtomicInteger currentFontId = new AtomicInteger(); + private final AtomicInteger currentImageId = new AtomicInteger(); + private final AtomicInteger currentTransparencyId = new AtomicInteger(); + + public Resources(int id, int version) { + super(id, version, null, null); + fonts = new HashMap<>(); + images = new HashMap<>(); + transparencies = new HashMap<>(); + dict.put(KEY_PROC_SET, VALUE_PROC_SET); + } + + private String getResourceId(Map resources, T resource, + String idPrefix, AtomicInteger idCounter) { + String id = resources.get(resource); + if (id == null) { + id = String.format("%s%d", idPrefix, idCounter.getAndIncrement()); + resources.put(resource, id); + } + return id; + } + + @SuppressWarnings("unchecked") + public String getId(Font font) { + Map> dictEntry = + (Map>) dict.get(KEY_FONT); + if (dictEntry == null) { + dictEntry = new LinkedHashMap<>(); + dict.put(KEY_FONT, dictEntry); + } + font = GraphicsUtils.getPhysicalFont(font); + String resourceId = getResourceId(fonts, font, PREFIX_FONT, currentFontId); + String fontName = font.getPSName(); + // TODO: Determine font encoding (e.g. MacRomanEncoding, MacExpertEncoding, WinAnsiEncoding) + String fontEncoding = "WinAnsiEncoding"; + dictEntry.put(resourceId, DataUtils.map( + new String[]{"Type", "Subtype", "Encoding", "BaseFont"}, + new Object[]{"Font", "TrueType", fontEncoding, fontName} + )); + return resourceId; + } + + @SuppressWarnings("unchecked") + public String getId(PDFObject image) { + Map dictEntry = (Map) dict.get(KEY_IMAGE); + if (dictEntry == null) { + dictEntry = new LinkedHashMap<>(); + dict.put(KEY_IMAGE, dictEntry); + } + String resourceId = getResourceId(images, image, PREFIX_IMAGE, currentImageId); + dictEntry.put(resourceId, image); + return resourceId; + } + + @SuppressWarnings("unchecked") + public String getId(Double transparency) { + Map> dictEntry = + (Map>) dict.get(KEY_TRANSPARENCY); + if (dictEntry == null) { + dictEntry = new LinkedHashMap<>(); + dict.put(KEY_TRANSPARENCY, dictEntry); + } + String resourceId = getResourceId(transparencies, transparency, + PREFIX_TRANSPARENCY, currentTransparencyId); + dictEntry.put(resourceId, DataUtils.map( + new String[]{"Type", "ca", "CA"}, + new Object[]{"ExtGState", transparency, transparency} + )); + return resourceId; + } +} diff --git a/chart/src/main/java/org/xbib/graphics/chart/io/vector/pdf/SizePayload.java b/chart/src/main/java/org/xbib/graphics/chart/io/vector/pdf/SizePayload.java new file mode 100644 index 0000000..1d3c156 --- /dev/null +++ b/chart/src/main/java/org/xbib/graphics/chart/io/vector/pdf/SizePayload.java @@ -0,0 +1,28 @@ +package org.xbib.graphics.chart.io.vector.pdf; + +import org.xbib.graphics.chart.io.vector.util.DataUtils; + +import java.io.IOException; + +public class SizePayload extends GeneratedPayload { + private final PDFObject object; + private final String charset; + + public SizePayload(PDFObject object, String charset, boolean stream) { + super(stream); + this.object = object; + this.charset = charset; + } + + @Override + protected byte[] generatePayload() { + try { + object.payload.close(); + String content = DataUtils.format(object.payload.getBytes().length); + return content.getBytes(charset); + } catch (IOException e) { + throw new RuntimeException(e); + } + } +} + diff --git a/chart/src/main/java/org/xbib/graphics/chart/io/vector/svg/SVGDocument.java b/chart/src/main/java/org/xbib/graphics/chart/io/vector/svg/SVGDocument.java new file mode 100644 index 0000000..82fe7a7 --- /dev/null +++ b/chart/src/main/java/org/xbib/graphics/chart/io/vector/svg/SVGDocument.java @@ -0,0 +1,552 @@ +package org.xbib.graphics.chart.io.vector.svg; + +import org.w3c.dom.DOMImplementation; +import org.w3c.dom.Document; +import org.w3c.dom.DocumentType; +import org.w3c.dom.Element; +import org.xbib.graphics.chart.io.vector.GraphicsState; +import org.xbib.graphics.chart.io.vector.SizedDocument; +import org.xbib.graphics.chart.io.vector.VectorHints; +import org.xbib.graphics.chart.io.vector.intermediate.commands.AffineTransformCommand; +import org.xbib.graphics.chart.io.vector.intermediate.commands.Command; +import org.xbib.graphics.chart.io.vector.intermediate.commands.CreateCommand; +import org.xbib.graphics.chart.io.vector.intermediate.commands.DisposeCommand; +import org.xbib.graphics.chart.io.vector.intermediate.commands.DrawImageCommand; +import org.xbib.graphics.chart.io.vector.intermediate.commands.DrawShapeCommand; +import org.xbib.graphics.chart.io.vector.intermediate.commands.DrawStringCommand; +import org.xbib.graphics.chart.io.vector.intermediate.commands.FillShapeCommand; +import org.xbib.graphics.chart.io.vector.intermediate.commands.Group; +import org.xbib.graphics.chart.io.vector.intermediate.commands.SetBackgroundCommand; +import org.xbib.graphics.chart.io.vector.intermediate.commands.SetClipCommand; +import org.xbib.graphics.chart.io.vector.intermediate.commands.SetColorCommand; +import org.xbib.graphics.chart.io.vector.intermediate.commands.SetCompositeCommand; +import org.xbib.graphics.chart.io.vector.intermediate.commands.SetFontCommand; +import org.xbib.graphics.chart.io.vector.intermediate.commands.SetHintCommand; +import org.xbib.graphics.chart.io.vector.intermediate.commands.SetPaintCommand; +import org.xbib.graphics.chart.io.vector.intermediate.commands.SetStrokeCommand; +import org.xbib.graphics.chart.io.vector.intermediate.commands.SetTransformCommand; +import org.xbib.graphics.chart.io.vector.util.Base64EncodeStream; +import org.xbib.graphics.chart.io.vector.util.DataUtils; +import org.xbib.graphics.chart.io.vector.util.GraphicsUtils; +import org.xbib.graphics.chart.io.vector.util.PageSize; + +import javax.imageio.ImageIO; +import javax.xml.parsers.DocumentBuilder; +import javax.xml.parsers.DocumentBuilderFactory; +import javax.xml.parsers.ParserConfigurationException; +import javax.xml.transform.OutputKeys; +import javax.xml.transform.Transformer; +import javax.xml.transform.TransformerException; +import javax.xml.transform.TransformerFactory; +import javax.xml.transform.dom.DOMSource; +import javax.xml.transform.stream.StreamResult; +import java.awt.BasicStroke; +import java.awt.Color; +import java.awt.Font; +import java.awt.Image; +import java.awt.Shape; +import java.awt.Stroke; +import java.awt.geom.AffineTransform; +import java.awt.geom.Ellipse2D; +import java.awt.geom.Line2D; +import java.awt.geom.PathIterator; +import java.awt.geom.Rectangle2D; +import java.awt.geom.RoundRectangle2D; +import java.awt.image.BufferedImage; +import java.io.ByteArrayOutputStream; +import java.io.IOException; +import java.io.OutputStream; +import java.util.HashMap; +import java.util.List; +import java.util.Locale; +import java.util.Map; +import java.util.Stack; + +public class SVGDocument extends SizedDocument { + private static final String SVG_DOCTYPE_QNAME = "svg"; + private static final String SVG_DOCTYPE_PUBLIC_ID = "-//W3C//DTD SVG 1.1//EN"; + private static final String SVG_DOCTYPE_SYSTEM_ID = "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"; + private static final String SVG_NAMESPACE_URI = "http://www.w3.org/2000/svg"; + private static final String XLINK_NAMESPACE = "xlink"; + private static final String XLINK_NAMESPACE_URI = "http://www.w3.org/1999/xlink"; + + private static final String PREFIX_CLIP = "clip"; + + private static final String CHARSET = "UTF-8"; + + private static final double DOTS_PER_MM = 2.834646; // 72 dpi + //private static final double DOTS_PER_MM = 11.811024; // 300 dpi + + /** + * Mapping of stroke endcap values from Java to SVG. + */ + private static final Map STROKE_ENDCAPS = + DataUtils.map(new Integer[]{BasicStroke.CAP_BUTT, BasicStroke.CAP_ROUND, BasicStroke.CAP_SQUARE}, + new String[]{"butt", "round", "square"} + ); + /** + * Mapping of line join values for path drawing from Java to SVG. + */ + private static final Map STROKE_LINEJOIN = + DataUtils.map(new Integer[]{BasicStroke.JOIN_MITER, BasicStroke.JOIN_ROUND, BasicStroke.JOIN_BEVEL}, + new String[]{"miter", "round", "bevel"} + ); + private final Stack states; + private final Document doc; + private final Element root; + private final Map clippingPathElements; + private Element group; + private boolean groupAdded; + private Element defs; + + public SVGDocument(PageSize pageSize) { + super(pageSize); + + states = new Stack(); + states.push(new GraphicsState()); + clippingPathElements = new HashMap(); + + // Prepare DOM + DocumentBuilderFactory docFactory = DocumentBuilderFactory.newInstance(); + docFactory.setValidating(false); + DocumentBuilder docBuilder; + try { + docBuilder = docFactory.newDocumentBuilder(); + } catch (ParserConfigurationException e) { + throw new IllegalStateException("Could not create XML builder."); + } + + // Create XML document with DOCTYPE + DOMImplementation domImpl = docBuilder.getDOMImplementation(); + DocumentType docType = domImpl.createDocumentType(SVG_DOCTYPE_QNAME, SVG_DOCTYPE_PUBLIC_ID, SVG_DOCTYPE_SYSTEM_ID); + doc = domImpl.createDocument(SVG_NAMESPACE_URI, "svg", docType); + // FIXME: Some XML parsers don't support setting standalone to "false" + try { + doc.setXmlStandalone(false); + } catch (AbstractMethodError e) { + System.err.println("Your XML parser does not support standalone XML documents."); + } + + root = doc.getDocumentElement(); + initRoot(); + + group = root; + } + + private static void appendStyle(StringBuilder style, String attribute, Object value) { + style.append(attribute).append(":") + .append(DataUtils.format(value)).append(";"); + } + + private static String getOutput(AffineTransform tx) { + StringBuilder out = new StringBuilder(); + // FIXME: Use tx.getType() to check for transformation components + if (AffineTransform.getTranslateInstance(tx.getTranslateX(), + tx.getTranslateY()).equals(tx)) { + out.append("translate(") + .append(DataUtils.format(tx.getTranslateX())).append(" ") + .append(DataUtils.format(tx.getTranslateY())).append(")"); + } else { + double[] matrix = new double[6]; + tx.getMatrix(matrix); + out.append("matrix(").append(DataUtils.join(" ", matrix)).append(")"); + } + return out.toString(); + } + + private static String getOutput(Color color) { + return String.format((Locale) null, "rgb(%d,%d,%d)", + color.getRed(), color.getGreen(), color.getBlue()); + } + + private static String getOutput(Shape shape) { + StringBuilder out = new StringBuilder(); + PathIterator segments = shape.getPathIterator(null); + double[] coords = new double[6]; + for (int i = 0; !segments.isDone(); i++, segments.next()) { + if (i > 0) { + out.append(" "); + } + int segmentType = segments.currentSegment(coords); + switch (segmentType) { + case PathIterator.SEG_MOVETO: + out.append("M").append(coords[0]).append(",").append(coords[1]); + break; + case PathIterator.SEG_LINETO: + out.append("L").append(coords[0]).append(",").append(coords[1]); + break; + case PathIterator.SEG_CUBICTO: + out.append("C") + .append(coords[0]).append(",").append(coords[1]).append(" ") + .append(coords[2]).append(",").append(coords[3]).append(" ") + .append(coords[4]).append(",").append(coords[5]); + break; + case PathIterator.SEG_QUADTO: + out.append("Q") + .append(coords[0]).append(",").append(coords[1]).append(" ") + .append(coords[2]).append(",").append(coords[3]); + break; + case PathIterator.SEG_CLOSE: + out.append("Z"); + break; + default: + throw new IllegalStateException("Unknown path operation."); + } + } + return out.toString(); + } + + private static String getOutput(Font font) { + StringBuilder out = new StringBuilder(); + if (!GraphicsState.DEFAULT_FONT.getFamily().equals(font.getFamily())) { + String physicalFamily = GraphicsUtils.getPhysicalFont(font).getFamily(); + out.append("font-family:\"").append(physicalFamily).append("\";"); + } + if (font.getSize2D() != GraphicsState.DEFAULT_FONT.getSize2D()) { + out.append("font-size:").append(DataUtils.format(font.getSize2D())).append("px;"); + } + if ((font.getStyle() & Font.ITALIC) != 0) { + out.append("font-style:italic;"); + } + if ((font.getStyle() & Font.BOLD) != 0) { + out.append("font-weight:bold;"); + } + return out.toString(); + } + + private static String getOutput(Image image, boolean lossyAllowed) { + BufferedImage bufferedImage = GraphicsUtils.toBufferedImage(image); + + String encoded = encodeImage(bufferedImage, "png"); + if (!GraphicsUtils.usesAlpha(bufferedImage) && lossyAllowed) { + String encodedLossy = encodeImage(bufferedImage, "jpeg"); + if (encodedLossy.length() > 0 && encodedLossy.length() < encoded.length()) { + encoded = encodedLossy; + } + } + + return encoded; + } + + private static String encodeImage(BufferedImage bufferedImage, String format) { + ByteArrayOutputStream byteStream = new ByteArrayOutputStream(); + Base64EncodeStream encodeStream = new Base64EncodeStream(byteStream); + try { + ImageIO.write(bufferedImage, format, encodeStream); + encodeStream.close(); + String encoded = byteStream.toString("ISO-8859-1"); + return String.format("data:image/%s;base64,%s", format, encoded); + } catch (IOException e) { + return ""; + } + } + + private GraphicsState getCurrentState() { + return states.peek(); + } + + private void initRoot() { + double x = getPageSize().x; + double y = getPageSize().y; + double width = getPageSize().width; + double height = getPageSize().height; + + // Add svg element + root.setAttribute("xmlns:" + XLINK_NAMESPACE, XLINK_NAMESPACE_URI); + root.setAttribute("version", "1.1"); + root.setAttribute("x", DataUtils.format(x / DOTS_PER_MM) + "mm"); + root.setAttribute("y", DataUtils.format(y / DOTS_PER_MM) + "mm"); + root.setAttribute("width", DataUtils.format(width / DOTS_PER_MM) + "mm"); + root.setAttribute("height", DataUtils.format(height / DOTS_PER_MM) + "mm"); + root.setAttribute("viewBox", DataUtils.join(" ", new double[]{x, y, width, height})); + } + + public void write(OutputStream out) throws IOException { + TransformerFactory transformerFactory = TransformerFactory.newInstance(); + try { + Transformer transformer = transformerFactory.newTransformer(); + transformer.setOutputProperty(OutputKeys.INDENT, "yes"); + transformer.setOutputProperty(OutputKeys.STANDALONE, "no"); + transformer.setOutputProperty("{http://xml.apache.org/xslt}indent-amount", "2"); + transformer.setOutputProperty(OutputKeys.ENCODING, CHARSET); + transformer.setOutputProperty(OutputKeys.DOCTYPE_PUBLIC, + doc.getDoctype().getPublicId()); + transformer.setOutputProperty(OutputKeys.DOCTYPE_SYSTEM, + doc.getDoctype().getSystemId()); + transformer.transform(new DOMSource(doc), new StreamResult(out)); + } catch (TransformerException e) { + throw new IOException(e.getMessage()); + } + } + + @Override + public String toString() { + ByteArrayOutputStream out = new ByteArrayOutputStream(); + try { + write(out); + return out.toString(CHARSET); + } catch (IOException e) { + return ""; + } + } + + private void newGroup() { + group = doc.createElement("g"); + groupAdded = false; + + Shape clip = getCurrentState().getClip(); + if (clip != GraphicsState.DEFAULT_CLIP) { + Element clipElem = getClipElement(clip); + String ref = "url(#" + clipElem.getAttribute("id") + ")"; + group.setAttribute("clip-path", ref); + } + + AffineTransform tx = getCurrentState().getTransform(); + if (!GraphicsState.DEFAULT_TRANSFORM.equals(tx)) { + group.setAttribute("transform", getOutput(tx)); + } + } + + private Element getClipElement(Shape clip) { + // Look for existing entries + Element path = clippingPathElements.get(clip.hashCode()); + if (path != null) { + return path; + } + + // Make sure exists + if (defs == null) { + defs = doc.createElement("defs"); + root.insertBefore(defs, root.getFirstChild()); + } + + // Store clipping path in without styling information + path = doc.createElement("clipPath"); + path.setAttribute("id", PREFIX_CLIP + clip.hashCode()); + Element shape = getElement(clip); + shape.removeAttribute("style"); + path.appendChild(shape); + defs.appendChild(path); + + // Register path + clippingPathElements.put(clip.hashCode(), path); + + return path; + } + + private void addToGroup(Element e) { + group.appendChild(e); + if (!groupAdded && group != root) { + root.appendChild(group); + groupAdded = true; + } + } + + public void handle(Command command) { + if (command instanceof Group) { + Group c = (Group) command; + applyStateCommands(c.getValue()); + if (containsGroupCommand(c.getValue())) { + newGroup(); + } + } else if (command instanceof DrawImageCommand) { + DrawImageCommand c = (DrawImageCommand) command; + Element e = getElement(c.getValue(), + c.getX(), c.getY(), c.getWidth(), c.getHeight()); + addToGroup(e); + } else if (command instanceof DrawShapeCommand) { + DrawShapeCommand c = (DrawShapeCommand) command; + Element e = getElement(c.getValue()); + e.setAttribute("style", getStyle(false)); + addToGroup(e); + } else if (command instanceof DrawStringCommand) { + DrawStringCommand c = (DrawStringCommand) command; + Element e = getElement(c.getValue(), c.getX(), c.getY()); + e.setAttribute("style", getStyle(getCurrentState().getFont())); + addToGroup(e); + } else if (command instanceof FillShapeCommand) { + FillShapeCommand c = (FillShapeCommand) command; + Element e = getElement(c.getValue()); + e.setAttribute("style", getStyle(true)); + addToGroup(e); + } + } + + private void applyStateCommands(List> commands) { + for (Command command : commands) { + GraphicsState state = getCurrentState(); + if (command instanceof SetBackgroundCommand) { + SetBackgroundCommand c = (SetBackgroundCommand) command; + state.setBackground(c.getValue()); + } else if (command instanceof SetClipCommand) { + SetClipCommand c = (SetClipCommand) command; + state.setClip(c.getValue()); + } else if (command instanceof SetColorCommand) { + SetColorCommand c = (SetColorCommand) command; + state.setColor(c.getValue()); + } else if (command instanceof SetCompositeCommand) { + SetCompositeCommand c = (SetCompositeCommand) command; + state.setComposite(c.getValue()); + } else if (command instanceof SetFontCommand) { + SetFontCommand c = (SetFontCommand) command; + state.setFont(c.getValue()); + } else if (command instanceof SetPaintCommand) { + SetPaintCommand c = (SetPaintCommand) command; + state.setPaint(c.getValue()); + } else if (command instanceof SetStrokeCommand) { + SetStrokeCommand c = (SetStrokeCommand) command; + state.setStroke(c.getValue()); + } else if (command instanceof SetTransformCommand) { + SetTransformCommand c = (SetTransformCommand) command; + state.setTransform(c.getValue()); + } else if (command instanceof AffineTransformCommand) { + AffineTransformCommand c = (AffineTransformCommand) command; + AffineTransform stateTransform = state.getTransform(); + AffineTransform transformToBeApplied = c.getValue(); + stateTransform.concatenate(transformToBeApplied); + state.setTransform(stateTransform); + } else if (command instanceof SetHintCommand) { + SetHintCommand c = (SetHintCommand) command; + state.getHints().put(c.getKey(), c.getValue()); + } else if (command instanceof CreateCommand) { + try { + states.push((GraphicsState) getCurrentState().clone()); + } catch (CloneNotSupportedException e) { + e.printStackTrace(); + } + } else if (command instanceof DisposeCommand) { + states.pop(); + } + } + } + + private boolean containsGroupCommand(List> commands) { + for (Command command : commands) { + if ((command instanceof SetClipCommand) || + (command instanceof SetTransformCommand) || + (command instanceof AffineTransformCommand)) { + return true; + } + } + return false; + } + + private String getStyle(boolean filled) { + StringBuilder style = new StringBuilder(); + + Color color = getCurrentState().getColor(); + String colorOutput = getOutput(color); + double opacity = color.getAlpha() / 255.0; + + if (filled) { + appendStyle(style, "fill", colorOutput); + if (color.getAlpha() < 255) { + appendStyle(style, "fill-opacity", opacity); + } + } else { + appendStyle(style, "fill", "none"); + } + + if (!filled) { + appendStyle(style, "stroke", colorOutput); + if (color.getAlpha() < 255) { + appendStyle(style, "stroke-opacity", opacity); + } + Stroke stroke = getCurrentState().getStroke(); + if (stroke instanceof BasicStroke) { + BasicStroke bs = (BasicStroke) stroke; + if (bs.getLineWidth() != 1f) { + appendStyle(style, "stroke-width", bs.getLineWidth()); + } + if (bs.getMiterLimit() != 4f) { + appendStyle(style, "stroke-miterlimit", bs.getMiterLimit()); + } + if (bs.getEndCap() != BasicStroke.CAP_BUTT) { + appendStyle(style, "stroke-linecap", STROKE_ENDCAPS.get(bs.getEndCap())); + } + if (bs.getLineJoin() != BasicStroke.JOIN_MITER) { + appendStyle(style, "stroke-linejoin", STROKE_LINEJOIN.get(bs.getLineJoin())); + } + if (bs.getDashArray() != null) { + appendStyle(style, "stroke-dasharray", DataUtils.join(",", bs.getDashArray())); + if (bs.getDashPhase() != 0f) { + appendStyle(style, "stroke-dashoffset", bs.getDashPhase()); + } + } + } + } else { + appendStyle(style, "stroke", "none"); + } + + return style.toString(); + } + + private String getStyle(Font font) { + String style = getStyle(true); + if (!GraphicsState.DEFAULT_FONT.equals(font)) { + style += getOutput(font); + } + return style; + } + + private Element getElement(Shape shape) { + Element elem; + if (shape instanceof Line2D) { + Line2D s = (Line2D) shape; + elem = doc.createElement("line"); + elem.setAttribute("x1", DataUtils.format(s.getX1())); + elem.setAttribute("y1", DataUtils.format(s.getY1())); + elem.setAttribute("x2", DataUtils.format(s.getX2())); + elem.setAttribute("y2", DataUtils.format(s.getY2())); + } else if (shape instanceof Rectangle2D) { + Rectangle2D s = (Rectangle2D) shape; + elem = doc.createElement("rect"); + elem.setAttribute("x", DataUtils.format(s.getX())); + elem.setAttribute("y", DataUtils.format(s.getY())); + elem.setAttribute("width", DataUtils.format(s.getWidth())); + elem.setAttribute("height", DataUtils.format(s.getHeight())); + } else if (shape instanceof RoundRectangle2D) { + RoundRectangle2D s = (RoundRectangle2D) shape; + elem = doc.createElement("rect"); + elem.setAttribute("x", DataUtils.format(s.getX())); + elem.setAttribute("y", DataUtils.format(s.getY())); + elem.setAttribute("width", DataUtils.format(s.getWidth())); + elem.setAttribute("height", DataUtils.format(s.getHeight())); + elem.setAttribute("rx", DataUtils.format(s.getArcWidth() / 2.0)); + elem.setAttribute("ry", DataUtils.format(s.getArcHeight() / 2.0)); + } else if (shape instanceof Ellipse2D) { + Ellipse2D s = (Ellipse2D) shape; + elem = doc.createElement("ellipse"); + elem.setAttribute("cx", DataUtils.format(s.getCenterX())); + elem.setAttribute("cy", DataUtils.format(s.getCenterY())); + elem.setAttribute("rx", DataUtils.format(s.getWidth() / 2.0)); + elem.setAttribute("ry", DataUtils.format(s.getHeight() / 2.0)); + } else { + elem = doc.createElement("path"); + elem.setAttribute("d", getOutput(shape)); + } + return elem; + } + + private Element getElement(String text, double x, double y) { + Element elem = doc.createElement("text"); + elem.appendChild(doc.createTextNode(text)); + elem.setAttribute("x", DataUtils.format(x)); + elem.setAttribute("y", DataUtils.format(y)); + return elem; + } + + private Element getElement(Image image, double x, double y, double width, double height) { + Element elem = doc.createElement("image"); + elem.setAttribute("x", DataUtils.format(x)); + elem.setAttribute("y", DataUtils.format(y)); + elem.setAttribute("width", DataUtils.format(width)); + elem.setAttribute("height", DataUtils.format(height)); + elem.setAttribute("preserveAspectRatio", "none"); + boolean lossyAllowed = getCurrentState().getHints().get(VectorHints.KEY_EXPORT) == + VectorHints.VALUE_EXPORT_SIZE; + elem.setAttribute("xlink:href", getOutput(image, lossyAllowed)); + return elem; + } +} + diff --git a/chart/src/main/java/org/xbib/graphics/chart/io/vector/svg/SVGProcessor.java b/chart/src/main/java/org/xbib/graphics/chart/io/vector/svg/SVGProcessor.java new file mode 100644 index 0000000..90be452 --- /dev/null +++ b/chart/src/main/java/org/xbib/graphics/chart/io/vector/svg/SVGProcessor.java @@ -0,0 +1,21 @@ +package org.xbib.graphics.chart.io.vector.svg; + +import org.xbib.graphics.chart.io.vector.Document; +import org.xbib.graphics.chart.io.vector.Processor; +import org.xbib.graphics.chart.io.vector.intermediate.commands.Command; +import org.xbib.graphics.chart.io.vector.intermediate.filters.FillPaintedShapeAsImageFilter; +import org.xbib.graphics.chart.io.vector.intermediate.filters.StateChangeGroupingFilter; +import org.xbib.graphics.chart.io.vector.util.PageSize; + +public class SVGProcessor implements Processor { + public Document process(Iterable> commands, PageSize pageSize) { + FillPaintedShapeAsImageFilter shapesAsImages = new FillPaintedShapeAsImageFilter(commands); + Iterable> filtered = new StateChangeGroupingFilter(shapesAsImages); + SVGDocument doc = new SVGDocument(pageSize); + for (Command command : filtered) { + doc.handle(command); + } + doc.close(); + return doc; + } +} diff --git a/chart/src/main/java/org/xbib/graphics/chart/io/vector/util/ASCII85EncodeStream.java b/chart/src/main/java/org/xbib/graphics/chart/io/vector/util/ASCII85EncodeStream.java new file mode 100644 index 0000000..887bece --- /dev/null +++ b/chart/src/main/java/org/xbib/graphics/chart/io/vector/util/ASCII85EncodeStream.java @@ -0,0 +1,102 @@ +package org.xbib.graphics.chart.io.vector.util; + +import java.io.FilterOutputStream; +import java.io.IOException; +import java.io.OutputStream; +import java.nio.charset.Charset; +import java.util.Arrays; + +public class ASCII85EncodeStream extends FilterOutputStream { + private static final Charset ISO88591 = Charset.forName("ISO-8859-1"); + private static final int BASE = 85; + private static final int[] POW_85 = + {BASE * BASE * BASE * BASE, BASE * BASE * BASE, BASE * BASE, BASE, 1}; + private static final char[] CHAR_MAP = + "!\"#$%&\'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\\]^_`abcdefghijklmnopqrstu" + .toCharArray(); + private final byte[] data; + private final byte[] prefixBytes; + private final byte[] suffixBytes; + private final byte[] encoded; + private boolean closed; + private int dataSize; + private boolean prefixDone; + + public ASCII85EncodeStream(OutputStream out, String prefix, String suffix) { + super(out); + prefixBytes = (prefix != null ? prefix : "").getBytes(ISO88591); + suffixBytes = (suffix != null ? suffix : "").getBytes(ISO88591); + data = new byte[4]; + encoded = new byte[5]; + } + + public ASCII85EncodeStream(OutputStream out) { + this(out, "", "~>"); + } + + private static long toUInt32(byte[] bytes, int size) { + long uint32 = 0L; + for (int i = 0; i < 4 && i < size; i++) { + uint32 |= (bytes[i] & 0xff) << (3 - i) * 8; + } + return toUnsignedInt(uint32); + } + + private static long toUnsignedInt(long x) { + return x & 0x00000000ffffffffL; + } + + @Override + public void write(int b) throws IOException { + if (closed) { + return; + } + if (!prefixDone) { + out.write(prefixBytes); + prefixDone = true; + } + if (dataSize == data.length) { + writeChunk(); + dataSize = 0; + } + data[dataSize++] = (byte) (b & 0xff); + } + + private void writeChunk() throws IOException { + if (dataSize == 0) { + return; + } + long uint32 = toUInt32(data, dataSize); + int padByteCount = data.length - dataSize; + int encodedSize = encodeChunk(uint32, padByteCount); + out.write(encoded, 0, encodedSize); + } + + private int encodeChunk(long uint32, int padByteCount) { + Arrays.fill(encoded, (byte) 0); + if (uint32 == 0L && padByteCount == 0) { + encoded[0] = 'z'; + return 1; + } + int size = encoded.length - padByteCount; + for (int i = 0; i < size; i++) { + encoded[i] = (byte) CHAR_MAP[(int) (uint32 / POW_85[i] % BASE)]; + } + return size; + } + + @Override + public void close() throws IOException { + if (closed) { + return; + } + + writeChunk(); + + out.write(suffixBytes); + + super.close(); + closed = true; + } +} + diff --git a/chart/src/main/java/org/xbib/graphics/chart/io/vector/util/AlphaToMaskOp.java b/chart/src/main/java/org/xbib/graphics/chart/io/vector/util/AlphaToMaskOp.java new file mode 100644 index 0000000..0e774d8 --- /dev/null +++ b/chart/src/main/java/org/xbib/graphics/chart/io/vector/util/AlphaToMaskOp.java @@ -0,0 +1,101 @@ +package org.xbib.graphics.chart.io.vector.util; + +import java.awt.RenderingHints; +import java.awt.geom.Point2D; +import java.awt.geom.Rectangle2D; +import java.awt.image.BufferedImage; +import java.awt.image.BufferedImageOp; +import java.awt.image.ColorModel; +import java.awt.image.Raster; +import java.awt.image.WritableRaster; +import java.util.Hashtable; + +public class AlphaToMaskOp implements BufferedImageOp { + private final boolean inverted; + + public AlphaToMaskOp(boolean inverted) { + this.inverted = inverted; + } + + public AlphaToMaskOp() { + this(false); + } + + public boolean isInverted() { + return inverted; + } + + public BufferedImage filter(BufferedImage src, BufferedImage dest) { + ColorModel cm = src.getColorModel(); + + if (dest == null) { + dest = createCompatibleDestImage(src, cm); + } else if (dest.getWidth() != src.getWidth() || dest.getHeight() != src.getHeight()) { + throw new IllegalArgumentException("Source and destination images have different dimensions."); + } else if (dest.getColorModel() != cm) { + throw new IllegalArgumentException("Color models don't match."); + } + + if (cm.hasAlpha()) { + Raster srcRaster = src.getRaster(); + WritableRaster destRaster = dest.getRaster(); + + for (int y = 0; y < srcRaster.getHeight(); y++) { + for (int x = 0; x < srcRaster.getWidth(); x++) { + int argb = cm.getRGB(srcRaster.getDataElements(x, y, null)); + int alpha = argb >>> 24; + if (alpha >= 127 && !isInverted() || alpha < 127 && isInverted()) { + argb |= 0xff000000; + } else { + argb &= 0x00ffffff; + } + destRaster.setDataElements(x, y, cm.getDataElements(argb, null)); + } + } + } + + return dest; + } + + public Rectangle2D getBounds2D(BufferedImage src) { + Rectangle2D bounds = new Rectangle2D.Double(); + bounds.setRect(src.getRaster().getBounds()); + return bounds; + } + + public BufferedImage createCompatibleDestImage(BufferedImage src, + ColorModel destCM) { + if (destCM == null) { + destCM = src.getColorModel(); + } + WritableRaster raster = destCM.createCompatibleWritableRaster( + src.getWidth(), src.getHeight()); + boolean isRasterPremultiplied = destCM.isAlphaPremultiplied(); + Hashtable properties = null; + if (src.getPropertyNames() != null) { + properties = new Hashtable(); + for (String key : src.getPropertyNames()) { + properties.put(key, src.getProperty(key)); + } + } + + BufferedImage bimage = new BufferedImage(destCM, raster, + isRasterPremultiplied, properties); + src.copyData(raster); + return bimage; + } + + public Point2D getPoint2D(Point2D srcPt, Point2D dstPt) { + if (dstPt == null) { + dstPt = new Point2D.Double(); + } + dstPt.setLocation(srcPt); + return dstPt; + } + + public RenderingHints getRenderingHints() { + return null; + } + +} + diff --git a/chart/src/main/java/org/xbib/graphics/chart/io/vector/util/Base64EncodeStream.java b/chart/src/main/java/org/xbib/graphics/chart/io/vector/util/Base64EncodeStream.java new file mode 100644 index 0000000..81a6929 --- /dev/null +++ b/chart/src/main/java/org/xbib/graphics/chart/io/vector/util/Base64EncodeStream.java @@ -0,0 +1,83 @@ +package org.xbib.graphics.chart.io.vector.util; + +import java.io.FilterOutputStream; +import java.io.IOException; +import java.io.OutputStream; +import java.util.Arrays; + +public class Base64EncodeStream extends FilterOutputStream { + private static final int BASE = 64; + private static final int[] POW_64 = + {BASE * BASE * BASE, BASE * BASE, BASE, 1}; + private static final char[] CHAR_MAP = + "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/" + .toCharArray(); + private final byte[] data; + private final byte[] encoded; + private boolean closed; + private int dataSize; + + public Base64EncodeStream(OutputStream out) { + super(out); + data = new byte[3]; + encoded = new byte[4]; + } + + private static long toUInt32(byte[] bytes, int size) { + long uint32 = 0L; + int offset = (3 - size) * 8; + for (int i = size - 1; i >= 0; i--) { + uint32 |= (bytes[i] & 0xff) << offset; + offset += 8; + } + return toUnsignedInt(uint32); + } + + private static long toUnsignedInt(long x) { + return x & 0x00000000ffffffffL; + } + + @Override + public void write(int b) throws IOException { + if (closed) { + return; + } + if (dataSize == data.length) { + writeChunk(); + dataSize = 0; + } + data[dataSize++] = (byte) (b & 0xff); + } + + private void writeChunk() throws IOException { + if (dataSize == 0) { + return; + } + long uint32 = toUInt32(data, dataSize); + int padByteCount = data.length - dataSize; + int encodedSize = encodeChunk(uint32, padByteCount); + out.write(encoded, 0, encodedSize); + } + + private int encodeChunk(long uint32, int padByteCount) { + Arrays.fill(encoded, (byte) '='); + int size = encoded.length - padByteCount; + for (int i = 0; i < size; i++) { + encoded[i] = (byte) CHAR_MAP[(int) (uint32 / POW_64[i] % BASE)]; + } + return encoded.length; + } + + @Override + public void close() throws IOException { + if (closed) { + return; + } + + writeChunk(); + + super.close(); + closed = true; + } +} + diff --git a/chart/src/main/java/org/xbib/graphics/chart/io/vector/util/DataUtils.java b/chart/src/main/java/org/xbib/graphics/chart/io/vector/util/DataUtils.java new file mode 100644 index 0000000..faa8a5f --- /dev/null +++ b/chart/src/main/java/org/xbib/graphics/chart/io/vector/util/DataUtils.java @@ -0,0 +1,240 @@ +package org.xbib.graphics.chart.io.vector.util; + +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.LinkedHashMap; +import java.util.List; +import java.util.Map; +import java.util.regex.Pattern; + +/** + * Abstract class that contains utility functions for working with data + * collections like maps or lists. + */ +public abstract class DataUtils { + /** + * Default constructor that prevents creation of class. + */ + protected DataUtils() { + throw new UnsupportedOperationException(); + } + + /** + * Creates a mapping from two arrays, one with keys, one with values. + * + * @param Data type of the keys. + * @param Data type of the values. + * @param keys Array containing the keys. + * @param values Array containing the values. + * @return Map with keys and values from the specified arrays. + */ + public static Map map(K[] keys, V[] values) { + // Check for valid parameters + if (keys.length != values.length) { + throw new IllegalArgumentException( + "Cannot create a Map: " + + "The number of keys and values differs."); + } + // Fill map with keys and values + Map map = new LinkedHashMap(keys.length); + for (int i = 0; i < keys.length; i++) { + K key = keys[i]; + V value = values[i]; + map.put(key, value); + } + return map; + } + + /** + * Returns a string containing all elements concatenated by a specified + * separator. + * + * @param separator Separator string. + * @param elements List of elements that should be concatenated. + * @return a concatenated string. + */ + public static String join(String separator, List elements) { + if (elements == null || elements.size() == 0) { + return ""; + } + StringBuilder sb = new StringBuilder(elements.size() * 3); + int i = 0; + for (Object elem : elements) { + if (separator.length() > 0 && i++ > 0) { + sb.append(separator); + } + sb.append(format(elem)); + } + return sb.toString(); + } + + /** + * Returns a string containing all elements concatenated by a specified + * separator. + * + * @param separator Separator string. + * @param elements Array of elements that should be concatenated. + * @return a concatenated string. + */ + public static String join(String separator, Object[] elements) { + if (elements == null || elements.length == 0) { + return ""; + } + return join(separator, Arrays.asList(elements)); + } + + /** + * Returns a string containing all double numbers concatenated by a + * specified separator. + * + * @param separator Separator string. + * @param elements Array of double numbers that should be concatenated. + * @return a concatenated string. + */ + public static String join(String separator, double[] elements) { + if (elements == null || elements.length == 0) { + return ""; + } + List list = new ArrayList(elements.length); + for (Double element : elements) { + list.add(element); + } + return join(separator, list); + } + + /** + * Returns a string containing all float numbers concatenated by a + * specified separator. + * + * @param separator Separator string. + * @param elements Array of float numbers that should be concatenated. + * @return a concatenated string. + */ + public static String join(String separator, float[] elements) { + if (elements == null || elements.length == 0) { + return ""; + } + List list = new ArrayList(elements.length); + for (Float element : elements) { + list.add(element); + } + return join(separator, list); + } + + /** + * Returns the largest of all specified values. + * + * @param values Several integer values. + * @return largest value. + */ + public static int max(int... values) { + int max = values[0]; + for (int i = 1; i < values.length; i++) { + if (values[i] > max) { + max = values[i]; + } + } + return max; + } + + /** + * Copies data from an input stream to an output stream using a buffer of + * specified size. + * + * @param in Input stream. + * @param out Output stream. + * @param bufferSize Size of the copy buffer. + * @throws IOException when an error occurs while copying. + */ + public static void transfer(InputStream in, OutputStream out, int bufferSize) + throws IOException { + byte[] buffer = new byte[bufferSize]; + int bytesRead; + while ((bytesRead = in.read(buffer)) != -1) { + out.write(buffer, 0, bytesRead); + } + } + + /** + * Returns a formatted string of the specified number. All trailing zeroes + * or decimal points will be stripped. + * + * @param number Number to convert to a string. + * @return A formatted string. + */ + public static String format(Number number) { + String formatted; + if (number instanceof Double || number instanceof Float) { + formatted = Double.toString(number.doubleValue()) + .replaceAll("\\.0+$", "") + .replaceAll("(\\.[0-9]*[1-9])0+$", "$1"); + } else { + formatted = number.toString(); + } + return formatted; + } + + /** + * Returns a formatted string of the specified object. + * + * @param obj Object to convert to a string. + * @return A formatted string. + */ + public static String format(Object obj) { + if (obj instanceof Number) { + return format((Number) obj); + } else { + return obj.toString(); + } + } + + /** + * Converts an array of {@code float} numbers to a list of {@code Float}s. + * The list will be empty if the array is empty or {@code null}. + * + * @param elements Array of float numbers. + * @return A list with all numbers as {@code Float}. + */ + public static List asList(float[] elements) { + int size = (elements != null) ? elements.length : 0; + List list = new ArrayList(size); + if (elements != null) { + for (Float elem : elements) { + list.add(elem); + } + } + return list; + } + + /** + * Converts an array of {@code double} numbers to a list of {@code Double}s. + * The list will be empty if the array is empty or {@code null}. + * + * @param elements Array of double numbers. + * @return A list with all numbers as {@code Double}. + */ + public static List asList(double[] elements) { + int size = (elements != null) ? elements.length : 0; + List list = new ArrayList(size); + if (elements != null) { + for (Double elem : elements) { + list.add(elem); + } + } + return list; + } + + /** + * Removes the specified trailing pattern from a string. + * + * @param s string. + * @param substr trailing pattern. + * @return A string without the trailing pattern. + */ + public static String stripTrailing(String s, String substr) { + return s.replaceAll("(" + Pattern.quote(substr) + ")+$", ""); + } +} diff --git a/chart/src/main/java/org/xbib/graphics/chart/io/vector/util/FlateEncodeStream.java b/chart/src/main/java/org/xbib/graphics/chart/io/vector/util/FlateEncodeStream.java new file mode 100644 index 0000000..7c4a756 --- /dev/null +++ b/chart/src/main/java/org/xbib/graphics/chart/io/vector/util/FlateEncodeStream.java @@ -0,0 +1,11 @@ +package org.xbib.graphics.chart.io.vector.util; + +import java.io.OutputStream; +import java.util.zip.DeflaterOutputStream; + +public class FlateEncodeStream extends DeflaterOutputStream { + public FlateEncodeStream(OutputStream out) { + super(out); + } +} + diff --git a/chart/src/main/java/org/xbib/graphics/chart/io/vector/util/FormattingWriter.java b/chart/src/main/java/org/xbib/graphics/chart/io/vector/util/FormattingWriter.java new file mode 100644 index 0000000..386884d --- /dev/null +++ b/chart/src/main/java/org/xbib/graphics/chart/io/vector/util/FormattingWriter.java @@ -0,0 +1,66 @@ +package org.xbib.graphics.chart.io.vector.util; + +import java.io.Closeable; +import java.io.Flushable; +import java.io.IOException; +import java.io.OutputStream; + +public class FormattingWriter implements Closeable, Flushable { + private final OutputStream out; + private final String encoding; + private final String eolString; + private long position; + + public FormattingWriter(OutputStream out, String encoding, String eol) { + this.out = out; + this.encoding = encoding; + this.eolString = eol; + } + + public FormattingWriter write(String string) throws IOException { + byte[] bytes = string.getBytes(encoding); + out.write(bytes, 0, bytes.length); + position += bytes.length; + return this; + } + + public FormattingWriter write(Number number) throws IOException { + write(DataUtils.format(number)); + return this; + } + + public FormattingWriter writeln() throws IOException { + write(eolString); + return this; + } + + public FormattingWriter writeln(String string) throws IOException { + write(string); + write(eolString); + return this; + } + + public FormattingWriter writeln(Number number) throws IOException { + write(number); + write(eolString); + return this; + } + + public FormattingWriter format(String format, Object... args) throws IOException { + write(String.format(null, format, args)); + return this; + } + + public void flush() throws IOException { + out.flush(); + } + + public void close() throws IOException { + out.close(); + } + + public long tell() { + return position; + } +} + diff --git a/chart/src/main/java/org/xbib/graphics/chart/io/vector/util/GraphicsUtils.java b/chart/src/main/java/org/xbib/graphics/chart/io/vector/util/GraphicsUtils.java new file mode 100644 index 0000000..1b64667 --- /dev/null +++ b/chart/src/main/java/org/xbib/graphics/chart/io/vector/util/GraphicsUtils.java @@ -0,0 +1,411 @@ +package org.xbib.graphics.chart.io.vector.util; + +import javax.swing.ImageIcon; +import java.awt.Font; +import java.awt.Graphics; +import java.awt.GraphicsConfiguration; +import java.awt.GraphicsDevice; +import java.awt.GraphicsEnvironment; +import java.awt.HeadlessException; +import java.awt.Image; +import java.awt.Polygon; +import java.awt.Rectangle; +import java.awt.Shape; +import java.awt.Transparency; +import java.awt.color.ColorSpace; +import java.awt.font.FontRenderContext; +import java.awt.font.TextLayout; +import java.awt.geom.Arc2D; +import java.awt.geom.CubicCurve2D; +import java.awt.geom.Ellipse2D; +import java.awt.geom.Line2D; +import java.awt.geom.Path2D; +import java.awt.geom.PathIterator; +import java.awt.geom.QuadCurve2D; +import java.awt.geom.Rectangle2D; +import java.awt.geom.RoundRectangle2D; +import java.awt.image.BufferedImage; +import java.awt.image.ColorModel; +import java.awt.image.ComponentColorModel; +import java.awt.image.DataBuffer; +import java.awt.image.PixelGrabber; +import java.awt.image.Raster; +import java.awt.image.RenderedImage; +import java.awt.image.WritableRaster; +import java.util.Comparator; +import java.util.HashSet; +import java.util.Hashtable; +import java.util.PriorityQueue; +import java.util.Queue; +import java.util.Set; + +/** + * Abstract class that contains utility functions for working with graphics. + * For example, this includes font handling. + */ +public abstract class GraphicsUtils { + private static final FontRenderContext FONT_RENDER_CONTEXT = + new FontRenderContext(null, false, true); + private static final String FONT_TEST_STRING = + "Falsches Üben von Xylophonmusik quält jeden größeren Zwerg"; + private static final FontExpressivenessComparator FONT_EXPRESSIVENESS_COMPARATOR = + new FontExpressivenessComparator(); + + /** + * Default constructor that prevents creation of class. + */ + protected GraphicsUtils() { + throw new UnsupportedOperationException(); + } + + /** + * This method returns {@code true} if the specified image has the + * possibility to store transparent pixels. + * Inspired by http://www.exampledepot.com/egs/java.awt.image/HasAlpha.html + * + * @param image Image that should be checked for alpha channel. + * @return {@code true} if the specified image can have transparent pixels, + * {@code false} otherwise + */ + public static boolean hasAlpha(Image image) { + ColorModel cm; + // If buffered image, the color model is readily available + if (image instanceof BufferedImage) { + BufferedImage bimage = (BufferedImage) image; + cm = bimage.getColorModel(); + } else { + // Use a pixel grabber to retrieve the image's color model; + // grabbing a single pixel is usually sufficient + PixelGrabber pg = new PixelGrabber(image, 0, 0, 1, 1, false); + try { + pg.grabPixels(); + } catch (InterruptedException e) { + return false; + } + // Get the image's color model + cm = pg.getColorModel(); + } + return cm.hasAlpha(); + } + + /** + * This method returns {@code true} if the specified image has at least one + * pixel that is not fully opaque. + * + * @param image Image that should be checked for non-opaque pixels. + * @return {@code true} if the specified image has transparent pixels, + * {@code false} otherwise + */ + public static boolean usesAlpha(Image image) { + if (image == null) { + return false; + } + BufferedImage bimage = toBufferedImage(image); + Raster alphaRaster = bimage.getAlphaRaster(); + if (alphaRaster == null) { + return false; + } + DataBuffer dataBuffer = alphaRaster.getDataBuffer(); + for (int i = 0; i < dataBuffer.getSize(); i++) { + int alpha = dataBuffer.getElem(i); + if (alpha < 255) { + return true; + } + } + return false; + } + + /** + * Converts an arbitrary image to a {@code BufferedImage}. + * + * @param image Image that should be converted. + * @return a buffered image containing the image pixels, or the original + * instance if the image already was of type {@code BufferedImage}. + */ + public static BufferedImage toBufferedImage(RenderedImage image) { + if (image instanceof BufferedImage) { + return (BufferedImage) image; + } + + ColorModel cm = image.getColorModel(); + WritableRaster raster = cm.createCompatibleWritableRaster( + image.getWidth(), image.getHeight()); + boolean isRasterPremultiplied = cm.isAlphaPremultiplied(); + Hashtable properties = null; + if (image.getPropertyNames() != null) { + properties = new Hashtable(); + for (String key : image.getPropertyNames()) { + properties.put(key, image.getProperty(key)); + } + } + + BufferedImage bimage = new BufferedImage(cm, raster, + isRasterPremultiplied, properties); + image.copyData(raster); + return bimage; + } + + /** + * This method returns a buffered image with the contents of an image. + * Taken from http://www.exampledepot.com/egs/java.awt.image/Image2Buf.html + * + * @param image Image to be converted + * @return a buffered image with the contents of the specified image + */ + public static BufferedImage toBufferedImage(Image image) { + if (image instanceof BufferedImage) { + return (BufferedImage) image; + } + // This code ensures that all the pixels in the image are loaded + image = new ImageIcon(image).getImage(); + // Determine if the image has transparent pixels + boolean hasAlpha = hasAlpha(image); + + // Create a buffered image with a format that's compatible with the + // screen + BufferedImage bimage; + GraphicsEnvironment ge = GraphicsEnvironment + .getLocalGraphicsEnvironment(); + try { + // Determine the type of transparency of the new buffered image + int transparency = Transparency.OPAQUE; + if (hasAlpha) { + transparency = Transparency.TRANSLUCENT; + } + // Create the buffered image + GraphicsDevice gs = ge.getDefaultScreenDevice(); + GraphicsConfiguration gc = gs.getDefaultConfiguration(); + bimage = gc.createCompatibleImage( + image.getWidth(null), image.getHeight(null), transparency); + } catch (HeadlessException e) { + // The system does not have a screen + bimage = null; + } + if (bimage == null) { + // Create a buffered image using the default color model + int type = BufferedImage.TYPE_INT_RGB; + if (hasAlpha) { + type = BufferedImage.TYPE_INT_ARGB; + } + bimage = new BufferedImage( + image.getWidth(null), image.getHeight(null), type); + } + // Copy image to buffered image + Graphics g = bimage.createGraphics(); + // Paint the image onto the buffered image + g.drawImage(image, 0, 0, null); + g.dispose(); + return bimage; + } + + public static Shape clone(Shape shape) { + if (shape == null) { + return null; + } + Shape clone; + if (shape instanceof Line2D) { + clone = (shape instanceof Line2D.Float) ? + new Line2D.Float() : new Line2D.Double(); + ((Line2D) clone).setLine((Line2D) shape); + } else if (shape instanceof Rectangle) { + clone = new Rectangle((Rectangle) shape); + } else if (shape instanceof Rectangle2D) { + clone = (shape instanceof Rectangle2D.Float) ? + new Rectangle2D.Float() : new Rectangle2D.Double(); + ((Rectangle2D) clone).setRect((Rectangle2D) shape); + } else if (shape instanceof RoundRectangle2D) { + clone = (shape instanceof RoundRectangle2D.Float) ? + new RoundRectangle2D.Float() : new RoundRectangle2D.Double(); + ((RoundRectangle2D) clone).setRoundRect((RoundRectangle2D) shape); + } else if (shape instanceof Ellipse2D) { + clone = (shape instanceof Ellipse2D.Float) ? + new Ellipse2D.Float() : new Ellipse2D.Double(); + ((Ellipse2D) clone).setFrame(((Ellipse2D) shape).getFrame()); + } else if (shape instanceof Arc2D) { + clone = (shape instanceof Arc2D.Float) ? + new Arc2D.Float() : new Arc2D.Double(); + ((Arc2D) clone).setArc((Arc2D) shape); + } else if (shape instanceof Polygon) { + Polygon p = (Polygon) shape; + clone = new Polygon(p.xpoints, p.ypoints, p.npoints); + } else if (shape instanceof CubicCurve2D) { + clone = (shape instanceof CubicCurve2D.Float) ? + new CubicCurve2D.Float() : new CubicCurve2D.Double(); + ((CubicCurve2D) clone).setCurve((CubicCurve2D) shape); + } else if (shape instanceof QuadCurve2D) { + clone = (shape instanceof QuadCurve2D.Float) ? + new QuadCurve2D.Float() : new QuadCurve2D.Double(); + ((QuadCurve2D) clone).setCurve((QuadCurve2D) shape); + } else if (shape instanceof Path2D.Float) { + clone = new Path2D.Float(shape); + } else { + clone = new Path2D.Double(shape); + } + return clone; + } + + private static boolean isLogicalFontFamily(String family) { + return (Font.DIALOG.equals(family) || + Font.DIALOG_INPUT.equals(family) || + Font.SANS_SERIF.equals(family) || + Font.SERIF.equals(family) || + Font.MONOSPACED.equals(family)); + } + + /** + * Try to guess physical font from the properties of a logical font, like + * "Dialog", "Serif", "Monospaced" etc. + * + * @param logicalFont Logical font object. + * @param testText Text used to determine font properties. + * @return An object of the first matching physical font. The original font + * object is returned if it was a physical font or no font matched. + */ + public static Font getPhysicalFont(Font logicalFont, String testText) { + String logicalFamily = logicalFont.getFamily(); + if (!isLogicalFontFamily(logicalFamily)) { + return logicalFont; + } + + final TextLayout logicalLayout = + new TextLayout(testText, logicalFont, FONT_RENDER_CONTEXT); + + // Create a list of matches sorted by font expressiveness (in descending order) + Queue physicalFonts = + new PriorityQueue(1, FONT_EXPRESSIVENESS_COMPARATOR); + + Font[] allPhysicalFonts = GraphicsEnvironment.getLocalGraphicsEnvironment().getAllFonts(); + for (Font physicalFont : allPhysicalFonts) { + String physicalFamily = physicalFont.getFamily(); + // Skip logical fonts + if (isLogicalFontFamily(physicalFamily)) { + continue; + } + + // Derive identical variant of physical font + physicalFont = physicalFont.deriveFont( + logicalFont.getStyle(), logicalFont.getSize2D()); + TextLayout physicalLayout = + new TextLayout(testText, physicalFont, FONT_RENDER_CONTEXT); + + // Compare various properties of physical and logical font + if (physicalLayout.getBounds().equals(logicalLayout.getBounds()) && + physicalLayout.getAscent() == logicalLayout.getAscent() && + physicalLayout.getDescent() == logicalLayout.getDescent() && + physicalLayout.getLeading() == logicalLayout.getLeading() && + physicalLayout.getAdvance() == logicalLayout.getAdvance() && + physicalLayout.getVisibleAdvance() == logicalLayout.getVisibleAdvance()) { + // Store matching font in list + physicalFonts.add(physicalFont); + } + } + + // Return a valid font even when no matching font could be found + if (physicalFonts.isEmpty()) { + return logicalFont; + } + + return physicalFonts.poll(); + } + + public static Font getPhysicalFont(Font logicalFont) { + return getPhysicalFont(logicalFont, FONT_TEST_STRING); + } + + public static BufferedImage getAlphaImage(BufferedImage image) { + WritableRaster alphaRaster = image.getAlphaRaster(); + int width = image.getWidth(); + int height = image.getHeight(); + + ColorModel cm; + WritableRaster raster; + // TODO Handle bitmap masks (work on ImageDataStream is necessary) + /* + if (image.getTransparency() == BufferedImage.BITMASK) { + byte[] arr = {(byte) 0, (byte) 255}; + + cm = new IndexColorModel(1, 2, arr, arr, arr); + raster = Raster.createPackedRaster(DataBuffer.TYPE_BYTE, + width, height, 1, 1, null); + } else {*/ + ColorSpace colorSpace = ColorSpace.getInstance(ColorSpace.CS_GRAY); + int[] bits = {8}; + cm = new ComponentColorModel(colorSpace, bits, false, true, + Transparency.OPAQUE, DataBuffer.TYPE_BYTE); + raster = cm.createCompatibleWritableRaster(width, height); + //} + + BufferedImage alphaImage = new BufferedImage(cm, raster, false, null); + + int[] alphaValues = new int[image.getWidth() * alphaRaster.getNumBands()]; + for (int y = 0; y < image.getHeight(); y++) { + alphaRaster.getPixels(0, y, image.getWidth(), 1, alphaValues); + // FIXME Don't force 8-bit alpha channel (see TODO above) + if (image.getTransparency() == BufferedImage.BITMASK) { + for (int i = 0; i < alphaValues.length; i++) { + if (alphaValues[i] > 0) { + alphaValues[i] = 255; + } + } + } + alphaImage.getRaster().setPixels(0, y, image.getWidth(), 1, alphaValues); + } + + return alphaImage; + } + + public static boolean equals(Shape shapeA, Shape shapeB) { + PathIterator pathAIterator = shapeA.getPathIterator(null); + PathIterator pathBIterator = shapeB.getPathIterator(null); + + if (pathAIterator.getWindingRule() != pathBIterator.getWindingRule()) { + return false; + } + double[] pathASegment = new double[6]; + double[] pathBSegment = new double[6]; + while (!pathAIterator.isDone()) { + int pathASegmentType = pathAIterator.currentSegment(pathASegment); + int pathBSegmentType = pathBIterator.currentSegment(pathBSegment); + if (pathASegmentType != pathBSegmentType) { + return false; + } + for (int segmentIndex = 0; segmentIndex < pathASegment.length; segmentIndex++) { + if (pathASegment[segmentIndex] != pathBSegment[segmentIndex]) { + return false; + } + } + + pathAIterator.next(); + pathBIterator.next(); + } + // When the iterator of shapeA is done and shapeA equals shapeB, the iterator of shapeB must also be done + if (!pathBIterator.isDone()) { + return false; + } + return true; + } + + private static class FontExpressivenessComparator implements Comparator { + private static final int[] STYLES = { + Font.PLAIN, Font.ITALIC, Font.BOLD, Font.BOLD | Font.ITALIC + }; + + public int compare(Font font1, Font font2) { + if (font1 == font2) { + return 0; + } + Set variantNames1 = new HashSet(); + Set variantNames2 = new HashSet(); + for (int style : STYLES) { + variantNames1.add(font1.deriveFont(style).getPSName()); + variantNames2.add(font2.deriveFont(style).getPSName()); + } + if (variantNames1.size() < variantNames2.size()) { + return 1; + } else if (variantNames1.size() > variantNames2.size()) { + return -1; + } + return font1.getName().compareTo(font2.getName()); + } + } +} diff --git a/chart/src/main/java/org/xbib/graphics/chart/io/vector/util/ImageDataStream.java b/chart/src/main/java/org/xbib/graphics/chart/io/vector/util/ImageDataStream.java new file mode 100644 index 0000000..e44e7ae --- /dev/null +++ b/chart/src/main/java/org/xbib/graphics/chart/io/vector/util/ImageDataStream.java @@ -0,0 +1,131 @@ +package org.xbib.graphics.chart.io.vector.util; + +import java.awt.image.BufferedImage; +import java.awt.image.Raster; +import java.io.IOException; +import java.io.InputStream; +import java.util.LinkedList; +import java.util.Queue; + +public class ImageDataStream extends InputStream { + private final BufferedImage image; + private final int width; + private final int height; + private final Interleaving interleaving; + private final Raster raster; + private final boolean opaque; + private final Queue byteBuffer; + private final int[] sampleValues; + private final int[] sampleSizes; + private int x; + private int y; + + public ImageDataStream(BufferedImage image, Interleaving interleaving) { + this.image = image; + this.interleaving = interleaving; + + width = image.getWidth(); + height = image.getHeight(); + x = -1; + y = 0; + + Raster alphaRaster = image.getAlphaRaster(); + if (interleaving == Interleaving.ALPHA_ONLY) { + raster = alphaRaster; + } else { + raster = image.getRaster(); + } + opaque = alphaRaster == null; + + byteBuffer = new LinkedList(); + sampleValues = new int[raster.getNumBands()]; + sampleSizes = raster.getSampleModel().getSampleSize(); + } + + public BufferedImage getImage() { + return image; + } + + public Interleaving getInterleaving() { + return interleaving; + } + + @Override + public int read() throws IOException { + if (!byteBuffer.isEmpty()) { + return byteBuffer.poll(); + } else { + if (!nextSample()) { + return -1; + } + int bands = sampleValues.length; + if (interleaving == Interleaving.WITHOUT_ALPHA || + interleaving == Interleaving.ALPHA_ONLY) { + if (interleaving == Interleaving.WITHOUT_ALPHA && !opaque) { + // Ignore alpha band + bands--; + } + for (int band = 0; band < bands; band++) { + bufferSampleValue(band); + } + } else { + if (opaque) { + for (int band = 0; band < bands; band++) { + bufferSampleValue(band); + } + } else { + for (int band = 0; band < bands; band++) { + // Fix order to be ARGB instead of RGBA + if (band == 0) { + bufferSampleValue(bands - 1); + } else { + bufferSampleValue(band - 1); + } + } + } + } + if (!byteBuffer.isEmpty()) { + return byteBuffer.poll(); + } else { + return -1; + } + } + } + + private void bufferSampleValue(int band) { + if (sampleSizes[band] < 8) { + int byteValue = sampleValues[band] & 0xFF; + byteBuffer.offer(byteValue); + } else { + int byteCount = sampleSizes[band] / 8; + for (int i = byteCount - 1; i >= 0; i--) { + int byteValue = (sampleValues[band] >> i * 8) & 0xFF; + byteBuffer.offer(byteValue); + } + } + } + + private boolean nextSample() { + if (interleaving == Interleaving.SAMPLE || interleaving == Interleaving.WITHOUT_ALPHA) { + x++; + if (x >= width) { + x = 0; + y++; + } + } + if (x < 0 || x >= width || y < 0 || y >= height) { + return false; + } else { + raster.getPixel(x, y, sampleValues); + return true; + } + } + + public enum Interleaving { + SAMPLE, + ROW, + WITHOUT_ALPHA, + ALPHA_ONLY + } +} + diff --git a/chart/src/main/java/org/xbib/graphics/chart/io/vector/util/LineWrapOutputStream.java b/chart/src/main/java/org/xbib/graphics/chart/io/vector/util/LineWrapOutputStream.java new file mode 100644 index 0000000..71263da --- /dev/null +++ b/chart/src/main/java/org/xbib/graphics/chart/io/vector/util/LineWrapOutputStream.java @@ -0,0 +1,37 @@ +package org.xbib.graphics.chart.io.vector.util; + +import java.io.FilterOutputStream; +import java.io.IOException; +import java.io.OutputStream; + +public class LineWrapOutputStream extends FilterOutputStream { + public static final String STANDARD_EOL = "\r\n"; + + private final int lineWidth; + private final byte[] eolBytes; + private int written; + + public LineWrapOutputStream(OutputStream sink, int lineWidth, String eol) { + super(sink); + this.lineWidth = lineWidth; + this.eolBytes = eol.getBytes(); + if (lineWidth <= 0) { + throw new IllegalArgumentException("Width must be at least 0."); + } + } + + public LineWrapOutputStream(OutputStream sink, int lineWidth) { + this(sink, lineWidth, STANDARD_EOL); + } + + @Override + public void write(int b) throws IOException { + if (written == lineWidth) { + out.write(eolBytes); + written = 0; + } + out.write(b); + written++; + } +} + diff --git a/chart/src/main/java/org/xbib/graphics/chart/io/vector/util/PageSize.java b/chart/src/main/java/org/xbib/graphics/chart/io/vector/util/PageSize.java new file mode 100644 index 0000000..f51f2c7 --- /dev/null +++ b/chart/src/main/java/org/xbib/graphics/chart/io/vector/util/PageSize.java @@ -0,0 +1,48 @@ +package org.xbib.graphics.chart.io.vector.util; + +import java.awt.geom.Rectangle2D; + +public class PageSize { + private static final double MM_PER_INCH = 2.54; + public static final PageSize TABLOID = new PageSize(11.0 * MM_PER_INCH, 17.0 * MM_PER_INCH); + public static final PageSize LETTER = new PageSize(8.5 * MM_PER_INCH, 11.0 * MM_PER_INCH); + public static final PageSize LEGAL = new PageSize(8.5 * MM_PER_INCH, 14.0 * MM_PER_INCH); + public static final PageSize LEDGER = TABLOID.getLandscape(); + public static final PageSize A3 = new PageSize(297.0, 420.0); + public static final PageSize A4 = new PageSize(210.0, 297.0); + public static final PageSize A5 = new PageSize(148.0, 210.0); + public final double x; + public final double y; + public final double width; + public final double height; + + public PageSize(double x, double y, double width, double height) { + this.x = x; + this.y = y; + this.width = width; + this.height = height; + } + + public PageSize(double width, double height) { + this(0.0, 0.0, width, height); + } + + public PageSize(Rectangle2D size) { + this(size.getX(), size.getY(), size.getWidth(), size.getHeight()); + } + + public PageSize getPortrait() { + if (width <= height) { + return this; + } + return new PageSize(x, y, height, width); + } + + public PageSize getLandscape() { + if (width >= height) { + return this; + } + return new PageSize(x, y, height, width); + } +} + diff --git a/chart/src/main/java/org/xbib/graphics/chart/legend/AbstractLegend.java b/chart/src/main/java/org/xbib/graphics/chart/legend/AbstractLegend.java new file mode 100644 index 0000000..20be123 --- /dev/null +++ b/chart/src/main/java/org/xbib/graphics/chart/legend/AbstractLegend.java @@ -0,0 +1,234 @@ +package org.xbib.graphics.chart.legend; + +import org.xbib.graphics.chart.theme.Theme; +import org.xbib.graphics.chart.Chart; +import org.xbib.graphics.chart.series.Series; +import org.xbib.graphics.chart.style.Styler; + +import java.awt.Graphics2D; +import java.awt.Shape; +import java.awt.font.FontRenderContext; +import java.awt.font.TextLayout; +import java.awt.geom.AffineTransform; +import java.awt.geom.Rectangle2D; +import java.util.LinkedHashMap; +import java.util.Map; + +public abstract class AbstractLegend implements Legend { + + protected static final int BOX_SIZE = 20; + protected static final int BOX_OUTLINE_WIDTH = 5; + + private static final int LEGEND_MARGIN = 6; + private static final int MULTI_LINE_SPACE = 3; + + protected Chart chart; + protected Rectangle2D bounds; + protected double xOffset = 0; + protected double yOffset = 0; + + protected AbstractLegend(Chart chart) { + this.chart = chart; + } + + public abstract double getSeriesLegendRenderGraphicHeight(S series); + + protected abstract void doPaint(Graphics2D g); + + @Override + public Rectangle2D getBounds() { + if (chart.getStyler().getLegendLayout() == LegendLayout.Vertical) { + return getBoundsHintVertical(); + } else { + return getBoundsHintHorizontal(); + } + } + + @Override + public void paint(Graphics2D g) { + if (!chart.getStyler().isLegendVisible()) { + return; + } + if (chart.getSeriesMap().isEmpty()) { + return; + } + if (chart.getPlot().getBounds().getWidth() < 30) { + return; + } + if (chart.getStyler().getLegendLayout() == LegendLayout.Vertical) { + bounds = getBoundsHintVertical(); + } else { + bounds = getBoundsHintHorizontal(); + } + double height = bounds.getHeight(); + switch (chart.getStyler().getLegendPosition()) { + case OutsideE: + xOffset = chart.getWidth() - bounds.getWidth() - chart.getStyler().getChartPadding(); + yOffset = chart.getPlot().getBounds().getY() + (chart.getPlot().getBounds().getHeight() - bounds.getHeight()) / 2.0; + break; + case InsideNW: + xOffset = chart.getPlot().getBounds().getX() + LEGEND_MARGIN; + yOffset = chart.getPlot().getBounds().getY() + LEGEND_MARGIN; + break; + case InsideNE: + xOffset = chart.getPlot().getBounds().getX() + chart.getPlot().getBounds().getWidth() - bounds.getWidth() - LEGEND_MARGIN; + yOffset = chart.getPlot().getBounds().getY() + LEGEND_MARGIN; + break; + case InsideSE: + xOffset = chart.getPlot().getBounds().getX() + chart.getPlot().getBounds().getWidth() - bounds.getWidth() - LEGEND_MARGIN; + yOffset = chart.getPlot().getBounds().getY() + chart.getPlot().getBounds().getHeight() - bounds.getHeight() - LEGEND_MARGIN; + break; + case InsideSW: + xOffset = chart.getPlot().getBounds().getX() + LEGEND_MARGIN; + yOffset = chart.getPlot().getBounds().getY() + chart.getPlot().getBounds().getHeight() - bounds.getHeight() - LEGEND_MARGIN; + break; + case InsideN: + xOffset = chart.getPlot().getBounds().getX() + (chart.getPlot().getBounds().getWidth() - bounds.getWidth()) / 2 + LEGEND_MARGIN; + yOffset = chart.getPlot().getBounds().getY() + LEGEND_MARGIN; + break; + case OutsideS: + xOffset = chart.getPlot().getBounds().getX() + (chart.getPlot().getBounds().getWidth() - bounds.getWidth()) / 2.0; + yOffset = chart.getHeight() - bounds.getHeight() - LEGEND_MARGIN; + break; + default: + break; + } + Shape rect = new Rectangle2D.Double(xOffset, yOffset, bounds.getWidth(), height); + g.setColor(chart.getStyler().getLegendBackgroundColor()); + g.fill(rect); + g.setStroke(Theme.Strokes.LEGEND); + g.setColor(chart.getStyler().getLegendBorderColor()); + g.draw(rect); + doPaint(g); + } + + protected Map getSeriesTextBounds(S series) { + String[] lines = series.getName().split("\\n"); + Map seriesTextBounds = new LinkedHashMap<>(lines.length); + for (String line : lines) { + TextLayout textLayout = new TextLayout(line, chart.getStyler().getLegendFont(), new FontRenderContext(null, true, false)); + Shape shape = textLayout.getOutline(null); + Rectangle2D bounds = shape.getBounds2D(); + seriesTextBounds.put(line, bounds); + } + return seriesTextBounds; + } + + protected float getLegendEntryHeight(Map seriesTextBounds, int markerSize) { + float legendEntryHeight = 0; + for (Map.Entry entry : seriesTextBounds.entrySet()) { + legendEntryHeight += entry.getValue().getHeight() + MULTI_LINE_SPACE; + } + legendEntryHeight -= MULTI_LINE_SPACE; + legendEntryHeight = Math.max(legendEntryHeight, markerSize); + return legendEntryHeight; + } + + protected float getLegendEntryWidth(Map seriesTextBounds, int markerSize) { + float legendEntryWidth = 0; + for (Map.Entry entry : seriesTextBounds.entrySet()) { + legendEntryWidth = Math.max(legendEntryWidth, (float) entry.getValue().getWidth()); + } + return legendEntryWidth + markerSize + chart.getStyler().getLegendPadding(); + } + + protected void paintSeriesText(Graphics2D g, Map seriesTextBounds, int markerSize, double x, double starty) { + g.setColor(chart.getStyler().getChartFontColor()); + g.setFont(chart.getStyler().getLegendFont()); + double multiLineOffset = 0.0; + for (Map.Entry entry : seriesTextBounds.entrySet()) { + double height = entry.getValue().getHeight(); + double centerOffsetY = (Math.max(markerSize, height) - height) / 2.0; + FontRenderContext frc = g.getFontRenderContext(); + TextLayout tl = new TextLayout(entry.getKey(), chart.getStyler().getLegendFont(), frc); + Shape shape = tl.getOutline(null); + AffineTransform orig = g.getTransform(); + AffineTransform at = new AffineTransform(); + at.translate(x, starty + height + centerOffsetY + multiLineOffset); + g.transform(at); + g.fill(shape); + g.setTransform(orig); + multiLineOffset += height + MULTI_LINE_SPACE; + } + } + + /** + * Determine the width and height of the chart legend. + */ + private Rectangle2D getBoundsHintVertical() { + if (!chart.getStyler().isLegendVisible()) { + return new Rectangle2D.Double(); + } + boolean containsBox = false; + double legendTextContentMaxWidth = 0; + double legendContentHeight = 0; + Map map = chart.getSeriesMap(); + for (S series : map.values()) { + if (series.isNotShownInLegend()) { + continue; + } + if (!series.isEnabled()) { + continue; + } + Map seriesTextBounds = getSeriesTextBounds(series); + double legendEntryHeight = 0; + for (Map.Entry entry : seriesTextBounds.entrySet()) { + legendEntryHeight += entry.getValue().getHeight() + MULTI_LINE_SPACE; + legendTextContentMaxWidth = Math.max(legendTextContentMaxWidth, entry.getValue().getWidth()); + } + legendEntryHeight -= MULTI_LINE_SPACE; + legendEntryHeight = Math.max(legendEntryHeight, (getSeriesLegendRenderGraphicHeight(series))); + legendContentHeight += legendEntryHeight + chart.getStyler().getLegendPadding(); + if (series.getLegendRenderType() == LegendRenderType.Box) { + containsBox = true; + } + } + double legendContentWidth; + if (!containsBox) { + legendContentWidth = chart.getStyler().getLegendSeriesLineLength() + chart.getStyler().getLegendPadding() + legendTextContentMaxWidth; + } else { + legendContentWidth = BOX_SIZE + chart.getStyler().getLegendPadding() + legendTextContentMaxWidth; + } + double width = legendContentWidth + 2 * chart.getStyler().getLegendPadding(); + double height = legendContentHeight + chart.getStyler().getLegendPadding(); + return new Rectangle2D.Double(Double.NaN, Double.NaN, width, height); + } + + private Rectangle2D getBoundsHintHorizontal() { + if (!chart.getStyler().isLegendVisible()) { + return new Rectangle2D.Double(); + } + double legendTextContentMaxHeight = 0; + double legendContentWidth = 0; + Map map = chart.getSeriesMap(); + for (S series : map.values()) { + if (series.isNotShownInLegend()) { + continue; + } + if (!series.isEnabled()) { + continue; + } + Map seriesTextBounds = getSeriesTextBounds(series); + double legendEntryHeight = 0; + double legendEntryMaxWidth = 0; + for (Map.Entry entry : seriesTextBounds.entrySet()) { + legendEntryHeight += entry.getValue().getHeight() + MULTI_LINE_SPACE; + legendEntryMaxWidth = Math.max(legendEntryMaxWidth, entry.getValue().getWidth()); + } + legendEntryHeight -= MULTI_LINE_SPACE; + legendTextContentMaxHeight = Math.max(legendEntryHeight, getSeriesLegendRenderGraphicHeight(series)); + legendContentWidth += legendEntryMaxWidth + chart.getStyler().getLegendPadding(); + if (series.getLegendRenderType() == LegendRenderType.Line) { + legendContentWidth = chart.getStyler().getLegendSeriesLineLength() + + chart.getStyler().getLegendPadding() + + legendContentWidth; + } else { + legendContentWidth = BOX_SIZE + chart.getStyler().getLegendPadding() + legendContentWidth; + } + } + double width = legendContentWidth + chart.getStyler().getLegendPadding(); + double height = legendTextContentMaxHeight + chart.getStyler().getLegendPadding() * 2; + return new Rectangle2D.Double(0, 0, width, height); + } + +} diff --git a/chart/src/main/java/org/xbib/graphics/chart/legend/Legend.java b/chart/src/main/java/org/xbib/graphics/chart/legend/Legend.java new file mode 100644 index 0000000..2397978 --- /dev/null +++ b/chart/src/main/java/org/xbib/graphics/chart/legend/Legend.java @@ -0,0 +1,10 @@ +package org.xbib.graphics.chart.legend; + +import org.xbib.graphics.chart.ChartComponent; +import org.xbib.graphics.chart.series.Series; +import org.xbib.graphics.chart.style.Styler; + +public interface Legend extends ChartComponent { + + double getSeriesLegendRenderGraphicHeight(S series); +} diff --git a/chart/src/main/java/org/xbib/graphics/chart/legend/LegendLayout.java b/chart/src/main/java/org/xbib/graphics/chart/legend/LegendLayout.java new file mode 100644 index 0000000..397b70b --- /dev/null +++ b/chart/src/main/java/org/xbib/graphics/chart/legend/LegendLayout.java @@ -0,0 +1,5 @@ +package org.xbib.graphics.chart.legend; + +public enum LegendLayout { + Vertical, Horizontal +} diff --git a/chart/src/main/java/org/xbib/graphics/chart/legend/LegendPosition.java b/chart/src/main/java/org/xbib/graphics/chart/legend/LegendPosition.java new file mode 100644 index 0000000..3f6791e --- /dev/null +++ b/chart/src/main/java/org/xbib/graphics/chart/legend/LegendPosition.java @@ -0,0 +1,5 @@ +package org.xbib.graphics.chart.legend; + +public enum LegendPosition { + OutsideE, InsideNW, InsideNE, InsideSE, InsideSW, InsideN, InsideS, OutsideS +} diff --git a/chart/src/main/java/org/xbib/graphics/chart/legend/LegendRenderType.java b/chart/src/main/java/org/xbib/graphics/chart/legend/LegendRenderType.java new file mode 100644 index 0000000..ff5cb8e --- /dev/null +++ b/chart/src/main/java/org/xbib/graphics/chart/legend/LegendRenderType.java @@ -0,0 +1,5 @@ +package org.xbib.graphics.chart.legend; + +public enum LegendRenderType { + Line, Scatter, Box, BoxNoOutline +} diff --git a/chart/src/main/java/org/xbib/graphics/chart/legend/LegendRenderable.java b/chart/src/main/java/org/xbib/graphics/chart/legend/LegendRenderable.java new file mode 100644 index 0000000..c891cad --- /dev/null +++ b/chart/src/main/java/org/xbib/graphics/chart/legend/LegendRenderable.java @@ -0,0 +1,6 @@ +package org.xbib.graphics.chart.legend; + +public interface LegendRenderable { + + LegendRenderType getLegendRenderType(); +} diff --git a/chart/src/main/java/org/xbib/graphics/chart/legend/MarkerLegend.java b/chart/src/main/java/org/xbib/graphics/chart/legend/MarkerLegend.java new file mode 100644 index 0000000..9575711 --- /dev/null +++ b/chart/src/main/java/org/xbib/graphics/chart/legend/MarkerLegend.java @@ -0,0 +1,117 @@ +package org.xbib.graphics.chart.legend; + +import org.xbib.graphics.chart.Chart; +import org.xbib.graphics.chart.series.MarkerSeries; +import org.xbib.graphics.chart.style.AxesChartStyler; +import org.xbib.graphics.chart.theme.Theme; + +import java.awt.BasicStroke; +import java.awt.Graphics2D; +import java.awt.RenderingHints; +import java.awt.Shape; +import java.awt.geom.Line2D; +import java.awt.geom.Path2D; +import java.awt.geom.Rectangle2D; +import java.util.Map; + +public class MarkerLegend extends AbstractLegend { + + private final ST axesChartStyler; + + public MarkerLegend(Chart chart) { + super(chart); + axesChartStyler = chart.getStyler(); + } + + @Override + public void doPaint(Graphics2D g) { + double startx = xOffset + chart.getStyler().getLegendPadding(); + double starty = yOffset + chart.getStyler().getLegendPadding(); + Object oldHint = g.getRenderingHint(RenderingHints.KEY_ANTIALIASING); + g.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON); + Map map = chart.getSeriesMap(); + for (S series : map.values()) { + if (series.isNotShownInLegend()) { + continue; + } + if (!series.isEnabled()) { + continue; + } + Map seriesTextBounds = getSeriesTextBounds(series); + float legendEntryHeight = getLegendEntryHeight(seriesTextBounds, ((series.getLegendRenderType() == LegendRenderType.Line || + series.getLegendRenderType() == LegendRenderType.Scatter) ? + axesChartStyler.getMarkerSize() : BOX_SIZE)); + if (series.getLegendRenderType() == LegendRenderType.Line || + series.getLegendRenderType() == LegendRenderType.Scatter) { + if (series.getLegendRenderType() == LegendRenderType.Line + && series.getLineStyle() != Theme.Series.NONE_STROKE) { + g.setColor(series.getLineColor()); + g.setStroke(series.getLineStyle()); + Shape line = new Line2D.Double(startx, + starty + legendEntryHeight / 2.0, + startx + chart.getStyler().getLegendSeriesLineLength(), + starty + legendEntryHeight / 2.0); + g.draw(line); + } + if (series.getMarker() != null) { + g.setColor(series.getMarkerColor()); + series.getMarker().paint(g, + startx + chart.getStyler().getLegendSeriesLineLength() / 2.0, + starty + legendEntryHeight / 2.0, + axesChartStyler.getMarkerSize()); + } + } else { + Shape rectSmall = new Rectangle2D.Double(startx, starty, BOX_SIZE, BOX_SIZE); + g.setColor(series.getFillColor()); + g.fill(rectSmall); + if (series.getLegendRenderType() != LegendRenderType.BoxNoOutline) { + g.setColor(series.getLineColor()); + BasicStroke existingLineStyle = series.getLineStyle(); + BasicStroke newLineStyle = new BasicStroke(Math.min(existingLineStyle.getLineWidth(), BOX_OUTLINE_WIDTH * 0.5f), + existingLineStyle.getEndCap(), + existingLineStyle.getLineJoin(), + existingLineStyle.getMiterLimit(), + existingLineStyle.getDashArray(), + existingLineStyle.getDashPhase()); + g.setPaint(series.getLineColor()); + g.setStroke(newLineStyle); + Path2D.Double outlinePath = new Path2D.Double(); + double lineOffset = existingLineStyle.getLineWidth() * 0.5; + outlinePath.moveTo(startx + lineOffset, starty + lineOffset); + outlinePath.lineTo(startx + lineOffset, starty + BOX_SIZE - lineOffset); + outlinePath.lineTo(startx + BOX_SIZE - lineOffset, starty + BOX_SIZE - lineOffset); + outlinePath.lineTo(startx + BOX_SIZE - lineOffset, starty + lineOffset); + outlinePath.closePath(); + g.draw(outlinePath); + } + } + if (series.getLegendRenderType() == LegendRenderType.Line || + series.getLegendRenderType() == LegendRenderType.Scatter) { + double x = startx + chart.getStyler().getLegendSeriesLineLength() + + chart.getStyler().getLegendPadding(); + paintSeriesText(g, seriesTextBounds, axesChartStyler.getMarkerSize(), x, starty); + } else { + double x = startx + BOX_SIZE + chart.getStyler().getLegendPadding(); + paintSeriesText(g, seriesTextBounds, BOX_SIZE, x, starty); + } + if (chart.getStyler().getLegendLayout() == LegendLayout.Vertical) { + starty += legendEntryHeight + chart.getStyler().getLegendPadding(); + } else { + int markerWidth = BOX_SIZE; + if (series.getLegendRenderType() == LegendRenderType.Line) { + markerWidth = chart.getStyler().getLegendSeriesLineLength(); + } + float legendEntryWidth = getLegendEntryWidth(seriesTextBounds, markerWidth); + startx += legendEntryWidth + chart.getStyler().getLegendPadding(); + } + } + g.setRenderingHint(RenderingHints.KEY_ANTIALIASING, oldHint); + } + + @Override + public double getSeriesLegendRenderGraphicHeight(S series) { + return (series.getLegendRenderType() == LegendRenderType.Box || + series.getLegendRenderType() == LegendRenderType.BoxNoOutline) ? + BOX_SIZE : axesChartStyler.getMarkerSize(); + } +} diff --git a/chart/src/main/java/org/xbib/graphics/chart/ohlc/OHLCChart.java b/chart/src/main/java/org/xbib/graphics/chart/ohlc/OHLCChart.java new file mode 100644 index 0000000..bc8b7e6 --- /dev/null +++ b/chart/src/main/java/org/xbib/graphics/chart/ohlc/OHLCChart.java @@ -0,0 +1,328 @@ +package org.xbib.graphics.chart.ohlc; + +import org.xbib.graphics.chart.axis.DataType; +import org.xbib.graphics.chart.axis.Axis; +import org.xbib.graphics.chart.axis.AxisPair; +import org.xbib.graphics.chart.Chart; +import org.xbib.graphics.chart.plot.AxesChartPlot; +import org.xbib.graphics.chart.plot.ContentPlot; +import org.xbib.graphics.chart.style.AxesChartStyler; +import org.xbib.graphics.chart.style.SeriesColorMarkerLineStyle; +import org.xbib.graphics.chart.style.SeriesColorMarkerLineStyleCycler; +import org.xbib.graphics.chart.theme.Theme; + +import java.awt.Color; +import java.awt.Graphics2D; +import java.awt.geom.Line2D; +import java.awt.geom.Rectangle2D; +import java.time.Instant; +import java.util.Iterator; +import java.util.List; +import java.util.Map; + +public class OHLCChart extends Chart { + + public OHLCChart(int width, int height) { + super(width, height, new OHLCStyler()); + axisPair = new AxisPair<>(this); + plot = new OHLCPlot<>(this); + legend = new OHLCLegend<>(this); + } + + public OHLCChart(int width, int height, Theme theme) { + this(width, height); + styler.setTheme(theme); + } + + public OHLCChart(OHLCChartBuilder chartBuilder) { + this(chartBuilder.getWidth(), chartBuilder.getHeight(), chartBuilder.getTheme()); + setTitle(chartBuilder.getTitle()); + setXAxisTitle(chartBuilder.getxAxisTitle()); + setYAxisTitle(chartBuilder.getyAxisTitle()); + } + + public OHLCSeries addSeries(String seriesName, + List xData, + List openData, + List highData, + List lowData, + List closeData) { + + DataType dataType = getDataType(xData); + if (dataType == DataType.Instant) { + return addSeries(seriesName, xData, openData, highData, lowData, closeData, DataType.Instant); + } + return addSeries(seriesName, xData, openData, highData, lowData, closeData, DataType.Number); + } + + public OHLCSeries addSeries(String seriesName, + double[] xData, + double[] openData, + double[] highData, + double[] lowData, + double[] closeData) { + return addSeries(seriesName, + listFromDoubleArray(xData), + listFromDoubleArray(openData), + listFromDoubleArray(highData), + listFromDoubleArray(lowData), + listFromDoubleArray(closeData), DataType.Number); + } + + private OHLCSeries addSeries(String seriesName, + List xData, + List openData, + List highData, + List lowData, + List closeData, + DataType dataType) { + if (seriesMap.containsKey(seriesName)) { + throw new IllegalArgumentException("Series name >" + seriesName + + "< has already been used. Use unique names for each series!!!"); + } + sanityCheck(seriesName, openData, highData, lowData, closeData); + final List xDataToUse; + if (xData != null) { + checkDataLengths(seriesName, "X-Axis", xData, closeData); + xDataToUse = xData; + } else { + xDataToUse = getGeneratedData(closeData.size()); + } + OHLCSeries series = new OHLCSeries(seriesName, xDataToUse, openData, highData, lowData, closeData, dataType); + seriesMap.put(seriesName, series); + return series; + } + + private DataType getDataType(List data) { + if (data == null) { + return DataType.Number; + } + DataType axisType; + Iterator itr = data.iterator(); + Object dataPoint = itr.next(); + if (dataPoint instanceof Number) { + axisType = DataType.Number; + } else if (dataPoint instanceof Instant) { + axisType = DataType.Instant; + } else { + throw new IllegalArgumentException("Series data must be either Number or Instant type"); + } + return axisType; + } + + private void checkData(String seriesName, String dataName, List data) { + if (data == null) { + throw new IllegalArgumentException(dataName + " data cannot be null >" + seriesName); + } + if (data.size() == 0) { + throw new IllegalArgumentException(dataName + " data cannot be empty >" + seriesName); + } + } + + private void checkDataLengths(String seriesName, String data1Name, + List data1, + List data2) { + String data2Name = "Close"; + if (data1.size() != data2.size()) { + throw new IllegalArgumentException( + data1Name + " and " + data2Name + " sizes are not the same >" + seriesName); + } + } + private void sanityCheck(String seriesName, + List openData, + List highData, + List lowData, + List closeData) { + checkData(seriesName, "Open", openData); + checkData(seriesName, "High", highData); + checkData(seriesName, "Low", lowData); + checkData(seriesName, "Close", closeData); + checkDataLengths(seriesName, "Open", openData, closeData); + checkDataLengths(seriesName, "High", highData, closeData); + checkDataLengths(seriesName, "Low", lowData, closeData); + } + + @Override + public void paint(Graphics2D g, int width, int height) { + setWidth(width); + setHeight(height); + // set the series render styles if they are not set. Legend and Plot need it. + for (OHLCSeries series : getSeriesMap().values()) { + OHLCSeriesRenderStyle renderStyle = + series.getOhlcSeriesRenderStyle(); // would be directly set + if (renderStyle == null) { // wasn't overridden, use default from Style Manager + series.setOhlcSeriesRenderStyle(getStyler().getDefaultSeriesRenderStyle()); + } + } + setSeriesStyles(); + paintBackground(g); + axisPair.paint(g); + plot.paint(g); + chartTitle.paint(g); + legend.paint(g); + } + + /** set the series color, marker and line style based on theme */ + private void setSeriesStyles() { + SeriesColorMarkerLineStyleCycler seriesColorMarkerLineStyleCycler = + new SeriesColorMarkerLineStyleCycler(getStyler().getSeriesColors(), + getStyler().getSeriesMarkers(), + getStyler().getSeriesLines()); + for (OHLCSeries series : getSeriesMap().values()) { + SeriesColorMarkerLineStyle seriesColorMarkerLineStyle = + seriesColorMarkerLineStyleCycler.getNextSeriesColorMarkerLineStyle(); + if (series.getLineStyle() == null) { + series.setLineStyle(seriesColorMarkerLineStyle.getStroke()); + } + if (series.getLineColor() == null) { + series.setLineColor(seriesColorMarkerLineStyle.getColor()); + } + if (series.getFillColor() == null) { + series.setFillColor(seriesColorMarkerLineStyle.getColor()); + } + if (series.getUpColor() == null) { + series.setUpColor(Color.GREEN); + } + if (series.getDownColor() == null) { + series.setDownColor(Color.RED); + } + } + } + + private static class OHLCPlot extends AxesChartPlot { + + private OHLCPlot(Chart chart) { + super(chart); + this.contentPlot = new ContentPlotOHLC<>(chart); + } + } + + private static class ContentPlotOHLC extends ContentPlot { + + private final ST ohlcStyler; + + private ContentPlotOHLC(Chart chart) { + super(chart); + ohlcStyler = chart.getStyler(); + } + + @Override + public void doPaint(Graphics2D g) { + double xTickSpace = ohlcStyler.getPlotContentSize() * getBounds().getWidth(); + double xLeftMargin = ((int) getBounds().getWidth() - xTickSpace) / 2.0; + double yTickSpace = ohlcStyler.getPlotContentSize() * getBounds().getHeight(); + double yTopMargin = ((int) getBounds().getHeight() - yTickSpace) / 2.0; + double xMin = chart.getXAxis().getMin(); + double xMax = chart.getXAxis().getMax(); + Line2D.Double line = new Line2D.Double(); + Rectangle2D.Double rect = new Rectangle2D.Double(); + if (ohlcStyler.isXAxisLogarithmic()) { + xMin = Math.log10(xMin); + xMax = Math.log10(xMax); + } + Map map = chart.getSeriesMap(); + for (S series : map.values()) { + if (!series.isEnabled()) { + continue; + } + Axis yAxis = chart.getYAxis(series.getYAxisGroup()); + double yMin = yAxis.getMin(); + double yMax = yAxis.getMax(); + if (ohlcStyler.isYAxisLogarithmic()) { + yMin = Math.log10(yMin); + yMax = Math.log10(yMax); + } + List xData = series.getXData(); + List openData = series.getOpenData(); + List highData = series.getHighData(); + List lowData = series.getLowData(); + List closeData = series.getCloseData(); + double candleHalfWidth = + Math.max(3, xTickSpace / xData.size() / 2 - ohlcStyler.getAxisTickPadding()); + for (int i = 0; i < xData.size(); i++) { + Double x = (Double) xData.get(i); + if (ohlcStyler.isXAxisLogarithmic()) { + x = Math.log10(x); + } + if (Double.isNaN((Double) closeData.get(i))) { + continue; + } + Double openOrig = (Double) openData.get(i); + Double highOrig = (Double) highData.get(i); + Double lowOrig = (Double) lowData.get(i); + Double closeOrig = (Double) closeData.get(i); + double openY; + double highY; + double lowY; + double closeY; + if (ohlcStyler.isYAxisLogarithmic()) { + openY = Math.log10(openOrig); + highY = Math.log10(highOrig); + lowY = Math.log10(lowOrig); + closeY = Math.log10(closeOrig); + } else { + openY = openOrig; + highY = highOrig; + lowY = lowOrig; + closeY = closeOrig; + } + double xTransform = xLeftMargin + ((x - xMin) / (xMax - xMin) * xTickSpace); + double openTransform = + getBounds().getHeight() - (yTopMargin + (openY - yMin) / (yMax - yMin) * yTickSpace); + double highTransform = + getBounds().getHeight() - (yTopMargin + (highY - yMin) / (yMax - yMin) * yTickSpace); + double lowTransform = + getBounds().getHeight() - (yTopMargin + (lowY - yMin) / (yMax - yMin) * yTickSpace); + double closeTransform = + getBounds().getHeight() - (yTopMargin + (closeY - yMin) / (yMax - yMin) * yTickSpace); + if (Math.abs(xMax - xMin) / 5 == 0.0) { + xTransform = getBounds().getWidth() / 2.0; + } + if (Math.abs(yMax - yMin) / 5 == 0.0) { + openTransform = getBounds().getHeight() / 2.0; + highTransform = getBounds().getHeight() / 2.0; + lowTransform = getBounds().getHeight() / 2.0; + closeTransform = getBounds().getHeight() / 2.0; + } + double xOffset = getBounds().getX() + xTransform; + double openOffset = getBounds().getY() + openTransform; + double highOffset = getBounds().getY() + highTransform; + double lowOffset = getBounds().getY() + lowTransform; + double closeOffset = getBounds().getY() + closeTransform; + if (series.getLineStyle() != Theme.Series.NONE_STROKE) { + if (xOffset != -Double.MAX_VALUE + && openOffset != -Double.MAX_VALUE + && highOffset != -Double.MAX_VALUE + && lowOffset != -Double.MAX_VALUE + && closeOffset != -Double.MAX_VALUE) { + g.setColor(series.getLineColor()); + g.setStroke(series.getLineStyle()); + line.setLine(xOffset, highOffset, xOffset, lowOffset); + g.draw(line); + final double xStart = xOffset - candleHalfWidth; + final double xEnd = xOffset + candleHalfWidth; + if (series.getOhlcSeriesRenderStyle() == OHLCSeriesRenderStyle.Candle) { + if (closeOrig > openOrig) { + g.setPaint(series.getUpColor()); + } else { + g.setPaint(series.getDownColor()); + } + rect.setRect(xStart, + Math.min(openOffset, closeOffset), + xEnd - xStart, + Math.abs(closeOffset - openOffset)); + g.fill(rect); + } else { + line.setLine(xStart, openOffset, xOffset, openOffset); + g.draw(line); + line.setLine(xOffset, closeOffset, xEnd, closeOffset); + g.draw(line); + } + } + } + } + g.setColor(series.getFillColor()); + } + } + } +} diff --git a/chart/src/main/java/org/xbib/graphics/chart/ohlc/OHLCChartBuilder.java b/chart/src/main/java/org/xbib/graphics/chart/ohlc/OHLCChartBuilder.java new file mode 100644 index 0000000..8fda9a9 --- /dev/null +++ b/chart/src/main/java/org/xbib/graphics/chart/ohlc/OHLCChartBuilder.java @@ -0,0 +1,35 @@ +package org.xbib.graphics.chart.ohlc; + +import org.xbib.graphics.chart.ChartBuilder; + +public class OHLCChartBuilder extends ChartBuilder { + + private String xAxisTitle = ""; + private String yAxisTitle = ""; + + public OHLCChartBuilder() { + } + + public OHLCChartBuilder xAxisTitle(String xAxisTitle) { + this.xAxisTitle = xAxisTitle; + return this; + } + + public String getxAxisTitle() { + return xAxisTitle; + } + + public OHLCChartBuilder yAxisTitle(String yAxisTitle) { + this.yAxisTitle = yAxisTitle; + return this; + } + + public String getyAxisTitle() { + return yAxisTitle; + } + + @Override + public OHLCChart build() { + return new OHLCChart(this); + } +} diff --git a/chart/src/main/java/org/xbib/graphics/chart/ohlc/OHLCLegend.java b/chart/src/main/java/org/xbib/graphics/chart/ohlc/OHLCLegend.java new file mode 100644 index 0000000..7ce5940 --- /dev/null +++ b/chart/src/main/java/org/xbib/graphics/chart/ohlc/OHLCLegend.java @@ -0,0 +1,72 @@ +package org.xbib.graphics.chart.ohlc; + +import org.xbib.graphics.chart.Chart; +import org.xbib.graphics.chart.legend.AbstractLegend; +import org.xbib.graphics.chart.theme.Theme; +import org.xbib.graphics.chart.legend.LegendLayout; +import org.xbib.graphics.chart.legend.LegendRenderType; + +import java.awt.Graphics2D; +import java.awt.RenderingHints; +import java.awt.Shape; +import java.awt.geom.Line2D; +import java.awt.geom.Rectangle2D; +import java.util.Map; + +public class OHLCLegend extends AbstractLegend { + + private final ST axesChartStyler; + + public OHLCLegend(Chart chart) { + super(chart); + axesChartStyler = chart.getStyler(); + } + + @Override + public void doPaint(Graphics2D g) { + double startx = xOffset + chart.getStyler().getLegendPadding(); + double starty = yOffset + chart.getStyler().getLegendPadding(); + Object oldHint = g.getRenderingHint(RenderingHints.KEY_ANTIALIASING); + g.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON); + Map map = chart.getSeriesMap(); + for (S series : map.values()) { + if (series.isNotShownInLegend()) { + continue; + } + if (!series.isEnabled()) { + continue; + } + Map seriesTextBounds = getSeriesTextBounds(series); + float legendEntryHeight = getLegendEntryHeight(seriesTextBounds, axesChartStyler.getMarkerSize()); + if (series.getLegendRenderType() == LegendRenderType.Line + && series.getLineStyle() != Theme.Series.NONE_STROKE) { + g.setColor(series.getLineColor()); + g.setStroke(series.getLineStyle()); + Shape line = new Line2D.Double(startx, + starty + legendEntryHeight / 2.0, + startx + chart.getStyler().getLegendSeriesLineLength(), + starty + legendEntryHeight / 2.0); + g.draw(line); + } + double x = startx + chart.getStyler().getLegendSeriesLineLength() + + chart.getStyler().getLegendPadding(); + paintSeriesText(g, seriesTextBounds, axesChartStyler.getMarkerSize(), x, starty); + if (chart.getStyler().getLegendLayout() == LegendLayout.Vertical) { + starty += legendEntryHeight + chart.getStyler().getLegendPadding(); + } else { + int markerWidth = chart.getStyler().getLegendSeriesLineLength(); + float legendEntryWidth = getLegendEntryWidth(seriesTextBounds, markerWidth); + startx += legendEntryWidth + chart.getStyler().getLegendPadding(); + } + } + g.setRenderingHint(RenderingHints.KEY_ANTIALIASING, oldHint); + } + + @Override + public double getSeriesLegendRenderGraphicHeight(S series) { + return (series.getLegendRenderType() == LegendRenderType.Box + || series.getLegendRenderType() == LegendRenderType.BoxNoOutline) + ? BOX_SIZE + : axesChartStyler.getMarkerSize(); + } +} diff --git a/chart/src/main/java/org/xbib/graphics/chart/ohlc/OHLCSeries.java b/chart/src/main/java/org/xbib/graphics/chart/ohlc/OHLCSeries.java new file mode 100644 index 0000000..46c863f --- /dev/null +++ b/chart/src/main/java/org/xbib/graphics/chart/ohlc/OHLCSeries.java @@ -0,0 +1,130 @@ +package org.xbib.graphics.chart.ohlc; + +import org.xbib.graphics.chart.axis.DataType; +import org.xbib.graphics.chart.series.AxesChartSeries; +import org.xbib.graphics.chart.legend.LegendRenderType; + +import java.awt.Color; +import java.time.Instant; +import java.util.Arrays; +import java.util.List; + +public class OHLCSeries extends AxesChartSeries { + + private final List xData; + private final List openData; + private final List highData; + private final List lowData; + private final List closeData; + private OHLCSeriesRenderStyle ohlcSeriesRenderStyle; + private Color upColor; + private Color downColor; + + public OHLCSeries(String name, + List xData, + List openData, + List highData, + List lowData, + List closeData, + DataType xDataType) { + super(name, xDataType); + this.xData = xData; + this.openData = openData; + this.highData = highData; + this.lowData = lowData; + this.closeData = closeData; + calculateMinMax(); + } + + public OHLCSeriesRenderStyle getOhlcSeriesRenderStyle() { + return ohlcSeriesRenderStyle; + } + + public void setOhlcSeriesRenderStyle(OHLCSeriesRenderStyle ohlcSeriesRenderStyle) { + this.ohlcSeriesRenderStyle = ohlcSeriesRenderStyle; + } + + public Color getUpColor() { + return upColor; + } + + public void setUpColor(Color color) { + this.upColor = color; + } + + public Color getDownColor() { + return downColor; + } + + public void setDownColor(Color color) { + this.downColor = color; + } + + @Override + public LegendRenderType getLegendRenderType() { + return ohlcSeriesRenderStyle.getLegendRenderType(); + } + + private List findMinMax(List lows, List highs) { + double min = Double.MAX_VALUE; + double max = -Double.MAX_VALUE; + for (int i = 0; i < highs.size(); i++) { + Object h = highs.get(i); + Object l = lows.get(i); + if (h instanceof Double) { + double d = (Double) h; + if (!Double.isNaN(d) && d > max) { + max = d; + } + } else if (h instanceof Instant) { + Instant t = (Instant) h; + if (t.toEpochMilli() > max) { + max = (double) t.toEpochMilli(); + } + } + if (l instanceof Double) { + double d = (Double) l; + if (!Double.isNaN(d) && d < min) { + min = d; + } + } else if (l instanceof Instant) { + Instant t = (Instant) l; + if (t.toEpochMilli() < min) { + min = (double) t.toEpochMilli(); + } + } + } + return Arrays.asList(min, max); + } + + @Override + protected void calculateMinMax() { + List xMinMax = findMinMax(xData, xData); + setXMin(xMinMax.get(0)); + setXMax(xMinMax.get(1)); + List yMinMax = findMinMax(lowData, highData); + setYMin(yMinMax.get(0)); + setYMax(yMinMax.get(1)); + } + + public List getXData() { + return xData; + } + + public List getOpenData() { + return openData; + } + + public List getHighData() { + return highData; + } + + public List getLowData() { + return lowData; + } + + public List getCloseData() { + return closeData; + } + +} diff --git a/chart/src/main/java/org/xbib/graphics/chart/ohlc/OHLCSeriesRenderStyle.java b/chart/src/main/java/org/xbib/graphics/chart/ohlc/OHLCSeriesRenderStyle.java new file mode 100644 index 0000000..5bf5149 --- /dev/null +++ b/chart/src/main/java/org/xbib/graphics/chart/ohlc/OHLCSeriesRenderStyle.java @@ -0,0 +1,21 @@ +package org.xbib.graphics.chart.ohlc; + +import org.xbib.graphics.chart.legend.LegendRenderable; +import org.xbib.graphics.chart.legend.LegendRenderType; + +public enum OHLCSeriesRenderStyle implements LegendRenderable { + Candle(LegendRenderType.Line), + + HiLo(LegendRenderType.Line); + + private final LegendRenderType legendRenderType; + + OHLCSeriesRenderStyle(LegendRenderType legendRenderType) { + this.legendRenderType = legendRenderType; + } + + @Override + public LegendRenderType getLegendRenderType() { + return legendRenderType; + } +} diff --git a/chart/src/main/java/org/xbib/graphics/chart/ohlc/OHLCStyler.java b/chart/src/main/java/org/xbib/graphics/chart/ohlc/OHLCStyler.java new file mode 100644 index 0000000..d9f77a9 --- /dev/null +++ b/chart/src/main/java/org/xbib/graphics/chart/ohlc/OHLCStyler.java @@ -0,0 +1,33 @@ +package org.xbib.graphics.chart.ohlc; + +import org.xbib.graphics.chart.style.AxesChartStyler; +import org.xbib.graphics.chart.theme.Theme; + +public class OHLCStyler extends AxesChartStyler { + + private OHLCSeriesRenderStyle ohlcSeriesRenderStyle; + + public OHLCStyler() { + this.setAllStyles(); + super.setAllStyles(); + } + + @Override + protected void setAllStyles() { + ohlcSeriesRenderStyle = OHLCSeriesRenderStyle.Candle; + } + + public OHLCSeriesRenderStyle getDefaultSeriesRenderStyle() { + return ohlcSeriesRenderStyle; + } + + public OHLCStyler setDefaultSeriesRenderStyle(OHLCSeriesRenderStyle ohlcSeriesRenderStyle) { + this.ohlcSeriesRenderStyle = ohlcSeriesRenderStyle; + return this; + } + + public void setTheme(Theme theme) { + this.theme = theme; + super.setAllStyles(); + } +} diff --git a/chart/src/main/java/org/xbib/graphics/chart/pie/PieChart.java b/chart/src/main/java/org/xbib/graphics/chart/pie/PieChart.java new file mode 100644 index 0000000..94b9118 --- /dev/null +++ b/chart/src/main/java/org/xbib/graphics/chart/pie/PieChart.java @@ -0,0 +1,354 @@ +package org.xbib.graphics.chart.pie; + +import org.xbib.graphics.chart.Chart; +import org.xbib.graphics.chart.plot.CircularPlot; +import org.xbib.graphics.chart.plot.ContentPlot; +import org.xbib.graphics.chart.plot.SurfacePlot; +import org.xbib.graphics.chart.series.Series; +import org.xbib.graphics.chart.style.SeriesColorMarkerLineStyle; +import org.xbib.graphics.chart.style.SeriesColorMarkerLineStyleCycler; +import org.xbib.graphics.chart.theme.Theme; + +import java.awt.Graphics2D; +import java.awt.RenderingHints; +import java.awt.Shape; +import java.awt.font.FontRenderContext; +import java.awt.font.TextLayout; +import java.awt.geom.AffineTransform; +import java.awt.geom.Arc2D; +import java.awt.geom.GeneralPath; +import java.awt.geom.Line2D; +import java.awt.geom.Point2D; +import java.awt.geom.Rectangle2D; +import java.text.DecimalFormat; +import java.util.Map; + +public class PieChart extends Chart { + + public PieChart(int width, int height) { + super(width, height, new PieStyler()); + plot = new PiePlot<>(this); + legend = new PieLegend<>(this); + } + + public PieChart(int width, int height, Theme theme) { + this(width, height); + styler.setTheme(theme); + } + + public PieChart(PieChartBuilder chartBuilder) { + this(chartBuilder.getWidth(), chartBuilder.getHeight(), chartBuilder.getTheme()); + setTitle(chartBuilder.getTitle()); + } + + public PieSeries addSeries(String seriesName, Number value) { + PieSeries series = new PieSeries(seriesName, value); + if (seriesMap.containsKey(seriesName)) { + throw new IllegalArgumentException("Series name >" + seriesName + "< has already been used. Use unique names for each series"); + } + seriesMap.put(seriesName, series); + return series; + } + + @Override + public void paint(Graphics2D g, int width, int height) { + setWidth(width); + setHeight(height); + for (PieSeries pieSeries : getSeriesMap().values()) { + PieSeriesRenderStyle seriesType = pieSeries.getPieSeriesRenderStyle(); + if (seriesType == null) { + pieSeries.setPieSeriesRenderStyle(getStyler().getDefaultSeriesRenderStyle()); + } + } + setSeriesStyles(); + g.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON); + g.setColor(styler.getChartBackgroundColor()); + Shape rect = new Rectangle2D.Double(0, 0, getWidth(), getHeight()); + g.fill(rect); + plot.paint(g); + chartTitle.paint(g); + legend.paint(g); + } + + public void setSeriesStyles() { + SeriesColorMarkerLineStyleCycler seriesColorMarkerLineStyleCycler = + new SeriesColorMarkerLineStyleCycler(getStyler().getSeriesColors(), + getStyler().getSeriesMarkers(), + getStyler().getSeriesLines()); + for (Series series : getSeriesMap().values()) { + SeriesColorMarkerLineStyle seriesColorMarkerLineStyle = + seriesColorMarkerLineStyleCycler.getNextSeriesColorMarkerLineStyle(); + if (series.getFillColor() == null) { // wasn't set manually + series.setFillColor(seriesColorMarkerLineStyle.getColor()); + } + } + } + + private static class PiePlot extends CircularPlot { + + private ContentPlot contentPlot; + + private SurfacePlot surfacePlot; + + private PiePlot(Chart chart) { + super(chart); + } + + protected void initContentAndSurface(Chart chart) { + this.contentPlot = new ContentPlotPie<>(chart); + this.surfacePlot = new SurfacePlotPie<>(chart); + } + + @Override + public void paint(Graphics2D g) { + super.paint(g); + surfacePlot.paint(g); + if (chart.getSeriesMap().isEmpty()) { + return; + } + contentPlot.paint(g); + } + } + + private static class ContentPlotPie extends ContentPlot { + + private final PieStyler pieStyler; + + private final DecimalFormat df = new DecimalFormat("#.0"); + + private ContentPlotPie(Chart chart) { + super(chart); + pieStyler = chart.getStyler(); + } + + @Override + public void doPaint(Graphics2D g) { + double pieFillPercentage = pieStyler.getPlotContentSize(); + double halfBorderPercentage = (1 - pieFillPercentage) / 2.0; + double width = pieStyler.isCircular() ? + Math.min(getBounds().getWidth(), getBounds().getHeight()) : getBounds().getWidth(); + double height = pieStyler.isCircular() ? + Math.min(getBounds().getWidth(), getBounds().getHeight()) : getBounds().getHeight(); + Rectangle2D pieBounds = new Rectangle2D.Double( + getBounds().getX() + + getBounds().getWidth() / 2 + - width / 2 + + halfBorderPercentage * width, + getBounds().getY() + + getBounds().getHeight() / 2 + - height / 2 + + halfBorderPercentage * height, + width * pieFillPercentage, + height * pieFillPercentage); + double total = 0.0; + Map map = chart.getSeriesMap(); + for (S series : map.values()) { + if (!series.isEnabled()) { + continue; + } + total += series.getValue().doubleValue(); + } + if (pieStyler.isSumVisible()) { + DecimalFormat totalDf = + (pieStyler.getDecimalPattern() == null) + ? df + : new DecimalFormat(pieStyler.getDecimalPattern()); + + String annotation = totalDf.format(total); + + TextLayout textLayout = + new TextLayout( + annotation, pieStyler.getSumFont(), new FontRenderContext(null, true, false)); + Shape shape = textLayout.getOutline(null); + g.setColor(pieStyler.getChartFontColor()); + + // compute center + Rectangle2D annotationRectangle = textLayout.getBounds(); + double xCenter = + pieBounds.getX() + pieBounds.getWidth() / 2 - annotationRectangle.getWidth() / 2; + double yCenter = + pieBounds.getY() + pieBounds.getHeight() / 2 + annotationRectangle.getHeight() / 2; + + // set text + AffineTransform orig = g.getTransform(); + AffineTransform at = new AffineTransform(); + at.translate(xCenter, yCenter); + g.transform(at); + g.fill(shape); + g.setTransform(orig); + } + double startAngle = pieStyler.getStartAngleInDegrees() + 90; + map = chart.getSeriesMap(); + for (S series : map.values()) { + if (!series.isEnabled()) { + continue; + } + Number y = series.getValue(); + // draw slice/donut + double arcAngle = (y.doubleValue() * 360 / total); + g.setColor(series.getFillColor()); + // slice + if (PieSeriesRenderStyle.Pie == series.getPieSeriesRenderStyle()) { + Arc2D.Double pieShape = new Arc2D.Double( + pieBounds.getX(), + pieBounds.getY(), + pieBounds.getWidth(), + pieBounds.getHeight(), + startAngle, + arcAngle, + Arc2D.PIE); + g.fill(pieShape); + g.setColor(pieStyler.getPlotBackgroundColor()); + g.draw(pieShape); + } + else { + Shape donutSlice = + getDonutSliceShape(pieBounds, pieStyler.getDonutThickness(), startAngle, arcAngle); + g.fill(donutSlice); + g.setColor(pieStyler.getPlotBackgroundColor()); + g.draw(donutSlice); + } + if (pieStyler.hasAnnotations()) { + String annotation = ""; + if (pieStyler.getAnnotationType() == PieStyler.AnnotationType.Value) { + if (pieStyler.getDecimalPattern() != null) { + DecimalFormat df = new DecimalFormat(pieStyler.getDecimalPattern()); + annotation = df.format(y); + } else { + annotation = y.toString(); + } + } else if (pieStyler.getAnnotationType() == PieStyler.AnnotationType.Label) { + annotation = series.getName(); + } else if (pieStyler.getAnnotationType() == PieStyler.AnnotationType.LabelAndPercentage) { + double percentage = y.doubleValue() / total * 100; + annotation = series.getName() + " (" + df.format(percentage) + "%)"; + } else if (pieStyler.getAnnotationType() == PieStyler.AnnotationType.Percentage) { + double percentage = y.doubleValue() / total * 100; + annotation = df.format(percentage) + "%"; + } + TextLayout textLayout = new TextLayout(annotation, + pieStyler.getAnnotationsFont(), + new FontRenderContext(null, true, false)); + Rectangle2D annotationRectangle = textLayout.getBounds(); + double xCenter = + pieBounds.getX() + pieBounds.getWidth() / 2 - annotationRectangle.getWidth() / 2; + double yCenter = + pieBounds.getY() + pieBounds.getHeight() / 2 + annotationRectangle.getHeight() / 2; + double angle = (arcAngle + startAngle) - arcAngle / 2; + double xOffset = xCenter + Math.cos(Math.toRadians(angle)) + * (pieBounds.getWidth() / 2 * pieStyler.getAnnotationDistance()); + double yOffset = yCenter - Math.sin(Math.toRadians(angle)) + * (pieBounds.getHeight() / 2 * pieStyler.getAnnotationDistance()); + + // get annotation width + Shape shape = textLayout.getOutline(null); + Rectangle2D annotationBounds = shape.getBounds2D(); + double annotationWidth = annotationBounds.getWidth(); + double annotationHeight = annotationBounds.getHeight(); + + // get slice area + double xOffset1 = xCenter + + Math.cos(Math.toRadians(startAngle)) + * (pieBounds.getWidth() / 2 * pieStyler.getAnnotationDistance()); + double yOffset1 = yCenter + - Math.sin(Math.toRadians(startAngle)) + * (pieBounds.getHeight() / 2 * pieStyler.getAnnotationDistance()); + double xOffset2 = xCenter + + Math.cos(Math.toRadians((arcAngle + startAngle))) + * (pieBounds.getWidth() / 2 * pieStyler.getAnnotationDistance()); + double yOffset2 = yCenter + - Math.sin(Math.toRadians((arcAngle + startAngle))) + * (pieBounds.getHeight() / 2 * pieStyler.getAnnotationDistance()); + double xDiff = Math.abs(xOffset1 - xOffset2); + double yDiff = Math.abs(yOffset1 - yOffset2); + boolean annotationWillFit = false; + if (xDiff >= yDiff) { // assume more vertically orientated slice + if (annotationWidth < xDiff) { + annotationWillFit = true; + } + } else if (xDiff <= yDiff) { // assume more horizontally orientated slice + if (annotationHeight < yDiff) { + annotationWillFit = true; + } + } + if (pieStyler.isDrawAllAnnotations() || annotationWillFit) { + g.setColor(pieStyler.getChartFontColor()); + g.setFont(pieStyler.getAnnotationsFont()); + AffineTransform orig = g.getTransform(); + AffineTransform at = new AffineTransform(); + if (pieStyler.getAnnotationDistance() <= 1.0) { + at.translate(xOffset, yOffset); + } + else { + xCenter = pieBounds.getX() + pieBounds.getWidth() / 2; + yCenter = pieBounds.getY() + pieBounds.getHeight() / 2; + double endPoint = (3.0 - pieStyler.getAnnotationDistance()); + double xOffsetStart = xCenter + Math.cos(Math.toRadians(angle)) * (pieBounds.getWidth() / 2.01); + double xOffsetEnd = xCenter + Math.cos(Math.toRadians(angle)) * (pieBounds.getWidth() / endPoint); + double yOffsetStart = yCenter - Math.sin(Math.toRadians(angle)) * (pieBounds.getHeight() / 2.01); + double yOffsetEnd = yCenter - Math.sin(Math.toRadians(angle)) * (pieBounds.getHeight() / endPoint); + g.setStroke(Theme.Strokes.PIE); + Shape line = new Line2D.Double(xOffsetStart, yOffsetStart, xOffsetEnd, yOffsetEnd); + g.draw(line); + at.translate(xOffset - Math.sin(Math.toRadians(angle - 90)) * annotationWidth / 2 + 3, yOffset); + } + g.transform(at); + g.fill(shape); + g.setTransform(orig); + } + } + startAngle += arcAngle; + } + } + + Shape getDonutSliceShape(Rectangle2D pieBounds, double thickness, double start, double extent) { + thickness = thickness / 2; + GeneralPath generalPath = new GeneralPath(); + GeneralPath dummy = new GeneralPath(); + double x = pieBounds.getX(); + double y = pieBounds.getY(); + double width = pieBounds.getWidth(); + double height = pieBounds.getHeight(); + Shape outer = new Arc2D.Double(x, y, width, height, start, extent, Arc2D.OPEN); + double wt = width * thickness; + double ht = height * thickness; + Shape inner = new Arc2D.Double(x + wt, y + ht, width - 2 * wt, height - 2 * ht, start + extent, -extent, Arc2D.OPEN); + generalPath.append(outer, false); + dummy.append(new Arc2D.Double(x + wt, y + ht, width - 2 * wt, height - 2 * ht, start, extent, Arc2D.OPEN), + false); + Point2D point = dummy.getCurrentPoint(); + if (point != null) { + generalPath.lineTo(point.getX(), point.getY()); + } + generalPath.append(inner, false); + dummy.append(new Arc2D.Double(x, y, width, height, start + extent, -extent, Arc2D.OPEN), false); + point = dummy.getCurrentPoint(); + generalPath.lineTo(point.getX(), point.getY()); + return generalPath; + } + } + + private static class SurfacePlotPie extends SurfacePlot { + + private final ST pieStyler; + + private SurfacePlotPie(Chart chart) { + super(chart); + this.pieStyler = chart.getStyler(); + } + + @Override + public void paint(Graphics2D g) { + Rectangle2D bounds = getBounds(); + if (bounds != null) { + Shape rect = new Rectangle2D.Double(bounds.getX(), bounds.getY(), bounds.getWidth(), bounds.getHeight()); + g.setColor(pieStyler.getPlotBackgroundColor()); + g.fill(rect); + if (pieStyler.isPlotBorderVisible()) { + g.setColor(pieStyler.getPlotBorderColor()); + g.draw(rect); + } + } + } + } +} diff --git a/chart/src/main/java/org/xbib/graphics/chart/pie/PieChartBuilder.java b/chart/src/main/java/org/xbib/graphics/chart/pie/PieChartBuilder.java new file mode 100644 index 0000000..ac793fe --- /dev/null +++ b/chart/src/main/java/org/xbib/graphics/chart/pie/PieChartBuilder.java @@ -0,0 +1,15 @@ +package org.xbib.graphics.chart.pie; + +import org.xbib.graphics.chart.ChartBuilder; + +public class PieChartBuilder extends ChartBuilder { + + public PieChartBuilder() { + } + + @Override + public PieChart build() { + + return new PieChart(this); + } +} diff --git a/chart/src/main/java/org/xbib/graphics/chart/pie/PieLegend.java b/chart/src/main/java/org/xbib/graphics/chart/pie/PieLegend.java new file mode 100644 index 0000000..2d9b577 --- /dev/null +++ b/chart/src/main/java/org/xbib/graphics/chart/pie/PieLegend.java @@ -0,0 +1,59 @@ +package org.xbib.graphics.chart.pie; + +import org.xbib.graphics.chart.Chart; +import org.xbib.graphics.chart.legend.AbstractLegend; +import org.xbib.graphics.chart.legend.LegendLayout; +import org.xbib.graphics.chart.legend.LegendRenderType; + +import java.awt.Graphics2D; +import java.awt.RenderingHints; +import java.awt.Shape; +import java.awt.geom.Rectangle2D; +import java.util.Map; + +public class PieLegend extends AbstractLegend { + + public PieLegend(Chart chart) { + super(chart); + } + + @Override + public void doPaint(Graphics2D g) { + double startx = xOffset + chart.getStyler().getLegendPadding(); + double starty = yOffset + chart.getStyler().getLegendPadding(); + Object oldHint = g.getRenderingHint(RenderingHints.KEY_ANTIALIASING); + g.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON); + Map map = chart.getSeriesMap(); + for (S series : map.values()) { + if (series.isNotShownInLegend()) { + continue; + } + if (!series.isEnabled()) { + continue; + } + Map seriesTextBounds = getSeriesTextBounds(series); + float legendEntryHeight = getLegendEntryHeight(seriesTextBounds, BOX_SIZE); + Shape rectSmall = new Rectangle2D.Double(startx, starty, BOX_SIZE, BOX_SIZE); + g.setColor(series.getFillColor()); + g.fill(rectSmall); + final double x = startx + BOX_SIZE + chart.getStyler().getLegendPadding(); + paintSeriesText(g, seriesTextBounds, BOX_SIZE, x, starty); + if (chart.getStyler().getLegendLayout() == LegendLayout.Vertical) { + starty += legendEntryHeight + chart.getStyler().getLegendPadding(); + } else { + int markerWidth = BOX_SIZE; + if (series.getLegendRenderType() == LegendRenderType.Line) { + markerWidth = chart.getStyler().getLegendSeriesLineLength(); + } + float legendEntryWidth = getLegendEntryWidth(seriesTextBounds, markerWidth); + startx += legendEntryWidth + chart.getStyler().getLegendPadding(); + } + } + g.setRenderingHint(RenderingHints.KEY_ANTIALIASING, oldHint); + } + + @Override + public double getSeriesLegendRenderGraphicHeight(S series) { + return BOX_SIZE; + } +} diff --git a/chart/src/main/java/org/xbib/graphics/chart/pie/PieSeries.java b/chart/src/main/java/org/xbib/graphics/chart/pie/PieSeries.java new file mode 100644 index 0000000..ddebf22 --- /dev/null +++ b/chart/src/main/java/org/xbib/graphics/chart/pie/PieSeries.java @@ -0,0 +1,41 @@ +package org.xbib.graphics.chart.pie; + +import org.xbib.graphics.chart.series.Series; +import org.xbib.graphics.chart.legend.LegendRenderType; + +/** + * A Series containing Pie data to be plotted on a Chart. + */ +public class PieSeries extends Series { + + private PieSeriesRenderStyle pieSeriesRenderStyle = null; + private Number value; + + + public PieSeries(String name, Number value) { + super(name); + this.value = value; + } + + public void setPieSeriesRenderStyle(PieSeriesRenderStyle pieSeriesRenderStyle) { + this.pieSeriesRenderStyle = pieSeriesRenderStyle; + } + + public PieSeriesRenderStyle getPieSeriesRenderStyle() { + return pieSeriesRenderStyle; + } + + @Override + public LegendRenderType getLegendRenderType() { + return null; + } + + public Number getValue() { + return value; + } + + public void setValue(Number value) { + this.value = value; + } + +} diff --git a/chart/src/main/java/org/xbib/graphics/chart/pie/PieSeriesRenderStyle.java b/chart/src/main/java/org/xbib/graphics/chart/pie/PieSeriesRenderStyle.java new file mode 100644 index 0000000..7276905 --- /dev/null +++ b/chart/src/main/java/org/xbib/graphics/chart/pie/PieSeriesRenderStyle.java @@ -0,0 +1,5 @@ +package org.xbib.graphics.chart.pie; + +public enum PieSeriesRenderStyle { + Pie, Donut; +} diff --git a/chart/src/main/java/org/xbib/graphics/chart/pie/PieStyler.java b/chart/src/main/java/org/xbib/graphics/chart/pie/PieStyler.java new file mode 100644 index 0000000..9032a41 --- /dev/null +++ b/chart/src/main/java/org/xbib/graphics/chart/pie/PieStyler.java @@ -0,0 +1,198 @@ +package org.xbib.graphics.chart.pie; + +import org.xbib.graphics.chart.style.Styler; +import org.xbib.graphics.chart.theme.Theme; + +import java.awt.Font; + +public class PieStyler extends Styler { + + private PieSeriesRenderStyle pieSeriesRenderStyle; + private boolean isCircular; + private double startAngleInDegrees; + private Font annotationFont; + private double annotationDistance; + private AnnotationType annotationType; + private boolean drawAllAnnotations; + private double donutThickness; + private boolean isSumVisible; + private Font sumFont; + + public PieStyler() { + this.setAllStyles(); + super.setAllStyles(); + } + + @Override + protected void setAllStyles() { + this.pieSeriesRenderStyle = PieSeriesRenderStyle.Pie; + this.isCircular = theme.isCircular(); + this.annotationFont = theme.getPieFont(); + this.annotationDistance = theme.getAnnotationDistance(); + this.annotationType = theme.getAnnotationType(); + this.drawAllAnnotations = theme.isDrawAllAnnotations(); + this.donutThickness = theme.getDonutThickness(); + this.hasAnnotations = true; + this.isSumVisible = theme.isSumVisible(); + this.sumFont = theme.getSumFont(); + } + + public PieSeriesRenderStyle getDefaultSeriesRenderStyle() { + return pieSeriesRenderStyle; + } + + /** + * Sets the default series render style for the chart (line, scatter, area, etc.) You can override the series + * render + * style individually on each Series object. + * + * @param pieSeriesRenderStyle render style + */ + public void setDefaultSeriesRenderStyle(PieSeriesRenderStyle pieSeriesRenderStyle) { + this.pieSeriesRenderStyle = pieSeriesRenderStyle; + } + + public boolean isCircular() { + return isCircular; + } + + /** + * Sets whether or not the pie chart is forced to be circular. Otherwise it's shape is oval, matching the + * containing + * plot. + * + * @param isCircular circular + */ + public void setCircular(boolean isCircular) { + this.isCircular = isCircular; + } + + public double getStartAngleInDegrees() { + return startAngleInDegrees; + } + + /** + * Sets the start angle in degrees. Zero degrees is straight up. + * + * @param startAngleInDegrees start angle in degrees + */ + public void setStartAngleInDegrees(double startAngleInDegrees) { + this.startAngleInDegrees = startAngleInDegrees; + } + + /** + * Sets the font used on the Pie Chart's annotations + * + * @param pieFont pie font + */ + public void setAnnotationFont(Font pieFont) { + this.annotationFont = pieFont; + } + + public Font getAnnotationFont() { + return annotationFont; + } + + /** + * Sets the distance of the pie chart's annotation where 0 is the center, 1 is at the edge and greater than 1 is + * outside of the pie chart. + * + * @param annotationDistance annotation distance + */ + public void setAnnotationDistance(double annotationDistance) { + this.annotationDistance = annotationDistance; + } + + public double getAnnotationDistance() { + return annotationDistance; + } + + /** + * Sets the Pie chart's annotation type + * + * @param annotationType annotation type + */ + public void setAnnotationType(AnnotationType annotationType) { + this.annotationType = annotationType; + } + + public AnnotationType getAnnotationType() { + return annotationType; + } + + public void setDrawAllAnnotations(boolean drawAllAnnotations) { + this.drawAllAnnotations = drawAllAnnotations; + } + + public boolean isDrawAllAnnotations() { + return drawAllAnnotations; + } + + /** + * Sets the thickness of the donut ring for donut style pie chart series. + * + * @param donutThickness - Valid range is between 0 and 1. + */ + public void setDonutThickness(double donutThickness) { + this.donutThickness = donutThickness; + } + + public double getDonutThickness() { + return donutThickness; + } + + /** + * Sets whether or not the sum is visible in the centre of the pie chart. + * + * @param isSumVisible sum visible + */ + public void setSumVisible(boolean isSumVisible) { + this.isSumVisible = isSumVisible; + } + + public boolean isSumVisible() { + return isSumVisible; + } + + /** + * Sets the font size for the sum. + * + * @param sumFontSize font size + */ + public void setSumFontSize(float sumFontSize) { + this.sumFont = this.sumFont.deriveFont(sumFontSize); + } + + public Font getSumFont() { + return sumFont; + } + + /** + * Sets the font for the sum. + * + * @param sumFont font + */ + public void setSumFont(Font sumFont) { + this.sumFont = sumFont; + } + + /** + * Set the theme the styler should use + * + * @param theme theme + */ + public void setTheme(Theme theme) { + this.theme = theme; + super.setAllStyles(); + } + + public Theme getTheme() { + return theme; + } + + + public enum AnnotationType { + Value, Percentage, Label, LabelAndPercentage + } + +} diff --git a/chart/src/main/java/org/xbib/graphics/chart/plot/AxesChartPlot.java b/chart/src/main/java/org/xbib/graphics/chart/plot/AxesChartPlot.java new file mode 100644 index 0000000..62d6490 --- /dev/null +++ b/chart/src/main/java/org/xbib/graphics/chart/plot/AxesChartPlot.java @@ -0,0 +1,43 @@ +package org.xbib.graphics.chart.plot; + +import org.xbib.graphics.chart.Chart; +import org.xbib.graphics.chart.series.Series; +import org.xbib.graphics.chart.style.AxesChartStyler; + +import java.awt.Graphics2D; +import java.awt.geom.Rectangle2D; + +public class AxesChartPlot extends Plot { + + protected ContentPlot contentPlot; + + protected SurfacePlot surfacePlot; + + protected Rectangle2D bounds; + + public AxesChartPlot(Chart chart) { + super(chart); + this.surfacePlot = new SurfacePlotAxesChart<>(chart); + } + + @Override + public void paint(Graphics2D g) { + Rectangle2D yAxisBounds = chart.getAxisPair().getLeftYAxisBounds(); + Rectangle2D xAxisBounds = chart.getXAxis().getBounds(); + double xOffset = xAxisBounds.getX(); + double yOffset = yAxisBounds.getY(); + double width = xAxisBounds.getWidth(); + double height = yAxisBounds.getHeight(); + this.bounds = new Rectangle2D.Double(xOffset, yOffset, width, height); + surfacePlot.paint(g); + if (chart.getSeriesMap().isEmpty()) { + return; + } + contentPlot.paint(g); + } + + @Override + public Rectangle2D getBounds() { + return bounds; + } +} diff --git a/chart/src/main/java/org/xbib/graphics/chart/plot/CircularPlot.java b/chart/src/main/java/org/xbib/graphics/chart/plot/CircularPlot.java new file mode 100644 index 0000000..683109b --- /dev/null +++ b/chart/src/main/java/org/xbib/graphics/chart/plot/CircularPlot.java @@ -0,0 +1,48 @@ +package org.xbib.graphics.chart.plot; + +import org.xbib.graphics.chart.Chart; +import org.xbib.graphics.chart.series.Series; +import org.xbib.graphics.chart.style.Styler; +import org.xbib.graphics.chart.legend.LegendPosition; + +import java.awt.Graphics2D; +import java.awt.geom.Rectangle2D; + +public abstract class CircularPlot extends Plot { + + protected Rectangle2D bounds; + + public CircularPlot(Chart chart) { + super(chart); + initContentAndSurface(chart); + } + + protected abstract void initContentAndSurface(Chart chart); + + @Override + public void paint(Graphics2D g) { + double xOffset = chart.getStyler().getChartPadding(); + double yOffset = chart.getChartTitle().getBounds().getHeight() + chart.getStyler().getChartPadding(); + double width = chart.getWidth() + - (chart.getStyler().getLegendPosition() == LegendPosition.OutsideE + ? chart.getLegend().getBounds().getWidth() + : 0) + - 2 * chart.getStyler().getChartPadding() + - (chart.getStyler().getLegendPosition() == LegendPosition.OutsideE + && chart.getStyler().isLegendVisible() + ? chart.getStyler().getChartPadding() + : 0); + double height = chart.getHeight() + - chart.getChartTitle().getBounds().getHeight() + - (chart.getStyler().getLegendPosition() == LegendPosition.OutsideS + ? chart.getLegend().getBounds().getHeight() + : 0) + - 2 * chart.getStyler().getChartPadding(); + this.bounds = new Rectangle2D.Double(xOffset, yOffset, width, height); + } + + @Override + public Rectangle2D getBounds() { + return bounds; + } +} diff --git a/chart/src/main/java/org/xbib/graphics/chart/plot/ContentPlot.java b/chart/src/main/java/org/xbib/graphics/chart/plot/ContentPlot.java new file mode 100644 index 0000000..ce3cee1 --- /dev/null +++ b/chart/src/main/java/org/xbib/graphics/chart/plot/ContentPlot.java @@ -0,0 +1,46 @@ +package org.xbib.graphics.chart.plot; + +import org.xbib.graphics.chart.Chart; +import org.xbib.graphics.chart.ChartComponent; +import org.xbib.graphics.chart.series.Series; +import org.xbib.graphics.chart.style.Styler; + +import java.awt.Graphics2D; +import java.awt.Shape; +import java.awt.geom.Path2D; +import java.awt.geom.Rectangle2D; + +public abstract class ContentPlot extends Plot implements ChartComponent { + + public ContentPlot(Chart chart) { + super(chart); + } + + @Override + public void paint(Graphics2D g) { + Rectangle2D bounds = getBounds(); + if (bounds != null) { + if (bounds.getWidth() < 30) { + return; + } + Shape saveClip = g.getClip(); + g.setClip(bounds.createIntersection(bounds)); + doPaint(g); + g.setClip(saveClip); + } + } + + /** + * Closes a path for area charts if one is available. + */ + protected void closePath(Graphics2D g, Path2D.Double path, double previousX, double yTopMargin) { + if (path != null) { + double yBottomOfArea = getBounds().getY() + getBounds().getHeight() - yTopMargin; + path.lineTo(previousX, yBottomOfArea); + path.closePath(); + g.fill(path); + } + } + + protected abstract void doPaint(Graphics2D g); +} diff --git a/chart/src/main/java/org/xbib/graphics/chart/plot/Plot.java b/chart/src/main/java/org/xbib/graphics/chart/plot/Plot.java new file mode 100644 index 0000000..a1853f5 --- /dev/null +++ b/chart/src/main/java/org/xbib/graphics/chart/plot/Plot.java @@ -0,0 +1,22 @@ +package org.xbib.graphics.chart.plot; + +import org.xbib.graphics.chart.Chart; +import org.xbib.graphics.chart.ChartComponent; +import org.xbib.graphics.chart.series.Series; +import org.xbib.graphics.chart.style.Styler; + +import java.awt.geom.Rectangle2D; + +public abstract class Plot implements ChartComponent { + + protected final Chart chart; + + public Plot(Chart chart) { + this.chart = chart; + } + + @Override + public Rectangle2D getBounds() { + return chart.getPlot().getBounds(); + } +} diff --git a/chart/src/main/java/org/xbib/graphics/chart/plot/SurfacePlot.java b/chart/src/main/java/org/xbib/graphics/chart/plot/SurfacePlot.java new file mode 100644 index 0000000..6628e51 --- /dev/null +++ b/chart/src/main/java/org/xbib/graphics/chart/plot/SurfacePlot.java @@ -0,0 +1,13 @@ +package org.xbib.graphics.chart.plot; + +import org.xbib.graphics.chart.Chart; +import org.xbib.graphics.chart.ChartComponent; +import org.xbib.graphics.chart.series.Series; +import org.xbib.graphics.chart.style.Styler; + +public abstract class SurfacePlot extends Plot implements ChartComponent { + + protected SurfacePlot(Chart chart) { + super(chart); + } +} diff --git a/chart/src/main/java/org/xbib/graphics/chart/plot/SurfacePlotAxesChart.java b/chart/src/main/java/org/xbib/graphics/chart/plot/SurfacePlotAxesChart.java new file mode 100644 index 0000000..34e36e6 --- /dev/null +++ b/chart/src/main/java/org/xbib/graphics/chart/plot/SurfacePlotAxesChart.java @@ -0,0 +1,104 @@ +package org.xbib.graphics.chart.plot; + +import org.xbib.graphics.chart.Chart; +import org.xbib.graphics.chart.series.Series; +import org.xbib.graphics.chart.style.AxesChartStyler; + +import java.awt.BasicStroke; +import java.awt.Graphics2D; +import java.awt.Shape; +import java.awt.geom.Line2D; +import java.awt.geom.Rectangle2D; +import java.util.List; + +/** + * Draws the plot background, the plot border and the horizontal and vertical grid lines. + */ +public class SurfacePlotAxesChart extends SurfacePlot { + + private final ST axesChartStyler; + + protected SurfacePlotAxesChart(Chart chart) { + super(chart); + this.axesChartStyler = chart.getStyler(); + } + + @Override + public void paint(Graphics2D g) { + Rectangle2D bounds = getBounds(); + Shape rect = new Rectangle2D.Double(bounds.getX(), bounds.getY(), bounds.getWidth(), bounds.getHeight()); + g.setColor(axesChartStyler.getPlotBackgroundColor()); + g.fill(rect); + if (axesChartStyler.isPlotGridHorizontalLinesVisible()) { + List yAxisTickLocations = chart.getYAxis().getAxisTickCalculator().getTickLocations(); + for (Double yAxisTickLocation : yAxisTickLocations) { + double yOffset = bounds.getY() + bounds.getHeight() - yAxisTickLocation; + if (yOffset > bounds.getY() && yOffset < bounds.getY() + bounds.getHeight()) { + g.setColor(axesChartStyler.getPlotGridLinesColor()); + g.setStroke(axesChartStyler.getPlotGridLinesStroke()); + Shape line = axesChartStyler.getPlotGridLinesStroke().createStrokedShape(new Line2D.Double( + bounds.getX(), yOffset, bounds.getX() + bounds.getWidth(), yOffset)); + g.draw(line); + } + } + } + if (axesChartStyler.isPlotTicksMarksVisible()) { + List yAxisTickLocations = + chart.getAxisPair().getLeftMainYAxis().getAxisTickCalculator().getTickLocations(); + for (Double yAxisTickLocation : yAxisTickLocations) { + double yOffset = bounds.getY() + bounds.getHeight() - yAxisTickLocation; + if (yOffset > bounds.getY() && yOffset < bounds.getY() + bounds.getHeight()) { + g.setColor(axesChartStyler.getAxisTickMarksColor()); + g.setStroke(axesChartStyler.getAxisTickMarksStroke()); + Shape line = new Line2D.Double(bounds.getX(), yOffset, + bounds.getX() + axesChartStyler.getAxisTickMarkLength(), + yOffset); + g.draw(line); + } + } + yAxisTickLocations = chart.getAxisPair().getRightMainYAxis().getAxisTickCalculator().getTickLocations(); + for (Double yAxisTickLocation : yAxisTickLocations) { + double yOffset = bounds.getY() + bounds.getHeight() - yAxisTickLocation; + if (yOffset > bounds.getY() && yOffset < bounds.getY() + bounds.getHeight()) { + g.setColor(axesChartStyler.getAxisTickMarksColor()); + g.setStroke(axesChartStyler.getAxisTickMarksStroke()); + Shape line = new Line2D.Double(bounds.getX() + bounds.getWidth(), yOffset, + bounds.getX() + bounds.getWidth() - axesChartStyler.getAxisTickMarkLength(), + yOffset); + g.draw(line); + } + } + } + if (axesChartStyler.isPlotGridVerticalLinesVisible() || axesChartStyler.isPlotTicksMarksVisible()) { + List xAxisTickLocations = chart.getXAxis().getAxisTickCalculator().getTickLocations(); + for (Double xAxisTickLocation : xAxisTickLocations) { + double tickLocation = xAxisTickLocation; + double xOffset = bounds.getX() + tickLocation; + if (xOffset > bounds.getX() && xOffset < bounds.getX() + bounds.getWidth()) { + if (axesChartStyler.isPlotGridVerticalLinesVisible()) { + g.setColor(axesChartStyler.getPlotGridLinesColor()); + g.setStroke(axesChartStyler.getPlotGridLinesStroke()); + Shape line = axesChartStyler.getPlotGridLinesStroke().createStrokedShape(new Line2D.Double( + xOffset, bounds.getY(), xOffset, bounds.getY() + bounds.getHeight())); + g.draw(line); + } + if (axesChartStyler.isPlotTicksMarksVisible()) { + g.setColor(axesChartStyler.getAxisTickMarksColor()); + g.setStroke(axesChartStyler.getAxisTickMarksStroke()); + Shape line = new Line2D.Double(xOffset, bounds.getY(), xOffset, + bounds.getY() + axesChartStyler.getAxisTickMarkLength()); + g.draw(line); + line = new Line2D.Double(xOffset, bounds.getY() + bounds.getHeight(), xOffset, + bounds.getY() + bounds.getHeight() - axesChartStyler.getAxisTickMarkLength()); + g.draw(line); + } + } + } + } + if (axesChartStyler.isPlotBorderVisible()) { + g.setColor(axesChartStyler.getPlotBorderColor()); + g.setStroke(new BasicStroke(1, BasicStroke.CAP_BUTT, BasicStroke.JOIN_BEVEL, 10.0f, new float[] {3.0f, 0.0f}, 0.0f)); + g.draw(rect); + } + } +} diff --git a/chart/src/main/java/org/xbib/graphics/chart/series/AxesChartSeries.java b/chart/src/main/java/org/xbib/graphics/chart/series/AxesChartSeries.java new file mode 100644 index 0000000..5c67460 --- /dev/null +++ b/chart/src/main/java/org/xbib/graphics/chart/series/AxesChartSeries.java @@ -0,0 +1,104 @@ +package org.xbib.graphics.chart.series; + +import org.xbib.graphics.chart.axis.DataType; + +import java.awt.BasicStroke; +import java.awt.Color; + +/** + * A Series containing X and Y data to be plotted on a Chart with X and Y Axes. + */ +public abstract class AxesChartSeries extends Series { + + private final DataType xAxisType; + private final DataType yAxisType; + private double xMin; + private double xMax; + private double yMin; + private double yMax; + private BasicStroke stroke; + private Color lineColor; + private float lineWidth = -1.0f; + + public AxesChartSeries(String name, DataType xAxisType) { + super(name); + this.xAxisType = xAxisType; + this.yAxisType = DataType.Number; + } + + protected abstract void calculateMinMax(); + + public void setLineColor(Color color) { + this.lineColor = color; + } + + public Color getLineColor() { + return lineColor; + } + + public DataType getxAxisDataType() { + return xAxisType; + } + + public DataType getyAxisDataType() { + return yAxisType; + } + + public void setXMin(double xMin) { + this.xMin = xMin; + } + + public double getXMin() { + return xMin; + } + + public void setXMax(double xMax) { + this.xMax = xMax; + } + + public double getXMax() { + return xMax; + } + + public void setYMin(double yMin) { + this.yMin = yMin; + } + + public double getYMin() { + return yMin; + } + + public void setYMax(double yMax) { + this.yMax = yMax; + } + + public double getYMax() { + return yMax; + } + + public void setLineStyle(BasicStroke basicStroke) { + stroke = basicStroke; + if (this.lineWidth > 0.0f) { + stroke = new BasicStroke(lineWidth, + this.stroke.getEndCap(), + this.stroke.getLineJoin(), + this.stroke.getMiterLimit(), + this.stroke.getDashArray(), + this.stroke.getDashPhase()); + } + } + + public BasicStroke getLineStyle() { + return stroke; + } + + public AxesChartSeries setLineWidth(float lineWidth) { + this.lineWidth = lineWidth; + return this; + } + + public float getLineWidth() { + return lineWidth; + } + +} diff --git a/chart/src/main/java/org/xbib/graphics/chart/series/AxesChartSeriesCategory.java b/chart/src/main/java/org/xbib/graphics/chart/series/AxesChartSeriesCategory.java new file mode 100644 index 0000000..5b055d4 --- /dev/null +++ b/chart/src/main/java/org/xbib/graphics/chart/series/AxesChartSeriesCategory.java @@ -0,0 +1,102 @@ +package org.xbib.graphics.chart.series; + +import org.xbib.graphics.chart.axis.DataType; + +import java.time.Instant; +import java.util.Collection; +import java.util.Iterator; +import java.util.List; + +/** + * A Series containing X and Y data to be plotted on a Chart with X and Y Axes. xData can be Number + * or Date or String, hence a List. + */ +public abstract class AxesChartSeriesCategory extends MarkerSeries { + + private final List xData; + private final List yData; + private final List extraValues; + + public AxesChartSeriesCategory(String name, + List xData, + List yData, + List extraValues, + DataType xDataType) { + super(name, xDataType); + this.xData = xData; + this.yData = yData; + this.extraValues = extraValues; + calculateMinMax(); + } + + @Override + protected void calculateMinMax() { + double[] xMinMax = findMinMax(xData, getxAxisDataType()); + setXMin(xMinMax[0]); + setXMax(xMinMax[1]); + double[] yMinMax; + if (extraValues == null) { + yMinMax = findMinMax(yData, getyAxisDataType()); + } else { + yMinMax = findMinMaxWithErrorBars(yData, extraValues); + } + setYMin(yMinMax[0]); + setYMax(yMinMax[1]); + } + + private double[] findMinMaxWithErrorBars(Collection data, Collection errorBars) { + double min = Double.MAX_VALUE; + double max = -Double.MAX_VALUE; + Iterator itr = data.iterator(); + Iterator ebItr = errorBars.iterator(); + while (itr.hasNext()) { + double bigDecimal = itr.next().doubleValue(); + double eb = ebItr.next().doubleValue(); + if (bigDecimal - eb < min) { + min = bigDecimal - eb; + } + if (bigDecimal + eb > max) { + max = bigDecimal + eb; + } + } + return new double[] {min, max}; + } + + private double[] findMinMax(Collection data, DataType dataType) { + double min = Double.MAX_VALUE; + double max = -Double.MAX_VALUE; + for (Object dataPoint : data) { + if (dataPoint == null) { + continue; + } + double value = 0.0; + if (dataType == DataType.Number) { + value = ((Number) dataPoint).doubleValue(); + } else if (dataType == DataType.Instant) { + Instant date = (Instant) dataPoint; + value = date.toEpochMilli(); + } else if (dataType == DataType.String) { + return new double[] {Double.NaN, Double.NaN}; + } + if (value < min) { + min = value; + } + if (value > max) { + max = value; + } + } + return new double[] {min, max}; + } + + public Collection getXData() { + return xData; + } + + public Collection getYData() { + return yData; + } + + public Collection getExtraValues() { + return extraValues; + } +} diff --git a/chart/src/main/java/org/xbib/graphics/chart/series/AxesChartSeriesNumericalNoErrorBars.java b/chart/src/main/java/org/xbib/graphics/chart/series/AxesChartSeriesNumericalNoErrorBars.java new file mode 100644 index 0000000..af1365c --- /dev/null +++ b/chart/src/main/java/org/xbib/graphics/chart/series/AxesChartSeriesNumericalNoErrorBars.java @@ -0,0 +1,147 @@ +package org.xbib.graphics.chart.series; + +import org.xbib.graphics.chart.axis.DataType; + +import java.math.BigDecimal; +import java.time.Instant; +import java.util.Arrays; +import java.util.List; + +/** + * A Series containing X and Y data to be plotted on a Chart with X and Y Axes. + */ +public abstract class AxesChartSeriesNumericalNoErrorBars extends MarkerSeries { + + protected List xData; // can be Number or Instant + protected List yData; + protected List extraValues; + + public AxesChartSeriesNumericalNoErrorBars(String name, + List xData, + List yData, + List extraValues, + DataType xDataType) { + super(name, xDataType); + this.xData = xData; + this.yData = yData; + this.extraValues = extraValues; + calculateMinMax(); + } + + @Override + protected void calculateMinMax() { + // xData + List xMinMax = findMinMax(xData); + setXMin(xMinMax.get(0)); + setXMax(xMinMax.get(1)); + // yData + List yMinMax; + if (extraValues == null) { + yMinMax = findMinMax(yData); + } else { + yMinMax = findMinMaxWithErrorBars(yData, extraValues); + } + setYMin(yMinMax.get(0)); + setYMax(yMinMax.get(1)); + } + + List findMinMax(List data) { + Double min = null; + Double max = null; + for (Object dataPoint : data) { + if (dataPoint instanceof Double) { + Double d = (Double) dataPoint; + if (min == null) { + min = d; + } + if (max == null) { + max = d; + } + if (!Double.isNaN(d)) { + if (d < min) { + min = d; + } + if (d > max) { + max = d; + } + } + } else if (dataPoint instanceof BigDecimal) { + BigDecimal bd = (BigDecimal) dataPoint; + if (min == null) { + min = bd.doubleValue(); + } + if (max == null) { + max = bd.doubleValue(); + } + if (bd.doubleValue() < min) { + min = bd.doubleValue(); + } + if (bd.doubleValue() > max) { + max = bd.doubleValue(); + } + } else if (dataPoint instanceof Integer) { + int i = (Integer) dataPoint; + if (min == null) { + min = (double) i; + } + if (max == null) { + max = (double) i; + } + if (i < min) { + min = (double) i; + } + if (i > max) { + max = (double) i; + } + } else if (dataPoint instanceof Instant) { + Instant instant = (Instant) dataPoint; + if (min == null) { + min = (double) instant.toEpochMilli(); + } + if (max == null) { + max = (double) instant.toEpochMilli(); + } + if (instant.toEpochMilli() < min) { + min = (double) instant.toEpochMilli(); + } + if (instant.toEpochMilli() > max) { + max = (double) instant.toEpochMilli(); + } + } + } + return Arrays.asList(min, max); + } + + private List findMinMaxWithErrorBars(List data, + List errorBars) { + double min = Double.MAX_VALUE; + double max = -Double.MAX_VALUE; + for (int i = 0; i < data.size(); i++) { + Number n = data.get(i); + Double d = n instanceof Double ? (Double) n : null; + Number nn = errorBars.get(i); + Double eb = nn instanceof Double ? (Double) nn : null; + if (d != null && eb != null) { + if (d - eb < min) { + min = d - eb; + } + if (d + eb > max) { + max = d + eb; + } + } + } + return Arrays.asList(min, max); + } + + public List getXData() { + return xData; + } + + public List getYData() { + return yData; + } + + public List getExtraValues() { + return extraValues; + } +} diff --git a/chart/src/main/java/org/xbib/graphics/chart/series/MarkerSeries.java b/chart/src/main/java/org/xbib/graphics/chart/series/MarkerSeries.java new file mode 100644 index 0000000..cd77894 --- /dev/null +++ b/chart/src/main/java/org/xbib/graphics/chart/series/MarkerSeries.java @@ -0,0 +1,37 @@ +package org.xbib.graphics.chart.series; + +import org.xbib.graphics.chart.axis.DataType; +import org.xbib.graphics.chart.theme.Theme; + +import java.awt.Color; + +/** + * A Series containing X and Y data to be plotted on a Chart with X and Y Axes, + * contains series markers and error bars. + */ +public abstract class MarkerSeries extends AxesChartSeries { + + private Theme.Series.Marker marker; + + private Color markerColor; + + protected MarkerSeries(String name, DataType xDataType) { + super(name, xDataType); + } + + public void setMarker(Theme.Series.Marker marker) { + this.marker = marker; + } + + public Theme.Series.Marker getMarker() { + return marker; + } + + public void setMarkerColor(Color color) { + this.markerColor = color; + } + + public Color getMarkerColor() { + return markerColor; + } +} diff --git a/chart/src/main/java/org/xbib/graphics/chart/series/NoMarkersSeries.java b/chart/src/main/java/org/xbib/graphics/chart/series/NoMarkersSeries.java new file mode 100644 index 0000000..a8fc08f --- /dev/null +++ b/chart/src/main/java/org/xbib/graphics/chart/series/NoMarkersSeries.java @@ -0,0 +1,31 @@ +package org.xbib.graphics.chart.series; + +import org.xbib.graphics.chart.axis.DataType; + +import java.util.List; + +/** + * A Series containing X and Y data to be plotted on a Chart with X and Y Axes, values associated + * with each X-Y point, could be used for bubble sizes for example, but no error bars, as the min + * and max are calculated differently. No markers. + */ +public abstract class NoMarkersSeries extends AxesChartSeriesNumericalNoErrorBars { + + protected NoMarkersSeries(String name, List xData, + List yData, + List extraValues, DataType axisType) { + super(name, xData, yData, extraValues, axisType); + this.extraValues = extraValues; + calculateMinMax(); + } + + @Override + protected void calculateMinMax() { + List xMinMax = findMinMax(xData); + setXMin(xMinMax.get(0)); + setXMax(xMinMax.get(1)); + List yMinMax = findMinMax(yData); + setYMin(yMinMax.get(0)); + setYMax(yMinMax.get(1)); + } +} diff --git a/chart/src/main/java/org/xbib/graphics/chart/series/Series.java b/chart/src/main/java/org/xbib/graphics/chart/series/Series.java new file mode 100644 index 0000000..c5199be --- /dev/null +++ b/chart/src/main/java/org/xbib/graphics/chart/series/Series.java @@ -0,0 +1,73 @@ +package org.xbib.graphics.chart.series; + +import org.xbib.graphics.chart.legend.LegendRenderType; + +import java.awt.Color; + +/** + * A Series containing data to be plotted on a Chart + */ +public abstract class Series { + + private final String name; + private String label; + private Color fillColor; + private boolean showInLegend = true; + private boolean isEnabled = true; + private int yAxisGroup = 0; + + public Series(String name) { + if (name == null || name.length() < 1) { + throw new IllegalArgumentException("Series name cannot be null or zero-length"); + } + this.name = name; + this.label = name; + } + + public abstract LegendRenderType getLegendRenderType(); + + public Color getFillColor() { + return fillColor; + } + + public void setFillColor(Color fillColor) { + this.fillColor = fillColor; + } + + public String getName() { + return name; + } + + public String getLabel() { + return label; + } + + public Series setLabel(String label) { + this.label = label; + return this; + } + + public boolean isNotShownInLegend() { + return !showInLegend; + } + + public void setShowInLegend(boolean showInLegend) { + this.showInLegend = showInLegend; + } + + public void setEnabled(boolean isEnabled) { + this.isEnabled = isEnabled; + } + + public boolean isEnabled() { + return isEnabled; + } + + public void setYAxisGroup(int yAxisGroup) { + this.yAxisGroup = yAxisGroup; + } + + public int getYAxisGroup() { + return yAxisGroup; + } +} diff --git a/chart/src/main/java/org/xbib/graphics/chart/style/AxesChartStyler.java b/chart/src/main/java/org/xbib/graphics/chart/style/AxesChartStyler.java new file mode 100644 index 0000000..69b9633 --- /dev/null +++ b/chart/src/main/java/org/xbib/graphics/chart/style/AxesChartStyler.java @@ -0,0 +1,677 @@ +package org.xbib.graphics.chart.style; + +import org.xbib.graphics.chart.axis.TextAlignment; + +import java.awt.Color; +import java.awt.Font; +import java.awt.Stroke; +import java.time.ZoneId; +import java.util.HashMap; +import java.util.Locale; +import java.util.Map; + +public abstract class AxesChartStyler extends Styler { + + private boolean xAxisTitleVisible; + private boolean yAxisTitleVisible; + private Font axisTitleFont; + private boolean xAxisTicksVisible; + private boolean yAxisTicksVisible; + private Font axisTickLabelsFont; + private int axisTickMarkLength; + private int axisTickPadding; + private Color axisTickMarksColor; + private Stroke axisTickMarksStroke; + private Color axisTickLabelsColor; + private boolean isAxisTicksLineVisible; + private boolean isAxisTicksMarksVisible; + private int plotMargin; + private int axisTitlePadding; + private int xAxisTickMarkSpacingHint; + private int yAxisTickMarkSpacingHint; + private boolean isXAxisLogarithmic; + private boolean isYAxisLogarithmic; + private Double xAxisMin; + private Double xAxisMax; + private final Map yAxisMinMap = new HashMap<>(); + private final Map yAxisMaxMap = new HashMap<>(); + private TextAlignment xAxisLabelAlignment = TextAlignment.Centre; + private TextAlignment xAxisLabelAlignmentVertical = TextAlignment.Centre; + private TextAlignment yAxisLabelAlignment = TextAlignment.Left; + private int xAxisLabelRotation = 0; + + // Chart Plot Area + private boolean isPlotGridHorizontalLinesVisible; + private boolean isPlotGridVerticalLinesVisible; + private boolean isPlotTicksMarksVisible; + private Color plotGridLinesColor; + private Stroke plotGridLinesStroke; + + private int markerSize; + + private Color errorBarsColor; + private boolean isErrorBarsColorSeriesColor; + + private Locale locale; + private ZoneId zoneId; + private String datePattern; + private String decimalPattern; + private String xAxisDecimalPattern; + private String yAxisDecimalPattern; + private boolean xAxisLogarithmicDecadeOnly; + private boolean yAxisLogarithmicDecadeOnly; + + @Override + protected void setAllStyles() { + super.setAllStyles(); + // axes + xAxisTitleVisible = theme.isXAxisTitleVisible(); + yAxisTitleVisible = theme.isYAxisTitleVisible(); + axisTitleFont = theme.getAxisTitleFont(); + xAxisTicksVisible = theme.isXAxisTicksVisible(); + yAxisTicksVisible = theme.isYAxisTicksVisible(); + axisTickLabelsFont = theme.getAxisTickLabelsFont(); + axisTickMarkLength = theme.getAxisTickMarkLength(); + axisTickPadding = theme.getAxisTickPadding(); + axisTickMarksColor = theme.getAxisTickMarksColor(); + axisTickMarksStroke = theme.getAxisTickMarksStroke(); + axisTickLabelsColor = theme.getAxisTickLabelsColor(); + isAxisTicksLineVisible = theme.isAxisTicksLineVisible(); + isAxisTicksMarksVisible = theme.isAxisTicksMarksVisible(); + plotMargin = theme.getPlotMargin(); + axisTitlePadding = theme.getAxisTitlePadding(); + xAxisTickMarkSpacingHint = theme.getXAxisTickMarkSpacingHint(); + yAxisTickMarkSpacingHint = theme.getYAxisTickMarkSpacingHint(); + isXAxisLogarithmic = false; + isYAxisLogarithmic = false; + xAxisMin = null; + xAxisMax = null; + this.yAxisMinMap.clear(); + this.yAxisMaxMap.clear(); + isPlotGridVerticalLinesVisible = theme.isPlotGridVerticalLinesVisible(); + isPlotGridHorizontalLinesVisible = theme.isPlotGridHorizontalLinesVisible(); + isPlotTicksMarksVisible = theme.isPlotTicksMarksVisible(); + plotGridLinesColor = theme.getPlotGridLinesColor(); + plotGridLinesStroke = theme.getPlotGridLinesStroke(); + markerSize = theme.getMarkerSize(); + errorBarsColor = theme.getErrorBarsColor(); + isErrorBarsColorSeriesColor = theme.isErrorBarsColorSeriesColor(); + locale = Locale.getDefault(); + zoneId = ZoneId.of("UTC"); + datePattern = "YYYY-MM-dd"; + decimalPattern = null; + xAxisDecimalPattern = null; + yAxisDecimalPattern = null; + } + + public boolean isXAxisTitleVisible() { + return xAxisTitleVisible; + } + + /** + * Set the x-axis title visibility + * + * @param xAxisTitleVisible + */ + public void setXAxisTitleVisible(boolean xAxisTitleVisible) { + this.xAxisTitleVisible = xAxisTitleVisible; + } + + public boolean isYAxisTitleVisible() { + return yAxisTitleVisible; + } + + /** + * Set the y-axis title visibility + * + * @param yAxisTitleVisible + */ + public void setYAxisTitleVisible(boolean yAxisTitleVisible) { + this.yAxisTitleVisible = yAxisTitleVisible; + } + + /** + * Set the x- and y-axis titles visibility + * + * @param isVisible + */ + public void setAxisTitlesVisible(boolean isVisible) { + this.xAxisTitleVisible = isVisible; + this.yAxisTitleVisible = isVisible; + } + + public Font getAxisTitleFont() { + return axisTitleFont; + } + + /** + * Set the x- and y-axis title font + * + * @param axisTitleFont + */ + public void setAxisTitleFont(Font axisTitleFont) { + this.axisTitleFont = axisTitleFont; + } + + public boolean isXAxisTicksVisible() { + return xAxisTicksVisible; + } + + /** + * Set the x-axis tick marks and labels visibility + * + * @param xAxisTicksVisible + */ + public void setXAxisTicksVisible(boolean xAxisTicksVisible) { + this.xAxisTicksVisible = xAxisTicksVisible; + } + + public boolean isYAxisTicksVisible() { + return yAxisTicksVisible; + } + + /** + * Set the y-axis tick marks and labels visibility + * + * @param yAxisTicksVisible + */ + public void setYAxisTicksVisible(boolean yAxisTicksVisible) { + this.yAxisTicksVisible = yAxisTicksVisible; + } + + /** + * Set the x- and y-axis tick marks and labels visibility + * + * @param isVisible + */ + public void setAxisTicksVisible(boolean isVisible) { + this.xAxisTicksVisible = isVisible; + this.yAxisTicksVisible = isVisible; + } + + public Font getAxisTickLabelsFont() { + return axisTickLabelsFont; + } + + /** + * Set the x- and y-axis tick label font + * + * @param axisTicksFont + */ + public void setAxisTickLabelsFont(Font axisTicksFont) { + this.axisTickLabelsFont = axisTicksFont; + } + + public int getAxisTickMarkLength() { + return axisTickMarkLength; + } + + /** + * Set the axis tick mark length + * + * @param axisTickMarkLength + */ + public void setAxisTickMarkLength(int axisTickMarkLength) { + this.axisTickMarkLength = axisTickMarkLength; + } + + public int getAxisTickPadding() { + return axisTickPadding; + } + + /** + * sets the padding between the tick labels and the tick marks + * + * @param axisTickPadding + */ + public void setAxisTickPadding(int axisTickPadding) { + this.axisTickPadding = axisTickPadding; + } + + public Color getAxisTickMarksColor() { + return axisTickMarksColor; + } + + /** + * sets the axis tick mark color + * + * @param axisTickColor + */ + public void setAxisTickMarksColor(Color axisTickColor) { + this.axisTickMarksColor = axisTickColor; + } + + public Stroke getAxisTickMarksStroke() { + return axisTickMarksStroke; + } + + /** + * sets the axis tick marks Stroke + * + * @param axisTickMarksStroke + */ + public void setAxisTickMarksStroke(Stroke axisTickMarksStroke) { + this.axisTickMarksStroke = axisTickMarksStroke; + } + + public Color getAxisTickLabelsColor() { + return axisTickLabelsColor; + } + + /** + * sets the axis tick label color + * + * @param axisTickLabelsColor + */ + public void setAxisTickLabelsColor(Color axisTickLabelsColor) { + this.axisTickLabelsColor = axisTickLabelsColor; + } + + public boolean isAxisTicksLineVisible() { + return isAxisTicksLineVisible; + } + + /** + * sets the visibility of the line parallel to the plot edges that go along with the tick marks + * + * @param isAxisTicksLineVisible + */ + public void setAxisTicksLineVisible(boolean isAxisTicksLineVisible) { + this.isAxisTicksLineVisible = isAxisTicksLineVisible; + } + + public boolean isAxisTicksMarksVisible() { + return isAxisTicksMarksVisible; + } + + /** + * sets the visibility of the tick marks + * + * @param isAxisTicksMarksVisible + */ + public void setAxisTicksMarksVisible(boolean isAxisTicksMarksVisible) { + this.isAxisTicksMarksVisible = isAxisTicksMarksVisible; + } + + public int getPlotMargin() { + return plotMargin; + } + + /** + * sets the margin around the plot area + * + * @param plotMargin + */ + public void setPlotMargin(int plotMargin) { + this.plotMargin = plotMargin; + } + + public int getAxisTitlePadding() { + return axisTitlePadding; + } + + /** + * sets the padding between the axis title and the tick labels + * + * @param axisTitlePadding + */ + public void setAxisTitlePadding(int axisTitlePadding) { + this.axisTitlePadding = axisTitlePadding; + } + + public int getXAxisTickMarkSpacingHint() { + return xAxisTickMarkSpacingHint; + } + + /** + * set the spacing between tick marks for the X-Axis + * + * @param xAxisTickMarkSpacingHint + */ + public void setXAxisTickMarkSpacingHint(int xAxisTickMarkSpacingHint) { + this.xAxisTickMarkSpacingHint = xAxisTickMarkSpacingHint; + } + + public int getYAxisTickMarkSpacingHint() { + return yAxisTickMarkSpacingHint; + } + + /** + * set the spacing between tick marks for the Y-Axis + * + * @param yAxisTickMarkSpacingHint + */ + public void setYAxisTickMarkSpacingHint(int yAxisTickMarkSpacingHint) { + this.yAxisTickMarkSpacingHint = yAxisTickMarkSpacingHint; + } + + public boolean isXAxisLogarithmic() { + + return isXAxisLogarithmic; + } + + /** + * sets the X-Axis to be rendered with a logarithmic scale or not + * + * @param isXAxisLogarithmic + */ + public void setXAxisLogarithmic(boolean isXAxisLogarithmic) { + this.isXAxisLogarithmic = isXAxisLogarithmic; + } + + public boolean isYAxisLogarithmic() { + return isYAxisLogarithmic; + } + + /** + * sets the Y-Axis to be rendered with a logarithmic scale or not + * + * @param isYAxisLogarithmic + */ + public void setYAxisLogarithmic(boolean isYAxisLogarithmic) { + this.isYAxisLogarithmic = isYAxisLogarithmic; + } + + public Double getXAxisMin() { + return xAxisMin; + } + + public void setXAxisMin(double xAxisMin) { + this.xAxisMin = xAxisMin; + } + + public Double getXAxisMax() { + return xAxisMax; + } + + public void setXAxisMax(double xAxisMax) { + this.xAxisMax = xAxisMax; + } + + public Double getYAxisMin() { + return yAxisMinMap.get(null); + } + + public AxesChartStyler setYAxisMin(Double yAxisMin) { + this.yAxisMinMap.put(null, yAxisMin); + return this; + } + + public Double getYAxisMin(Integer yAxisGroup) { + return yAxisMinMap.get(yAxisGroup); + } + + public AxesChartStyler setYAxisMax(Integer yAxisGroup, Double yAxisMax) { + this.yAxisMaxMap.put(yAxisGroup, yAxisMax); + return this; + } + + public Double getYAxisMax() { + return yAxisMaxMap.get(null); + } + + public AxesChartStyler setYAxisMax(Double yAxisMax) { + this.yAxisMaxMap.put(null, yAxisMax); + return this; + } + + public Double getYAxisMax(Integer yAxisGroup) { + return yAxisMaxMap.get(yAxisGroup); + } + + public TextAlignment getXAxisLabelAlignment() { + return xAxisLabelAlignment; + } + + public void setXAxisLabelAlignment(TextAlignment xAxisLabelAlignment) { + this.xAxisLabelAlignment = xAxisLabelAlignment; + } + + public TextAlignment getYAxisLabelAlignment() { + return yAxisLabelAlignment; + } + + public void setYAxisLabelAlignment(TextAlignment yAxisLabelAlignment) { + this.yAxisLabelAlignment = yAxisLabelAlignment; + } + + public int getXAxisLabelRotation() { + return xAxisLabelRotation; + } + + public void setXAxisLabelRotation(int xAxisLabelRotation) { + this.xAxisLabelRotation = xAxisLabelRotation; + } + + public boolean isPlotGridLinesVisible() { + return isPlotGridHorizontalLinesVisible && isPlotGridVerticalLinesVisible; + } + + /** + * sets the visibility of the gridlines on the plot area + * + * @param isPlotGridLinesVisible + */ + public void setPlotGridLinesVisible(boolean isPlotGridLinesVisible) { + this.isPlotGridHorizontalLinesVisible = isPlotGridLinesVisible; + this.isPlotGridVerticalLinesVisible = isPlotGridLinesVisible; + } + + public boolean isPlotGridHorizontalLinesVisible() { + return isPlotGridHorizontalLinesVisible; + } + + /** + * sets the visibility of the horizontal gridlines on the plot area + * + * @param isPlotGridHorizontalLinesVisible + */ + public void setPlotGridHorizontalLinesVisible(boolean isPlotGridHorizontalLinesVisible) { + this.isPlotGridHorizontalLinesVisible = isPlotGridHorizontalLinesVisible; + } + + public boolean isPlotGridVerticalLinesVisible() { + return isPlotGridVerticalLinesVisible; + } + + /** + * sets the visibility of the vertical gridlines on the plot area + * + * @param isPlotGridVerticalLinesVisible + */ + public void setPlotGridVerticalLinesVisible(boolean isPlotGridVerticalLinesVisible) { + this.isPlotGridVerticalLinesVisible = isPlotGridVerticalLinesVisible; + } + + public boolean isPlotTicksMarksVisible() { + return isPlotTicksMarksVisible; + } + + /** + * sets the visibility of the ticks marks inside the plot area + * + * @param isPlotTicksMarksVisible + */ + public void setPlotTicksMarksVisible(boolean isPlotTicksMarksVisible) { + this.isPlotTicksMarksVisible = isPlotTicksMarksVisible; + } + + public Color getPlotGridLinesColor() { + return plotGridLinesColor; + } + + /** + * set the plot area's grid lines color + * + * @param plotGridLinesColor + */ + public void setPlotGridLinesColor(Color plotGridLinesColor) { + this.plotGridLinesColor = plotGridLinesColor; + } + + public Stroke getPlotGridLinesStroke() { + return plotGridLinesStroke; + } + + /** + * set the plot area's grid lines Stroke + * + * @param plotGridLinesStroke + */ + public void setPlotGridLinesStroke(Stroke plotGridLinesStroke) { + this.plotGridLinesStroke = plotGridLinesStroke; + } + + + public int getMarkerSize() { + return markerSize; + } + + /** + * Sets the size of the markers in pixels + * + * @param markerSize + */ + public void setMarkerSize(int markerSize) { + this.markerSize = markerSize; + } + + + public Color getErrorBarsColor() { + return errorBarsColor; + } + + /** + * Sets the color of the error bars + * + * @param errorBarsColor + */ + public void setErrorBarsColor(Color errorBarsColor) { + this.errorBarsColor = errorBarsColor; + } + + public boolean isErrorBarsColorSeriesColor() { + return isErrorBarsColorSeriesColor; + } + + /** + * Set true if the the error bar color should match the series color. + */ + public void setErrorBarsColorSeriesColor(boolean isErrorBarsColorSeriesColor) { + this.isErrorBarsColorSeriesColor = isErrorBarsColorSeriesColor; + } + + public Locale getLocale() { + return locale; + } + + /** + * Set the locale to use for rendering the chart + * + * @param locale - the locale to use when formatting Strings and dates for the axis tick labels + */ + public void setLocale(Locale locale) { + this.locale = locale; + } + + public ZoneId getZoneId() { + return zoneId; + } + + /** + * Set the zone ID to use for formatting time instant axis tick labels + * + * @param zoneId the zone ID to use when formatting time instants + */ + public void setZoneId(ZoneId zoneId) { + this.zoneId = zoneId; + } + + public String getDatePattern() { + return datePattern; + } + + /** + * Set the String formatter for Data x-axis + * + * @param datePattern - the pattern describing the date and time format + */ + public void setDatePattern(String datePattern) { + this.datePattern = datePattern; + } + + public String getDecimalPattern() { + return decimalPattern; + } + + /** + * Set the decimal formatter for all tick labels + * + * @param decimalPattern - the pattern describing the decimal format + */ + public void setDecimalPattern(String decimalPattern) { + this.decimalPattern = decimalPattern; + } + + public String getXAxisDecimalPattern() { + return xAxisDecimalPattern; + } + + /** + * Set the decimal formatting pattern for the X-Axis + * + * @param xAxisDecimalPattern + */ + public void setXAxisDecimalPattern(String xAxisDecimalPattern) { + this.xAxisDecimalPattern = xAxisDecimalPattern; + } + + public String getYAxisDecimalPattern() { + return yAxisDecimalPattern; + } + + /** + * Set the decimal formatting pattern for the Y-Axis + * + * @param yAxisDecimalPattern + */ + public void setYAxisDecimalPattern(String yAxisDecimalPattern) { + this.yAxisDecimalPattern = yAxisDecimalPattern; + } + + public boolean isXAxisLogarithmicDecadeOnly() { + return xAxisLogarithmicDecadeOnly; + } + + /** + * Set the decade only support for logarithmic Y-Axis + * + * @param xAxisLogarithmicDecadeOnly + */ + public AxesChartStyler setXAxisLogarithmicDecadeOnly(boolean xAxisLogarithmicDecadeOnly) { + this.xAxisLogarithmicDecadeOnly = xAxisLogarithmicDecadeOnly; + return this; + } + + public boolean isYAxisLogarithmicDecadeOnly() { + return yAxisLogarithmicDecadeOnly; + } + + /** + * Set the decade only support for logarithmic Y-Axis + * + * @param yAxisLogarithmicDecadeOnly + */ + public AxesChartStyler setYAxisLogarithmicDecadeOnly(boolean yAxisLogarithmicDecadeOnly) { + this.yAxisLogarithmicDecadeOnly = yAxisLogarithmicDecadeOnly; + return this; + } + + public TextAlignment getXAxisLabelAlignmentVertical() { + return xAxisLabelAlignmentVertical; + } + + public void setXAxisLabelAlignmentVertical(TextAlignment xAxisLabelAlignmentVertical) { + this.xAxisLabelAlignmentVertical = xAxisLabelAlignmentVertical; + } +} diff --git a/chart/src/main/java/org/xbib/graphics/chart/style/SeriesColorMarkerLineStyle.java b/chart/src/main/java/org/xbib/graphics/chart/style/SeriesColorMarkerLineStyle.java new file mode 100644 index 0000000..130fa8a --- /dev/null +++ b/chart/src/main/java/org/xbib/graphics/chart/style/SeriesColorMarkerLineStyle.java @@ -0,0 +1,31 @@ +package org.xbib.graphics.chart.style; + +import org.xbib.graphics.chart.theme.Theme; + +import java.awt.BasicStroke; +import java.awt.Color; + +public final class SeriesColorMarkerLineStyle { + + private final Color color; + private final Theme.Series.Marker marker; + private final BasicStroke stroke; + + public SeriesColorMarkerLineStyle(Color color, Theme.Series.Marker marker, BasicStroke stroke) { + this.color = color; + this.marker = marker; + this.stroke = stroke; + } + + public Color getColor() { + return color; + } + + public Theme.Series.Marker getMarker() { + return marker; + } + + public BasicStroke getStroke() { + return stroke; + } +} diff --git a/chart/src/main/java/org/xbib/graphics/chart/style/SeriesColorMarkerLineStyleCycler.java b/chart/src/main/java/org/xbib/graphics/chart/style/SeriesColorMarkerLineStyleCycler.java new file mode 100644 index 0000000..ea31b41 --- /dev/null +++ b/chart/src/main/java/org/xbib/graphics/chart/style/SeriesColorMarkerLineStyleCycler.java @@ -0,0 +1,45 @@ +package org.xbib.graphics.chart.style; + +import org.xbib.graphics.chart.theme.Theme; + +import java.awt.BasicStroke; +import java.awt.Color; +import java.util.List; + +/** + * Cycles through the different colors, markers, and strokes in a predetermined way. + */ +public class SeriesColorMarkerLineStyleCycler { + + private final List seriesColorList; + private final List seriesMarkerList; + private final List seriesLineStyleList; + private int colorCounter = 0; + private int markerCounter = 0; + private int strokeCounter = 0; + + public SeriesColorMarkerLineStyleCycler(List seriesColorList, + List seriesMarkerList, + List seriesLineStyleList) { + this.seriesColorList = seriesColorList; + this.seriesMarkerList = seriesMarkerList; + this.seriesLineStyleList = seriesLineStyleList; + } + + public SeriesColorMarkerLineStyle getNextSeriesColorMarkerLineStyle() { + if (colorCounter >= seriesColorList.size()) { + colorCounter = 0; + strokeCounter++; + } + Color seriesColor = seriesColorList.get(colorCounter++); + if (strokeCounter >= seriesLineStyleList.size()) { + strokeCounter = 0; + } + BasicStroke seriesLineStyle = seriesLineStyleList.get(strokeCounter); + if (markerCounter >= seriesMarkerList.size()) { + markerCounter = 0; + } + Theme.Series.Marker marker = seriesMarkerList.get(markerCounter++); + return new SeriesColorMarkerLineStyle(seriesColor, marker, seriesLineStyle); + } +} diff --git a/chart/src/main/java/org/xbib/graphics/chart/style/Styler.java b/chart/src/main/java/org/xbib/graphics/chart/style/Styler.java new file mode 100644 index 0000000..af465b4 --- /dev/null +++ b/chart/src/main/java/org/xbib/graphics/chart/style/Styler.java @@ -0,0 +1,518 @@ +package org.xbib.graphics.chart.style; + +import org.xbib.graphics.chart.theme.Theme; +import org.xbib.graphics.chart.theme.DefaultTheme; +import org.xbib.graphics.chart.legend.LegendLayout; +import org.xbib.graphics.chart.legend.LegendPosition; +import org.xbib.graphics.chart.axis.YAxisPosition; + +import java.awt.BasicStroke; +import java.awt.Color; +import java.awt.Font; +import java.util.HashMap; +import java.util.List; + +/** + * The styler is used to manage all things related to styling of the vast number of Chart components + */ +public abstract class Styler { + + protected Theme theme = new DefaultTheme(); + + protected boolean hasAnnotations = false; // set by subclass + + // Chart Style + private Font baseFont; + private Color chartBackgroundColor; + private Color chartFontColor; + private int chartPadding; + private List seriesColors; + private List seriesLines; + private List seriesMarkers; + // Chart Title + private Font chartTitleFont; + private boolean isChartTitleVisible; + private boolean isChartTitleBoxVisible; + private Color chartTitleBoxBackgroundColor; + private Color chartTitleBoxBorderColor; + private int chartTitlePadding; + // Chart Legend + private boolean isLegendVisible; + private Color legendBackgroundColor; + private Color legendBorderColor; + private Font legendFont; + private int legendPadding; + private int legendSeriesLineLength; + private LegendPosition legendPosition; + private LegendLayout legendLayout = LegendLayout.Vertical; + // Chart Plot Area + private Color plotBackgroundColor; + private Color plotBorderColor; + private boolean isPlotBorderVisible; + private double plotContentSize = .92; + private Font annotationsFont; + private boolean antiAlias = true; + private String decimalPattern; + private final HashMap yAxisAlignmentMap = new HashMap<>(); + + protected void setAllStyles() { + // Chart Style + chartBackgroundColor = theme.getChartBackgroundColor(); + chartFontColor = theme.getChartFontColor(); + chartPadding = theme.getChartPadding(); + seriesColors = theme.getSeriesColors(); + seriesLines = theme.getSeriesLines(); + seriesMarkers = theme.getSeriesMarkers(); + + // Chart Title + chartTitleFont = theme.getChartTitleFont(); + isChartTitleVisible = theme.isChartTitleVisible(); + isChartTitleBoxVisible = theme.isChartTitleBoxVisible(); + chartTitleBoxBackgroundColor = theme.getChartTitleBoxBackgroundColor(); + chartTitleBoxBorderColor = theme.getChartTitleBoxBorderColor(); + chartTitlePadding = theme.getChartTitlePadding(); + + // legend + isLegendVisible = theme.isLegendVisible(); + legendBackgroundColor = theme.getLegendBackgroundColor(); + legendBorderColor = theme.getLegendBorderColor(); + legendFont = theme.getLegendFont(); + legendPadding = theme.getLegendPadding(); + legendSeriesLineLength = theme.getLegendSeriesLineLength(); + legendPosition = theme.getLegendPosition(); + + // Chart Plot Area + plotBackgroundColor = theme.getPlotBackgroundColor(); + plotBorderColor = theme.getPlotBorderColor(); + isPlotBorderVisible = theme.isPlotBorderVisible(); + plotContentSize = theme.getPlotContentSize(); + + annotationsFont = theme.getAnnotationFont(); + + // Formatting + decimalPattern = null; + } + + /** + * Set the base font + * + * @param baseFont + * @return styler + */ + public Styler setBaseFont(Font baseFont) { + this.baseFont = baseFont; + return this; + } + + public Font getBaseFont() { + return baseFont; + } + + public Color getChartBackgroundColor() { + return chartBackgroundColor; + } + + /** + * Set the chart background color - the part around the edge of the chart + * + * @param color + */ + public void setChartBackgroundColor(Color color) { + this.chartBackgroundColor = color; + } + + public Color getChartFontColor() { + return chartFontColor; + } + + // Chart Style + + /** + * Set the chart font color. includes: Chart title, axes label, legend + * + * @param color + */ + public void setChartFontColor(Color color) { + this.chartFontColor = color; + } + + public int getChartPadding() { + return chartPadding; + } + + /** + * Set the chart padding + * + * @param chartPadding + */ + public void setChartPadding(int chartPadding) { + this.chartPadding = chartPadding; + } + + public List getSeriesColors() { + return seriesColors; + } + + public void setSeriesColors(List seriesColors) { + this.seriesColors = seriesColors; + } + + public List getSeriesLines() { + return seriesLines; + } + + // Chart Title + + public void setSeriesLines(List seriesLines) { + this.seriesLines = seriesLines; + } + + public List getSeriesMarkers() { + return seriesMarkers; + } + + public void setSeriesMarkers(List seriesMarkers) { + this.seriesMarkers = seriesMarkers; + } + + public Font getChartTitleFont() { + return chartTitleFont; + } + + /** + * Set the chart title font + * + * @param chartTitleFont font + */ + public void setChartTitleFont(Font chartTitleFont) { + this.chartTitleFont = chartTitleFont; + } + + public boolean isChartTitleVisible() { + return isChartTitleVisible; + } + + /** + * Set the chart title visibility + * + * @param isChartTitleVisible + */ + public void setChartTitleVisible(boolean isChartTitleVisible) { + this.isChartTitleVisible = isChartTitleVisible; + } + + public boolean isChartTitleBoxVisible() { + return isChartTitleBoxVisible; + } + + /** + * Set the chart title box visibility + * + * @param isChartTitleBoxVisible + */ + public void setChartTitleBoxVisible(boolean isChartTitleBoxVisible) { + + this.isChartTitleBoxVisible = isChartTitleBoxVisible; + } + + public Color getChartTitleBoxBackgroundColor() { + + return chartTitleBoxBackgroundColor; + } + + /** + * set the chart title box background color + * + * @param chartTitleBoxBackgroundColor + */ + public void setChartTitleBoxBackgroundColor(Color chartTitleBoxBackgroundColor) { + + this.chartTitleBoxBackgroundColor = chartTitleBoxBackgroundColor; + } + + public Color getChartTitleBoxBorderColor() { + + return chartTitleBoxBorderColor; + } + + /** + * set the chart title box border color + * + * @param chartTitleBoxBorderColor + */ + public void setChartTitleBoxBorderColor(Color chartTitleBoxBorderColor) { + + this.chartTitleBoxBorderColor = chartTitleBoxBorderColor; + } + + public int getChartTitlePadding() { + + return chartTitlePadding; + } + + /** + * set the chart title padding; the space between the chart title and the plot area + * + * @param chartTitlePadding + */ + public void setChartTitlePadding(int chartTitlePadding) { + + this.chartTitlePadding = chartTitlePadding; + } + + public Color getLegendBackgroundColor() { + + return legendBackgroundColor; + } + + /** + * Set the chart legend background color + * + * @param color + */ + public void setLegendBackgroundColor(Color color) { + + this.legendBackgroundColor = color; + } + + /** + * Set the chart legend border color + * + * @return + */ + public Color getLegendBorderColor() { + + return legendBorderColor; + } + + // Chart Legend + + public void setLegendBorderColor(Color legendBorderColor) { + + this.legendBorderColor = legendBorderColor; + } + + public Font getLegendFont() { + + return legendFont; + } + + /** + * Set the chart legend font + * + * @param font + */ + public void setLegendFont(Font font) { + + this.legendFont = font; + } + + public boolean isLegendVisible() { + + return isLegendVisible; + } + + /** + * Set the chart legend visibility + * + * @param isLegendVisible + */ + public void setLegendVisible(boolean isLegendVisible) { + + this.isLegendVisible = isLegendVisible; + } + + public int getLegendPadding() { + + return legendPadding; + } + + /** + * Set the chart legend padding + * + * @param legendPadding + */ + public void setLegendPadding(int legendPadding) { + + this.legendPadding = legendPadding; + } + + public int getLegendSeriesLineLength() { + + return legendSeriesLineLength; + } + + /** + * Set the chart legend series line length + * + * @param legendSeriesLineLength + */ + public void setLegendSeriesLineLength(int legendSeriesLineLength) { + + if (legendSeriesLineLength < 0) { + this.legendSeriesLineLength = 0; + } else { + this.legendSeriesLineLength = legendSeriesLineLength; + } + } + + public LegendPosition getLegendPosition() { + + return legendPosition; + } + + /** + * sets the legend position + * + * @param legendPosition + */ + public void setLegendPosition(LegendPosition legendPosition) { + + this.legendPosition = legendPosition; + } + + /** + * Set the legend layout + * + * @return + */ + public LegendLayout getLegendLayout() { + + return legendLayout; + } + + public void setLegendLayout(LegendLayout legendLayout) { + + this.legendLayout = legendLayout; + } + + + public Color getPlotBackgroundColor() { + + return plotBackgroundColor; + } + + /** + * set the plot area's background color + * + * @param plotBackgroundColor + */ + public void setPlotBackgroundColor(Color plotBackgroundColor) { + + this.plotBackgroundColor = plotBackgroundColor; + } + + public Color getPlotBorderColor() { + + return plotBorderColor; + } + + // Chart Plot + + /** + * set the plot area's border color + * + * @param plotBorderColor + */ + public void setPlotBorderColor(Color plotBorderColor) { + + this.plotBorderColor = plotBorderColor; + } + + public boolean isPlotBorderVisible() { + + return isPlotBorderVisible; + } + + /** + * sets the visibility of the border around the plot area + * + * @param isPlotBorderVisible + */ + public void setPlotBorderVisible(boolean isPlotBorderVisible) { + + this.isPlotBorderVisible = isPlotBorderVisible; + } + + public double getPlotContentSize() { + + return plotContentSize; + } + + /** + * Sets the content size of the plot inside the plot area of the chart. To fill the area 100%, use a value of 1.0. + * + * @param plotContentSize - Valid range is between 0 and 1. + */ + public void setPlotContentSize(double plotContentSize) { + + if (plotContentSize < 0 || plotContentSize > 1) { + throw new IllegalArgumentException("Plot content size must be tween 0 and 1"); + } + + this.plotContentSize = plotContentSize; + } + + public Boolean hasAnnotations() { + + return hasAnnotations; + } + + /** + * Sets if annotations should be added to charts. Each chart type has a different annotation type + * + * @param hasAnnotations + */ + public void setHasAnnotations(boolean hasAnnotations) { + this.hasAnnotations = hasAnnotations; + } + + public Font getAnnotationsFont() { + return annotationsFont; + } + + /** + * Sets the Font used for chart annotations + * + * @param annotationsFont + */ + public void setAnnotationsFont(Font annotationsFont) { + this.annotationsFont = annotationsFont; + } + + /** + * Set the decimal formatter for all numbers on the chart rendered as Strings + * + * @param decimalPattern - the pattern describing the decimal format + */ + public void setDecimalPattern(String decimalPattern) { + this.decimalPattern = decimalPattern; + } + + public String getDecimalPattern() { + return decimalPattern; + } + + /** + * Set the YAxis group position. + * + * @param yAxisGroup + * @param yAxisPosition + */ + public void setYAxisGroupPosition(int yAxisGroup, YAxisPosition yAxisPosition) { + yAxisAlignmentMap.put(yAxisGroup, yAxisPosition); + } + + public YAxisPosition getYAxisGroupPosistion(int yAxisGroup) { + return yAxisAlignmentMap.get(yAxisGroup); + } + + public void setAntiAlias(boolean newVal) { + antiAlias = newVal; + } + + public boolean getAntiAlias() { + return antiAlias; + } + +} diff --git a/chart/src/main/java/org/xbib/graphics/chart/theme/DefaultTheme.java b/chart/src/main/java/org/xbib/graphics/chart/theme/DefaultTheme.java new file mode 100644 index 0000000..bb2a30c --- /dev/null +++ b/chart/src/main/java/org/xbib/graphics/chart/theme/DefaultTheme.java @@ -0,0 +1,313 @@ +package org.xbib.graphics.chart.theme; + +import org.xbib.graphics.chart.legend.LegendPosition; +import org.xbib.graphics.chart.pie.PieStyler.AnnotationType; + +import java.awt.BasicStroke; +import java.awt.Color; +import java.awt.Font; +import java.awt.Stroke; +import java.util.List; + +public class DefaultTheme implements Theme { + + @Override + public Font getBaseFont() { + return Fonts.SANS_SERIF_PLAIN_10; + } + + @Override + public boolean isDrawAllAnnotations() { + return false; + } + + @Override + public double getDonutThickness() { + return .33; + } + + @Override + public boolean isSumVisible() { + return false; + } + + @Override + public Font getSumFont() { + return getAnnotationFont(); + } + + @Override + public Font getAnnotationFont() { + return getPieFont().deriveFont(12f); + } + + @Override + public Color getChartBackgroundColor() { + return Colors.WHITE; + } + + @Override + public Color getChartFontColor() { + return Colors.BLACK; + } + + @Override + public int getChartPadding() { + return 10; + } + + @Override + public List getSeriesColors() { + return PrinterFriendly.SERIES_COLORS; + } + + @Override + public List getSeriesMarkers() { + return Default.SERIES_MARKERS; + } + + @Override + public List getSeriesLines() { + return Default.SERIES_LINES; + } + + @Override + public Font getChartTitleFont() { + return Fonts.SANS_SERIF_BOLD_14; + } + + @Override + public boolean isChartTitleVisible() { + return true; + } + + @Override + public boolean isChartTitleBoxVisible() { + return false; + } + + @Override + public Color getChartTitleBoxBackgroundColor() { + return Colors.GREY; + } + + @Override + public Color getChartTitleBoxBorderColor() { + return Colors.GREY; + } + + @Override + public int getChartTitlePadding() { + return 5; + } + + @Override + public Font getLegendFont() { + return Fonts.SANS_SERIF_PLAIN_11; + } + + @Override + public boolean isLegendVisible() { + return true; + } + + @Override + public Color getLegendBackgroundColor() { + return Colors.WHITE; + } + + @Override + public Color getLegendBorderColor() { + return Colors.DARK_GREY; + } + + @Override + public int getLegendPadding() { + return 10; + } + + @Override + public int getLegendSeriesLineLength() { + return 24; + } + + @Override + public LegendPosition getLegendPosition() { + return LegendPosition.OutsideE; + } + + @Override + public boolean isXAxisTitleVisible() { + return true; + } + + @Override + public boolean isYAxisTitleVisible() { + return true; + } + + @Override + public Font getAxisTitleFont() { + return Fonts.SANS_SERIF_BOLD_12; + } + + @Override + public boolean isXAxisTicksVisible() { + return true; + } + + @Override + public boolean isYAxisTicksVisible() { + return true; + } + + @Override + public Font getAxisTickLabelsFont() { + return Fonts.SANS_SERIF_BOLD_12; + } + + @Override + public int getAxisTickMarkLength() { + return 3; + } + + @Override + public int getAxisTickPadding() { + return 4; + } + + @Override + public int getPlotMargin() { + return 4; + } + + @Override + public Color getAxisTickMarksColor() { + return Colors.DARK_GREY; + } + + @Override + public Stroke getAxisTickMarksStroke() { + return Standard.AXIS_TICKMARK; + } + + @Override + public Color getAxisTickLabelsColor() { + return Colors.BLACK; + } + + @Override + public boolean isAxisTicksLineVisible() { + return true; + } + + @Override + public boolean isAxisTicksMarksVisible() { + return true; + } + + @Override + public int getAxisTitlePadding() { + return 10; + } + + @Override + public int getXAxisTickMarkSpacingHint() { + return 74; + } + + @Override + public int getYAxisTickMarkSpacingHint() { + return 44; + } + + @Override + public boolean isPlotGridVerticalLinesVisible() { + return true; + } + + @Override + public boolean isPlotGridHorizontalLinesVisible() { + return true; + } + + @Override + public Color getPlotBackgroundColor() { + return Colors.WHITE; + } + + @Override + public Color getPlotBorderColor() { + return Colors.DARK_GREY; + } + + @Override + public boolean isPlotBorderVisible() { + return true; + } + + @Override + public boolean isPlotTicksMarksVisible() { + return false; + } + + @Override + public Color getPlotGridLinesColor() { + return Colors.WHITE; + } + + @Override + public Stroke getPlotGridLinesStroke() { + return Standard.GRID_LINES; + } + + @Override + public double getPlotContentSize() { + return .92; + } + + @Override + public double getAvailableSpaceFill() { + return 0.9; + } + + @Override + public boolean isOverlapped() { + return false; + } + + @Override + public boolean isCircular() { + return true; + } + + @Override + public Font getPieFont() { + return Fonts.SANS_SERIF_PLAIN_15; + } + + @Override + public double getAnnotationDistance() { + return .67; + } + + @Override + public AnnotationType getAnnotationType() { + return AnnotationType.Percentage; + } + + @Override + public int getMarkerSize() { + return 8; + } + + @Override + public Color getErrorBarsColor() { + return Colors.BLACK; + } + + @Override + public boolean isErrorBarsColorSeriesColor() { + return false; + } +} diff --git a/chart/src/main/java/org/xbib/graphics/chart/theme/GGPlot2Theme.java b/chart/src/main/java/org/xbib/graphics/chart/theme/GGPlot2Theme.java new file mode 100644 index 0000000..1d22013 --- /dev/null +++ b/chart/src/main/java/org/xbib/graphics/chart/theme/GGPlot2Theme.java @@ -0,0 +1,283 @@ +package org.xbib.graphics.chart.theme; + +import org.xbib.graphics.chart.legend.LegendPosition; +import org.xbib.graphics.chart.pie.PieStyler.AnnotationType; + +import java.awt.BasicStroke; +import java.awt.Color; +import java.awt.Font; +import java.awt.Stroke; +import java.util.List; + +public class GGPlot2Theme extends DefaultTheme { + + @Override + public Color getChartBackgroundColor() { + return Theme.Colors.WHITE; + } + + @Override + public Color getChartFontColor() { + return Colors.BLACK; + } + + @Override + public int getChartPadding() { + return 10; + } + + @Override + public List getSeriesMarkers() { + return GGPlot2.SERIES_MARKERS; + } + + @Override + public List getSeriesLines() { + return GGPlot2.SERIES_LINES; + } + + @Override + public List getSeriesColors() { + return GGPlot2.SERIES_COLORS; + } + + @Override + public Font getChartTitleFont() { + return Fonts.SANS_SERIF_PLAIN_14; + } + + @Override + public boolean isChartTitleVisible() { + return true; + } + + @Override + public boolean isChartTitleBoxVisible() { + return true; + } + + @Override + public Color getChartTitleBoxBackgroundColor() { + return Colors.GREY; + } + + @Override + public Color getChartTitleBoxBorderColor() { + return Colors.GREY; + } + + @Override + public int getChartTitlePadding() { + return 5; + } + + @Override + public Font getLegendFont() { + return Fonts.SANS_SERIF_PLAIN_14; + } + + @Override + public boolean isLegendVisible() { + return true; + } + + @Override + public Color getLegendBackgroundColor() { + return Colors.WHITE; + } + + @Override + public Color getLegendBorderColor() { + return Colors.WHITE; + } + + @Override + public int getLegendPadding() { + return 10; + } + + @Override + public int getLegendSeriesLineLength() { + return 24; + } + + @Override + public LegendPosition getLegendPosition() { + return LegendPosition.OutsideE; + } + + @Override + public boolean isXAxisTitleVisible() { + return true; + } + + @Override + public boolean isYAxisTitleVisible() { + return true; + } + + @Override + public Font getAxisTitleFont() { + return Fonts.SANS_SERIF_PLAIN_14; + } + + @Override + public boolean isXAxisTicksVisible() { + return true; + } + + @Override + public boolean isYAxisTicksVisible() { + return true; + } + + @Override + public Font getAxisTickLabelsFont() { + return Fonts.SANS_SERIF_BOLD_13; + } + + @Override + public int getAxisTickMarkLength() { + return 8; + } + + @Override + public int getAxisTickPadding() { + return 5; + } + + @Override + public int getPlotMargin() { + return 0; + } + + @Override + public boolean isAxisTicksLineVisible() { + return false; + } + + @Override + public boolean isAxisTicksMarksVisible() { + return true; + } + + @Override + public Color getAxisTickMarksColor() { + return Colors.DARK_GREY; + } + + @Override + public Stroke getAxisTickMarksStroke() { + return GGPlot2.AXIS_TICKMARK; + } + + @Override + public Color getAxisTickLabelsColor() { + return Colors.DARK_GREY; + } + + @Override + public int getAxisTitlePadding() { + return 10; + } + + @Override + public int getXAxisTickMarkSpacingHint() { + return 74; + } + + @Override + public int getYAxisTickMarkSpacingHint() { + return 44; + } + + @Override + public boolean isPlotGridVerticalLinesVisible() { + return true; + } + + @Override + public boolean isPlotGridHorizontalLinesVisible() { + return true; + } + + @Override + public Color getPlotBackgroundColor() { + return Colors.LIGHT_GREY; + } + + @Override + public Color getPlotBorderColor() { + return Colors.WHITE; + } + + @Override + public boolean isPlotBorderVisible() { + return false; + } + + @Override + public boolean isPlotTicksMarksVisible() { + return false; + } + + @Override + public Color getPlotGridLinesColor() { + return Colors.WHITE; + } + + @Override + public Stroke getPlotGridLinesStroke() { + return GGPlot2.GRID_LINES; + } + + @Override + public double getPlotContentSize() { + return .92; + } + + @Override + public double getAvailableSpaceFill() { + return 0.9; + } + + @Override + public boolean isOverlapped() { + return false; + } + + @Override + public boolean isCircular() { + return true; + } + + @Override + public Font getPieFont() { + return Fonts.SANS_SERIF_PLAIN_15; + } + + @Override + public double getAnnotationDistance() { + return .67; + } + + @Override + public AnnotationType getAnnotationType() { + return AnnotationType.LabelAndPercentage; + } + + @Override + public int getMarkerSize() { + return 8; + } + + @Override + public Color getErrorBarsColor() { + return Colors.DARK_GREY; + } + + @Override + public boolean isErrorBarsColorSeriesColor() { + return false; + } +} diff --git a/chart/src/main/java/org/xbib/graphics/chart/theme/MatlabTheme.java b/chart/src/main/java/org/xbib/graphics/chart/theme/MatlabTheme.java new file mode 100644 index 0000000..eb498a9 --- /dev/null +++ b/chart/src/main/java/org/xbib/graphics/chart/theme/MatlabTheme.java @@ -0,0 +1,284 @@ +package org.xbib.graphics.chart.theme; + +import org.xbib.graphics.chart.legend.LegendPosition; +import org.xbib.graphics.chart.pie.PieStyler.AnnotationType; + +import java.awt.BasicStroke; +import java.awt.Color; +import java.awt.Font; +import java.awt.Stroke; +import java.util.List; + +public class MatlabTheme extends DefaultTheme { + + @Override + public Color getChartBackgroundColor() { + return Colors.WHITE; + } + + @Override + public Color getChartFontColor() { + return Colors.BLACK; + } + + @Override + public int getChartPadding() { + return 10; + } + + @Override + public List getSeriesMarkers() { + return Matlab.SERIES_MARKERS; + } + + @Override + public List getSeriesLines() { + return Matlab.SERIES_LINES; + } + + @Override + public List getSeriesColors() { + return Matlab.SERIES_COLORS; + } + + @Override + public Font getChartTitleFont() { + return Fonts.SANS_SERIF_BOLD_14; + } + + @Override + public boolean isChartTitleVisible() { + return true; + } + + @Override + public boolean isChartTitleBoxVisible() { + return false; + } + + @Override + public Color getChartTitleBoxBackgroundColor() { + return Colors.WHITE; + } + + @Override + public Color getChartTitleBoxBorderColor() { + return Colors.WHITE; + } + + @Override + public int getChartTitlePadding() { + return 5; + } + + @Override + public Font getLegendFont() { + return Fonts.SANS_SERIF_PLAIN_11; + } + + @Override + public boolean isLegendVisible() { + return true; + } + + @Override + public Color getLegendBackgroundColor() { + return Colors.WHITE; + } + + @Override + public Color getLegendBorderColor() { + return Colors.BLACK; + } + + @Override + public int getLegendPadding() { + return 10; + } + + @Override + public int getLegendSeriesLineLength() { + return 24; + } + + @Override + public LegendPosition getLegendPosition() { + return LegendPosition.OutsideE; + } + + @Override + public boolean isXAxisTitleVisible() { + return true; + } + + @Override + public boolean isYAxisTitleVisible() { + return true; + } + + @Override + public Font getAxisTitleFont() { + return Fonts.SANS_SERIF_PLAIN_12; + } + + @Override + public boolean isXAxisTicksVisible() { + return true; + } + + @Override + public boolean isYAxisTicksVisible() { + return true; + } + + @Override + public Font getAxisTickLabelsFont() { + return Fonts.SANS_SERIF_PLAIN_12; + } + + @Override + public int getAxisTickMarkLength() { + return 5; + } + + @Override + public int getAxisTickPadding() { + return 4; + } + + @Override + public int getPlotMargin() { + return 3; + } + + @Override + public Color getAxisTickMarksColor() { + return Colors.BLACK; + } + + @Override + public Stroke getAxisTickMarksStroke() { + return Matlab.AXIS_TICKMARK; + } + + @Override + public Color getAxisTickLabelsColor() { + return Colors.BLACK; + } + + @Override + public boolean isAxisTicksLineVisible() { + return false; + } + + @Override + public boolean isAxisTicksMarksVisible() { + return false; + } + + @Override + public int getAxisTitlePadding() { + return 10; + } + + @Override + public int getXAxisTickMarkSpacingHint() { + return 74; + } + + @Override + public int getYAxisTickMarkSpacingHint() { + return 44; + } + + @Override + public boolean isPlotGridVerticalLinesVisible() { + return true; + } + + @Override + public boolean isPlotGridHorizontalLinesVisible() { + return true; + } + + @Override + public Color getPlotBackgroundColor() { + return Colors.WHITE; + } + + @Override + public Color getPlotBorderColor() { + return Colors.BLACK; + } + + @Override + public boolean isPlotBorderVisible() { + return true; + } + + @Override + public boolean isPlotTicksMarksVisible() { + return true; + } + + @Override + public Color getPlotGridLinesColor() { + return Colors.BLACK; + } + + @Override + public Stroke getPlotGridLinesStroke() { + return Matlab.GRID_LINES; + } + + @Override + public double getPlotContentSize() { + return .92; + } + + @Override + public double getAvailableSpaceFill() { + return 0.9; + } + + @Override + public boolean isOverlapped() { + return false; + } + + @Override + public boolean isCircular() { + return true; + } + + @Override + public Font getPieFont() { + return Fonts.SANS_SERIF_PLAIN_15; + } + + @Override + public double getAnnotationDistance() { + return .67; + } + + @Override + public AnnotationType getAnnotationType() { + return AnnotationType.Label; + } + + @Override + public int getMarkerSize() { + return 8; + } + + @Override + public Color getErrorBarsColor() { + return Colors.BLACK; + } + + @Override + public boolean isErrorBarsColorSeriesColor() { + return false; + } + +} diff --git a/chart/src/main/java/org/xbib/graphics/chart/theme/Theme.java b/chart/src/main/java/org/xbib/graphics/chart/theme/Theme.java new file mode 100644 index 0000000..85776be --- /dev/null +++ b/chart/src/main/java/org/xbib/graphics/chart/theme/Theme.java @@ -0,0 +1,431 @@ +package org.xbib.graphics.chart.theme; + +import org.xbib.graphics.chart.legend.LegendPosition; +import org.xbib.graphics.chart.pie.PieStyler.AnnotationType; + +import java.awt.BasicStroke; +import java.awt.Color; +import java.awt.Font; +import java.awt.Graphics2D; +import java.awt.Shape; +import java.awt.Stroke; +import java.awt.geom.Ellipse2D; +import java.awt.geom.Path2D; +import java.awt.geom.Rectangle2D; +import java.util.Arrays; +import java.util.Collections; +import java.util.List; + +public interface Theme { + + interface Default { + + List SERIES_COLORS = Arrays.asList( + new Color(141, 211, 199), + new Color(255, 255, 179), + new Color(190, 186, 218), + new Color(251, 128, 114), + new Color(128, 177, 211), + new Color(253, 180, 98), + new Color(179, 222, 105), + new Color(252, 205, 229), + new Color(217, 217, 217), + new Color(188, 128, 189), + new Color(204, 235, 197), + new Color(255, 237, 111) + ); + + List SERIES_MARKERS = Arrays.asList( + Series.CIRCLE_MARKER, + Series.SQUARE_MARKER, + Series.DIAMOND_MARKER, + Series.TRIANGLE_UP_MARKER, + Series.TRIANGLE_DOWN_MARKER, + Series.CROSS_MARKER); + + List SERIES_LINES = Arrays.asList( + Series.SOLID_STROKE, + Series.DOT_DOT_STROKE, + Series.DASH_DASH_STROKE, + Series.DASH_DOT_STROKE); + + BasicStroke AXIS_TICKMARK = new BasicStroke(1.0f); + + BasicStroke GRID_LINES = new BasicStroke(1.0f, BasicStroke.CAP_BUTT, BasicStroke.JOIN_BEVEL, 10.0f, new float[] {3.0f, 5.0f}, 0.0f); + } + + interface Standard { + + Color BLUE = new Color(0, 55, 255, 180); + Color ORANGE = new Color(255, 172, 0, 180); + Color PURPLE = new Color(128, 0, 255, 180); + Color GREEN = new Color(0, 205, 0, 180); + Color RED = new Color(205, 0, 0, 180); + Color YELLOW = new Color(255, 215, 0, 180); + Color MAGENTA = new Color(255, 0, 255, 180); + Color PINK = new Color(255, 166, 201, 180); + Color LIGHT_GREY = new Color(207, 207, 207, 180); + Color CYAN = new Color(0, 255, 255, 180); + Color BROWN = new Color(102, 56, 10, 180); + Color BLACK = new Color(0, 0, 0, 180); + + List SERIES_COLORS = Arrays.asList( + BLUE, ORANGE, PURPLE, GREEN, RED, YELLOW, MAGENTA, PINK, LIGHT_GREY, CYAN, BROWN, BLACK + ); + + List SERIES_MARKERS = Arrays.asList( + Series.CIRCLE_MARKER, + Series.DIAMOND_MARKER, + Series.SQUARE_MARKER, + Series.TRIANGLE_DOWN_MARKER, + Series.TRIANGLE_UP_MARKER); + + + List SERIES_LINES = Arrays.asList( + Series.SOLID_STROKE, + Series.DASH_DOT_STROKE, + Series.DASH_DASH_STROKE, + Series.DOT_DOT_STROKE); + + BasicStroke AXIS_TICKMARK = new BasicStroke(1, BasicStroke.CAP_BUTT, BasicStroke.JOIN_BEVEL, 10.0f, new float[]{3.0f, 0.0f}, 0.0f); + + BasicStroke GRID_LINES = new BasicStroke(1, BasicStroke.CAP_BUTT, BasicStroke.JOIN_BEVEL, 10.0f, new float[]{3.0f, 3.0f}, 0.0f); + } + + interface GGPlot2 { + List SERIES_LINES = + Arrays.asList(Series.SOLID_STROKE, Series.DOT_DOT_STROKE, Series.DASH_DASH_STROKE); + + List SERIES_MARKERS = Arrays.asList(Series.CIRCLE_MARKER, + Series.DIAMOND_MARKER); + + Color RED = new Color(248, 118, 109, 255); + Color YELLOW_GREEN = new Color(163, 165, 0, 255); + Color GREEN = new Color(0, 191, 125, 255); + Color BLUE = new Color(0, 176, 246, 255); + Color PURPLE = new Color(231, 107, 243, 255); + + List SERIES_COLORS = Arrays.asList(RED, YELLOW_GREEN, GREEN, BLUE, PURPLE); + + BasicStroke AXIS_TICKMARK = new BasicStroke(1.5f, BasicStroke.CAP_BUTT, BasicStroke.JOIN_BEVEL, 10.0f, new float[]{3.0f, 0.0f}, 0.0f); + + BasicStroke GRID_LINES = new BasicStroke(1.5f, BasicStroke.CAP_BUTT, BasicStroke.JOIN_BEVEL, 10.0f, new float[]{3.0f, 0.0f}, 0.0f); + } + + interface Matlab { + + List SERIES_LINES = + Arrays.asList(Series.SOLID_STROKE/*, Series.DASH_DASH_STROKE, Series.DOT_DOT_STROKE*/); + + List SERIES_MARKERS = + Collections.singletonList(Series.NONE_MARKER); + + Color BLUE = new Color(0, 0, 255, 255); + Color GREEN = new Color(0, 128, 0, 255); + Color RED = new Color(255, 0, 0, 255); + Color TURQUOISE = new Color(0, 191, 191, 255); + Color MAGENTA = new Color(191, 0, 191, 255); + Color YELLOW = new Color(191, 191, 0, 255); + Color DARK_GREY = new Color(64, 64, 64, 255); + + List SERIES_COLORS = + Arrays.asList(BLUE, GREEN, RED, TURQUOISE, MAGENTA, YELLOW, DARK_GREY); + + BasicStroke AXIS_TICKMARK = new BasicStroke(.5f); + //new BasicStroke(1, BasicStroke.CAP_BUTT, BasicStroke.JOIN_BEVEL, 10.0f, new float[]{3.0f, 0.0f}, 0.0f); + + BasicStroke GRID_LINES = new BasicStroke(.5f, BasicStroke.CAP_BUTT, BasicStroke.JOIN_ROUND, 10.0f, new float[] {1f, 3.0f}, 0.0f); + + //new BasicStroke(1.0f, BasicStroke.CAP_BUTT, BasicStroke.JOIN_ROUND, 10.0f, new float[]{1.0f, 2.0f}, 0.0f); + } + + interface PrinterFriendly { + Color RED = new Color(228, 26, 28, 180); + Color GREEN = new Color(55, 126, 184, 180); + Color BLUE = new Color(77, 175, 74, 180); + Color PURPLE = new Color(152, 78, 163, 180); + Color ORANGE = new Color(255, 127, 0, 180); + Color YELLOW = new Color(255, 255, 51, 180); + Color BROWN = new Color(166, 86, 40, 180); + Color PINK = new Color(247, 129, 191, 180); + List SERIES_COLORS = + Arrays.asList(RED, GREEN, BLUE, PURPLE, ORANGE); + } + + interface ColorBlindFriendly { + Color BLACK = new Color(0, 0, 0, 255); + Color ORANGE = new Color(230, 159, 0, 255); + Color SKY_BLUE = new Color(86, 180, 233, 255); + Color BLUISH_GREEN = new Color(0, 158, 115, 255); + Color YELLOW = new Color(240, 228, 66, 255); + Color BLUE = new Color(0, 114, 178, 255); + Color VERMILLION = new Color(213, 94, 0, 255); + Color REDDISH_PURPLE = new Color(204, 121, 167, 255); + + List SERIES_COLORS = + Arrays.asList(BLACK, ORANGE, SKY_BLUE, BLUISH_GREEN, YELLOW, BLUE, VERMILLION, REDDISH_PURPLE); + } + + interface Colors { + Color BLACK = new Color(0, 0, 0); + Color DARK_GREY = new Color(130, 130, 130); + Color GREY = new Color(210, 210, 210); + Color LIGHT_GREY = new Color(230, 230, 230); + Color WHITE = new Color(255, 255, 255); + } + + interface Fonts { + Font SANS_SERIF_PLAIN_10 = new Font(Font.SANS_SERIF, Font.PLAIN, 10); + Font SANS_SERIF_PLAIN_11 = new Font(Font.SANS_SERIF, Font.PLAIN, 11); + Font SANS_SERIF_PLAIN_12 = new Font(Font.SANS_SERIF, Font.PLAIN, 12); + Font SANS_SERIF_PLAIN_14 = new Font(Font.SANS_SERIF, Font.PLAIN, 14); + Font SANS_SERIF_PLAIN_15 = new Font(Font.SANS_SERIF, Font.PLAIN, 15); + Font SANS_SERIF_BOLD_12 = new Font(Font.SANS_SERIF, Font.BOLD, 12); + Font SANS_SERIF_BOLD_13 = new Font(Font.SANS_SERIF, Font.BOLD, 13); + Font SANS_SERIF_BOLD_14 = new Font(Font.SANS_SERIF, Font.BOLD, 14); + } + + interface Strokes { + BasicStroke TITLE = new BasicStroke(1, BasicStroke.CAP_BUTT, BasicStroke.JOIN_BEVEL); + BasicStroke LEGEND = new BasicStroke(1, BasicStroke.CAP_BUTT, BasicStroke.JOIN_BEVEL, 10.0f, new float[] {3.0f, 0.0f}, 0.0f); + BasicStroke ERROR_BARS = new BasicStroke(1.0f, BasicStroke.CAP_BUTT, BasicStroke.JOIN_BEVEL); + BasicStroke PIE = new BasicStroke(2.0f, BasicStroke.CAP_BUTT, BasicStroke.JOIN_MITER); + } + + interface Series { + + BasicStroke NONE_STROKE = new BasicStroke(); + BasicStroke SOLID_STROKE = new BasicStroke(2.0f, BasicStroke.CAP_BUTT, BasicStroke.JOIN_MITER); + BasicStroke DASH_DOT_STROKE = new BasicStroke(2.0f, BasicStroke.CAP_BUTT, BasicStroke.JOIN_MITER, 10.0f, new float[]{3.0f, 1.0f}, 0.0f); + BasicStroke DASH_DASH_STROKE = new BasicStroke(2.0f, BasicStroke.CAP_BUTT, BasicStroke.JOIN_MITER, 10.0f, new float[]{3.0f, 3.0f}, 0.0f); + BasicStroke DOT_DOT_STROKE = new BasicStroke(2.0f, BasicStroke.CAP_BUTT, BasicStroke.JOIN_ROUND, 10.0f, new float[]{2.0f}, 0.0f); + + abstract class Marker { + + protected BasicStroke stroke = new BasicStroke(1, BasicStroke.CAP_BUTT, BasicStroke.JOIN_BEVEL); + + public abstract void paint(Graphics2D g, double xOffset, double yOffset, int markerSize); + + } + + class Circle extends Marker { + + @Override + public void paint(Graphics2D g, double xOffset, double yOffset, int markerSize) { + g.setStroke(stroke); + double halfSize = (double) markerSize / 2; + Shape circle = new Ellipse2D.Double(xOffset - halfSize, yOffset - halfSize, markerSize, markerSize); + g.fill(circle); + } + } + + class Cross extends Marker { + + @Override + public void paint(Graphics2D g, double xOffset, double yOffset, int markerSize) { + g.setStroke(stroke); + double halfSize = (double) markerSize / 2; + Path2D.Double path = new Path2D.Double(); + path.moveTo(xOffset - halfSize, yOffset - halfSize); + path.lineTo(xOffset + halfSize, yOffset + halfSize); + path.moveTo(xOffset - halfSize, yOffset + halfSize); + path.lineTo(xOffset + halfSize, yOffset - halfSize); + g.draw(path); + } + } + + class Diamond extends Marker { + + @Override + public void paint(Graphics2D g, double xOffset, double yOffset, int markerSize) { + g.setStroke(stroke); + double diamondHalfSize = (double) markerSize / 2 * 1.3; + Path2D.Double path = new Path2D.Double(); + path.moveTo(xOffset - diamondHalfSize, yOffset); + path.lineTo(xOffset, yOffset - diamondHalfSize); + path.lineTo(xOffset + diamondHalfSize, yOffset); + path.lineTo(xOffset, yOffset + diamondHalfSize); + path.closePath(); + g.fill(path); + } + } + + class None extends Marker { + + @Override + public void paint(Graphics2D g, double xOffset, double yOffset, int markerSize) { + // do nothing! + } + } + + class Square extends Marker { + + @Override + public void paint(Graphics2D g, double xOffset, double yOffset, int markerSize) { + g.setStroke(stroke); + double halfSize = (double) markerSize / 2; + Shape square = new Rectangle2D.Double(xOffset - halfSize, yOffset - halfSize, markerSize, markerSize); + g.fill(square); + } + } + + class TriangleDown extends Marker { + + @Override + public void paint(Graphics2D g, double xOffset, double yOffset, int markerSize) { + g.setStroke(stroke); + double halfSize = (double) markerSize / 2; + Path2D.Double path = new Path2D.Double(); + path.moveTo(xOffset - halfSize, 1 + yOffset - halfSize); + path.lineTo(xOffset, 1 + yOffset - halfSize + markerSize); + path.lineTo(xOffset - halfSize + markerSize, 1 + yOffset - halfSize); + path.closePath(); + g.fill(path); + } + } + + class TriangleUp extends Marker { + + @Override + public void paint(Graphics2D g, double xOffset, double yOffset, int markerSize) { + g.setStroke(stroke); + double halfSize = (double) markerSize / 2; + Path2D.Double path = new Path2D.Double(); + path.moveTo(xOffset - halfSize, yOffset - halfSize + markerSize - 1); + path.lineTo(xOffset - halfSize + markerSize, yOffset - halfSize + markerSize - 1); + path.lineTo(xOffset, yOffset - halfSize - 1); + path.closePath(); + g.fill(path); + } + } + + Marker NONE_MARKER = new None(); + Marker CIRCLE_MARKER = new Circle(); + Marker CROSS_MARKER = new Cross(); + Marker DIAMOND_MARKER = new Diamond(); + Marker SQUARE_MARKER = new Square(); + Marker TRIANGLE_DOWN_MARKER = new TriangleDown(); + Marker TRIANGLE_UP_MARKER = new TriangleUp(); + } + + Font getBaseFont(); + + Color getChartBackgroundColor(); + + Color getChartFontColor(); + + int getChartPadding(); + + Font getChartTitleFont(); + + boolean isChartTitleVisible(); + + boolean isChartTitleBoxVisible(); + + Color getChartTitleBoxBackgroundColor(); + + Color getChartTitleBoxBorderColor(); + + int getChartTitlePadding(); + + Font getLegendFont(); + + boolean isLegendVisible(); + + Color getLegendBackgroundColor(); + + Color getLegendBorderColor(); + + int getLegendPadding(); + + int getLegendSeriesLineLength(); + + LegendPosition getLegendPosition(); + + boolean isXAxisTitleVisible(); + + boolean isYAxisTitleVisible(); + + Font getAxisTitleFont(); + + boolean isXAxisTicksVisible(); + + boolean isYAxisTicksVisible(); + + Font getAxisTickLabelsFont(); + + int getAxisTickMarkLength(); + + int getAxisTickPadding(); + + Color getAxisTickMarksColor(); + + Stroke getAxisTickMarksStroke(); + + Color getAxisTickLabelsColor(); + + boolean isAxisTicksLineVisible(); + + boolean isAxisTicksMarksVisible(); + + int getAxisTitlePadding(); + + int getXAxisTickMarkSpacingHint(); + + int getYAxisTickMarkSpacingHint(); + + boolean isPlotGridVerticalLinesVisible(); + + boolean isPlotGridHorizontalLinesVisible(); + + Color getPlotBackgroundColor(); + + Color getPlotBorderColor(); + + boolean isPlotBorderVisible(); + + Color getPlotGridLinesColor(); + + Stroke getPlotGridLinesStroke(); + + boolean isPlotTicksMarksVisible(); + + double getPlotContentSize(); + + int getPlotMargin(); + + double getAvailableSpaceFill(); + + boolean isOverlapped(); + + boolean isCircular(); + + Font getPieFont(); + + double getAnnotationDistance(); + + AnnotationType getAnnotationType(); + + boolean isDrawAllAnnotations(); + + double getDonutThickness(); + + boolean isSumVisible(); + + Font getSumFont(); + + int getMarkerSize(); + + Color getErrorBarsColor(); + + boolean isErrorBarsColorSeriesColor(); + + Font getAnnotationFont(); + + List getSeriesMarkers(); + + List getSeriesLines(); + + List getSeriesColors(); + +} diff --git a/chart/src/main/java/org/xbib/graphics/chart/xy/XYChart.java b/chart/src/main/java/org/xbib/graphics/chart/xy/XYChart.java new file mode 100644 index 0000000..5abb9f6 --- /dev/null +++ b/chart/src/main/java/org/xbib/graphics/chart/xy/XYChart.java @@ -0,0 +1,364 @@ +package org.xbib.graphics.chart.xy; + +import org.xbib.graphics.chart.axis.DataType; +import org.xbib.graphics.chart.axis.Axis; +import org.xbib.graphics.chart.axis.AxisPair; +import org.xbib.graphics.chart.Chart; +import org.xbib.graphics.chart.legend.MarkerLegend; +import org.xbib.graphics.chart.plot.AxesChartPlot; +import org.xbib.graphics.chart.plot.ContentPlot; +import org.xbib.graphics.chart.style.AxesChartStyler; +import org.xbib.graphics.chart.style.SeriesColorMarkerLineStyle; +import org.xbib.graphics.chart.style.SeriesColorMarkerLineStyleCycler; +import org.xbib.graphics.chart.theme.Theme; + +import java.awt.Graphics2D; +import java.awt.RenderingHints; +import java.awt.Shape; +import java.awt.geom.Line2D; +import java.awt.geom.Path2D; +import java.awt.geom.Rectangle2D; +import java.time.Instant; +import java.util.Collection; +import java.util.Iterator; +import java.util.List; +import java.util.Map; + +public class XYChart extends Chart { + + public XYChart(int width, int height) { + super(width, height, new XYStyler()); + axisPair = new AxisPair<>(this); + plot = new XYPlot<>(this); + legend = new MarkerLegend<>(this); + } + + public XYChart(int width, int height, Theme theme) { + this(width, height); + styler.setTheme(theme); + } + + public XYChart(XYChartBuilder chartBuilder) { + this(chartBuilder.getWidth(), chartBuilder.getHeight(), chartBuilder.getTheme()); + setTitle(chartBuilder.getTitle()); + setXAxisTitle(chartBuilder.getxAxisTitle()); + setYAxisTitle(chartBuilder.getyAxisTitle()); + } + + public XYSeries addSeries(String seriesName, List xData, List yData) { + return addSeries(seriesName, xData, yData, null, getDataType(xData)); + } + + public XYSeries addSeries(String seriesName, List xData, List yData, List errorBars) { + return addSeries(seriesName, xData, yData, errorBars, getDataType(xData)); + } + + public XYSeries addSeries(String seriesName, double[] xData, double[] yData) { + return addSeries(seriesName, xData, yData, null); + } + + public XYSeries addSeries(String seriesName, double[] xData, double[] yData, double[] errorBars) { + return addSeries(seriesName, + listFromDoubleArray(xData), + listFromDoubleArray(yData), + listFromDoubleArray(errorBars), + DataType.Number); + } + + public XYSeries addSeries(String seriesName, int[] xData, int[] yData) { + return addSeries(seriesName, xData, yData, null); + } + + public XYSeries addSeries(String seriesName, int[] xData, int[] yData, int[] errorBars) { + return addSeries(seriesName, listFromIntArray(xData), listFromIntArray(yData), + listFromIntArray(errorBars), DataType.Number); + } + + public XYSeries addSeries(String seriesName, + List xData, + List yData, + List errorBars, + DataType dataType) { + sanityCheck(seriesName, xData, yData, errorBars); + XYSeries series; + if (xData != null) { + if (xData.size() != yData.size()) { + throw new IllegalArgumentException("X and Y-Axis sizes are not the same"); + } + series = new XYSeries(seriesName, xData, yData, errorBars, dataType); + } else { + series = new XYSeries(seriesName, getGeneratedData(yData.size()), yData, errorBars, dataType); + } + seriesMap.put(seriesName, series); + return series; + } + + @Override + public void paint(Graphics2D g, int width, int height) { + setWidth(width); + setHeight(height); + for (XYSeries XYSeries : getSeriesMap().values()) { + XYSeriesRenderStyle XYSeriesRenderStyle = XYSeries.getXySeriesRenderStyle(); + if (XYSeriesRenderStyle == null) { + XYSeries.setXySeriesRenderStyle(getStyler().getDefaultSeriesRenderStyle()); + } + } + setSeriesStyles(); + g.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON); + g.setColor(styler.getChartBackgroundColor()); + Shape rect = new Rectangle2D.Double(0, 0, getWidth(), getHeight()); + g.fill(rect); + axisPair.paint(g); + plot.paint(g); + chartTitle.paint(g); + legend.paint(g); + g.dispose(); + } + + private void setSeriesStyles() { + SeriesColorMarkerLineStyleCycler seriesColorMarkerLineStyleCycler = + new SeriesColorMarkerLineStyleCycler(getStyler().getSeriesColors(), + getStyler().getSeriesMarkers(), + getStyler().getSeriesLines()); + for (XYSeries series : getSeriesMap().values()) { + SeriesColorMarkerLineStyle seriesColorMarkerLineStyle = seriesColorMarkerLineStyleCycler.getNextSeriesColorMarkerLineStyle(); + if (series.getLineStyle() == null) { + series.setLineStyle(seriesColorMarkerLineStyle.getStroke()); + } + if (series.getLineColor() == null) { + series.setLineColor(seriesColorMarkerLineStyle.getColor()); + } + if (series.getFillColor() == null) { + series.setFillColor(seriesColorMarkerLineStyle.getColor()); + } + if (series.getMarker() == null) { + series.setMarker(seriesColorMarkerLineStyle.getMarker()); + } + if (series.getMarkerColor() == null) { + series.setMarkerColor(seriesColorMarkerLineStyle.getColor()); + } + } + } + + private DataType getDataType(List data) { + if (data == null) { + return DataType.Number; + } + DataType axisType; + Iterator itr = data.iterator(); + Object dataPoint = itr.next(); + if (dataPoint instanceof Number) { + axisType = DataType.Number; + } else if (dataPoint instanceof Instant) { + axisType = DataType.Instant; + } else { + throw new IllegalArgumentException("Series data must be either Number or Instant type"); + } + return axisType; + } + + private static class XYPlot extends AxesChartPlot { + + private XYPlot(Chart chart) { + super(chart); + this.contentPlot = new ContentPlotXY<>(chart); + } + } + + private static class ContentPlotXY extends ContentPlot { + + private final ST xystyler; + + private ContentPlotXY(Chart chart) { + super(chart); + xystyler = chart.getStyler(); + } + + @Override + public void doPaint(Graphics2D g) { + double xTickSpace = xystyler.getPlotContentSize() * getBounds().getWidth(); + double xLeftMargin = ((int) getBounds().getWidth() - xTickSpace) / 2.0; + double yTickSpace = xystyler.getPlotContentSize() * getBounds().getHeight(); + double yTopMargin = ((int) getBounds().getHeight() - yTickSpace) / 2.0; + double xMin = chart.getXAxis().getMin(); + double xMax = chart.getXAxis().getMax(); + Line2D.Double line = new Line2D.Double(); + if (xystyler.isXAxisLogarithmic()) { + xMin = Math.log10(xMin); + xMax = Math.log10(xMax); + } + Map map = chart.getSeriesMap(); + for (S series : map.values()) { + if (!series.isEnabled()) { + continue; + } + Axis yAxis = chart.getYAxis(series.getYAxisGroup()); + double yMin = yAxis.getMin(); + double yMax = yAxis.getMax(); + if (xystyler.isYAxisLogarithmic()) { + yMin = Math.log10(yMin); + yMax = Math.log10(yMax); + } + Collection xData = series.getXData(); + Collection yData = series.getYData(); + double previousX = -Double.MAX_VALUE; + double previousY = -Double.MAX_VALUE; + Iterator xItr = xData.iterator(); + Iterator yItr = yData.iterator(); + Iterator ebItr = null; + Collection errorBars = series.getExtraValues(); + if (errorBars != null) { + ebItr = errorBars.iterator(); + } + Path2D.Double path = null; + while (xItr.hasNext()) { + Double x = null; + if (chart.getXAxis().getDataType() == DataType.Number) { + Number number = (Number) xItr.next(); + x = number != null ? number.doubleValue() : null; + } else if (chart.getXAxis().getDataType() == DataType.Instant) { + Instant instant = (Instant) xItr.next(); + x = instant != null ? (double) instant.toEpochMilli() : null; + } + if (xystyler.isXAxisLogarithmic()) { + x = x != null ? Math.log10(x) : null; + } + Number next = yItr.next(); + if (x == null || next == null) { + closePath(g, path, previousX, yTopMargin); + path = null; + previousX = -Double.MAX_VALUE; + previousY = -Double.MAX_VALUE; + continue; + } + double yOrig = next.doubleValue(); + double y; + if (xystyler.isYAxisLogarithmic()) { + y = Math.log10(yOrig); + } else { + y = yOrig; + } + double xTransform = xLeftMargin + ((x - xMin) / (xMax - xMin) * xTickSpace); + double yTransform = getBounds().getHeight() - (yTopMargin + (y - yMin) / (yMax - yMin) * yTickSpace); + if (Math.abs(xMax - xMin) / 5 == 0.0) { + xTransform = getBounds().getWidth() / 2.0; + } + if (Math.abs(yMax - yMin) / 5 == 0.0) { + yTransform = getBounds().getHeight() / 2.0; + } + double xOffset = getBounds().getX() + xTransform; + double yOffset = getBounds().getY() + yTransform; + boolean isSeriesLineOrArea = (XYSeriesRenderStyle.Line == series.getXySeriesRenderStyle()) || + (XYSeriesRenderStyle.Area == series.getXySeriesRenderStyle()); + boolean isSeriesStepLineOrStepArea = XYSeriesRenderStyle.Step == series.getXySeriesRenderStyle() || + XYSeriesRenderStyle.StepArea == series.getXySeriesRenderStyle(); + if (isSeriesLineOrArea || isSeriesStepLineOrStepArea) { + if (series.getLineStyle() != Theme.Series.NONE_STROKE) { + if (previousX != -Double.MAX_VALUE && previousY != -Double.MAX_VALUE) { + g.setColor(series.getLineColor()); + g.setStroke(series.getLineStyle()); + if (isSeriesLineOrArea) { + line.setLine(previousX, previousY, xOffset, yOffset); + g.draw(line); + } else { + if (previousX != xOffset) { + line.setLine(previousX, previousY, xOffset, previousY); + g.draw(line); + } + if (previousY != yOffset) { + line.setLine(xOffset, previousY, xOffset, yOffset); + g.draw(line); + } + } + } + } + } + if (XYSeriesRenderStyle.Area == series.getXySeriesRenderStyle() || + XYSeriesRenderStyle.StepArea == series.getXySeriesRenderStyle()) { + if (previousX != -Double.MAX_VALUE && previousY != -Double.MAX_VALUE) { + g.setColor(series.getFillColor()); + double yBottomOfArea = getBounds().getY() + getBounds().getHeight() - yTopMargin; + if (path == null) { + path = new Path2D.Double(); + path.moveTo(previousX, yBottomOfArea); + path.lineTo(previousX, previousY); + } + if (XYSeriesRenderStyle.Area == series.getXySeriesRenderStyle()) { + path.lineTo(xOffset, yOffset); + } else { + if (previousX != xOffset) { + path.lineTo(xOffset, previousY); + } + if (previousY != yOffset) { + path.lineTo(xOffset, yOffset); + } + } + } + if (xOffset < previousX) { + throw new RuntimeException("X-Data must be in ascending order for Area Charts"); + } + } + previousX = xOffset; + previousY = yOffset; + if (series.getMarker() != null) { + g.setColor(series.getMarkerColor()); + series.getMarker().paint(g, xOffset, yOffset, xystyler.getMarkerSize()); + } + if (errorBars != null) { + double eb = ebItr.next().doubleValue(); + if (xystyler.isErrorBarsColorSeriesColor()) { + g.setColor(series.getLineColor()); + } else { + g.setColor(xystyler.getErrorBarsColor()); + } + g.setStroke(Theme.Strokes.ERROR_BARS); + double topValue; + if (xystyler.isYAxisLogarithmic()) { + topValue = yOrig + eb; + topValue = Math.log10(topValue); + } else { + topValue = y + eb; + } + double topEBTransform = getBounds().getHeight() - (yTopMargin + (topValue - yMin) / (yMax - yMin) * yTickSpace); + double topEBOffset = getBounds().getY() + topEBTransform; + double bottomValue; + if (xystyler.isYAxisLogarithmic()) { + bottomValue = yOrig - eb; + bottomValue = Math.log10(bottomValue); + } else { + bottomValue = y - eb; + } + double bottomEBTransform = getBounds().getHeight() - (yTopMargin + (bottomValue - yMin) / (yMax - yMin) * yTickSpace); + double bottomEBOffset = getBounds().getY() + bottomEBTransform; + line = new Line2D.Double(xOffset, topEBOffset, xOffset, bottomEBOffset); + g.draw(line); + line = new Line2D.Double(xOffset - 3, bottomEBOffset, xOffset + 3, bottomEBOffset); + g.draw(line); + line = new Line2D.Double(xOffset - 3, topEBOffset, xOffset + 3, topEBOffset); + g.draw(line); + } + } + g.setColor(series.getFillColor()); + closePath(g, path, previousX, yTopMargin); + } + } + } + + private void sanityCheck(String seriesName, List xData, List yData, List errorBars) { + if (seriesMap.containsKey(seriesName)) { + throw new IllegalArgumentException("Series name >" + seriesName + "< has already been used. Use unique names for each series"); + } + if (yData == null) { + throw new IllegalArgumentException("Y-Axis data cannot be null"); + } + if (yData.size() == 0) { + throw new IllegalArgumentException("Y-Axis data cannot be empty"); + } + if (xData != null && xData.size() == 0) { + throw new IllegalArgumentException("X-Axis data cannot be empty"); + } + if (errorBars != null && errorBars.size() != yData.size()) { + throw new IllegalArgumentException("Error bars and Y-Axis sizes are not the same"); + } + } +} diff --git a/chart/src/main/java/org/xbib/graphics/chart/xy/XYChartBuilder.java b/chart/src/main/java/org/xbib/graphics/chart/xy/XYChartBuilder.java new file mode 100644 index 0000000..ab48cfe --- /dev/null +++ b/chart/src/main/java/org/xbib/graphics/chart/xy/XYChartBuilder.java @@ -0,0 +1,37 @@ +package org.xbib.graphics.chart.xy; + +import org.xbib.graphics.chart.ChartBuilder; + +public class XYChartBuilder extends ChartBuilder { + + private String xAxisTitle; + private String yAxisTitle; + + public XYChartBuilder() { + this.xAxisTitle = ""; + this.yAxisTitle = ""; + } + + public XYChartBuilder xAxisTitle(String xAxisTitle) { + this.xAxisTitle = xAxisTitle; + return this; + } + + public XYChartBuilder yAxisTitle(String yAxisTitle) { + this.yAxisTitle = yAxisTitle; + return this; + } + + public String getxAxisTitle() { + return xAxisTitle; + } + + public String getyAxisTitle() { + return yAxisTitle; + } + + @Override + public XYChart build() { + return new XYChart(this); + } +} diff --git a/chart/src/main/java/org/xbib/graphics/chart/xy/XYSeries.java b/chart/src/main/java/org/xbib/graphics/chart/xy/XYSeries.java new file mode 100644 index 0000000..676f317 --- /dev/null +++ b/chart/src/main/java/org/xbib/graphics/chart/xy/XYSeries.java @@ -0,0 +1,38 @@ +package org.xbib.graphics.chart.xy; + +import org.xbib.graphics.chart.axis.DataType; +import org.xbib.graphics.chart.series.AxesChartSeriesNumericalNoErrorBars; +import org.xbib.graphics.chart.legend.LegendRenderType; + +import java.util.List; + +/** + * A Series containing X and Y data to be plotted on a Chart + */ +public class XYSeries extends AxesChartSeriesNumericalNoErrorBars { + + private XYSeriesRenderStyle xySeriesRenderStyle; + + public XYSeries(String name, + List xData, + List yData, + List errorBars, + DataType dataType) { + super(name, xData, yData, errorBars, dataType); + } + + public XYSeriesRenderStyle getXySeriesRenderStyle() { + return xySeriesRenderStyle; + } + + public void setXySeriesRenderStyle(XYSeriesRenderStyle xySeriesRenderStyle) { + this.xySeriesRenderStyle = xySeriesRenderStyle; + } + + @Override + public LegendRenderType getLegendRenderType() { + return xySeriesRenderStyle.getLegendRenderType(); + } + + +} \ No newline at end of file diff --git a/chart/src/main/java/org/xbib/graphics/chart/xy/XYSeriesRenderStyle.java b/chart/src/main/java/org/xbib/graphics/chart/xy/XYSeriesRenderStyle.java new file mode 100644 index 0000000..b65bc3b --- /dev/null +++ b/chart/src/main/java/org/xbib/graphics/chart/xy/XYSeriesRenderStyle.java @@ -0,0 +1,28 @@ +package org.xbib.graphics.chart.xy; + +import org.xbib.graphics.chart.legend.LegendRenderable; +import org.xbib.graphics.chart.legend.LegendRenderType; + +public enum XYSeriesRenderStyle implements LegendRenderable { + + Line(LegendRenderType.Line), + + Area(LegendRenderType.Line), + + Step(LegendRenderType.Line), + + StepArea(LegendRenderType.Line), + + Scatter(LegendRenderType.Scatter); + + private final LegendRenderType legendRenderType; + + XYSeriesRenderStyle(LegendRenderType legendRenderType) { + this.legendRenderType = legendRenderType; + } + + @Override + public LegendRenderType getLegendRenderType() { + return legendRenderType; + } +} diff --git a/chart/src/main/java/org/xbib/graphics/chart/xy/XYStyler.java b/chart/src/main/java/org/xbib/graphics/chart/xy/XYStyler.java new file mode 100644 index 0000000..e109ee3 --- /dev/null +++ b/chart/src/main/java/org/xbib/graphics/chart/xy/XYStyler.java @@ -0,0 +1,49 @@ +package org.xbib.graphics.chart.xy; + +import org.xbib.graphics.chart.style.AxesChartStyler; +import org.xbib.graphics.chart.theme.Theme; + +public class XYStyler extends AxesChartStyler { + + private XYSeriesRenderStyle xySeriesRenderStyle; + + public XYStyler() { + this.setAllStyles(); + super.setAllStyles(); + } + + @Override + protected void setAllStyles() { + xySeriesRenderStyle = XYSeriesRenderStyle.Line; // set default to line + } + + public XYSeriesRenderStyle getDefaultSeriesRenderStyle() { + return xySeriesRenderStyle; + } + + /** + * Sets the default series render style for the chart (line, scatter, area, etc.) You can override the series + * render + * style individually on each Series object. + * + * @param style style + */ + public void setDefaultSeriesRenderStyle(XYSeriesRenderStyle style) { + this.xySeriesRenderStyle = style; + } + + public Theme getTheme() { + return theme; + } + + /** + * Set the theme the styler should use + * + * @param theme theme + */ + protected void setTheme(Theme theme) { + this.theme = theme; + super.setAllStyles(); + } + +} diff --git a/chart/src/test/java/org/xbib/graphics/chart/HistogramTest.java b/chart/src/test/java/org/xbib/graphics/chart/HistogramTest.java new file mode 100755 index 0000000..1aa3eb1 --- /dev/null +++ b/chart/src/test/java/org/xbib/graphics/chart/HistogramTest.java @@ -0,0 +1,28 @@ +package org.xbib.graphics.chart; + +import java.util.Arrays; + +import static org.hamcrest.CoreMatchers.equalTo; +import static org.hamcrest.MatcherAssert.assertThat; +import org.junit.jupiter.api.Test; + +public class HistogramTest { + + @Test + public void test1() { + Histogram histogram = new Histogram(Arrays.asList(1, 2, 3, 4, 5, 6), 2, 0, 4); + assertThat(histogram.getMax(), equalTo(4.0)); + assertThat(histogram.getMin(), equalTo(0.0)); + assertThat(histogram.getNumBins(), equalTo(2)); + assertThat(histogram.getyAxisData().get(0) + histogram.getyAxisData().get(1), equalTo(4.0)); + } + + @Test + public void testNegativeValues() { + Histogram histogram = new Histogram(Arrays.asList(-1, -2, -3, -4, -5, -6), 3); + assertThat(histogram.getMax(), equalTo(-1.0)); + assertThat(histogram.getMin(), equalTo(-6.0)); + assertThat(histogram.getNumBins(), equalTo(3)); + } + +} diff --git a/chart/src/test/java/org/xbib/graphics/chart/MatlabTest.java b/chart/src/test/java/org/xbib/graphics/chart/MatlabTest.java new file mode 100644 index 0000000..c321f9c --- /dev/null +++ b/chart/src/test/java/org/xbib/graphics/chart/MatlabTest.java @@ -0,0 +1,67 @@ +package org.xbib.graphics.chart; + +import org.junit.jupiter.api.Test; +import org.xbib.graphics.chart.io.VectorGraphicsFormat; +import org.xbib.graphics.chart.theme.MatlabTheme; +import org.xbib.graphics.chart.xy.XYChart; +import org.xbib.graphics.chart.xy.XYChartBuilder; + +import java.io.IOException; +import java.nio.file.Files; +import java.nio.file.Paths; +import java.time.Instant; +import java.util.ArrayList; +import java.util.List; + +public class MatlabTest { + + @Test + public void testMatlabInstants() throws IOException { + XYChart chart = new XYChartBuilder().width(800).height(600) + .theme(new MatlabTheme()) + .title("Matlab Theme") + .xAxisTitle("X") + .yAxisTitle("Y") + .build(); + chart.getStyler().setPlotGridLinesVisible(false); + chart.getStyler().setXAxisTickMarkSpacingHint(100); + chart.getStyler().setDatePattern("HH:mm:ss"); + List xData = new ArrayList<>(); + List y1Data = new ArrayList<>(); + List y2Data = new ArrayList<>(); + + xData.add(Instant.parse("2012-08-01T00:00:00Z")); + y1Data.add(120d); + y2Data.add(15d); + + xData.add(Instant.parse("2012-08-01T01:00:00Z")); + y1Data.add(165d); + y2Data.add(15d); + + xData.add(Instant.parse("2012-08-01T02:00:00Z")); + y1Data.add(210d); + y2Data.add(20d); + + xData.add(Instant.parse("2012-08-01T03:00:00Z")); + y1Data.add(400d); + y2Data.add(30d); + + xData.add(Instant.parse("2012-08-01T04:00:00Z")); + y1Data.add(800d); + y2Data.add(100d); + + xData.add(Instant.parse("2012-08-01T05:00:00Z")); + y1Data.add(2000d); + y2Data.add(120d); + + xData.add(Instant.parse("2012-08-01T06:00:00Z")); + y1Data.add(3000d); + y2Data.add(150d); + + chart.addSeries("downloads", xData, y1Data); + chart.addSeries("price", xData, y2Data); + + chart.write(Files.newOutputStream(Paths.get("build/matlab.svg")), + VectorGraphicsFormat.SVG ); + } +} diff --git a/chart/src/test/java/org/xbib/graphics/chart/demo/AreaChartTest.java b/chart/src/test/java/org/xbib/graphics/chart/demo/AreaChartTest.java new file mode 100644 index 0000000..6c4a32a --- /dev/null +++ b/chart/src/test/java/org/xbib/graphics/chart/demo/AreaChartTest.java @@ -0,0 +1,146 @@ +package org.xbib.graphics.chart.demo; + +import org.junit.jupiter.api.Test; +import org.xbib.graphics.chart.io.VectorGraphicsFormat; +import org.xbib.graphics.chart.axis.TextAlignment; +import org.xbib.graphics.chart.theme.Theme; +import org.xbib.graphics.chart.legend.LegendPosition; +import org.xbib.graphics.chart.xy.XYChart; +import org.xbib.graphics.chart.xy.XYChartBuilder; +import org.xbib.graphics.chart.xy.XYSeries; +import org.xbib.graphics.chart.xy.XYSeriesRenderStyle; + +import java.io.IOException; +import java.nio.file.Files; +import java.nio.file.Paths; +import java.util.ArrayList; +import java.util.List; + +public class AreaChartTest { + + @Test + public void testAreaChart1() throws IOException { + XYChart chart = + new XYChartBuilder() + .width(800) + .height(600) + .title(getClass().getSimpleName()) + .xAxisTitle("X") + .yAxisTitle("Y") + .build(); + + chart.getStyler().setLegendPosition(LegendPosition.InsideNE); + chart.getStyler().setAxisTitlesVisible(false); + chart.getStyler().setDefaultSeriesRenderStyle(XYSeriesRenderStyle.Area); + chart.getStyler().setSeriesColors(Theme.PrinterFriendly.SERIES_COLORS); + + chart.addSeries("a", new double[] {0, 3, 5, 7, 9}, new double[] {-3, 5, 9, 6, 5}); + chart.addSeries("b", new double[] {0, 2, 4, 6, 9}, new double[] {-1, 6, 4, 0, 4}); + chart.addSeries("c", new double[] {0, 1, 3, 8, 9}, new double[] {-2, -1, 1, 0, 1}); + + chart.write(Files.newOutputStream(Paths.get("build/areachart1.svg")), + VectorGraphicsFormat.SVG); + } + + @Test + public void testAreaChart2() throws IOException { + XYChart chart = new XYChartBuilder() + .width(800) + .height(600) + .title(getClass().getSimpleName()) + .xAxisTitle("X") + .yAxisTitle("Y") + .build(); + + chart.getStyler().setDefaultSeriesRenderStyle(XYSeriesRenderStyle.Area); + chart.getStyler().setLegendPosition(LegendPosition.InsideNW); + List xData = new ArrayList<>(); + List yData = new ArrayList<>(); + for (int i = 0; i < 5; i++) { + xData.add(i); + yData.add(i * i); + } + xData.add(5); + yData.add(null); + for (int i = 6; i < 10; i++) { + xData.add(i); + yData.add(i * i); + } + xData.add(10); + yData.add(null); + xData.add(11); + yData.add(100); + xData.add(12); + yData.add(90); + chart.addSeries("a", xData, yData); + + chart.write( Files.newOutputStream(Paths.get("build/areachart2.svg")), + VectorGraphicsFormat.SVG); + + } + + @Test + public void testAreaChart3() throws IOException { + XYChart chart = new XYChartBuilder().width(800).height(600).title(getClass().getSimpleName()).xAxisTitle("Age").yAxisTitle("Amount").build(); + + chart.getStyler().setLegendPosition(LegendPosition.InsideNW); + chart.getStyler().setDefaultSeriesRenderStyle(XYSeriesRenderStyle.Line); + chart.getStyler().setYAxisLabelAlignment(TextAlignment.Right); + chart.getStyler().setYAxisDecimalPattern("$ #,###.##"); + chart.getStyler().setPlotMargin(0); + chart.getStyler().setPlotContentSize(.95); + + double[] xAges = new double[] { 60, 61, 62, 63, 64, 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79, 80, 81, 82, 83, 84, 85, 86, 87, + 88, 89, 90, 91, 92, 93, 94, 95, 96, 97, 98, 99, 100 }; + + double[] yLiability = new double[] { 672234, 691729, 711789, 732431, 753671, 775528, 798018, 821160, 844974, 869478, 907735, 887139, 865486, + 843023, 819621, 795398, 770426, 744749, 719011, 693176, 667342, 641609, 616078, 590846, 565385, 540002, 514620, 489380, 465149, 441817, + 419513, 398465, 377991, 358784, 340920, 323724, 308114, 293097, 279356, 267008, 254873 }; + + double[] yPercentile75th = new double[] { 800000, 878736, 945583, 1004209, 1083964, 1156332, 1248041, 1340801, 1440138, 1550005, 1647728, + 1705046, 1705032, 1710672, 1700847, 1683418, 1686522, 1674901, 1680456, 1679164, 1668514, 1672860, 1673988, 1646597, 1641842, 1653758, + 1636317, 1620725, 1589985, 1586451, 1559507, 1544234, 1529700, 1507496, 1474907, 1422169, 1415079, 1346929, 1311689, 1256114, 1221034 }; + + double[] yPercentile50th = new double[] { 800000, 835286, 873456, 927048, 969305, 1030749, 1101102, 1171396, 1246486, 1329076, 1424666, 1424173, + 1421853, 1397093, 1381882, 1364562, 1360050, 1336885, 1340431, 1312217, 1288274, 1271615, 1262682, 1237287, 1211335, 1191953, 1159689, + 1117412, 1078875, 1021020, 974933, 910189, 869154, 798476, 744934, 674501, 609237, 524516, 442234, 343960, 257025 }; + + double[] yPercentile25th = new double[] { 800000, 791439, 809744, 837020, 871166, 914836, 958257, 1002955, 1054094, 1118934, 1194071, 1185041, + 1175401, 1156578, 1132121, 1094879, 1066202, 1054411, 1028619, 987730, 944977, 914929, 880687, 809330, 783318, 739751, 696201, 638242, + 565197, 496959, 421280, 358113, 276518, 195571, 109514, 13876, 29, 0, 0, 0, 0 }; + + XYSeries seriesLiability = chart.addSeries("Liability", xAges, yLiability); + seriesLiability.setXySeriesRenderStyle(XYSeriesRenderStyle.Area); + seriesLiability.setMarker(Theme.Series.NONE_MARKER); + + chart.addSeries("75th Percentile", xAges, yPercentile75th); + chart.addSeries("50th Percentile", xAges, yPercentile50th); + chart.addSeries("25th Percentile", xAges, yPercentile25th); + + chart.write(Files.newOutputStream(Paths.get("build/areachart3.svg")), + VectorGraphicsFormat.SVG); + } + + @Test + public void testAreaChart4() throws IOException { + XYChart chart = + new XYChartBuilder() + .width(800) + .height(600) + .title(getClass().getSimpleName()) + .xAxisTitle("X") + .yAxisTitle("Y") + .build(); + + chart.getStyler().setLegendPosition(LegendPosition.InsideNE); + chart.getStyler().setAxisTitlesVisible(false); + chart.getStyler().setDefaultSeriesRenderStyle(XYSeriesRenderStyle.StepArea); + + chart.addSeries("a", new double[] {0, 3, 5, 7, 9}, new double[] {-3, 5, 9, 6, 5}); + chart.addSeries("b", new double[] {0, 2, 4, 6, 9}, new double[] {-1, 6, 4, 0, 4}); + chart.addSeries("c", new double[] {0, 1, 3, 8, 9}, new double[] {-2, -1, 1, 0, 1}); + + chart.write(Files.newOutputStream(Paths.get("build/areachart4.svg")), + VectorGraphicsFormat.SVG); + } +} diff --git a/chart/src/test/java/org/xbib/graphics/chart/demo/BarChartTest.java b/chart/src/test/java/org/xbib/graphics/chart/demo/BarChartTest.java new file mode 100644 index 0000000..d6aa88b --- /dev/null +++ b/chart/src/test/java/org/xbib/graphics/chart/demo/BarChartTest.java @@ -0,0 +1,134 @@ +package org.xbib.graphics.chart.demo; + +import org.junit.jupiter.api.Test; +import org.xbib.graphics.chart.Histogram; +import org.xbib.graphics.chart.theme.GGPlot2Theme; +import org.xbib.graphics.chart.io.VectorGraphicsFormat; +import org.xbib.graphics.chart.category.CategoryChart; +import org.xbib.graphics.chart.category.CategoryChartBuilder; +import org.xbib.graphics.chart.category.CategorySeries; +import org.xbib.graphics.chart.category.CategorySeriesRenderStyle; +import org.xbib.graphics.chart.legend.LegendPosition; + +import java.awt.Color; +import java.io.IOException; +import java.nio.file.Files; +import java.nio.file.Paths; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; +import java.util.Random; + +public class BarChartTest { + + @Test + public void testBarChart1() throws IOException { + CategoryChart chart = + new CategoryChartBuilder() + .width(800) + .height(600) + .title("Score Histogram") + .xAxisTitle("Score") + .yAxisTitle("Number") + .build(); + + chart.getStyler().setLegendPosition(LegendPosition.InsideNW); + chart.getStyler().setPlotGridLinesVisible(false); + + chart.addSeries("test 1", Arrays.asList(0, 1, 2, 3, 4), Arrays.asList(4, 5, 9, 6, 5)); + + chart.write(Files.newOutputStream(Paths.get("build/barchart1.svg")), + VectorGraphicsFormat.SVG); + } + + @Test + public void testBarChart6() throws IOException { + CategoryChart chart = new CategoryChartBuilder().width(800).height(600).title("Score Histogram").xAxisTitle("Mean").yAxisTitle("Count").build(); + + chart.getStyler().setLegendPosition(LegendPosition.InsideNW); + chart.getStyler().setAvailableSpaceFill(.96); + chart.getStyler().setOverlapped(true); + + Histogram histogram1 = new Histogram(getGaussianData(10000), 20, -20, 20); + Histogram histogram2 = new Histogram(getGaussianData(5000), 20, -20, 20); + chart.addSeries("histogram 1", histogram1.getxAxisData(), histogram1.getyAxisData()); + chart.addSeries("histogram 2", histogram2.getxAxisData(), histogram2.getyAxisData()); + + chart.write(Files.newOutputStream(Paths.get("build/barchart6.svg")), + VectorGraphicsFormat.SVG); + } + + private List getGaussianData(int count) { + List data = new ArrayList<>(count); + Random r = new Random(); + for (int i = 0; i < count; i++) { + data.add(r.nextGaussian() * 10); + } + return data; + } + + @Test + public void testGGPlot1() throws IOException { + CategoryChart chart = + new CategoryChartBuilder() + .width(800) + .height(600) + .title("Temperature vs. Color") + .xAxisTitle("Color") + .yAxisTitle("Temperature") + .theme(new GGPlot2Theme()) + .build(); + + chart.getStyler().setPlotGridVerticalLinesVisible(false); + + chart.addSeries("fish", Arrays.asList("Blue", "Red", "Green", "Yellow", "Orange"), + Arrays.asList(-40, 30, 20, 60, 60)); + chart.addSeries("worms", Arrays.asList("Blue", "Red", "Green", "Yellow", "Orange"), + Arrays.asList(50, 10, -20, 40, 60)); + chart.addSeries("birds", Arrays.asList("Blue", "Red", "Green", "Yellow", "Orange"), + Arrays.asList(13, 22, -23, -34, 37)); + chart.addSeries("ants", Arrays.asList("Blue", "Red", "Green", "Yellow", "Orange"), + Arrays.asList(50, 57, -14, -20, 31)); + chart.addSeries("slugs", Arrays.asList("Blue", "Red", "Green", "Yellow", "Orange"), + Arrays.asList(-2, 29, 49, -16, -43)); + + chart.write(Files.newOutputStream(Paths.get("build/ggplot1.svg")), + VectorGraphicsFormat.SVG); + } + + + @Test + public void testGGPlot2() throws IOException { + CategoryChart chart = + new CategoryChartBuilder() + .width(800) + .height(600) + .title("Temperature vs. Color") + .xAxisTitle("Color") + .yAxisTitle("Temperature") + .theme(new GGPlot2Theme()) + .build(); + + chart.getStyler().setPlotGridVerticalLinesVisible(false); + List categorySeries = new ArrayList<>(); + + categorySeries.add(chart.addSeries("fish", Arrays.asList("Blue", "Red", "Green", "Yellow", "Orange"), + Arrays.asList(-40, 30, 20, 60, 60))); + categorySeries.add(chart.addSeries("worms", Arrays.asList("Blue", "Red", "Green", "Yellow", "Orange"), + Arrays.asList(50, 10, -20, 40, 60))); + categorySeries.add(chart.addSeries("birds", Arrays.asList("Blue", "Red", "Green", "Yellow", "Orange"), + Arrays.asList(13, 22, -23, -34, 37))); + categorySeries.add(chart.addSeries("ants", Arrays.asList("Blue", "Red", "Green", "Yellow", "Orange"), + Arrays.asList(50, 57, -14, -20, 31))); + categorySeries.add(chart.addSeries("slugs", Arrays.asList("Blue", "Red", "Green", "Yellow", "Orange"), + Arrays.asList(-2, 29, 49, -16, -43))); + + for (CategorySeries series : categorySeries) { + series.setCategorySeriesRenderStyle(CategorySeriesRenderStyle.SteppedBar); + series.setFillColor(new Color(0, 0, 0, 0)); + } + chart.write(Files.newOutputStream(Paths.get("build/ggplot2.svg")), + VectorGraphicsFormat.SVG); + } + +} diff --git a/chart/src/test/java/org/xbib/graphics/chart/demo/BubbleChartTest.java b/chart/src/test/java/org/xbib/graphics/chart/demo/BubbleChartTest.java new file mode 100644 index 0000000..1b5e583 --- /dev/null +++ b/chart/src/test/java/org/xbib/graphics/chart/demo/BubbleChartTest.java @@ -0,0 +1,34 @@ +package org.xbib.graphics.chart.demo; + +import org.junit.jupiter.api.Test; +import org.xbib.graphics.chart.io.VectorGraphicsFormat; +import org.xbib.graphics.chart.bubble.BubbleChart; +import org.xbib.graphics.chart.bubble.BubbleChartBuilder; + +import java.io.IOException; +import java.nio.file.Files; +import java.nio.file.Paths; +import java.util.Arrays; +import java.util.List; + +public class BubbleChartTest { + + @Test + public void testBubble1() throws IOException { + BubbleChart chart = new BubbleChartBuilder().width(800).height(600).title("BubbleChart01").xAxisTitle("X").yAxisTitle("Y").build(); + + List xData = Arrays.asList(1.5, 2.6, 3.3, 4.9, 5.5, 6.3, 1.0, 2.0, 3.0, 4.0, 5.0, 6.0); + List yData = Arrays.asList(10.0, 4.0, 7.0, 7.7, 7.0, 5.5, 10.0, 4.0, 7.0, 1.0, 7.0, 9.0); + List bubbleData = Arrays.asList(17.0, 40.0, 50.0, 51.0, 26.0, 20.0, 66.0, 35.0, 80.0, 27.0, 29.0, 44.0); + + List xData2 = Arrays.asList(1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 1.5, 2.6, 3.3, 4.9, 5.5, 6.3); + List yData2 = Arrays.asList(1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 10.0, 8.5, 4.0, 1.0, 4.7, 9.0); + List bubbleData2 = Arrays.asList(37.0, 35.0, 80.0, 27.0, 29.0, 44.0, 57.0, 40.0, 50.0, 33.0, 26.0, 20.0); + + chart.addSeries("A", xData, yData, bubbleData); + chart.addSeries("B", xData2, yData2, bubbleData2); + + chart.write(Files.newOutputStream(Paths.get("build/bubblechart1.svg")), + VectorGraphicsFormat.SVG); + } +} diff --git a/chart/src/test/java/org/xbib/graphics/chart/demo/PieChartTest.java b/chart/src/test/java/org/xbib/graphics/chart/demo/PieChartTest.java new file mode 100644 index 0000000..4d12750 --- /dev/null +++ b/chart/src/test/java/org/xbib/graphics/chart/demo/PieChartTest.java @@ -0,0 +1,41 @@ +package org.xbib.graphics.chart.demo; + +import org.junit.jupiter.api.Test; +import org.xbib.graphics.chart.io.VectorGraphicsFormat; +import org.xbib.graphics.chart.pie.PieChart; +import org.xbib.graphics.chart.pie.PieChartBuilder; + +import java.awt.Color; +import java.io.IOException; +import java.nio.file.Files; +import java.nio.file.Paths; +import java.util.Arrays; +import java.util.List; + +public class PieChartTest { + + @Test + public void testPieChart2() throws IOException { + PieChart chart = new PieChartBuilder() + .width(800) + .height(600) + .title(getClass().getSimpleName()) + .build(); + List sliceColors = Arrays.asList(new Color(224, 68, 14), + new Color(230, 105, 62), + new Color(236, 143, 110), + new Color(243, 180, 159), + new Color(246, 199, 182)); + chart.getStyler().setSeriesColors(sliceColors); + + chart.addSeries("Gold", 24); + chart.addSeries("Silver", 21); + chart.addSeries("Platinum", 39); + chart.addSeries("Copper", 17); + chart.addSeries("Zinc", 40); + + chart.write(Files.newOutputStream(Paths.get("build/piechart2.svg")), + VectorGraphicsFormat.SVG); + + } +} diff --git a/chart/src/test/java/org/xbib/graphics/chart/demo/QuickChartTest.java b/chart/src/test/java/org/xbib/graphics/chart/demo/QuickChartTest.java new file mode 100644 index 0000000..e7eb2e0 --- /dev/null +++ b/chart/src/test/java/org/xbib/graphics/chart/demo/QuickChartTest.java @@ -0,0 +1,23 @@ +package org.xbib.graphics.chart.demo; + +import org.junit.jupiter.api.Test; +import org.xbib.graphics.chart.QuickChart; +import org.xbib.graphics.chart.io.VectorGraphicsFormat; +import org.xbib.graphics.chart.xy.XYChart; + +import java.io.IOException; +import java.nio.file.Files; +import java.nio.file.Paths; + +public class QuickChartTest { + + @Test + public void testQuick1() throws IOException { + double[] xData = new double[] { 0.0, 1.0, 2.0 }; + double[] yData = new double[] { 2.0, 1.0, 0.0 }; + XYChart chart = QuickChart.getChart("Sample Chart", + "X", "Y", "y(x)", xData, yData); + chart.write(Files.newOutputStream(Paths.get("build/quickchart1.pdf")), + VectorGraphicsFormat.PDF); + } +} diff --git a/chart/src/test/java/org/xbib/graphics/chart/demo/ScatterChartTest.java b/chart/src/test/java/org/xbib/graphics/chart/demo/ScatterChartTest.java new file mode 100644 index 0000000..435270a --- /dev/null +++ b/chart/src/test/java/org/xbib/graphics/chart/demo/ScatterChartTest.java @@ -0,0 +1,107 @@ +package org.xbib.graphics.chart.demo; + +import org.junit.jupiter.api.Test; +import org.xbib.graphics.chart.theme.Theme; +import org.xbib.graphics.chart.io.VectorGraphicsFormat; +import org.xbib.graphics.chart.axis.YAxisPosition; +import org.xbib.graphics.chart.legend.LegendPosition; +import org.xbib.graphics.chart.xy.XYChart; +import org.xbib.graphics.chart.xy.XYChartBuilder; +import org.xbib.graphics.chart.xy.XYSeries; +import org.xbib.graphics.chart.xy.XYSeriesRenderStyle; + +import java.io.IOException; +import java.nio.file.Files; +import java.nio.file.Paths; +import java.util.ArrayList; +import java.util.LinkedList; +import java.util.List; +import java.util.Random; + +public class ScatterChartTest { + + @Test + public void testScatterChart0() throws IOException { + XYChart chart = new XYChartBuilder().width(600).height(500).title("Gaussian Blobs").xAxisTitle("X").yAxisTitle("Y").build(); + chart.getStyler().setDefaultSeriesRenderStyle(XYSeriesRenderStyle.Scatter); + chart.getStyler().setChartTitleVisible(false); + chart.getStyler().setLegendPosition(LegendPosition.InsideSW); + chart.getStyler().setMarkerSize(16); + chart.addSeries("Gaussian Blob 1", getGaussian(1000, 1, 10), getGaussian(1000, 1, 10)); + XYSeries series = chart.addSeries("Gaussian Blob 2", getGaussian(1000, 1, 10), getGaussian(1000, 0, 5)); + series.setMarker(Theme.Series.DIAMOND_MARKER); + chart.write(Files.newOutputStream(Paths.get("build/scatterchart0.svg")), + VectorGraphicsFormat.SVG); + } + + private static final Random random = new Random(); + + private static List getGaussian(int number, double mean, double std) { + List seriesData = new LinkedList<>(); + for (int i = 0; i < number; i++) { + seriesData.add(mean + std * random.nextGaussian()); + } + return seriesData; + } + + @Test + public void testScatterChart1() throws IOException { + XYChart chart = new XYChartBuilder().width(800).height(600).build(); + chart.getStyler().setDefaultSeriesRenderStyle(XYSeriesRenderStyle.Scatter); + chart.getStyler().setChartTitleVisible(false); + chart.getStyler().setLegendVisible(false); + chart.getStyler().setMarkerSize(16); + chart.getStyler().setYAxisGroupPosition(0, YAxisPosition.Right); + List xData = new LinkedList<>(); + List yData = new LinkedList<>(); + Random random = new Random(); + int size = 1000; + for (int i = 0; i < size; i++) { + xData.add(random.nextGaussian() / 1000); + yData.add(-1000000 + random.nextGaussian()); + } + XYSeries series = chart.addSeries("Gaussian Blob", xData, yData); + series.setMarker(Theme.Series.CROSS_MARKER); + + chart.write(Files.newOutputStream(Paths.get("build/scatterchart1.svg")), + VectorGraphicsFormat.SVG); + } + + @Test + public void testScatterChart2() throws IOException { + XYChart chart = new XYChartBuilder().width(800).height(600).title("Logarithmic Data").build(); + chart.getStyler().setDefaultSeriesRenderStyle(XYSeriesRenderStyle.Scatter); + chart.getStyler().setXAxisLogarithmic(true); + chart.getStyler().setLegendPosition(LegendPosition.InsideN); + List xData = new ArrayList<>(); + List yData = new ArrayList<>(); + Random random = new Random(); + int size = 400; + for (int i = 0; i < size; i++) { + double nextRandom = random.nextDouble(); + xData.add(Math.pow(10, nextRandom * 10)); + yData.add(1000000000.0 + nextRandom); + } + chart.addSeries("logarithmic data", xData, yData); + + chart.write(Files.newOutputStream(Paths.get("build/scatterchart2.svg")), + VectorGraphicsFormat.SVG); + } + + @Test + public void testScatterChart3() throws IOException { + XYChart chart = + new XYChartBuilder() + .width(800) + .height(600) + .title("Single Point") + .xAxisTitle("X") + .yAxisTitle("Y") + .build(); + chart.getStyler().setDefaultSeriesRenderStyle(XYSeriesRenderStyle.Scatter); + chart.addSeries("single point (1,1)", new double[] {1}, new double[] {1}); + + chart.write(Files.newOutputStream(Paths.get("build/scatterchart3.svg")), + VectorGraphicsFormat.SVG); + } +} \ No newline at end of file diff --git a/chart/src/test/java/org/xbib/graphics/chart/demo/StickChartTest.java b/chart/src/test/java/org/xbib/graphics/chart/demo/StickChartTest.java new file mode 100644 index 0000000..1db9b43 --- /dev/null +++ b/chart/src/test/java/org/xbib/graphics/chart/demo/StickChartTest.java @@ -0,0 +1,34 @@ +package org.xbib.graphics.chart.demo; + +import org.junit.jupiter.api.Test; +import org.xbib.graphics.chart.io.VectorGraphicsFormat; +import org.xbib.graphics.chart.category.CategoryChart; +import org.xbib.graphics.chart.category.CategoryChartBuilder; +import org.xbib.graphics.chart.category.CategorySeriesRenderStyle; +import org.xbib.graphics.chart.legend.LegendPosition; + +import java.io.IOException; +import java.nio.file.Files; +import java.nio.file.Paths; +import java.util.ArrayList; +import java.util.List; + +public class StickChartTest { + + @Test + public void testStick1() throws IOException { + CategoryChart chart = new CategoryChartBuilder().width(800).height(600).title("Stick").build(); + chart.getStyler().setChartTitleVisible(true); + chart.getStyler().setLegendPosition(LegendPosition.InsideNW); + chart.getStyler().setDefaultSeriesRenderStyle(CategorySeriesRenderStyle.Stick); + List xData = new ArrayList<>(); + List yData = new ArrayList<>(); + for (int i = -3; i <= 24; i++) { + xData.add(i); + yData.add(i); + } + chart.addSeries("data", xData, yData); + chart.write(Files.newOutputStream(Paths.get("build/stickchart1.svg")), + VectorGraphicsFormat.SVG); + } +} diff --git a/chart/src/test/java/org/xbib/graphics/chart/io/vector/GraphicsStateTest.java b/chart/src/test/java/org/xbib/graphics/chart/io/vector/GraphicsStateTest.java new file mode 100644 index 0000000..4ac6ae8 --- /dev/null +++ b/chart/src/test/java/org/xbib/graphics/chart/io/vector/GraphicsStateTest.java @@ -0,0 +1,63 @@ +package org.xbib.graphics.chart.io.vector; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertNotSame; +import org.junit.jupiter.api.Test; +import java.awt.Color; +import java.awt.Graphics2D; +import java.awt.geom.AffineTransform; +import java.awt.geom.Rectangle2D; +import java.awt.image.BufferedImage; + +public class GraphicsStateTest { + + @Test + public void testInitialStateIsEqualToGraphics2D() { + BufferedImage image = new BufferedImage(1, 1, BufferedImage.TYPE_INT_ARGB); + Graphics2D g2d = (Graphics2D) image.getGraphics(); + GraphicsState state = new GraphicsState(); + + assertEquals(state.getBackground(), g2d.getBackground()); + assertEquals(state.getColor(), g2d.getColor()); + assertEquals(state.getClip(), g2d.getClip()); + assertEquals(state.getComposite(), g2d.getComposite()); + assertEquals(state.getFont(), g2d.getFont()); + assertEquals(state.getPaint(), g2d.getPaint()); + assertEquals(state.getStroke(), g2d.getStroke()); + assertEquals(state.getTransform(), g2d.getTransform()); + } + + @Test + public void testEquals() { + GraphicsState state1 = new GraphicsState(); + state1.setBackground(Color.WHITE); + state1.setColor(Color.BLACK); + state1.setClip(new Rectangle2D.Double(0, 0, 10, 10)); + + GraphicsState state2 = new GraphicsState(); + state2.setBackground(Color.WHITE); + state2.setColor(Color.BLACK); + state2.setClip(new Rectangle2D.Double(0, 0, 10, 10)); + + assertEquals(state1, state2); + + state2.setTransform(AffineTransform.getTranslateInstance(5, 5)); + + assertFalse(state1.equals(state2)); + } + + @Test + public void testClone() throws CloneNotSupportedException { + GraphicsState state = new GraphicsState(); + state.setBackground(Color.BLUE); + state.setColor(Color.GREEN); + state.setClip(new Rectangle2D.Double(2, 3, 4, 2)); + + GraphicsState clone = (GraphicsState) state.clone(); + + assertNotSame(state, clone); + assertEquals(state, clone); + } +} + diff --git a/chart/src/test/java/org/xbib/graphics/chart/io/vector/TestUtils.java b/chart/src/test/java/org/xbib/graphics/chart/io/vector/TestUtils.java new file mode 100644 index 0000000..f8208f0 --- /dev/null +++ b/chart/src/test/java/org/xbib/graphics/chart/io/vector/TestUtils.java @@ -0,0 +1,267 @@ +package org.xbib.graphics.chart.io.vector; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertTrue; +import java.util.Collections; +import java.util.IdentityHashMap; +import java.util.Iterator; +import java.util.LinkedList; +import java.util.List; +import java.util.Map; +import java.util.TreeMap; +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +public abstract class TestUtils { + + protected TestUtils() { + throw new UnsupportedOperationException(); + } + + public static void assertTemplateEquals(Template expected, Template actual) { + Iterator itExpected = expected.iterator(); + Iterator itActual = actual.iterator(); + while (itExpected.hasNext() && itActual.hasNext()) { + Object lineExpected = itExpected.next(); + Object lineActual = itActual.next(); + + if (lineExpected == null) { + continue; + } + assertTrue(lineActual instanceof String, + String.format("Line is of type %s, expected String.", lineActual.getClass())); + + if (lineExpected instanceof String) { + assertEquals(lineExpected, lineActual); + } else if (lineExpected instanceof Pattern) { + Pattern expectedPattern = (Pattern) lineExpected; + Matcher matcher = expectedPattern.matcher((String) lineActual); + assertTrue(matcher.matches(), + String.format("Line didn't match pattern.\nExpected: \"%s\"\nActual: \"%s\"", matcher.pattern(), lineActual)); + } + } + assertEquals(expected.size(), actual.size(), "Wrong number of lines in template."); + } + + private static List parseXML(String xmlString) { + XMLFragment frag; + List fragments = new LinkedList(); + int startPos = 0; + while ((frag = XMLFragment.parse(xmlString, startPos)) != null) { + fragments.add(frag); + startPos = frag.matchEnd; + } + return fragments; + } + + public static void assertXMLEquals(String expected, String actual) { + List expectedFrags = parseXML(expected); + List actualFrags = parseXML(actual); + + Iterator itExpected = expectedFrags.iterator(); + Iterator itActual = actualFrags.iterator(); + while (itExpected.hasNext() && itActual.hasNext()) { + XMLFragment expectedFrag = itExpected.next(); + XMLFragment actualFrag = itActual.next(); + assertEquals(expectedFrag, actualFrag); + } + + assertEquals(expectedFrags.size(), actualFrags.size()); + } + + @SuppressWarnings("serial") + public static class Template extends LinkedList { + public Template(Object[] lines) { + Collections.addAll(this, lines); + } + + public Template(Template[] templates) { + for (Template template : templates) { + addAll(template); + } + } + } + + public static class XMLFragment { + + private static final Pattern CDATA = Pattern.compile("\\s*"); + + private static final Pattern COMMENT = Pattern.compile("\\s*"); + + private static final Pattern TAG_BEGIN = Pattern.compile("\\s*<(/|\\?|!)?\\s*([^\\s>/\\?]+)"); + + private static final Pattern TAG_END = Pattern.compile("\\s*(/|\\?)?>"); + + private static final Pattern TAG_ATTRIBUTE = Pattern.compile("\\s*([^\\s>=]+)=(\"[^\"]*\"|'[^']*')"); + + private static final Pattern DOCTYPE_PART = Pattern.compile("\\s*(\"[^\"]*\"|'[^']*'|[^\\s>]+)"); + + public final String name; + + public final FragmentType type; + + public final Map attributes; + + public final int matchStart; + + public final int matchEnd; + + public XMLFragment(String name, FragmentType type, Map attributes, + int matchStart, int matchEnd) { + this.name = name; + this.type = type; + this.attributes = Collections.unmodifiableMap( + new TreeMap(attributes)); + this.matchStart = matchStart; + this.matchEnd = matchEnd; + } + + public static XMLFragment parse(String xmlString, int matchStart) { + Map attrs = new IdentityHashMap(); + + Matcher cdataMatch = CDATA.matcher(xmlString); + cdataMatch.region(matchStart, xmlString.length()); + if (cdataMatch.lookingAt()) { + attrs.put("value", cdataMatch.group(1)); + return new XMLFragment("", FragmentType.CDATA, attrs, matchStart, cdataMatch.end()); + } + + Matcher commentMatch = COMMENT.matcher(xmlString); + commentMatch.region(matchStart, xmlString.length()); + if (commentMatch.lookingAt()) { + attrs.put("value", commentMatch.group(1).trim()); + return new XMLFragment("", FragmentType.COMMENT, attrs, matchStart, commentMatch.end()); + } + + Matcher beginMatch = TAG_BEGIN.matcher(xmlString); + beginMatch.region(matchStart, xmlString.length()); + if (!beginMatch.lookingAt()) { + return null; + } + int matchEndPrev = beginMatch.end(); + + String modifiers = beginMatch.group(1); + String name = beginMatch.group(2); + boolean endTag = "/".equals(modifiers); + boolean declarationStart = "?".equals(modifiers); + boolean doctype = "!".equals(modifiers) && "DOCTYPE".equals(name); + + if (doctype) { + int partNo = 0; + while (true) { + Matcher attrMatch = DOCTYPE_PART.matcher(xmlString); + attrMatch.region(matchEndPrev, xmlString.length()); + if (!attrMatch.lookingAt()) { + break; + } + matchEndPrev = attrMatch.end(); + + String partValue = attrMatch.group(1); + if (partValue.startsWith("\"") || partValue.startsWith("'")) { + partValue = partValue.substring(1, partValue.length() - 1); + } + + String partId = String.format("doctype %02d", partNo++); + attrs.put(partId, partValue); + } + } else { + while (true) { + Matcher attrMatch = TAG_ATTRIBUTE.matcher(xmlString); + attrMatch.region(matchEndPrev, xmlString.length()); + if (!attrMatch.lookingAt()) { + break; + } + matchEndPrev = attrMatch.end(); + + String attrName = attrMatch.group(1); + String attrValue = attrMatch.group(2); + attrValue = attrValue.substring(1, attrValue.length() - 1); + attrs.put(attrName, attrValue); + } + } + + Matcher endMatch = TAG_END.matcher(xmlString); + endMatch.region(matchEndPrev, xmlString.length()); + if (!endMatch.lookingAt()) { + throw new AssertionError(String.format("No tag end found: %s", xmlString.substring(0, matchEndPrev))); + } + matchEndPrev = endMatch.end(); + + modifiers = endMatch.group(1); + boolean emptyElement = "/".equals(modifiers); + boolean declarationEnd = "?".equals(modifiers); + + FragmentType type = FragmentType.START_TAG; + if (endTag) { + type = FragmentType.END_TAG; + } else if (emptyElement) { + type = FragmentType.EMPTY_ELEMENT; + } else if (declarationStart && declarationEnd) { + type = FragmentType.DECLARATION; + } else if (doctype) { + type = FragmentType.DOCTYPE; + } + + return new XMLFragment(name, type, attrs, matchStart, matchEndPrev); + } + + @Override + public boolean equals(Object o) { + if (!(o instanceof XMLFragment)) { + return false; + } + XMLFragment frag = (XMLFragment) o; + if (!type.equals(frag.type) || !name.equals(frag.name)) { + return false; + } + Iterator> itThis = attributes.entrySet().iterator(); + Iterator> itFrag = frag.attributes.entrySet().iterator(); + while (itThis.hasNext() && itFrag.hasNext()) { + Map.Entry attrThis = itThis.next(); + Map.Entry attrFrag = itFrag.next(); + if (!attrThis.getKey().equals(attrFrag.getKey()) || + !attrThis.getValue().equals(attrFrag.getValue())) { + return false; + } + } + return true; + } + + @Override + public int hashCode() { + return type.hashCode() ^ attributes.hashCode(); + } + + @Override + public String toString() { + StringBuilder s = new StringBuilder("<"); + if (FragmentType.END_TAG.equals(type)) { + s.append("/"); + } else if (FragmentType.DECLARATION.equals(type)) { + s.append("?"); + } + + if (FragmentType.DOCTYPE.equals(type)) { + s.append("!").append(name); + for (String partValue : attributes.values()) { + s.append(" ").append(partValue); + } + } else { + s.append(name); + for (Map.Entry attr : attributes.entrySet()) { + s.append(" ").append(attr.getKey()).append("=\"").append(attr.getValue()).append("\""); + } + } + if (FragmentType.DECLARATION.equals(type)) { + s.append("?"); + } + s.append(">"); + return s.toString(); + } + + public enum FragmentType { + START_TAG, END_TAG, EMPTY_ELEMENT, CDATA, + DECLARATION, DOCTYPE, COMMENT + } + } +} diff --git a/chart/src/test/java/org/xbib/graphics/chart/io/vector/TestUtilsTest.java b/chart/src/test/java/org/xbib/graphics/chart/io/vector/TestUtilsTest.java new file mode 100644 index 0000000..9047d58 --- /dev/null +++ b/chart/src/test/java/org/xbib/graphics/chart/io/vector/TestUtilsTest.java @@ -0,0 +1,191 @@ +package org.xbib.graphics.chart.io.vector; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertTrue; +import org.junit.jupiter.api.Test; +import org.xbib.graphics.chart.io.vector.TestUtils.XMLFragment; + + +public class TestUtilsTest { + + @Test + public void testParseXmlStartTag() throws Exception { + String xmlTagName = "foo:bar.baz_tag"; + String xmlString; + XMLFragment frag; + + xmlString = "<" + xmlTagName + ">"; + frag = XMLFragment.parse(xmlString, 0); + assertEquals(xmlTagName, frag.name); + assertEquals(XMLFragment.FragmentType.START_TAG, frag.type); + assertTrue(frag.attributes.isEmpty()); + assertEquals(0, frag.matchStart); + assertEquals(xmlString.length(), frag.matchEnd); + + xmlString = "< " + xmlTagName + " >"; + frag = XMLFragment.parse(xmlString, 0); + assertEquals(xmlTagName, frag.name); + assertEquals(XMLFragment.FragmentType.START_TAG, frag.type); + assertTrue(frag.attributes.isEmpty()); + assertEquals(0, frag.matchStart); + assertEquals(xmlString.length(), frag.matchEnd); + } + + @Test + public void testParseXmlEndTag() throws Exception { + String xmlTagName = "foo:bar.baz_tag"; + String xmlString; + XMLFragment frag; + + xmlString = ""; + frag = XMLFragment.parse(xmlString, 0); + assertEquals(xmlTagName, frag.name); + assertEquals(XMLFragment.FragmentType.END_TAG, frag.type); + assertTrue(frag.attributes.isEmpty()); + assertEquals(0, frag.matchStart); + assertEquals(xmlString.length(), frag.matchEnd); + + xmlString = ""; + frag = XMLFragment.parse(xmlString, 0); + assertEquals(xmlTagName, frag.name); + assertEquals(XMLFragment.FragmentType.END_TAG, frag.type); + assertTrue(frag.attributes.isEmpty()); + assertEquals(0, frag.matchStart); + assertEquals(xmlString.length(), frag.matchEnd); + } + + @Test + public void testParseXmlEmptyElement() throws Exception { + String xmlTagName = "foo:bar.baz_tag"; + String xmlString; + XMLFragment frag; + + xmlString = "<" + xmlTagName + "/>"; + frag = XMLFragment.parse(xmlString, 0); + assertEquals(xmlTagName, frag.name); + assertEquals(XMLFragment.FragmentType.EMPTY_ELEMENT, frag.type); + assertTrue(frag.attributes.isEmpty()); + assertEquals(0, frag.matchStart); + assertEquals(xmlString.length(), frag.matchEnd); + + xmlString = "< " + xmlTagName + " />"; + frag = XMLFragment.parse(xmlString, 0); + assertEquals(xmlTagName, frag.name); + assertEquals(XMLFragment.FragmentType.EMPTY_ELEMENT, frag.type); + assertTrue(frag.attributes.isEmpty()); + assertEquals(0, frag.matchStart); + assertEquals(xmlString.length(), frag.matchEnd); + } + + @Test + public void testParseXmlCDATA() throws Exception { + String xmlString; + XMLFragment frag; + + xmlString = ""; + frag = XMLFragment.parse(xmlString, 0); + assertEquals("", frag.name); + assertEquals(XMLFragment.FragmentType.CDATA, frag.type); + assertEquals("foo bar", frag.attributes.get("value")); + assertEquals(0, frag.matchStart); + assertEquals(xmlString.length(), frag.matchEnd); + + xmlString = "foo bar]]>"; + frag = XMLFragment.parse(xmlString, 0); + assertEquals("", frag.name); + assertEquals(XMLFragment.FragmentType.CDATA, frag.type); + assertEquals("foo bar", frag.attributes.get("value")); + assertEquals(0, frag.matchStart); + assertEquals(xmlString.length(), frag.matchEnd); + } + + @Test + public void testParseXmlDeclaration() throws Exception { + String xmlString; + XMLFragment frag; + + xmlString = ""; + frag = XMLFragment.parse(xmlString, 0); + assertEquals("xml", frag.name); + assertEquals(XMLFragment.FragmentType.DECLARATION, frag.type); + assertEquals("1.0", frag.attributes.get("version")); + assertEquals("UTF-8", frag.attributes.get("encoding")); + assertEquals(0, frag.matchStart); + assertEquals(xmlString.length(), frag.matchEnd); + } + + @Test + public void testParseXmlDoctype() throws Exception { + String xmlString; + XMLFragment frag; + + xmlString = ""; + frag = XMLFragment.parse(xmlString, 0); + assertEquals(XMLFragment.FragmentType.DOCTYPE, frag.type); + assertEquals("html", frag.attributes.get("doctype 00")); + assertEquals(0, frag.matchStart); + assertEquals(xmlString.length(), frag.matchEnd); + + xmlString = ""; + frag = XMLFragment.parse(xmlString, 0); + assertEquals(XMLFragment.FragmentType.DOCTYPE, frag.type); + assertEquals("svg", frag.attributes.get("doctype 00")); + assertEquals("PUBLIC", frag.attributes.get("doctype 01")); + assertEquals("-//W3C//DTD SVG 1.1 Tiny//EN", frag.attributes.get("doctype 02")); + assertEquals("http://www.w3.org/Graphics/SVG/1.1/DTD/svg11-tiny.dtd", frag.attributes.get("doctype 03")); + assertEquals(0, frag.matchStart); + assertEquals(xmlString.length(), frag.matchEnd); + } + + @Test + public void testParseXmlComment() throws Exception { + String xmlString; + XMLFragment frag; + + xmlString = ""; + frag = XMLFragment.parse(xmlString, 0); + assertEquals("", frag.name); + assertEquals(XMLFragment.FragmentType.COMMENT, frag.type); + assertEquals("foo bar", frag.attributes.get("value")); + assertEquals(0, frag.matchStart); + assertEquals(xmlString.length(), frag.matchEnd); + + xmlString = ""; + frag = XMLFragment.parse(xmlString, 0); + assertEquals("", frag.name); + assertEquals(XMLFragment.FragmentType.COMMENT, frag.type); + assertEquals("foo bar", frag.attributes.get("value")); + assertEquals(0, frag.matchStart); + assertEquals(xmlString.length(), frag.matchEnd); + } + + @Test + public void testParseXMLAttributesTag() throws Exception { + String xmlTagName = "foo:bar.baz_tag"; + String xmlString; + XMLFragment frag; + + xmlString = "<" + xmlTagName + " foo='bar'>"; + frag = XMLFragment.parse(xmlString, 0); + assertEquals(xmlTagName, frag.name); + assertEquals("bar", frag.attributes.get("foo")); + assertEquals(0, frag.matchStart); + assertEquals(xmlString.length(), frag.matchEnd); + + xmlString = "<" + xmlTagName + " foo=\"bar\">"; + frag = XMLFragment.parse(xmlString, 0); + assertEquals(xmlTagName, frag.name); + assertEquals("bar", frag.attributes.get("foo")); + assertEquals(0, frag.matchStart); + assertEquals(xmlString.length(), frag.matchEnd); + + xmlString = "<" + xmlTagName + " foo=\"bar\" baz='qux'>"; + frag = XMLFragment.parse(xmlString, 0); + assertEquals(xmlTagName, frag.name); + assertEquals("bar", frag.attributes.get("foo")); + assertEquals("qux", frag.attributes.get("baz")); + assertEquals(0, frag.matchStart); + assertEquals(xmlString.length(), frag.matchEnd); + } +} diff --git a/chart/src/test/java/org/xbib/graphics/chart/io/vector/VectorGraphics2DTest.java b/chart/src/test/java/org/xbib/graphics/chart/io/vector/VectorGraphics2DTest.java new file mode 100644 index 0000000..93cfd97 --- /dev/null +++ b/chart/src/test/java/org/xbib/graphics/chart/io/vector/VectorGraphics2DTest.java @@ -0,0 +1,63 @@ +package org.xbib.graphics.chart.io.vector; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertNotEquals; +import static org.junit.jupiter.api.Assertions.assertNotNull; +import static org.junit.jupiter.api.Assertions.assertTrue; +import org.junit.jupiter.api.Test; +import org.xbib.graphics.chart.io.vector.intermediate.commands.Command; +import org.xbib.graphics.chart.io.vector.intermediate.commands.CreateCommand; +import org.xbib.graphics.chart.io.vector.intermediate.commands.DisposeCommand; + +import java.awt.Color; +import java.awt.Graphics2D; +import java.util.Iterator; + +public class VectorGraphics2DTest { + + @Test + public void testEmptyVectorGraphics2DStartsWithCreateCommand() { + VectorGraphics2D g = new VectorGraphics2D(); + Iterable> commands = g.getCommands(); + Iterator> commandIterator = commands.iterator(); + assertTrue(commandIterator.hasNext()); + Command firstCommand = commandIterator.next(); + assertTrue(firstCommand instanceof CreateCommand); + assertEquals(g, ((CreateCommand) firstCommand).getValue()); + } + + @Test + public void testCreateEmitsCreateCommand() { + VectorGraphics2D g = new VectorGraphics2D(); + Iterable> gCommands = g.getCommands(); + Iterator> gCommandIterator = gCommands.iterator(); + CreateCommand gCreateCommand = (CreateCommand) gCommandIterator.next(); + + VectorGraphics2D g2 = (VectorGraphics2D) g.create(); + CreateCommand g2CreateCommand = null; + for (Command g2Command : g2.getCommands()) { + if (g2Command instanceof CreateCommand) { + g2CreateCommand = (CreateCommand) g2Command; + } + } + assertNotNull(g2CreateCommand); + assertNotEquals(gCreateCommand, g2CreateCommand); + assertEquals(g2, g2CreateCommand.getValue()); + } + + @Test + public void testDisposeCommandEmitted() { + VectorGraphics2D g = new VectorGraphics2D(); + g.setColor(Color.RED); + Graphics2D g2 = (Graphics2D) g.create(); + g2.setColor(Color.BLUE); + g2.dispose(); + Iterable> commands = g.getCommands(); + Command lastCommand = null; + for (Command command : commands) { + lastCommand = command; + } + assertTrue(lastCommand instanceof DisposeCommand); + assertEquals(Color.BLUE, ((DisposeCommand) lastCommand).getValue().getColor()); + } +} diff --git a/chart/src/test/java/org/xbib/graphics/chart/io/vector/eps/EPSProcessorTest.java b/chart/src/test/java/org/xbib/graphics/chart/io/vector/eps/EPSProcessorTest.java new file mode 100644 index 0000000..ee39aa3 --- /dev/null +++ b/chart/src/test/java/org/xbib/graphics/chart/io/vector/eps/EPSProcessorTest.java @@ -0,0 +1,70 @@ +package org.xbib.graphics.chart.io.vector.eps; + +import org.junit.jupiter.api.Test; +import org.xbib.graphics.chart.io.vector.Document; +import org.xbib.graphics.chart.io.vector.intermediate.commands.Command; +import org.xbib.graphics.chart.io.vector.util.PageSize; + +import java.io.ByteArrayOutputStream; +import java.io.IOException; +import java.nio.charset.StandardCharsets; +import java.util.Collections; +import java.util.LinkedList; +import java.util.List; +import java.util.regex.Pattern; + +import static org.xbib.graphics.chart.io.vector.TestUtils.Template; +import static org.xbib.graphics.chart.io.vector.TestUtils.assertTemplateEquals; + +public class EPSProcessorTest { + private static final String EOL = "\n"; + private static final Object[] HEADER = { + "%!PS-Adobe-3.0 EPSF-3.0", + "%%BoundingBox: 0 28 57 114", + "%%HiResBoundingBox: 0.0 28.34645669291339 56.69291338582678 113.38582677165356", + "%%LanguageLevel: 3", + "%%Pages: 1", + "%%EndComments", + "%%Page: 1 1", + "/M /moveto load def", + "/L /lineto load def", + "/C /curveto load def", + "/Z /closepath load def", + "/RL /rlineto load def", + "/rgb /setrgbcolor load def", + "/rect { /height exch def /width exch def /y exch def /x exch def x y M width 0 RL 0 height RL width neg 0 RL } bind def", + "/ellipse { /endangle exch def /startangle exch def /ry exch def /rx exch def /y exch def /x exch def /savematrix matrix currentmatrix def x y translate rx ry scale 0 0 1 startangle endangle arcn savematrix setmatrix } bind def", + "/imgdict { /datastream exch def /hasdata exch def /decodeScale exch def /bits exch def /bands exch def /imgheight exch def /imgwidth exch def << /ImageType 1 /Width imgwidth /Height imgheight /BitsPerComponent bits /Decode [bands {0 decodeScale} repeat] ", + "/ImageMatrix [imgwidth 0 0 imgheight 0 0] hasdata { /DataSource datastream } if >> } bind def", + "/latinize { /fontName exch def /fontNameNew exch def fontName findfont 0 dict copy begin /Encoding ISOLatin1Encoding def fontNameNew /FontName def currentdict end dup /FID undef fontNameNew exch definefont pop } bind def", + Pattern.compile("/\\S+?Lat /\\S+ latinize /\\S+?Lat 12.0 selectfont"), + "gsave", + "clipsave", + "/DeviceRGB setcolorspace", + "0 85.03937007874016 translate", + "2.834645669291339 -2.834645669291339 scale", + "/basematrix matrix currentmatrix def", + "%%EOF" + }; + private static final PageSize PAGE_SIZE = new PageSize(0.0, 10.0, 20.0, 30.0); + + private final EPSProcessor processor = new EPSProcessor(); + private final List> commands = new LinkedList>(); + private final ByteArrayOutputStream bytes = new ByteArrayOutputStream(); + + private String process(Command... commands) throws IOException { + Collections.addAll(this.commands, commands); + Document processed = processor.process(this.commands, PAGE_SIZE); + processed.write(bytes); + return bytes.toString(StandardCharsets.ISO_8859_1); + } + + @Test + public void envelopeForEmptyDocument() throws IOException { + String result = process(); + Template actual = new Template(result.split(EOL)); + Template expected = new Template(HEADER); + assertTemplateEquals(expected, actual); + } + +} diff --git a/chart/src/test/java/org/xbib/graphics/chart/io/vector/intermediate/filters/AbsoluteToRelativeTransformsFilterTest.java b/chart/src/test/java/org/xbib/graphics/chart/io/vector/intermediate/filters/AbsoluteToRelativeTransformsFilterTest.java new file mode 100644 index 0000000..8b69958 --- /dev/null +++ b/chart/src/test/java/org/xbib/graphics/chart/io/vector/intermediate/filters/AbsoluteToRelativeTransformsFilterTest.java @@ -0,0 +1,91 @@ +package org.xbib.graphics.chart.io.vector.intermediate.filters; + +import org.junit.jupiter.api.Test; +import org.xbib.graphics.chart.io.vector.intermediate.commands.Command; +import org.xbib.graphics.chart.io.vector.intermediate.commands.CreateCommand; +import org.xbib.graphics.chart.io.vector.intermediate.commands.DisposeCommand; +import org.xbib.graphics.chart.io.vector.intermediate.commands.SetTransformCommand; +import org.xbib.graphics.chart.io.vector.intermediate.commands.TransformCommand; +import org.xbib.graphics.chart.io.vector.intermediate.commands.TranslateCommand; + +import java.awt.geom.AffineTransform; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; + +import static org.hamcrest.CoreMatchers.is; +import static org.hamcrest.MatcherAssert.assertThat; + +public class AbsoluteToRelativeTransformsFilterTest { + + @Test + public void testAbsoluteAndRelativeTransformsIdentical() { + AffineTransform absoluteTransform = new AffineTransform(); + absoluteTransform.rotate(42.0); + absoluteTransform.translate(4.0, 2.0); + List> commands = wrapCommands( + new SetTransformCommand(absoluteTransform) + ); + + AbsoluteToRelativeTransformsFilter filter = new AbsoluteToRelativeTransformsFilter(commands); + + filter.next(); + AffineTransform relativeTransform = ((TransformCommand) filter.next()).getValue(); + assertThat(relativeTransform, is(absoluteTransform)); + } + + @Test + public void testTranslateCorrect() { + AffineTransform absoluteTransform = new AffineTransform(); + absoluteTransform.scale(2.0, 2.0); + absoluteTransform.translate(4.2, 4.2); // (8.4, 8.4) + List> commands = wrapCommands( + new TranslateCommand(4.0, 2.0), + new SetTransformCommand(absoluteTransform) + ); + + AbsoluteToRelativeTransformsFilter filter = new AbsoluteToRelativeTransformsFilter(commands); + + TransformCommand transformCommand = null; + while (filter.hasNext()) { + Command filteredCommand = filter.next(); + if (filteredCommand instanceof TransformCommand) { + transformCommand = (TransformCommand) filteredCommand; + } + } + AffineTransform relativeTransform = transformCommand.getValue(); + assertThat(relativeTransform.getTranslateX(), is(4.4)); + assertThat(relativeTransform.getTranslateY(), is(6.4)); + } + + @Test + public void testRelativeTransformAfterDispose() { + AffineTransform absoluteTransform = new AffineTransform(); + absoluteTransform.rotate(42.0); + absoluteTransform.translate(4.0, 2.0); + List> commands = wrapCommands( + new CreateCommand(null), + new TransformCommand(absoluteTransform), + new DisposeCommand(null), + new SetTransformCommand(absoluteTransform) + ); + + AbsoluteToRelativeTransformsFilter filter = new AbsoluteToRelativeTransformsFilter(commands); + TransformCommand lastTransformCommand = null; + for (Command filteredCommand : filter) { + if (filteredCommand instanceof TransformCommand) { + lastTransformCommand = (TransformCommand) filteredCommand; + } + } + assertThat(lastTransformCommand.getValue(), is(absoluteTransform)); + } + + private List> wrapCommands(Command... commands) { + List> commandList = new ArrayList>(commands.length + 2); + commandList.add(new CreateCommand(null)); + commandList.addAll(Arrays.asList(commands)); + commandList.add(new DisposeCommand(null)); + return commandList; + } +} + diff --git a/chart/src/test/java/org/xbib/graphics/chart/io/vector/intermediate/filters/FillPaintedShapeAsImageFilterTest.java b/chart/src/test/java/org/xbib/graphics/chart/io/vector/intermediate/filters/FillPaintedShapeAsImageFilterTest.java new file mode 100644 index 0000000..72ba55f --- /dev/null +++ b/chart/src/test/java/org/xbib/graphics/chart/io/vector/intermediate/filters/FillPaintedShapeAsImageFilterTest.java @@ -0,0 +1,54 @@ +package org.xbib.graphics.chart.io.vector.intermediate.filters; + +import org.junit.jupiter.api.Test; +import org.xbib.graphics.chart.io.vector.intermediate.commands.Command; +import org.xbib.graphics.chart.io.vector.intermediate.commands.DrawImageCommand; +import org.xbib.graphics.chart.io.vector.intermediate.commands.FillShapeCommand; +import org.xbib.graphics.chart.io.vector.intermediate.commands.RotateCommand; +import org.xbib.graphics.chart.io.vector.intermediate.commands.SetPaintCommand; + +import java.awt.Color; +import java.awt.GradientPaint; +import java.awt.geom.Rectangle2D; +import java.util.Iterator; +import java.util.LinkedList; +import java.util.List; + +import static org.hamcrest.CoreMatchers.any; +import static org.hamcrest.CoreMatchers.hasItem; +import static org.hamcrest.CoreMatchers.not; +import static org.hamcrest.MatcherAssert.assertThat; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertFalse; + +public class FillPaintedShapeAsImageFilterTest { + + @Test + public void testFillShapeReplacedWithDrawImage() { + List> commands = new LinkedList>(); + commands.add(new SetPaintCommand(new GradientPaint(0.0f, 0.0f, Color.BLACK, 100.0f, 100.0f, Color.WHITE))); + commands.add(new RotateCommand(10.0, 4.0, 2.0)); + commands.add(new FillShapeCommand(new Rectangle2D.Double(10.0, 10.0, 100.0, 100.0))); + + FillPaintedShapeAsImageFilter filter = new FillPaintedShapeAsImageFilter(commands); + + assertThat(filter, hasItem(any(DrawImageCommand.class))); + assertThat(filter, not(hasItem(any(FillShapeCommand.class)))); + } + + @Test + public void testFillShapeNotReplacedWithoutPaintCommand() { + List> commands = new LinkedList>(); + commands.add(new RotateCommand(10.0, 4.0, 2.0)); + commands.add(new FillShapeCommand(new Rectangle2D.Double(10.0, 10.0, 100.0, 100.0))); + + FillPaintedShapeAsImageFilter filter = new FillPaintedShapeAsImageFilter(commands); + + Iterator> filterIterator = filter.iterator(); + for (Command command : commands) { + assertEquals(command, filterIterator.next()); + } + assertFalse(filterIterator.hasNext()); + } +} + diff --git a/chart/src/test/java/org/xbib/graphics/chart/io/vector/intermediate/filters/FilterTest.java b/chart/src/test/java/org/xbib/graphics/chart/io/vector/intermediate/filters/FilterTest.java new file mode 100644 index 0000000..fbb954e --- /dev/null +++ b/chart/src/test/java/org/xbib/graphics/chart/io/vector/intermediate/filters/FilterTest.java @@ -0,0 +1,98 @@ +package org.xbib.graphics.chart.io.vector.intermediate.filters; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertTrue; +import org.junit.jupiter.api.Test; +import org.xbib.graphics.chart.io.vector.intermediate.commands.Command; +import org.xbib.graphics.chart.io.vector.intermediate.commands.DrawShapeCommand; +import org.xbib.graphics.chart.io.vector.intermediate.commands.SetColorCommand; +import org.xbib.graphics.chart.io.vector.intermediate.commands.SetStrokeCommand; +import org.xbib.graphics.chart.io.vector.intermediate.commands.SetTransformCommand; + +import java.awt.BasicStroke; +import java.awt.Color; +import java.awt.geom.AffineTransform; +import java.awt.geom.Line2D; +import java.util.Arrays; +import java.util.Iterator; +import java.util.LinkedList; +import java.util.List; + + +public class FilterTest { + + @Test + public void filterNone() { + List> stream = new LinkedList>(); + stream.add(new SetColorCommand(Color.BLACK)); + stream.add(new SetStrokeCommand(new BasicStroke(1f))); + stream.add(new DrawShapeCommand(new Line2D.Double(0.0, 1.0, 10.0, 11.0))); + stream.add(new SetTransformCommand(AffineTransform.getTranslateInstance(5.0, 5.0))); + stream.add(new DrawShapeCommand(new Line2D.Double(0.0, 1.0, 5.0, 6.0))); + + Iterator> unfiltered = stream.iterator(); + + Filter filtered = new Filter(stream) { + @Override + protected List> filter(Command command) { + return Arrays.>asList(command); + } + }; + + while (filtered.hasNext() || unfiltered.hasNext()) { + Command expected = unfiltered.next(); + Command result = filtered.next(); + assertEquals(expected, result); + } + } + + @Test + public void filterAll() { + List> stream = new LinkedList>(); + stream.add(new SetColorCommand(Color.BLACK)); + stream.add(new SetStrokeCommand(new BasicStroke(1f))); + stream.add(new DrawShapeCommand(new Line2D.Double(0.0, 1.0, 10.0, 11.0))); + stream.add(new SetTransformCommand(AffineTransform.getTranslateInstance(5.0, 5.0))); + stream.add(new DrawShapeCommand(new Line2D.Double(0.0, 1.0, 5.0, 6.0))); + + Iterator> unfiltered = stream.iterator(); + + Filter filtered = new Filter(stream) { + @Override + protected List> filter(Command command) { + return null; + } + }; + assertTrue(unfiltered.hasNext()); + assertFalse(filtered.hasNext()); + } + + @Test + public void duplicate() { + List> stream = new LinkedList>(); + stream.add(new SetColorCommand(Color.BLACK)); + stream.add(new SetStrokeCommand(new BasicStroke(1f))); + stream.add(new DrawShapeCommand(new Line2D.Double(0.0, 1.0, 10.0, 11.0))); + stream.add(new SetTransformCommand(AffineTransform.getTranslateInstance(5.0, 5.0))); + stream.add(new DrawShapeCommand(new Line2D.Double(0.0, 1.0, 5.0, 6.0))); + + Iterator> unfiltered = stream.iterator(); + + Filter filtered = new Filter(stream) { + @Override + protected List> filter(Command command) { + return Arrays.asList(command, command); + } + }; + + while (filtered.hasNext() || unfiltered.hasNext()) { + Command expected = unfiltered.next(); + Command result1 = filtered.next(); + Command result2 = filtered.next(); + assertEquals(expected, result1); + assertEquals(expected, result2); + } + } +} + diff --git a/chart/src/test/java/org/xbib/graphics/chart/io/vector/intermediate/filters/GroupingFilterTest.java b/chart/src/test/java/org/xbib/graphics/chart/io/vector/intermediate/filters/GroupingFilterTest.java new file mode 100644 index 0000000..0f1281e --- /dev/null +++ b/chart/src/test/java/org/xbib/graphics/chart/io/vector/intermediate/filters/GroupingFilterTest.java @@ -0,0 +1,56 @@ +package org.xbib.graphics.chart.io.vector.intermediate.filters; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import org.junit.jupiter.api.Test; +import org.xbib.graphics.chart.io.vector.intermediate.commands.Command; +import org.xbib.graphics.chart.io.vector.intermediate.commands.DrawShapeCommand; +import org.xbib.graphics.chart.io.vector.intermediate.commands.Group; +import org.xbib.graphics.chart.io.vector.intermediate.commands.SetColorCommand; +import org.xbib.graphics.chart.io.vector.intermediate.commands.SetStrokeCommand; +import org.xbib.graphics.chart.io.vector.intermediate.commands.SetTransformCommand; +import org.xbib.graphics.chart.io.vector.intermediate.commands.StateCommand; + +import java.awt.BasicStroke; +import java.awt.Color; +import java.awt.geom.AffineTransform; +import java.awt.geom.Line2D; +import java.util.Iterator; +import java.util.LinkedList; +import java.util.List; + +public class GroupingFilterTest { + + @Test + public void filtered() { + List> resultStream = new LinkedList>(); + resultStream.add(new SetColorCommand(Color.BLACK)); + resultStream.add(new SetStrokeCommand(new BasicStroke(1f))); + resultStream.add(new DrawShapeCommand(new Line2D.Double(0.0, 1.0, 10.0, 11.0))); + resultStream.add(new SetTransformCommand(AffineTransform.getTranslateInstance(5.0, 5.0))); + resultStream.add(new DrawShapeCommand(new Line2D.Double(0.0, 1.0, 5.0, 6.0))); + List> expectedStream = new LinkedList>(); + Iterator> resultCloneIterator = resultStream.iterator(); + Group group1 = new Group(); + group1.add(resultCloneIterator.next()); + group1.add(resultCloneIterator.next()); + expectedStream.add(group1); + expectedStream.add(resultCloneIterator.next()); + Group group2 = new Group(); + group2.add(resultCloneIterator.next()); + expectedStream.add(group2); + expectedStream.add(resultCloneIterator.next()); + Iterator> expectedIterator = expectedStream.iterator(); + Filter resultIterator = new GroupingFilter(resultStream) { + @Override + protected boolean isGrouped(Command command) { + return command instanceof StateCommand; + } + }; + while (resultIterator.hasNext() || expectedIterator.hasNext()) { + Command result = resultIterator.next(); + Command expected = expectedIterator.next(); + assertEquals(expected, result); + } + } +} + diff --git a/chart/src/test/java/org/xbib/graphics/chart/io/vector/pdf/PDFProcessorTest.java b/chart/src/test/java/org/xbib/graphics/chart/io/vector/pdf/PDFProcessorTest.java new file mode 100644 index 0000000..85fd19f --- /dev/null +++ b/chart/src/test/java/org/xbib/graphics/chart/io/vector/pdf/PDFProcessorTest.java @@ -0,0 +1,114 @@ +package org.xbib.graphics.chart.io.vector.pdf; + +import org.junit.jupiter.api.Test; +import org.xbib.graphics.chart.io.vector.Document; +import org.xbib.graphics.chart.io.vector.intermediate.commands.Command; +import org.xbib.graphics.chart.io.vector.util.PageSize; + +import java.io.ByteArrayOutputStream; +import java.io.IOException; +import java.nio.charset.StandardCharsets; +import java.util.Collections; +import java.util.LinkedList; +import java.util.List; +import java.util.regex.Pattern; + +import static org.xbib.graphics.chart.io.vector.TestUtils.Template; +import static org.xbib.graphics.chart.io.vector.TestUtils.assertTemplateEquals; + +public class PDFProcessorTest { + private static final String EOL = "\n"; + private static final String HEADER = "%PDF-1.4"; + private static final String FOOTER = "%%EOF"; + private static final PageSize PAGE_SIZE = new PageSize(0.0, 10.0, 20.0, 30.0); + + private final PDFProcessor processor = new PDFProcessor(); + private final List> commands = new LinkedList>(); + private final ByteArrayOutputStream bytes = new ByteArrayOutputStream(); + + private String process(Command... commands) throws IOException { + Collections.addAll(this.commands, commands); + Document processed = processor.process(this.commands, PAGE_SIZE); + processed.write(bytes); + return bytes.toString(StandardCharsets.ISO_8859_1); + } + + @Test + public void envelopeForEmptyDocument() throws IOException { + String result = process(); + Template actual = new Template(result.split(EOL)); + Template expected = new Template(new Object[]{ + HEADER, + "1 0 obj", + "<<", + "/Type /Catalog", + "/Pages 2 0 R", + ">>", + "endobj", + "2 0 obj", + "<<", + "/Type /Pages", + "/Kids [3 0 R]", + "/Count 1", + ">>", + "endobj", + "3 0 obj", + "<<", + "/Type /Page", + "/Parent 2 0 R", + "/MediaBox [0 28.34645669291339 56.69291338582678 85.03937007874016]", + "/Contents 4 0 R", + "/Resources 6 0 R", + ">>", + "endobj", + "4 0 obj", + "<<", + "/Length 5 0 R", + ">>", + "stream", + "q", + "1 1 1 rg 1 1 1 RG", + "2.834645669291339 0 0 -2.834645669291339 0 85.03937007874016 cm", + "/Fnt0 12.0 Tf", + "Q", + "endstream", + "endobj", + "5 0 obj", + "100", + "endobj", + "6 0 obj", + "<<", + "/ProcSet [/PDF /Text /ImageB /ImageC /ImageI]", + "/Font <<", + "/Fnt0 <<", + "/Type /Font", + "/Subtype /TrueType", + "/Encoding /WinAnsiEncoding", + Pattern.compile("/BaseFont /\\S+"), + ">>", + ">>", + ">>", + "endobj", + "xref", + "0 7", + "0000000000 65535 f ", + Pattern.compile("\\d{10} 00000 n "), + Pattern.compile("\\d{10} 00000 n "), + Pattern.compile("\\d{10} 00000 n "), + Pattern.compile("\\d{10} 00000 n "), + Pattern.compile("\\d{10} 00000 n "), + Pattern.compile("\\d{10} 00000 n "), + "trailer", + "<<", + "/Size 7", + "/Root 1 0 R", + ">>", + "startxref", + Pattern.compile("[1-9]\\d*"), + FOOTER + }); + + assertTemplateEquals(expected, actual); + } +} + diff --git a/chart/src/test/java/org/xbib/graphics/chart/io/vector/svg/SVGProcessorTest.java b/chart/src/test/java/org/xbib/graphics/chart/io/vector/svg/SVGProcessorTest.java new file mode 100644 index 0000000..309ec35 --- /dev/null +++ b/chart/src/test/java/org/xbib/graphics/chart/io/vector/svg/SVGProcessorTest.java @@ -0,0 +1,70 @@ +package org.xbib.graphics.chart.io.vector.svg; + +import org.junit.jupiter.api.Test; +import org.xbib.graphics.chart.io.vector.Document; +import org.xbib.graphics.chart.io.vector.intermediate.commands.Command; +import org.xbib.graphics.chart.io.vector.intermediate.commands.DrawShapeCommand; +import org.xbib.graphics.chart.io.vector.intermediate.commands.FillShapeCommand; +import org.xbib.graphics.chart.io.vector.util.PageSize; + +import java.awt.geom.Rectangle2D; +import java.io.ByteArrayOutputStream; +import java.io.IOException; +import java.nio.charset.StandardCharsets; +import java.util.Collections; +import java.util.LinkedList; +import java.util.List; + +import static org.xbib.graphics.chart.io.vector.TestUtils.assertXMLEquals; + +public class SVGProcessorTest { + private static final String EOL = "\n"; + private static final String HEADER = + "" + EOL + + "" + EOL + + "" + EOL; + private static final String FOOTER = ""; + private static final PageSize PAGE_SIZE = new PageSize(0.0, 10.0, 20.0, 30.0); + + private final SVGProcessor processor = new SVGProcessor(); + private final List> commands = new LinkedList>(); + private final ByteArrayOutputStream bytes = new ByteArrayOutputStream(); + + private String process(Command... commands) throws IOException { + Collections.addAll(this.commands, commands); + Document processed = processor.process(this.commands, PAGE_SIZE); + processed.write(bytes); + return bytes.toString(StandardCharsets.UTF_8); + } + + @Test + public void envelopeForEmptyDocument() throws Exception { + String result = process(); + String expected = HEADER.replaceAll(">$", "/>"); + assertXMLEquals(expected, result); + } + + @Test + public void drawShapeBlack() throws Exception { + String result = process( + new DrawShapeCommand(new Rectangle2D.Double(1, 2, 3, 4)) + ); + String expected = + HEADER + EOL + + " " + EOL + + FOOTER; + assertXMLEquals(expected, result); + } + + @Test + public void fillShapeBlack() throws Exception { + String result = process( + new FillShapeCommand(new Rectangle2D.Double(1, 2, 3, 4)) + ); + String expected = + HEADER + EOL + + " " + EOL + + FOOTER; + assertXMLEquals(expected, result); + } +} diff --git a/chart/src/test/java/org/xbib/graphics/chart/io/vector/util/ASCII85EncodeStreamTest.java b/chart/src/test/java/org/xbib/graphics/chart/io/vector/util/ASCII85EncodeStreamTest.java new file mode 100644 index 0000000..3cbff85 --- /dev/null +++ b/chart/src/test/java/org/xbib/graphics/chart/io/vector/util/ASCII85EncodeStreamTest.java @@ -0,0 +1,53 @@ +package org.xbib.graphics.chart.io.vector.util; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import org.junit.jupiter.api.Test; +import java.io.ByteArrayInputStream; +import java.io.ByteArrayOutputStream; +import java.io.IOException; +import java.io.OutputStream; +import java.nio.charset.StandardCharsets; + +public class ASCII85EncodeStreamTest { + + private static void assertEncodedString(String expected, String input) throws IOException { + ByteArrayInputStream inputStream = new ByteArrayInputStream(input.getBytes()); + ByteArrayOutputStream outStream = new ByteArrayOutputStream(); + OutputStream encodeStream = new ASCII85EncodeStream(outStream); + byte[] buffer = new byte[1024]; + for (int count = inputStream.read(buffer); count >= 0; count = inputStream.read(buffer)) { + encodeStream.write(buffer, 0, count); + } + encodeStream.close(); + String encoded = outStream.toString(StandardCharsets.ISO_8859_1); + assertEquals(expected, encoded); + } + + @Test + public void testEncoding() throws IOException { + String input = + "Man is distinguished, not only by his reason, but by this singular passion " + + "from other animals, which is a lust of the mind, that by a perseverance of " + + "delight in the continued and indefatigable generation of knowledge, exceeds " + + "the short vehemence of any carnal pleasure."; + + String expected = + "9jqo^BlbD-BleB1DJ+*+F(f,q/0JhKFCj@.4Gp$d7F!,L7@<6@)/0JDEF@3BB/F*&OCAfu2/AKYi(DIb:@FD,*)" + + "+C]U=@3BN#EcYf8ATD3s@q?d$AftVqCh[NqF-FD5W8ARlolDIal(DIduD.RTpAKYo'+CT/5+Cei#" + + "DII?(E,9)oF*2M7/c~>"; + + assertEncodedString(expected, input); + } + + @Test + public void testPadding() throws IOException { + assertEncodedString("/c~>", "."); + } + + @Test + public void testEmpty() throws IOException { + assertEncodedString("~>", ""); + } +} diff --git a/chart/src/test/java/org/xbib/graphics/chart/io/vector/util/Base64EncodeStreamTest.java b/chart/src/test/java/org/xbib/graphics/chart/io/vector/util/Base64EncodeStreamTest.java new file mode 100644 index 0000000..4c1ac19 --- /dev/null +++ b/chart/src/test/java/org/xbib/graphics/chart/io/vector/util/Base64EncodeStreamTest.java @@ -0,0 +1,63 @@ +package org.xbib.graphics.chart.io.vector.util; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import org.junit.jupiter.api.Test; +import java.io.ByteArrayInputStream; +import java.io.ByteArrayOutputStream; +import java.io.IOException; +import java.io.OutputStream; +import java.nio.charset.StandardCharsets; + +public class Base64EncodeStreamTest { + + private static void assertEncodedString(String expected, String input) throws IOException { + ByteArrayInputStream inputStream = new ByteArrayInputStream(input.getBytes()); + ByteArrayOutputStream outStream = new ByteArrayOutputStream(); + OutputStream encodeStream = new Base64EncodeStream(outStream); + byte[] buffer = new byte[1024]; + for (int count = inputStream.read(buffer); count >= 0; count = inputStream.read(buffer)) { + encodeStream.write(buffer, 0, count); + } + encodeStream.close(); + String encoded = outStream.toString(StandardCharsets.ISO_8859_1); + assertEquals(expected, encoded); + } + + @Test + public void testEncoding() throws IOException { + String input = + "Man is distinguished, not only by his reason, but by this singular passion " + + "from other animals, which is a lust of the mind, that by a perseverance of " + + "delight in the continued and indefatigable generation of knowledge, exceeds " + + "the short vehemence of any carnal pleasure."; + + String expected = + "TWFuIGlzIGRpc3Rpbmd1aXNoZWQsIG5vdCBvbmx5IGJ5IGhpcyByZWFzb24sIGJ1dCBieSB0aGlz" + + "IHNpbmd1bGFyIHBhc3Npb24gZnJvbSBvdGhlciBhbmltYWxzLCB3aGljaCBpcyBhIGx1c3Qgb2Yg" + + "dGhlIG1pbmQsIHRoYXQgYnkgYSBwZXJzZXZlcmFuY2Ugb2YgZGVsaWdodCBpbiB0aGUgY29udGlu" + + "dWVkIGFuZCBpbmRlZmF0aWdhYmxlIGdlbmVyYXRpb24gb2Yga25vd2xlZGdlLCBleGNlZWRzIHRo" + + "ZSBzaG9ydCB2ZWhlbWVuY2Ugb2YgYW55IGNhcm5hbCBwbGVhc3VyZS4="; + + assertEncodedString(expected, input); + } + + @Test + public void testPadding() throws IOException { + assertEncodedString("YW55IGNhcm5hbCBwbGVhc3VyZS4=", "any carnal pleasure."); + assertEncodedString("YW55IGNhcm5hbCBwbGVhc3VyZQ==", "any carnal pleasure"); + assertEncodedString("YW55IGNhcm5hbCBwbGVhc3Vy", "any carnal pleasur"); + assertEncodedString("YW55IGNhcm5hbCBwbGVhc3U=", "any carnal pleasu"); + assertEncodedString("YW55IGNhcm5hbCBwbGVhcw==", "any carnal pleas"); + + assertEncodedString("cGxlYXN1cmUu", "pleasure."); + assertEncodedString("bGVhc3VyZS4=", "leasure."); + assertEncodedString("ZWFzdXJlLg==", "easure."); + assertEncodedString("YXN1cmUu", "asure."); + assertEncodedString("c3VyZS4=", "sure."); + } + + @Test + public void testEmpty() throws IOException { + assertEncodedString("", ""); + } +} diff --git a/chart/src/test/java/org/xbib/graphics/chart/io/vector/util/DataUtilsTest.java b/chart/src/test/java/org/xbib/graphics/chart/io/vector/util/DataUtilsTest.java new file mode 100644 index 0000000..8591fb8 --- /dev/null +++ b/chart/src/test/java/org/xbib/graphics/chart/io/vector/util/DataUtilsTest.java @@ -0,0 +1,28 @@ +package org.xbib.graphics.chart.io.vector.util; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import org.junit.jupiter.api.Test; + +public class DataUtilsTest { + + @Test + public void stripTrailingSpaces() { + String result = DataUtils.stripTrailing(" foo bar! ", " "); + String expected = " foo bar!"; + assertEquals(expected, result); + } + + @Test + public void stripTrailingSpacesInMultilineString() { + String result = DataUtils.stripTrailing(" foo bar! \n ", " "); + String expected = " foo bar! \n"; + assertEquals(expected, result); + } + + @Test + public void stripComplexSubstring() { + String result = DataUtils.stripTrailing("+bar foo+bar+bar+bar", "+bar"); + String expected = "+bar foo"; + assertEquals(expected, result); + } +} diff --git a/chart/src/test/java/org/xbib/graphics/chart/io/vector/util/GraphicsUtilsTest.java b/chart/src/test/java/org/xbib/graphics/chart/io/vector/util/GraphicsUtilsTest.java new file mode 100644 index 0000000..c1dd08c --- /dev/null +++ b/chart/src/test/java/org/xbib/graphics/chart/io/vector/util/GraphicsUtilsTest.java @@ -0,0 +1,142 @@ +package org.xbib.graphics.chart.io.vector.util; + +import static org.junit.jupiter.api.Assertions.assertArrayEquals; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertNotNull; +import static org.junit.jupiter.api.Assertions.assertNotSame; +import static org.junit.jupiter.api.Assertions.assertTrue; +import org.junit.jupiter.api.Test; +import java.awt.Font; +import java.awt.Image; +import java.awt.Polygon; +import java.awt.Rectangle; +import java.awt.Shape; +import java.awt.Toolkit; +import java.awt.geom.Arc2D; +import java.awt.geom.CubicCurve2D; +import java.awt.geom.Ellipse2D; +import java.awt.geom.Line2D; +import java.awt.geom.Path2D; +import java.awt.geom.PathIterator; +import java.awt.geom.QuadCurve2D; +import java.awt.geom.Rectangle2D; +import java.awt.geom.RoundRectangle2D; +import java.awt.image.BufferedImage; +import java.awt.image.FilteredImageSource; +import java.awt.image.RGBImageFilter; +import java.lang.reflect.InvocationTargetException; + +/** + * On Linux, the package msttcorefonts need to be installed + */ +public class GraphicsUtilsTest { + private static final double DELTA = 1e-15; + + private static void assertShapeEquals(Shape expected, Shape actual) { + if ((expected instanceof Line2D) && (actual instanceof Line2D)) { + assertEquals(((Line2D) expected).getP1(), ((Line2D) actual).getP1()); + assertEquals(((Line2D) expected).getP2(), ((Line2D) actual).getP2()); + } else if ((expected instanceof Polygon) && (actual instanceof Polygon)) { + int n = ((Polygon) actual).npoints; + assertEquals(((Polygon) expected).npoints, n); + if (n > 0) { + assertArrayEquals(((Polygon) expected).xpoints, ((Polygon) actual).xpoints); + assertArrayEquals(((Polygon) expected).ypoints, ((Polygon) actual).ypoints); + } + } else if ((expected instanceof QuadCurve2D) && (actual instanceof QuadCurve2D)) { + assertEquals(((QuadCurve2D) expected).getP1(), ((QuadCurve2D) actual).getP1()); + assertEquals(((QuadCurve2D) expected).getCtrlPt(), ((QuadCurve2D) actual).getCtrlPt()); + assertEquals(((QuadCurve2D) expected).getP2(), ((QuadCurve2D) actual).getP2()); + } else if ((expected instanceof CubicCurve2D) && (actual instanceof CubicCurve2D)) { + assertEquals(((CubicCurve2D) expected).getP1(), ((CubicCurve2D) actual).getP1()); + assertEquals(((CubicCurve2D) expected).getCtrlP1(), ((CubicCurve2D) actual).getCtrlP1()); + assertEquals(((CubicCurve2D) expected).getCtrlP2(), ((CubicCurve2D) actual).getCtrlP2()); + assertEquals(((CubicCurve2D) expected).getP2(), ((CubicCurve2D) actual).getP2()); + } else if ((expected instanceof Path2D) && (actual instanceof Path2D)) { + PathIterator itExpected = expected.getPathIterator(null); + PathIterator itActual = actual.getPathIterator(null); + double[] segmentExpected = new double[6]; + double[] segmentActual = new double[6]; + for (; !itExpected.isDone() || !itActual.isDone(); itExpected.next(), itActual.next()) { + assertEquals(itExpected.getWindingRule(), itActual.getWindingRule()); + itExpected.currentSegment(segmentExpected); + itActual.currentSegment(segmentActual); + assertArrayEquals(segmentExpected, segmentActual, DELTA); + } + } else { + assertEquals(expected, actual); + } + } + + @Test + public void testToBufferedImage() { + Image[] images = { + new BufferedImage(320, 240, BufferedImage.TYPE_INT_ARGB), + new BufferedImage(320, 240, BufferedImage.TYPE_INT_RGB), + Toolkit.getDefaultToolkit().createImage(new FilteredImageSource( + new BufferedImage(320, 240, BufferedImage.TYPE_INT_RGB).getSource(), + new RGBImageFilter() { + @Override + public int filterRGB(int x, int y, int rgb) { + return rgb & 0xff; + } + } + )) + }; + + for (Image image : images) { + BufferedImage bimage = GraphicsUtils.toBufferedImage(image); + assertNotNull(bimage); + assertEquals(BufferedImage.class, bimage.getClass()); + assertEquals(image.getWidth(null), bimage.getWidth()); + assertEquals(image.getHeight(null), bimage.getHeight()); + } + } + + @Test + public void testHasAlpha() { + Image image; + image = new BufferedImage(320, 240, BufferedImage.TYPE_INT_ARGB); + assertTrue(GraphicsUtils.hasAlpha(image)); + image = new BufferedImage(320, 240, BufferedImage.TYPE_INT_RGB); + assertFalse(GraphicsUtils.hasAlpha(image)); + } + + @Test + public void testPhysicalFont() { + Font font = new Font("Monospaced", Font.PLAIN, 12); + assertNotSame(font, GraphicsUtils.getPhysicalFont(font)); + } + + @Test + public void testCloneShape() + throws InstantiationException, IllegalAccessException, NoSuchMethodException, InvocationTargetException { + Class[] shapeClasses = { + Line2D.Float.class, + Line2D.Double.class, + Rectangle.class, + Rectangle2D.Float.class, + Rectangle2D.Double.class, + RoundRectangle2D.Float.class, + RoundRectangle2D.Double.class, + Ellipse2D.Float.class, + Ellipse2D.Double.class, + Arc2D.Float.class, + Arc2D.Double.class, + Polygon.class, + CubicCurve2D.Float.class, + CubicCurve2D.Double.class, + QuadCurve2D.Float.class, + QuadCurve2D.Double.class, + Path2D.Float.class, + Path2D.Double.class + }; + for (Class shapeClass : shapeClasses) { + Shape shape = (Shape) shapeClass.getDeclaredConstructor().newInstance(); + Shape clone = GraphicsUtils.clone(shape); + assertNotNull(clone); + assertShapeEquals(shape, clone); + } + } +} diff --git a/chart/src/test/java/org/xbib/graphics/chart/io/vector/visual/AbstractTest.java b/chart/src/test/java/org/xbib/graphics/chart/io/vector/visual/AbstractTest.java new file mode 100644 index 0000000..75c86a8 --- /dev/null +++ b/chart/src/test/java/org/xbib/graphics/chart/io/vector/visual/AbstractTest.java @@ -0,0 +1,170 @@ +package org.xbib.graphics.chart.io.vector.visual; + +import org.xbib.graphics.chart.io.vector.EPSGraphics2D; +import org.xbib.graphics.chart.io.vector.PDFGraphics2D; +import org.xbib.graphics.chart.io.vector.SVGGraphics2D; +import org.xbib.graphics.chart.io.vector.util.PageSize; + +import javax.imageio.ImageIO; +import java.awt.Color; +import java.awt.Graphics2D; +import java.awt.RenderingHints; +import java.awt.image.BufferedImage; +import java.io.ByteArrayInputStream; +import java.io.File; +import java.io.IOException; +import java.io.InputStream; + +public abstract class AbstractTest { + + private final PageSize pageSize; + + private final BufferedImage reference; + + private final EPSGraphics2D epsGraphics; + + private final PDFGraphics2D pdfGraphics; + + private final SVGGraphics2D svgGraphics; + + public AbstractTest() { + int width = 150; + int height = 150; + pageSize = new PageSize(0.0, 0.0, width, height); + epsGraphics = new EPSGraphics2D(0, 0, width, height); + draw(epsGraphics); + pdfGraphics = new PDFGraphics2D(0, 0, width, height); + draw(pdfGraphics); + svgGraphics = new SVGGraphics2D(0, 0, width, height); + draw(svgGraphics); + reference = new BufferedImage(width, height, BufferedImage.TYPE_INT_ARGB); + Graphics2D referenceGraphics = reference.createGraphics(); + referenceGraphics.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON); + referenceGraphics.setRenderingHint(RenderingHints.KEY_RENDERING, RenderingHints.VALUE_RENDER_QUALITY); + referenceGraphics.setBackground(new Color(1f, 1f, 1f, 0f)); + referenceGraphics.clearRect(0, 0, reference.getWidth(), reference.getHeight()); + referenceGraphics.setColor(Color.BLACK); + draw(referenceGraphics); + try { + File referenceImage = File.createTempFile(getClass().getName() + ".reference", ".png"); + referenceImage.deleteOnExit(); + ImageIO.write(reference, "png", referenceImage); + } catch (IOException e) { + throw new RuntimeException(e); + } + } + + public abstract void draw(Graphics2D g); + + public PageSize getPageSize() { + return pageSize; + } + + public BufferedImage getReference() { + return reference; + } + + public InputStream getEPS() { + return new ByteArrayInputStream(epsGraphics.getBytes()); + } + + public InputStream getPDF() { + return new ByteArrayInputStream(pdfGraphics.getBytes()); + } + + public InputStream getSVG() { + return new ByteArrayInputStream(svgGraphics.getBytes()); + } + + /*public BufferedImage getRasterizedEPS() throws GhostscriptException, IOException { + if (rasterizedEPS != null) { + return rasterizedEPS; + } + + File epsInputFile = File.createTempFile(getClass().getName() + ".testEPS", ".eps"); + epsInputFile.deleteOnExit(); + OutputStream epsInput = new FileOutputStream(epsInputFile); + epsInput.write(epsGraphics.getBytes()); + epsInput.close(); + + File pngOutputFile = File.createTempFile(getClass().getName() + ".testEPS", "png"); + pngOutputFile.deleteOnExit(); + Ghostscript gs = Ghostscript.getInstance(); + gs.initialize(new String[]{ + "-dBATCH", + "-dQUIET", + "-dNOPAUSE", + "-dSAFER", + String.format("-g%dx%d", Math.round(getPageSize().width), Math.round(getPageSize().height)), + "-dGraphicsAlphaBits=4", + "-dAlignToPixels=0", + "-dEPSCrop", + "-dPSFitPage", + "-sDEVICE=pngalpha", + "-sOutputFile=" + pngOutputFile.toString(), + epsInputFile.toString() + }); + gs.exit(); + rasterizedEPS = ImageIO.read(pngOutputFile); + return rasterizedEPS; + }*/ + + /*public BufferedImage getRasterizedPDF() throws GhostscriptException, IOException { + if (rasterizedPDF != null) { + return rasterizedPDF; + } + + File pdfInputFile = File.createTempFile(getClass().getName() + ".testPDF", ".pdf"); + pdfInputFile.deleteOnExit(); + OutputStream pdfInput = new FileOutputStream(pdfInputFile); + pdfInput.write(pdfGraphics.getBytes()); + pdfInput.close(); + + File pngOutputFile = File.createTempFile(getClass().getName() + ".testPDF", "png"); + pngOutputFile.deleteOnExit(); + Ghostscript gs = Ghostscript.getInstance(); + gs.initialize(new String[]{ + "-dBATCH", + "-dQUIET", + "-dNOPAUSE", + "-dSAFER", + String.format("-g%dx%d", Math.round(getPageSize().width), Math.round(getPageSize().height)), + "-dGraphicsAlphaBits=4", + // TODO: More robust settings for gs? DPI value is estimated. + "-r25", + "-dAlignToPixels=0", + "-dPDFFitPage", + "-sDEVICE=pngalpha", + "-sOutputFile=" + pngOutputFile.toString(), + pdfInputFile.toString() + }); + gs.exit(); + rasterizedPDF = ImageIO.read(pngOutputFile); + return rasterizedPDF; + }*/ + + /*public BufferedImage getRasterizedSVG() throws TranscoderException { + if (rasterizedSVG != null) { + return rasterizedSVG; + } + + rasterizedSVG = new BufferedImage( + (int) Math.round(getPageSize().width), (int) Math.round(getPageSize().height), + BufferedImage.TYPE_INT_ARGB); + + ImageTranscoder transcoder = new ImageTranscoder() { + @Override + public BufferedImage createImage(int width, int height) { + return rasterizedSVG; + } + + @Override + public void writeImage(BufferedImage bufferedImage, TranscoderOutput transcoderOutput) throws TranscoderException { + } + }; + + transcoder.transcode(new TranscoderInput(getSVG()), null); + + return rasterizedSVG; + }*/ +} diff --git a/chart/src/test/java/org/xbib/graphics/chart/io/vector/visual/CharacterTest.java b/chart/src/test/java/org/xbib/graphics/chart/io/vector/visual/CharacterTest.java new file mode 100644 index 0000000..aecd35a --- /dev/null +++ b/chart/src/test/java/org/xbib/graphics/chart/io/vector/visual/CharacterTest.java @@ -0,0 +1,40 @@ +package org.xbib.graphics.chart.io.vector.visual; + +import java.awt.Graphics2D; +import java.nio.charset.Charset; +import java.nio.charset.CharsetEncoder; +import java.nio.charset.StandardCharsets; +import java.util.ArrayList; +import java.util.List; + +public class CharacterTest extends AbstractTest { + + @Override + public void draw(Graphics2D g) { + double w = getPageSize().width; + double h = getPageSize().height; + Charset latin1 = StandardCharsets.ISO_8859_1; + CharsetEncoder latin1Encoder = latin1.newEncoder(); + List charactersInCharset = new ArrayList<>(); + for (char char_ = Character.MIN_VALUE; char_ < Character.MAX_VALUE; char_++) { + String javaString = String.valueOf(char_); + if (latin1Encoder.canEncode(char_)) { + charactersInCharset.add(javaString); + } + } + final int colCount = (int) Math.ceil(Math.sqrt(charactersInCharset.size())); + final int rowCount = colCount; + double tileWidth = w / colCount; + double tileHeight = h / rowCount; + int charIndex = 0; + for (double y = 0.0; y < h; y += tileHeight) { + for (double x = 0.0; x < w; x += tileWidth) { + String c = charactersInCharset.get(charIndex); + double tileCenterX = x + tileWidth / 2.0; + double tileCenterY = y + tileHeight / 2.0; + g.drawString(c, (float) tileCenterX, (float) tileCenterY); + charIndex++; + } + } + } +} diff --git a/chart/src/test/java/org/xbib/graphics/chart/io/vector/visual/ClippingTest.java b/chart/src/test/java/org/xbib/graphics/chart/io/vector/visual/ClippingTest.java new file mode 100644 index 0000000..8807512 --- /dev/null +++ b/chart/src/test/java/org/xbib/graphics/chart/io/vector/visual/ClippingTest.java @@ -0,0 +1,33 @@ +package org.xbib.graphics.chart.io.vector.visual; + +import java.awt.Graphics2D; +import java.awt.geom.AffineTransform; +import java.awt.geom.Ellipse2D; +import java.awt.geom.Line2D; + +public class ClippingTest extends AbstractTest { + + @Override + public void draw(Graphics2D g) { + double w = getPageSize().width; + double h = getPageSize().height; + + AffineTransform txOrig = g.getTransform(); + g.translate(w / 2.0, h / 2.0); + + g.setClip(new Ellipse2D.Double(-0.6 * w / 2.0, -h / 2.0, 0.6 * w, h)); + for (double x = -w / 2.0; x < w / 2.0; x += 4.0) { + g.draw(new Line2D.Double(x, -h / 2.0, x, h / 2.0)); + } + + g.rotate(Math.toRadians(-90.0)); + g.clip(new Ellipse2D.Double(-0.6 * w / 2.0, -h / 2.0, 0.6 * w, h)); + for (double x = -h / 2.0; x < h / 2.0; x += 4.0) { + g.draw(new Line2D.Double(x, -w / 2.0, x, w / 2.0)); + } + + g.setTransform(txOrig); + g.setClip(null); + g.draw(new Line2D.Double(0.0, 0.0, w, h)); + } +} diff --git a/chart/src/test/java/org/xbib/graphics/chart/io/vector/visual/ColorTest.java b/chart/src/test/java/org/xbib/graphics/chart/io/vector/visual/ColorTest.java new file mode 100644 index 0000000..b54f923 --- /dev/null +++ b/chart/src/test/java/org/xbib/graphics/chart/io/vector/visual/ColorTest.java @@ -0,0 +1,28 @@ +package org.xbib.graphics.chart.io.vector.visual; + +import java.awt.Color; +import java.awt.Graphics2D; +import java.awt.geom.Rectangle2D; + +public class ColorTest extends AbstractTest { + + @Override + public void draw(Graphics2D g) { + final float wPage = (float) getPageSize().width; + final float hPage = (float) getPageSize().height; + final float wTile = Math.min(wPage / 15f, hPage / 15f); + final float hTile = wTile; + float w = wPage - wTile; + float h = hPage - hTile; + for (float y = (hPage - h) / 2f; y < h; y += hTile) { + float yRel = y / h; + for (float x = (wPage - w) / 2f; x < w; x += wTile) { + float xRel = x / w; + Color c = Color.getHSBColor(yRel, 1f, 1f); + int alpha = 255 - (int) (xRel * 255f); + g.setColor(new Color(c.getRed(), c.getGreen(), c.getBlue(), alpha)); + g.fill(new Rectangle2D.Float(x, y, wTile, hTile)); + } + } + } +} diff --git a/chart/src/test/java/org/xbib/graphics/chart/io/vector/visual/EmptyFileTest.java b/chart/src/test/java/org/xbib/graphics/chart/io/vector/visual/EmptyFileTest.java new file mode 100644 index 0000000..503f42b --- /dev/null +++ b/chart/src/test/java/org/xbib/graphics/chart/io/vector/visual/EmptyFileTest.java @@ -0,0 +1,10 @@ +package org.xbib.graphics.chart.io.vector.visual; + +import java.awt.Graphics2D; + +public class EmptyFileTest extends AbstractTest { + + @Override + public void draw(Graphics2D g) { + } +} diff --git a/chart/src/test/java/org/xbib/graphics/chart/io/vector/visual/FontTest.java b/chart/src/test/java/org/xbib/graphics/chart/io/vector/visual/FontTest.java new file mode 100644 index 0000000..ef690ee --- /dev/null +++ b/chart/src/test/java/org/xbib/graphics/chart/io/vector/visual/FontTest.java @@ -0,0 +1,47 @@ +package org.xbib.graphics.chart.io.vector.visual; + +import org.xbib.graphics.chart.io.vector.GraphicsState; + +import java.awt.Font; +import java.awt.Graphics2D; + +public class FontTest extends AbstractTest { + + @Override + public void draw(Graphics2D g) { + final int tileCountH = 4; + final int tileCountV = 8; + final double wTile = getPageSize().width / tileCountH; + final double hTile = getPageSize().height / tileCountV; + final double xOrigin = (getPageSize().width - tileCountH * wTile) / 2.0; + final double yOrigin = (getPageSize().height - tileCountV * hTile) / 2.0; + double x = xOrigin; + double y = yOrigin; + + final float[] sizes = { + GraphicsState.DEFAULT_FONT.getSize2D(), GraphicsState.DEFAULT_FONT.getSize2D() / 2f + }; + final String[] names = { + GraphicsState.DEFAULT_FONT.getName(), Font.SERIF, Font.MONOSPACED, "Monospaced" + }; + final int[] styles = { + Font.PLAIN, Font.ITALIC, Font.BOLD, Font.BOLD | Font.ITALIC + }; + + for (float size : sizes) { + for (String name : names) { + for (int style : styles) { + Font font = new Font(name, style, 10).deriveFont(size); + g.setFont(font); + g.drawString("vg2d", (float) x, (float) y); + + x += wTile; + if (x >= tileCountH * wTile) { + x = xOrigin; + y += hTile; + } + } + } + } + } +} diff --git a/chart/src/test/java/org/xbib/graphics/chart/io/vector/visual/ImageTest.java b/chart/src/test/java/org/xbib/graphics/chart/io/vector/visual/ImageTest.java new file mode 100644 index 0000000..03ba67a --- /dev/null +++ b/chart/src/test/java/org/xbib/graphics/chart/io/vector/visual/ImageTest.java @@ -0,0 +1,29 @@ +package org.xbib.graphics.chart.io.vector.visual; + +import java.awt.Color; +import java.awt.GradientPaint; +import java.awt.Graphics2D; +import java.awt.geom.Point2D; +import java.awt.geom.Rectangle2D; +import java.awt.image.BufferedImage; + +public class ImageTest extends AbstractTest { + + @Override + public void draw(Graphics2D g) { + // Draw an image + BufferedImage image = new BufferedImage(4, 3, BufferedImage.TYPE_INT_ARGB); + Graphics2D gImage = (Graphics2D) image.getGraphics(); + gImage.setPaint(new GradientPaint( + new Point2D.Double(0.0, 0.0), Color.RED, + new Point2D.Double(3.0, 2.0), Color.BLUE) + ); + gImage.fill(new Rectangle2D.Double(0.0, 0.0, 4.0, 3.0)); + + g.drawImage(image, 0, 0, (int) getPageSize().width, (int) (0.5 * getPageSize().height), null); + + g.rotate(-10.0 / 180.0 * Math.PI, 2.0, 1.5); + g.drawImage(image, (int) (0.1 * getPageSize().width), (int) (0.6 * getPageSize().height), + (int) (0.33 * getPageSize().width), (int) (0.33 * getPageSize().height), null); + } +} diff --git a/chart/src/test/java/org/xbib/graphics/chart/io/vector/visual/PaintTest.java b/chart/src/test/java/org/xbib/graphics/chart/io/vector/visual/PaintTest.java new file mode 100644 index 0000000..ca1a6e3 --- /dev/null +++ b/chart/src/test/java/org/xbib/graphics/chart/io/vector/visual/PaintTest.java @@ -0,0 +1,37 @@ +package org.xbib.graphics.chart.io.vector.visual; + +import java.awt.Color; +import java.awt.GradientPaint; +import java.awt.Graphics2D; +import java.awt.geom.AffineTransform; +import java.awt.geom.Rectangle2D; + +public class PaintTest extends AbstractTest { + + @Override + public void draw(Graphics2D g) { + // Draw multiple rotated rectangles + final int steps = 25; + final int cols = 5; + final int rows = steps / cols; + final double tileWidth = getPageSize().width / cols; + final double tileHeight = getPageSize().height / rows; + g.translate(tileWidth / 2, tileHeight / 2); + + final double rectWidth = tileWidth * 0.8; + final double rectHeight = tileHeight * 0.8; + Rectangle2D rect = new Rectangle2D.Double(-rectWidth / 2, -rectHeight / 2, rectWidth, rectHeight); + g.setPaint(new GradientPaint(0f, (float) (-rectHeight / 2), Color.RED, 0f, (float) (rectHeight / 2), Color.BLUE)); + for (int i = 0; i < steps; i++) { + AffineTransform txOld = g.getTransform(); + AffineTransform tx = new AffineTransform(txOld); + int col = i % 5; + int row = i / 5; + tx.translate(col * tileWidth, row * tileHeight); + tx.rotate(i * Math.toRadians(360.0 / steps)); + g.setTransform(tx); + g.fill(rect); + g.setTransform(txOld); + } + } +} diff --git a/chart/src/test/java/org/xbib/graphics/chart/io/vector/visual/ShapesTest.java b/chart/src/test/java/org/xbib/graphics/chart/io/vector/visual/ShapesTest.java new file mode 100644 index 0000000..40314ee --- /dev/null +++ b/chart/src/test/java/org/xbib/graphics/chart/io/vector/visual/ShapesTest.java @@ -0,0 +1,127 @@ +package org.xbib.graphics.chart.io.vector.visual; + +import java.awt.Graphics2D; +import java.awt.Polygon; +import java.awt.geom.AffineTransform; +import java.awt.geom.Arc2D; +import java.awt.geom.CubicCurve2D; +import java.awt.geom.Ellipse2D; +import java.awt.geom.Line2D; +import java.awt.geom.Path2D; +import java.awt.geom.QuadCurve2D; +import java.awt.geom.Rectangle2D; +import java.awt.geom.RoundRectangle2D; + +public class ShapesTest extends AbstractTest { + + @Override + public void draw(Graphics2D g) { + final int tileCountH = 4; + final int tileCountV = 6; + final double wTile = getPageSize().width / tileCountH; + final double hTile = getPageSize().height / tileCountV; + final double xOrigin = (getPageSize().width - tileCountH * wTile) / 2.0; + final double yOrigin = (getPageSize().height - tileCountV * hTile) / 2.0; + double x = xOrigin; + double y = yOrigin; + + g.draw(new Line2D.Double(x, y, x + 0.8 * wTile, y + 0.6 * hTile)); + x += wTile; + g.draw(new QuadCurve2D.Double(x, y, x + 0.8 * wTile, y, x + 0.8 * wTile, y + 0.6 * hTile)); + x += wTile; + g.draw(new CubicCurve2D.Double(x, y, x + 0.8 * wTile, y, x, y + 0.6 * hTile, x + 0.8 * wTile, y + 0.6 * hTile)); + + x = xOrigin; + y += hTile; + g.fill(new Rectangle2D.Double(x, y, 0.8 * wTile, 0.6 * hTile)); + x += wTile; + g.draw(new Rectangle2D.Double(x, y, 0.8 * wTile, 0.6 * hTile)); + x += wTile; + + g.fill(new RoundRectangle2D.Double(x, y, 0.8 * wTile, 0.6 * hTile, 0.2 * wTile, 0.2 * hTile)); + x += wTile; + g.draw(new RoundRectangle2D.Double(x, y, 0.8 * wTile, 0.6 * hTile, 0.2 * wTile, 0.2 * hTile)); + x += wTile; + + + x = xOrigin; + y += hTile; + g.fill(new Ellipse2D.Double(x, y, 0.8 * wTile, 0.6 * hTile)); + x += wTile; + g.draw(new Ellipse2D.Double(x, y, 0.8 * wTile, 0.6 * hTile)); + x += wTile; + + g.fill(new Polygon( + new int[]{(int) (x), (int) (x + 0.8 * wTile / 2.0), (int) (x + 0.8 * wTile)}, + new int[]{(int) (y + 0.6 * hTile), (int) (y), (int) (y + 0.6 * hTile)}, + 3 + )); + x += wTile; + g.draw(new Polygon( + new int[]{(int) (x), (int) (x + 0.8 * wTile / 2.0), (int) (x + 0.8 * wTile)}, + new int[]{(int) (y + 0.6 * hTile), (int) (y), (int) (y + 0.6 * hTile)}, + 3 + )); + + + x = xOrigin; + y += hTile; + g.fill(new Arc2D.Double(x, y, 0.8 * wTile, 0.6 * hTile, 110, 320, Arc2D.PIE)); + x += wTile; + g.draw(new Arc2D.Double(x, y, 0.8 * wTile, 0.6 * hTile, 110, 320, Arc2D.PIE)); + x += wTile; + g.fill(new Arc2D.Double(x, y, 0.6 * hTile, 0.8 * wTile, 10, 320, Arc2D.CHORD)); + x += wTile; + g.draw(new Arc2D.Double(x, y, 0.6 * hTile, 0.8 * wTile, 10, 320, Arc2D.CHORD)); + + + x = xOrigin; + y += hTile; + g.fill(new Arc2D.Double(x, y, 0.6 * hTile, 0.8 * wTile, 10, 320, Arc2D.OPEN)); + x += wTile; + g.draw(new Arc2D.Double(x, y, 0.6 * hTile, 0.8 * wTile, 10, 320, Arc2D.OPEN)); + x += wTile; + g.fill(new Arc2D.Double(x, y, 0.6 * hTile, 0.8 * wTile, 10, 320, Arc2D.PIE)); + x += wTile; + g.draw(new Arc2D.Double(x, y, 0.6 * hTile, 0.8 * wTile, 10, 320, Arc2D.PIE)); + + + x = xOrigin; + y += hTile; + + final Path2D path1 = new Path2D.Double(); + path1.moveTo(0.00, 0.00); + path1.lineTo(0.33, 1.00); + path1.lineTo(0.67, 0.00); + path1.quadTo(0.33, 0.00, 0.33, 0.50); + path1.quadTo(0.33, 1.00, 0.67, 1.00); + path1.quadTo(1.00, 1.00, 1.00, 0.50); + path1.lineTo(0.67, 0.50); + path1.transform(AffineTransform.getScaleInstance(0.8 * wTile, 0.6 * hTile)); + + path1.transform(AffineTransform.getTranslateInstance(x, y)); + g.fill(path1); + x += wTile; + path1.transform(AffineTransform.getTranslateInstance(wTile, 0.0)); + g.draw(path1); + x += wTile; + + final Path2D path2 = new Path2D.Double(); + path2.moveTo(0.0, 0.4); + path2.curveTo(0.0, 0.3, 0.0, 0.0, 0.2, 0.0); + path2.curveTo(0.3, 0.0, 0.4, 0.1, 0.4, 0.3); + path2.curveTo(0.4, 0.5, 0.2, 0.8, 0.0, 1.0); + path2.lineTo(0.6, 1.0); + path2.lineTo(0.6, 0.0); + path2.curveTo(0.8, 0.0, 1.0, 0.2, 1.0, 0.5); + path2.curveTo(1.0, 0.6, 1.0, 0.8, 0.9, 0.9); + path2.closePath(); + path2.transform(AffineTransform.getScaleInstance(0.8 * wTile, 0.6 * hTile)); + + path2.transform(AffineTransform.getTranslateInstance(x, y)); + g.fill(path2); + x += wTile; + path2.transform(AffineTransform.getTranslateInstance(wTile, 0.0)); + g.draw(path2); + } +} diff --git a/chart/src/test/java/org/xbib/graphics/chart/io/vector/visual/StrokeTest.java b/chart/src/test/java/org/xbib/graphics/chart/io/vector/visual/StrokeTest.java new file mode 100644 index 0000000..3455ab1 --- /dev/null +++ b/chart/src/test/java/org/xbib/graphics/chart/io/vector/visual/StrokeTest.java @@ -0,0 +1,90 @@ +package org.xbib.graphics.chart.io.vector.visual; + +import java.awt.BasicStroke; +import java.awt.Graphics2D; +import java.awt.Stroke; +import java.awt.geom.AffineTransform; +import java.awt.geom.Path2D; + +public class StrokeTest extends AbstractTest { + + private static final Stroke[] strokes = { + // Width + new BasicStroke(0.0f), + new BasicStroke(0.5f), + new BasicStroke(1.0f), + new BasicStroke(2.0f), + // Cap + new BasicStroke(1f, BasicStroke.CAP_BUTT, BasicStroke.JOIN_MITER), + new BasicStroke(1f, BasicStroke.CAP_ROUND, BasicStroke.JOIN_MITER), + new BasicStroke(1f, BasicStroke.CAP_SQUARE, BasicStroke.JOIN_MITER), + null, + // Join + new BasicStroke(1f, BasicStroke.CAP_BUTT, BasicStroke.JOIN_BEVEL), + new BasicStroke(1f, BasicStroke.CAP_BUTT, BasicStroke.JOIN_MITER), + new BasicStroke(1f, BasicStroke.CAP_BUTT, BasicStroke.JOIN_ROUND), + null, + // Miter limit + new BasicStroke(1f, BasicStroke.CAP_BUTT, BasicStroke.JOIN_MITER, 1f), + new BasicStroke(1f, BasicStroke.CAP_BUTT, BasicStroke.JOIN_MITER, 2f), + new BasicStroke(1f, BasicStroke.CAP_BUTT, BasicStroke.JOIN_MITER, 3f), + new BasicStroke(1f, BasicStroke.CAP_BUTT, BasicStroke.JOIN_MITER, 10f), + // Dash pattern + new BasicStroke(1f, BasicStroke.CAP_BUTT, BasicStroke.JOIN_MITER, 10f, new float[]{1f}, 0f), + new BasicStroke(1f, BasicStroke.CAP_BUTT, BasicStroke.JOIN_MITER, 10f, new float[]{1f, 1f}, 0f), + new BasicStroke(1f, BasicStroke.CAP_BUTT, BasicStroke.JOIN_MITER, 10f, new float[]{3f, 1f, 1f}, 0f), + new BasicStroke(1f, BasicStroke.CAP_BUTT, BasicStroke.JOIN_MITER, 10f, new float[]{3f, 1f, 4f, 1f}, 0f), + // Dash phase + new BasicStroke(1f, BasicStroke.CAP_BUTT, BasicStroke.JOIN_MITER, 10f, new float[]{3f, 1f}, 0.5f), + new BasicStroke(1f, BasicStroke.CAP_BUTT, BasicStroke.JOIN_MITER, 10f, new float[]{3f, 1f}, 1.0f), + new BasicStroke(1f, BasicStroke.CAP_BUTT, BasicStroke.JOIN_MITER, 10f, new float[]{3f, 1f}, 1.5f), + new BasicStroke(1f, BasicStroke.CAP_BUTT, BasicStroke.JOIN_MITER, 10f, new float[]{3f, 1f}, 2.5f), + }; + + @Override + public void draw(Graphics2D g) { + final int tileCountH = 4; + final int tileCountV = 6; + final double wTile = getPageSize().width / tileCountH; + final double hTile = getPageSize().height / tileCountV; + final double xOrigin = (getPageSize().width - tileCountH * wTile) / 2.0; + final double yOrigin = (getPageSize().height - tileCountV * hTile) / 2.0; + + final Path2D path = new Path2D.Double(); + path.moveTo(0.00, 0.00); + path.lineTo(0.33, 1.00); + path.lineTo(0.67, 0.00); + path.quadTo(0.33, 0.00, 0.33, 0.50); + path.quadTo(0.33, 1.00, 0.67, 1.00); + path.quadTo(1.00, 1.00, 1.00, 0.50); + path.lineTo(0.67, 0.50); + path.moveTo(1.0, 0.4); + path.curveTo(1.0, 0.3, 1.0, 0.0, 1.2, 0.0); + path.curveTo(1.3, 0.0, 1.4, 0.1, 1.4, 0.3); + path.curveTo(1.4, 0.5, 1.2, 0.8, 1.0, 1.0); + path.lineTo(1.6, 1.0); + path.lineTo(1.6, 0.0); + path.curveTo(1.8, 0.0, 2.0, 0.2, 2.0, 0.5); + path.curveTo(2.0, 0.6, 2.0, 0.8, 1.9, 0.9); + + path.transform(AffineTransform.getScaleInstance(0.8 * wTile / 2.0, 0.6 * hTile)); + + double x = xOrigin; + double y = yOrigin; + for (Stroke stroke : strokes) { + if (stroke != null) { + Path2D p = new Path2D.Double(path); + p.transform(AffineTransform.getTranslateInstance(x, y)); + + g.setStroke(stroke); + g.draw(p); + } + + x += wTile; + if (x >= tileCountH * wTile) { + x = xOrigin; + y += hTile; + } + } + } +} diff --git a/chart/src/test/java/org/xbib/graphics/chart/io/vector/visual/SwingExportTest.java b/chart/src/test/java/org/xbib/graphics/chart/io/vector/visual/SwingExportTest.java new file mode 100644 index 0000000..f34ce73 --- /dev/null +++ b/chart/src/test/java/org/xbib/graphics/chart/io/vector/visual/SwingExportTest.java @@ -0,0 +1,24 @@ +package org.xbib.graphics.chart.io.vector.visual; + +import javax.swing.JButton; +import javax.swing.JFrame; +import javax.swing.JSlider; +import java.awt.BorderLayout; +import java.awt.Graphics2D; + +public class SwingExportTest extends AbstractTest { + + @Override + public void draw(Graphics2D g) { + JFrame frame = new JFrame(); + frame.getContentPane().add(new JButton("Hello Swing!"), BorderLayout.CENTER); + frame.getContentPane().add(new JSlider(), BorderLayout.NORTH); + frame.setSize(200, 250); + + //g.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON); + frame.setVisible(true); + frame.printAll(g); + frame.setVisible(false); + frame.dispose(); + } +} diff --git a/chart/src/test/java/org/xbib/graphics/chart/io/vector/visual/TestBrowser.java b/chart/src/test/java/org/xbib/graphics/chart/io/vector/visual/TestBrowser.java new file mode 100644 index 0000000..7917948 --- /dev/null +++ b/chart/src/test/java/org/xbib/graphics/chart/io/vector/visual/TestBrowser.java @@ -0,0 +1,263 @@ +package org.xbib.graphics.chart.io.vector.visual; + +import javax.swing.Box; +import javax.swing.BoxLayout; +import javax.swing.DefaultListCellRenderer; +import javax.swing.ImageIcon; +import javax.swing.JButton; +import javax.swing.JComboBox; +import javax.swing.JComponent; +import javax.swing.JFileChooser; +import javax.swing.JFrame; +import javax.swing.JLabel; +import javax.swing.JList; +import javax.swing.JPanel; +import javax.swing.JSplitPane; +import javax.swing.ListSelectionModel; +import javax.swing.WindowConstants; +import java.awt.BorderLayout; +import java.awt.Component; +import java.awt.event.ItemEvent; +import java.awt.event.ItemListener; +import java.awt.image.BufferedImage; +import java.io.File; +import java.io.FileOutputStream; +import java.io.IOException; +import java.io.InputStream; +import java.util.ArrayList; +import java.util.List; + +@SuppressWarnings("serial") +public class TestBrowser extends JFrame { + + private final List testCases; + + private final ImageComparisonPanel imageComparisonPanel; + + private AbstractTest testCase; + + public TestBrowser() { + super("Test browser"); + setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE); + setSize(1024, 768); + testCases = new ArrayList<>(); + testCases.add(new ColorTest()); + testCases.add(new StrokeTest()); + testCases.add(new ShapesTest()); + testCases.add(new FontTest()); + testCases.add(new CharacterTest()); + testCases.add(new EmptyFileTest()); + testCases.add(new ImageTest()); + testCases.add(new ClippingTest()); + testCases.add(new PaintTest()); + testCases.add(new SwingExportTest()); + testCases.add(new TransformTest()); + final JList testList = new JList<>(testCases.toArray()); + testList.setSelectionMode(ListSelectionModel.SINGLE_SELECTION); + testList.setCellRenderer(new DefaultListCellRenderer() { + @Override + public Component getListCellRendererComponent(JList list, Object value, int index, boolean isSelected, boolean cellHasFocus) { + String testName = value.getClass().getSimpleName(); + return super.getListCellRendererComponent(list, testName, index, isSelected, cellHasFocus); + } + }); + testList.addListSelectionListener(e -> { + if (!e.getValueIsAdjusting()) { + int index = testList.getSelectedIndex(); + if (index < 0) { + return; + } + AbstractTest test = testCases.get(index); + testCase = test; + try { + setTestCase(test); + } catch (IOException e1) { + e1.printStackTrace(); + } + } + }); + getContentPane().add(testList, BorderLayout.WEST); + + JPanel configurableImageComparisonPanel = new JPanel(new BorderLayout()); + getContentPane().add(configurableImageComparisonPanel, BorderLayout.CENTER); + + ImageFormat startingImageFormat = ImageFormat.EPS; + JComboBox imageFormatSelector = new JComboBox<>(ImageFormat.values()); + configurableImageComparisonPanel.add(imageFormatSelector, BorderLayout.NORTH); + imageFormatSelector.setSelectedItem(startingImageFormat); + imageFormatSelector.addItemListener(new ItemListener() { + @Override + public void itemStateChanged(ItemEvent itemEvent) { + ImageFormat format = (ImageFormat) itemEvent.getItem(); + imageComparisonPanel.setImageFormat(format); + + AbstractTest test = getTestCase(); + if (test != null) { + try { + setTestCase(test); + } catch (IOException e) { + e.printStackTrace(); + } + } + } + }); + + imageComparisonPanel = new ImageComparisonPanel(startingImageFormat); + configurableImageComparisonPanel.add(imageComparisonPanel, BorderLayout.CENTER); + } + + public static void main(String[] args) { + new TestBrowser().setVisible(true); + } + + public AbstractTest getTestCase() { + return testCase; + } + + public void setTestCase(AbstractTest test) throws IOException { + BufferedImage reference = test.getReference(); + imageComparisonPanel.setLeftComponent(new ImageDisplayPanel(reference, null)); + //ImageDisplayPanel imageDisplayPanel; + switch (imageComparisonPanel.getImageFormat()) { + case EPS: + //imageDisplayPanel = new ImageDisplayPanel(test.getRasterizedEPS(), test.getEPS()); + //imageComparisonPanel.setRightComponent(imageDisplayPanel); + break; + case PDF: + //imageDisplayPanel = new ImageDisplayPanel(test.getRasterizedPDF(), test.getPDF()); + //imageComparisonPanel.setRightComponent(imageDisplayPanel); + break; + case SVG: + //imageDisplayPanel = new ImageDisplayPanel(test.getRasterizedSVG(), test.getSVG()); + //imageComparisonPanel.setRightComponent(imageDisplayPanel); + break; + default: + throw new IllegalArgumentException("Unknown image format: " + imageComparisonPanel.getImageFormat()); + } + } + + private enum ImageFormat { + EPS("EPS"), + PDF("PDF"), + SVG("SVG"); + + private final String name; + + ImageFormat(String name) { + this.name = name; + } + + public String getName() { + return name; + } + } + + private static class ImageComparisonPanel extends Box { + + private final Box leftPanel; + + private final Box rightPanel; + + private ImageFormat imageFormat; + + private JComponent leftComponent; + + private JComponent rightComponent; + + public ImageComparisonPanel(ImageFormat imageFormat) { + super(BoxLayout.PAGE_AXIS); + + this.imageFormat = imageFormat; + + JSplitPane splitPane = new JSplitPane(JSplitPane.HORIZONTAL_SPLIT); + splitPane.setResizeWeight(0.5); + add(splitPane); + + leftPanel = new Box(BoxLayout.PAGE_AXIS); + leftPanel.add(new JLabel("Graphics2D")); + splitPane.setTopComponent(leftPanel); + + rightPanel = new Box(BoxLayout.PAGE_AXIS); + rightPanel.add(new JLabel(imageFormat.getName())); + splitPane.setBottomComponent(rightPanel); + } + + public void setLeftComponent(JComponent leftComponent) { + if (this.leftComponent != null) { + leftPanel.remove(this.leftComponent); + } + this.leftComponent = leftComponent; + leftPanel.add(leftComponent); + leftPanel.revalidate(); + leftPanel.repaint(); + } + + public void setRightComponent(JComponent rightComponent) { + if (this.rightComponent != null) { + rightPanel.remove(this.rightComponent); + } + this.rightComponent = rightComponent; + rightPanel.add(rightComponent); + rightPanel.revalidate(); + rightPanel.repaint(); + } + + public ImageFormat getImageFormat() { + return imageFormat; + } + + public void setImageFormat(ImageFormat imageFormat) { + this.imageFormat = imageFormat; + JLabel imageFormatLabel = (JLabel) rightPanel.getComponent(0); + imageFormatLabel.setText(imageFormat.getName()); + imageFormatLabel.repaint(); + } + } + + private static class ImageDisplayPanel extends JPanel { + private final InputStream imageData; + + public ImageDisplayPanel(BufferedImage renderedImage, InputStream imageData) { + super(new BorderLayout()); + this.imageData = imageData; + + JLabel imageLabel = new JLabel(new ImageIcon(renderedImage)); + add(imageLabel, BorderLayout.CENTER); + + JButton saveToFileButton = new JButton("Save as..."); + if (imageData == null) { + saveToFileButton.setEnabled(false); + } + saveToFileButton.addActionListener(e -> { + JFileChooser saveFileDialog = new JFileChooser(); + saveFileDialog.setFileSelectionMode(JFileChooser.FILES_ONLY); + saveFileDialog.setMultiSelectionEnabled(false); + int userChoice = saveFileDialog.showSaveDialog(ImageDisplayPanel.this); + if (userChoice != JFileChooser.APPROVE_OPTION) { + return; + } + + File dest = saveFileDialog.getSelectedFile(); + FileOutputStream destStream = null; + try { + destStream = new FileOutputStream(dest); + int imageDataChunk; + while ((imageDataChunk = ImageDisplayPanel.this.imageData.read()) != -1) { + destStream.write(imageDataChunk); + } + } catch (IOException e1) { + e1.printStackTrace(); + } finally { + if (destStream != null) { + try { + destStream.close(); + } catch (IOException e1) { + e1.printStackTrace(); + } + } + } + }); + add(saveToFileButton, BorderLayout.SOUTH); + } + } +} diff --git a/chart/src/test/java/org/xbib/graphics/chart/io/vector/visual/TransformTest.java b/chart/src/test/java/org/xbib/graphics/chart/io/vector/visual/TransformTest.java new file mode 100644 index 0000000..4bc25b5 --- /dev/null +++ b/chart/src/test/java/org/xbib/graphics/chart/io/vector/visual/TransformTest.java @@ -0,0 +1,59 @@ +package org.xbib.graphics.chart.io.vector.visual; + +import java.awt.Graphics2D; +import java.awt.Shape; +import java.awt.geom.AffineTransform; +import java.awt.geom.Rectangle2D; + +public class TransformTest extends AbstractTest { + + @Override + public void draw(Graphics2D g) { + final int rowCount = 2; + final int colCount = 4; + double wTile = getPageSize().width / colCount; + double hTile = wTile; + + g.translate(0.5 * wTile, 0.5 * hTile); + AffineTransform txOrig = g.getTransform(); + + Shape s = new Rectangle2D.Double(0.0, 0.0, 0.5 * wTile, 0.75 * hTile); + + // Row 1 + + g.draw(s); + + g.translate(wTile, 0.0); + g.draw(s); + + g.translate(wTile, 0.0); + { + Graphics2D g2 = (Graphics2D) g.create(); + g2.scale(0.5, 0.5); + g2.draw(s); + g2.dispose(); + } + + g.translate(wTile, 0.0); + { + Graphics2D g2 = (Graphics2D) g.create(); + g2.rotate(Math.toRadians(30.0)); + g2.draw(s); + g2.dispose(); + } + + // Row 2 + + g.setTransform(txOrig); + g.translate(0.0, hTile); + + g.shear(0.5, 0.0); + g.draw(s); + g.shear(-0.5, 0.0); + g.translate(wTile, 0.0); + + g.shear(0.0, 0.5); + g.draw(s); + g.shear(0.0, -0.5); + } +} diff --git a/chart/src/test/java/org/xbib/graphics/chart/swing/SwingWrapper.java b/chart/src/test/java/org/xbib/graphics/chart/swing/SwingWrapper.java new file mode 100644 index 0000000..2543eb0 --- /dev/null +++ b/chart/src/test/java/org/xbib/graphics/chart/swing/SwingWrapper.java @@ -0,0 +1,282 @@ +package org.xbib.graphics.chart.swing; + +import org.xbib.graphics.chart.io.BitmapFormat; +import org.xbib.graphics.chart.io.VectorGraphicsFormat; +import org.xbib.graphics.chart.Chart; + +import javax.swing.AbstractAction; +import javax.swing.JFileChooser; +import javax.swing.JFrame; +import javax.swing.JMenuItem; +import javax.swing.JPanel; +import javax.swing.JPopupMenu; +import javax.swing.KeyStroke; +import javax.swing.SwingUtilities; +import javax.swing.WindowConstants; +import javax.swing.filechooser.FileFilter; +import java.awt.Dimension; +import java.awt.Graphics; +import java.awt.Graphics2D; +import java.awt.GridLayout; +import java.awt.Toolkit; +import java.awt.event.ActionEvent; +import java.awt.event.KeyEvent; +import java.awt.event.MouseAdapter; +import java.awt.event.MouseEvent; +import java.awt.event.MouseListener; +import java.io.File; +import java.io.IOException; +import java.io.OutputStream; +import java.nio.file.Files; +import java.util.ArrayList; +import java.util.List; + +/** + * A convenience class used to display a Chart in a Swing application. + */ +public class SwingWrapper { + + private String windowTitle = "Chart"; + + private List> charts = new ArrayList<>(); + + private int numRows; + + private int numColumns; + + public SwingWrapper(Chart chart) { + this.charts.add(chart); + } + + public SwingWrapper(List> charts) { + this.charts = charts; + this.numRows = (int) (Math.sqrt(charts.size()) + .5); + this.numColumns = (int) ((double) charts.size() / this.numRows + 1); + } + + public SwingWrapper(List> charts, int numRows, int numColumns) { + this.charts = charts; + this.numRows = numRows; + this.numColumns = numColumns; + } + + public JFrame displayChart(String windowTitle) { + this.windowTitle = windowTitle; + return displayChart(); + } + + public JFrame displayChart() { + final JFrame frame = new JFrame(windowTitle); + SwingUtilities.invokeLater(() -> { + frame.setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE); + JPanel chartPanel = new ChartPanel<>(charts.get(0)); + frame.add(chartPanel); + frame.pack(); + frame.setVisible(true); + }); + + return frame; + } + + public JFrame displayChartMatrix(String windowTitle) { + this.windowTitle = windowTitle; + return displayChartMatrix(); + } + + private JFrame displayChartMatrix() { + final JFrame frame = new JFrame(windowTitle); + SwingUtilities.invokeLater(() -> { + frame.setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE); + frame.getContentPane().setLayout(new GridLayout(numRows, numColumns)); + for (Chart chart : charts) { + if (chart != null) { + JPanel chartPanel = new ChartPanel<>(chart); + frame.add(chartPanel); + } else { + JPanel chartPanel = new JPanel(); + frame.getContentPane().add(chartPanel); + } + } + frame.pack(); + frame.setVisible(true); + }); + return frame; + } + + @SuppressWarnings("serial") + private static class ChartPanel> extends JPanel { + + private final T chart; + + private final Dimension preferredSize; + + private String saveAsString = "Save As..."; + + public ChartPanel(T chart) { + this.chart = chart; + preferredSize = new Dimension(chart.getWidth(), chart.getHeight()); + // Right-click listener for saving chart + this.addMouseListener(new PopUpMenuClickListener()); + // Control+S key listener for saving chart + KeyStroke ctrlS = KeyStroke.getKeyStroke(KeyEvent.VK_S, Toolkit.getDefaultToolkit().getMenuShortcutKeyMaskEx()); + this.getInputMap(WHEN_IN_FOCUSED_WINDOW).put(ctrlS, "save"); + this.getActionMap().put("save", new SaveAction()); + } + + public void setSaveAsString(String saveAsString) { + this.saveAsString = saveAsString; + } + + @Override + protected void paintComponent(Graphics g) { + super.paintComponent(g); + Graphics2D g2d = (Graphics2D) g.create(); + chart.paint(g2d, getWidth(), getHeight()); + g2d.dispose(); + } + + public T getChart() { + return this.chart; + } + + @Override + public Dimension getPreferredSize() { + return this.preferredSize; + } + + void showSaveAsDialog() { + JFileChooser fileChooser = new JFileChooser(); + fileChooser.addChoosableFileFilter(new SuffixSaveFilter("jpg")); + FileFilter pngFileFilter = new SuffixSaveFilter("png"); + fileChooser.addChoosableFileFilter(pngFileFilter); + fileChooser.addChoosableFileFilter(new SuffixSaveFilter("bmp")); + fileChooser.addChoosableFileFilter(new SuffixSaveFilter("gif")); + fileChooser.addChoosableFileFilter(new SuffixSaveFilter("svg")); + fileChooser.addChoosableFileFilter(new SuffixSaveFilter("eps")); + fileChooser.addChoosableFileFilter(new SuffixSaveFilter("pdf")); + fileChooser.setAcceptAllFileFilterUsed(false); + fileChooser.setFileFilter(pngFileFilter); + if (fileChooser.showSaveDialog(null) == JFileChooser.APPROVE_OPTION) { + + if (fileChooser.getSelectedFile() != null) { + File theFileToSave = fileChooser.getSelectedFile(); + try { + OutputStream outputStream = Files.newOutputStream(theFileToSave.toPath()); + if (fileChooser.getFileFilter() == null) { + chart.saveBitmap(outputStream, BitmapFormat.PNG); + } else if (fileChooser.getFileFilter().getDescription().equals("*.jpg,*.JPG")) { + chart.saveJPGWithQuality(outputStream, 1.0f); + } else if (fileChooser.getFileFilter().getDescription().equals("*.png,*.PNG")) { + chart.saveBitmap(outputStream, BitmapFormat.PNG); + } else if (fileChooser.getFileFilter().getDescription().equals("*.bmp,*.BMP")) { + chart.saveBitmap(outputStream, BitmapFormat.BMP); + } else if (fileChooser.getFileFilter().getDescription().equals("*.gif,*.GIF")) { + chart.saveBitmap(outputStream, BitmapFormat.GIF); + } else if (fileChooser.getFileFilter().getDescription().equals("*.svg,*.SVG")) { + chart.write(outputStream, VectorGraphicsFormat.SVG); + } else if (fileChooser.getFileFilter().getDescription().equals("*.eps,*.EPS")) { + chart.write(outputStream, VectorGraphicsFormat.EPS); + } else if (fileChooser.getFileFilter().getDescription().equals("*.pdf,*.PDF")) { + chart.write(outputStream, VectorGraphicsFormat.PDF); + } + } catch (IOException e) { + e.printStackTrace(); + } + } + + } + } + + class SaveAction extends AbstractAction { + + public SaveAction() { + super("save"); + } + + @Override + public void actionPerformed(ActionEvent e) { + showSaveAsDialog(); + } + } + + class SuffixSaveFilter extends FileFilter { + + private final String suffix; + + public SuffixSaveFilter(String suffix) { + this.suffix = suffix; + } + + @Override + public boolean accept(File f) { + if (f.isDirectory()) { + return true; + } + String s = f.getName(); + return s.endsWith("." + suffix) || s.endsWith("." + suffix.toUpperCase()); + } + + @Override + public String getDescription() { + return "*." + suffix + ",*." + suffix.toUpperCase(); + } + } + + class PopUpMenuClickListener extends MouseAdapter { + + @Override + public void mousePressed(MouseEvent e) { + if (e.isPopupTrigger()) { + doPop(e); + } + } + + @Override + public void mouseReleased(MouseEvent e) { + if (e.isPopupTrigger()) { + doPop(e); + } + } + + private void doPop(MouseEvent e) { + ChartPanelPopupMenu menu = new ChartPanelPopupMenu(); + menu.show(e.getComponent(), e.getX(), e.getY()); + } + } + + class ChartPanelPopupMenu extends JPopupMenu { + + JMenuItem saveAsMenuItem; + + ChartPanelPopupMenu() { + saveAsMenuItem = new JMenuItem(saveAsString); + saveAsMenuItem.addMouseListener(new MouseListener() { + + @Override + public void mouseReleased(MouseEvent e) { + showSaveAsDialog(); + } + + @Override + public void mousePressed(MouseEvent e) { + } + + @Override + public void mouseExited(MouseEvent e) { + } + + @Override + public void mouseEntered(MouseEvent e) { + } + + @Override + public void mouseClicked(MouseEvent e) { + } + }); + add(saveAsMenuItem); + } + } + } + + +} diff --git a/gradle.properties b/gradle.properties index 75b39d6..b6622fd 100644 --- a/gradle.properties +++ b/gradle.properties @@ -1,4 +1,8 @@ -group = org.xbib +group = org.xbib.graphics name = graphics -version = 0.0.1 +version = 3.0.0 +gradle.wrapper.version = 6.6.1 +pdfbox.version = 2.0.19 +zxing.version = 3.3.1 +reflections.version = 0.9.11 diff --git a/gradle/compile/java.gradle b/gradle/compile/java.gradle new file mode 100644 index 0000000..c983c98 --- /dev/null +++ b/gradle/compile/java.gradle @@ -0,0 +1,44 @@ + +apply plugin: 'java-library' + +java { + modularity.inferModulePath.set(true) +} + +compileJava { + sourceCompatibility = JavaVersion.VERSION_11 + targetCompatibility = JavaVersion.VERSION_11 +} + +compileTestJava { + sourceCompatibility = JavaVersion.VERSION_11 + targetCompatibility = JavaVersion.VERSION_11 +} + +jar { + manifest { + attributes('Implementation-Version': project.version) + } +} + +task sourcesJar(type: Jar, dependsOn: classes) { + classifier 'sources' + from sourceSets.main.allSource + duplicatesStrategy = DuplicatesStrategy.EXCLUDE +} + +task javadocJar(type: Jar, dependsOn: javadoc) { + classifier 'javadoc' +} + +artifacts { + archives sourcesJar, javadocJar +} + +tasks.withType(JavaCompile) { + options.compilerArgs << '-Xlint:all' +} + +javadoc { + options.addStringOption('Xdoclint:none', '-quiet') +} diff --git a/gradle/documentation/asciidoc.gradle b/gradle/documentation/asciidoc.gradle new file mode 100644 index 0000000..87ba22e --- /dev/null +++ b/gradle/documentation/asciidoc.gradle @@ -0,0 +1,55 @@ +apply plugin: 'org.xbib.gradle.plugin.asciidoctor' + +configurations { + asciidoclet +} + +dependencies { + asciidoclet "org.asciidoctor:asciidoclet:${project.property('asciidoclet.version')}" +} + + +asciidoctor { + backends 'html5' + outputDir = file("${rootProject.projectDir}/docs") + separateOutputDirs = false + attributes 'source-highlighter': 'coderay', + idprefix: '', + idseparator: '-', + toc: 'left', + doctype: 'book', + icons: 'font', + encoding: 'utf-8', + sectlink: true, + sectanchors: true, + linkattrs: true, + imagesdir: 'img', + stylesheet: "${projectDir}/src/docs/asciidoc/css/foundation.css" +} + + +/*javadoc { +options.docletpath = configurations.asciidoclet.files.asType(List) +options.doclet = 'org.asciidoctor.Asciidoclet' +//options.overview = "src/docs/asciidoclet/overview.adoc" +options.addStringOption "-base-dir", "${projectDir}" +options.addStringOption "-attribute", + "name=${project.name},version=${project.version},title-link=https://github.com/xbib/${project.name}" +configure(options) { + noTimestamp = true +} +}*/ + + +/*javadoc { + options.docletpath = configurations.asciidoclet.files.asType(List) + options.doclet = 'org.asciidoctor.Asciidoclet' + options.overview = "${rootProject.projectDir}/src/docs/asciidoclet/overview.adoc" + options.addStringOption "-base-dir", "${projectDir}" + options.addStringOption "-attribute", + "name=${project.name},version=${project.version},title-link=https://github.com/xbib/${project.name}" + options.destinationDirectory(file("${projectDir}/docs/javadoc")) + configure(options) { + noTimestamp = true + } +}*/ diff --git a/gradle/ext.gradle b/gradle/ext.gradle deleted file mode 100644 index 567648e..0000000 --- a/gradle/ext.gradle +++ /dev/null @@ -1,8 +0,0 @@ -ext { - user = 'xbib' - projectName = 'graphics' - projectDescription = 'More graphics classes for Java' - scmUrl = 'https://github.com/xbib/graphics' - scmConnection = 'scm:git:git://github.com/xbib/graphics.git' - scmDeveloperConnection = 'scm:git:git://github.com/xbib/graphics.git' -} diff --git a/gradle/ide/idea.gradle b/gradle/ide/idea.gradle new file mode 100644 index 0000000..64e2167 --- /dev/null +++ b/gradle/ide/idea.gradle @@ -0,0 +1,13 @@ +apply plugin: 'idea' + +idea { + module { + outputDir file('build/classes/java/main') + testOutputDir file('build/classes/java/test') + } +} + +if (project.convention.findPlugin(JavaPluginConvention)) { + //sourceSets.main.output.classesDirs = file("build/classes/java/main") + //sourceSets.test.output.classesDirs = file("build/classes/java/test") +} diff --git a/gradle/publish.gradle b/gradle/publish.gradle deleted file mode 100644 index dd4f6ee..0000000 --- a/gradle/publish.gradle +++ /dev/null @@ -1,70 +0,0 @@ - -task xbibUpload(type: Upload, dependsOn: build) { - configuration = configurations.archives - uploadDescriptor = true - repositories { - if (project.hasProperty('xbibUsername')) { - mavenDeployer { - configuration = configurations.wagon - repository(url: uri('sftp://xbib.org/repository')) { - authentication(userName: xbibUsername, privateKey: xbibPrivateKey) - } - } - } - } -} - -task sonatypeUpload(type: Upload, dependsOn: build) { - configuration = configurations.archives - uploadDescriptor = true - repositories { - if (project.hasProperty('ossrhUsername')) { - mavenDeployer { - beforeDeployment { MavenDeployment deployment -> signing.signPom(deployment) } - repository(url: uri(ossrhReleaseUrl)) { - authentication(userName: ossrhUsername, password: ossrhPassword) - } - snapshotRepository(url: uri(ossrhSnapshotUrl)) { - authentication(userName: ossrhUsername, password: ossrhPassword) - } - pom.project { - groupId project.group - artifactId project.name - version project.version - name project.name - description projectDescription - packaging 'jar' - inceptionYear '2018' - url scmUrl - organization { - name 'xbib' - url 'http://xbib.org' - } - developers { - developer { - id user - name 'Jörg Prante' - email 'joergprante@gmail.com' - url 'https://github.com/jprante' - } - } - scm { - url scmUrl - connection scmConnection - developerConnection scmDeveloperConnection - } - licenses { - license { - name 'The GNU General Public License (GPL)' - url 'http://www.gnu.org/licenses/old-licenses/gpl-2.0.html' - } - } - } - } - } - } -} - -nexusStaging { - packageGroup = "org.xbib" -} diff --git a/gradle/publishing/publication.gradle b/gradle/publishing/publication.gradle new file mode 100644 index 0000000..a0f826e --- /dev/null +++ b/gradle/publishing/publication.gradle @@ -0,0 +1,66 @@ +import java.time.Duration + +apply plugin: "de.marcphilipp.nexus-publish" + +publishing { + publications { + mavenJava(MavenPublication) { + from components.java + artifact sourcesJar + artifact javadocJar + pom { + name = project.name + description = rootProject.ext.description + url = rootProject.ext.url + inceptionYear = rootProject.ext.inceptionYear + packaging = 'jar' + organization { + name = 'xbib' + url = 'https://xbib.org' + } + developers { + developer { + id = 'jprante' + name = 'Jörg Prante' + email = 'joergprante@gmail.com' + url = 'https://github.com/jprante' + } + } + scm { + url = rootProject.ext.scmUrl + connection = rootProject.ext.scmConnection + developerConnection = rootProject.ext.scmDeveloperConnection + } + issueManagement { + system = rootProject.ext.issueManagementSystem + url = rootProject.ext.issueManagementUrl + } + licenses { + license { + name = rootProject.ext.licenseName + url = rootProject.ext.licenseUrl + distribution = 'repo' + } + } + } + } + } +} + +if (project.hasProperty("signing.keyId")) { + apply plugin: 'signing' + signing { + sign publishing.publications.mavenJava + } +} + +nexusPublishing { + repositories { + sonatype { + username = project.property('ossrhUsername') + password = project.property('ossrhPassword') + packageGroup = "org.xbib" + } + } + clientTimeout = Duration.ofSeconds(600) +} diff --git a/gradle/publishing/sonatype.gradle b/gradle/publishing/sonatype.gradle new file mode 100644 index 0000000..e1813f3 --- /dev/null +++ b/gradle/publishing/sonatype.gradle @@ -0,0 +1,11 @@ + +if (project.hasProperty('ossrhUsername') && project.hasProperty('ossrhPassword')) { + + apply plugin: 'io.codearte.nexus-staging' + + nexusStaging { + username = project.property('ossrhUsername') + password = project.property('ossrhPassword') + packageGroup = "org.xbib" + } +} diff --git a/gradle/test/junit5.gradle b/gradle/test/junit5.gradle new file mode 100644 index 0000000..188ea0d --- /dev/null +++ b/gradle/test/junit5.gradle @@ -0,0 +1,28 @@ + +def junitVersion = project.hasProperty('junit.version')?project.property('junit.version'):'5.7.0' +def hamcrestVersion = project.hasProperty('hamcrest.version')?project.property('hamcrest.version'):'2.2' + +dependencies { + testImplementation "org.junit.jupiter:junit-jupiter-api:${junitVersion}" + testImplementation "org.junit.jupiter:junit-jupiter-params:${junitVersion}" + testImplementation "org.hamcrest:hamcrest-library:${hamcrestVersion}" + testRuntimeOnly "org.junit.jupiter:junit-jupiter-engine:${junitVersion}" +} + +test { + useJUnitPlatform() + failFast = false + testLogging { + events 'STARTED', 'PASSED', 'FAILED', 'SKIPPED' + showStandardStreams = true + } + afterSuite { desc, result -> + if (!desc.parent) { + println "\nTest result: ${result.resultType}" + println "Test summary: ${result.testCount} tests, " + + "${result.successfulTestCount} succeeded, " + + "${result.failedTestCount} failed, " + + "${result.skippedTestCount} skipped" + } + } +} diff --git a/gradle/wrapper/gradle-wrapper.jar b/gradle/wrapper/gradle-wrapper.jar new file mode 100644 index 0000000000000000000000000000000000000000..e708b1c023ec8b20f512888fe07c5bd3ff77bb8f GIT binary patch literal 59203 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

K7UpUK$>l@ zw`1S}(D?mUXu_C{wupRS-jiX~w=Uqqhf|Vb3Cm9L=T+w91Cu^ z*&Ty%sN?x*h~mJc4g~k{xD4ZmF%FXZNC;oVDwLZ_WvrnzY|{v8hc1nmx4^}Z;yriXsAf+Lp+OFLbR!&Ox?xABwl zu8w&|5pCxmu#$?Cv2_-Vghl2LZ6m7}VLEfR5o2Ou$x02uA-%QB2$c(c1rH3R9hesc zfpn#oqpbKuVsdfV#cv@5pV4^f_!WS+F>SV6N0JQ9E!T90EX((_{bSSFv9ld%I0&}9 zH&Jd4MEX1e0iqDtq~h?DBrxQX1iI0lIs<|kB$Yrh&cpeK0-^K%=FBsCBT46@h#yi!AyDq1V(#V}^;{{V*@T4WJ&U-NTq43w=|K>z8%pr_nC>%C(Wa_l78Ufib$r8Od)IIN=u>417 z`Hl{9A$mI5A(;+-Q&$F&h-@;NR>Z<2U;Y21>>Z;s@0V@SbkMQQj%_;~+qTuQ?c|AV zcWm3XZQHhP&R%QWarS%mJ!9R^&!_)*s(v+VR@I#QrAT}`17Y+l<`b-nvmDNW`De%y zrwTZ9EJrj1AFA>B`1jYDow}~*dfPs}IZMO3=a{Fy#IOILc8F0;JS4x(k-NSpbN@qM z`@aE_e}5{!$v3+qVs7u?sOV(y@1Os*Fgu`fCW9=G@F_#VQ%xf$hj0~wnnP0$hFI+@ zkQj~v#V>xn)u??YutKsX>pxKCl^p!C-o?+9;!Nug^ z{rP!|+KsP5%uF;ZCa5F;O^9TGac=M|=V z_H(PfkV1rz4jl?gJ(ArXMyWT4y(86d3`$iI4^l9`vLdZkzpznSd5Ikfrs8qcSy&>z zTIZgWZGXw0n9ibQxYWE@gI0(3#KA-dAdPcsL_|hg2@~C!VZDM}5;v_Nykfq!*@*Zf zE_wVgx82GMDryKO{U{D>vSzSc%B~|cjDQrt5BN=Ugpsf8H8f1lR4SGo#hCuXPL;QQ z#~b?C4MoepT3X`qdW2dNn& zo8)K}%Lpu>0tQei+{>*VGErz|qjbK#9 zvtd8rcHplw%YyQCKR{kyo6fgg!)6tHUYT(L>B7er5)41iG`j$qe*kSh$fY!PehLcD zWeKZHn<492B34*JUQh=CY1R~jT9Jt=k=jCU2=SL&&y5QI2uAG2?L8qd2U(^AW#{(x zThSy=C#>k+QMo^7caQcpU?Qn}j-`s?1vXuzG#j8(A+RUAY})F@=r&F(8nI&HspAy4 z4>(M>hI9c7?DCW8rw6|23?qQMSq?*Vx?v30U%luBo)B-k2mkL)Ljk5xUha3pK>EEj z@(;tH|M@xkuN?gsz;*bygizwYR!6=(Xgcg^>WlGtRYCozY<rFX2E>kaZo)O<^J7a`MX8Pf`gBd4vrtD|qKn&B)C&wp0O-x*@-|m*0egT=-t@%dD zgP2D+#WPptnc;_ugD6%zN}Z+X4=c61XNLb7L1gWd8;NHrBXwJ7s0ce#lWnnFUMTR& z1_R9Fin4!d17d4jpKcfh?MKRxxQk$@)*hradH2$3)nyXep5Z;B z?yX+-Bd=TqO2!11?MDtG0n(*T^!CIiF@ZQymqq1wPM_X$Iu9-P=^}v7npvvPBu!d$ z7K?@CsA8H38+zjA@{;{kG)#AHME>Ix<711_iQ@WWMObXyVO)a&^qE1GqpP47Q|_AG zP`(AD&r!V^MXQ^e+*n5~Lp9!B+#y3#f8J^5!iC@3Y@P`;FoUH{G*pj*q7MVV)29+j z>BC`a|1@U_v%%o9VH_HsSnM`jZ-&CDvbiqDg)tQEnV>b%Ptm)T|1?TrpIl)Y$LnG_ zzKi5j2Fx^K^PG1=*?GhK;$(UCF-tM~^=Z*+Wp{FSuy7iHt9#4n(sUuHK??@v+6*|10Csdnyg9hAsC5_OrSL;jVkLlf zHXIPukLqbhs~-*oa^gqgvtpgTk_7GypwH><53riYYL*M=Q@F-yEPLqQ&1Sc zZB%w}T~RO|#jFjMWcKMZccxm-SL)s_ig?OC?y_~gLFj{n8D$J_Kw%{r0oB8?@dWzn zB528d-wUBQzrrSSLq?fR!K%59Zv9J4yCQhhDGwhptpA5O5U?Hjqt>8nOD zi{)0CI|&Gu%zunGI*XFZh(ix)q${jT8wnnzbBMPYVJc4HX*9d^mz|21$=R$J$(y7V zo0dxdbX3N#=F$zjstTf*t8vL)2*{XH!+<2IJ1VVFa67|{?LP&P41h$2i2;?N~RA30LV`BsUcj zfO9#Pg1$t}7zpv#&)8`mis3~o+P(DxOMgz-V*(?wWaxi?R=NhtW}<#^Z?(BhSwyar zG|A#Q7wh4OfK<|DAcl9THc-W4*>J4nTevsD%dkj`U~wSUCh15?_N@uMdF^Kw+{agk zJ`im^wDqj`Ev)W3k3stasP`88-M0ZBs7;B6{-tSm3>I@_e-QfT?7|n0D~0RRqDb^G zyHb=is;IwuQ&ITzL4KsP@Z`b$d%B0Wuhioo1CWttW8yhsER1ZUZzA{F*K=wmi-sb#Ju+j z-l@In^IKnb{bQG}Ps>+Vu_W#grNKNGto+yjA)?>0?~X`4I3T@5G1)RqGUZuP^NJCq&^HykuYtMDD8qq+l8RcZNJsvN(10{ zQ1$XcGt}QH-U^WU!-wRR1d--{B$%vY{JLWIV%P4-KQuxxDeJaF#{eu&&r!3Qu{w}0f--8^H|KwE>)ORrcR+2Qf zb})DRcH>k0zWK8@{RX}NYvTF;E~phK{+F;MkIP$)T$93Ba2R2TvKc>`D??#mv9wg$ zd~|-`Qx5LwwsZ2hb*Rt4S9dsF%Cny5<1fscy~)d;0m2r$f=83<->c~!GNyb!U)PA; zq^!`@@)UaG)Ew(9V?5ZBq#c%dCWZrplmuM`o~TyHjAIMh0*#1{B>K4po-dx$Tk-Cq z=WZDkP5x2W&Os`N8KiYHRH#UY*n|nvd(U>yO=MFI-2BEp?x@=N<~CbLJBf6P)}vLS?xJXYJ2^<3KJUdrwKnJnTp{ zjIi|R=L7rn9b*D#Xxr4*R<3T5AuOS+#U8hNlfo&^9JO{VbH!v9^JbK=TCGR-5EWR@ zN8T-_I|&@A}(hKeL4_*eb!1G8p~&_Im8|wc>Cdir+gg90n1dw?QaXcx6Op_W1r=axRw>4;rM*UOpT#Eb9xU1IiWo@h?|5uP zka>-XW0Ikp@dIe;MN8B01a7+5V@h3WN{J=HJ*pe0uwQ3S&MyWFni47X32Q7SyCTNQ z+sR!_9IZa5!>f&V$`q!%H8ci!a|RMx5}5MA_kr+bhtQy{-^)(hCVa@I!^TV4RBi zAFa!Nsi3y37I5EK;0cqu|9MRj<^r&h1lF}u0KpKQD^5Y+LvFEwM zLU@@v4_Na#Axy6tn3P%sD^5P#<7F;sd$f4a7LBMk zGU^RZHBcxSA%kCx*eH&wgA?Qwazm8>9SCSz_!;MqY-QX<1@p$*T8lc?@`ikEqJ>#w zcG``^CoFMAhdEXT9qt47g0IZkaU)4R7wkGs^Ax}usqJ5HfDYAV$!=6?>J6+Ha1I<5 z|6=9soU4>E))tW$<#>F ziZ$6>KJf0bPfbx_)7-}tMINlc=}|H+$uX)mhC6-Hz+XZxsKd^b?RFB6et}O#+>Wmw9Ec9) z{q}XFWp{3@qmyK*Jvzpyqv57LIR;hPXKsrh{G?&dRjF%Zt5&m20Ll?OyfUYC3WRn{cgQ?^V~UAv+5 z&_m#&nIwffgX1*Z2#5^Kl4DbE#NrD&Hi4|7SPqZ}(>_+JMz=s|k77aEL}<=0Zfb)a z%F(*L3zCA<=xO)2U3B|pcTqDbBoFp>QyAEU(jMu8(jLA61-H!ucI804+B!$E^cQQa z)_ERrW3g!B9iLb3nn3dlkvD7KsY?sRvls3QC0qPi>o<)GHx%4Xb$5a3GBTJ(k@`e@ z$RUa^%S15^1oLEmA=sayrP5;9qtf!Z1*?e$ORVPsXpL{jL<6E)0sj&swP3}NPmR%FM?O>SQgN5XfHE< zo(4#Cv11(%Nnw_{_Ro}r6=gKd{k?NebJ~<~Kv0r(r0qe4n3LFx$5%x(BKvrz$m?LG zjLIc;hbj0FMdb9aH9Lpsof#yG$(0sG2%RL;d(n>;#jb!R_+dad+K;Ccw!|RY?uS(a zj~?=&M!4C(5LnlH6k%aYvz@7?xRa^2gml%vn&eKl$R_lJ+e|xsNfXzr#xuh(>`}9g zLHSyiFwK^-p!;p$yt7$F|3*IfO3Mlu9e>Dpx8O`37?fA`cj`C0B-m9uRhJjs^mRp# zWB;Aj6|G^1V6`jg7#7V9UFvnB4((nIwG?k%c7h`?0tS8J3Bn0t#pb#SA}N-|45$-j z$R>%7cc2ebAClXc(&0UtHX<>pd)akR3Kx_cK+n<}FhzmTx!8e9^u2e4%x{>T6pQ`6 zO182bh$-W5A3^wos0SV_TgPmF4WUP-+D25KjbC{y_6W_9I2_vNKwU(^qSdn&>^=*t z&uvp*@c8#2*paD!ZMCi3;K{Na;I4Q35zw$YrW5U@Kk~)&rw;G?d7Q&c9|x<Hg|CNMsxovmfth*|E*GHezPTWa^Hd^F4!B3sF;)? z(NaPyAhocu1jUe(!5Cy|dh|W2=!@fNmuNOzxi^tE_jAtzNJ0JR-avc_H|ve#KO}#S z#a(8secu|^Tx553d4r@3#6^MHbH)vmiBpn0X^29xEv!Vuh1n(Sr5I0V&`jA2;WS|Y zbf0e}X|)wA-Pf5gBZ>r4YX3Mav1kKY(ulAJ0Q*jB)YhviHK)w!TJsi3^dMa$L@^{` z_De`fF4;M87vM3Ph9SzCoCi$#Fsd38u!^0#*sPful^p5oI(xGU?yeYjn;Hq1!wzFk zG&2w}W3`AX4bxoVm03y>ts{KaDf!}b&7$(P4KAMP=vK5?1In^-YYNtx1f#}+2QK@h zeSeAI@E6Z8a?)>sZ`fbq9_snl6LCu6g>o)rO;ijp3|$vig+4t} zylEo7$SEW<_U+qgVcaVhk+4k+C9THI5V10qV*dOV6pPtAI$)QN{!JRBKh-D zk2^{j@bZ}yqW?<#VVuI_27*cI-V~sJiqQv&m07+10XF+#ZnIJdr8t`9s_EE;T2V;B z4UnQUH9EdX%zwh-5&wflY#ve!IWt0UE-My3?L#^Bh%kcgP1q{&26eXLn zTkjJ*w+(|_>Pq0v8{%nX$QZbf)tbJaLY$03;MO=Ic-uqYUmUCuXD>J>o6BCRF=xa% z3R4SK9#t1!K4I_d>tZgE>&+kZ?Q}1qo4&h%U$GfY058s%*=!kac{0Z+4Hwm!)pFLR zJ+5*OpgWUrm0FPI2ib4NPJ+Sk07j(`diti^i#kh&f}i>P4~|d?RFb#!JN)~D@)beox}bw?4VCf^y*`2{4`-@%SFTry2h z>9VBc9#JxEs1+0i2^LR@B1J`B9Ac=#FW=(?2;5;#U$0E0UNag_!jY$&2diQk_n)bT zl5Me_SUvqUjwCqmVcyb`igygB_4YUB*m$h5oeKv3uIF0sk}~es!{D>4r%PC*F~FN3owq5e0|YeUTSG#Vq%&Gk7uwW z0lDo#_wvflqHeRm*}l?}o;EILszBt|EW*zNPmq#?4A+&i0xx^?9obLyY4xx=Y9&^G;xYXYPxG)DOpPg!i_Ccl#3L}6xAAZzNhPK1XaC_~ z!A|mlo?Be*8Nn=a+FhgpOj@G7yYs(Qk(8&|h@_>w8Y^r&5nCqe0V60rRz?b5%J;GYeBqSAjo|K692GxD4` zRZyM2FdI+-jK2}WAZTZ()w_)V{n5tEb@>+JYluDozCb$fA4H)$bzg(Ux{*hXurjO^ zwAxc+UXu=&JV*E59}h3kzQPG4M)X8E*}#_&}w*KEgtX)cU{vm9b$atHa;s>| z+L6&cn8xUL*OSjx4YGjf6{Eq+Q3{!ZyhrL&^6Vz@jGbI%cAM9GkmFlamTbcQGvOlL zmJ?(FI)c86=JEs|*;?h~o)88>12nXlpMR4@yh%qdwFNpct;vMlc=;{FSo*apJ;p}! zAX~t;3tb~VuP|ZW;z$=IHf->F@Ml)&-&Bnb{iQyE#;GZ@C$PzEf6~q}4D>9jic@mTO5x76ulDz@+XAcm35!VSu zT*Gs>;f0b2TNpjU_BjHZ&S6Sqk6V1370+!eppV2H+FY!q*n=GHQ!9Rn6MjY!Jc77A zG7Y!lFp8?TIHN!LXO?gCnsYM-gQxsm=Ek**VmZu7vnuufD7K~GIxfxbsQ@qv2T zPa`tvHB$fFCyZl>3oYg?_wW)C>^_iDOc^B7klnTOoytQH18WkOk)L2BSD0r%xgRSW zQS9elF^?O=_@|58zKLK;(f77l-Zzu}4{fXed2saq!5k#UZAoDBqYQS{sn@j@Vtp|$ zG%gnZ$U|9@u#w1@11Sjl8ze^Co=)7yS(}=;68a3~g;NDe_X^}yJj;~s8xq9ahQ5_r zxAlTMnep*)w1e(TG%tWsjo3RR;yVGPEO4V{Zp?=a_0R#=V^ioQu4YL=BO4r0$$XTX zZfnw#_$V}sDAIDrezGQ+h?q24St0QNug_?{s-pI(^jg`#JRxM1YBV;a@@JQvH8*>> zIJvku74E0NlXkYe_624>znU0J@L<-c=G#F3k4A_)*;ky!C(^uZfj%WB3-*{*B$?9+ zDm$WFp=0(xnt6`vDQV3Jl5f&R(Mp};;q8d3I%Kn>Kx=^;uSVCw0L=gw53%Bp==8Sw zxtx=cs!^-_+i{2OK`Q;913+AXc_&Z5$@z3<)So0CU3;JAv=H?@Zpi~riQ{z-zLtVL z!oF<}@IgJp)Iyz1zVJ42!SPHSkjYNS4%ulVVIXdRuiZ@5Mx8LJS}J#qD^Zi_xQ@>DKDr-_e#>5h3dtje*NcwH_h;i{Sx7}dkdpuW z(yUCjckQsagv*QGMSi9u1`Z|V^}Wjf7B@q%j2DQXyd0nOyqg%m{CK_lAoKlJ7#8M} z%IvR?Vh$6aDWK2W!=i?*<77q&B8O&3?zP(Cs@kapc)&p7En?J;t-TX9abGT#H?TW? ztO5(lPKRuC7fs}zwcUKbRh=7E8wzTsa#Z{a`WR}?UZ%!HohN}d&xJ=JQhpO1PI#>X zHkb>pW04pU%Bj_mf~U}1F1=wxdBZu1790>3Dm44bQ#F=T4V3&HlOLsGH)+AK$cHk6 zia$=$kog?)07HCL*PI6}DRhpM^*%I*kHM<#1Se+AQ!!xyhcy6j7`iDX7Z-2i73_n# zas*?7LkxS-XSqv;YBa zW_n*32D(HTYQ0$feV_Fru1ZxW0g&iwqixPX3=9t4o)o|kOo79V$?$uh?#8Q8e>4e)V6;_(x&ViUVxma+i25qea;d-oK7ouuDsB^ab{ zu1qjQ%`n56VtxBE#0qAzb7lph`Eb-}TYpXB!H-}3Ykqyp`otprp7{VEuW*^IR2n$Fb99*nAtqT&oOFIf z@w*6>YvOGw@Ja?Pp1=whZqydzx@9X4n^2!n83C5{C?G@|E?&$?p*g68)kNvUTJ)I6 z1Q|(#UuP6pj78GUxq11m-GSszc+)X{C2eo-?8ud9sB=3(D47v?`JAa{V(IF zPZQ_0AY*9M97>Jf<o%#O_%Wq}8>YM=q0|tGY+hlXcpE=Z4Od z`NT7Hu2hnvRoqOw@g1f=bv`+nba{GwA$Ak0INlqI1k<9!x_!sL()h?hEWoWrdU3w` zZ%%)VR+Bc@_v!C#koM1p-3v_^L6)_Ktj4HE>aUh%2XZE@JFMOn)J~c`_7VWNb9c-N z2b|SZMR4Z@E7j&q&9(6H3yjEu6HV7{2!1t0lgizD;mZ9$r(r7W5G$ky@w(T_dFnOD z*p#+z$@pKE+>o@%eT(2-p_C}wbQ5s(%Sn_{$HDN@MB+Ev?t@3dPy`%TZ!z}AThZSu zN<1i$siJhXFdjV zP*y|V<`V8t=h#XTRUR~5`c`Z9^-`*BZf?WAehGdg)E2Je)hqFa!k{V(u+(hTf^Yq& zoruUh2(^3pe)2{bvt4&4Y9CY3js)PUHtd4rVG57}uFJL)D(JfSIo^{P=7liFXG zq5yqgof0V8paQcP!gy+;^pp-DA5pj=gbMN0eW=-eY+N8~y+G>t+x}oa!5r>tW$xhI zPQSv=pi;~653Gvf6~*JcQ%t1xOrH2l3Zy@8AoJ+wz@daW@m7?%LXkr!bw9GY@ns3e zSfuWF_gkWnesv?s3I`@}NgE2xwgs&rj?kH-FEy82=O8`+szN ziHch`vvS`zNfap14!&#i9H@wF7}yIPm=UB%(o(}F{wsZ(wA0nJ2aD^@B41>>o-_U6 zUqD~vdo48S8~FTb^+%#zcbQiiYoDKYcj&$#^;Smmb+Ljp(L=1Kt_J!;0s%1|JK}Wi z;={~oL!foo5n8=}rs6MmUW~R&;SIJO3TL4Ky?kh+b2rT9B1Jl4>#Uh-Bec z`Hsp<==#UEW6pGPhNk8H!!DUQR~#F9jEMI6T*OWfN^Ze&X(4nV$wa8QUJ>oTkruH# zm~O<`J7Wxseo@FqaZMl#Y(mrFW9AHM9Kb|XBMqaZ2a)DvJgYipkDD_VUF_PKd~dT7 z#02}bBfPn9a!X!O#83=lbJSK#E}K&yx-HI#T6ua)6o0{|={*HFusCkHzs|Fn&|C3H zBck1cmfcWVUN&i>X$YU^Sn6k2H;r3zuXbJFz)r5~3$d$tUj(l1?o={MM){kjgqXRO zc5R*#{;V7AQh|G|)jLM@wGAK&rm2~@{Pewv#06pHbKn#wL0P6F1!^qw9g&cW3Z=9} zj)POhOlwsh@eF=>z?#sIs*C-Nl(yU!#DaiaxhEs#iJqQ8w%(?+6lU02MYSeDkr!B- zPjMv+on6OLXgGnAtl(ao>|X2Y8*Hb}GRW5}-IzXnoo-d0!m4Vy$GS!XOLy>3_+UGs z2D|YcQx@M#M|}TDOetGi{9lGo9m-=0-^+nKE^*?$^uHkxZh}I{#UTQd;X!L+W@jm( zDg@N4+lUqI92o_rNk{3P>1gxAL=&O;x)ZT=q1mk0kLlE$WeWuY_$0`0jY-Kkt zP*|m3AF}Ubd=`<>(Xg0har*_@x2YH}bn0Wk*OZz3*e5;Zc;2uBdnl8?&XjupbkOeNZsNh6pvsq_ydmJI+*z**{I{0K)-;p1~k8cpJXL$^t!-`E}=*4G^-E8>H!LjTPxSx zcF+cS`ommfKMhNSbas^@YbTpH1*RFrBuATUR zt{oFWSk^$xU&kbFQ;MCX22RAN5F6eq9UfR$ut`Jw--p2YX)A*J69m^!oYfj2y7NYcH6&r+0~_sH^c^nzeN1AU4Ga7=FlR{S|Mm~MpzY0$Z+p2W(a={b-pR9EO1Rs zB%KY|@wLcAA@)KXi!d2_BxrkhDn`DT1=Dec}V!okd{$+wK z4E{n8R*xKyci1(CnNdhf$Dp2(Jpof0-0%-38X=Dd9PQgT+w%Lshx9+loPS~MOm%ZT zt%2B2iL_KU_ita%N>xjB!#71_3=3c}o zgeW~^U_ZTJQ2!PqXulQd=3b=XOQhwATK$y(9$#1jOQ4}4?~l#&nek)H(04f(Sr=s| zWv7Lu1=%WGk4FSw^;;!8&YPM)pQDCY9DhU`hMty1@sq1=Tj7bFsOOBZOFlpR`W>-J$-(kezWJj;`?x-v>ev{*8V z8p|KXJPV$HyQr1A(9LVrM47u-XpcrIyO`yWvx1pVYc&?154aneRpLqgx)EMvRaa#|9?Wwqs2+W8n5~79G z(}iCiLk;?enn}ew`HzhG+tu+Ru@T+K5juvZN)wY;x6HjvqD!&!)$$;1VAh~7fg0K| zEha#aN=Yv|3^~YFH}cc38ovVb%L|g@9W6fo(JtT6$fa?zf@Ct88e}m?i)b*Jgc{fl zExfdvw-BYDmH6>(4QMt#p0;FUIQqkhD}aH?a7)_%JtA~soqj{ppP_82yi9kaxuK>~ ze_)Zt>1?q=ZH*kF{1iq9sr*tVuy=u>Zev}!gEZx@O6-fjyu9X00gpIl-fS_pzjpqJ z1yqBmf9NF!jaF<+YxgH6oXBdK)sH(>VZ)1siyA$P<#KDt;8NT*l_0{xit~5j1P)FN zI8hhYKhQ)i z37^aP13B~u65?sg+_@2Kr^iWHN=U;EDSZ@2W2!5ALhGNWXnFBY%7W?1 z=HI9JzQ-pLKZDYTv<0-lt|6c-RwhxZ)mU2Os{bsX_i^@*fKUj8*aDO5pks=qn3Dv6 zwggpKLuyRCTVPwmw1r}B#AS}?X7b837UlXwp~E2|PJw2SGVueL7){Y&z!jL!XN=0i zU^Eig`S2`{+gU$68aRdWx?BZ{sU_f=8sn~>s~M?GU~`fH5kCc; z8ICp+INM3(3{#k32RZdv6b9MQYdZXNuk7ed8;G?S2nT+NZBG=Tar^KFl2SvhW$bGW#kdWL-I)s_IqVnCDDM9fm8g;P;8 z7t4yZn3^*NQfx7SwmkzP$=fwdC}bafQSEF@pd&P8@H#`swGy_rz;Z?Ty5mkS%>m#% zp_!m9e<()sfKiY(nF<1zBz&&`ZlJf6QLvLhl`_``%RW&{+O>Xhp;lwSsyRqGf=RWd zpftiR`={2(siiPAS|p}@q=NhVc0ELprt%=fMXO3B)4ryC2LT(o=sLM7hJC!}T1@)E zA3^J$3&1*M6Xq>03FX`R&w*NkrZE?FwU+Muut;>qNhj@bX17ZJxnOlPSZ=Zeiz~T_ zOu#yc3t6ONHB;?|r4w+pI)~KGN;HOGC)txxiUN8#mexj+W(cz%9a4sx|IRG=}ia zuEBuba3AHsV2feqw-3MvuL`I+2|`Ud4~7ZkN=JZ;L20|Oxna5vx1qbIh#k2O4$RQF zo`tL()zxaqibg^GbB+BS5#U{@K;WWQj~GcB1zb}zJkPwH|5hZ9iH2308!>_;%msji zJHSL~s)YHBR=Koa1mLEOHos*`gp=s8KA-C zu0aE+W!#iJ*0xqKm3A`fUGy#O+X+5W36myS>Uh2!R*s$aCU^`K&KKLCCDkejX2p=5 z%o7-fl03x`gaSNyr?3_JLv?2RLS3F*8ub>Jd@^Cc17)v8vYEK4aqo?OS@W9mt%ITJ z9=S2%R8M){CugT@k~~0x`}Vl!svYqX=E)c_oU6o}#Hb^%G1l3BudxA{F*tbjG;W_>=xV73pKY53v%>I)@D36I_@&p$h|Aw zonQS`07z_F#@T-%@-Tb|)7;;anoD_WH>9ewFy(ZcEOM$#Y)8>qi7rCnsH9GO-_7zF zu*C87{Df1P4TEOsnzZ@H%&lvV(3V@;Q!%+OYRp`g05PjY^gL$^$-t0Y>H*CDDs?FZly*oZ&dxvsxaUWF!{em4{A>n@vpXg$dwvt@_rgmHF z-MER`ABa8R-t_H*kv>}CzOpz;!>p^^9ztHMsHL|SRnS<-y5Z*r(_}c4=fXF`l^-i}>e7v!qs_jv zqvWhX^F=2sDNWA9c@P0?lUlr6ecrTKM%pNQ^?*Lq?p-0~?_j50xV%^(+H>sMul#Tw zeciF*1=?a7cI(}352%>LO96pD+?9!fNyl^9v3^v&Y4L)mNGK0FN43&Xf8jUlxW1Bw zyiu2;qW-aGNhs=zbuoxnxiwZ3{PFZM#Kw)9H@(hgX23h(`Wm~m4&TvoZoYp{plb^> z_#?vXcxd>r7K+1HKJvhed>gtK`TAbJUazUWQY6T~t2af%#<+Veyr%7-#*A#@&*;@g58{i|E%6yC_InGXCOd{L0;$)z#?n7M`re zh!kO{6=>7I?*}czyF7_frt#)s1CFJ_XE&VrDA?Dp3XbvF{qsEJgb&OLSNz_5g?HpK z9)8rsr4JN!Af3G9!#Qn(6zaUDqLN(g2g8*M)Djap?WMK9NKlkC)E2|-g|#-rp%!Gz zAHd%`iq|81efi93m3yTBw3g0j#;Yb2X{mhRAI?&KDmbGqou(2xiRNb^sV}%%Wu0?< z?($L>(#BO*)^)rSgyNRni$i`R4v;GhlCZ8$@e^ROX(p=2_v6Y!%^As zu022)fHdv_-~Yu_H6WVPLpHQx!W%^6j)cBhS`O3QBW#x(eX54d&I22op(N59b*&$v zFiSRY6rOc^(dgSV1>a7-5C;(5S5MvKcM2Jm-LD9TGqDpP097%52V+0>Xqq!! zq4e3vj53SE6i8J`XcQB|MZPP8j;PAOnpGnllH6#Ku~vS42xP*Nz@~y%db7Xi8s09P z1)e%8ys6&M8D=Dt6&t`iKG_4X=!kgRQoh%Z`dc&mlOUqXk-k`jKv9@(a^2-Upw>?< zt5*^DV~6Zedbec4NVl($2T{&b)zA@b#dUyd>`2JC0=xa_fIm8{5um zr-!ApXZhC8@=vC2WyxO|!@0Km)h8ep*`^he92$@YwP>VcdoS5OC^s38e#7RPsg4j+ zbVGG}WRSET&ZfrcR(x~k8n1rTP%CnfUNKUonD$P?FtNFF#cn!wEIab-;jU=B1dHK@ z(;(yAQJ`O$sMn>h;pf^8{JISW%d+@v6@CnXh9n5TXGC}?FI9i-D0OMaIg&mAg=0Kn zNJ7oz5*ReJukD55fUsMuaP+H4tDN&V9zfqF@ zr=#ecUk9wu{0;!+gl;3Bw=Vn^)z$ahVhhw)io!na&9}LmWurLb0zubxK=UEnU*{5P z+SP}&*(iBKSO4{alBHaY^)5Q=mZ+2OwIooJ7*Q5XJ+2|q`9#f?6myq!&oz?klihLq z4C)$XP!BNS0G_Z1&TM>?Jk{S~{F3n83ioli=IO6f%wkvCl(RFFw~j0tb{GvXTx>*sB0McY0s&SNvj4+^h`9nJ_wM>F!Uc>X}9PifQekn0sKI2SAJP!a4h z5cyGTuCj3ZBM^&{dRelIlT^9zcfaAuL5Y~bl!ppSf`wZbK$z#6U~rdclk``e+!qhe z6Qspo*%<)eu6?C;Bp<^VuW6JI|Ncvyn+LlSl;Mp22Bl7ARQ0Xc24%29(ZrdsIPw&-=yHQ7_Vle|5h>AST0 zUGX2Zk34vp?U~IHT|;$U86T+UUHl_NE4m|}>E~6q``7hccCaT^#y+?wD##Q%HwPd8 zV3x4L4|qqu`B$4(LXqDJngNy-{&@aFBvVsywt@X^}iH7P%>bR?ciC$I^U-4Foa`YKI^qDyGK7k%E%c_P=yzAi`YnxGA%DeNd++j3*h^ z=rn>oBd0|~lZ<6YvmkKY*ZJlJ;Im0tqgWu&E92eqt;+NYdxx`eS(4Hw_Jb5|yVvBg z*tbdY^!AN;luEyN4VRhS@-_DC{({ziH{&Z}iGElSV~qvT>L-8G%+yEL zX#MFOhj{InyKG=mvW-<1B@c-}x$vA(nU?>S>0*eN#!SLzQ)Ex7fvQ)S4D<8|I#N$3 zT5Ei`Z?cxBODHX8(Xp73v`IsAYC@9b;t}z0wxVuQSY1J^GRwDPN@qbM-ZF48T$GZ< z8WU+;Pqo?{ghI-KZ-i*ydXu`Ep0Xw^McH_KE9J0S7G;x8Fe`DVG?j3Pv=0YzJ}yZR z%2=oqHiUjvuk0~Ca>Kol4CFi0_xQT~;_F?=u+!kIDl-9g`#ZNZ9HCy17Ga1v^Jv9# z{T4Kb1-AzUxq*MutfOWWZgD*HnFfyYg0&e9f(5tZ>krPF6{VikNeHoc{linPPt#Si z&*g>(c54V8rT_AX!J&bNm-!umPvOR}vDai#`CX___J#=zeB*{4<&2WpaDncZsOkp* zsg<%@@rbrMkR_ux9?LsQxzoBa1s%$BBn6vk#{&&zUwcfzeCBJUwFYSF$08qDsB;gWQN*g!p8pxjofWbqNSZOEKOaTx@+* zwdt5*Q47@EOZ~EZL9s?1o?A%9TJT=Ob_13yyugvPg*e&ZU(r6^k4=2+D-@n=Hv5vu zSXG|hM(>h9^zn=eQ=$6`JO&70&2|%V5Lsx>)(%#;pcOfu>*nk_3HB_BNaH$`jM<^S zcSftDU1?nL;jy)+sfonQN}(}gUW?d_ikr*3=^{G)=tjBtEPe>TO|0ddVB zTklrSHiW+!#26frPXQQ(YN8DG$PZo?(po(QUCCf_OJC`pw*uey00%gmH!`WJkrKXj2!#6?`T25mTu9OJp2L8z3! z=arrL$ZqxuE{%yV)14Kd>k}j7pxZ6#$Dz8$@WV5p8kTqN<-7W)Q7Gt2{KoOPK_tZ| zf2WG~O5@{qPI+W<4f_;reuFVdO^5`ADC1!JQE|N`s3cq@(0WB!n0uh@*c{=LAd;~} zyGK@hbF-Oo+!nN)@i*O(`@FA#u?o=~e{`4O#5}z&=UkU*50fOrzi11D^&FOqe>wii z?*k+2|EcUs;Gx{!@KBT~>PAwLrIDT7Th=Utu?~?np@t^gFs?zgX=D${RwOY^WGh-+ z+#4$066ISh8eYW#FXWp~S`<*%O^ZuItL1Tyqt8#tZ zY120E;^VG`!lZn&3sPd$RkdHpU#|w+bYV)pJC|SH9g%|5IkxVTQcBA4CL0}$&}ef@ zW^Vtj%M;;_1xxP9x#ex17&4N*{ksO*_4O}xYu(p*JkL#yr}@7b)t5X?%CY<+s5_MJ zuiqt+N_;A(_)%lumoyRFixWa-M7qK_9s6<1X?JDa9fP!+_6u~~M$5L=ipB=7(j#f< zZ34J%=bs549%~_mA(|={uZNs_0?o7;-LBP(ZRnkd{-^|2|=4vUTmtByHL8 zEph`(LSEzQj68a+`d$V<45J7cyv^#|^|%fD#si1Nx!4NW*`l*{->HEWNh6-|g>-=r zXmQ|-i}Ku$ndUeHQ^&ieT!Lf}vf6GaqW9$DJ2NWrqwPY%%4nip$@vK$nRp*_C-v<| zuKz~ZyN&<%!NS26&x?jhy+@awJipMQ-8(X4#Ae5??U<1QMt1l9R=w9fAnEF}NYu$2 z>6}Vkc zIb*A?G*z8^IvibmBKn_u^5&T_1oey0gZS2~obf(#xk=erZGTEdQnt3DMGM+0oPwss zj5zXD;(oWhB_T@~Ig#9@v)AKtXu3>Inmgf@A|-lD-1U>cNyl3h?ADD9)GG4}zUGPk zZzaXe!~Kf?<~@$G?Uql3t8jy9{2!doq4=J}j9ktTxss{p6!9UdjyDERlA*xZ!=Q)KDs5O)phz>Vq3BNGoM(H|=1*Q4$^2fTZw z(%nq1P|5Rt81}SYJpEEzMPl5VJsV5&4e)ZWKDyoZ>1EwpkHx-AQVQc8%JMz;{H~p{=FXV>jIxvm4X*qv52e?Y-f%DJ zxEA165GikEASQ^fH6K#d!Tpu2HP{sFs%E=e$gYd$aj$+xue6N+Wc(rAz~wUsk2`(b z8Kvmyz%bKQxpP}~baG-rwYcYCvkHOi zlkR<=>ZBTU*8RF_d#Bl@zZsRIhx<%~Z@Z=ik z>adw3!DK(8R|q$vy{FTxw%#xliD~6qXmY^7_9kthVPTF~Xy1CfBqbU~?1QmxmU=+k z(ggxvEuA;0e&+ci-zQR{-f7aO{O(Pz_OsEjLh_K>MbvoZ4nxtk5u{g@nPv)cgW_R} z9}EA4K4@z0?7ue}Z(o~R(X&FjejUI2g~08PH1E4w>9o{)S(?1>Z0XMvTb|;&EuyOE zGvWNpYX)Nv<8|a^;1>bh#&znEcl-r!T#pn= z4$?Yudha6F%4b>*8@=BdtXXY4N+`U4Dmx$}>HeVJk-QdTG@t!tVT#0(LeV0gvqyyw z2sEp^9eY0N`u10Tm4n8No&A=)IeEC|gnmEXoNSzu!1<4R<%-9kY_8~5Ej?zRegMn78wuMs#;i&eUA0Zk_RXQ3b&TT} z;SCI=7-FUB@*&;8|n>(_g^HGf3@QODE3LpmX~ELnymQm{Sx9xrKS zK29p~?v@R$0=v6Dr5aW>-!{+h@?Q58|Kz8{{W`%J+lDAdb&M5VHrX_mDY;1-JLnf)ezmPau$)1;=`-FU=-r-83tX=C`S#}GZufju zQ>sXNT0Ny=k@nc%cFnvA_i4SC)?_ORXHq8B4D%el1uPX`c~uG#S1M7C+*MMqLw78E zhY2dI8@+N^qrMI1+;TUda(vGqGSRyU{Fnm`aqrr7bz42c5xsOO-~oZpkzorD1g}Y<6rk&3>PsSGy}W?MtqFky@A(X# zIuNZK0cK?^=;PUAu>j0#HtjbHCV*6?jzA&OoE$*Jlga*}LF`SF?WLhv1O|zqC<>*> zYB;#lsYKx0&kH@BFpW8n*yDcc6?;_zaJs<-jPSkCsSX-!aV=P5kUgF@Nu<{a%#K*F z134Q{9|YX7X(v$62_cY3^G%t~rD>Q0z@)1|zs)vjJ6Jq9;7#Ki`w+eS**En?7;n&7 zu==V3T&eFboN3ZiMx3D8qYc;VjFUk_H-WWCau(VFXSQf~viH0L$gwD$UfFHqNcgN`x}M+YQ6RnN<+@t>JUp#)9YOkqst-Ga?{FsDpEeX0(5v{0J~SEbWiL zXC2}M4?UH@u&|;%0y`eb33ldo4~z-x8zY!oVmV=c+f$m?RfDC35mdQ2E>Pze7KWP- z>!Bh<&57I+O_^s}9Tg^k)h7{xx@0a0IA~GAOt2yy!X%Q$1rt~LbTB6@Du!_0%HV>N zlf)QI1&gvERKwso23mJ!Ou6ZS#zCS5W`gxE5T>C#E|{i<1D35C222I33?Njaz`On7 zi<+VWFP6D{e-{yiN#M|Jgk<44u1TiMI78S5W`Sdb5f+{zu34s{CfWN7a3Cf^@L%!& zN$?|!!9j2c)j$~+R6n#891w-z8(!oBpL2K=+%a$r2|~8-(vQj5_XT`<0Ksf;oP+tz z9CObS!0m)Tgg`K#xBM8B(|Z)Wb&DYL{WTYv`;A=q6~Nnx2+!lTIXtj8J7dZE!P_{z z#f8w6F}^!?^KE#+ZDv+xd5O&3EmomZzsv?>E-~ygGum45fk!SBN&|eo1rKw^?aZJ4 E2O(~oYXATM literal 0 HcmV?d00001 diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties new file mode 100644 index 0000000..33682bb --- /dev/null +++ b/gradle/wrapper/gradle-wrapper.properties @@ -0,0 +1,5 @@ +distributionBase=GRADLE_USER_HOME +distributionPath=wrapper/dists +distributionUrl=https\://services.gradle.org/distributions/gradle-6.6.1-all.zip +zipStoreBase=GRADLE_USER_HOME +zipStorePath=wrapper/dists diff --git a/gradlew b/gradlew new file mode 100755 index 0000000..4f906e0 --- /dev/null +++ b/gradlew @@ -0,0 +1,185 @@ +#!/usr/bin/env sh + +# +# Copyright 2015 the original author or authors. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# https://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +############################################################################## +## +## Gradle start up script for UN*X +## +############################################################################## + +# Attempt to set APP_HOME +# Resolve links: $0 may be a link +PRG="$0" +# Need this for relative symlinks. +while [ -h "$PRG" ] ; do + ls=`ls -ld "$PRG"` + link=`expr "$ls" : '.*-> \(.*\)$'` + if expr "$link" : '/.*' > /dev/null; then + PRG="$link" + else + PRG=`dirname "$PRG"`"/$link" + fi +done +SAVED="`pwd`" +cd "`dirname \"$PRG\"`/" >/dev/null +APP_HOME="`pwd -P`" +cd "$SAVED" >/dev/null + +APP_NAME="Gradle" +APP_BASE_NAME=`basename "$0"` + +# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. +DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"' + +# Use the maximum available, or set MAX_FD != -1 to use that value. +MAX_FD="maximum" + +warn () { + echo "$*" +} + +die () { + echo + echo "$*" + echo + exit 1 +} + +# OS specific support (must be 'true' or 'false'). +cygwin=false +msys=false +darwin=false +nonstop=false +case "`uname`" in + CYGWIN* ) + cygwin=true + ;; + Darwin* ) + darwin=true + ;; + MINGW* ) + msys=true + ;; + NONSTOP* ) + nonstop=true + ;; +esac + +CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar + + +# Determine the Java command to use to start the JVM. +if [ -n "$JAVA_HOME" ] ; then + if [ -x "$JAVA_HOME/jre/sh/java" ] ; then + # IBM's JDK on AIX uses strange locations for the executables + JAVACMD="$JAVA_HOME/jre/sh/java" + else + JAVACMD="$JAVA_HOME/bin/java" + fi + if [ ! -x "$JAVACMD" ] ; then + die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME + +Please set the JAVA_HOME variable in your environment to match the +location of your Java installation." + fi +else + JAVACMD="java" + which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. + +Please set the JAVA_HOME variable in your environment to match the +location of your Java installation." +fi + +# Increase the maximum file descriptors if we can. +if [ "$cygwin" = "false" -a "$darwin" = "false" -a "$nonstop" = "false" ] ; then + MAX_FD_LIMIT=`ulimit -H -n` + if [ $? -eq 0 ] ; then + if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then + MAX_FD="$MAX_FD_LIMIT" + fi + ulimit -n $MAX_FD + if [ $? -ne 0 ] ; then + warn "Could not set maximum file descriptor limit: $MAX_FD" + fi + else + warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT" + fi +fi + +# For Darwin, add options to specify how the application appears in the dock +if $darwin; then + GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\"" +fi + +# For Cygwin or MSYS, switch paths to Windows format before running java +if [ "$cygwin" = "true" -o "$msys" = "true" ] ; then + APP_HOME=`cygpath --path --mixed "$APP_HOME"` + CLASSPATH=`cygpath --path --mixed "$CLASSPATH"` + + JAVACMD=`cygpath --unix "$JAVACMD"` + + # We build the pattern for arguments to be converted via cygpath + ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null` + SEP="" + for dir in $ROOTDIRSRAW ; do + ROOTDIRS="$ROOTDIRS$SEP$dir" + SEP="|" + done + OURCYGPATTERN="(^($ROOTDIRS))" + # Add a user-defined pattern to the cygpath arguments + if [ "$GRADLE_CYGPATTERN" != "" ] ; then + OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)" + fi + # Now convert the arguments - kludge to limit ourselves to /bin/sh + i=0 + for arg in "$@" ; do + CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -` + CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option + + if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition + eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"` + else + eval `echo args$i`="\"$arg\"" + fi + i=`expr $i + 1` + done + case $i in + 0) set -- ;; + 1) set -- "$args0" ;; + 2) set -- "$args0" "$args1" ;; + 3) set -- "$args0" "$args1" "$args2" ;; + 4) set -- "$args0" "$args1" "$args2" "$args3" ;; + 5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;; + 6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;; + 7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;; + 8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;; + 9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;; + esac +fi + +# Escape application args +save () { + for i do printf %s\\n "$i" | sed "s/'/'\\\\''/g;1s/^/'/;\$s/\$/' \\\\/" ; done + echo " " +} +APP_ARGS=`save "$@"` + +# Collect all arguments for the java command, following the shell quoting and substitution rules +eval set -- $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS "\"-Dorg.gradle.appname=$APP_BASE_NAME\"" -classpath "\"$CLASSPATH\"" org.gradle.wrapper.GradleWrapperMain "$APP_ARGS" + +exec "$JAVACMD" "$@" diff --git a/gradlew.bat b/gradlew.bat new file mode 100644 index 0000000..ac1b06f --- /dev/null +++ b/gradlew.bat @@ -0,0 +1,89 @@ +@rem +@rem Copyright 2015 the original author or authors. +@rem +@rem Licensed under the Apache License, Version 2.0 (the "License"); +@rem you may not use this file except in compliance with the License. +@rem You may obtain a copy of the License at +@rem +@rem https://www.apache.org/licenses/LICENSE-2.0 +@rem +@rem Unless required by applicable law or agreed to in writing, software +@rem distributed under the License is distributed on an "AS IS" BASIS, +@rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +@rem See the License for the specific language governing permissions and +@rem limitations under the License. +@rem + +@if "%DEBUG%" == "" @echo off +@rem ########################################################################## +@rem +@rem Gradle startup script for Windows +@rem +@rem ########################################################################## + +@rem Set local scope for the variables with windows NT shell +if "%OS%"=="Windows_NT" setlocal + +set DIRNAME=%~dp0 +if "%DIRNAME%" == "" set DIRNAME=. +set APP_BASE_NAME=%~n0 +set APP_HOME=%DIRNAME% + +@rem Resolve any "." and ".." in APP_HOME to make it shorter. +for %%i in ("%APP_HOME%") do set APP_HOME=%%~fi + +@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. +set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m" + +@rem Find java.exe +if defined JAVA_HOME goto findJavaFromJavaHome + +set JAVA_EXE=java.exe +%JAVA_EXE% -version >NUL 2>&1 +if "%ERRORLEVEL%" == "0" goto execute + +echo. +echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. +echo. +echo Please set the JAVA_HOME variable in your environment to match the +echo location of your Java installation. + +goto fail + +:findJavaFromJavaHome +set JAVA_HOME=%JAVA_HOME:"=% +set JAVA_EXE=%JAVA_HOME%/bin/java.exe + +if exist "%JAVA_EXE%" goto execute + +echo. +echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% +echo. +echo Please set the JAVA_HOME variable in your environment to match the +echo location of your Java installation. + +goto fail + +:execute +@rem Setup the command line + +set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar + + +@rem Execute Gradle +"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %* + +:end +@rem End local scope for the variables with windows NT shell +if "%ERRORLEVEL%"=="0" goto mainEnd + +:fail +rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of +rem the _cmd.exe /c_ return code! +if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1 +exit /b 1 + +:mainEnd +if "%OS%"=="Windows_NT" endlocal + +:omega diff --git a/io-vector/NOTICE.txt b/io-vector/NOTICE.txt new file mode 100644 index 0000000..e729ca7 --- /dev/null +++ b/io-vector/NOTICE.txt @@ -0,0 +1,5 @@ +This work is based on VectorGraphics2D by Erich Seifert + +https://github.com/eseifert/vectorgraphics2d + +Lesser GNU Public License (LGPL 3) diff --git a/io-vector/build/docs/javadoc/allclasses-index.html b/io-vector/build/docs/javadoc/allclasses-index.html new file mode 100644 index 0000000..eb207b4 --- /dev/null +++ b/io-vector/build/docs/javadoc/allclasses-index.html @@ -0,0 +1,372 @@ + + + + + +All Classes (io-vector 3.0.0 API) + + + + + + + + + + + + +

JavaScript is disabled on your browser.
+ +
+ +
+
+
+

All Classes

+
+
+
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
ClassDescription
AbsoluteToRelativeTransformsFilter 
AffineTransformCommand 
AlphaToMaskOp 
ASCII85EncodeStream 
Base64EncodeStream 
Command<T> 
CreateCommand 
DataUtils +
Abstract class that contains utility functions for working with data + collections like maps or lists.
+
DisposeCommand 
DrawImageCommand 
DrawShapeCommand 
DrawStringCommand 
EPSGraphics2D +
Graphics2D implementation that saves all operations to a string + in the Encapsulated PostScript® (EPS) format.
+
EPSProcessor 
EPSProcessorResult 
FillPaintedShapeAsImageFilter 
FillShapeCommand 
Filter 
FlateEncodeStream 
FormattingWriter 
GeneratedPayload 
GraphicsState 
GraphicsUtils +
Abstract class that contains utility functions for working with graphics.
+
Group 
GroupingFilter 
ImageDataStream 
ImageDataStream.Interleaving 
LineWrapOutputStream 
OptimizeFilter 
PageSize 
Payload 
PDFGraphics2D +
Graphics2D implementation that saves all operations to a string + in the Portable Document Format (PDF).
+
PDFObject 
PDFProcessor 
PDFProcessorResult 
Processor 
ProcessorResult 
Resources 
RotateCommand 
ScaleCommand 
SetBackgroundCommand 
SetClipCommand 
SetColorCommand 
SetCompositeCommand 
SetFontCommand 
SetHintCommand 
SetPaintCommand 
SetStrokeCommand 
SetTransformCommand 
SetXORModeCommand 
ShearCommand 
SizePayload 
StateChangeGroupingFilter 
StateCommand<T> 
SVGGraphics2D +
Graphics2D implementation that saves all operations to a string + in the Scaled Vector Graphics (SVG) format.
+
SVGProcessor 
SVGProcessorResult 
TransformCommand 
TranslateCommand 
VectorGraphics2D +
Base for classes that want to implement vector export.
+
VectorGraphicsFormat 
VectorHints 
VectorHints.Key 
VectorHints.Value 
+
+
+
+ +
+
+ + diff --git a/io-vector/build/docs/javadoc/allpackages-index.html b/io-vector/build/docs/javadoc/allpackages-index.html new file mode 100644 index 0000000..1a65515 --- /dev/null +++ b/io-vector/build/docs/javadoc/allpackages-index.html @@ -0,0 +1,120 @@ + + + + + +All Packages (io-vector 3.0.0 API) + + + + + + + + + + + + + +
+ +
+
+
+

All Packages

+
+ +
+ +
+
+ + diff --git a/io-vector/build/docs/javadoc/constant-values.html b/io-vector/build/docs/javadoc/constant-values.html new file mode 100644 index 0000000..c898561 --- /dev/null +++ b/io-vector/build/docs/javadoc/constant-values.html @@ -0,0 +1,111 @@ + + + + + +Constant Field Values (io-vector 3.0.0 API) + + + + + + + + + + + + + +
+ +
+
+
+

Constant Field Values

+
+

Contents

+ +
+
+
+

org.xbib.*

+
    +
  • +
    + + + + + + + + + + + + + + + + +
    org.xbib.graphics.io.vector.util.LineWrapOutputStream
    Modifier and TypeConstant FieldValue
    public static final java.lang.StringSTANDARD_EOL"\r\n"
    +
    +
  • +
+
+
+ +
+
+ + diff --git a/io-vector/build/docs/javadoc/deprecated-list.html b/io-vector/build/docs/javadoc/deprecated-list.html new file mode 100644 index 0000000..3cbf57f --- /dev/null +++ b/io-vector/build/docs/javadoc/deprecated-list.html @@ -0,0 +1,80 @@ + + + + + +Deprecated List (io-vector 3.0.0 API) + + + + + + + + + + + + + +
+ +
+
+
+

Deprecated API

+

Contents

+
+
+ +
+
+ + diff --git a/io-vector/build/docs/javadoc/element-list b/io-vector/build/docs/javadoc/element-list new file mode 100644 index 0000000..a1bcee9 --- /dev/null +++ b/io-vector/build/docs/javadoc/element-list @@ -0,0 +1,8 @@ +module:org.xbib.graphics.io.vector +org.xbib.graphics.io.vector +org.xbib.graphics.io.vector.commands +org.xbib.graphics.io.vector.eps +org.xbib.graphics.io.vector.filters +org.xbib.graphics.io.vector.pdf +org.xbib.graphics.io.vector.svg +org.xbib.graphics.io.vector.util diff --git a/io-vector/build/docs/javadoc/help-doc.html b/io-vector/build/docs/javadoc/help-doc.html new file mode 100644 index 0000000..08411e4 --- /dev/null +++ b/io-vector/build/docs/javadoc/help-doc.html @@ -0,0 +1,185 @@ + + + + + +API Help (io-vector 3.0.0 API) + + + + + + + + + + + + + +
+ +
+
+
+

How This API Document Is Organized

+
This API (Application Programming Interface) document has pages corresponding to the items in the navigation bar, described as follows.
+
+
+

Module

+

Each module has a page that contains a list of its packages, dependencies on other modules, and services, with a summary for each. These pages may contain three categories:

+
    +
  • Packages
  • +
  • Modules
  • +
  • Services
  • +
+
+
+

Package

+

Each package has a page that contains a list of its classes and interfaces, with a summary for each. These pages may contain six categories:

+
    +
  • Interfaces
  • +
  • Classes
  • +
  • Enums
  • +
  • Exceptions
  • +
  • Errors
  • +
  • Annotation Types
  • +
+
+
+

Class or Interface

+

Each class, interface, nested class and nested interface has its own separate page. Each of these pages has three sections consisting of a class/interface description, summary tables, and detailed member descriptions:

+
    +
  • Class Inheritance Diagram
  • +
  • Direct Subclasses
  • +
  • All Known Subinterfaces
  • +
  • All Known Implementing Classes
  • +
  • Class or Interface Declaration
  • +
  • Class or Interface Description
  • +
+
+
    +
  • Nested Class Summary
  • +
  • Field Summary
  • +
  • Property Summary
  • +
  • Constructor Summary
  • +
  • Method Summary
  • +
+
+
    +
  • Field Details
  • +
  • Property Details
  • +
  • Constructor Details
  • +
  • Method Details
  • +
+

The summary entries are alphabetical, while the detailed descriptions are in the order they appear in the source code. This preserves the logical groupings established by the programmer.

+
+
+

Annotation Type

+

Each annotation type has its own separate page with the following sections:

+
    +
  • Annotation Type Declaration
  • +
  • Annotation Type Description
  • +
  • Required Element Summary
  • +
  • Optional Element Summary
  • +
  • Element Details
  • +
+
+
+

Enum

+

Each enum has its own separate page with the following sections:

+
    +
  • Enum Declaration
  • +
  • Enum Description
  • +
  • Enum Constant Summary
  • +
  • Enum Constant Details
  • +
+
+
+

Tree (Class Hierarchy)

+

There is a Class Hierarchy page for all packages, plus a hierarchy for each package. Each hierarchy page contains a list of classes and a list of interfaces. Classes are organized by inheritance structure starting with java.lang.Object. Interfaces do not inherit from java.lang.Object.

+
    +
  • When viewing the Overview page, clicking on "Tree" displays the hierarchy for all packages.
  • +
  • When viewing a particular package, class or interface page, clicking on "Tree" displays the hierarchy for only that package.
  • +
+
+
+

Deprecated API

+

The Deprecated API page lists all of the API that have been deprecated. A deprecated API is not recommended for use, generally due to shortcomings, and a replacement API is usually given. Deprecated APIs may be removed in future implementations.

+
+
+

Index

+

The Index contains an alphabetic index of all classes, interfaces, constructors, methods, and fields, as well as lists of all packages and all classes.

+
+
+

Serialized Form

+

Each serializable or externalizable class has a description of its serialization fields and methods. This information is of interest to those who implement rather than use the API. While there is no link in the navigation bar, you can get to this information by going to any serialized class and clicking "Serialized Form" in the "See Also" section of the class description.

+
+
+

Constant Field Values

+

The Constant Field Values page lists the static final fields and their values.

+
+
+

Search

+

You can search for definitions of modules, packages, types, fields, methods, system properties and other terms defined in the API, using some or all of the name, optionally using "camel-case" abbreviations. For example:

+
    +
  • j.l.obj will match "java.lang.Object"
  • +
  • InpStr will match "java.io.InputStream"
  • +
  • HM.cK will match "java.util.HashMap.containsKey(Object)"
  • +
+

Refer to the Javadoc Search Specification for a full description of search features.

+
+
+This help file applies to API documentation generated by the standard doclet.
+ +
+
+ + diff --git a/io-vector/build/docs/javadoc/index-all.html b/io-vector/build/docs/javadoc/index-all.html new file mode 100644 index 0000000..892b78b --- /dev/null +++ b/io-vector/build/docs/javadoc/index-all.html @@ -0,0 +1,1081 @@ + + + + + +Index (io-vector 3.0.0 API) + + + + + + + + + + + + + +
+ +
+
+
+

Index

+
+A B C D E F G H I J K L M N O P R S T U V W 
All Classes|All Packages +

A

+
+
A3 - Static variable in class org.xbib.graphics.io.vector.PageSize
+
 
+
A4 - Static variable in class org.xbib.graphics.io.vector.PageSize
+
 
+
A5 - Static variable in class org.xbib.graphics.io.vector.PageSize
+
 
+
AbsoluteToRelativeTransformsFilter - Class in org.xbib.graphics.io.vector.filters
+
 
+
AbsoluteToRelativeTransformsFilter(Iterable<Command<?>>) - Constructor for class org.xbib.graphics.io.vector.filters.AbsoluteToRelativeTransformsFilter
+
 
+
add(Command<?>) - Method in class org.xbib.graphics.io.vector.commands.Group
+
 
+
addFilter(Class<? extends FilterOutputStream>) - Method in class org.xbib.graphics.io.vector.pdf.Payload
+
 
+
addRenderingHints(Map<?, ?>) - Method in class org.xbib.graphics.io.vector.VectorGraphics2D
+
 
+
AffineTransformCommand - Class in org.xbib.graphics.io.vector.commands
+
 
+
AffineTransformCommand(AffineTransform) - Constructor for class org.xbib.graphics.io.vector.commands.AffineTransformCommand
+
 
+
ALPHA_ONLY - org.xbib.graphics.io.vector.util.ImageDataStream.Interleaving
+
 
+
AlphaToMaskOp - Class in org.xbib.graphics.io.vector.util
+
 
+
AlphaToMaskOp() - Constructor for class org.xbib.graphics.io.vector.util.AlphaToMaskOp
+
 
+
AlphaToMaskOp(boolean) - Constructor for class org.xbib.graphics.io.vector.util.AlphaToMaskOp
+
 
+
ASCII85EncodeStream - Class in org.xbib.graphics.io.vector.util
+
 
+
ASCII85EncodeStream(OutputStream) - Constructor for class org.xbib.graphics.io.vector.util.ASCII85EncodeStream
+
 
+
ASCII85EncodeStream(OutputStream, String, String) - Constructor for class org.xbib.graphics.io.vector.util.ASCII85EncodeStream
+
 
+
asList(double[]) - Static method in class org.xbib.graphics.io.vector.util.DataUtils
+
+
Converts an array of double numbers to a list of Doubles.
+
+
asList(float[]) - Static method in class org.xbib.graphics.io.vector.util.DataUtils
+
+
Converts an array of float numbers to a list of Floats.
+
+
+

B

+
+
Base64EncodeStream - Class in org.xbib.graphics.io.vector.util
+
 
+
Base64EncodeStream(OutputStream) - Constructor for class org.xbib.graphics.io.vector.util.Base64EncodeStream
+
 
+
+

C

+
+
clearRect(int, int, int, int) - Method in class org.xbib.graphics.io.vector.VectorGraphics2D
+
 
+
clip(Shape) - Method in class org.xbib.graphics.io.vector.VectorGraphics2D
+
 
+
clipRect(int, int, int, int) - Method in class org.xbib.graphics.io.vector.VectorGraphics2D
+
 
+
clone() - Method in class org.xbib.graphics.io.vector.GraphicsState
+
 
+
clone() - Method in class org.xbib.graphics.io.vector.VectorGraphics2D
+
 
+
clone(Shape) - Static method in class org.xbib.graphics.io.vector.util.GraphicsUtils
+
 
+
close() - Method in class org.xbib.graphics.io.vector.eps.EPSProcessorResult
+
 
+
close() - Method in class org.xbib.graphics.io.vector.pdf.Payload
+
 
+
close() - Method in class org.xbib.graphics.io.vector.pdf.PDFProcessorResult
+
 
+
close() - Method in interface org.xbib.graphics.io.vector.ProcessorResult
+
 
+
close() - Method in class org.xbib.graphics.io.vector.svg.SVGProcessorResult
+
 
+
close() - Method in class org.xbib.graphics.io.vector.util.ASCII85EncodeStream
+
 
+
close() - Method in class org.xbib.graphics.io.vector.util.Base64EncodeStream
+
 
+
close() - Method in class org.xbib.graphics.io.vector.util.FormattingWriter
+
 
+
Command<T> - Class in org.xbib.graphics.io.vector
+
 
+
Command(T) - Constructor for class org.xbib.graphics.io.vector.Command
+
 
+
copyArea(int, int, int, int, int, int) - Method in class org.xbib.graphics.io.vector.VectorGraphics2D
+
 
+
create() - Method in class org.xbib.graphics.io.vector.VectorGraphics2D
+
 
+
CreateCommand - Class in org.xbib.graphics.io.vector.commands
+
 
+
CreateCommand(VectorGraphics2D) - Constructor for class org.xbib.graphics.io.vector.commands.CreateCommand
+
 
+
createCompatibleDestImage(BufferedImage, ColorModel) - Method in class org.xbib.graphics.io.vector.util.AlphaToMaskOp
+
 
+
+

D

+
+
DataUtils - Class in org.xbib.graphics.io.vector.util
+
+
Abstract class that contains utility functions for working with data + collections like maps or lists.
+
+
DataUtils() - Constructor for class org.xbib.graphics.io.vector.util.DataUtils
+
+
Default constructor that prevents creation of class.
+
+
DEFAULT_BACKGROUND - Static variable in class org.xbib.graphics.io.vector.GraphicsState
+
+
Default background color.
+
+
DEFAULT_CLIP - Static variable in class org.xbib.graphics.io.vector.GraphicsState
+
+
Default clipping shape.
+
+
DEFAULT_COLOR - Static variable in class org.xbib.graphics.io.vector.GraphicsState
+
+
Default color.
+
+
DEFAULT_COMPOSITE - Static variable in class org.xbib.graphics.io.vector.GraphicsState
+
+
Default composite mode.
+
+
DEFAULT_FONT - Static variable in class org.xbib.graphics.io.vector.GraphicsState
+
+
Default font.
+
+
DEFAULT_PAINT - Static variable in class org.xbib.graphics.io.vector.GraphicsState
+
+
Default paint.
+
+
DEFAULT_STROKE - Static variable in class org.xbib.graphics.io.vector.GraphicsState
+
+
Default stroke.
+
+
DEFAULT_TRANSFORM - Static variable in class org.xbib.graphics.io.vector.GraphicsState
+
+
Default transformation.
+
+
DEFAULT_XOR_MODE - Static variable in class org.xbib.graphics.io.vector.GraphicsState
+
+
Default XOR mode.
+
+
dict - Variable in class org.xbib.graphics.io.vector.pdf.PDFObject
+
 
+
dispose() - Method in class org.xbib.graphics.io.vector.VectorGraphics2D
+
 
+
DisposeCommand - Class in org.xbib.graphics.io.vector.commands
+
 
+
DisposeCommand(VectorGraphics2D) - Constructor for class org.xbib.graphics.io.vector.commands.DisposeCommand
+
 
+
draw(Shape) - Method in class org.xbib.graphics.io.vector.VectorGraphics2D
+
 
+
drawArc(int, int, int, int, int, int) - Method in class org.xbib.graphics.io.vector.VectorGraphics2D
+
 
+
drawGlyphVector(GlyphVector, float, float) - Method in class org.xbib.graphics.io.vector.VectorGraphics2D
+
 
+
drawImage(BufferedImage, BufferedImageOp, int, int) - Method in class org.xbib.graphics.io.vector.VectorGraphics2D
+
 
+
drawImage(Image, int, int, int, int, int, int, int, int, Color, ImageObserver) - Method in class org.xbib.graphics.io.vector.VectorGraphics2D
+
 
+
drawImage(Image, int, int, int, int, int, int, int, int, ImageObserver) - Method in class org.xbib.graphics.io.vector.VectorGraphics2D
+
 
+
drawImage(Image, int, int, int, int, Color, ImageObserver) - Method in class org.xbib.graphics.io.vector.VectorGraphics2D
+
 
+
drawImage(Image, int, int, int, int, ImageObserver) - Method in class org.xbib.graphics.io.vector.VectorGraphics2D
+
 
+
drawImage(Image, int, int, Color, ImageObserver) - Method in class org.xbib.graphics.io.vector.VectorGraphics2D
+
 
+
drawImage(Image, int, int, ImageObserver) - Method in class org.xbib.graphics.io.vector.VectorGraphics2D
+
 
+
drawImage(Image, AffineTransform, ImageObserver) - Method in class org.xbib.graphics.io.vector.VectorGraphics2D
+
 
+
DrawImageCommand - Class in org.xbib.graphics.io.vector.commands
+
 
+
DrawImageCommand(Image, int, int, double, double, double, double) - Constructor for class org.xbib.graphics.io.vector.commands.DrawImageCommand
+
 
+
drawLine(int, int, int, int) - Method in class org.xbib.graphics.io.vector.VectorGraphics2D
+
 
+
drawOval(int, int, int, int) - Method in class org.xbib.graphics.io.vector.VectorGraphics2D
+
 
+
drawPolygon(int[], int[], int) - Method in class org.xbib.graphics.io.vector.VectorGraphics2D
+
 
+
drawPolygon(Polygon) - Method in class org.xbib.graphics.io.vector.VectorGraphics2D
+
 
+
drawPolyline(int[], int[], int) - Method in class org.xbib.graphics.io.vector.VectorGraphics2D
+
 
+
drawRect(int, int, int, int) - Method in class org.xbib.graphics.io.vector.VectorGraphics2D
+
 
+
drawRenderableImage(RenderableImage, AffineTransform) - Method in class org.xbib.graphics.io.vector.VectorGraphics2D
+
 
+
drawRenderedImage(RenderedImage, AffineTransform) - Method in class org.xbib.graphics.io.vector.VectorGraphics2D
+
 
+
drawRoundRect(int, int, int, int, int, int) - Method in class org.xbib.graphics.io.vector.VectorGraphics2D
+
 
+
DrawShapeCommand - Class in org.xbib.graphics.io.vector.commands
+
 
+
DrawShapeCommand(Shape) - Constructor for class org.xbib.graphics.io.vector.commands.DrawShapeCommand
+
 
+
drawString(String, float, float) - Method in class org.xbib.graphics.io.vector.VectorGraphics2D
+
 
+
drawString(String, int, int) - Method in class org.xbib.graphics.io.vector.VectorGraphics2D
+
 
+
drawString(AttributedCharacterIterator, float, float) - Method in class org.xbib.graphics.io.vector.VectorGraphics2D
+
 
+
drawString(AttributedCharacterIterator, int, int) - Method in class org.xbib.graphics.io.vector.VectorGraphics2D
+
 
+
DrawStringCommand - Class in org.xbib.graphics.io.vector.commands
+
 
+
DrawStringCommand(String, double, double) - Constructor for class org.xbib.graphics.io.vector.commands.DrawStringCommand
+
 
+
+

E

+
+
EPS - org.xbib.graphics.io.vector.VectorGraphicsFormat
+
 
+
EPSGraphics2D - Class in org.xbib.graphics.io.vector.eps
+
+
Graphics2D implementation that saves all operations to a string + in the Encapsulated PostScript® (EPS) format.
+
+
EPSGraphics2D(double, double, double, double) - Constructor for class org.xbib.graphics.io.vector.eps.EPSGraphics2D
+
+
Initializes a new VectorGraphics2D pipeline for translating Graphics2D + commands to EPS data.
+
+
EPSProcessor - Class in org.xbib.graphics.io.vector.eps
+
 
+
EPSProcessor() - Constructor for class org.xbib.graphics.io.vector.eps.EPSProcessor
+
 
+
EPSProcessorResult - Class in org.xbib.graphics.io.vector.eps
+
 
+
EPSProcessorResult(PageSize) - Constructor for class org.xbib.graphics.io.vector.eps.EPSProcessorResult
+
 
+
equals(Shape, Shape) - Static method in class org.xbib.graphics.io.vector.util.GraphicsUtils
+
 
+
equals(Object) - Method in class org.xbib.graphics.io.vector.Command
+
 
+
equals(Object) - Method in class org.xbib.graphics.io.vector.GraphicsState
+
 
+
+

F

+
+
fill(Shape) - Method in class org.xbib.graphics.io.vector.VectorGraphics2D
+
 
+
fillArc(int, int, int, int, int, int) - Method in class org.xbib.graphics.io.vector.VectorGraphics2D
+
 
+
fillOval(int, int, int, int) - Method in class org.xbib.graphics.io.vector.VectorGraphics2D
+
 
+
FillPaintedShapeAsImageFilter - Class in org.xbib.graphics.io.vector.filters
+
 
+
FillPaintedShapeAsImageFilter(Iterable<Command<?>>) - Constructor for class org.xbib.graphics.io.vector.filters.FillPaintedShapeAsImageFilter
+
 
+
fillPolygon(int[], int[], int) - Method in class org.xbib.graphics.io.vector.VectorGraphics2D
+
 
+
fillPolygon(Polygon) - Method in class org.xbib.graphics.io.vector.VectorGraphics2D
+
 
+
fillRect(int, int, int, int) - Method in class org.xbib.graphics.io.vector.VectorGraphics2D
+
 
+
fillRoundRect(int, int, int, int, int, int) - Method in class org.xbib.graphics.io.vector.VectorGraphics2D
+
 
+
FillShapeCommand - Class in org.xbib.graphics.io.vector.commands
+
 
+
FillShapeCommand(Shape) - Constructor for class org.xbib.graphics.io.vector.commands.FillShapeCommand
+
 
+
filter(BufferedImage, BufferedImage) - Method in class org.xbib.graphics.io.vector.util.AlphaToMaskOp
+
 
+
filter(Command<?>) - Method in class org.xbib.graphics.io.vector.filters.AbsoluteToRelativeTransformsFilter
+
 
+
filter(Command<?>) - Method in class org.xbib.graphics.io.vector.filters.FillPaintedShapeAsImageFilter
+
 
+
filter(Command<?>) - Method in class org.xbib.graphics.io.vector.filters.Filter
+
 
+
filter(Command<?>) - Method in class org.xbib.graphics.io.vector.filters.GroupingFilter
+
 
+
filter(Command<?>) - Method in class org.xbib.graphics.io.vector.filters.OptimizeFilter
+
 
+
Filter - Class in org.xbib.graphics.io.vector.filters
+
 
+
Filter(Iterable<Command<?>>) - Constructor for class org.xbib.graphics.io.vector.filters.Filter
+
 
+
FlateEncodeStream - Class in org.xbib.graphics.io.vector.util
+
 
+
FlateEncodeStream(OutputStream) - Constructor for class org.xbib.graphics.io.vector.util.FlateEncodeStream
+
 
+
flush() - Method in class org.xbib.graphics.io.vector.util.FormattingWriter
+
 
+
format(Number) - Static method in class org.xbib.graphics.io.vector.util.DataUtils
+
+
Returns a formatted string of the specified number.
+
+
format(Object) - Static method in class org.xbib.graphics.io.vector.util.DataUtils
+
+
Returns a formatted string of the specified object.
+
+
format(String, Object...) - Method in class org.xbib.graphics.io.vector.util.FormattingWriter
+
 
+
FormattingWriter - Class in org.xbib.graphics.io.vector.util
+
 
+
FormattingWriter(OutputStream, String, String) - Constructor for class org.xbib.graphics.io.vector.util.FormattingWriter
+
 
+
+

G

+
+
GeneratedPayload - Class in org.xbib.graphics.io.vector.pdf
+
 
+
GeneratedPayload(boolean) - Constructor for class org.xbib.graphics.io.vector.pdf.GeneratedPayload
+
 
+
generatePayload() - Method in class org.xbib.graphics.io.vector.pdf.GeneratedPayload
+
 
+
generatePayload() - Method in class org.xbib.graphics.io.vector.pdf.SizePayload
+
 
+
getAlphaImage(BufferedImage) - Static method in class org.xbib.graphics.io.vector.util.GraphicsUtils
+
 
+
getBackground() - Method in class org.xbib.graphics.io.vector.GraphicsState
+
 
+
getBackground() - Method in class org.xbib.graphics.io.vector.VectorGraphics2D
+
 
+
getBounds2D(BufferedImage) - Method in class org.xbib.graphics.io.vector.util.AlphaToMaskOp
+
 
+
getBytes() - Method in class org.xbib.graphics.io.vector.pdf.GeneratedPayload
+
 
+
getBytes() - Method in class org.xbib.graphics.io.vector.pdf.Payload
+
 
+
getBytes() - Method in class org.xbib.graphics.io.vector.VectorGraphics2D
+
 
+
getCenterX() - Method in class org.xbib.graphics.io.vector.commands.RotateCommand
+
 
+
getCenterY() - Method in class org.xbib.graphics.io.vector.commands.RotateCommand
+
 
+
getClip() - Method in class org.xbib.graphics.io.vector.GraphicsState
+
 
+
getClip() - Method in class org.xbib.graphics.io.vector.VectorGraphics2D
+
 
+
getClipBounds() - Method in class org.xbib.graphics.io.vector.VectorGraphics2D
+
 
+
getColor() - Method in class org.xbib.graphics.io.vector.GraphicsState
+
 
+
getColor() - Method in class org.xbib.graphics.io.vector.VectorGraphics2D
+
 
+
getCommands() - Method in class org.xbib.graphics.io.vector.VectorGraphics2D
+
 
+
getComposite() - Method in class org.xbib.graphics.io.vector.GraphicsState
+
 
+
getComposite() - Method in class org.xbib.graphics.io.vector.VectorGraphics2D
+
 
+
getDeltaX() - Method in class org.xbib.graphics.io.vector.commands.TranslateCommand
+
 
+
getDeltaY() - Method in class org.xbib.graphics.io.vector.commands.TranslateCommand
+
 
+
getDeviceConfiguration() - Method in class org.xbib.graphics.io.vector.VectorGraphics2D
+
 
+
getFont() - Method in class org.xbib.graphics.io.vector.GraphicsState
+
 
+
getFont() - Method in class org.xbib.graphics.io.vector.VectorGraphics2D
+
 
+
getFontMetrics(Font) - Method in class org.xbib.graphics.io.vector.VectorGraphics2D
+
 
+
getFontRenderContext() - Method in class org.xbib.graphics.io.vector.VectorGraphics2D
+
 
+
getHeight() - Method in class org.xbib.graphics.io.vector.commands.DrawImageCommand
+
 
+
getHeight() - Method in class org.xbib.graphics.io.vector.PageSize
+
 
+
getHints() - Method in class org.xbib.graphics.io.vector.GraphicsState
+
 
+
getId() - Method in class org.xbib.graphics.io.vector.util.VectorHints.Value
+
 
+
getId(Font) - Method in class org.xbib.graphics.io.vector.pdf.Resources
+
 
+
getId(Double) - Method in class org.xbib.graphics.io.vector.pdf.Resources
+
 
+
getId(PDFObject) - Method in class org.xbib.graphics.io.vector.pdf.Resources
+
 
+
getImage() - Method in class org.xbib.graphics.io.vector.util.ImageDataStream
+
 
+
getImageHeight() - Method in class org.xbib.graphics.io.vector.commands.DrawImageCommand
+
 
+
getImageWidth() - Method in class org.xbib.graphics.io.vector.commands.DrawImageCommand
+
 
+
getIndex() - Method in class org.xbib.graphics.io.vector.util.VectorHints.Key
+
 
+
getIndex() - Method in class org.xbib.graphics.io.vector.util.VectorHints.Value
+
 
+
getInterleaving() - Method in class org.xbib.graphics.io.vector.util.ImageDataStream
+
 
+
getKey() - Method in class org.xbib.graphics.io.vector.commands.SetHintCommand
+
 
+
getLandscape() - Method in class org.xbib.graphics.io.vector.PageSize
+
 
+
getPageSize() - Method in class org.xbib.graphics.io.vector.VectorGraphics2D
+
 
+
getPaint() - Method in class org.xbib.graphics.io.vector.GraphicsState
+
 
+
getPaint() - Method in class org.xbib.graphics.io.vector.VectorGraphics2D
+
 
+
getPhysicalFont(Font) - Static method in class org.xbib.graphics.io.vector.util.GraphicsUtils
+
 
+
getPhysicalFont(Font, String) - Static method in class org.xbib.graphics.io.vector.util.GraphicsUtils
+
+
Try to guess physical font from the properties of a logical font, like + "Dialog", "Serif", "Monospaced" etc.
+
+
getPoint2D(Point2D, Point2D) - Method in class org.xbib.graphics.io.vector.util.AlphaToMaskOp
+
 
+
getPortrait() - Method in class org.xbib.graphics.io.vector.PageSize
+
 
+
getRenderingHint(RenderingHints.Key) - Method in class org.xbib.graphics.io.vector.VectorGraphics2D
+
 
+
getRenderingHints() - Method in class org.xbib.graphics.io.vector.util.AlphaToMaskOp
+
 
+
getRenderingHints() - Method in class org.xbib.graphics.io.vector.VectorGraphics2D
+
 
+
getScaleX() - Method in class org.xbib.graphics.io.vector.commands.ScaleCommand
+
 
+
getScaleY() - Method in class org.xbib.graphics.io.vector.commands.ScaleCommand
+
 
+
getShearX() - Method in class org.xbib.graphics.io.vector.commands.ShearCommand
+
 
+
getShearY() - Method in class org.xbib.graphics.io.vector.commands.ShearCommand
+
 
+
getStroke() - Method in class org.xbib.graphics.io.vector.GraphicsState
+
 
+
getStroke() - Method in class org.xbib.graphics.io.vector.VectorGraphics2D
+
 
+
getTheta() - Method in class org.xbib.graphics.io.vector.commands.RotateCommand
+
 
+
getTransform() - Method in class org.xbib.graphics.io.vector.commands.TransformCommand
+
 
+
getTransform() - Method in class org.xbib.graphics.io.vector.GraphicsState
+
 
+
getTransform() - Method in class org.xbib.graphics.io.vector.VectorGraphics2D
+
 
+
getValue() - Method in class org.xbib.graphics.io.vector.Command
+
 
+
getWidth() - Method in class org.xbib.graphics.io.vector.commands.DrawImageCommand
+
 
+
getWidth() - Method in class org.xbib.graphics.io.vector.PageSize
+
 
+
getX() - Method in class org.xbib.graphics.io.vector.commands.DrawImageCommand
+
 
+
getX() - Method in class org.xbib.graphics.io.vector.commands.DrawStringCommand
+
 
+
getX() - Method in class org.xbib.graphics.io.vector.PageSize
+
 
+
getXorMode() - Method in class org.xbib.graphics.io.vector.GraphicsState
+
 
+
getXORMode() - Method in class org.xbib.graphics.io.vector.VectorGraphics2D
+
 
+
getY() - Method in class org.xbib.graphics.io.vector.commands.DrawImageCommand
+
 
+
getY() - Method in class org.xbib.graphics.io.vector.commands.DrawStringCommand
+
 
+
getY() - Method in class org.xbib.graphics.io.vector.PageSize
+
 
+
GraphicsState - Class in org.xbib.graphics.io.vector
+
 
+
GraphicsState() - Constructor for class org.xbib.graphics.io.vector.GraphicsState
+
 
+
GraphicsUtils - Class in org.xbib.graphics.io.vector.util
+
+
Abstract class that contains utility functions for working with graphics.
+
+
GraphicsUtils() - Constructor for class org.xbib.graphics.io.vector.util.GraphicsUtils
+
+
Default constructor that prevents creation of class.
+
+
Group - Class in org.xbib.graphics.io.vector.commands
+
 
+
Group() - Constructor for class org.xbib.graphics.io.vector.commands.Group
+
 
+
GroupingFilter - Class in org.xbib.graphics.io.vector.filters
+
 
+
GroupingFilter(Iterable<Command<?>>) - Constructor for class org.xbib.graphics.io.vector.filters.GroupingFilter
+
 
+
+

H

+
+
handle(Command<?>) - Method in class org.xbib.graphics.io.vector.eps.EPSProcessorResult
+
 
+
handle(Command<?>) - Method in class org.xbib.graphics.io.vector.pdf.PDFProcessorResult
+
 
+
handle(Command<?>) - Method in interface org.xbib.graphics.io.vector.ProcessorResult
+
 
+
handle(Command<?>) - Method in class org.xbib.graphics.io.vector.svg.SVGProcessorResult
+
 
+
hasAlpha(Image) - Static method in class org.xbib.graphics.io.vector.util.GraphicsUtils
+
+
This method returns true if the specified image has the + possibility to store transparent pixels.
+
+
hashCode() - Method in class org.xbib.graphics.io.vector.GraphicsState
+
 
+
hasNext() - Method in class org.xbib.graphics.io.vector.filters.Filter
+
 
+
hasNext() - Method in class org.xbib.graphics.io.vector.filters.GroupingFilter
+
 
+
hasNext() - Method in class org.xbib.graphics.io.vector.filters.OptimizeFilter
+
 
+
hit(Rectangle, Shape, boolean) - Method in class org.xbib.graphics.io.vector.VectorGraphics2D
+
 
+
+

I

+
+
id - Variable in class org.xbib.graphics.io.vector.pdf.PDFObject
+
 
+
ImageDataStream - Class in org.xbib.graphics.io.vector.util
+
 
+
ImageDataStream(BufferedImage, ImageDataStream.Interleaving) - Constructor for class org.xbib.graphics.io.vector.util.ImageDataStream
+
 
+
ImageDataStream.Interleaving - Enum in org.xbib.graphics.io.vector.util
+
 
+
isCompatibleKey(RenderingHints.Key) - Method in class org.xbib.graphics.io.vector.util.VectorHints.Value
+
 
+
isCompatibleValue(Object) - Method in class org.xbib.graphics.io.vector.util.VectorHints.Key
+
 
+
isDefault() - Method in class org.xbib.graphics.io.vector.GraphicsState
+
 
+
isDisposed() - Method in class org.xbib.graphics.io.vector.VectorGraphics2D
+
 
+
isGrouped(Command<?>) - Method in class org.xbib.graphics.io.vector.filters.GroupingFilter
+
 
+
isGrouped(Command<?>) - Method in class org.xbib.graphics.io.vector.filters.StateChangeGroupingFilter
+
 
+
isInverted() - Method in class org.xbib.graphics.io.vector.util.AlphaToMaskOp
+
 
+
isStream() - Method in class org.xbib.graphics.io.vector.pdf.Payload
+
 
+
iterator() - Method in class org.xbib.graphics.io.vector.filters.Filter
+
 
+
+

J

+
+
join(String, double[]) - Static method in class org.xbib.graphics.io.vector.util.DataUtils
+
+
Returns a string containing all double numbers concatenated by a + specified separator.
+
+
join(String, float[]) - Static method in class org.xbib.graphics.io.vector.util.DataUtils
+
+
Returns a string containing all float numbers concatenated by a + specified separator.
+
+
join(String, Object[]) - Static method in class org.xbib.graphics.io.vector.util.DataUtils
+
+
Returns a string containing all elements concatenated by a specified + separator.
+
+
join(String, List<?>) - Static method in class org.xbib.graphics.io.vector.util.DataUtils
+
+
Returns a string containing all elements concatenated by a specified + separator.
+
+
+

K

+
+
Key(int, String) - Constructor for class org.xbib.graphics.io.vector.util.VectorHints.Key
+
 
+
KEY_EXPORT - Static variable in class org.xbib.graphics.io.vector.util.VectorHints
+
 
+
KEY_TEXT - Static variable in class org.xbib.graphics.io.vector.util.VectorHints
+
 
+
+

L

+
+
LEDGER - Static variable in class org.xbib.graphics.io.vector.PageSize
+
 
+
LEGAL - Static variable in class org.xbib.graphics.io.vector.PageSize
+
 
+
LETTER - Static variable in class org.xbib.graphics.io.vector.PageSize
+
 
+
LineWrapOutputStream - Class in org.xbib.graphics.io.vector.util
+
 
+
LineWrapOutputStream(OutputStream, int) - Constructor for class org.xbib.graphics.io.vector.util.LineWrapOutputStream
+
 
+
LineWrapOutputStream(OutputStream, int, String) - Constructor for class org.xbib.graphics.io.vector.util.LineWrapOutputStream
+
 
+
+

M

+
+
map(K[], V[]) - Static method in class org.xbib.graphics.io.vector.util.DataUtils
+
+
Creates a mapping from two arrays, one with keys, one with values.
+
+
max(int...) - Static method in class org.xbib.graphics.io.vector.util.DataUtils
+
+
Returns the largest of all specified values.
+
+
+

N

+
+
next() - Method in class org.xbib.graphics.io.vector.filters.AbsoluteToRelativeTransformsFilter
+
 
+
next() - Method in class org.xbib.graphics.io.vector.filters.FillPaintedShapeAsImageFilter
+
 
+
next() - Method in class org.xbib.graphics.io.vector.filters.Filter
+
 
+
next() - Method in class org.xbib.graphics.io.vector.filters.GroupingFilter
+
 
+
next() - Method in class org.xbib.graphics.io.vector.filters.OptimizeFilter
+
 
+
+

O

+
+
OptimizeFilter - Class in org.xbib.graphics.io.vector.filters
+
 
+
OptimizeFilter(Iterable<Command<?>>) - Constructor for class org.xbib.graphics.io.vector.filters.OptimizeFilter
+
 
+
org.xbib.graphics.io.vector - module org.xbib.graphics.io.vector
+
 
+
org.xbib.graphics.io.vector - package org.xbib.graphics.io.vector
+
 
+
org.xbib.graphics.io.vector.commands - package org.xbib.graphics.io.vector.commands
+
 
+
org.xbib.graphics.io.vector.eps - package org.xbib.graphics.io.vector.eps
+
 
+
org.xbib.graphics.io.vector.filters - package org.xbib.graphics.io.vector.filters
+
 
+
org.xbib.graphics.io.vector.pdf - package org.xbib.graphics.io.vector.pdf
+
 
+
org.xbib.graphics.io.vector.svg - package org.xbib.graphics.io.vector.svg
+
 
+
org.xbib.graphics.io.vector.util - package org.xbib.graphics.io.vector.util
+
 
+
+

P

+
+
PageSize - Class in org.xbib.graphics.io.vector
+
 
+
PageSize(double, double) - Constructor for class org.xbib.graphics.io.vector.PageSize
+
 
+
PageSize(double, double, double, double) - Constructor for class org.xbib.graphics.io.vector.PageSize
+
 
+
PageSize(Rectangle2D) - Constructor for class org.xbib.graphics.io.vector.PageSize
+
 
+
payload - Variable in class org.xbib.graphics.io.vector.pdf.PDFObject
+
 
+
Payload - Class in org.xbib.graphics.io.vector.pdf
+
 
+
Payload(boolean) - Constructor for class org.xbib.graphics.io.vector.pdf.Payload
+
 
+
PDF - org.xbib.graphics.io.vector.VectorGraphicsFormat
+
 
+
PDFGraphics2D - Class in org.xbib.graphics.io.vector.pdf
+
+
Graphics2D implementation that saves all operations to a string + in the Portable Document Format (PDF).
+
+
PDFGraphics2D(double, double, double, double) - Constructor for class org.xbib.graphics.io.vector.pdf.PDFGraphics2D
+
+
Initializes a new VectorGraphics2D pipeline for translating Graphics2D + commands to PDF data.
+
+
PDFObject - Class in org.xbib.graphics.io.vector.pdf
+
 
+
PDFObject(int, int, Map<String, Object>, Payload) - Constructor for class org.xbib.graphics.io.vector.pdf.PDFObject
+
 
+
PDFProcessor - Class in org.xbib.graphics.io.vector.pdf
+
 
+
PDFProcessor() - Constructor for class org.xbib.graphics.io.vector.pdf.PDFProcessor
+
 
+
PDFProcessor(boolean) - Constructor for class org.xbib.graphics.io.vector.pdf.PDFProcessor
+
 
+
PDFProcessorResult - Class in org.xbib.graphics.io.vector.pdf
+
 
+
PDFProcessorResult(PageSize) - Constructor for class org.xbib.graphics.io.vector.pdf.PDFProcessorResult
+
 
+
process(Iterable<Command<?>>, PageSize) - Method in class org.xbib.graphics.io.vector.eps.EPSProcessor
+
 
+
process(Iterable<Command<?>>, PageSize) - Method in class org.xbib.graphics.io.vector.pdf.PDFProcessor
+
 
+
process(Iterable<Command<?>>, PageSize) - Method in interface org.xbib.graphics.io.vector.Processor
+
 
+
process(Iterable<Command<?>>, PageSize) - Method in class org.xbib.graphics.io.vector.svg.SVGProcessor
+
 
+
Processor - Interface in org.xbib.graphics.io.vector
+
 
+
ProcessorResult - Interface in org.xbib.graphics.io.vector
+
 
+
+

R

+
+
read() - Method in class org.xbib.graphics.io.vector.util.ImageDataStream
+
 
+
remove() - Method in class org.xbib.graphics.io.vector.filters.Filter
+
 
+
Resources - Class in org.xbib.graphics.io.vector.pdf
+
 
+
Resources(int, int) - Constructor for class org.xbib.graphics.io.vector.pdf.Resources
+
 
+
rotate(double) - Method in class org.xbib.graphics.io.vector.VectorGraphics2D
+
 
+
rotate(double, double, double) - Method in class org.xbib.graphics.io.vector.VectorGraphics2D
+
 
+
RotateCommand - Class in org.xbib.graphics.io.vector.commands
+
 
+
RotateCommand(double, double, double) - Constructor for class org.xbib.graphics.io.vector.commands.RotateCommand
+
 
+
ROW - org.xbib.graphics.io.vector.util.ImageDataStream.Interleaving
+
 
+
+

S

+
+
SAMPLE - org.xbib.graphics.io.vector.util.ImageDataStream.Interleaving
+
 
+
scale(double, double) - Method in class org.xbib.graphics.io.vector.VectorGraphics2D
+
 
+
ScaleCommand - Class in org.xbib.graphics.io.vector.commands
+
 
+
ScaleCommand(double, double) - Constructor for class org.xbib.graphics.io.vector.commands.ScaleCommand
+
 
+
setBackground(Color) - Method in class org.xbib.graphics.io.vector.GraphicsState
+
 
+
setBackground(Color) - Method in class org.xbib.graphics.io.vector.VectorGraphics2D
+
 
+
SetBackgroundCommand - Class in org.xbib.graphics.io.vector.commands
+
 
+
SetBackgroundCommand(Color) - Constructor for class org.xbib.graphics.io.vector.commands.SetBackgroundCommand
+
 
+
setClip(int, int, int, int) - Method in class org.xbib.graphics.io.vector.VectorGraphics2D
+
 
+
setClip(Shape) - Method in class org.xbib.graphics.io.vector.GraphicsState
+
 
+
setClip(Shape) - Method in class org.xbib.graphics.io.vector.VectorGraphics2D
+
 
+
SetClipCommand - Class in org.xbib.graphics.io.vector.commands
+
 
+
SetClipCommand(Shape) - Constructor for class org.xbib.graphics.io.vector.commands.SetClipCommand
+
 
+
setColor(Color) - Method in class org.xbib.graphics.io.vector.GraphicsState
+
 
+
setColor(Color) - Method in class org.xbib.graphics.io.vector.VectorGraphics2D
+
 
+
SetColorCommand - Class in org.xbib.graphics.io.vector.commands
+
 
+
SetColorCommand(Color) - Constructor for class org.xbib.graphics.io.vector.commands.SetColorCommand
+
 
+
setComposite(Composite) - Method in class org.xbib.graphics.io.vector.GraphicsState
+
 
+
setComposite(Composite) - Method in class org.xbib.graphics.io.vector.VectorGraphics2D
+
 
+
SetCompositeCommand - Class in org.xbib.graphics.io.vector.commands
+
 
+
SetCompositeCommand(Composite) - Constructor for class org.xbib.graphics.io.vector.commands.SetCompositeCommand
+
 
+
setCompressed(boolean) - Method in class org.xbib.graphics.io.vector.pdf.PDFProcessorResult
+
 
+
setFont(Font) - Method in class org.xbib.graphics.io.vector.GraphicsState
+
 
+
setFont(Font) - Method in class org.xbib.graphics.io.vector.VectorGraphics2D
+
 
+
SetFontCommand - Class in org.xbib.graphics.io.vector.commands
+
 
+
SetFontCommand(Font) - Constructor for class org.xbib.graphics.io.vector.commands.SetFontCommand
+
 
+
SetHintCommand - Class in org.xbib.graphics.io.vector.commands
+
 
+
SetHintCommand(Object, Object) - Constructor for class org.xbib.graphics.io.vector.commands.SetHintCommand
+
 
+
setPaint(Paint) - Method in class org.xbib.graphics.io.vector.GraphicsState
+
 
+
setPaint(Paint) - Method in class org.xbib.graphics.io.vector.VectorGraphics2D
+
 
+
SetPaintCommand - Class in org.xbib.graphics.io.vector.commands
+
 
+
SetPaintCommand(Paint) - Constructor for class org.xbib.graphics.io.vector.commands.SetPaintCommand
+
 
+
setPaintMode() - Method in class org.xbib.graphics.io.vector.VectorGraphics2D
+
 
+
setRenderingHint(RenderingHints.Key, Object) - Method in class org.xbib.graphics.io.vector.VectorGraphics2D
+
 
+
setRenderingHints(Map<?, ?>) - Method in class org.xbib.graphics.io.vector.VectorGraphics2D
+
 
+
setStroke(Stroke) - Method in class org.xbib.graphics.io.vector.GraphicsState
+
 
+
setStroke(Stroke) - Method in class org.xbib.graphics.io.vector.VectorGraphics2D
+
 
+
SetStrokeCommand - Class in org.xbib.graphics.io.vector.commands
+
 
+
SetStrokeCommand(Stroke) - Constructor for class org.xbib.graphics.io.vector.commands.SetStrokeCommand
+
 
+
setTransform(AffineTransform) - Method in class org.xbib.graphics.io.vector.GraphicsState
+
 
+
setTransform(AffineTransform) - Method in class org.xbib.graphics.io.vector.VectorGraphics2D
+
 
+
SetTransformCommand - Class in org.xbib.graphics.io.vector.commands
+
 
+
SetTransformCommand(AffineTransform) - Constructor for class org.xbib.graphics.io.vector.commands.SetTransformCommand
+
 
+
setXorMode(Color) - Method in class org.xbib.graphics.io.vector.GraphicsState
+
 
+
setXORMode(Color) - Method in class org.xbib.graphics.io.vector.VectorGraphics2D
+
 
+
SetXORModeCommand - Class in org.xbib.graphics.io.vector.commands
+
 
+
SetXORModeCommand(Color) - Constructor for class org.xbib.graphics.io.vector.commands.SetXORModeCommand
+
 
+
shear(double, double) - Method in class org.xbib.graphics.io.vector.VectorGraphics2D
+
 
+
ShearCommand - Class in org.xbib.graphics.io.vector.commands
+
 
+
ShearCommand(double, double) - Constructor for class org.xbib.graphics.io.vector.commands.ShearCommand
+
 
+
SizePayload - Class in org.xbib.graphics.io.vector.pdf
+
 
+
SizePayload(PDFObject, String, boolean) - Constructor for class org.xbib.graphics.io.vector.pdf.SizePayload
+
 
+
STANDARD_EOL - Static variable in class org.xbib.graphics.io.vector.util.LineWrapOutputStream
+
 
+
StateChangeGroupingFilter - Class in org.xbib.graphics.io.vector.filters
+
 
+
StateChangeGroupingFilter(Iterable<Command<?>>) - Constructor for class org.xbib.graphics.io.vector.filters.StateChangeGroupingFilter
+
 
+
StateCommand<T> - Class in org.xbib.graphics.io.vector.commands
+
 
+
StateCommand(T) - Constructor for class org.xbib.graphics.io.vector.commands.StateCommand
+
 
+
stripTrailing(String, String) - Static method in class org.xbib.graphics.io.vector.util.DataUtils
+
+
Removes the specified trailing pattern from a string.
+
+
SVG - org.xbib.graphics.io.vector.VectorGraphicsFormat
+
 
+
SVGGraphics2D - Class in org.xbib.graphics.io.vector.svg
+
+
Graphics2D implementation that saves all operations to a string + in the Scaled Vector Graphics (SVG) format.
+
+
SVGGraphics2D(double, double, double, double) - Constructor for class org.xbib.graphics.io.vector.svg.SVGGraphics2D
+
+
Initializes a new VectorGraphics2D pipeline for translating Graphics2D + commands to SVG data.
+
+
SVGProcessor - Class in org.xbib.graphics.io.vector.svg
+
 
+
SVGProcessor() - Constructor for class org.xbib.graphics.io.vector.svg.SVGProcessor
+
 
+
SVGProcessorResult - Class in org.xbib.graphics.io.vector.svg
+
 
+
SVGProcessorResult(PageSize) - Constructor for class org.xbib.graphics.io.vector.svg.SVGProcessorResult
+
 
+
+

T

+
+
TABLOID - Static variable in class org.xbib.graphics.io.vector.PageSize
+
 
+
tell() - Method in class org.xbib.graphics.io.vector.util.FormattingWriter
+
 
+
toBufferedImage(Image) - Static method in class org.xbib.graphics.io.vector.util.GraphicsUtils
+
+
This method returns a buffered image with the contents of an image.
+
+
toBufferedImage(RenderedImage) - Static method in class org.xbib.graphics.io.vector.util.GraphicsUtils
+
+
Converts an arbitrary image to a BufferedImage.
+
+
toString() - Method in class org.xbib.graphics.io.vector.Command
+
 
+
toString() - Method in class org.xbib.graphics.io.vector.commands.DrawImageCommand
+
 
+
toString() - Method in class org.xbib.graphics.io.vector.commands.DrawStringCommand
+
 
+
toString() - Method in class org.xbib.graphics.io.vector.commands.RotateCommand
+
 
+
toString() - Method in class org.xbib.graphics.io.vector.commands.ScaleCommand
+
 
+
toString() - Method in class org.xbib.graphics.io.vector.commands.SetHintCommand
+
 
+
toString() - Method in class org.xbib.graphics.io.vector.commands.ShearCommand
+
 
+
toString() - Method in class org.xbib.graphics.io.vector.commands.TranslateCommand
+
 
+
toString() - Method in class org.xbib.graphics.io.vector.svg.SVGProcessorResult
+
 
+
toString() - Method in class org.xbib.graphics.io.vector.util.VectorHints.Key
+
 
+
toString() - Method in class org.xbib.graphics.io.vector.util.VectorHints.Value
+
 
+
toString(PDFObject) - Static method in class org.xbib.graphics.io.vector.pdf.PDFProcessorResult
+
 
+
transfer(InputStream, OutputStream, int) - Static method in class org.xbib.graphics.io.vector.util.DataUtils
+
+
Copies data from an input stream to an output stream using a buffer of + specified size.
+
+
transform(AffineTransform) - Method in class org.xbib.graphics.io.vector.VectorGraphics2D
+
 
+
TransformCommand - Class in org.xbib.graphics.io.vector.commands
+
 
+
TransformCommand(AffineTransform) - Constructor for class org.xbib.graphics.io.vector.commands.TransformCommand
+
 
+
transformShape(Shape) - Method in class org.xbib.graphics.io.vector.GraphicsState
+
 
+
translate(double, double) - Method in class org.xbib.graphics.io.vector.VectorGraphics2D
+
 
+
translate(int, int) - Method in class org.xbib.graphics.io.vector.VectorGraphics2D
+
 
+
TranslateCommand - Class in org.xbib.graphics.io.vector.commands
+
 
+
TranslateCommand(double, double) - Constructor for class org.xbib.graphics.io.vector.commands.TranslateCommand
+
 
+
+

U

+
+
untransformShape(Shape) - Method in class org.xbib.graphics.io.vector.GraphicsState
+
 
+
usesAlpha(Image) - Static method in class org.xbib.graphics.io.vector.util.GraphicsUtils
+
+
This method returns true if the specified image has at least one + pixel that is not fully opaque.
+
+
+

V

+
+
Value(VectorHints.Key, int, String) - Constructor for class org.xbib.graphics.io.vector.util.VectorHints.Value
+
 
+
VALUE_EXPORT_QUALITY - Static variable in class org.xbib.graphics.io.vector.util.VectorHints
+
 
+
VALUE_EXPORT_READABILITY - Static variable in class org.xbib.graphics.io.vector.util.VectorHints
+
 
+
VALUE_EXPORT_SIZE - Static variable in class org.xbib.graphics.io.vector.util.VectorHints
+
 
+
VALUE_TEXT_DEFAULT - Static variable in class org.xbib.graphics.io.vector.util.VectorHints
+
 
+
VALUE_TEXT_VECTOR - Static variable in class org.xbib.graphics.io.vector.util.VectorHints
+
 
+
valueOf(String) - Static method in enum org.xbib.graphics.io.vector.util.ImageDataStream.Interleaving
+
+
Returns the enum constant of this type with the specified name.
+
+
valueOf(String) - Static method in enum org.xbib.graphics.io.vector.VectorGraphicsFormat
+
+
Returns the enum constant of this type with the specified name.
+
+
values() - Static method in enum org.xbib.graphics.io.vector.util.ImageDataStream.Interleaving
+
+
Returns an array containing the constants of this enum type, in +the order they are declared.
+
+
values() - Static method in enum org.xbib.graphics.io.vector.VectorGraphicsFormat
+
+
Returns an array containing the constants of this enum type, in +the order they are declared.
+
+
VectorGraphics2D - Class in org.xbib.graphics.io.vector
+
+
Base for classes that want to implement vector export.
+
+
VectorGraphics2D() - Constructor for class org.xbib.graphics.io.vector.VectorGraphics2D
+
 
+
VectorGraphics2D(Processor, PageSize) - Constructor for class org.xbib.graphics.io.vector.VectorGraphics2D
+
 
+
VectorGraphicsFormat - Enum in org.xbib.graphics.io.vector
+
 
+
VectorHints - Class in org.xbib.graphics.io.vector.util
+
 
+
VectorHints() - Constructor for class org.xbib.graphics.io.vector.util.VectorHints
+
 
+
VectorHints.Key - Class in org.xbib.graphics.io.vector.util
+
 
+
VectorHints.Value - Class in org.xbib.graphics.io.vector.util
+
 
+
version - Variable in class org.xbib.graphics.io.vector.pdf.PDFObject
+
 
+
+

W

+
+
WITHOUT_ALPHA - org.xbib.graphics.io.vector.util.ImageDataStream.Interleaving
+
 
+
write(int) - Method in class org.xbib.graphics.io.vector.pdf.GeneratedPayload
+
 
+
write(int) - Method in class org.xbib.graphics.io.vector.pdf.Payload
+
 
+
write(int) - Method in class org.xbib.graphics.io.vector.util.ASCII85EncodeStream
+
 
+
write(int) - Method in class org.xbib.graphics.io.vector.util.Base64EncodeStream
+
 
+
write(int) - Method in class org.xbib.graphics.io.vector.util.LineWrapOutputStream
+
 
+
write(OutputStream) - Method in class org.xbib.graphics.io.vector.eps.EPSProcessorResult
+
 
+
write(OutputStream) - Method in class org.xbib.graphics.io.vector.pdf.PDFProcessorResult
+
 
+
write(OutputStream) - Method in interface org.xbib.graphics.io.vector.ProcessorResult
+
 
+
write(OutputStream) - Method in class org.xbib.graphics.io.vector.svg.SVGProcessorResult
+
 
+
write(Number) - Method in class org.xbib.graphics.io.vector.util.FormattingWriter
+
 
+
write(String) - Method in class org.xbib.graphics.io.vector.util.FormattingWriter
+
 
+
writeln() - Method in class org.xbib.graphics.io.vector.util.FormattingWriter
+
 
+
writeln(Number) - Method in class org.xbib.graphics.io.vector.util.FormattingWriter
+
 
+
writeln(String) - Method in class org.xbib.graphics.io.vector.util.FormattingWriter
+
 
+
writeTo(OutputStream) - Method in class org.xbib.graphics.io.vector.VectorGraphics2D
+
 
+
+A B C D E F G H I J K L M N O P R S T U V W 
All Classes|All Packages
+ +
+
+ + diff --git a/io-vector/build/docs/javadoc/index.html b/io-vector/build/docs/javadoc/index.html new file mode 100644 index 0000000..49ed4e7 --- /dev/null +++ b/io-vector/build/docs/javadoc/index.html @@ -0,0 +1,25 @@ + + + + + +io-vector 3.0.0 API + + + + + + + + + +
+ +

org.xbib.graphics.io.vector/module-summary.html

+
+ + diff --git a/io-vector/build/docs/javadoc/jquery-ui.overrides.css b/io-vector/build/docs/javadoc/jquery-ui.overrides.css new file mode 100644 index 0000000..f89acb6 --- /dev/null +++ b/io-vector/build/docs/javadoc/jquery-ui.overrides.css @@ -0,0 +1,34 @@ +/* + * Copyright (c) 2020, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +.ui-state-active, +.ui-widget-content .ui-state-active, +.ui-widget-header .ui-state-active, +a.ui-button:active, +.ui-button:active, +.ui-button.ui-state-active:hover { + /* Overrides the color of selection used in jQuery UI */ + background: #F8981D; +} diff --git a/io-vector/build/docs/javadoc/member-search-index.js b/io-vector/build/docs/javadoc/member-search-index.js new file mode 100644 index 0000000..966ff1d --- /dev/null +++ b/io-vector/build/docs/javadoc/member-search-index.js @@ -0,0 +1 @@ +memberSearchIndex = [{"p":"org.xbib.graphics.io.vector","c":"PageSize","l":"A3"},{"p":"org.xbib.graphics.io.vector","c":"PageSize","l":"A4"},{"p":"org.xbib.graphics.io.vector","c":"PageSize","l":"A5"},{"p":"org.xbib.graphics.io.vector.filters","c":"AbsoluteToRelativeTransformsFilter","l":"AbsoluteToRelativeTransformsFilter(Iterable>)","u":"%3Cinit%3E(java.lang.Iterable)"},{"p":"org.xbib.graphics.io.vector.commands","c":"Group","l":"add(Command)","u":"add(org.xbib.graphics.io.vector.Command)"},{"p":"org.xbib.graphics.io.vector.pdf","c":"Payload","l":"addFilter(Class)","u":"addFilter(java.lang.Class)"},{"p":"org.xbib.graphics.io.vector","c":"VectorGraphics2D","l":"addRenderingHints(Map)","u":"addRenderingHints(java.util.Map)"},{"p":"org.xbib.graphics.io.vector.commands","c":"AffineTransformCommand","l":"AffineTransformCommand(AffineTransform)","u":"%3Cinit%3E(java.awt.geom.AffineTransform)"},{"p":"org.xbib.graphics.io.vector.util","c":"ImageDataStream.Interleaving","l":"ALPHA_ONLY"},{"p":"org.xbib.graphics.io.vector.util","c":"AlphaToMaskOp","l":"AlphaToMaskOp()","u":"%3Cinit%3E()"},{"p":"org.xbib.graphics.io.vector.util","c":"AlphaToMaskOp","l":"AlphaToMaskOp(boolean)","u":"%3Cinit%3E(boolean)"},{"p":"org.xbib.graphics.io.vector.util","c":"ASCII85EncodeStream","l":"ASCII85EncodeStream(OutputStream)","u":"%3Cinit%3E(java.io.OutputStream)"},{"p":"org.xbib.graphics.io.vector.util","c":"ASCII85EncodeStream","l":"ASCII85EncodeStream(OutputStream, String, String)","u":"%3Cinit%3E(java.io.OutputStream,java.lang.String,java.lang.String)"},{"p":"org.xbib.graphics.io.vector.util","c":"DataUtils","l":"asList(double[])"},{"p":"org.xbib.graphics.io.vector.util","c":"DataUtils","l":"asList(float[])"},{"p":"org.xbib.graphics.io.vector.util","c":"Base64EncodeStream","l":"Base64EncodeStream(OutputStream)","u":"%3Cinit%3E(java.io.OutputStream)"},{"p":"org.xbib.graphics.io.vector","c":"VectorGraphics2D","l":"clearRect(int, int, int, int)","u":"clearRect(int,int,int,int)"},{"p":"org.xbib.graphics.io.vector","c":"VectorGraphics2D","l":"clip(Shape)","u":"clip(java.awt.Shape)"},{"p":"org.xbib.graphics.io.vector","c":"VectorGraphics2D","l":"clipRect(int, int, int, int)","u":"clipRect(int,int,int,int)"},{"p":"org.xbib.graphics.io.vector","c":"GraphicsState","l":"clone()"},{"p":"org.xbib.graphics.io.vector","c":"VectorGraphics2D","l":"clone()"},{"p":"org.xbib.graphics.io.vector.util","c":"GraphicsUtils","l":"clone(Shape)","u":"clone(java.awt.Shape)"},{"p":"org.xbib.graphics.io.vector","c":"ProcessorResult","l":"close()"},{"p":"org.xbib.graphics.io.vector.eps","c":"EPSProcessorResult","l":"close()"},{"p":"org.xbib.graphics.io.vector.pdf","c":"PDFProcessorResult","l":"close()"},{"p":"org.xbib.graphics.io.vector.pdf","c":"Payload","l":"close()"},{"p":"org.xbib.graphics.io.vector.svg","c":"SVGProcessorResult","l":"close()"},{"p":"org.xbib.graphics.io.vector.util","c":"ASCII85EncodeStream","l":"close()"},{"p":"org.xbib.graphics.io.vector.util","c":"Base64EncodeStream","l":"close()"},{"p":"org.xbib.graphics.io.vector.util","c":"FormattingWriter","l":"close()"},{"p":"org.xbib.graphics.io.vector","c":"Command","l":"Command(T)","u":"%3Cinit%3E(T)"},{"p":"org.xbib.graphics.io.vector","c":"VectorGraphics2D","l":"copyArea(int, int, int, int, int, int)","u":"copyArea(int,int,int,int,int,int)"},{"p":"org.xbib.graphics.io.vector","c":"VectorGraphics2D","l":"create()"},{"p":"org.xbib.graphics.io.vector.commands","c":"CreateCommand","l":"CreateCommand(VectorGraphics2D)","u":"%3Cinit%3E(org.xbib.graphics.io.vector.VectorGraphics2D)"},{"p":"org.xbib.graphics.io.vector.util","c":"AlphaToMaskOp","l":"createCompatibleDestImage(BufferedImage, ColorModel)","u":"createCompatibleDestImage(java.awt.image.BufferedImage,java.awt.image.ColorModel)"},{"p":"org.xbib.graphics.io.vector.util","c":"DataUtils","l":"DataUtils()","u":"%3Cinit%3E()"},{"p":"org.xbib.graphics.io.vector","c":"GraphicsState","l":"DEFAULT_BACKGROUND"},{"p":"org.xbib.graphics.io.vector","c":"GraphicsState","l":"DEFAULT_CLIP"},{"p":"org.xbib.graphics.io.vector","c":"GraphicsState","l":"DEFAULT_COLOR"},{"p":"org.xbib.graphics.io.vector","c":"GraphicsState","l":"DEFAULT_COMPOSITE"},{"p":"org.xbib.graphics.io.vector","c":"GraphicsState","l":"DEFAULT_FONT"},{"p":"org.xbib.graphics.io.vector","c":"GraphicsState","l":"DEFAULT_PAINT"},{"p":"org.xbib.graphics.io.vector","c":"GraphicsState","l":"DEFAULT_STROKE"},{"p":"org.xbib.graphics.io.vector","c":"GraphicsState","l":"DEFAULT_TRANSFORM"},{"p":"org.xbib.graphics.io.vector","c":"GraphicsState","l":"DEFAULT_XOR_MODE"},{"p":"org.xbib.graphics.io.vector.pdf","c":"PDFObject","l":"dict"},{"p":"org.xbib.graphics.io.vector","c":"VectorGraphics2D","l":"dispose()"},{"p":"org.xbib.graphics.io.vector.commands","c":"DisposeCommand","l":"DisposeCommand(VectorGraphics2D)","u":"%3Cinit%3E(org.xbib.graphics.io.vector.VectorGraphics2D)"},{"p":"org.xbib.graphics.io.vector","c":"VectorGraphics2D","l":"draw(Shape)","u":"draw(java.awt.Shape)"},{"p":"org.xbib.graphics.io.vector","c":"VectorGraphics2D","l":"drawArc(int, int, int, int, int, int)","u":"drawArc(int,int,int,int,int,int)"},{"p":"org.xbib.graphics.io.vector","c":"VectorGraphics2D","l":"drawGlyphVector(GlyphVector, float, float)","u":"drawGlyphVector(java.awt.font.GlyphVector,float,float)"},{"p":"org.xbib.graphics.io.vector","c":"VectorGraphics2D","l":"drawImage(BufferedImage, BufferedImageOp, int, int)","u":"drawImage(java.awt.image.BufferedImage,java.awt.image.BufferedImageOp,int,int)"},{"p":"org.xbib.graphics.io.vector","c":"VectorGraphics2D","l":"drawImage(Image, AffineTransform, ImageObserver)","u":"drawImage(java.awt.Image,java.awt.geom.AffineTransform,java.awt.image.ImageObserver)"},{"p":"org.xbib.graphics.io.vector","c":"VectorGraphics2D","l":"drawImage(Image, int, int, Color, ImageObserver)","u":"drawImage(java.awt.Image,int,int,java.awt.Color,java.awt.image.ImageObserver)"},{"p":"org.xbib.graphics.io.vector","c":"VectorGraphics2D","l":"drawImage(Image, int, int, ImageObserver)","u":"drawImage(java.awt.Image,int,int,java.awt.image.ImageObserver)"},{"p":"org.xbib.graphics.io.vector","c":"VectorGraphics2D","l":"drawImage(Image, int, int, int, int, Color, ImageObserver)","u":"drawImage(java.awt.Image,int,int,int,int,java.awt.Color,java.awt.image.ImageObserver)"},{"p":"org.xbib.graphics.io.vector","c":"VectorGraphics2D","l":"drawImage(Image, int, int, int, int, ImageObserver)","u":"drawImage(java.awt.Image,int,int,int,int,java.awt.image.ImageObserver)"},{"p":"org.xbib.graphics.io.vector","c":"VectorGraphics2D","l":"drawImage(Image, int, int, int, int, int, int, int, int, Color, ImageObserver)","u":"drawImage(java.awt.Image,int,int,int,int,int,int,int,int,java.awt.Color,java.awt.image.ImageObserver)"},{"p":"org.xbib.graphics.io.vector","c":"VectorGraphics2D","l":"drawImage(Image, int, int, int, int, int, int, int, int, ImageObserver)","u":"drawImage(java.awt.Image,int,int,int,int,int,int,int,int,java.awt.image.ImageObserver)"},{"p":"org.xbib.graphics.io.vector.commands","c":"DrawImageCommand","l":"DrawImageCommand(Image, int, int, double, double, double, double)","u":"%3Cinit%3E(java.awt.Image,int,int,double,double,double,double)"},{"p":"org.xbib.graphics.io.vector","c":"VectorGraphics2D","l":"drawLine(int, int, int, int)","u":"drawLine(int,int,int,int)"},{"p":"org.xbib.graphics.io.vector","c":"VectorGraphics2D","l":"drawOval(int, int, int, int)","u":"drawOval(int,int,int,int)"},{"p":"org.xbib.graphics.io.vector","c":"VectorGraphics2D","l":"drawPolygon(int[], int[], int)","u":"drawPolygon(int[],int[],int)"},{"p":"org.xbib.graphics.io.vector","c":"VectorGraphics2D","l":"drawPolygon(Polygon)","u":"drawPolygon(java.awt.Polygon)"},{"p":"org.xbib.graphics.io.vector","c":"VectorGraphics2D","l":"drawPolyline(int[], int[], int)","u":"drawPolyline(int[],int[],int)"},{"p":"org.xbib.graphics.io.vector","c":"VectorGraphics2D","l":"drawRect(int, int, int, int)","u":"drawRect(int,int,int,int)"},{"p":"org.xbib.graphics.io.vector","c":"VectorGraphics2D","l":"drawRenderableImage(RenderableImage, AffineTransform)","u":"drawRenderableImage(java.awt.image.renderable.RenderableImage,java.awt.geom.AffineTransform)"},{"p":"org.xbib.graphics.io.vector","c":"VectorGraphics2D","l":"drawRenderedImage(RenderedImage, AffineTransform)","u":"drawRenderedImage(java.awt.image.RenderedImage,java.awt.geom.AffineTransform)"},{"p":"org.xbib.graphics.io.vector","c":"VectorGraphics2D","l":"drawRoundRect(int, int, int, int, int, int)","u":"drawRoundRect(int,int,int,int,int,int)"},{"p":"org.xbib.graphics.io.vector.commands","c":"DrawShapeCommand","l":"DrawShapeCommand(Shape)","u":"%3Cinit%3E(java.awt.Shape)"},{"p":"org.xbib.graphics.io.vector","c":"VectorGraphics2D","l":"drawString(AttributedCharacterIterator, float, float)","u":"drawString(java.text.AttributedCharacterIterator,float,float)"},{"p":"org.xbib.graphics.io.vector","c":"VectorGraphics2D","l":"drawString(AttributedCharacterIterator, int, int)","u":"drawString(java.text.AttributedCharacterIterator,int,int)"},{"p":"org.xbib.graphics.io.vector","c":"VectorGraphics2D","l":"drawString(String, float, float)","u":"drawString(java.lang.String,float,float)"},{"p":"org.xbib.graphics.io.vector","c":"VectorGraphics2D","l":"drawString(String, int, int)","u":"drawString(java.lang.String,int,int)"},{"p":"org.xbib.graphics.io.vector.commands","c":"DrawStringCommand","l":"DrawStringCommand(String, double, double)","u":"%3Cinit%3E(java.lang.String,double,double)"},{"p":"org.xbib.graphics.io.vector","c":"VectorGraphicsFormat","l":"EPS"},{"p":"org.xbib.graphics.io.vector.eps","c":"EPSGraphics2D","l":"EPSGraphics2D(double, double, double, double)","u":"%3Cinit%3E(double,double,double,double)"},{"p":"org.xbib.graphics.io.vector.eps","c":"EPSProcessor","l":"EPSProcessor()","u":"%3Cinit%3E()"},{"p":"org.xbib.graphics.io.vector.eps","c":"EPSProcessorResult","l":"EPSProcessorResult(PageSize)","u":"%3Cinit%3E(org.xbib.graphics.io.vector.PageSize)"},{"p":"org.xbib.graphics.io.vector","c":"Command","l":"equals(Object)","u":"equals(java.lang.Object)"},{"p":"org.xbib.graphics.io.vector","c":"GraphicsState","l":"equals(Object)","u":"equals(java.lang.Object)"},{"p":"org.xbib.graphics.io.vector.util","c":"GraphicsUtils","l":"equals(Shape, Shape)","u":"equals(java.awt.Shape,java.awt.Shape)"},{"p":"org.xbib.graphics.io.vector","c":"VectorGraphics2D","l":"fill(Shape)","u":"fill(java.awt.Shape)"},{"p":"org.xbib.graphics.io.vector","c":"VectorGraphics2D","l":"fillArc(int, int, int, int, int, int)","u":"fillArc(int,int,int,int,int,int)"},{"p":"org.xbib.graphics.io.vector","c":"VectorGraphics2D","l":"fillOval(int, int, int, int)","u":"fillOval(int,int,int,int)"},{"p":"org.xbib.graphics.io.vector.filters","c":"FillPaintedShapeAsImageFilter","l":"FillPaintedShapeAsImageFilter(Iterable>)","u":"%3Cinit%3E(java.lang.Iterable)"},{"p":"org.xbib.graphics.io.vector","c":"VectorGraphics2D","l":"fillPolygon(int[], int[], int)","u":"fillPolygon(int[],int[],int)"},{"p":"org.xbib.graphics.io.vector","c":"VectorGraphics2D","l":"fillPolygon(Polygon)","u":"fillPolygon(java.awt.Polygon)"},{"p":"org.xbib.graphics.io.vector","c":"VectorGraphics2D","l":"fillRect(int, int, int, int)","u":"fillRect(int,int,int,int)"},{"p":"org.xbib.graphics.io.vector","c":"VectorGraphics2D","l":"fillRoundRect(int, int, int, int, int, int)","u":"fillRoundRect(int,int,int,int,int,int)"},{"p":"org.xbib.graphics.io.vector.commands","c":"FillShapeCommand","l":"FillShapeCommand(Shape)","u":"%3Cinit%3E(java.awt.Shape)"},{"p":"org.xbib.graphics.io.vector.util","c":"AlphaToMaskOp","l":"filter(BufferedImage, BufferedImage)","u":"filter(java.awt.image.BufferedImage,java.awt.image.BufferedImage)"},{"p":"org.xbib.graphics.io.vector.filters","c":"AbsoluteToRelativeTransformsFilter","l":"filter(Command)","u":"filter(org.xbib.graphics.io.vector.Command)"},{"p":"org.xbib.graphics.io.vector.filters","c":"FillPaintedShapeAsImageFilter","l":"filter(Command)","u":"filter(org.xbib.graphics.io.vector.Command)"},{"p":"org.xbib.graphics.io.vector.filters","c":"Filter","l":"filter(Command)","u":"filter(org.xbib.graphics.io.vector.Command)"},{"p":"org.xbib.graphics.io.vector.filters","c":"GroupingFilter","l":"filter(Command)","u":"filter(org.xbib.graphics.io.vector.Command)"},{"p":"org.xbib.graphics.io.vector.filters","c":"OptimizeFilter","l":"filter(Command)","u":"filter(org.xbib.graphics.io.vector.Command)"},{"p":"org.xbib.graphics.io.vector.filters","c":"Filter","l":"Filter(Iterable>)","u":"%3Cinit%3E(java.lang.Iterable)"},{"p":"org.xbib.graphics.io.vector.util","c":"FlateEncodeStream","l":"FlateEncodeStream(OutputStream)","u":"%3Cinit%3E(java.io.OutputStream)"},{"p":"org.xbib.graphics.io.vector.util","c":"FormattingWriter","l":"flush()"},{"p":"org.xbib.graphics.io.vector.util","c":"DataUtils","l":"format(Number)","u":"format(java.lang.Number)"},{"p":"org.xbib.graphics.io.vector.util","c":"DataUtils","l":"format(Object)","u":"format(java.lang.Object)"},{"p":"org.xbib.graphics.io.vector.util","c":"FormattingWriter","l":"format(String, Object...)","u":"format(java.lang.String,java.lang.Object...)"},{"p":"org.xbib.graphics.io.vector.util","c":"FormattingWriter","l":"FormattingWriter(OutputStream, String, String)","u":"%3Cinit%3E(java.io.OutputStream,java.lang.String,java.lang.String)"},{"p":"org.xbib.graphics.io.vector.pdf","c":"GeneratedPayload","l":"GeneratedPayload(boolean)","u":"%3Cinit%3E(boolean)"},{"p":"org.xbib.graphics.io.vector.pdf","c":"GeneratedPayload","l":"generatePayload()"},{"p":"org.xbib.graphics.io.vector.pdf","c":"SizePayload","l":"generatePayload()"},{"p":"org.xbib.graphics.io.vector.util","c":"GraphicsUtils","l":"getAlphaImage(BufferedImage)","u":"getAlphaImage(java.awt.image.BufferedImage)"},{"p":"org.xbib.graphics.io.vector","c":"GraphicsState","l":"getBackground()"},{"p":"org.xbib.graphics.io.vector","c":"VectorGraphics2D","l":"getBackground()"},{"p":"org.xbib.graphics.io.vector.util","c":"AlphaToMaskOp","l":"getBounds2D(BufferedImage)","u":"getBounds2D(java.awt.image.BufferedImage)"},{"p":"org.xbib.graphics.io.vector","c":"VectorGraphics2D","l":"getBytes()"},{"p":"org.xbib.graphics.io.vector.pdf","c":"GeneratedPayload","l":"getBytes()"},{"p":"org.xbib.graphics.io.vector.pdf","c":"Payload","l":"getBytes()"},{"p":"org.xbib.graphics.io.vector.commands","c":"RotateCommand","l":"getCenterX()"},{"p":"org.xbib.graphics.io.vector.commands","c":"RotateCommand","l":"getCenterY()"},{"p":"org.xbib.graphics.io.vector","c":"GraphicsState","l":"getClip()"},{"p":"org.xbib.graphics.io.vector","c":"VectorGraphics2D","l":"getClip()"},{"p":"org.xbib.graphics.io.vector","c":"VectorGraphics2D","l":"getClipBounds()"},{"p":"org.xbib.graphics.io.vector","c":"GraphicsState","l":"getColor()"},{"p":"org.xbib.graphics.io.vector","c":"VectorGraphics2D","l":"getColor()"},{"p":"org.xbib.graphics.io.vector","c":"VectorGraphics2D","l":"getCommands()"},{"p":"org.xbib.graphics.io.vector","c":"GraphicsState","l":"getComposite()"},{"p":"org.xbib.graphics.io.vector","c":"VectorGraphics2D","l":"getComposite()"},{"p":"org.xbib.graphics.io.vector.commands","c":"TranslateCommand","l":"getDeltaX()"},{"p":"org.xbib.graphics.io.vector.commands","c":"TranslateCommand","l":"getDeltaY()"},{"p":"org.xbib.graphics.io.vector","c":"VectorGraphics2D","l":"getDeviceConfiguration()"},{"p":"org.xbib.graphics.io.vector","c":"GraphicsState","l":"getFont()"},{"p":"org.xbib.graphics.io.vector","c":"VectorGraphics2D","l":"getFont()"},{"p":"org.xbib.graphics.io.vector","c":"VectorGraphics2D","l":"getFontMetrics(Font)","u":"getFontMetrics(java.awt.Font)"},{"p":"org.xbib.graphics.io.vector","c":"VectorGraphics2D","l":"getFontRenderContext()"},{"p":"org.xbib.graphics.io.vector","c":"PageSize","l":"getHeight()"},{"p":"org.xbib.graphics.io.vector.commands","c":"DrawImageCommand","l":"getHeight()"},{"p":"org.xbib.graphics.io.vector","c":"GraphicsState","l":"getHints()"},{"p":"org.xbib.graphics.io.vector.util","c":"VectorHints.Value","l":"getId()"},{"p":"org.xbib.graphics.io.vector.pdf","c":"Resources","l":"getId(Double)","u":"getId(java.lang.Double)"},{"p":"org.xbib.graphics.io.vector.pdf","c":"Resources","l":"getId(Font)","u":"getId(java.awt.Font)"},{"p":"org.xbib.graphics.io.vector.pdf","c":"Resources","l":"getId(PDFObject)","u":"getId(org.xbib.graphics.io.vector.pdf.PDFObject)"},{"p":"org.xbib.graphics.io.vector.util","c":"ImageDataStream","l":"getImage()"},{"p":"org.xbib.graphics.io.vector.commands","c":"DrawImageCommand","l":"getImageHeight()"},{"p":"org.xbib.graphics.io.vector.commands","c":"DrawImageCommand","l":"getImageWidth()"},{"p":"org.xbib.graphics.io.vector.util","c":"VectorHints.Key","l":"getIndex()"},{"p":"org.xbib.graphics.io.vector.util","c":"VectorHints.Value","l":"getIndex()"},{"p":"org.xbib.graphics.io.vector.util","c":"ImageDataStream","l":"getInterleaving()"},{"p":"org.xbib.graphics.io.vector.commands","c":"SetHintCommand","l":"getKey()"},{"p":"org.xbib.graphics.io.vector","c":"PageSize","l":"getLandscape()"},{"p":"org.xbib.graphics.io.vector","c":"VectorGraphics2D","l":"getPageSize()"},{"p":"org.xbib.graphics.io.vector","c":"GraphicsState","l":"getPaint()"},{"p":"org.xbib.graphics.io.vector","c":"VectorGraphics2D","l":"getPaint()"},{"p":"org.xbib.graphics.io.vector.util","c":"GraphicsUtils","l":"getPhysicalFont(Font)","u":"getPhysicalFont(java.awt.Font)"},{"p":"org.xbib.graphics.io.vector.util","c":"GraphicsUtils","l":"getPhysicalFont(Font, String)","u":"getPhysicalFont(java.awt.Font,java.lang.String)"},{"p":"org.xbib.graphics.io.vector.util","c":"AlphaToMaskOp","l":"getPoint2D(Point2D, Point2D)","u":"getPoint2D(java.awt.geom.Point2D,java.awt.geom.Point2D)"},{"p":"org.xbib.graphics.io.vector","c":"PageSize","l":"getPortrait()"},{"p":"org.xbib.graphics.io.vector","c":"VectorGraphics2D","l":"getRenderingHint(RenderingHints.Key)","u":"getRenderingHint(java.awt.RenderingHints.Key)"},{"p":"org.xbib.graphics.io.vector","c":"VectorGraphics2D","l":"getRenderingHints()"},{"p":"org.xbib.graphics.io.vector.util","c":"AlphaToMaskOp","l":"getRenderingHints()"},{"p":"org.xbib.graphics.io.vector.commands","c":"ScaleCommand","l":"getScaleX()"},{"p":"org.xbib.graphics.io.vector.commands","c":"ScaleCommand","l":"getScaleY()"},{"p":"org.xbib.graphics.io.vector.commands","c":"ShearCommand","l":"getShearX()"},{"p":"org.xbib.graphics.io.vector.commands","c":"ShearCommand","l":"getShearY()"},{"p":"org.xbib.graphics.io.vector","c":"GraphicsState","l":"getStroke()"},{"p":"org.xbib.graphics.io.vector","c":"VectorGraphics2D","l":"getStroke()"},{"p":"org.xbib.graphics.io.vector.commands","c":"RotateCommand","l":"getTheta()"},{"p":"org.xbib.graphics.io.vector","c":"GraphicsState","l":"getTransform()"},{"p":"org.xbib.graphics.io.vector","c":"VectorGraphics2D","l":"getTransform()"},{"p":"org.xbib.graphics.io.vector.commands","c":"TransformCommand","l":"getTransform()"},{"p":"org.xbib.graphics.io.vector","c":"Command","l":"getValue()"},{"p":"org.xbib.graphics.io.vector","c":"PageSize","l":"getWidth()"},{"p":"org.xbib.graphics.io.vector.commands","c":"DrawImageCommand","l":"getWidth()"},{"p":"org.xbib.graphics.io.vector","c":"PageSize","l":"getX()"},{"p":"org.xbib.graphics.io.vector.commands","c":"DrawImageCommand","l":"getX()"},{"p":"org.xbib.graphics.io.vector.commands","c":"DrawStringCommand","l":"getX()"},{"p":"org.xbib.graphics.io.vector","c":"GraphicsState","l":"getXorMode()"},{"p":"org.xbib.graphics.io.vector","c":"VectorGraphics2D","l":"getXORMode()"},{"p":"org.xbib.graphics.io.vector","c":"PageSize","l":"getY()"},{"p":"org.xbib.graphics.io.vector.commands","c":"DrawImageCommand","l":"getY()"},{"p":"org.xbib.graphics.io.vector.commands","c":"DrawStringCommand","l":"getY()"},{"p":"org.xbib.graphics.io.vector","c":"GraphicsState","l":"GraphicsState()","u":"%3Cinit%3E()"},{"p":"org.xbib.graphics.io.vector.util","c":"GraphicsUtils","l":"GraphicsUtils()","u":"%3Cinit%3E()"},{"p":"org.xbib.graphics.io.vector.commands","c":"Group","l":"Group()","u":"%3Cinit%3E()"},{"p":"org.xbib.graphics.io.vector.filters","c":"GroupingFilter","l":"GroupingFilter(Iterable>)","u":"%3Cinit%3E(java.lang.Iterable)"},{"p":"org.xbib.graphics.io.vector","c":"ProcessorResult","l":"handle(Command)","u":"handle(org.xbib.graphics.io.vector.Command)"},{"p":"org.xbib.graphics.io.vector.eps","c":"EPSProcessorResult","l":"handle(Command)","u":"handle(org.xbib.graphics.io.vector.Command)"},{"p":"org.xbib.graphics.io.vector.pdf","c":"PDFProcessorResult","l":"handle(Command)","u":"handle(org.xbib.graphics.io.vector.Command)"},{"p":"org.xbib.graphics.io.vector.svg","c":"SVGProcessorResult","l":"handle(Command)","u":"handle(org.xbib.graphics.io.vector.Command)"},{"p":"org.xbib.graphics.io.vector.util","c":"GraphicsUtils","l":"hasAlpha(Image)","u":"hasAlpha(java.awt.Image)"},{"p":"org.xbib.graphics.io.vector","c":"GraphicsState","l":"hashCode()"},{"p":"org.xbib.graphics.io.vector.filters","c":"Filter","l":"hasNext()"},{"p":"org.xbib.graphics.io.vector.filters","c":"GroupingFilter","l":"hasNext()"},{"p":"org.xbib.graphics.io.vector.filters","c":"OptimizeFilter","l":"hasNext()"},{"p":"org.xbib.graphics.io.vector","c":"VectorGraphics2D","l":"hit(Rectangle, Shape, boolean)","u":"hit(java.awt.Rectangle,java.awt.Shape,boolean)"},{"p":"org.xbib.graphics.io.vector.pdf","c":"PDFObject","l":"id"},{"p":"org.xbib.graphics.io.vector.util","c":"ImageDataStream","l":"ImageDataStream(BufferedImage, ImageDataStream.Interleaving)","u":"%3Cinit%3E(java.awt.image.BufferedImage,org.xbib.graphics.io.vector.util.ImageDataStream.Interleaving)"},{"p":"org.xbib.graphics.io.vector.util","c":"VectorHints.Value","l":"isCompatibleKey(RenderingHints.Key)","u":"isCompatibleKey(java.awt.RenderingHints.Key)"},{"p":"org.xbib.graphics.io.vector.util","c":"VectorHints.Key","l":"isCompatibleValue(Object)","u":"isCompatibleValue(java.lang.Object)"},{"p":"org.xbib.graphics.io.vector","c":"GraphicsState","l":"isDefault()"},{"p":"org.xbib.graphics.io.vector","c":"VectorGraphics2D","l":"isDisposed()"},{"p":"org.xbib.graphics.io.vector.filters","c":"GroupingFilter","l":"isGrouped(Command)","u":"isGrouped(org.xbib.graphics.io.vector.Command)"},{"p":"org.xbib.graphics.io.vector.filters","c":"StateChangeGroupingFilter","l":"isGrouped(Command)","u":"isGrouped(org.xbib.graphics.io.vector.Command)"},{"p":"org.xbib.graphics.io.vector.util","c":"AlphaToMaskOp","l":"isInverted()"},{"p":"org.xbib.graphics.io.vector.pdf","c":"Payload","l":"isStream()"},{"p":"org.xbib.graphics.io.vector.filters","c":"Filter","l":"iterator()"},{"p":"org.xbib.graphics.io.vector.util","c":"DataUtils","l":"join(String, double[])","u":"join(java.lang.String,double[])"},{"p":"org.xbib.graphics.io.vector.util","c":"DataUtils","l":"join(String, float[])","u":"join(java.lang.String,float[])"},{"p":"org.xbib.graphics.io.vector.util","c":"DataUtils","l":"join(String, List)","u":"join(java.lang.String,java.util.List)"},{"p":"org.xbib.graphics.io.vector.util","c":"DataUtils","l":"join(String, Object[])","u":"join(java.lang.String,java.lang.Object[])"},{"p":"org.xbib.graphics.io.vector.util","c":"VectorHints","l":"KEY_EXPORT"},{"p":"org.xbib.graphics.io.vector.util","c":"VectorHints","l":"KEY_TEXT"},{"p":"org.xbib.graphics.io.vector.util","c":"VectorHints.Key","l":"Key(int, String)","u":"%3Cinit%3E(int,java.lang.String)"},{"p":"org.xbib.graphics.io.vector","c":"PageSize","l":"LEDGER"},{"p":"org.xbib.graphics.io.vector","c":"PageSize","l":"LEGAL"},{"p":"org.xbib.graphics.io.vector","c":"PageSize","l":"LETTER"},{"p":"org.xbib.graphics.io.vector.util","c":"LineWrapOutputStream","l":"LineWrapOutputStream(OutputStream, int)","u":"%3Cinit%3E(java.io.OutputStream,int)"},{"p":"org.xbib.graphics.io.vector.util","c":"LineWrapOutputStream","l":"LineWrapOutputStream(OutputStream, int, String)","u":"%3Cinit%3E(java.io.OutputStream,int,java.lang.String)"},{"p":"org.xbib.graphics.io.vector.util","c":"DataUtils","l":"map(K[], V[])","u":"map(K[],V[])"},{"p":"org.xbib.graphics.io.vector.util","c":"DataUtils","l":"max(int...)"},{"p":"org.xbib.graphics.io.vector.filters","c":"AbsoluteToRelativeTransformsFilter","l":"next()"},{"p":"org.xbib.graphics.io.vector.filters","c":"FillPaintedShapeAsImageFilter","l":"next()"},{"p":"org.xbib.graphics.io.vector.filters","c":"Filter","l":"next()"},{"p":"org.xbib.graphics.io.vector.filters","c":"GroupingFilter","l":"next()"},{"p":"org.xbib.graphics.io.vector.filters","c":"OptimizeFilter","l":"next()"},{"p":"org.xbib.graphics.io.vector.filters","c":"OptimizeFilter","l":"OptimizeFilter(Iterable>)","u":"%3Cinit%3E(java.lang.Iterable)"},{"p":"org.xbib.graphics.io.vector","c":"PageSize","l":"PageSize(double, double)","u":"%3Cinit%3E(double,double)"},{"p":"org.xbib.graphics.io.vector","c":"PageSize","l":"PageSize(double, double, double, double)","u":"%3Cinit%3E(double,double,double,double)"},{"p":"org.xbib.graphics.io.vector","c":"PageSize","l":"PageSize(Rectangle2D)","u":"%3Cinit%3E(java.awt.geom.Rectangle2D)"},{"p":"org.xbib.graphics.io.vector.pdf","c":"PDFObject","l":"payload"},{"p":"org.xbib.graphics.io.vector.pdf","c":"Payload","l":"Payload(boolean)","u":"%3Cinit%3E(boolean)"},{"p":"org.xbib.graphics.io.vector","c":"VectorGraphicsFormat","l":"PDF"},{"p":"org.xbib.graphics.io.vector.pdf","c":"PDFGraphics2D","l":"PDFGraphics2D(double, double, double, double)","u":"%3Cinit%3E(double,double,double,double)"},{"p":"org.xbib.graphics.io.vector.pdf","c":"PDFObject","l":"PDFObject(int, int, Map, Payload)","u":"%3Cinit%3E(int,int,java.util.Map,org.xbib.graphics.io.vector.pdf.Payload)"},{"p":"org.xbib.graphics.io.vector.pdf","c":"PDFProcessor","l":"PDFProcessor()","u":"%3Cinit%3E()"},{"p":"org.xbib.graphics.io.vector.pdf","c":"PDFProcessor","l":"PDFProcessor(boolean)","u":"%3Cinit%3E(boolean)"},{"p":"org.xbib.graphics.io.vector.pdf","c":"PDFProcessorResult","l":"PDFProcessorResult(PageSize)","u":"%3Cinit%3E(org.xbib.graphics.io.vector.PageSize)"},{"p":"org.xbib.graphics.io.vector","c":"Processor","l":"process(Iterable>, PageSize)","u":"process(java.lang.Iterable,org.xbib.graphics.io.vector.PageSize)"},{"p":"org.xbib.graphics.io.vector.eps","c":"EPSProcessor","l":"process(Iterable>, PageSize)","u":"process(java.lang.Iterable,org.xbib.graphics.io.vector.PageSize)"},{"p":"org.xbib.graphics.io.vector.pdf","c":"PDFProcessor","l":"process(Iterable>, PageSize)","u":"process(java.lang.Iterable,org.xbib.graphics.io.vector.PageSize)"},{"p":"org.xbib.graphics.io.vector.svg","c":"SVGProcessor","l":"process(Iterable>, PageSize)","u":"process(java.lang.Iterable,org.xbib.graphics.io.vector.PageSize)"},{"p":"org.xbib.graphics.io.vector.util","c":"ImageDataStream","l":"read()"},{"p":"org.xbib.graphics.io.vector.filters","c":"Filter","l":"remove()"},{"p":"org.xbib.graphics.io.vector.pdf","c":"Resources","l":"Resources(int, int)","u":"%3Cinit%3E(int,int)"},{"p":"org.xbib.graphics.io.vector","c":"VectorGraphics2D","l":"rotate(double)"},{"p":"org.xbib.graphics.io.vector","c":"VectorGraphics2D","l":"rotate(double, double, double)","u":"rotate(double,double,double)"},{"p":"org.xbib.graphics.io.vector.commands","c":"RotateCommand","l":"RotateCommand(double, double, double)","u":"%3Cinit%3E(double,double,double)"},{"p":"org.xbib.graphics.io.vector.util","c":"ImageDataStream.Interleaving","l":"ROW"},{"p":"org.xbib.graphics.io.vector.util","c":"ImageDataStream.Interleaving","l":"SAMPLE"},{"p":"org.xbib.graphics.io.vector","c":"VectorGraphics2D","l":"scale(double, double)","u":"scale(double,double)"},{"p":"org.xbib.graphics.io.vector.commands","c":"ScaleCommand","l":"ScaleCommand(double, double)","u":"%3Cinit%3E(double,double)"},{"p":"org.xbib.graphics.io.vector","c":"GraphicsState","l":"setBackground(Color)","u":"setBackground(java.awt.Color)"},{"p":"org.xbib.graphics.io.vector","c":"VectorGraphics2D","l":"setBackground(Color)","u":"setBackground(java.awt.Color)"},{"p":"org.xbib.graphics.io.vector.commands","c":"SetBackgroundCommand","l":"SetBackgroundCommand(Color)","u":"%3Cinit%3E(java.awt.Color)"},{"p":"org.xbib.graphics.io.vector","c":"VectorGraphics2D","l":"setClip(int, int, int, int)","u":"setClip(int,int,int,int)"},{"p":"org.xbib.graphics.io.vector","c":"GraphicsState","l":"setClip(Shape)","u":"setClip(java.awt.Shape)"},{"p":"org.xbib.graphics.io.vector","c":"VectorGraphics2D","l":"setClip(Shape)","u":"setClip(java.awt.Shape)"},{"p":"org.xbib.graphics.io.vector.commands","c":"SetClipCommand","l":"SetClipCommand(Shape)","u":"%3Cinit%3E(java.awt.Shape)"},{"p":"org.xbib.graphics.io.vector","c":"GraphicsState","l":"setColor(Color)","u":"setColor(java.awt.Color)"},{"p":"org.xbib.graphics.io.vector","c":"VectorGraphics2D","l":"setColor(Color)","u":"setColor(java.awt.Color)"},{"p":"org.xbib.graphics.io.vector.commands","c":"SetColorCommand","l":"SetColorCommand(Color)","u":"%3Cinit%3E(java.awt.Color)"},{"p":"org.xbib.graphics.io.vector","c":"GraphicsState","l":"setComposite(Composite)","u":"setComposite(java.awt.Composite)"},{"p":"org.xbib.graphics.io.vector","c":"VectorGraphics2D","l":"setComposite(Composite)","u":"setComposite(java.awt.Composite)"},{"p":"org.xbib.graphics.io.vector.commands","c":"SetCompositeCommand","l":"SetCompositeCommand(Composite)","u":"%3Cinit%3E(java.awt.Composite)"},{"p":"org.xbib.graphics.io.vector.pdf","c":"PDFProcessorResult","l":"setCompressed(boolean)"},{"p":"org.xbib.graphics.io.vector","c":"GraphicsState","l":"setFont(Font)","u":"setFont(java.awt.Font)"},{"p":"org.xbib.graphics.io.vector","c":"VectorGraphics2D","l":"setFont(Font)","u":"setFont(java.awt.Font)"},{"p":"org.xbib.graphics.io.vector.commands","c":"SetFontCommand","l":"SetFontCommand(Font)","u":"%3Cinit%3E(java.awt.Font)"},{"p":"org.xbib.graphics.io.vector.commands","c":"SetHintCommand","l":"SetHintCommand(Object, Object)","u":"%3Cinit%3E(java.lang.Object,java.lang.Object)"},{"p":"org.xbib.graphics.io.vector","c":"GraphicsState","l":"setPaint(Paint)","u":"setPaint(java.awt.Paint)"},{"p":"org.xbib.graphics.io.vector","c":"VectorGraphics2D","l":"setPaint(Paint)","u":"setPaint(java.awt.Paint)"},{"p":"org.xbib.graphics.io.vector.commands","c":"SetPaintCommand","l":"SetPaintCommand(Paint)","u":"%3Cinit%3E(java.awt.Paint)"},{"p":"org.xbib.graphics.io.vector","c":"VectorGraphics2D","l":"setPaintMode()"},{"p":"org.xbib.graphics.io.vector","c":"VectorGraphics2D","l":"setRenderingHint(RenderingHints.Key, Object)","u":"setRenderingHint(java.awt.RenderingHints.Key,java.lang.Object)"},{"p":"org.xbib.graphics.io.vector","c":"VectorGraphics2D","l":"setRenderingHints(Map)","u":"setRenderingHints(java.util.Map)"},{"p":"org.xbib.graphics.io.vector","c":"GraphicsState","l":"setStroke(Stroke)","u":"setStroke(java.awt.Stroke)"},{"p":"org.xbib.graphics.io.vector","c":"VectorGraphics2D","l":"setStroke(Stroke)","u":"setStroke(java.awt.Stroke)"},{"p":"org.xbib.graphics.io.vector.commands","c":"SetStrokeCommand","l":"SetStrokeCommand(Stroke)","u":"%3Cinit%3E(java.awt.Stroke)"},{"p":"org.xbib.graphics.io.vector","c":"GraphicsState","l":"setTransform(AffineTransform)","u":"setTransform(java.awt.geom.AffineTransform)"},{"p":"org.xbib.graphics.io.vector","c":"VectorGraphics2D","l":"setTransform(AffineTransform)","u":"setTransform(java.awt.geom.AffineTransform)"},{"p":"org.xbib.graphics.io.vector.commands","c":"SetTransformCommand","l":"SetTransformCommand(AffineTransform)","u":"%3Cinit%3E(java.awt.geom.AffineTransform)"},{"p":"org.xbib.graphics.io.vector","c":"GraphicsState","l":"setXorMode(Color)","u":"setXorMode(java.awt.Color)"},{"p":"org.xbib.graphics.io.vector","c":"VectorGraphics2D","l":"setXORMode(Color)","u":"setXORMode(java.awt.Color)"},{"p":"org.xbib.graphics.io.vector.commands","c":"SetXORModeCommand","l":"SetXORModeCommand(Color)","u":"%3Cinit%3E(java.awt.Color)"},{"p":"org.xbib.graphics.io.vector","c":"VectorGraphics2D","l":"shear(double, double)","u":"shear(double,double)"},{"p":"org.xbib.graphics.io.vector.commands","c":"ShearCommand","l":"ShearCommand(double, double)","u":"%3Cinit%3E(double,double)"},{"p":"org.xbib.graphics.io.vector.pdf","c":"SizePayload","l":"SizePayload(PDFObject, String, boolean)","u":"%3Cinit%3E(org.xbib.graphics.io.vector.pdf.PDFObject,java.lang.String,boolean)"},{"p":"org.xbib.graphics.io.vector.util","c":"LineWrapOutputStream","l":"STANDARD_EOL"},{"p":"org.xbib.graphics.io.vector.filters","c":"StateChangeGroupingFilter","l":"StateChangeGroupingFilter(Iterable>)","u":"%3Cinit%3E(java.lang.Iterable)"},{"p":"org.xbib.graphics.io.vector.commands","c":"StateCommand","l":"StateCommand(T)","u":"%3Cinit%3E(T)"},{"p":"org.xbib.graphics.io.vector.util","c":"DataUtils","l":"stripTrailing(String, String)","u":"stripTrailing(java.lang.String,java.lang.String)"},{"p":"org.xbib.graphics.io.vector","c":"VectorGraphicsFormat","l":"SVG"},{"p":"org.xbib.graphics.io.vector.svg","c":"SVGGraphics2D","l":"SVGGraphics2D(double, double, double, double)","u":"%3Cinit%3E(double,double,double,double)"},{"p":"org.xbib.graphics.io.vector.svg","c":"SVGProcessor","l":"SVGProcessor()","u":"%3Cinit%3E()"},{"p":"org.xbib.graphics.io.vector.svg","c":"SVGProcessorResult","l":"SVGProcessorResult(PageSize)","u":"%3Cinit%3E(org.xbib.graphics.io.vector.PageSize)"},{"p":"org.xbib.graphics.io.vector","c":"PageSize","l":"TABLOID"},{"p":"org.xbib.graphics.io.vector.util","c":"FormattingWriter","l":"tell()"},{"p":"org.xbib.graphics.io.vector.util","c":"GraphicsUtils","l":"toBufferedImage(Image)","u":"toBufferedImage(java.awt.Image)"},{"p":"org.xbib.graphics.io.vector.util","c":"GraphicsUtils","l":"toBufferedImage(RenderedImage)","u":"toBufferedImage(java.awt.image.RenderedImage)"},{"p":"org.xbib.graphics.io.vector","c":"Command","l":"toString()"},{"p":"org.xbib.graphics.io.vector.commands","c":"DrawImageCommand","l":"toString()"},{"p":"org.xbib.graphics.io.vector.commands","c":"DrawStringCommand","l":"toString()"},{"p":"org.xbib.graphics.io.vector.commands","c":"RotateCommand","l":"toString()"},{"p":"org.xbib.graphics.io.vector.commands","c":"ScaleCommand","l":"toString()"},{"p":"org.xbib.graphics.io.vector.commands","c":"SetHintCommand","l":"toString()"},{"p":"org.xbib.graphics.io.vector.commands","c":"ShearCommand","l":"toString()"},{"p":"org.xbib.graphics.io.vector.commands","c":"TranslateCommand","l":"toString()"},{"p":"org.xbib.graphics.io.vector.svg","c":"SVGProcessorResult","l":"toString()"},{"p":"org.xbib.graphics.io.vector.util","c":"VectorHints.Key","l":"toString()"},{"p":"org.xbib.graphics.io.vector.util","c":"VectorHints.Value","l":"toString()"},{"p":"org.xbib.graphics.io.vector.pdf","c":"PDFProcessorResult","l":"toString(PDFObject)","u":"toString(org.xbib.graphics.io.vector.pdf.PDFObject)"},{"p":"org.xbib.graphics.io.vector.util","c":"DataUtils","l":"transfer(InputStream, OutputStream, int)","u":"transfer(java.io.InputStream,java.io.OutputStream,int)"},{"p":"org.xbib.graphics.io.vector","c":"VectorGraphics2D","l":"transform(AffineTransform)","u":"transform(java.awt.geom.AffineTransform)"},{"p":"org.xbib.graphics.io.vector.commands","c":"TransformCommand","l":"TransformCommand(AffineTransform)","u":"%3Cinit%3E(java.awt.geom.AffineTransform)"},{"p":"org.xbib.graphics.io.vector","c":"GraphicsState","l":"transformShape(Shape)","u":"transformShape(java.awt.Shape)"},{"p":"org.xbib.graphics.io.vector","c":"VectorGraphics2D","l":"translate(double, double)","u":"translate(double,double)"},{"p":"org.xbib.graphics.io.vector","c":"VectorGraphics2D","l":"translate(int, int)","u":"translate(int,int)"},{"p":"org.xbib.graphics.io.vector.commands","c":"TranslateCommand","l":"TranslateCommand(double, double)","u":"%3Cinit%3E(double,double)"},{"p":"org.xbib.graphics.io.vector","c":"GraphicsState","l":"untransformShape(Shape)","u":"untransformShape(java.awt.Shape)"},{"p":"org.xbib.graphics.io.vector.util","c":"GraphicsUtils","l":"usesAlpha(Image)","u":"usesAlpha(java.awt.Image)"},{"p":"org.xbib.graphics.io.vector.util","c":"VectorHints","l":"VALUE_EXPORT_QUALITY"},{"p":"org.xbib.graphics.io.vector.util","c":"VectorHints","l":"VALUE_EXPORT_READABILITY"},{"p":"org.xbib.graphics.io.vector.util","c":"VectorHints","l":"VALUE_EXPORT_SIZE"},{"p":"org.xbib.graphics.io.vector.util","c":"VectorHints","l":"VALUE_TEXT_DEFAULT"},{"p":"org.xbib.graphics.io.vector.util","c":"VectorHints","l":"VALUE_TEXT_VECTOR"},{"p":"org.xbib.graphics.io.vector.util","c":"VectorHints.Value","l":"Value(VectorHints.Key, int, String)","u":"%3Cinit%3E(org.xbib.graphics.io.vector.util.VectorHints.Key,int,java.lang.String)"},{"p":"org.xbib.graphics.io.vector","c":"VectorGraphicsFormat","l":"valueOf(String)","u":"valueOf(java.lang.String)"},{"p":"org.xbib.graphics.io.vector.util","c":"ImageDataStream.Interleaving","l":"valueOf(String)","u":"valueOf(java.lang.String)"},{"p":"org.xbib.graphics.io.vector","c":"VectorGraphicsFormat","l":"values()"},{"p":"org.xbib.graphics.io.vector.util","c":"ImageDataStream.Interleaving","l":"values()"},{"p":"org.xbib.graphics.io.vector","c":"VectorGraphics2D","l":"VectorGraphics2D()","u":"%3Cinit%3E()"},{"p":"org.xbib.graphics.io.vector","c":"VectorGraphics2D","l":"VectorGraphics2D(Processor, PageSize)","u":"%3Cinit%3E(org.xbib.graphics.io.vector.Processor,org.xbib.graphics.io.vector.PageSize)"},{"p":"org.xbib.graphics.io.vector.util","c":"VectorHints","l":"VectorHints()","u":"%3Cinit%3E()"},{"p":"org.xbib.graphics.io.vector.pdf","c":"PDFObject","l":"version"},{"p":"org.xbib.graphics.io.vector.util","c":"ImageDataStream.Interleaving","l":"WITHOUT_ALPHA"},{"p":"org.xbib.graphics.io.vector.pdf","c":"GeneratedPayload","l":"write(int)"},{"p":"org.xbib.graphics.io.vector.pdf","c":"Payload","l":"write(int)"},{"p":"org.xbib.graphics.io.vector.util","c":"ASCII85EncodeStream","l":"write(int)"},{"p":"org.xbib.graphics.io.vector.util","c":"Base64EncodeStream","l":"write(int)"},{"p":"org.xbib.graphics.io.vector.util","c":"LineWrapOutputStream","l":"write(int)"},{"p":"org.xbib.graphics.io.vector.util","c":"FormattingWriter","l":"write(Number)","u":"write(java.lang.Number)"},{"p":"org.xbib.graphics.io.vector","c":"ProcessorResult","l":"write(OutputStream)","u":"write(java.io.OutputStream)"},{"p":"org.xbib.graphics.io.vector.eps","c":"EPSProcessorResult","l":"write(OutputStream)","u":"write(java.io.OutputStream)"},{"p":"org.xbib.graphics.io.vector.pdf","c":"PDFProcessorResult","l":"write(OutputStream)","u":"write(java.io.OutputStream)"},{"p":"org.xbib.graphics.io.vector.svg","c":"SVGProcessorResult","l":"write(OutputStream)","u":"write(java.io.OutputStream)"},{"p":"org.xbib.graphics.io.vector.util","c":"FormattingWriter","l":"write(String)","u":"write(java.lang.String)"},{"p":"org.xbib.graphics.io.vector.util","c":"FormattingWriter","l":"writeln()"},{"p":"org.xbib.graphics.io.vector.util","c":"FormattingWriter","l":"writeln(Number)","u":"writeln(java.lang.Number)"},{"p":"org.xbib.graphics.io.vector.util","c":"FormattingWriter","l":"writeln(String)","u":"writeln(java.lang.String)"},{"p":"org.xbib.graphics.io.vector","c":"VectorGraphics2D","l":"writeTo(OutputStream)","u":"writeTo(java.io.OutputStream)"}];updateSearchResults(); \ No newline at end of file diff --git a/io-vector/build/docs/javadoc/module-search-index.js b/io-vector/build/docs/javadoc/module-search-index.js new file mode 100644 index 0000000..932a926 --- /dev/null +++ b/io-vector/build/docs/javadoc/module-search-index.js @@ -0,0 +1 @@ +moduleSearchIndex = [{"l":"org.xbib.graphics.io.vector"}];updateSearchResults(); \ No newline at end of file diff --git a/io-vector/build/docs/javadoc/org.xbib.graphics.io.vector/module-summary.html b/io-vector/build/docs/javadoc/org.xbib.graphics.io.vector/module-summary.html new file mode 100644 index 0000000..005cdf4 --- /dev/null +++ b/io-vector/build/docs/javadoc/org.xbib.graphics.io.vector/module-summary.html @@ -0,0 +1,138 @@ + + + + + +org.xbib.graphics.io.vector (io-vector 3.0.0 API) + + + + + + + + + + + + + +
+ +
+
+
+

Module org.xbib.graphics.io.vector

+
+
+ +
+
+ +
+
+ + diff --git a/io-vector/build/docs/javadoc/org.xbib.graphics.io.vector/org/xbib/graphics/io/vector/Command.html b/io-vector/build/docs/javadoc/org.xbib.graphics.io.vector/org/xbib/graphics/io/vector/Command.html new file mode 100644 index 0000000..976f26d --- /dev/null +++ b/io-vector/build/docs/javadoc/org.xbib.graphics.io.vector/org/xbib/graphics/io/vector/Command.html @@ -0,0 +1,252 @@ + + + + + +Command (io-vector 3.0.0 API) + + + + + + + + + + + + + +
+ +
+
+ +
+ + +

Class Command<T>

+
+
java.lang.Object +
org.xbib.graphics.io.vector.Command<T>
+
+
+
+
Direct Known Subclasses:
+
DrawImageCommand, DrawShapeCommand, DrawStringCommand, FillShapeCommand, Group, StateCommand
+
+
+
public abstract class Command<T>
+extends java.lang.Object
+
+
+
    + +
  • +
    +

    Constructor Summary

    +
    + + + + + + + + + + + + + + +
    Constructors
    ConstructorDescription
    Command​(T value) 
    +
    +
    +
  • + +
  • +
    +

    Method Summary

    +
    +
    +
    + + + + + + + + + + + + + + + + + + + + + + + + + +
    Modifier and TypeMethodDescription
    booleanequals​(java.lang.Object obj) 
    TgetValue() 
    java.lang.StringtoString() 
    +
    +
    +
    +

    Methods inherited from class java.lang.Object

    +clone, finalize, getClass, hashCode, notify, notifyAll, wait, wait, wait
    +
    +
  • +
+
+
+
    + +
  • +
    +

    Constructor Details

    +
      +
    • +
      +

      Command

      +
      public Command​(T value)
      +
      +
    • +
    +
    +
  • + +
  • +
    +

    Method Details

    +
      +
    • +
      +

      getValue

      +
      public T getValue()
      +
      +
    • +
    • +
      +

      toString

      +
      public java.lang.String toString()
      +
      +
      Overrides:
      +
      toString in class java.lang.Object
      +
      +
      +
    • +
    • +
      +

      equals

      +
      public boolean equals​(java.lang.Object obj)
      +
      +
      Overrides:
      +
      equals in class java.lang.Object
      +
      +
      +
    • +
    +
    +
  • +
+
+ +
+ +
+
+ + diff --git a/io-vector/build/docs/javadoc/org.xbib.graphics.io.vector/org/xbib/graphics/io/vector/GraphicsState.html b/io-vector/build/docs/javadoc/org.xbib.graphics.io.vector/org/xbib/graphics/io/vector/GraphicsState.html new file mode 100644 index 0000000..bd03826 --- /dev/null +++ b/io-vector/build/docs/javadoc/org.xbib.graphics.io.vector/org/xbib/graphics/io/vector/GraphicsState.html @@ -0,0 +1,656 @@ + + + + + +GraphicsState (io-vector 3.0.0 API) + + + + + + + + + + + + + +
+ +
+
+ +
+ + +

Class GraphicsState

+
+
java.lang.Object +
org.xbib.graphics.io.vector.GraphicsState
+
+
+
+
All Implemented Interfaces:
+
java.lang.Cloneable
+
+
+
public class GraphicsState
+extends java.lang.Object
+implements java.lang.Cloneable
+
+
+
    + +
  • +
    +

    Field Summary

    +
    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    Fields
    Modifier and TypeFieldDescription
    static java.awt.ColorDEFAULT_BACKGROUND +
    Default background color.
    +
    static java.awt.ShapeDEFAULT_CLIP +
    Default clipping shape.
    +
    static java.awt.ColorDEFAULT_COLOR +
    Default color.
    +
    static java.awt.CompositeDEFAULT_COMPOSITE +
    Default composite mode.
    +
    static java.awt.FontDEFAULT_FONT +
    Default font.
    +
    static java.awt.ColorDEFAULT_PAINT +
    Default paint.
    +
    static java.awt.StrokeDEFAULT_STROKE +
    Default stroke.
    +
    static java.awt.geom.AffineTransformDEFAULT_TRANSFORM +
    Default transformation.
    +
    static java.awt.ColorDEFAULT_XOR_MODE +
    Default XOR mode.
    +
    +
    +
    +
  • + +
  • +
    +

    Constructor Summary

    +
    + + + + + + + + + + + + + + +
    Constructors
    ConstructorDescription
    GraphicsState() 
    +
    +
    +
  • + +
  • +
    +

    Method Summary

    +
    +
    +
    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    Modifier and TypeMethodDescription
    java.lang.Objectclone() 
    booleanequals​(java.lang.Object obj) 
    java.awt.ColorgetBackground() 
    java.awt.ShapegetClip() 
    java.awt.ColorgetColor() 
    java.awt.CompositegetComposite() 
    java.awt.FontgetFont() 
    java.awt.RenderingHintsgetHints() 
    java.awt.PaintgetPaint() 
    java.awt.StrokegetStroke() 
    java.awt.geom.AffineTransformgetTransform() 
    java.awt.ColorgetXorMode() 
    inthashCode() 
    booleanisDefault() 
    voidsetBackground​(java.awt.Color background) 
    voidsetClip​(java.awt.Shape clip) 
    voidsetColor​(java.awt.Color color) 
    voidsetComposite​(java.awt.Composite composite) 
    voidsetFont​(java.awt.Font font) 
    voidsetPaint​(java.awt.Paint paint) 
    voidsetStroke​(java.awt.Stroke stroke) 
    voidsetTransform​(java.awt.geom.AffineTransform tx) 
    voidsetXorMode​(java.awt.Color xorMode) 
    java.awt.ShapetransformShape​(java.awt.Shape shape) 
    java.awt.ShapeuntransformShape​(java.awt.Shape shape) 
    +
    +
    +
    +

    Methods inherited from class java.lang.Object

    +finalize, getClass, notify, notifyAll, toString, wait, wait, wait
    +
    +
  • +
+
+
+
    + +
  • +
    +

    Field Details

    +
      +
    • +
      +

      DEFAULT_BACKGROUND

      +
      public static final java.awt.Color DEFAULT_BACKGROUND
      +
      Default background color.
      +
      +
    • +
    • +
      +

      DEFAULT_COLOR

      +
      public static final java.awt.Color DEFAULT_COLOR
      +
      Default color.
      +
      +
    • +
    • +
      +

      DEFAULT_CLIP

      +
      public static final java.awt.Shape DEFAULT_CLIP
      +
      Default clipping shape.
      +
      +
    • +
    • +
      +

      DEFAULT_COMPOSITE

      +
      public static final java.awt.Composite DEFAULT_COMPOSITE
      +
      Default composite mode.
      +
      +
    • +
    • +
      +

      DEFAULT_FONT

      +
      public static final java.awt.Font DEFAULT_FONT
      +
      Default font.
      +
      +
    • +
    • +
      +

      DEFAULT_PAINT

      +
      public static final java.awt.Color DEFAULT_PAINT
      +
      Default paint.
      +
      +
    • +
    • +
      +

      DEFAULT_STROKE

      +
      public static final java.awt.Stroke DEFAULT_STROKE
      +
      Default stroke.
      +
      +
    • +
    • +
      +

      DEFAULT_TRANSFORM

      +
      public static final java.awt.geom.AffineTransform DEFAULT_TRANSFORM
      +
      Default transformation.
      +
      +
    • +
    • +
      +

      DEFAULT_XOR_MODE

      +
      public static final java.awt.Color DEFAULT_XOR_MODE
      +
      Default XOR mode.
      +
      +
    • +
    +
    +
  • + +
  • +
    +

    Constructor Details

    +
      +
    • +
      +

      GraphicsState

      +
      public GraphicsState()
      +
      +
    • +
    +
    +
  • + +
  • +
    +

    Method Details

    +
      +
    • +
      +

      clone

      +
      public java.lang.Object clone() + throws java.lang.CloneNotSupportedException
      +
      +
      Overrides:
      +
      clone in class java.lang.Object
      +
      Throws:
      +
      java.lang.CloneNotSupportedException
      +
      +
      +
    • +
    • +
      +

      transformShape

      +
      public java.awt.Shape transformShape​(java.awt.Shape shape)
      +
      +
    • +
    • +
      +

      untransformShape

      +
      public java.awt.Shape untransformShape​(java.awt.Shape shape)
      +
      +
    • +
    • +
      +

      getHints

      +
      public java.awt.RenderingHints getHints()
      +
      +
    • +
    • +
      +

      getBackground

      +
      public java.awt.Color getBackground()
      +
      +
    • +
    • +
      +

      setBackground

      +
      public void setBackground​(java.awt.Color background)
      +
      +
    • +
    • +
      +

      getColor

      +
      public java.awt.Color getColor()
      +
      +
    • +
    • +
      +

      setColor

      +
      public void setColor​(java.awt.Color color)
      +
      +
    • +
    • +
      +

      getClip

      +
      public java.awt.Shape getClip()
      +
      +
    • +
    • +
      +

      setClip

      +
      public void setClip​(java.awt.Shape clip)
      +
      +
    • +
    • +
      +

      getComposite

      +
      public java.awt.Composite getComposite()
      +
      +
    • +
    • +
      +

      setComposite

      +
      public void setComposite​(java.awt.Composite composite)
      +
      +
    • +
    • +
      +

      getFont

      +
      public java.awt.Font getFont()
      +
      +
    • +
    • +
      +

      setFont

      +
      public void setFont​(java.awt.Font font)
      +
      +
    • +
    • +
      +

      getPaint

      +
      public java.awt.Paint getPaint()
      +
      +
    • +
    • +
      +

      setPaint

      +
      public void setPaint​(java.awt.Paint paint)
      +
      +
    • +
    • +
      +

      getStroke

      +
      public java.awt.Stroke getStroke()
      +
      +
    • +
    • +
      +

      setStroke

      +
      public void setStroke​(java.awt.Stroke stroke)
      +
      +
    • +
    • +
      +

      getTransform

      +
      public java.awt.geom.AffineTransform getTransform()
      +
      +
    • +
    • +
      +

      setTransform

      +
      public void setTransform​(java.awt.geom.AffineTransform tx)
      +
      +
    • +
    • +
      +

      getXorMode

      +
      public java.awt.Color getXorMode()
      +
      +
    • +
    • +
      +

      setXorMode

      +
      public void setXorMode​(java.awt.Color xorMode)
      +
      +
    • +
    • +
      +

      equals

      +
      public boolean equals​(java.lang.Object obj)
      +
      +
      Overrides:
      +
      equals in class java.lang.Object
      +
      +
      +
    • +
    • +
      +

      hashCode

      +
      public int hashCode()
      +
      +
      Overrides:
      +
      hashCode in class java.lang.Object
      +
      +
      +
    • +
    • +
      +

      isDefault

      +
      public boolean isDefault()
      +
      +
    • +
    +
    +
  • +
+
+ +
+ +
+
+ + diff --git a/io-vector/build/docs/javadoc/org.xbib.graphics.io.vector/org/xbib/graphics/io/vector/PageSize.html b/io-vector/build/docs/javadoc/org.xbib.graphics.io.vector/org/xbib/graphics/io/vector/PageSize.html new file mode 100644 index 0000000..8781738 --- /dev/null +++ b/io-vector/build/docs/javadoc/org.xbib.graphics.io.vector/org/xbib/graphics/io/vector/PageSize.html @@ -0,0 +1,406 @@ + + + + + +PageSize (io-vector 3.0.0 API) + + + + + + + + + + + + + +
+ +
+
+ +
+ + +

Class PageSize

+
+
java.lang.Object +
org.xbib.graphics.io.vector.PageSize
+
+
+
+
public class PageSize
+extends java.lang.Object
+
+
+
    + +
  • +
    +

    Field Summary

    +
    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    Fields
    Modifier and TypeFieldDescription
    static PageSizeA3 
    static PageSizeA4 
    static PageSizeA5 
    static PageSizeLEDGER 
    static PageSizeLEGAL 
    static PageSizeLETTER 
    static PageSizeTABLOID 
    +
    +
    +
  • + +
  • +
    +

    Constructor Summary

    +
    + + + + + + + + + + + + + + + + + + + + + + +
    Constructors
    ConstructorDescription
    PageSize​(double width, +double height) 
    PageSize​(double x, +double y, +double width, +double height) 
    PageSize​(java.awt.geom.Rectangle2D size) 
    +
    +
    +
  • + +
  • +
    +

    Method Summary

    +
    +
    +
    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    Modifier and TypeMethodDescription
    doublegetHeight() 
    PageSizegetLandscape() 
    PageSizegetPortrait() 
    doublegetWidth() 
    doublegetX() 
    doublegetY() 
    +
    +
    +
    +

    Methods inherited from class java.lang.Object

    +clone, equals, finalize, getClass, hashCode, notify, notifyAll, toString, wait, wait, wait
    +
    +
  • +
+
+
+
    + +
  • +
    +

    Field Details

    +
      +
    • +
      +

      TABLOID

      +
      public static final PageSize TABLOID
      +
      +
    • +
    • +
      +

      LETTER

      +
      public static final PageSize LETTER
      +
      +
    • +
    • + +
    • +
    • +
      +

      LEDGER

      +
      public static final PageSize LEDGER
      +
      +
    • +
    • +
      +

      A3

      +
      public static final PageSize A3
      +
      +
    • +
    • +
      +

      A4

      +
      public static final PageSize A4
      +
      +
    • +
    • +
      +

      A5

      +
      public static final PageSize A5
      +
      +
    • +
    +
    +
  • + +
  • +
    +

    Constructor Details

    +
      +
    • +
      +

      PageSize

      +
      public PageSize​(double x, +double y, +double width, +double height)
      +
      +
    • +
    • +
      +

      PageSize

      +
      public PageSize​(double width, +double height)
      +
      +
    • +
    • +
      +

      PageSize

      +
      public PageSize​(java.awt.geom.Rectangle2D size)
      +
      +
    • +
    +
    +
  • + +
  • +
    +

    Method Details

    +
      +
    • +
      +

      getPortrait

      +
      public PageSize getPortrait()
      +
      +
    • +
    • +
      +

      getLandscape

      +
      public PageSize getLandscape()
      +
      +
    • +
    • +
      +

      getX

      +
      public double getX()
      +
      +
    • +
    • +
      +

      getY

      +
      public double getY()
      +
      +
    • +
    • +
      +

      getHeight

      +
      public double getHeight()
      +
      +
    • +
    • +
      +

      getWidth

      +
      public double getWidth()
      +
      +
    • +
    +
    +
  • +
+
+ +
+ +
+
+ + diff --git a/io-vector/build/docs/javadoc/org.xbib.graphics.io.vector/org/xbib/graphics/io/vector/Processor.html b/io-vector/build/docs/javadoc/org.xbib.graphics.io.vector/org/xbib/graphics/io/vector/Processor.html new file mode 100644 index 0000000..8535fad --- /dev/null +++ b/io-vector/build/docs/javadoc/org.xbib.graphics.io.vector/org/xbib/graphics/io/vector/Processor.html @@ -0,0 +1,185 @@ + + + + + +Processor (io-vector 3.0.0 API) + + + + + + + + + + + + + +
+ +
+
+ +
+ + +

Interface Processor

+
+
+
+
All Known Implementing Classes:
+
EPSProcessor, PDFProcessor, SVGProcessor
+
+
+
public interface Processor
+
+
+
    + +
  • +
    +

    Method Summary

    +
    +
    +
    + + + + + + + + + + + + + + + +
    Modifier and TypeMethodDescription
    ProcessorResultprocess​(java.lang.Iterable<Command<?>> commands, +PageSize pageSize) 
    +
    +
    +
    +
  • +
+
+
+
    + +
  • +
    +

    Method Details

    +
      +
    • +
      +

      process

      +
      ProcessorResult process​(java.lang.Iterable<Command<?>> commands, +PageSize pageSize) + throws java.io.IOException
      +
      +
      Throws:
      +
      java.io.IOException
      +
      +
      +
    • +
    +
    +
  • +
+
+ +
+ +
+
+ + diff --git a/io-vector/build/docs/javadoc/org.xbib.graphics.io.vector/org/xbib/graphics/io/vector/ProcessorResult.html b/io-vector/build/docs/javadoc/org.xbib.graphics.io.vector/org/xbib/graphics/io/vector/ProcessorResult.html new file mode 100644 index 0000000..4d552b7 --- /dev/null +++ b/io-vector/build/docs/javadoc/org.xbib.graphics.io.vector/org/xbib/graphics/io/vector/ProcessorResult.html @@ -0,0 +1,215 @@ + + + + + +ProcessorResult (io-vector 3.0.0 API) + + + + + + + + + + + + + +
+ +
+
+ +
+ + +

Interface ProcessorResult

+
+
+
+
All Known Implementing Classes:
+
EPSProcessorResult, PDFProcessorResult, SVGProcessorResult
+
+
+
public interface ProcessorResult
+
+
+
    + +
  • +
    +

    Method Summary

    +
    +
    +
    + + + + + + + + + + + + + + + + + + + + + + + + + +
    Modifier and TypeMethodDescription
    voidclose() 
    voidhandle​(Command<?> command) 
    voidwrite​(java.io.OutputStream out) 
    +
    +
    +
    +
  • +
+
+
+
    + +
  • +
    +

    Method Details

    +
      +
    • +
      +

      handle

      +
      void handle​(Command<?> command) + throws java.io.IOException
      +
      +
      Throws:
      +
      java.io.IOException
      +
      +
      +
    • +
    • +
      +

      write

      +
      void write​(java.io.OutputStream out) + throws java.io.IOException
      +
      +
      Throws:
      +
      java.io.IOException
      +
      +
      +
    • +
    • +
      +

      close

      +
      void close() + throws java.io.IOException
      +
      +
      Throws:
      +
      java.io.IOException
      +
      +
      +
    • +
    +
    +
  • +
+
+ +
+ +
+
+ + diff --git a/io-vector/build/docs/javadoc/org.xbib.graphics.io.vector/org/xbib/graphics/io/vector/VectorGraphics2D.html b/io-vector/build/docs/javadoc/org.xbib.graphics.io.vector/org/xbib/graphics/io/vector/VectorGraphics2D.html new file mode 100644 index 0000000..9d27dfb --- /dev/null +++ b/io-vector/build/docs/javadoc/org.xbib.graphics.io.vector/org/xbib/graphics/io/vector/VectorGraphics2D.html @@ -0,0 +1,1664 @@ + + + + + +VectorGraphics2D (io-vector 3.0.0 API) + + + + + + + + + + + + + +
+ +
+
+ +
+ + +

Class VectorGraphics2D

+
+
java.lang.Object +
java.awt.Graphics +
java.awt.Graphics2D +
org.xbib.graphics.io.vector.VectorGraphics2D
+
+
+
+
+
+
All Implemented Interfaces:
+
java.lang.Cloneable
+
+
+
Direct Known Subclasses:
+
EPSGraphics2D, PDFGraphics2D, SVGGraphics2D
+
+
+
public class VectorGraphics2D
+extends java.awt.Graphics2D
+implements java.lang.Cloneable
+
Base for classes that want to implement vector export.
+
+
+
    + +
  • +
    +

    Constructor Summary

    +
    + + + + + + + + + + + + + + + + + + +
    Constructors
    ConstructorDescription
    VectorGraphics2D() 
    VectorGraphics2D​(Processor processor, +PageSize pageSize) 
    +
    +
    +
  • + +
  • +
    +

    Method Summary

    +
    +
    +
    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    Modifier and TypeMethodDescription
    voidaddRenderingHints​(java.util.Map<?,​?> hints) 
    voidclearRect​(int x, +int y, +int width, +int height) 
    voidclip​(java.awt.Shape s) 
    voidclipRect​(int x, +int y, +int width, +int height) 
    java.lang.Objectclone() 
    voidcopyArea​(int x, +int y, +int width, +int height, +int dx, +int dy) 
    java.awt.Graphicscreate() 
    voiddispose() 
    voiddraw​(java.awt.Shape s) 
    voiddrawArc​(int x, +int y, +int width, +int height, +int startAngle, +int arcAngle) 
    voiddrawGlyphVector​(java.awt.font.GlyphVector g, +float x, +float y) 
    voiddrawImage​(java.awt.image.BufferedImage img, +java.awt.image.BufferedImageOp op, +int x, +int y) 
    booleandrawImage​(java.awt.Image img, +int dx1, +int dy1, +int dx2, +int dy2, +int sx1, +int sy1, +int sx2, +int sy2, +java.awt.Color bgcolor, +java.awt.image.ImageObserver observer) 
    booleandrawImage​(java.awt.Image img, +int dx1, +int dy1, +int dx2, +int dy2, +int sx1, +int sy1, +int sx2, +int sy2, +java.awt.image.ImageObserver observer) 
    booleandrawImage​(java.awt.Image img, +int x, +int y, +int width, +int height, +java.awt.Color bgcolor, +java.awt.image.ImageObserver observer) 
    booleandrawImage​(java.awt.Image img, +int x, +int y, +int width, +int height, +java.awt.image.ImageObserver observer) 
    booleandrawImage​(java.awt.Image img, +int x, +int y, +java.awt.Color bgcolor, +java.awt.image.ImageObserver observer) 
    booleandrawImage​(java.awt.Image img, +int x, +int y, +java.awt.image.ImageObserver observer) 
    booleandrawImage​(java.awt.Image img, +java.awt.geom.AffineTransform xform, +java.awt.image.ImageObserver obs) 
    voiddrawLine​(int x1, +int y1, +int x2, +int y2) 
    voiddrawOval​(int x, +int y, +int width, +int height) 
    voiddrawPolygon​(int[] xPoints, +int[] yPoints, +int nPoints) 
    voiddrawPolygon​(java.awt.Polygon p) 
    voiddrawPolyline​(int[] xPoints, +int[] yPoints, +int nPoints) 
    voiddrawRect​(int x, +int y, +int width, +int height) 
    voiddrawRenderableImage​(java.awt.image.renderable.RenderableImage img, +java.awt.geom.AffineTransform xform) 
    voiddrawRenderedImage​(java.awt.image.RenderedImage img, +java.awt.geom.AffineTransform xform) 
    voiddrawRoundRect​(int x, +int y, +int width, +int height, +int arcWidth, +int arcHeight) 
    voiddrawString​(java.lang.String str, +float x, +float y) 
    voiddrawString​(java.lang.String str, +int x, +int y) 
    voiddrawString​(java.text.AttributedCharacterIterator iterator, +float x, +float y) 
    voiddrawString​(java.text.AttributedCharacterIterator iterator, +int x, +int y) 
    voidfill​(java.awt.Shape s) 
    voidfillArc​(int x, +int y, +int width, +int height, +int startAngle, +int arcAngle) 
    voidfillOval​(int x, +int y, +int width, +int height) 
    voidfillPolygon​(int[] xPoints, +int[] yPoints, +int nPoints) 
    voidfillPolygon​(java.awt.Polygon p) 
    voidfillRect​(int x, +int y, +int width, +int height) 
    voidfillRoundRect​(int x, +int y, +int width, +int height, +int arcWidth, +int arcHeight) 
    java.awt.ColorgetBackground() 
    byte[]getBytes() 
    java.awt.ShapegetClip() 
    java.awt.RectanglegetClipBounds() 
    java.awt.ColorgetColor() 
    protected java.lang.Iterable<Command<?>>getCommands() 
    java.awt.CompositegetComposite() 
    java.awt.GraphicsConfigurationgetDeviceConfiguration() 
    java.awt.FontgetFont() 
    java.awt.FontMetricsgetFontMetrics​(java.awt.Font f) 
    java.awt.font.FontRenderContextgetFontRenderContext() 
    PageSizegetPageSize() 
    java.awt.PaintgetPaint() 
    java.lang.ObjectgetRenderingHint​(java.awt.RenderingHints.Key hintKey) 
    java.awt.RenderingHintsgetRenderingHints() 
    java.awt.StrokegetStroke() 
    java.awt.geom.AffineTransformgetTransform() 
    java.awt.ColorgetXORMode() 
    booleanhit​(java.awt.Rectangle rect, +java.awt.Shape s, +boolean onStroke) 
    protected booleanisDisposed() 
    voidrotate​(double theta) 
    voidrotate​(double theta, +double x, +double y) 
    voidscale​(double sx, +double sy) 
    voidsetBackground​(java.awt.Color color) 
    voidsetClip​(int x, +int y, +int width, +int height) 
    voidsetClip​(java.awt.Shape clip) 
    voidsetColor​(java.awt.Color c) 
    voidsetComposite​(java.awt.Composite comp) 
    voidsetFont​(java.awt.Font font) 
    voidsetPaint​(java.awt.Paint paint) 
    voidsetPaintMode() 
    voidsetRenderingHint​(java.awt.RenderingHints.Key hintKey, +java.lang.Object hintValue) 
    voidsetRenderingHints​(java.util.Map<?,​?> hints) 
    voidsetStroke​(java.awt.Stroke s) 
    voidsetTransform​(java.awt.geom.AffineTransform tx) 
    voidsetXORMode​(java.awt.Color c1) 
    voidshear​(double shx, +double shy) 
    voidtransform​(java.awt.geom.AffineTransform tx) 
    voidtranslate​(double tx, +double ty) 
    voidtranslate​(int x, +int y) 
    voidwriteTo​(java.io.OutputStream out) 
    +
    +
    +
    +

    Methods inherited from class java.awt.Graphics2D

    +draw3DRect, fill3DRect
    +
    +

    Methods inherited from class java.awt.Graphics

    +create, drawBytes, drawChars, finalize, getClipBounds, getClipRect, getFontMetrics, hitClip, toString
    +
    +

    Methods inherited from class java.lang.Object

    +equals, getClass, hashCode, notify, notifyAll, wait, wait, wait
    +
    +
  • +
+
+
+
    + +
  • +
    +

    Constructor Details

    +
      +
    • +
      +

      VectorGraphics2D

      +
      public VectorGraphics2D()
      +
      +
    • +
    • +
      +

      VectorGraphics2D

      +
      public VectorGraphics2D​(Processor processor, +PageSize pageSize)
      +
      +
    • +
    +
    +
  • + +
  • +
    +

    Method Details

    +
      +
    • +
      +

      getPageSize

      +
      public PageSize getPageSize()
      +
      +
    • +
    • +
      +

      getBytes

      +
      public byte[] getBytes() + throws java.io.IOException
      +
      +
      Throws:
      +
      java.io.IOException
      +
      +
      +
    • +
    • +
      +

      writeTo

      +
      public void writeTo​(java.io.OutputStream out) + throws java.io.IOException
      +
      +
      Throws:
      +
      java.io.IOException
      +
      +
      +
    • +
    • +
      +

      clone

      +
      public java.lang.Object clone() + throws java.lang.CloneNotSupportedException
      +
      +
      Overrides:
      +
      clone in class java.lang.Object
      +
      Throws:
      +
      java.lang.CloneNotSupportedException
      +
      +
      +
    • +
    • +
      +

      addRenderingHints

      +
      public void addRenderingHints​(java.util.Map<?,​?> hints)
      +
      +
      Specified by:
      +
      addRenderingHints in class java.awt.Graphics2D
      +
      +
      +
    • +
    • +
      +

      clip

      +
      public void clip​(java.awt.Shape s)
      +
      +
      Specified by:
      +
      clip in class java.awt.Graphics2D
      +
      +
      +
    • +
    • +
      +

      draw

      +
      public void draw​(java.awt.Shape s)
      +
      +
      Specified by:
      +
      draw in class java.awt.Graphics2D
      +
      +
      +
    • +
    • +
      +

      drawGlyphVector

      +
      public void drawGlyphVector​(java.awt.font.GlyphVector g, +float x, +float y)
      +
      +
      Specified by:
      +
      drawGlyphVector in class java.awt.Graphics2D
      +
      +
      +
    • +
    • +
      +

      drawImage

      +
      public boolean drawImage​(java.awt.Image img, +java.awt.geom.AffineTransform xform, +java.awt.image.ImageObserver obs)
      +
      +
      Specified by:
      +
      drawImage in class java.awt.Graphics2D
      +
      +
      +
    • +
    • +
      +

      drawImage

      +
      public void drawImage​(java.awt.image.BufferedImage img, +java.awt.image.BufferedImageOp op, +int x, +int y)
      +
      +
      Specified by:
      +
      drawImage in class java.awt.Graphics2D
      +
      +
      +
    • +
    • +
      +

      drawRenderableImage

      +
      public void drawRenderableImage​(java.awt.image.renderable.RenderableImage img, +java.awt.geom.AffineTransform xform)
      +
      +
      Specified by:
      +
      drawRenderableImage in class java.awt.Graphics2D
      +
      +
      +
    • +
    • +
      +

      drawRenderedImage

      +
      public void drawRenderedImage​(java.awt.image.RenderedImage img, +java.awt.geom.AffineTransform xform)
      +
      +
      Specified by:
      +
      drawRenderedImage in class java.awt.Graphics2D
      +
      +
      +
    • +
    • +
      +

      drawString

      +
      public void drawString​(java.lang.String str, +int x, +int y)
      +
      +
      Specified by:
      +
      drawString in class java.awt.Graphics2D
      +
      +
      +
    • +
    • +
      +

      drawString

      +
      public void drawString​(java.lang.String str, +float x, +float y)
      +
      +
      Specified by:
      +
      drawString in class java.awt.Graphics2D
      +
      +
      +
    • +
    • +
      +

      drawString

      +
      public void drawString​(java.text.AttributedCharacterIterator iterator, +int x, +int y)
      +
      +
      Specified by:
      +
      drawString in class java.awt.Graphics2D
      +
      +
      +
    • +
    • +
      +

      drawString

      +
      public void drawString​(java.text.AttributedCharacterIterator iterator, +float x, +float y)
      +
      +
      Specified by:
      +
      drawString in class java.awt.Graphics2D
      +
      +
      +
    • +
    • +
      +

      fill

      +
      public void fill​(java.awt.Shape s)
      +
      +
      Specified by:
      +
      fill in class java.awt.Graphics2D
      +
      +
      +
    • +
    • +
      +

      getBackground

      +
      public java.awt.Color getBackground()
      +
      +
      Specified by:
      +
      getBackground in class java.awt.Graphics2D
      +
      +
      +
    • +
    • +
      +

      setBackground

      +
      public void setBackground​(java.awt.Color color)
      +
      +
      Specified by:
      +
      setBackground in class java.awt.Graphics2D
      +
      +
      +
    • +
    • +
      +

      getComposite

      +
      public java.awt.Composite getComposite()
      +
      +
      Specified by:
      +
      getComposite in class java.awt.Graphics2D
      +
      +
      +
    • +
    • +
      +

      setComposite

      +
      public void setComposite​(java.awt.Composite comp)
      +
      +
      Specified by:
      +
      setComposite in class java.awt.Graphics2D
      +
      +
      +
    • +
    • +
      +

      getDeviceConfiguration

      +
      public java.awt.GraphicsConfiguration getDeviceConfiguration()
      +
      +
      Specified by:
      +
      getDeviceConfiguration in class java.awt.Graphics2D
      +
      +
      +
    • +
    • +
      +

      getFontRenderContext

      +
      public java.awt.font.FontRenderContext getFontRenderContext()
      +
      +
      Specified by:
      +
      getFontRenderContext in class java.awt.Graphics2D
      +
      +
      +
    • +
    • +
      +

      getPaint

      +
      public java.awt.Paint getPaint()
      +
      +
      Specified by:
      +
      getPaint in class java.awt.Graphics2D
      +
      +
      +
    • +
    • +
      +

      setPaint

      +
      public void setPaint​(java.awt.Paint paint)
      +
      +
      Specified by:
      +
      setPaint in class java.awt.Graphics2D
      +
      +
      +
    • +
    • +
      +

      getRenderingHint

      +
      public java.lang.Object getRenderingHint​(java.awt.RenderingHints.Key hintKey)
      +
      +
      Specified by:
      +
      getRenderingHint in class java.awt.Graphics2D
      +
      +
      +
    • +
    • +
      +

      getRenderingHints

      +
      public java.awt.RenderingHints getRenderingHints()
      +
      +
      Specified by:
      +
      getRenderingHints in class java.awt.Graphics2D
      +
      +
      +
    • +
    • +
      +

      setRenderingHints

      +
      public void setRenderingHints​(java.util.Map<?,​?> hints)
      +
      +
      Specified by:
      +
      setRenderingHints in class java.awt.Graphics2D
      +
      +
      +
    • +
    • +
      +

      getStroke

      +
      public java.awt.Stroke getStroke()
      +
      +
      Specified by:
      +
      getStroke in class java.awt.Graphics2D
      +
      +
      +
    • +
    • +
      +

      setStroke

      +
      public void setStroke​(java.awt.Stroke s)
      +
      +
      Specified by:
      +
      setStroke in class java.awt.Graphics2D
      +
      +
      +
    • +
    • +
      +

      hit

      +
      public boolean hit​(java.awt.Rectangle rect, +java.awt.Shape s, +boolean onStroke)
      +
      +
      Specified by:
      +
      hit in class java.awt.Graphics2D
      +
      +
      +
    • +
    • +
      +

      setRenderingHint

      +
      public void setRenderingHint​(java.awt.RenderingHints.Key hintKey, +java.lang.Object hintValue)
      +
      +
      Specified by:
      +
      setRenderingHint in class java.awt.Graphics2D
      +
      +
      +
    • +
    • +
      +

      getTransform

      +
      public java.awt.geom.AffineTransform getTransform()
      +
      +
      Specified by:
      +
      getTransform in class java.awt.Graphics2D
      +
      +
      +
    • +
    • +
      +

      setTransform

      +
      public void setTransform​(java.awt.geom.AffineTransform tx)
      +
      +
      Specified by:
      +
      setTransform in class java.awt.Graphics2D
      +
      +
      +
    • +
    • +
      +

      shear

      +
      public void shear​(double shx, +double shy)
      +
      +
      Specified by:
      +
      shear in class java.awt.Graphics2D
      +
      +
      +
    • +
    • +
      +

      transform

      +
      public void transform​(java.awt.geom.AffineTransform tx)
      +
      +
      Specified by:
      +
      transform in class java.awt.Graphics2D
      +
      +
      +
    • +
    • +
      +

      translate

      +
      public void translate​(int x, +int y)
      +
      +
      Specified by:
      +
      translate in class java.awt.Graphics2D
      +
      +
      +
    • +
    • +
      +

      translate

      +
      public void translate​(double tx, +double ty)
      +
      +
      Specified by:
      +
      translate in class java.awt.Graphics2D
      +
      +
      +
    • +
    • +
      +

      rotate

      +
      public void rotate​(double theta)
      +
      +
      Specified by:
      +
      rotate in class java.awt.Graphics2D
      +
      +
      +
    • +
    • +
      +

      rotate

      +
      public void rotate​(double theta, +double x, +double y)
      +
      +
      Specified by:
      +
      rotate in class java.awt.Graphics2D
      +
      +
      +
    • +
    • +
      +

      scale

      +
      public void scale​(double sx, +double sy)
      +
      +
      Specified by:
      +
      scale in class java.awt.Graphics2D
      +
      +
      +
    • +
    • +
      +

      clearRect

      +
      public void clearRect​(int x, +int y, +int width, +int height)
      +
      +
      Specified by:
      +
      clearRect in class java.awt.Graphics
      +
      +
      +
    • +
    • +
      +

      clipRect

      +
      public void clipRect​(int x, +int y, +int width, +int height)
      +
      +
      Specified by:
      +
      clipRect in class java.awt.Graphics
      +
      +
      +
    • +
    • +
      +

      copyArea

      +
      public void copyArea​(int x, +int y, +int width, +int height, +int dx, +int dy)
      +
      +
      Specified by:
      +
      copyArea in class java.awt.Graphics
      +
      +
      +
    • +
    • +
      +

      create

      +
      public java.awt.Graphics create()
      +
      +
      Specified by:
      +
      create in class java.awt.Graphics
      +
      +
      +
    • +
    • +
      +

      dispose

      +
      public void dispose()
      +
      +
      Specified by:
      +
      dispose in class java.awt.Graphics
      +
      +
      +
    • +
    • +
      +

      drawArc

      +
      public void drawArc​(int x, +int y, +int width, +int height, +int startAngle, +int arcAngle)
      +
      +
      Specified by:
      +
      drawArc in class java.awt.Graphics
      +
      +
      +
    • +
    • +
      +

      drawImage

      +
      public boolean drawImage​(java.awt.Image img, +int x, +int y, +java.awt.image.ImageObserver observer)
      +
      +
      Specified by:
      +
      drawImage in class java.awt.Graphics
      +
      +
      +
    • +
    • +
      +

      drawImage

      +
      public boolean drawImage​(java.awt.Image img, +int x, +int y, +java.awt.Color bgcolor, +java.awt.image.ImageObserver observer)
      +
      +
      Specified by:
      +
      drawImage in class java.awt.Graphics
      +
      +
      +
    • +
    • +
      +

      drawImage

      +
      public boolean drawImage​(java.awt.Image img, +int x, +int y, +int width, +int height, +java.awt.image.ImageObserver observer)
      +
      +
      Specified by:
      +
      drawImage in class java.awt.Graphics
      +
      +
      +
    • +
    • +
      +

      drawImage

      +
      public boolean drawImage​(java.awt.Image img, +int x, +int y, +int width, +int height, +java.awt.Color bgcolor, +java.awt.image.ImageObserver observer)
      +
      +
      Specified by:
      +
      drawImage in class java.awt.Graphics
      +
      +
      +
    • +
    • +
      +

      drawImage

      +
      public boolean drawImage​(java.awt.Image img, +int dx1, +int dy1, +int dx2, +int dy2, +int sx1, +int sy1, +int sx2, +int sy2, +java.awt.image.ImageObserver observer)
      +
      +
      Specified by:
      +
      drawImage in class java.awt.Graphics
      +
      +
      +
    • +
    • +
      +

      drawImage

      +
      public boolean drawImage​(java.awt.Image img, +int dx1, +int dy1, +int dx2, +int dy2, +int sx1, +int sy1, +int sx2, +int sy2, +java.awt.Color bgcolor, +java.awt.image.ImageObserver observer)
      +
      +
      Specified by:
      +
      drawImage in class java.awt.Graphics
      +
      +
      +
    • +
    • +
      +

      drawLine

      +
      public void drawLine​(int x1, +int y1, +int x2, +int y2)
      +
      +
      Specified by:
      +
      drawLine in class java.awt.Graphics
      +
      +
      +
    • +
    • +
      +

      drawOval

      +
      public void drawOval​(int x, +int y, +int width, +int height)
      +
      +
      Specified by:
      +
      drawOval in class java.awt.Graphics
      +
      +
      +
    • +
    • +
      +

      drawPolygon

      +
      public void drawPolygon​(java.awt.Polygon p)
      +
      +
      Overrides:
      +
      drawPolygon in class java.awt.Graphics
      +
      +
      +
    • +
    • +
      +

      drawPolygon

      +
      public void drawPolygon​(int[] xPoints, +int[] yPoints, +int nPoints)
      +
      +
      Specified by:
      +
      drawPolygon in class java.awt.Graphics
      +
      +
      +
    • +
    • +
      +

      drawPolyline

      +
      public void drawPolyline​(int[] xPoints, +int[] yPoints, +int nPoints)
      +
      +
      Specified by:
      +
      drawPolyline in class java.awt.Graphics
      +
      +
      +
    • +
    • +
      +

      drawRect

      +
      public void drawRect​(int x, +int y, +int width, +int height)
      +
      +
      Overrides:
      +
      drawRect in class java.awt.Graphics
      +
      +
      +
    • +
    • +
      +

      drawRoundRect

      +
      public void drawRoundRect​(int x, +int y, +int width, +int height, +int arcWidth, +int arcHeight)
      +
      +
      Specified by:
      +
      drawRoundRect in class java.awt.Graphics
      +
      +
      +
    • +
    • +
      +

      fillArc

      +
      public void fillArc​(int x, +int y, +int width, +int height, +int startAngle, +int arcAngle)
      +
      +
      Specified by:
      +
      fillArc in class java.awt.Graphics
      +
      +
      +
    • +
    • +
      +

      fillOval

      +
      public void fillOval​(int x, +int y, +int width, +int height)
      +
      +
      Specified by:
      +
      fillOval in class java.awt.Graphics
      +
      +
      +
    • +
    • +
      +

      fillPolygon

      +
      public void fillPolygon​(java.awt.Polygon p)
      +
      +
      Overrides:
      +
      fillPolygon in class java.awt.Graphics
      +
      +
      +
    • +
    • +
      +

      fillPolygon

      +
      public void fillPolygon​(int[] xPoints, +int[] yPoints, +int nPoints)
      +
      +
      Specified by:
      +
      fillPolygon in class java.awt.Graphics
      +
      +
      +
    • +
    • +
      +

      fillRect

      +
      public void fillRect​(int x, +int y, +int width, +int height)
      +
      +
      Specified by:
      +
      fillRect in class java.awt.Graphics
      +
      +
      +
    • +
    • +
      +

      fillRoundRect

      +
      public void fillRoundRect​(int x, +int y, +int width, +int height, +int arcWidth, +int arcHeight)
      +
      +
      Specified by:
      +
      fillRoundRect in class java.awt.Graphics
      +
      +
      +
    • +
    • +
      +

      getClip

      +
      public java.awt.Shape getClip()
      +
      +
      Specified by:
      +
      getClip in class java.awt.Graphics
      +
      +
      +
    • +
    • +
      +

      setClip

      +
      public void setClip​(java.awt.Shape clip)
      +
      +
      Specified by:
      +
      setClip in class java.awt.Graphics
      +
      +
      +
    • +
    • +
      +

      getClipBounds

      +
      public java.awt.Rectangle getClipBounds()
      +
      +
      Specified by:
      +
      getClipBounds in class java.awt.Graphics
      +
      +
      +
    • +
    • +
      +

      getColor

      +
      public java.awt.Color getColor()
      +
      +
      Specified by:
      +
      getColor in class java.awt.Graphics
      +
      +
      +
    • +
    • +
      +

      setColor

      +
      public void setColor​(java.awt.Color c)
      +
      +
      Specified by:
      +
      setColor in class java.awt.Graphics
      +
      +
      +
    • +
    • +
      +

      getFont

      +
      public java.awt.Font getFont()
      +
      +
      Specified by:
      +
      getFont in class java.awt.Graphics
      +
      +
      +
    • +
    • +
      +

      setFont

      +
      public void setFont​(java.awt.Font font)
      +
      +
      Specified by:
      +
      setFont in class java.awt.Graphics
      +
      +
      +
    • +
    • +
      +

      getFontMetrics

      +
      public java.awt.FontMetrics getFontMetrics​(java.awt.Font f)
      +
      +
      Specified by:
      +
      getFontMetrics in class java.awt.Graphics
      +
      +
      +
    • +
    • +
      +

      setClip

      +
      public void setClip​(int x, +int y, +int width, +int height)
      +
      +
      Specified by:
      +
      setClip in class java.awt.Graphics
      +
      +
      +
    • +
    • +
      +

      setPaintMode

      +
      public void setPaintMode()
      +
      +
      Specified by:
      +
      setPaintMode in class java.awt.Graphics
      +
      +
      +
    • +
    • +
      +

      getXORMode

      +
      public java.awt.Color getXORMode()
      +
      +
    • +
    • +
      +

      setXORMode

      +
      public void setXORMode​(java.awt.Color c1)
      +
      +
      Specified by:
      +
      setXORMode in class java.awt.Graphics
      +
      +
      +
    • +
    • +
      +

      getCommands

      +
      protected java.lang.Iterable<Command<?>> getCommands()
      +
      +
    • +
    • +
      +

      isDisposed

      +
      protected boolean isDisposed()
      +
      +
    • +
    +
    +
  • +
+
+ +
+ +
+
+ + diff --git a/io-vector/build/docs/javadoc/org.xbib.graphics.io.vector/org/xbib/graphics/io/vector/VectorGraphicsFormat.html b/io-vector/build/docs/javadoc/org.xbib.graphics.io.vector/org/xbib/graphics/io/vector/VectorGraphicsFormat.html new file mode 100644 index 0000000..f862782 --- /dev/null +++ b/io-vector/build/docs/javadoc/org.xbib.graphics.io.vector/org/xbib/graphics/io/vector/VectorGraphicsFormat.html @@ -0,0 +1,291 @@ + + + + + +VectorGraphicsFormat (io-vector 3.0.0 API) + + + + + + + + + + + + + +
+ +
+
+ +
+ + +

Enum VectorGraphicsFormat

+
+
java.lang.Object +
java.lang.Enum<VectorGraphicsFormat> +
org.xbib.graphics.io.vector.VectorGraphicsFormat
+
+
+
+
+
All Implemented Interfaces:
+
java.io.Serializable, java.lang.Comparable<VectorGraphicsFormat>, java.lang.constant.Constable
+
+
+
public enum VectorGraphicsFormat
+extends java.lang.Enum<VectorGraphicsFormat>
+
+
+
    + +
  • +
    +

    Nested Class Summary

    +
    +

    Nested classes/interfaces inherited from class java.lang.Enum

    +java.lang.Enum.EnumDesc<E extends java.lang.Enum<E>>
    +
    +
  • + +
  • +
    +

    Enum Constant Summary

    +
    + + + + + + + + + + + + + + + + + + + + + + +
    Enum Constants
    Enum ConstantDescription
    EPS 
    PDF 
    SVG 
    +
    +
    +
  • + +
  • +
    +

    Method Summary

    +
    +
    +
    + + + + + + + + + + + + + + + + + + + + +
    Modifier and TypeMethodDescription
    static VectorGraphicsFormatvalueOf​(java.lang.String name) +
    Returns the enum constant of this type with the specified name.
    +
    static VectorGraphicsFormat[]values() +
    Returns an array containing the constants of this enum type, in +the order they are declared.
    +
    +
    +
    +
    +

    Methods inherited from class java.lang.Enum

    +clone, compareTo, describeConstable, equals, finalize, getDeclaringClass, hashCode, name, ordinal, toString, valueOf
    +
    +

    Methods inherited from class java.lang.Object

    +getClass, notify, notifyAll, wait, wait, wait
    +
    +
  • +
+
+
+
    + +
  • +
    +

    Enum Constant Details

    + +
    +
  • + +
  • +
    +

    Method Details

    +
      +
    • +
      +

      values

      +
      public static VectorGraphicsFormat[] values()
      +
      Returns an array containing the constants of this enum type, in +the order they are declared.
      +
      +
      Returns:
      +
      an array containing the constants of this enum type, in the order they are declared
      +
      +
      +
    • +
    • +
      +

      valueOf

      +
      public static VectorGraphicsFormat valueOf​(java.lang.String name)
      +
      Returns the enum constant of this type with the specified name. +The string must match exactly an identifier used to declare an +enum constant in this type. (Extraneous whitespace characters are +not permitted.)
      +
      +
      Parameters:
      +
      name - the name of the enum constant to be returned.
      +
      Returns:
      +
      the enum constant with the specified name
      +
      Throws:
      +
      java.lang.IllegalArgumentException - if this enum type has no constant with the specified name
      +
      java.lang.NullPointerException - if the argument is null
      +
      +
      +
    • +
    +
    +
  • +
+
+ +
+ +
+
+ + diff --git a/io-vector/build/docs/javadoc/org.xbib.graphics.io.vector/org/xbib/graphics/io/vector/commands/AffineTransformCommand.html b/io-vector/build/docs/javadoc/org.xbib.graphics.io.vector/org/xbib/graphics/io/vector/commands/AffineTransformCommand.html new file mode 100644 index 0000000..c2debd2 --- /dev/null +++ b/io-vector/build/docs/javadoc/org.xbib.graphics.io.vector/org/xbib/graphics/io/vector/commands/AffineTransformCommand.html @@ -0,0 +1,188 @@ + + + + + +AffineTransformCommand (io-vector 3.0.0 API) + + + + + + + + + + + + + +
+ +
+
+ +
+ + +

Class AffineTransformCommand

+
+
java.lang.Object +
org.xbib.graphics.io.vector.Command<T> +
org.xbib.graphics.io.vector.commands.StateCommand<java.awt.geom.AffineTransform> +
org.xbib.graphics.io.vector.commands.AffineTransformCommand
+
+
+
+
+
+
Direct Known Subclasses:
+
RotateCommand, ScaleCommand, ShearCommand, TransformCommand, TranslateCommand
+
+
+
public abstract class AffineTransformCommand
+extends StateCommand<java.awt.geom.AffineTransform>
+
+
+
    + +
  • +
    +

    Constructor Summary

    +
    + + + + + + + + + + + + + + +
    Constructors
    ConstructorDescription
    AffineTransformCommand​(java.awt.geom.AffineTransform transform) 
    +
    +
    +
  • + +
  • +
    +

    Method Summary

    +
    +

    Methods inherited from class org.xbib.graphics.io.vector.Command

    +equals, getValue, toString
    +
    +

    Methods inherited from class java.lang.Object

    +clone, finalize, getClass, hashCode, notify, notifyAll, wait, wait, wait
    +
    +
  • +
+
+
+
    + +
  • +
    +

    Constructor Details

    +
      +
    • +
      +

      AffineTransformCommand

      +
      public AffineTransformCommand​(java.awt.geom.AffineTransform transform)
      +
      +
    • +
    +
    +
  • +
+
+ +
+ +
+
+ + diff --git a/io-vector/build/docs/javadoc/org.xbib.graphics.io.vector/org/xbib/graphics/io/vector/commands/CreateCommand.html b/io-vector/build/docs/javadoc/org.xbib.graphics.io.vector/org/xbib/graphics/io/vector/commands/CreateCommand.html new file mode 100644 index 0000000..8298f78 --- /dev/null +++ b/io-vector/build/docs/javadoc/org.xbib.graphics.io.vector/org/xbib/graphics/io/vector/commands/CreateCommand.html @@ -0,0 +1,184 @@ + + + + + +CreateCommand (io-vector 3.0.0 API) + + + + + + + + + + + + + +
+ +
+
+ +
+ + +

Class CreateCommand

+
+
java.lang.Object + +
+
+
+
public class CreateCommand
+extends StateCommand<VectorGraphics2D>
+
+
+
    + +
  • +
    +

    Constructor Summary

    +
    + + + + + + + + + + + + + + +
    Constructors
    ConstructorDescription
    CreateCommand​(VectorGraphics2D graphics) 
    +
    +
    +
  • + +
  • +
    +

    Method Summary

    +
    +

    Methods inherited from class org.xbib.graphics.io.vector.Command

    +equals, getValue, toString
    +
    +

    Methods inherited from class java.lang.Object

    +clone, finalize, getClass, hashCode, notify, notifyAll, wait, wait, wait
    +
    +
  • +
+
+
+
    + +
  • +
    +

    Constructor Details

    + +
    +
  • +
+
+ +
+ +
+
+ + diff --git a/io-vector/build/docs/javadoc/org.xbib.graphics.io.vector/org/xbib/graphics/io/vector/commands/DisposeCommand.html b/io-vector/build/docs/javadoc/org.xbib.graphics.io.vector/org/xbib/graphics/io/vector/commands/DisposeCommand.html new file mode 100644 index 0000000..f45496c --- /dev/null +++ b/io-vector/build/docs/javadoc/org.xbib.graphics.io.vector/org/xbib/graphics/io/vector/commands/DisposeCommand.html @@ -0,0 +1,184 @@ + + + + + +DisposeCommand (io-vector 3.0.0 API) + + + + + + + + + + + + + +
+ +
+
+ +
+ + +

Class DisposeCommand

+
+
java.lang.Object + +
+
+
+
public class DisposeCommand
+extends StateCommand<VectorGraphics2D>
+
+
+
    + +
  • +
    +

    Constructor Summary

    +
    + + + + + + + + + + + + + + +
    Constructors
    ConstructorDescription
    DisposeCommand​(VectorGraphics2D graphics) 
    +
    +
    +
  • + +
  • +
    +

    Method Summary

    +
    +

    Methods inherited from class org.xbib.graphics.io.vector.Command

    +equals, getValue, toString
    +
    +

    Methods inherited from class java.lang.Object

    +clone, finalize, getClass, hashCode, notify, notifyAll, wait, wait, wait
    +
    +
  • +
+
+
+
    + +
  • +
    +

    Constructor Details

    +
      +
    • +
      +

      DisposeCommand

      +
      public DisposeCommand​(VectorGraphics2D graphics)
      +
      +
    • +
    +
    +
  • +
+
+ +
+ +
+
+ + diff --git a/io-vector/build/docs/javadoc/org.xbib.graphics.io.vector/org/xbib/graphics/io/vector/commands/DrawImageCommand.html b/io-vector/build/docs/javadoc/org.xbib.graphics.io.vector/org/xbib/graphics/io/vector/commands/DrawImageCommand.html new file mode 100644 index 0000000..2f79991 --- /dev/null +++ b/io-vector/build/docs/javadoc/org.xbib.graphics.io.vector/org/xbib/graphics/io/vector/commands/DrawImageCommand.html @@ -0,0 +1,305 @@ + + + + + +DrawImageCommand (io-vector 3.0.0 API) + + + + + + + + + + + + + +
+ +
+
+ +
+ + +

Class DrawImageCommand

+
+
java.lang.Object +
org.xbib.graphics.io.vector.Command<java.awt.Image> +
org.xbib.graphics.io.vector.commands.DrawImageCommand
+
+
+
+
+
public class DrawImageCommand
+extends Command<java.awt.Image>
+
+
+
    + +
  • +
    +

    Constructor Summary

    +
    + + + + + + + + + + + + + + +
    Constructors
    ConstructorDescription
    DrawImageCommand​(java.awt.Image image, +int imageWidth, +int imageHeight, +double x, +double y, +double width, +double height) 
    +
    +
    +
  • + +
  • +
    +

    Method Summary

    +
    +
    +
    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    Modifier and TypeMethodDescription
    doublegetHeight() 
    intgetImageHeight() 
    intgetImageWidth() 
    doublegetWidth() 
    doublegetX() 
    doublegetY() 
    java.lang.StringtoString() 
    +
    +
    +
    +

    Methods inherited from class org.xbib.graphics.io.vector.Command

    +equals, getValue
    +
    +

    Methods inherited from class java.lang.Object

    +clone, finalize, getClass, hashCode, notify, notifyAll, wait, wait, wait
    +
    +
  • +
+
+
+
    + +
  • +
    +

    Constructor Details

    +
      +
    • +
      +

      DrawImageCommand

      +
      public DrawImageCommand​(java.awt.Image image, +int imageWidth, +int imageHeight, +double x, +double y, +double width, +double height)
      +
      +
    • +
    +
    +
  • + +
  • +
    +

    Method Details

    +
      +
    • +
      +

      getImageWidth

      +
      public int getImageWidth()
      +
      +
    • +
    • +
      +

      getImageHeight

      +
      public int getImageHeight()
      +
      +
    • +
    • +
      +

      getX

      +
      public double getX()
      +
      +
    • +
    • +
      +

      getY

      +
      public double getY()
      +
      +
    • +
    • +
      +

      getWidth

      +
      public double getWidth()
      +
      +
    • +
    • +
      +

      getHeight

      +
      public double getHeight()
      +
      +
    • +
    • +
      +

      toString

      +
      public java.lang.String toString()
      +
      +
      Overrides:
      +
      toString in class Command<java.awt.Image>
      +
      +
      +
    • +
    +
    +
  • +
+
+ +
+ +
+
+ + diff --git a/io-vector/build/docs/javadoc/org.xbib.graphics.io.vector/org/xbib/graphics/io/vector/commands/DrawShapeCommand.html b/io-vector/build/docs/javadoc/org.xbib.graphics.io.vector/org/xbib/graphics/io/vector/commands/DrawShapeCommand.html new file mode 100644 index 0000000..70bd5f0 --- /dev/null +++ b/io-vector/build/docs/javadoc/org.xbib.graphics.io.vector/org/xbib/graphics/io/vector/commands/DrawShapeCommand.html @@ -0,0 +1,182 @@ + + + + + +DrawShapeCommand (io-vector 3.0.0 API) + + + + + + + + + + + + + +
+ +
+
+ +
+ + +

Class DrawShapeCommand

+
+
java.lang.Object +
org.xbib.graphics.io.vector.Command<java.awt.Shape> +
org.xbib.graphics.io.vector.commands.DrawShapeCommand
+
+
+
+
+
public class DrawShapeCommand
+extends Command<java.awt.Shape>
+
+
+
    + +
  • +
    +

    Constructor Summary

    +
    + + + + + + + + + + + + + + +
    Constructors
    ConstructorDescription
    DrawShapeCommand​(java.awt.Shape shape) 
    +
    +
    +
  • + +
  • +
    +

    Method Summary

    +
    +

    Methods inherited from class org.xbib.graphics.io.vector.Command

    +equals, getValue, toString
    +
    +

    Methods inherited from class java.lang.Object

    +clone, finalize, getClass, hashCode, notify, notifyAll, wait, wait, wait
    +
    +
  • +
+
+
+
    + +
  • +
    +

    Constructor Details

    +
      +
    • +
      +

      DrawShapeCommand

      +
      public DrawShapeCommand​(java.awt.Shape shape)
      +
      +
    • +
    +
    +
  • +
+
+ +
+ +
+
+ + diff --git a/io-vector/build/docs/javadoc/org.xbib.graphics.io.vector/org/xbib/graphics/io/vector/commands/DrawStringCommand.html b/io-vector/build/docs/javadoc/org.xbib.graphics.io.vector/org/xbib/graphics/io/vector/commands/DrawStringCommand.html new file mode 100644 index 0000000..065b1e2 --- /dev/null +++ b/io-vector/build/docs/javadoc/org.xbib.graphics.io.vector/org/xbib/graphics/io/vector/commands/DrawStringCommand.html @@ -0,0 +1,253 @@ + + + + + +DrawStringCommand (io-vector 3.0.0 API) + + + + + + + + + + + + + +
+ +
+
+ +
+ + +

Class DrawStringCommand

+
+
java.lang.Object +
org.xbib.graphics.io.vector.Command<java.lang.String> +
org.xbib.graphics.io.vector.commands.DrawStringCommand
+
+
+
+
+
public class DrawStringCommand
+extends Command<java.lang.String>
+
+
+
    + +
  • +
    +

    Constructor Summary

    +
    + + + + + + + + + + + + + + +
    Constructors
    ConstructorDescription
    DrawStringCommand​(java.lang.String string, +double x, +double y) 
    +
    +
    +
  • + +
  • +
    +

    Method Summary

    +
    +
    +
    + + + + + + + + + + + + + + + + + + + + + + + + + +
    Modifier and TypeMethodDescription
    doublegetX() 
    doublegetY() 
    java.lang.StringtoString() 
    +
    +
    +
    +

    Methods inherited from class org.xbib.graphics.io.vector.Command

    +equals, getValue
    +
    +

    Methods inherited from class java.lang.Object

    +clone, finalize, getClass, hashCode, notify, notifyAll, wait, wait, wait
    +
    +
  • +
+
+
+
    + +
  • +
    +

    Constructor Details

    +
      +
    • +
      +

      DrawStringCommand

      +
      public DrawStringCommand​(java.lang.String string, +double x, +double y)
      +
      +
    • +
    +
    +
  • + +
  • +
    +

    Method Details

    +
      +
    • +
      +

      getX

      +
      public double getX()
      +
      +
    • +
    • +
      +

      getY

      +
      public double getY()
      +
      +
    • +
    • +
      +

      toString

      +
      public java.lang.String toString()
      +
      +
      Overrides:
      +
      toString in class Command<java.lang.String>
      +
      +
      +
    • +
    +
    +
  • +
+
+ +
+ +
+
+ + diff --git a/io-vector/build/docs/javadoc/org.xbib.graphics.io.vector/org/xbib/graphics/io/vector/commands/FillShapeCommand.html b/io-vector/build/docs/javadoc/org.xbib.graphics.io.vector/org/xbib/graphics/io/vector/commands/FillShapeCommand.html new file mode 100644 index 0000000..e34dac6 --- /dev/null +++ b/io-vector/build/docs/javadoc/org.xbib.graphics.io.vector/org/xbib/graphics/io/vector/commands/FillShapeCommand.html @@ -0,0 +1,182 @@ + + + + + +FillShapeCommand (io-vector 3.0.0 API) + + + + + + + + + + + + + +
+ +
+
+ +
+ + +

Class FillShapeCommand

+
+
java.lang.Object +
org.xbib.graphics.io.vector.Command<java.awt.Shape> +
org.xbib.graphics.io.vector.commands.FillShapeCommand
+
+
+
+
+
public class FillShapeCommand
+extends Command<java.awt.Shape>
+
+
+
    + +
  • +
    +

    Constructor Summary

    +
    + + + + + + + + + + + + + + +
    Constructors
    ConstructorDescription
    FillShapeCommand​(java.awt.Shape shape) 
    +
    +
    +
  • + +
  • +
    +

    Method Summary

    +
    +

    Methods inherited from class org.xbib.graphics.io.vector.Command

    +equals, getValue, toString
    +
    +

    Methods inherited from class java.lang.Object

    +clone, finalize, getClass, hashCode, notify, notifyAll, wait, wait, wait
    +
    +
  • +
+
+
+
    + +
  • +
    +

    Constructor Details

    +
      +
    • +
      +

      FillShapeCommand

      +
      public FillShapeCommand​(java.awt.Shape shape)
      +
      +
    • +
    +
    +
  • +
+
+ +
+ +
+
+ + diff --git a/io-vector/build/docs/javadoc/org.xbib.graphics.io.vector/org/xbib/graphics/io/vector/commands/Group.html b/io-vector/build/docs/javadoc/org.xbib.graphics.io.vector/org/xbib/graphics/io/vector/commands/Group.html new file mode 100644 index 0000000..13a1241 --- /dev/null +++ b/io-vector/build/docs/javadoc/org.xbib.graphics.io.vector/org/xbib/graphics/io/vector/commands/Group.html @@ -0,0 +1,223 @@ + + + + + +Group (io-vector 3.0.0 API) + + + + + + + + + + + + + +
+ +
+
+ + +
java.lang.Object +
org.xbib.graphics.io.vector.Command<java.util.List<Command<?>>> +
org.xbib.graphics.io.vector.commands.Group
+
+
+
+
+
public class Group
+extends Command<java.util.List<Command<?>>>
+
+
+
    + +
  • +
    +

    Constructor Summary

    +
    + + + + + + + + + + + + + + +
    Constructors
    ConstructorDescription
    Group() 
    +
    +
    +
  • + +
  • +
    +

    Method Summary

    +
    +
    +
    + + + + + + + + + + + + + + + +
    Modifier and TypeMethodDescription
    voidadd​(Command<?> command) 
    +
    +
    +
    +

    Methods inherited from class org.xbib.graphics.io.vector.Command

    +equals, getValue, toString
    +
    +

    Methods inherited from class java.lang.Object

    +clone, finalize, getClass, hashCode, notify, notifyAll, wait, wait, wait
    +
    +
  • +
+
+
+
    + +
  • +
    +

    Constructor Details

    +
      +
    • +
      +

      Group

      +
      public Group()
      +
      +
    • +
    +
    +
  • + +
  • +
    +

    Method Details

    +
      +
    • +
      +

      add

      +
      public void add​(Command<?> command)
      +
      +
    • +
    +
    +
  • +
+
+ +
+ +
+
+ + diff --git a/io-vector/build/docs/javadoc/org.xbib.graphics.io.vector/org/xbib/graphics/io/vector/commands/RotateCommand.html b/io-vector/build/docs/javadoc/org.xbib.graphics.io.vector/org/xbib/graphics/io/vector/commands/RotateCommand.html new file mode 100644 index 0000000..e517e41 --- /dev/null +++ b/io-vector/build/docs/javadoc/org.xbib.graphics.io.vector/org/xbib/graphics/io/vector/commands/RotateCommand.html @@ -0,0 +1,268 @@ + + + + + +RotateCommand (io-vector 3.0.0 API) + + + + + + + + + + + + + +
+ +
+
+ +
+ + +

Class RotateCommand

+
+
java.lang.Object +
org.xbib.graphics.io.vector.Command<T> +
org.xbib.graphics.io.vector.commands.StateCommand<java.awt.geom.AffineTransform> +
org.xbib.graphics.io.vector.commands.AffineTransformCommand +
org.xbib.graphics.io.vector.commands.RotateCommand
+
+
+
+
+
+
+
public class RotateCommand
+extends AffineTransformCommand
+
+
+
    + +
  • +
    +

    Constructor Summary

    +
    + + + + + + + + + + + + + + +
    Constructors
    ConstructorDescription
    RotateCommand​(double theta, +double centerX, +double centerY) 
    +
    +
    +
  • + +
  • +
    +

    Method Summary

    +
    +
    +
    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    Modifier and TypeMethodDescription
    doublegetCenterX() 
    doublegetCenterY() 
    doublegetTheta() 
    java.lang.StringtoString() 
    +
    +
    +
    +

    Methods inherited from class org.xbib.graphics.io.vector.Command

    +equals, getValue
    +
    +

    Methods inherited from class java.lang.Object

    +clone, finalize, getClass, hashCode, notify, notifyAll, wait, wait, wait
    +
    +
  • +
+
+
+
    + +
  • +
    +

    Constructor Details

    +
      +
    • +
      +

      RotateCommand

      +
      public RotateCommand​(double theta, +double centerX, +double centerY)
      +
      +
    • +
    +
    +
  • + +
  • +
    +

    Method Details

    +
      +
    • +
      +

      getTheta

      +
      public double getTheta()
      +
      +
    • +
    • +
      +

      getCenterX

      +
      public double getCenterX()
      +
      +
    • +
    • +
      +

      getCenterY

      +
      public double getCenterY()
      +
      +
    • +
    • +
      +

      toString

      +
      public java.lang.String toString()
      +
      +
      Overrides:
      +
      toString in class Command<java.awt.geom.AffineTransform>
      +
      +
      +
    • +
    +
    +
  • +
+
+ +
+ +
+
+ + diff --git a/io-vector/build/docs/javadoc/org.xbib.graphics.io.vector/org/xbib/graphics/io/vector/commands/ScaleCommand.html b/io-vector/build/docs/javadoc/org.xbib.graphics.io.vector/org/xbib/graphics/io/vector/commands/ScaleCommand.html new file mode 100644 index 0000000..18f43bf --- /dev/null +++ b/io-vector/build/docs/javadoc/org.xbib.graphics.io.vector/org/xbib/graphics/io/vector/commands/ScaleCommand.html @@ -0,0 +1,255 @@ + + + + + +ScaleCommand (io-vector 3.0.0 API) + + + + + + + + + + + + + +
+ +
+
+ +
+ + +

Class ScaleCommand

+
+
java.lang.Object + +
+
+
+
public class ScaleCommand
+extends AffineTransformCommand
+
+
+
    + +
  • +
    +

    Constructor Summary

    +
    + + + + + + + + + + + + + + +
    Constructors
    ConstructorDescription
    ScaleCommand​(double scaleX, +double scaleY) 
    +
    +
    +
  • + +
  • +
    +

    Method Summary

    +
    +
    +
    + + + + + + + + + + + + + + + + + + + + + + + + + +
    Modifier and TypeMethodDescription
    doublegetScaleX() 
    doublegetScaleY() 
    java.lang.StringtoString() 
    +
    +
    +
    +

    Methods inherited from class org.xbib.graphics.io.vector.Command

    +equals, getValue
    +
    +

    Methods inherited from class java.lang.Object

    +clone, finalize, getClass, hashCode, notify, notifyAll, wait, wait, wait
    +
    +
  • +
+
+
+
    + +
  • +
    +

    Constructor Details

    +
      +
    • +
      +

      ScaleCommand

      +
      public ScaleCommand​(double scaleX, +double scaleY)
      +
      +
    • +
    +
    +
  • + +
  • +
    +

    Method Details

    +
      +
    • +
      +

      getScaleX

      +
      public double getScaleX()
      +
      +
    • +
    • +
      +

      getScaleY

      +
      public double getScaleY()
      +
      +
    • +
    • +
      +

      toString

      +
      public java.lang.String toString()
      +
      +
      Overrides:
      +
      toString in class Command<java.awt.geom.AffineTransform>
      +
      +
      +
    • +
    +
    +
  • +
+
+ +
+ +
+
+ + diff --git a/io-vector/build/docs/javadoc/org.xbib.graphics.io.vector/org/xbib/graphics/io/vector/commands/SetBackgroundCommand.html b/io-vector/build/docs/javadoc/org.xbib.graphics.io.vector/org/xbib/graphics/io/vector/commands/SetBackgroundCommand.html new file mode 100644 index 0000000..2b0dcb9 --- /dev/null +++ b/io-vector/build/docs/javadoc/org.xbib.graphics.io.vector/org/xbib/graphics/io/vector/commands/SetBackgroundCommand.html @@ -0,0 +1,184 @@ + + + + + +SetBackgroundCommand (io-vector 3.0.0 API) + + + + + + + + + + + + + +
+ +
+
+ +
+ + +

Class SetBackgroundCommand

+
+
java.lang.Object +
org.xbib.graphics.io.vector.Command<T> +
org.xbib.graphics.io.vector.commands.StateCommand<java.awt.Color> +
org.xbib.graphics.io.vector.commands.SetBackgroundCommand
+
+
+
+
+
+
public class SetBackgroundCommand
+extends StateCommand<java.awt.Color>
+
+
+
    + +
  • +
    +

    Constructor Summary

    +
    + + + + + + + + + + + + + + +
    Constructors
    ConstructorDescription
    SetBackgroundCommand​(java.awt.Color color) 
    +
    +
    +
  • + +
  • +
    +

    Method Summary

    +
    +

    Methods inherited from class org.xbib.graphics.io.vector.Command

    +equals, getValue, toString
    +
    +

    Methods inherited from class java.lang.Object

    +clone, finalize, getClass, hashCode, notify, notifyAll, wait, wait, wait
    +
    +
  • +
+
+
+
    + +
  • +
    +

    Constructor Details

    +
      +
    • +
      +

      SetBackgroundCommand

      +
      public SetBackgroundCommand​(java.awt.Color color)
      +
      +
    • +
    +
    +
  • +
+
+ +
+ +
+
+ + diff --git a/io-vector/build/docs/javadoc/org.xbib.graphics.io.vector/org/xbib/graphics/io/vector/commands/SetClipCommand.html b/io-vector/build/docs/javadoc/org.xbib.graphics.io.vector/org/xbib/graphics/io/vector/commands/SetClipCommand.html new file mode 100644 index 0000000..ef6e4c3 --- /dev/null +++ b/io-vector/build/docs/javadoc/org.xbib.graphics.io.vector/org/xbib/graphics/io/vector/commands/SetClipCommand.html @@ -0,0 +1,184 @@ + + + + + +SetClipCommand (io-vector 3.0.0 API) + + + + + + + + + + + + + +
+ +
+
+ +
+ + +

Class SetClipCommand

+
+
java.lang.Object +
org.xbib.graphics.io.vector.Command<T> +
org.xbib.graphics.io.vector.commands.StateCommand<java.awt.Shape> +
org.xbib.graphics.io.vector.commands.SetClipCommand
+
+
+
+
+
+
public class SetClipCommand
+extends StateCommand<java.awt.Shape>
+
+
+
    + +
  • +
    +

    Constructor Summary

    +
    + + + + + + + + + + + + + + +
    Constructors
    ConstructorDescription
    SetClipCommand​(java.awt.Shape shape) 
    +
    +
    +
  • + +
  • +
    +

    Method Summary

    +
    +

    Methods inherited from class org.xbib.graphics.io.vector.Command

    +equals, getValue, toString
    +
    +

    Methods inherited from class java.lang.Object

    +clone, finalize, getClass, hashCode, notify, notifyAll, wait, wait, wait
    +
    +
  • +
+
+
+
    + +
  • +
    +

    Constructor Details

    +
      +
    • +
      +

      SetClipCommand

      +
      public SetClipCommand​(java.awt.Shape shape)
      +
      +
    • +
    +
    +
  • +
+
+ +
+ +
+
+ + diff --git a/io-vector/build/docs/javadoc/org.xbib.graphics.io.vector/org/xbib/graphics/io/vector/commands/SetColorCommand.html b/io-vector/build/docs/javadoc/org.xbib.graphics.io.vector/org/xbib/graphics/io/vector/commands/SetColorCommand.html new file mode 100644 index 0000000..df7b1c5 --- /dev/null +++ b/io-vector/build/docs/javadoc/org.xbib.graphics.io.vector/org/xbib/graphics/io/vector/commands/SetColorCommand.html @@ -0,0 +1,184 @@ + + + + + +SetColorCommand (io-vector 3.0.0 API) + + + + + + + + + + + + + +
+ +
+
+ +
+ + +

Class SetColorCommand

+
+
java.lang.Object +
org.xbib.graphics.io.vector.Command<T> +
org.xbib.graphics.io.vector.commands.StateCommand<java.awt.Color> +
org.xbib.graphics.io.vector.commands.SetColorCommand
+
+
+
+
+
+
public class SetColorCommand
+extends StateCommand<java.awt.Color>
+
+
+
    + +
  • +
    +

    Constructor Summary

    +
    + + + + + + + + + + + + + + +
    Constructors
    ConstructorDescription
    SetColorCommand​(java.awt.Color color) 
    +
    +
    +
  • + +
  • +
    +

    Method Summary

    +
    +

    Methods inherited from class org.xbib.graphics.io.vector.Command

    +equals, getValue, toString
    +
    +

    Methods inherited from class java.lang.Object

    +clone, finalize, getClass, hashCode, notify, notifyAll, wait, wait, wait
    +
    +
  • +
+
+
+
    + +
  • +
    +

    Constructor Details

    +
      +
    • +
      +

      SetColorCommand

      +
      public SetColorCommand​(java.awt.Color color)
      +
      +
    • +
    +
    +
  • +
+
+ +
+ +
+
+ + diff --git a/io-vector/build/docs/javadoc/org.xbib.graphics.io.vector/org/xbib/graphics/io/vector/commands/SetCompositeCommand.html b/io-vector/build/docs/javadoc/org.xbib.graphics.io.vector/org/xbib/graphics/io/vector/commands/SetCompositeCommand.html new file mode 100644 index 0000000..a199abb --- /dev/null +++ b/io-vector/build/docs/javadoc/org.xbib.graphics.io.vector/org/xbib/graphics/io/vector/commands/SetCompositeCommand.html @@ -0,0 +1,184 @@ + + + + + +SetCompositeCommand (io-vector 3.0.0 API) + + + + + + + + + + + + + +
+ +
+
+ +
+ + +

Class SetCompositeCommand

+
+
java.lang.Object +
org.xbib.graphics.io.vector.Command<T> +
org.xbib.graphics.io.vector.commands.StateCommand<java.awt.Composite> +
org.xbib.graphics.io.vector.commands.SetCompositeCommand
+
+
+
+
+
+
public class SetCompositeCommand
+extends StateCommand<java.awt.Composite>
+
+
+
    + +
  • +
    +

    Constructor Summary

    +
    + + + + + + + + + + + + + + +
    Constructors
    ConstructorDescription
    SetCompositeCommand​(java.awt.Composite composite) 
    +
    +
    +
  • + +
  • +
    +

    Method Summary

    +
    +

    Methods inherited from class org.xbib.graphics.io.vector.Command

    +equals, getValue, toString
    +
    +

    Methods inherited from class java.lang.Object

    +clone, finalize, getClass, hashCode, notify, notifyAll, wait, wait, wait
    +
    +
  • +
+
+
+
    + +
  • +
    +

    Constructor Details

    +
      +
    • +
      +

      SetCompositeCommand

      +
      public SetCompositeCommand​(java.awt.Composite composite)
      +
      +
    • +
    +
    +
  • +
+
+ +
+ +
+
+ + diff --git a/io-vector/build/docs/javadoc/org.xbib.graphics.io.vector/org/xbib/graphics/io/vector/commands/SetFontCommand.html b/io-vector/build/docs/javadoc/org.xbib.graphics.io.vector/org/xbib/graphics/io/vector/commands/SetFontCommand.html new file mode 100644 index 0000000..37af80a --- /dev/null +++ b/io-vector/build/docs/javadoc/org.xbib.graphics.io.vector/org/xbib/graphics/io/vector/commands/SetFontCommand.html @@ -0,0 +1,184 @@ + + + + + +SetFontCommand (io-vector 3.0.0 API) + + + + + + + + + + + + + +
+ +
+
+ +
+ + +

Class SetFontCommand

+
+
java.lang.Object +
org.xbib.graphics.io.vector.Command<T> +
org.xbib.graphics.io.vector.commands.StateCommand<java.awt.Font> +
org.xbib.graphics.io.vector.commands.SetFontCommand
+
+
+
+
+
+
public class SetFontCommand
+extends StateCommand<java.awt.Font>
+
+
+
    + +
  • +
    +

    Constructor Summary

    +
    + + + + + + + + + + + + + + +
    Constructors
    ConstructorDescription
    SetFontCommand​(java.awt.Font font) 
    +
    +
    +
  • + +
  • +
    +

    Method Summary

    +
    +

    Methods inherited from class org.xbib.graphics.io.vector.Command

    +equals, getValue, toString
    +
    +

    Methods inherited from class java.lang.Object

    +clone, finalize, getClass, hashCode, notify, notifyAll, wait, wait, wait
    +
    +
  • +
+
+
+
    + +
  • +
    +

    Constructor Details

    +
      +
    • +
      +

      SetFontCommand

      +
      public SetFontCommand​(java.awt.Font font)
      +
      +
    • +
    +
    +
  • +
+
+ +
+ +
+
+ + diff --git a/io-vector/build/docs/javadoc/org.xbib.graphics.io.vector/org/xbib/graphics/io/vector/commands/SetHintCommand.html b/io-vector/build/docs/javadoc/org.xbib.graphics.io.vector/org/xbib/graphics/io/vector/commands/SetHintCommand.html new file mode 100644 index 0000000..7a6afb5 --- /dev/null +++ b/io-vector/build/docs/javadoc/org.xbib.graphics.io.vector/org/xbib/graphics/io/vector/commands/SetHintCommand.html @@ -0,0 +1,242 @@ + + + + + +SetHintCommand (io-vector 3.0.0 API) + + + + + + + + + + + + + +
+ +
+
+ +
+ + +

Class SetHintCommand

+
+
java.lang.Object +
org.xbib.graphics.io.vector.Command<T> +
org.xbib.graphics.io.vector.commands.StateCommand<java.lang.Object> +
org.xbib.graphics.io.vector.commands.SetHintCommand
+
+
+
+
+
+
public class SetHintCommand
+extends StateCommand<java.lang.Object>
+
+
+
    + +
  • +
    +

    Constructor Summary

    +
    + + + + + + + + + + + + + + +
    Constructors
    ConstructorDescription
    SetHintCommand​(java.lang.Object hintKey, +java.lang.Object hintValue) 
    +
    +
    +
  • + +
  • +
    +

    Method Summary

    +
    +
    +
    + + + + + + + + + + + + + + + + + + + + +
    Modifier and TypeMethodDescription
    java.lang.ObjectgetKey() 
    java.lang.StringtoString() 
    +
    +
    +
    +

    Methods inherited from class org.xbib.graphics.io.vector.Command

    +equals, getValue
    +
    +

    Methods inherited from class java.lang.Object

    +clone, finalize, getClass, hashCode, notify, notifyAll, wait, wait, wait
    +
    +
  • +
+
+
+
    + +
  • +
    +

    Constructor Details

    +
      +
    • +
      +

      SetHintCommand

      +
      public SetHintCommand​(java.lang.Object hintKey, +java.lang.Object hintValue)
      +
      +
    • +
    +
    +
  • + +
  • +
    +

    Method Details

    +
      +
    • +
      +

      getKey

      +
      public java.lang.Object getKey()
      +
      +
    • +
    • +
      +

      toString

      +
      public java.lang.String toString()
      +
      +
      Overrides:
      +
      toString in class Command<java.lang.Object>
      +
      +
      +
    • +
    +
    +
  • +
+
+ +
+ +
+
+ + diff --git a/io-vector/build/docs/javadoc/org.xbib.graphics.io.vector/org/xbib/graphics/io/vector/commands/SetPaintCommand.html b/io-vector/build/docs/javadoc/org.xbib.graphics.io.vector/org/xbib/graphics/io/vector/commands/SetPaintCommand.html new file mode 100644 index 0000000..29cc1d1 --- /dev/null +++ b/io-vector/build/docs/javadoc/org.xbib.graphics.io.vector/org/xbib/graphics/io/vector/commands/SetPaintCommand.html @@ -0,0 +1,184 @@ + + + + + +SetPaintCommand (io-vector 3.0.0 API) + + + + + + + + + + + + + +
+ +
+
+ +
+ + +

Class SetPaintCommand

+
+
java.lang.Object +
org.xbib.graphics.io.vector.Command<T> +
org.xbib.graphics.io.vector.commands.StateCommand<java.awt.Paint> +
org.xbib.graphics.io.vector.commands.SetPaintCommand
+
+
+
+
+
+
public class SetPaintCommand
+extends StateCommand<java.awt.Paint>
+
+
+
    + +
  • +
    +

    Constructor Summary

    +
    + + + + + + + + + + + + + + +
    Constructors
    ConstructorDescription
    SetPaintCommand​(java.awt.Paint paint) 
    +
    +
    +
  • + +
  • +
    +

    Method Summary

    +
    +

    Methods inherited from class org.xbib.graphics.io.vector.Command

    +equals, getValue, toString
    +
    +

    Methods inherited from class java.lang.Object

    +clone, finalize, getClass, hashCode, notify, notifyAll, wait, wait, wait
    +
    +
  • +
+
+
+
    + +
  • +
    +

    Constructor Details

    +
      +
    • +
      +

      SetPaintCommand

      +
      public SetPaintCommand​(java.awt.Paint paint)
      +
      +
    • +
    +
    +
  • +
+
+ +
+ +
+
+ + diff --git a/io-vector/build/docs/javadoc/org.xbib.graphics.io.vector/org/xbib/graphics/io/vector/commands/SetStrokeCommand.html b/io-vector/build/docs/javadoc/org.xbib.graphics.io.vector/org/xbib/graphics/io/vector/commands/SetStrokeCommand.html new file mode 100644 index 0000000..7db8a5f --- /dev/null +++ b/io-vector/build/docs/javadoc/org.xbib.graphics.io.vector/org/xbib/graphics/io/vector/commands/SetStrokeCommand.html @@ -0,0 +1,184 @@ + + + + + +SetStrokeCommand (io-vector 3.0.0 API) + + + + + + + + + + + + + +
+ +
+
+ +
+ + +

Class SetStrokeCommand

+
+
java.lang.Object +
org.xbib.graphics.io.vector.Command<T> +
org.xbib.graphics.io.vector.commands.StateCommand<java.awt.Stroke> +
org.xbib.graphics.io.vector.commands.SetStrokeCommand
+
+
+
+
+
+
public class SetStrokeCommand
+extends StateCommand<java.awt.Stroke>
+
+
+
    + +
  • +
    +

    Constructor Summary

    +
    + + + + + + + + + + + + + + +
    Constructors
    ConstructorDescription
    SetStrokeCommand​(java.awt.Stroke stroke) 
    +
    +
    +
  • + +
  • +
    +

    Method Summary

    +
    +

    Methods inherited from class org.xbib.graphics.io.vector.Command

    +equals, getValue, toString
    +
    +

    Methods inherited from class java.lang.Object

    +clone, finalize, getClass, hashCode, notify, notifyAll, wait, wait, wait
    +
    +
  • +
+
+
+
    + +
  • +
    +

    Constructor Details

    +
      +
    • +
      +

      SetStrokeCommand

      +
      public SetStrokeCommand​(java.awt.Stroke stroke)
      +
      +
    • +
    +
    +
  • +
+
+ +
+ +
+
+ + diff --git a/io-vector/build/docs/javadoc/org.xbib.graphics.io.vector/org/xbib/graphics/io/vector/commands/SetTransformCommand.html b/io-vector/build/docs/javadoc/org.xbib.graphics.io.vector/org/xbib/graphics/io/vector/commands/SetTransformCommand.html new file mode 100644 index 0000000..f735a1b --- /dev/null +++ b/io-vector/build/docs/javadoc/org.xbib.graphics.io.vector/org/xbib/graphics/io/vector/commands/SetTransformCommand.html @@ -0,0 +1,184 @@ + + + + + +SetTransformCommand (io-vector 3.0.0 API) + + + + + + + + + + + + + +
+ +
+
+ +
+ + +

Class SetTransformCommand

+
+
java.lang.Object +
org.xbib.graphics.io.vector.Command<T> +
org.xbib.graphics.io.vector.commands.StateCommand<java.awt.geom.AffineTransform> +
org.xbib.graphics.io.vector.commands.SetTransformCommand
+
+
+
+
+
+
public class SetTransformCommand
+extends StateCommand<java.awt.geom.AffineTransform>
+
+
+
    + +
  • +
    +

    Constructor Summary

    +
    + + + + + + + + + + + + + + +
    Constructors
    ConstructorDescription
    SetTransformCommand​(java.awt.geom.AffineTransform transform) 
    +
    +
    +
  • + +
  • +
    +

    Method Summary

    +
    +

    Methods inherited from class org.xbib.graphics.io.vector.Command

    +equals, getValue, toString
    +
    +

    Methods inherited from class java.lang.Object

    +clone, finalize, getClass, hashCode, notify, notifyAll, wait, wait, wait
    +
    +
  • +
+
+
+
    + +
  • +
    +

    Constructor Details

    +
      +
    • +
      +

      SetTransformCommand

      +
      public SetTransformCommand​(java.awt.geom.AffineTransform transform)
      +
      +
    • +
    +
    +
  • +
+
+ +
+ +
+
+ + diff --git a/io-vector/build/docs/javadoc/org.xbib.graphics.io.vector/org/xbib/graphics/io/vector/commands/SetXORModeCommand.html b/io-vector/build/docs/javadoc/org.xbib.graphics.io.vector/org/xbib/graphics/io/vector/commands/SetXORModeCommand.html new file mode 100644 index 0000000..d0c5f1f --- /dev/null +++ b/io-vector/build/docs/javadoc/org.xbib.graphics.io.vector/org/xbib/graphics/io/vector/commands/SetXORModeCommand.html @@ -0,0 +1,184 @@ + + + + + +SetXORModeCommand (io-vector 3.0.0 API) + + + + + + + + + + + + + +
+ +
+
+ +
+ + +

Class SetXORModeCommand

+
+
java.lang.Object +
org.xbib.graphics.io.vector.Command<T> +
org.xbib.graphics.io.vector.commands.StateCommand<java.awt.Color> +
org.xbib.graphics.io.vector.commands.SetXORModeCommand
+
+
+
+
+
+
public class SetXORModeCommand
+extends StateCommand<java.awt.Color>
+
+
+
    + +
  • +
    +

    Constructor Summary

    +
    + + + + + + + + + + + + + + +
    Constructors
    ConstructorDescription
    SetXORModeCommand​(java.awt.Color mode) 
    +
    +
    +
  • + +
  • +
    +

    Method Summary

    +
    +

    Methods inherited from class org.xbib.graphics.io.vector.Command

    +equals, getValue, toString
    +
    +

    Methods inherited from class java.lang.Object

    +clone, finalize, getClass, hashCode, notify, notifyAll, wait, wait, wait
    +
    +
  • +
+
+
+
    + +
  • +
    +

    Constructor Details

    +
      +
    • +
      +

      SetXORModeCommand

      +
      public SetXORModeCommand​(java.awt.Color mode)
      +
      +
    • +
    +
    +
  • +
+
+ +
+ +
+
+ + diff --git a/io-vector/build/docs/javadoc/org.xbib.graphics.io.vector/org/xbib/graphics/io/vector/commands/ShearCommand.html b/io-vector/build/docs/javadoc/org.xbib.graphics.io.vector/org/xbib/graphics/io/vector/commands/ShearCommand.html new file mode 100644 index 0000000..93ca541 --- /dev/null +++ b/io-vector/build/docs/javadoc/org.xbib.graphics.io.vector/org/xbib/graphics/io/vector/commands/ShearCommand.html @@ -0,0 +1,255 @@ + + + + + +ShearCommand (io-vector 3.0.0 API) + + + + + + + + + + + + + +
+ +
+
+ +
+ + +

Class ShearCommand

+
+
java.lang.Object + +
+
+
+
public class ShearCommand
+extends AffineTransformCommand
+
+
+
    + +
  • +
    +

    Constructor Summary

    +
    + + + + + + + + + + + + + + +
    Constructors
    ConstructorDescription
    ShearCommand​(double shearX, +double shearY) 
    +
    +
    +
  • + +
  • +
    +

    Method Summary

    +
    +
    +
    + + + + + + + + + + + + + + + + + + + + + + + + + +
    Modifier and TypeMethodDescription
    doublegetShearX() 
    doublegetShearY() 
    java.lang.StringtoString() 
    +
    +
    +
    +

    Methods inherited from class org.xbib.graphics.io.vector.Command

    +equals, getValue
    +
    +

    Methods inherited from class java.lang.Object

    +clone, finalize, getClass, hashCode, notify, notifyAll, wait, wait, wait
    +
    +
  • +
+
+
+
    + +
  • +
    +

    Constructor Details

    +
      +
    • +
      +

      ShearCommand

      +
      public ShearCommand​(double shearX, +double shearY)
      +
      +
    • +
    +
    +
  • + +
  • +
    +

    Method Details

    +
      +
    • +
      +

      getShearX

      +
      public double getShearX()
      +
      +
    • +
    • +
      +

      getShearY

      +
      public double getShearY()
      +
      +
    • +
    • +
      +

      toString

      +
      public java.lang.String toString()
      +
      +
      Overrides:
      +
      toString in class Command<java.awt.geom.AffineTransform>
      +
      +
      +
    • +
    +
    +
  • +
+
+ +
+ +
+
+ + diff --git a/io-vector/build/docs/javadoc/org.xbib.graphics.io.vector/org/xbib/graphics/io/vector/commands/StateCommand.html b/io-vector/build/docs/javadoc/org.xbib.graphics.io.vector/org/xbib/graphics/io/vector/commands/StateCommand.html new file mode 100644 index 0000000..a8182f6 --- /dev/null +++ b/io-vector/build/docs/javadoc/org.xbib.graphics.io.vector/org/xbib/graphics/io/vector/commands/StateCommand.html @@ -0,0 +1,186 @@ + + + + + +StateCommand (io-vector 3.0.0 API) + + + + + + + + + + + + + +
+ +
+
+ +
+ + +

Class StateCommand<T>

+
+
java.lang.Object +
org.xbib.graphics.io.vector.Command<T> +
org.xbib.graphics.io.vector.commands.StateCommand<T>
+
+
+
+
+
Direct Known Subclasses:
+
AffineTransformCommand, CreateCommand, DisposeCommand, SetBackgroundCommand, SetClipCommand, SetColorCommand, SetCompositeCommand, SetFontCommand, SetHintCommand, SetPaintCommand, SetStrokeCommand, SetTransformCommand, SetXORModeCommand
+
+
+
public abstract class StateCommand<T>
+extends Command<T>
+
+
+
    + +
  • +
    +

    Constructor Summary

    +
    + + + + + + + + + + + + + + +
    Constructors
    ConstructorDescription
    StateCommand​(T value) 
    +
    +
    +
  • + +
  • +
    +

    Method Summary

    +
    +

    Methods inherited from class org.xbib.graphics.io.vector.Command

    +equals, getValue, toString
    +
    +

    Methods inherited from class java.lang.Object

    +clone, finalize, getClass, hashCode, notify, notifyAll, wait, wait, wait
    +
    +
  • +
+
+
+
    + +
  • +
    +

    Constructor Details

    +
      +
    • +
      +

      StateCommand

      +
      public StateCommand​(T value)
      +
      +
    • +
    +
    +
  • +
+
+ +
+ +
+
+ + diff --git a/io-vector/build/docs/javadoc/org.xbib.graphics.io.vector/org/xbib/graphics/io/vector/commands/TransformCommand.html b/io-vector/build/docs/javadoc/org.xbib.graphics.io.vector/org/xbib/graphics/io/vector/commands/TransformCommand.html new file mode 100644 index 0000000..396f1b1 --- /dev/null +++ b/io-vector/build/docs/javadoc/org.xbib.graphics.io.vector/org/xbib/graphics/io/vector/commands/TransformCommand.html @@ -0,0 +1,227 @@ + + + + + +TransformCommand (io-vector 3.0.0 API) + + + + + + + + + + + + + +
+ +
+
+ +
+ + +

Class TransformCommand

+
+
java.lang.Object +
org.xbib.graphics.io.vector.Command<T> +
org.xbib.graphics.io.vector.commands.StateCommand<java.awt.geom.AffineTransform> +
org.xbib.graphics.io.vector.commands.AffineTransformCommand +
org.xbib.graphics.io.vector.commands.TransformCommand
+
+
+
+
+
+
+
public class TransformCommand
+extends AffineTransformCommand
+
+
+
    + +
  • +
    +

    Constructor Summary

    +
    + + + + + + + + + + + + + + +
    Constructors
    ConstructorDescription
    TransformCommand​(java.awt.geom.AffineTransform transform) 
    +
    +
    +
  • + +
  • +
    +

    Method Summary

    +
    +
    +
    + + + + + + + + + + + + + + + +
    Modifier and TypeMethodDescription
    java.awt.geom.AffineTransformgetTransform() 
    +
    +
    +
    +

    Methods inherited from class org.xbib.graphics.io.vector.Command

    +equals, getValue, toString
    +
    +

    Methods inherited from class java.lang.Object

    +clone, finalize, getClass, hashCode, notify, notifyAll, wait, wait, wait
    +
    +
  • +
+
+
+
    + +
  • +
    +

    Constructor Details

    +
      +
    • +
      +

      TransformCommand

      +
      public TransformCommand​(java.awt.geom.AffineTransform transform)
      +
      +
    • +
    +
    +
  • + +
  • +
    +

    Method Details

    +
      +
    • +
      +

      getTransform

      +
      public java.awt.geom.AffineTransform getTransform()
      +
      +
    • +
    +
    +
  • +
+
+ +
+ +
+
+ + diff --git a/io-vector/build/docs/javadoc/org.xbib.graphics.io.vector/org/xbib/graphics/io/vector/commands/TranslateCommand.html b/io-vector/build/docs/javadoc/org.xbib.graphics.io.vector/org/xbib/graphics/io/vector/commands/TranslateCommand.html new file mode 100644 index 0000000..6822cae --- /dev/null +++ b/io-vector/build/docs/javadoc/org.xbib.graphics.io.vector/org/xbib/graphics/io/vector/commands/TranslateCommand.html @@ -0,0 +1,255 @@ + + + + + +TranslateCommand (io-vector 3.0.0 API) + + + + + + + + + + + + + +
+ +
+
+ +
+ + +

Class TranslateCommand

+
+
java.lang.Object +
org.xbib.graphics.io.vector.Command<T> +
org.xbib.graphics.io.vector.commands.StateCommand<java.awt.geom.AffineTransform> +
org.xbib.graphics.io.vector.commands.AffineTransformCommand +
org.xbib.graphics.io.vector.commands.TranslateCommand
+
+
+
+
+
+
+
public class TranslateCommand
+extends AffineTransformCommand
+
+
+
    + +
  • +
    +

    Constructor Summary

    +
    + + + + + + + + + + + + + + +
    Constructors
    ConstructorDescription
    TranslateCommand​(double x, +double y) 
    +
    +
    +
  • + +
  • +
    +

    Method Summary

    +
    +
    +
    + + + + + + + + + + + + + + + + + + + + + + + + + +
    Modifier and TypeMethodDescription
    doublegetDeltaX() 
    doublegetDeltaY() 
    java.lang.StringtoString() 
    +
    +
    +
    +

    Methods inherited from class org.xbib.graphics.io.vector.Command

    +equals, getValue
    +
    +

    Methods inherited from class java.lang.Object

    +clone, finalize, getClass, hashCode, notify, notifyAll, wait, wait, wait
    +
    +
  • +
+
+
+
    + +
  • +
    +

    Constructor Details

    +
      +
    • +
      +

      TranslateCommand

      +
      public TranslateCommand​(double x, +double y)
      +
      +
    • +
    +
    +
  • + +
  • +
    +

    Method Details

    +
      +
    • +
      +

      getDeltaX

      +
      public double getDeltaX()
      +
      +
    • +
    • +
      +

      getDeltaY

      +
      public double getDeltaY()
      +
      +
    • +
    • +
      +

      toString

      +
      public java.lang.String toString()
      +
      +
      Overrides:
      +
      toString in class Command<java.awt.geom.AffineTransform>
      +
      +
      +
    • +
    +
    +
  • +
+
+ +
+ +
+
+ + diff --git a/io-vector/build/docs/javadoc/org.xbib.graphics.io.vector/org/xbib/graphics/io/vector/commands/package-summary.html b/io-vector/build/docs/javadoc/org.xbib.graphics.io.vector/org/xbib/graphics/io/vector/commands/package-summary.html new file mode 100644 index 0000000..2f8ae00 --- /dev/null +++ b/io-vector/build/docs/javadoc/org.xbib.graphics.io.vector/org/xbib/graphics/io/vector/commands/package-summary.html @@ -0,0 +1,195 @@ + + + + + +org.xbib.graphics.io.vector.commands (io-vector 3.0.0 API) + + + + + + + + + + + + + +
+ +
+
+
+ +

Package org.xbib.graphics.io.vector.commands

+
+
+ +
+
+ +
+
+ + diff --git a/io-vector/build/docs/javadoc/org.xbib.graphics.io.vector/org/xbib/graphics/io/vector/commands/package-tree.html b/io-vector/build/docs/javadoc/org.xbib.graphics.io.vector/org/xbib/graphics/io/vector/commands/package-tree.html new file mode 100644 index 0000000..c7197e8 --- /dev/null +++ b/io-vector/build/docs/javadoc/org.xbib.graphics.io.vector/org/xbib/graphics/io/vector/commands/package-tree.html @@ -0,0 +1,126 @@ + + + + + +org.xbib.graphics.io.vector.commands Class Hierarchy (io-vector 3.0.0 API) + + + + + + + + + + + + + +
+ +
+
+
+

Hierarchy For Package org.xbib.graphics.io.vector.commands

+Package Hierarchies: + +
+
+

Class Hierarchy

+ +
+
+ +
+
+ + diff --git a/io-vector/build/docs/javadoc/org.xbib.graphics.io.vector/org/xbib/graphics/io/vector/eps/EPSGraphics2D.html b/io-vector/build/docs/javadoc/org.xbib.graphics.io.vector/org/xbib/graphics/io/vector/eps/EPSGraphics2D.html new file mode 100644 index 0000000..93cc807 --- /dev/null +++ b/io-vector/build/docs/javadoc/org.xbib.graphics.io.vector/org/xbib/graphics/io/vector/eps/EPSGraphics2D.html @@ -0,0 +1,217 @@ + + + + + +EPSGraphics2D (io-vector 3.0.0 API) + + + + + + + + + + + + + +
+ +
+
+ +
+ + +

Class EPSGraphics2D

+
+
java.lang.Object +
java.awt.Graphics +
java.awt.Graphics2D +
org.xbib.graphics.io.vector.VectorGraphics2D +
org.xbib.graphics.io.vector.eps.EPSGraphics2D
+
+
+
+
+
+
+
All Implemented Interfaces:
+
java.lang.Cloneable
+
+
+
public class EPSGraphics2D
+extends VectorGraphics2D
+
Graphics2D implementation that saves all operations to a string + in the Encapsulated PostScript® (EPS) format.
+
+
+ +
+
+
    + +
  • +
    +

    Constructor Details

    +
      +
    • +
      +

      EPSGraphics2D

      +
      public EPSGraphics2D​(double x, +double y, +double width, +double height)
      +
      Initializes a new VectorGraphics2D pipeline for translating Graphics2D + commands to EPS data. The document dimensions must be specified as + parameters.
      +
      +
      Parameters:
      +
      x - Left offset.
      +
      y - Top offset
      +
      width - Width.
      +
      height - Height.
      +
      +
      +
    • +
    +
    +
  • +
+
+ +
+ +
+
+ + diff --git a/io-vector/build/docs/javadoc/org.xbib.graphics.io.vector/org/xbib/graphics/io/vector/eps/EPSProcessor.html b/io-vector/build/docs/javadoc/org.xbib.graphics.io.vector/org/xbib/graphics/io/vector/eps/EPSProcessor.html new file mode 100644 index 0000000..ce9f358 --- /dev/null +++ b/io-vector/build/docs/javadoc/org.xbib.graphics.io.vector/org/xbib/graphics/io/vector/eps/EPSProcessor.html @@ -0,0 +1,232 @@ + + + + + +EPSProcessor (io-vector 3.0.0 API) + + + + + + + + + + + + + +
+ +
+
+ +
+ + +

Class EPSProcessor

+
+
java.lang.Object +
org.xbib.graphics.io.vector.eps.EPSProcessor
+
+
+
+
All Implemented Interfaces:
+
Processor
+
+
+
public class EPSProcessor
+extends java.lang.Object
+implements Processor
+
+
+
    + +
  • +
    +

    Constructor Summary

    +
    + + + + + + + + + + + + + + +
    Constructors
    ConstructorDescription
    EPSProcessor() 
    +
    +
    +
  • + +
  • +
    +

    Method Summary

    +
    +
    +
    + + + + + + + + + + + + + + + +
    Modifier and TypeMethodDescription
    ProcessorResultprocess​(java.lang.Iterable<Command<?>> commands, +PageSize pageSize) 
    +
    +
    +
    +

    Methods inherited from class java.lang.Object

    +clone, equals, finalize, getClass, hashCode, notify, notifyAll, toString, wait, wait, wait
    +
    +
  • +
+
+
+
    + +
  • +
    +

    Constructor Details

    +
      +
    • +
      +

      EPSProcessor

      +
      public EPSProcessor()
      +
      +
    • +
    +
    +
  • + +
  • +
    +

    Method Details

    +
      +
    • +
      +

      process

      +
      public ProcessorResult process​(java.lang.Iterable<Command<?>> commands, +PageSize pageSize) + throws java.io.IOException
      +
      +
      Specified by:
      +
      process in interface Processor
      +
      Throws:
      +
      java.io.IOException
      +
      +
      +
    • +
    +
    +
  • +
+
+ +
+ +
+
+ + diff --git a/io-vector/build/docs/javadoc/org.xbib.graphics.io.vector/org/xbib/graphics/io/vector/eps/EPSProcessorResult.html b/io-vector/build/docs/javadoc/org.xbib.graphics.io.vector/org/xbib/graphics/io/vector/eps/EPSProcessorResult.html new file mode 100644 index 0000000..14548be --- /dev/null +++ b/io-vector/build/docs/javadoc/org.xbib.graphics.io.vector/org/xbib/graphics/io/vector/eps/EPSProcessorResult.html @@ -0,0 +1,263 @@ + + + + + +EPSProcessorResult (io-vector 3.0.0 API) + + + + + + + + + + + + + +
+ +
+
+ +
+ + +

Class EPSProcessorResult

+
+
java.lang.Object +
org.xbib.graphics.io.vector.eps.EPSProcessorResult
+
+
+
+
All Implemented Interfaces:
+
ProcessorResult
+
+
+
public class EPSProcessorResult
+extends java.lang.Object
+implements ProcessorResult
+
+
+
    + +
  • +
    +

    Constructor Summary

    +
    + + + + + + + + + + + + + + +
    Constructors
    ConstructorDescription
    EPSProcessorResult​(PageSize pageSize) 
    +
    +
    +
  • + +
  • +
    +

    Method Summary

    +
    +
    +
    + + + + + + + + + + + + + + + + + + + + + + + + + +
    Modifier and TypeMethodDescription
    voidclose() 
    voidhandle​(Command<?> command) 
    voidwrite​(java.io.OutputStream out) 
    +
    +
    +
    +

    Methods inherited from class java.lang.Object

    +clone, equals, finalize, getClass, hashCode, notify, notifyAll, toString, wait, wait, wait
    +
    +
  • +
+
+
+
    + +
  • +
    +

    Constructor Details

    +
      +
    • +
      +

      EPSProcessorResult

      +
      public EPSProcessorResult​(PageSize pageSize)
      +
      +
    • +
    +
    +
  • + +
  • +
    +

    Method Details

    +
      +
    • +
      +

      write

      +
      public void write​(java.io.OutputStream out) + throws java.io.IOException
      +
      +
      Specified by:
      +
      write in interface ProcessorResult
      +
      Throws:
      +
      java.io.IOException
      +
      +
      +
    • +
    • +
      +

      close

      +
      public void close()
      +
      +
      Specified by:
      +
      close in interface ProcessorResult
      +
      +
      +
    • +
    • +
      +

      handle

      +
      public void handle​(Command<?> command) + throws java.io.IOException
      +
      +
      Specified by:
      +
      handle in interface ProcessorResult
      +
      Throws:
      +
      java.io.IOException
      +
      +
      +
    • +
    +
    +
  • +
+
+ +
+ +
+
+ + diff --git a/io-vector/build/docs/javadoc/org.xbib.graphics.io.vector/org/xbib/graphics/io/vector/eps/package-summary.html b/io-vector/build/docs/javadoc/org.xbib.graphics.io.vector/org/xbib/graphics/io/vector/eps/package-summary.html new file mode 100644 index 0000000..cf3c2fb --- /dev/null +++ b/io-vector/build/docs/javadoc/org.xbib.graphics.io.vector/org/xbib/graphics/io/vector/eps/package-summary.html @@ -0,0 +1,114 @@ + + + + + +org.xbib.graphics.io.vector.eps (io-vector 3.0.0 API) + + + + + + + + + + + + + +
+ +
+
+
+ +

Package org.xbib.graphics.io.vector.eps

+
+
+
    +
  • +
    + + + + + + + + + + + + + + + + + + + + + + +
    Class Summary
    ClassDescription
    EPSGraphics2D +
    Graphics2D implementation that saves all operations to a string + in the Encapsulated PostScript® (EPS) format.
    +
    EPSProcessor 
    EPSProcessorResult 
    +
    +
  • +
+
+
+ +
+
+ + diff --git a/io-vector/build/docs/javadoc/org.xbib.graphics.io.vector/org/xbib/graphics/io/vector/eps/package-tree.html b/io-vector/build/docs/javadoc/org.xbib.graphics.io.vector/org/xbib/graphics/io/vector/eps/package-tree.html new file mode 100644 index 0000000..7ad64b6 --- /dev/null +++ b/io-vector/build/docs/javadoc/org.xbib.graphics.io.vector/org/xbib/graphics/io/vector/eps/package-tree.html @@ -0,0 +1,107 @@ + + + + + +org.xbib.graphics.io.vector.eps Class Hierarchy (io-vector 3.0.0 API) + + + + + + + + + + + + + +
+ +
+
+
+

Hierarchy For Package org.xbib.graphics.io.vector.eps

+Package Hierarchies: + +
+
+

Class Hierarchy

+
    +
  • java.lang.Object + +
  • +
+
+
+ +
+
+ + diff --git a/io-vector/build/docs/javadoc/org.xbib.graphics.io.vector/org/xbib/graphics/io/vector/filters/AbsoluteToRelativeTransformsFilter.html b/io-vector/build/docs/javadoc/org.xbib.graphics.io.vector/org/xbib/graphics/io/vector/filters/AbsoluteToRelativeTransformsFilter.html new file mode 100644 index 0000000..8a7a2dc --- /dev/null +++ b/io-vector/build/docs/javadoc/org.xbib.graphics.io.vector/org/xbib/graphics/io/vector/filters/AbsoluteToRelativeTransformsFilter.html @@ -0,0 +1,254 @@ + + + + + +AbsoluteToRelativeTransformsFilter (io-vector 3.0.0 API) + + + + + + + + + + + + + +
+ +
+
+ +
+ + +

Class AbsoluteToRelativeTransformsFilter

+
+
java.lang.Object +
org.xbib.graphics.io.vector.filters.Filter +
org.xbib.graphics.io.vector.filters.AbsoluteToRelativeTransformsFilter
+
+
+
+
+
All Implemented Interfaces:
+
java.lang.Iterable<Command<?>>, java.util.Iterator<Command<?>>
+
+
+
public class AbsoluteToRelativeTransformsFilter
+extends Filter
+
+
+
    + +
  • +
    +

    Constructor Summary

    +
    + + + + + + + + + + + + + + +
    Constructors
    ConstructorDescription
    AbsoluteToRelativeTransformsFilter​(java.lang.Iterable<Command<?>> stream) 
    +
    +
    +
  • + +
  • +
    +

    Method Summary

    +
    +
    +
    + + + + + + + + + + + + + + + + + + + + +
    Modifier and TypeMethodDescription
    protected java.util.List<Command<?>>filter​(Command<?> command) 
    Command<?>next() 
    +
    +
    +
    +

    Methods inherited from class org.xbib.graphics.io.vector.filters.Filter

    +hasNext, iterator, remove
    +
    +

    Methods inherited from class java.lang.Object

    +clone, equals, finalize, getClass, hashCode, notify, notifyAll, toString, wait, wait, wait
    +
    +

    Methods inherited from interface java.lang.Iterable

    +forEach, spliterator
    +
    +

    Methods inherited from interface java.util.Iterator

    +forEachRemaining
    +
    +
  • +
+
+
+
    + +
  • +
    +

    Constructor Details

    +
      +
    • +
      +

      AbsoluteToRelativeTransformsFilter

      +
      public AbsoluteToRelativeTransformsFilter​(java.lang.Iterable<Command<?>> stream)
      +
      +
    • +
    +
    +
  • + +
  • +
    +

    Method Details

    +
      +
    • +
      +

      next

      +
      public Command<?> next()
      +
      +
      Specified by:
      +
      next in interface java.util.Iterator<Command<?>>
      +
      Overrides:
      +
      next in class Filter
      +
      +
      +
    • +
    • +
      +

      filter

      +
      protected java.util.List<Command<?>> filter​(Command<?> command)
      +
      +
      Specified by:
      +
      filter in class Filter
      +
      +
      +
    • +
    +
    +
  • +
+
+ +
+ +
+
+ + diff --git a/io-vector/build/docs/javadoc/org.xbib.graphics.io.vector/org/xbib/graphics/io/vector/filters/FillPaintedShapeAsImageFilter.html b/io-vector/build/docs/javadoc/org.xbib.graphics.io.vector/org/xbib/graphics/io/vector/filters/FillPaintedShapeAsImageFilter.html new file mode 100644 index 0000000..22157fa --- /dev/null +++ b/io-vector/build/docs/javadoc/org.xbib.graphics.io.vector/org/xbib/graphics/io/vector/filters/FillPaintedShapeAsImageFilter.html @@ -0,0 +1,254 @@ + + + + + +FillPaintedShapeAsImageFilter (io-vector 3.0.0 API) + + + + + + + + + + + + + +
+ +
+
+ +
+ + +

Class FillPaintedShapeAsImageFilter

+
+
java.lang.Object +
org.xbib.graphics.io.vector.filters.Filter +
org.xbib.graphics.io.vector.filters.FillPaintedShapeAsImageFilter
+
+
+
+
+
All Implemented Interfaces:
+
java.lang.Iterable<Command<?>>, java.util.Iterator<Command<?>>
+
+
+
public class FillPaintedShapeAsImageFilter
+extends Filter
+
+
+
    + +
  • +
    +

    Constructor Summary

    +
    + + + + + + + + + + + + + + +
    Constructors
    ConstructorDescription
    FillPaintedShapeAsImageFilter​(java.lang.Iterable<Command<?>> stream) 
    +
    +
    +
  • + +
  • +
    +

    Method Summary

    +
    +
    +
    + + + + + + + + + + + + + + + + + + + + +
    Modifier and TypeMethodDescription
    protected java.util.List<Command<?>>filter​(Command<?> command) 
    Command<?>next() 
    +
    +
    +
    +

    Methods inherited from class org.xbib.graphics.io.vector.filters.Filter

    +hasNext, iterator, remove
    +
    +

    Methods inherited from class java.lang.Object

    +clone, equals, finalize, getClass, hashCode, notify, notifyAll, toString, wait, wait, wait
    +
    +

    Methods inherited from interface java.lang.Iterable

    +forEach, spliterator
    +
    +

    Methods inherited from interface java.util.Iterator

    +forEachRemaining
    +
    +
  • +
+
+
+
    + +
  • +
    +

    Constructor Details

    +
      +
    • +
      +

      FillPaintedShapeAsImageFilter

      +
      public FillPaintedShapeAsImageFilter​(java.lang.Iterable<Command<?>> stream)
      +
      +
    • +
    +
    +
  • + +
  • +
    +

    Method Details

    +
      +
    • +
      +

      next

      +
      public Command<?> next()
      +
      +
      Specified by:
      +
      next in interface java.util.Iterator<Command<?>>
      +
      Overrides:
      +
      next in class Filter
      +
      +
      +
    • +
    • +
      +

      filter

      +
      protected java.util.List<Command<?>> filter​(Command<?> command)
      +
      +
      Specified by:
      +
      filter in class Filter
      +
      +
      +
    • +
    +
    +
  • +
+
+ +
+ +
+
+ + diff --git a/io-vector/build/docs/javadoc/org.xbib.graphics.io.vector/org/xbib/graphics/io/vector/filters/Filter.html b/io-vector/build/docs/javadoc/org.xbib.graphics.io.vector/org/xbib/graphics/io/vector/filters/Filter.html new file mode 100644 index 0000000..2e89ecf --- /dev/null +++ b/io-vector/build/docs/javadoc/org.xbib.graphics.io.vector/org/xbib/graphics/io/vector/filters/Filter.html @@ -0,0 +1,293 @@ + + + + + +Filter (io-vector 3.0.0 API) + + + + + + + + + + + + + +
+ +
+
+ + +
java.lang.Object +
org.xbib.graphics.io.vector.filters.Filter
+
+
+
+
All Implemented Interfaces:
+
java.lang.Iterable<Command<?>>, java.util.Iterator<Command<?>>
+
+
+
Direct Known Subclasses:
+
AbsoluteToRelativeTransformsFilter, FillPaintedShapeAsImageFilter, GroupingFilter, OptimizeFilter
+
+
+
public abstract class Filter
+extends java.lang.Object
+implements java.lang.Iterable<Command<?>>, java.util.Iterator<Command<?>>
+
+
+
    + +
  • +
    +

    Constructor Summary

    +
    + + + + + + + + + + + + + + +
    Constructors
    ConstructorDescription
    Filter​(java.lang.Iterable<Command<?>> stream) 
    +
    +
    +
  • + +
  • +
    +

    Method Summary

    +
    +
    +
    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    Modifier and TypeMethodDescription
    protected abstract java.util.List<Command<?>>filter​(Command<?> command) 
    booleanhasNext() 
    java.util.Iterator<Command<?>>iterator() 
    Command<?>next() 
    voidremove() 
    +
    +
    +
    +

    Methods inherited from class java.lang.Object

    +clone, equals, finalize, getClass, hashCode, notify, notifyAll, toString, wait, wait, wait
    +
    +

    Methods inherited from interface java.lang.Iterable

    +forEach, spliterator
    +
    +

    Methods inherited from interface java.util.Iterator

    +forEachRemaining
    +
    +
  • +
+
+
+
    + +
  • +
    +

    Constructor Details

    +
      +
    • +
      +

      Filter

      +
      public Filter​(java.lang.Iterable<Command<?>> stream)
      +
      +
    • +
    +
    +
  • + +
  • +
    +

    Method Details

    +
      +
    • +
      +

      iterator

      +
      public java.util.Iterator<Command<?>> iterator()
      +
      +
      Specified by:
      +
      iterator in interface java.lang.Iterable<Command<?>>
      +
      +
      +
    • +
    • +
      +

      hasNext

      +
      public boolean hasNext()
      +
      +
      Specified by:
      +
      hasNext in interface java.util.Iterator<Command<?>>
      +
      +
      +
    • +
    • +
      +

      next

      +
      public Command<?> next()
      +
      +
      Specified by:
      +
      next in interface java.util.Iterator<Command<?>>
      +
      +
      +
    • +
    • +
      +

      remove

      +
      public void remove()
      +
      +
      Specified by:
      +
      remove in interface java.util.Iterator<Command<?>>
      +
      +
      +
    • +
    • +
      +

      filter

      +
      protected abstract java.util.List<Command<?>> filter​(Command<?> command)
      +
      +
    • +
    +
    +
  • +
+
+ +
+ +
+
+ + diff --git a/io-vector/build/docs/javadoc/org.xbib.graphics.io.vector/org/xbib/graphics/io/vector/filters/GroupingFilter.html b/io-vector/build/docs/javadoc/org.xbib.graphics.io.vector/org/xbib/graphics/io/vector/filters/GroupingFilter.html new file mode 100644 index 0000000..7b386e0 --- /dev/null +++ b/io-vector/build/docs/javadoc/org.xbib.graphics.io.vector/org/xbib/graphics/io/vector/filters/GroupingFilter.html @@ -0,0 +1,286 @@ + + + + + +GroupingFilter (io-vector 3.0.0 API) + + + + + + + + + + + + + +
+ +
+
+ +
+ + +

Class GroupingFilter

+
+
java.lang.Object +
org.xbib.graphics.io.vector.filters.Filter +
org.xbib.graphics.io.vector.filters.GroupingFilter
+
+
+
+
+
All Implemented Interfaces:
+
java.lang.Iterable<Command<?>>, java.util.Iterator<Command<?>>
+
+
+
Direct Known Subclasses:
+
StateChangeGroupingFilter
+
+
+
public abstract class GroupingFilter
+extends Filter
+
+
+
    + +
  • +
    +

    Constructor Summary

    +
    + + + + + + + + + + + + + + +
    Constructors
    ConstructorDescription
    GroupingFilter​(java.lang.Iterable<Command<?>> stream) 
    +
    +
    +
  • + +
  • +
    +

    Method Summary

    +
    +
    +
    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    Modifier and TypeMethodDescription
    protected java.util.List<Command<?>>filter​(Command<?> command) 
    booleanhasNext() 
    protected abstract booleanisGrouped​(Command<?> command) 
    Command<?>next() 
    +
    +
    +
    +

    Methods inherited from class org.xbib.graphics.io.vector.filters.Filter

    +iterator, remove
    +
    +

    Methods inherited from class java.lang.Object

    +clone, equals, finalize, getClass, hashCode, notify, notifyAll, toString, wait, wait, wait
    +
    +

    Methods inherited from interface java.lang.Iterable

    +forEach, spliterator
    +
    +

    Methods inherited from interface java.util.Iterator

    +forEachRemaining
    +
    +
  • +
+
+
+
    + +
  • +
    +

    Constructor Details

    +
      +
    • +
      +

      GroupingFilter

      +
      public GroupingFilter​(java.lang.Iterable<Command<?>> stream)
      +
      +
    • +
    +
    +
  • + +
  • +
    +

    Method Details

    +
      +
    • +
      +

      hasNext

      +
      public boolean hasNext()
      +
      +
      Specified by:
      +
      hasNext in interface java.util.Iterator<Command<?>>
      +
      Overrides:
      +
      hasNext in class Filter
      +
      +
      +
    • +
    • +
      +

      next

      +
      public Command<?> next()
      +
      +
      Specified by:
      +
      next in interface java.util.Iterator<Command<?>>
      +
      Overrides:
      +
      next in class Filter
      +
      +
      +
    • +
    • +
      +

      filter

      +
      protected java.util.List<Command<?>> filter​(Command<?> command)
      +
      +
      Specified by:
      +
      filter in class Filter
      +
      +
      +
    • +
    • +
      +

      isGrouped

      +
      protected abstract boolean isGrouped​(Command<?> command)
      +
      +
    • +
    +
    +
  • +
+
+ +
+ +
+
+ + diff --git a/io-vector/build/docs/javadoc/org.xbib.graphics.io.vector/org/xbib/graphics/io/vector/filters/OptimizeFilter.html b/io-vector/build/docs/javadoc/org.xbib.graphics.io.vector/org/xbib/graphics/io/vector/filters/OptimizeFilter.html new file mode 100644 index 0000000..0a442b8 --- /dev/null +++ b/io-vector/build/docs/javadoc/org.xbib.graphics.io.vector/org/xbib/graphics/io/vector/filters/OptimizeFilter.html @@ -0,0 +1,271 @@ + + + + + +OptimizeFilter (io-vector 3.0.0 API) + + + + + + + + + + + + + +
+ +
+
+ +
+ + +

Class OptimizeFilter

+
+
java.lang.Object +
org.xbib.graphics.io.vector.filters.Filter +
org.xbib.graphics.io.vector.filters.OptimizeFilter
+
+
+
+
+
All Implemented Interfaces:
+
java.lang.Iterable<Command<?>>, java.util.Iterator<Command<?>>
+
+
+
public class OptimizeFilter
+extends Filter
+
+
+
    + +
  • +
    +

    Constructor Summary

    +
    + + + + + + + + + + + + + + +
    Constructors
    ConstructorDescription
    OptimizeFilter​(java.lang.Iterable<Command<?>> stream) 
    +
    +
    +
  • + +
  • +
    +

    Method Summary

    +
    +
    +
    + + + + + + + + + + + + + + + + + + + + + + + + + +
    Modifier and TypeMethodDescription
    protected java.util.List<Command<?>>filter​(Command<?> command) 
    booleanhasNext() 
    Command<?>next() 
    +
    +
    +
    +

    Methods inherited from class org.xbib.graphics.io.vector.filters.Filter

    +iterator, remove
    +
    +

    Methods inherited from class java.lang.Object

    +clone, equals, finalize, getClass, hashCode, notify, notifyAll, toString, wait, wait, wait
    +
    +

    Methods inherited from interface java.lang.Iterable

    +forEach, spliterator
    +
    +

    Methods inherited from interface java.util.Iterator

    +forEachRemaining
    +
    +
  • +
+
+
+
    + +
  • +
    +

    Constructor Details

    +
      +
    • +
      +

      OptimizeFilter

      +
      public OptimizeFilter​(java.lang.Iterable<Command<?>> stream)
      +
      +
    • +
    +
    +
  • + +
  • +
    +

    Method Details

    +
      +
    • +
      +

      hasNext

      +
      public boolean hasNext()
      +
      +
      Specified by:
      +
      hasNext in interface java.util.Iterator<Command<?>>
      +
      Overrides:
      +
      hasNext in class Filter
      +
      +
      +
    • +
    • +
      +

      next

      +
      public Command<?> next()
      +
      +
      Specified by:
      +
      next in interface java.util.Iterator<Command<?>>
      +
      Overrides:
      +
      next in class Filter
      +
      +
      +
    • +
    • +
      +

      filter

      +
      protected java.util.List<Command<?>> filter​(Command<?> command)
      +
      +
      Specified by:
      +
      filter in class Filter
      +
      +
      +
    • +
    +
    +
  • +
+
+ +
+ +
+
+ + diff --git a/io-vector/build/docs/javadoc/org.xbib.graphics.io.vector/org/xbib/graphics/io/vector/filters/StateChangeGroupingFilter.html b/io-vector/build/docs/javadoc/org.xbib.graphics.io.vector/org/xbib/graphics/io/vector/filters/StateChangeGroupingFilter.html new file mode 100644 index 0000000..e878f1b --- /dev/null +++ b/io-vector/build/docs/javadoc/org.xbib.graphics.io.vector/org/xbib/graphics/io/vector/filters/StateChangeGroupingFilter.html @@ -0,0 +1,242 @@ + + + + + +StateChangeGroupingFilter (io-vector 3.0.0 API) + + + + + + + + + + + + + +
+ +
+
+ +
+ + +

Class StateChangeGroupingFilter

+
+
java.lang.Object +
org.xbib.graphics.io.vector.filters.Filter +
org.xbib.graphics.io.vector.filters.GroupingFilter +
org.xbib.graphics.io.vector.filters.StateChangeGroupingFilter
+
+
+
+
+
+
All Implemented Interfaces:
+
java.lang.Iterable<Command<?>>, java.util.Iterator<Command<?>>
+
+
+
public class StateChangeGroupingFilter
+extends GroupingFilter
+
+
+
    + +
  • +
    +

    Constructor Summary

    +
    + + + + + + + + + + + + + + +
    Constructors
    ConstructorDescription
    StateChangeGroupingFilter​(java.lang.Iterable<Command<?>> stream) 
    +
    +
    +
  • + +
  • +
    +

    Method Summary

    +
    +
    +
    + + + + + + + + + + + + + + + +
    Modifier and TypeMethodDescription
    protected booleanisGrouped​(Command<?> command) 
    +
    +
    +
    +

    Methods inherited from class org.xbib.graphics.io.vector.filters.GroupingFilter

    +filter, hasNext, next
    +
    +

    Methods inherited from class org.xbib.graphics.io.vector.filters.Filter

    +iterator, remove
    +
    +

    Methods inherited from class java.lang.Object

    +clone, equals, finalize, getClass, hashCode, notify, notifyAll, toString, wait, wait, wait
    +
    +

    Methods inherited from interface java.lang.Iterable

    +forEach, spliterator
    +
    +

    Methods inherited from interface java.util.Iterator

    +forEachRemaining
    +
    +
  • +
+
+
+
    + +
  • +
    +

    Constructor Details

    +
      +
    • +
      +

      StateChangeGroupingFilter

      +
      public StateChangeGroupingFilter​(java.lang.Iterable<Command<?>> stream)
      +
      +
    • +
    +
    +
  • + +
  • +
    +

    Method Details

    + +
    +
  • +
+
+ +
+ +
+
+ + diff --git a/io-vector/build/docs/javadoc/org.xbib.graphics.io.vector/org/xbib/graphics/io/vector/filters/package-summary.html b/io-vector/build/docs/javadoc/org.xbib.graphics.io.vector/org/xbib/graphics/io/vector/filters/package-summary.html new file mode 100644 index 0000000..bffcac4 --- /dev/null +++ b/io-vector/build/docs/javadoc/org.xbib.graphics.io.vector/org/xbib/graphics/io/vector/filters/package-summary.html @@ -0,0 +1,123 @@ + + + + + +org.xbib.graphics.io.vector.filters (io-vector 3.0.0 API) + + + + + + + + + + + + + +
+ +
+
+
+ +

Package org.xbib.graphics.io.vector.filters

+
+
+ +
+
+ +
+
+ + diff --git a/io-vector/build/docs/javadoc/org.xbib.graphics.io.vector/org/xbib/graphics/io/vector/filters/package-tree.html b/io-vector/build/docs/javadoc/org.xbib.graphics.io.vector/org/xbib/graphics/io/vector/filters/package-tree.html new file mode 100644 index 0000000..322cfb7 --- /dev/null +++ b/io-vector/build/docs/javadoc/org.xbib.graphics.io.vector/org/xbib/graphics/io/vector/filters/package-tree.html @@ -0,0 +1,104 @@ + + + + + +org.xbib.graphics.io.vector.filters Class Hierarchy (io-vector 3.0.0 API) + + + + + + + + + + + + + +
+ +
+
+
+

Hierarchy For Package org.xbib.graphics.io.vector.filters

+Package Hierarchies: + +
+
+

Class Hierarchy

+ +
+
+ +
+
+ + diff --git a/io-vector/build/docs/javadoc/org.xbib.graphics.io.vector/org/xbib/graphics/io/vector/package-summary.html b/io-vector/build/docs/javadoc/org.xbib.graphics.io.vector/org/xbib/graphics/io/vector/package-summary.html new file mode 100644 index 0000000..927ffff --- /dev/null +++ b/io-vector/build/docs/javadoc/org.xbib.graphics.io.vector/org/xbib/graphics/io/vector/package-summary.html @@ -0,0 +1,159 @@ + + + + + +org.xbib.graphics.io.vector (io-vector 3.0.0 API) + + + + + + + + + + + + + +
+ +
+
+
+ +

Package org.xbib.graphics.io.vector

+
+
+ +
+
+ +
+
+ + diff --git a/io-vector/build/docs/javadoc/org.xbib.graphics.io.vector/org/xbib/graphics/io/vector/package-tree.html b/io-vector/build/docs/javadoc/org.xbib.graphics.io.vector/org/xbib/graphics/io/vector/package-tree.html new file mode 100644 index 0000000..4a41ef1 --- /dev/null +++ b/io-vector/build/docs/javadoc/org.xbib.graphics.io.vector/org/xbib/graphics/io/vector/package-tree.html @@ -0,0 +1,125 @@ + + + + + +org.xbib.graphics.io.vector Class Hierarchy (io-vector 3.0.0 API) + + + + + + + + + + + + + +
+ +
+
+
+

Hierarchy For Package org.xbib.graphics.io.vector

+Package Hierarchies: + +
+
+

Class Hierarchy

+
    +
  • java.lang.Object +
      +
    • org.xbib.graphics.io.vector.Command<T>
    • +
    • java.awt.Graphics +
        +
      • java.awt.Graphics2D +
          +
        • org.xbib.graphics.io.vector.VectorGraphics2D (implements java.lang.Cloneable)
        • +
        +
      • +
      +
    • +
    • org.xbib.graphics.io.vector.GraphicsState (implements java.lang.Cloneable)
    • +
    • org.xbib.graphics.io.vector.PageSize
    • +
    +
  • +
+
+
+

Interface Hierarchy

+ +
+
+

Enum Hierarchy

+
    +
  • java.lang.Object +
      +
    • java.lang.Enum<E> (implements java.lang.Comparable<T>, java.lang.constant.Constable, java.io.Serializable) + +
    • +
    +
  • +
+
+
+ +
+
+ + diff --git a/io-vector/build/docs/javadoc/org.xbib.graphics.io.vector/org/xbib/graphics/io/vector/pdf/GeneratedPayload.html b/io-vector/build/docs/javadoc/org.xbib.graphics.io.vector/org/xbib/graphics/io/vector/pdf/GeneratedPayload.html new file mode 100644 index 0000000..00be0a2 --- /dev/null +++ b/io-vector/build/docs/javadoc/org.xbib.graphics.io.vector/org/xbib/graphics/io/vector/pdf/GeneratedPayload.html @@ -0,0 +1,277 @@ + + + + + +GeneratedPayload (io-vector 3.0.0 API) + + + + + + + + + + + + + +
+ +
+
+ +
+ + +

Class GeneratedPayload

+
+
java.lang.Object +
java.io.OutputStream +
org.xbib.graphics.io.vector.pdf.Payload +
org.xbib.graphics.io.vector.pdf.GeneratedPayload
+
+
+
+
+
+
All Implemented Interfaces:
+
java.io.Closeable, java.io.Flushable, java.lang.AutoCloseable
+
+
+
Direct Known Subclasses:
+
SizePayload
+
+
+
public abstract class GeneratedPayload
+extends Payload
+
+
+
    + +
  • +
    +

    Constructor Summary

    +
    + + + + + + + + + + + + + + +
    Constructors
    ConstructorDescription
    GeneratedPayload​(boolean stream) 
    +
    +
    +
  • + +
  • +
    +

    Method Summary

    +
    +
    +
    + + + + + + + + + + + + + + + + + + + + + + + + + +
    Modifier and TypeMethodDescription
    protected abstract byte[]generatePayload() 
    byte[]getBytes() 
    voidwrite​(int b) 
    +
    +
    +
    +

    Methods inherited from class org.xbib.graphics.io.vector.pdf.Payload

    +addFilter, close, isStream
    +
    +

    Methods inherited from class java.io.OutputStream

    +flush, nullOutputStream, write, write
    +
    +

    Methods inherited from class java.lang.Object

    +clone, equals, finalize, getClass, hashCode, notify, notifyAll, toString, wait, wait, wait
    +
    +
  • +
+
+
+
    + +
  • +
    +

    Constructor Details

    +
      +
    • +
      +

      GeneratedPayload

      +
      public GeneratedPayload​(boolean stream)
      +
      +
    • +
    +
    +
  • + +
  • +
    +

    Method Details

    +
      +
    • +
      +

      getBytes

      +
      public byte[] getBytes() + throws java.io.IOException
      +
      +
      Overrides:
      +
      getBytes in class Payload
      +
      Throws:
      +
      java.io.IOException
      +
      +
      +
    • +
    • +
      +

      write

      +
      public void write​(int b) + throws java.io.IOException
      +
      +
      Overrides:
      +
      write in class Payload
      +
      Throws:
      +
      java.io.IOException
      +
      +
      +
    • +
    • +
      +

      generatePayload

      +
      protected abstract byte[] generatePayload() + throws java.io.IOException
      +
      +
      Throws:
      +
      java.io.IOException
      +
      +
      +
    • +
    +
    +
  • +
+
+ +
+ +
+
+ + diff --git a/io-vector/build/docs/javadoc/org.xbib.graphics.io.vector/org/xbib/graphics/io/vector/pdf/PDFGraphics2D.html b/io-vector/build/docs/javadoc/org.xbib.graphics.io.vector/org/xbib/graphics/io/vector/pdf/PDFGraphics2D.html new file mode 100644 index 0000000..b51ac79 --- /dev/null +++ b/io-vector/build/docs/javadoc/org.xbib.graphics.io.vector/org/xbib/graphics/io/vector/pdf/PDFGraphics2D.html @@ -0,0 +1,217 @@ + + + + + +PDFGraphics2D (io-vector 3.0.0 API) + + + + + + + + + + + + + +
+ +
+
+ +
+ + +

Class PDFGraphics2D

+
+
java.lang.Object +
java.awt.Graphics +
java.awt.Graphics2D +
org.xbib.graphics.io.vector.VectorGraphics2D +
org.xbib.graphics.io.vector.pdf.PDFGraphics2D
+
+
+
+
+
+
+
All Implemented Interfaces:
+
java.lang.Cloneable
+
+
+
public class PDFGraphics2D
+extends VectorGraphics2D
+
Graphics2D implementation that saves all operations to a string + in the Portable Document Format (PDF).
+
+
+ +
+
+
    + +
  • +
    +

    Constructor Details

    +
      +
    • +
      +

      PDFGraphics2D

      +
      public PDFGraphics2D​(double x, +double y, +double width, +double height)
      +
      Initializes a new VectorGraphics2D pipeline for translating Graphics2D + commands to PDF data. The document dimensions must be specified as + parameters.
      +
      +
      Parameters:
      +
      x - Left offset.
      +
      y - Top offset
      +
      width - Width.
      +
      height - Height.
      +
      +
      +
    • +
    +
    +
  • +
+
+ +
+ +
+
+ + diff --git a/io-vector/build/docs/javadoc/org.xbib.graphics.io.vector/org/xbib/graphics/io/vector/pdf/PDFObject.html b/io-vector/build/docs/javadoc/org.xbib.graphics.io.vector/org/xbib/graphics/io/vector/pdf/PDFObject.html new file mode 100644 index 0000000..4bfb061 --- /dev/null +++ b/io-vector/build/docs/javadoc/org.xbib.graphics.io.vector/org/xbib/graphics/io/vector/pdf/PDFObject.html @@ -0,0 +1,259 @@ + + + + + +PDFObject (io-vector 3.0.0 API) + + + + + + + + + + + + + +
+ +
+
+ +
+ + +

Class PDFObject

+
+
java.lang.Object +
org.xbib.graphics.io.vector.pdf.PDFObject
+
+
+
+
Direct Known Subclasses:
+
Resources
+
+
+
public class PDFObject
+extends java.lang.Object
+
+
+
    + +
  • +
    +

    Field Summary

    +
    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    Fields
    Modifier and TypeFieldDescription
    java.util.Map<java.lang.String,​java.lang.Object>dict 
    intid 
    Payloadpayload 
    intversion 
    +
    +
    +
  • + +
  • +
    +

    Constructor Summary

    +
    + + + + + + + + + + + + + + +
    Constructors
    ConstructorDescription
    PDFObject​(int id, +int version, +java.util.Map<java.lang.String,​java.lang.Object> dict, +Payload payload) 
    +
    +
    +
  • + +
  • +
    +

    Method Summary

    +
    +

    Methods inherited from class java.lang.Object

    +clone, equals, finalize, getClass, hashCode, notify, notifyAll, toString, wait, wait, wait
    +
    +
  • +
+
+
+
    + +
  • +
    +

    Field Details

    +
      +
    • +
      +

      id

      +
      public final int id
      +
      +
    • +
    • +
      +

      version

      +
      public final int version
      +
      +
    • +
    • +
      +

      dict

      +
      public final java.util.Map<java.lang.String,​java.lang.Object> dict
      +
      +
    • +
    • +
      +

      payload

      +
      public final Payload payload
      +
      +
    • +
    +
    +
  • + +
  • +
    +

    Constructor Details

    +
      +
    • +
      +

      PDFObject

      +
      public PDFObject​(int id, +int version, +java.util.Map<java.lang.String,​java.lang.Object> dict, +Payload payload)
      +
      +
    • +
    +
    +
  • +
+
+ +
+ +
+
+ + diff --git a/io-vector/build/docs/javadoc/org.xbib.graphics.io.vector/org/xbib/graphics/io/vector/pdf/PDFProcessor.html b/io-vector/build/docs/javadoc/org.xbib.graphics.io.vector/org/xbib/graphics/io/vector/pdf/PDFProcessor.html new file mode 100644 index 0000000..e25b4c2 --- /dev/null +++ b/io-vector/build/docs/javadoc/org.xbib.graphics.io.vector/org/xbib/graphics/io/vector/pdf/PDFProcessor.html @@ -0,0 +1,242 @@ + + + + + +PDFProcessor (io-vector 3.0.0 API) + + + + + + + + + + + + + +
+ +
+
+ +
+ + +

Class PDFProcessor

+
+
java.lang.Object +
org.xbib.graphics.io.vector.pdf.PDFProcessor
+
+
+
+
All Implemented Interfaces:
+
Processor
+
+
+
public class PDFProcessor
+extends java.lang.Object
+implements Processor
+
+
+
    + +
  • +
    +

    Constructor Summary

    +
    + + + + + + + + + + + + + + + + + + +
    Constructors
    ConstructorDescription
    PDFProcessor() 
    PDFProcessor​(boolean compressed) 
    +
    +
    +
  • + +
  • +
    +

    Method Summary

    +
    +
    +
    + + + + + + + + + + + + + + + +
    Modifier and TypeMethodDescription
    ProcessorResultprocess​(java.lang.Iterable<Command<?>> commands, +PageSize pageSize) 
    +
    +
    +
    +

    Methods inherited from class java.lang.Object

    +clone, equals, finalize, getClass, hashCode, notify, notifyAll, toString, wait, wait, wait
    +
    +
  • +
+
+
+
    + +
  • +
    +

    Constructor Details

    +
      +
    • +
      +

      PDFProcessor

      +
      public PDFProcessor()
      +
      +
    • +
    • +
      +

      PDFProcessor

      +
      public PDFProcessor​(boolean compressed)
      +
      +
    • +
    +
    +
  • + +
  • +
    +

    Method Details

    +
      +
    • +
      +

      process

      +
      public ProcessorResult process​(java.lang.Iterable<Command<?>> commands, +PageSize pageSize) + throws java.io.IOException
      +
      +
      Specified by:
      +
      process in interface Processor
      +
      Throws:
      +
      java.io.IOException
      +
      +
      +
    • +
    +
    +
  • +
+
+ +
+ +
+
+ + diff --git a/io-vector/build/docs/javadoc/org.xbib.graphics.io.vector/org/xbib/graphics/io/vector/pdf/PDFProcessorResult.html b/io-vector/build/docs/javadoc/org.xbib.graphics.io.vector/org/xbib/graphics/io/vector/pdf/PDFProcessorResult.html new file mode 100644 index 0000000..d76c884 --- /dev/null +++ b/io-vector/build/docs/javadoc/org.xbib.graphics.io.vector/org/xbib/graphics/io/vector/pdf/PDFProcessorResult.html @@ -0,0 +1,298 @@ + + + + + +PDFProcessorResult (io-vector 3.0.0 API) + + + + + + + + + + + + + +
+ +
+
+ +
+ + +

Class PDFProcessorResult

+
+
java.lang.Object +
org.xbib.graphics.io.vector.pdf.PDFProcessorResult
+
+
+
+
All Implemented Interfaces:
+
ProcessorResult
+
+
+
public class PDFProcessorResult
+extends java.lang.Object
+implements ProcessorResult
+
+
+
    + +
  • +
    +

    Constructor Summary

    +
    + + + + + + + + + + + + + + +
    Constructors
    ConstructorDescription
    PDFProcessorResult​(PageSize pageSize) 
    +
    +
    +
  • + +
  • +
    +

    Method Summary

    +
    +
    +
    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    Modifier and TypeMethodDescription
    voidclose() 
    voidhandle​(Command<?> command) 
    voidsetCompressed​(boolean compressed) 
    static java.lang.StringtoString​(PDFObject obj) 
    voidwrite​(java.io.OutputStream out) 
    +
    +
    +
    +

    Methods inherited from class java.lang.Object

    +clone, equals, finalize, getClass, hashCode, notify, notifyAll, toString, wait, wait, wait
    +
    +
  • +
+
+
+
    + +
  • +
    +

    Constructor Details

    +
      +
    • +
      +

      PDFProcessorResult

      +
      public PDFProcessorResult​(PageSize pageSize) + throws java.io.IOException
      +
      +
      Throws:
      +
      java.io.IOException
      +
      +
      +
    • +
    +
    +
  • + +
  • +
    +

    Method Details

    +
      +
    • +
      +

      setCompressed

      +
      public void setCompressed​(boolean compressed)
      +
      +
    • +
    • +
      +

      toString

      +
      public static java.lang.String toString​(PDFObject obj) + throws java.io.IOException
      +
      +
      Throws:
      +
      java.io.IOException
      +
      +
      +
    • +
    • +
      +

      write

      +
      public void write​(java.io.OutputStream out) + throws java.io.IOException
      +
      +
      Specified by:
      +
      write in interface ProcessorResult
      +
      Throws:
      +
      java.io.IOException
      +
      +
      +
    • +
    • +
      +

      handle

      +
      public void handle​(Command<?> command) + throws java.io.IOException
      +
      +
      Specified by:
      +
      handle in interface ProcessorResult
      +
      Throws:
      +
      java.io.IOException
      +
      +
      +
    • +
    • +
      +

      close

      +
      public void close() + throws java.io.IOException
      +
      +
      Specified by:
      +
      close in interface ProcessorResult
      +
      Throws:
      +
      java.io.IOException
      +
      +
      +
    • +
    +
    +
  • +
+
+ +
+ +
+
+ + diff --git a/io-vector/build/docs/javadoc/org.xbib.graphics.io.vector/org/xbib/graphics/io/vector/pdf/Payload.html b/io-vector/build/docs/javadoc/org.xbib.graphics.io.vector/org/xbib/graphics/io/vector/pdf/Payload.html new file mode 100644 index 0000000..03891bb --- /dev/null +++ b/io-vector/build/docs/javadoc/org.xbib.graphics.io.vector/org/xbib/graphics/io/vector/pdf/Payload.html @@ -0,0 +1,309 @@ + + + + + +Payload (io-vector 3.0.0 API) + + + + + + + + + + + + + +
+ +
+
+ + +
java.lang.Object +
java.io.OutputStream +
org.xbib.graphics.io.vector.pdf.Payload
+
+
+
+
+
All Implemented Interfaces:
+
java.io.Closeable, java.io.Flushable, java.lang.AutoCloseable
+
+
+
Direct Known Subclasses:
+
GeneratedPayload
+
+
+
public class Payload
+extends java.io.OutputStream
+
+
+
    + +
  • +
    +

    Constructor Summary

    +
    + + + + + + + + + + + + + + +
    Constructors
    ConstructorDescription
    Payload​(boolean stream) 
    +
    +
    +
  • + +
  • +
    +

    Method Summary

    +
    +
    +
    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    Modifier and TypeMethodDescription
    voidaddFilter​(java.lang.Class<? extends java.io.FilterOutputStream> filterClass) 
    voidclose() 
    byte[]getBytes() 
    booleanisStream() 
    voidwrite​(int b) 
    +
    +
    +
    +

    Methods inherited from class java.io.OutputStream

    +flush, nullOutputStream, write, write
    +
    +

    Methods inherited from class java.lang.Object

    +clone, equals, finalize, getClass, hashCode, notify, notifyAll, toString, wait, wait, wait
    +
    +
  • +
+
+
+
    + +
  • +
    +

    Constructor Details

    +
      +
    • +
      +

      Payload

      +
      public Payload​(boolean stream)
      +
      +
    • +
    +
    +
  • + +
  • +
    +

    Method Details

    +
      +
    • +
      +

      getBytes

      +
      public byte[] getBytes() + throws java.io.IOException
      +
      +
      Throws:
      +
      java.io.IOException
      +
      +
      +
    • +
    • +
      +

      isStream

      +
      public boolean isStream()
      +
      +
    • +
    • +
      +

      write

      +
      public void write​(int b) + throws java.io.IOException
      +
      +
      Specified by:
      +
      write in class java.io.OutputStream
      +
      Throws:
      +
      java.io.IOException
      +
      +
      +
    • +
    • +
      +

      close

      +
      public void close() + throws java.io.IOException
      +
      +
      Specified by:
      +
      close in interface java.lang.AutoCloseable
      +
      Specified by:
      +
      close in interface java.io.Closeable
      +
      Overrides:
      +
      close in class java.io.OutputStream
      +
      Throws:
      +
      java.io.IOException
      +
      +
      +
    • +
    • +
      +

      addFilter

      +
      public void addFilter​(java.lang.Class<? extends java.io.FilterOutputStream> filterClass) + throws java.lang.NoSuchMethodException, +java.lang.IllegalAccessException, +java.lang.reflect.InvocationTargetException, +java.lang.InstantiationException
      +
      +
      Throws:
      +
      java.lang.NoSuchMethodException
      +
      java.lang.IllegalAccessException
      +
      java.lang.reflect.InvocationTargetException
      +
      java.lang.InstantiationException
      +
      +
      +
    • +
    +
    +
  • +
+
+ +
+ +
+
+ + diff --git a/io-vector/build/docs/javadoc/org.xbib.graphics.io.vector/org/xbib/graphics/io/vector/pdf/Resources.html b/io-vector/build/docs/javadoc/org.xbib.graphics.io.vector/org/xbib/graphics/io/vector/pdf/Resources.html new file mode 100644 index 0000000..82d851d --- /dev/null +++ b/io-vector/build/docs/javadoc/org.xbib.graphics.io.vector/org/xbib/graphics/io/vector/pdf/Resources.html @@ -0,0 +1,253 @@ + + + + + +Resources (io-vector 3.0.0 API) + + + + + + + + + + + + + +
+ +
+
+ +
+ + +

Class Resources

+
+
java.lang.Object +
org.xbib.graphics.io.vector.pdf.PDFObject +
org.xbib.graphics.io.vector.pdf.Resources
+
+
+
+
+
public class Resources
+extends PDFObject
+
+
+
    + +
  • +
    +

    Field Summary

    +
    +

    Fields inherited from class org.xbib.graphics.io.vector.pdf.PDFObject

    +dict, id, payload, version
    +
    +
  • + +
  • +
    +

    Constructor Summary

    +
    + + + + + + + + + + + + + + +
    Constructors
    ConstructorDescription
    Resources​(int id, +int version) 
    +
    +
    +
  • + +
  • +
    +

    Method Summary

    +
    +
    +
    + + + + + + + + + + + + + + + + + + + + + + + + + +
    Modifier and TypeMethodDescription
    java.lang.StringgetId​(java.awt.Font font) 
    java.lang.StringgetId​(java.lang.Double transparency) 
    java.lang.StringgetId​(PDFObject image) 
    +
    +
    +
    +

    Methods inherited from class java.lang.Object

    +clone, equals, finalize, getClass, hashCode, notify, notifyAll, toString, wait, wait, wait
    +
    +
  • +
+
+
+
    + +
  • +
    +

    Constructor Details

    +
      +
    • +
      +

      Resources

      +
      public Resources​(int id, +int version)
      +
      +
    • +
    +
    +
  • + +
  • +
    +

    Method Details

    +
      +
    • +
      +

      getId

      +
      public java.lang.String getId​(java.awt.Font font)
      +
      +
    • +
    • +
      +

      getId

      +
      public java.lang.String getId​(PDFObject image)
      +
      +
    • +
    • +
      +

      getId

      +
      public java.lang.String getId​(java.lang.Double transparency)
      +
      +
    • +
    +
    +
  • +
+
+ +
+ +
+
+ + diff --git a/io-vector/build/docs/javadoc/org.xbib.graphics.io.vector/org/xbib/graphics/io/vector/pdf/SizePayload.html b/io-vector/build/docs/javadoc/org.xbib.graphics.io.vector/org/xbib/graphics/io/vector/pdf/SizePayload.html new file mode 100644 index 0000000..3515bc4 --- /dev/null +++ b/io-vector/build/docs/javadoc/org.xbib.graphics.io.vector/org/xbib/graphics/io/vector/pdf/SizePayload.html @@ -0,0 +1,248 @@ + + + + + +SizePayload (io-vector 3.0.0 API) + + + + + + + + + + + + + +
+ +
+
+ +
+ + +

Class SizePayload

+
+
java.lang.Object +
java.io.OutputStream + +
+
+
+
+
All Implemented Interfaces:
+
java.io.Closeable, java.io.Flushable, java.lang.AutoCloseable
+
+
+
public class SizePayload
+extends GeneratedPayload
+
+
+
    + +
  • +
    +

    Constructor Summary

    +
    + + + + + + + + + + + + + + +
    Constructors
    ConstructorDescription
    SizePayload​(PDFObject object, +java.lang.String charset, +boolean stream) 
    +
    +
    +
  • + +
  • +
    +

    Method Summary

    +
    +
    +
    + + + + + + + + + + + + + + + +
    Modifier and TypeMethodDescription
    protected byte[]generatePayload() 
    +
    +
    +
    +

    Methods inherited from class org.xbib.graphics.io.vector.pdf.GeneratedPayload

    +getBytes, write
    +
    +

    Methods inherited from class org.xbib.graphics.io.vector.pdf.Payload

    +addFilter, close, isStream
    +
    +

    Methods inherited from class java.io.OutputStream

    +flush, nullOutputStream, write, write
    +
    +

    Methods inherited from class java.lang.Object

    +clone, equals, finalize, getClass, hashCode, notify, notifyAll, toString, wait, wait, wait
    +
    +
  • +
+
+
+
    + +
  • +
    +

    Constructor Details

    +
      +
    • +
      +

      SizePayload

      +
      public SizePayload​(PDFObject object, +java.lang.String charset, +boolean stream)
      +
      +
    • +
    +
    +
  • + +
  • +
    +

    Method Details

    +
      +
    • +
      +

      generatePayload

      +
      protected byte[] generatePayload() + throws java.io.IOException
      +
      +
      Specified by:
      +
      generatePayload in class GeneratedPayload
      +
      Throws:
      +
      java.io.IOException
      +
      +
      +
    • +
    +
    +
  • +
+
+ +
+ +
+
+ + diff --git a/io-vector/build/docs/javadoc/org.xbib.graphics.io.vector/org/xbib/graphics/io/vector/pdf/package-summary.html b/io-vector/build/docs/javadoc/org.xbib.graphics.io.vector/org/xbib/graphics/io/vector/pdf/package-summary.html new file mode 100644 index 0000000..65c0bb6 --- /dev/null +++ b/io-vector/build/docs/javadoc/org.xbib.graphics.io.vector/org/xbib/graphics/io/vector/pdf/package-summary.html @@ -0,0 +1,134 @@ + + + + + +org.xbib.graphics.io.vector.pdf (io-vector 3.0.0 API) + + + + + + + + + + + + + +
+ +
+
+
+ +

Package org.xbib.graphics.io.vector.pdf

+
+
+ +
+
+ +
+
+ + diff --git a/io-vector/build/docs/javadoc/org.xbib.graphics.io.vector/org/xbib/graphics/io/vector/pdf/package-tree.html b/io-vector/build/docs/javadoc/org.xbib.graphics.io.vector/org/xbib/graphics/io/vector/pdf/package-tree.html new file mode 100644 index 0000000..5271000 --- /dev/null +++ b/io-vector/build/docs/javadoc/org.xbib.graphics.io.vector/org/xbib/graphics/io/vector/pdf/package-tree.html @@ -0,0 +1,125 @@ + + + + + +org.xbib.graphics.io.vector.pdf Class Hierarchy (io-vector 3.0.0 API) + + + + + + + + + + + + + +
+ +
+
+
+

Hierarchy For Package org.xbib.graphics.io.vector.pdf

+Package Hierarchies: + +
+
+

Class Hierarchy

+
    +
  • java.lang.Object +
      +
    • java.awt.Graphics +
        +
      • java.awt.Graphics2D + +
      • +
      +
    • +
    • java.io.OutputStream (implements java.io.Closeable, java.io.Flushable) + +
    • +
    • org.xbib.graphics.io.vector.pdf.PDFObject +
        +
      • org.xbib.graphics.io.vector.pdf.Resources
      • +
      +
    • +
    • org.xbib.graphics.io.vector.pdf.PDFProcessor (implements org.xbib.graphics.io.vector.Processor)
    • +
    • org.xbib.graphics.io.vector.pdf.PDFProcessorResult (implements org.xbib.graphics.io.vector.ProcessorResult)
    • +
    +
  • +
+
+
+ +
+
+ + diff --git a/io-vector/build/docs/javadoc/org.xbib.graphics.io.vector/org/xbib/graphics/io/vector/svg/SVGGraphics2D.html b/io-vector/build/docs/javadoc/org.xbib.graphics.io.vector/org/xbib/graphics/io/vector/svg/SVGGraphics2D.html new file mode 100644 index 0000000..4cf5b6d --- /dev/null +++ b/io-vector/build/docs/javadoc/org.xbib.graphics.io.vector/org/xbib/graphics/io/vector/svg/SVGGraphics2D.html @@ -0,0 +1,217 @@ + + + + + +SVGGraphics2D (io-vector 3.0.0 API) + + + + + + + + + + + + + +
+ +
+
+ +
+ + +

Class SVGGraphics2D

+
+
java.lang.Object +
java.awt.Graphics +
java.awt.Graphics2D +
org.xbib.graphics.io.vector.VectorGraphics2D +
org.xbib.graphics.io.vector.svg.SVGGraphics2D
+
+
+
+
+
+
+
All Implemented Interfaces:
+
java.lang.Cloneable
+
+
+
public class SVGGraphics2D
+extends VectorGraphics2D
+
Graphics2D implementation that saves all operations to a string + in the Scaled Vector Graphics (SVG) format.
+
+
+ +
+
+
    + +
  • +
    +

    Constructor Details

    +
      +
    • +
      +

      SVGGraphics2D

      +
      public SVGGraphics2D​(double x, +double y, +double width, +double height)
      +
      Initializes a new VectorGraphics2D pipeline for translating Graphics2D + commands to SVG data. The document dimensions must be specified as + parameters.
      +
      +
      Parameters:
      +
      x - Left offset.
      +
      y - Top offset
      +
      width - Width.
      +
      height - Height.
      +
      +
      +
    • +
    +
    +
  • +
+
+ +
+ +
+
+ + diff --git a/io-vector/build/docs/javadoc/org.xbib.graphics.io.vector/org/xbib/graphics/io/vector/svg/SVGProcessor.html b/io-vector/build/docs/javadoc/org.xbib.graphics.io.vector/org/xbib/graphics/io/vector/svg/SVGProcessor.html new file mode 100644 index 0000000..9b211b0 --- /dev/null +++ b/io-vector/build/docs/javadoc/org.xbib.graphics.io.vector/org/xbib/graphics/io/vector/svg/SVGProcessor.html @@ -0,0 +1,229 @@ + + + + + +SVGProcessor (io-vector 3.0.0 API) + + + + + + + + + + + + + +
+ +
+
+ +
+ + +

Class SVGProcessor

+
+
java.lang.Object +
org.xbib.graphics.io.vector.svg.SVGProcessor
+
+
+
+
All Implemented Interfaces:
+
Processor
+
+
+
public class SVGProcessor
+extends java.lang.Object
+implements Processor
+
+
+
    + +
  • +
    +

    Constructor Summary

    +
    + + + + + + + + + + + + + + +
    Constructors
    ConstructorDescription
    SVGProcessor() 
    +
    +
    +
  • + +
  • +
    +

    Method Summary

    +
    +
    +
    + + + + + + + + + + + + + + + +
    Modifier and TypeMethodDescription
    ProcessorResultprocess​(java.lang.Iterable<Command<?>> commands, +PageSize pageSize) 
    +
    +
    +
    +

    Methods inherited from class java.lang.Object

    +clone, equals, finalize, getClass, hashCode, notify, notifyAll, toString, wait, wait, wait
    +
    +
  • +
+
+
+
    + +
  • +
    +

    Constructor Details

    +
      +
    • +
      +

      SVGProcessor

      +
      public SVGProcessor()
      +
      +
    • +
    +
    +
  • + +
  • +
    +

    Method Details

    + +
    +
  • +
+
+ +
+ +
+
+ + diff --git a/io-vector/build/docs/javadoc/org.xbib.graphics.io.vector/org/xbib/graphics/io/vector/svg/SVGProcessorResult.html b/io-vector/build/docs/javadoc/org.xbib.graphics.io.vector/org/xbib/graphics/io/vector/svg/SVGProcessorResult.html new file mode 100644 index 0000000..6c766cd --- /dev/null +++ b/io-vector/build/docs/javadoc/org.xbib.graphics.io.vector/org/xbib/graphics/io/vector/svg/SVGProcessorResult.html @@ -0,0 +1,275 @@ + + + + + +SVGProcessorResult (io-vector 3.0.0 API) + + + + + + + + + + + + + +
+ +
+
+ +
+ + +

Class SVGProcessorResult

+
+
java.lang.Object +
org.xbib.graphics.io.vector.svg.SVGProcessorResult
+
+
+
+
All Implemented Interfaces:
+
ProcessorResult
+
+
+
public class SVGProcessorResult
+extends java.lang.Object
+implements ProcessorResult
+
+
+
    + +
  • +
    +

    Constructor Summary

    +
    + + + + + + + + + + + + + + +
    Constructors
    ConstructorDescription
    SVGProcessorResult​(PageSize pageSize) 
    +
    +
    +
  • + +
  • +
    +

    Method Summary

    +
    +
    +
    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    Modifier and TypeMethodDescription
    voidclose() 
    voidhandle​(Command<?> command) 
    java.lang.StringtoString() 
    voidwrite​(java.io.OutputStream out) 
    +
    +
    +
    +

    Methods inherited from class java.lang.Object

    +clone, equals, finalize, getClass, hashCode, notify, notifyAll, wait, wait, wait
    +
    +
  • +
+
+
+
    + +
  • +
    +

    Constructor Details

    +
      +
    • +
      +

      SVGProcessorResult

      +
      public SVGProcessorResult​(PageSize pageSize)
      +
      +
    • +
    +
    +
  • + +
  • +
    +

    Method Details

    +
      +
    • +
      +

      write

      +
      public void write​(java.io.OutputStream out) + throws java.io.IOException
      +
      +
      Specified by:
      +
      write in interface ProcessorResult
      +
      Throws:
      +
      java.io.IOException
      +
      +
      +
    • +
    • +
      +

      close

      +
      public void close()
      +
      +
      Specified by:
      +
      close in interface ProcessorResult
      +
      +
      +
    • +
    • +
      +

      toString

      +
      public java.lang.String toString()
      +
      +
      Overrides:
      +
      toString in class java.lang.Object
      +
      +
      +
    • +
    • +
      +

      handle

      +
      public void handle​(Command<?> command)
      +
      +
      Specified by:
      +
      handle in interface ProcessorResult
      +
      +
      +
    • +
    +
    +
  • +
+
+ +
+ +
+
+ + diff --git a/io-vector/build/docs/javadoc/org.xbib.graphics.io.vector/org/xbib/graphics/io/vector/svg/package-summary.html b/io-vector/build/docs/javadoc/org.xbib.graphics.io.vector/org/xbib/graphics/io/vector/svg/package-summary.html new file mode 100644 index 0000000..8c70de0 --- /dev/null +++ b/io-vector/build/docs/javadoc/org.xbib.graphics.io.vector/org/xbib/graphics/io/vector/svg/package-summary.html @@ -0,0 +1,114 @@ + + + + + +org.xbib.graphics.io.vector.svg (io-vector 3.0.0 API) + + + + + + + + + + + + + +
+ +
+
+
+ +

Package org.xbib.graphics.io.vector.svg

+
+
+
    +
  • +
    + + + + + + + + + + + + + + + + + + + + + + +
    Class Summary
    ClassDescription
    SVGGraphics2D +
    Graphics2D implementation that saves all operations to a string + in the Scaled Vector Graphics (SVG) format.
    +
    SVGProcessor 
    SVGProcessorResult 
    +
    +
  • +
+
+
+ +
+
+ + diff --git a/io-vector/build/docs/javadoc/org.xbib.graphics.io.vector/org/xbib/graphics/io/vector/svg/package-tree.html b/io-vector/build/docs/javadoc/org.xbib.graphics.io.vector/org/xbib/graphics/io/vector/svg/package-tree.html new file mode 100644 index 0000000..7a46cbb --- /dev/null +++ b/io-vector/build/docs/javadoc/org.xbib.graphics.io.vector/org/xbib/graphics/io/vector/svg/package-tree.html @@ -0,0 +1,107 @@ + + + + + +org.xbib.graphics.io.vector.svg Class Hierarchy (io-vector 3.0.0 API) + + + + + + + + + + + + + +
+ +
+
+
+

Hierarchy For Package org.xbib.graphics.io.vector.svg

+Package Hierarchies: + +
+
+

Class Hierarchy

+
    +
  • java.lang.Object + +
  • +
+
+
+ +
+
+ + diff --git a/io-vector/build/docs/javadoc/org.xbib.graphics.io.vector/org/xbib/graphics/io/vector/util/ASCII85EncodeStream.html b/io-vector/build/docs/javadoc/org.xbib.graphics.io.vector/org/xbib/graphics/io/vector/util/ASCII85EncodeStream.html new file mode 100644 index 0000000..5f84e4f --- /dev/null +++ b/io-vector/build/docs/javadoc/org.xbib.graphics.io.vector/org/xbib/graphics/io/vector/util/ASCII85EncodeStream.html @@ -0,0 +1,284 @@ + + + + + +ASCII85EncodeStream (io-vector 3.0.0 API) + + + + + + + + + + + + + +
+ +
+
+ +
+ + +

Class ASCII85EncodeStream

+
+
java.lang.Object +
java.io.OutputStream +
java.io.FilterOutputStream +
org.xbib.graphics.io.vector.util.ASCII85EncodeStream
+
+
+
+
+
+
All Implemented Interfaces:
+
java.io.Closeable, java.io.Flushable, java.lang.AutoCloseable
+
+
+
public class ASCII85EncodeStream
+extends java.io.FilterOutputStream
+
+
+
    + +
  • +
    +

    Field Summary

    +
    +

    Fields inherited from class java.io.FilterOutputStream

    +out
    +
    +
  • + +
  • +
    +

    Constructor Summary

    +
    + + + + + + + + + + + + + + + + + + +
    Constructors
    ConstructorDescription
    ASCII85EncodeStream​(java.io.OutputStream out) 
    ASCII85EncodeStream​(java.io.OutputStream out, +java.lang.String prefix, +java.lang.String suffix) 
    +
    +
    +
  • + +
  • +
    +

    Method Summary

    +
    +
    +
    + + + + + + + + + + + + + + + + + + + + +
    Modifier and TypeMethodDescription
    voidclose() 
    voidwrite​(int b) 
    +
    +
    +
    +

    Methods inherited from class java.io.FilterOutputStream

    +flush, write, write
    +
    +

    Methods inherited from class java.io.OutputStream

    +nullOutputStream
    +
    +

    Methods inherited from class java.lang.Object

    +clone, equals, finalize, getClass, hashCode, notify, notifyAll, toString, wait, wait, wait
    +
    +
  • +
+
+
+
    + +
  • +
    +

    Constructor Details

    +
      +
    • +
      +

      ASCII85EncodeStream

      +
      public ASCII85EncodeStream​(java.io.OutputStream out, +java.lang.String prefix, +java.lang.String suffix)
      +
      +
    • +
    • +
      +

      ASCII85EncodeStream

      +
      public ASCII85EncodeStream​(java.io.OutputStream out)
      +
      +
    • +
    +
    +
  • + +
  • +
    +

    Method Details

    +
      +
    • +
      +

      write

      +
      public void write​(int b) + throws java.io.IOException
      +
      +
      Overrides:
      +
      write in class java.io.FilterOutputStream
      +
      Throws:
      +
      java.io.IOException
      +
      +
      +
    • +
    • +
      +

      close

      +
      public void close() + throws java.io.IOException
      +
      +
      Specified by:
      +
      close in interface java.lang.AutoCloseable
      +
      Specified by:
      +
      close in interface java.io.Closeable
      +
      Overrides:
      +
      close in class java.io.FilterOutputStream
      +
      Throws:
      +
      java.io.IOException
      +
      +
      +
    • +
    +
    +
  • +
+
+ +
+ +
+
+ + diff --git a/io-vector/build/docs/javadoc/org.xbib.graphics.io.vector/org/xbib/graphics/io/vector/util/AlphaToMaskOp.html b/io-vector/build/docs/javadoc/org.xbib.graphics.io.vector/org/xbib/graphics/io/vector/util/AlphaToMaskOp.html new file mode 100644 index 0000000..30c6c27 --- /dev/null +++ b/io-vector/build/docs/javadoc/org.xbib.graphics.io.vector/org/xbib/graphics/io/vector/util/AlphaToMaskOp.html @@ -0,0 +1,314 @@ + + + + + +AlphaToMaskOp (io-vector 3.0.0 API) + + + + + + + + + + + + + +
+ +
+
+ +
+ + +

Class AlphaToMaskOp

+
+
java.lang.Object +
org.xbib.graphics.io.vector.util.AlphaToMaskOp
+
+
+
+
All Implemented Interfaces:
+
java.awt.image.BufferedImageOp
+
+
+
public class AlphaToMaskOp
+extends java.lang.Object
+implements java.awt.image.BufferedImageOp
+
+
+
    + +
  • +
    +

    Constructor Summary

    +
    + + + + + + + + + + + + + + + + + + +
    Constructors
    ConstructorDescription
    AlphaToMaskOp() 
    AlphaToMaskOp​(boolean inverted) 
    +
    +
    +
  • + +
  • +
    +

    Method Summary

    +
    +
    +
    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    Modifier and TypeMethodDescription
    java.awt.image.BufferedImagecreateCompatibleDestImage​(java.awt.image.BufferedImage src, +java.awt.image.ColorModel destCM) 
    java.awt.image.BufferedImagefilter​(java.awt.image.BufferedImage src, +java.awt.image.BufferedImage dest) 
    java.awt.geom.Rectangle2DgetBounds2D​(java.awt.image.BufferedImage src) 
    java.awt.geom.Point2DgetPoint2D​(java.awt.geom.Point2D srcPt, +java.awt.geom.Point2D dstPt) 
    java.awt.RenderingHintsgetRenderingHints() 
    booleanisInverted() 
    +
    +
    +
    +

    Methods inherited from class java.lang.Object

    +clone, equals, finalize, getClass, hashCode, notify, notifyAll, toString, wait, wait, wait
    +
    +
  • +
+
+
+
    + +
  • +
    +

    Constructor Details

    +
      +
    • +
      +

      AlphaToMaskOp

      +
      public AlphaToMaskOp​(boolean inverted)
      +
      +
    • +
    • +
      +

      AlphaToMaskOp

      +
      public AlphaToMaskOp()
      +
      +
    • +
    +
    +
  • + +
  • +
    +

    Method Details

    +
      +
    • +
      +

      isInverted

      +
      public boolean isInverted()
      +
      +
    • +
    • +
      +

      filter

      +
      public java.awt.image.BufferedImage filter​(java.awt.image.BufferedImage src, +java.awt.image.BufferedImage dest)
      +
      +
      Specified by:
      +
      filter in interface java.awt.image.BufferedImageOp
      +
      +
      +
    • +
    • +
      +

      getBounds2D

      +
      public java.awt.geom.Rectangle2D getBounds2D​(java.awt.image.BufferedImage src)
      +
      +
      Specified by:
      +
      getBounds2D in interface java.awt.image.BufferedImageOp
      +
      +
      +
    • +
    • +
      +

      createCompatibleDestImage

      +
      public java.awt.image.BufferedImage createCompatibleDestImage​(java.awt.image.BufferedImage src, +java.awt.image.ColorModel destCM)
      +
      +
      Specified by:
      +
      createCompatibleDestImage in interface java.awt.image.BufferedImageOp
      +
      +
      +
    • +
    • +
      +

      getPoint2D

      +
      public java.awt.geom.Point2D getPoint2D​(java.awt.geom.Point2D srcPt, +java.awt.geom.Point2D dstPt)
      +
      +
      Specified by:
      +
      getPoint2D in interface java.awt.image.BufferedImageOp
      +
      +
      +
    • +
    • +
      +

      getRenderingHints

      +
      public java.awt.RenderingHints getRenderingHints()
      +
      +
      Specified by:
      +
      getRenderingHints in interface java.awt.image.BufferedImageOp
      +
      +
      +
    • +
    +
    +
  • +
+
+ +
+ +
+
+ + diff --git a/io-vector/build/docs/javadoc/org.xbib.graphics.io.vector/org/xbib/graphics/io/vector/util/Base64EncodeStream.html b/io-vector/build/docs/javadoc/org.xbib.graphics.io.vector/org/xbib/graphics/io/vector/util/Base64EncodeStream.html new file mode 100644 index 0000000..3888609 --- /dev/null +++ b/io-vector/build/docs/javadoc/org.xbib.graphics.io.vector/org/xbib/graphics/io/vector/util/Base64EncodeStream.html @@ -0,0 +1,270 @@ + + + + + +Base64EncodeStream (io-vector 3.0.0 API) + + + + + + + + + + + + + +
+ +
+
+ +
+ + +

Class Base64EncodeStream

+
+
java.lang.Object +
java.io.OutputStream +
java.io.FilterOutputStream +
org.xbib.graphics.io.vector.util.Base64EncodeStream
+
+
+
+
+
+
All Implemented Interfaces:
+
java.io.Closeable, java.io.Flushable, java.lang.AutoCloseable
+
+
+
public class Base64EncodeStream
+extends java.io.FilterOutputStream
+
+
+
    + +
  • +
    +

    Field Summary

    +
    +

    Fields inherited from class java.io.FilterOutputStream

    +out
    +
    +
  • + +
  • +
    +

    Constructor Summary

    +
    + + + + + + + + + + + + + + +
    Constructors
    ConstructorDescription
    Base64EncodeStream​(java.io.OutputStream out) 
    +
    +
    +
  • + +
  • +
    +

    Method Summary

    +
    +
    +
    + + + + + + + + + + + + + + + + + + + + +
    Modifier and TypeMethodDescription
    voidclose() 
    voidwrite​(int b) 
    +
    +
    +
    +

    Methods inherited from class java.io.FilterOutputStream

    +flush, write, write
    +
    +

    Methods inherited from class java.io.OutputStream

    +nullOutputStream
    +
    +

    Methods inherited from class java.lang.Object

    +clone, equals, finalize, getClass, hashCode, notify, notifyAll, toString, wait, wait, wait
    +
    +
  • +
+
+
+
    + +
  • +
    +

    Constructor Details

    +
      +
    • +
      +

      Base64EncodeStream

      +
      public Base64EncodeStream​(java.io.OutputStream out)
      +
      +
    • +
    +
    +
  • + +
  • +
    +

    Method Details

    +
      +
    • +
      +

      write

      +
      public void write​(int b) + throws java.io.IOException
      +
      +
      Overrides:
      +
      write in class java.io.FilterOutputStream
      +
      Throws:
      +
      java.io.IOException
      +
      +
      +
    • +
    • +
      +

      close

      +
      public void close() + throws java.io.IOException
      +
      +
      Specified by:
      +
      close in interface java.lang.AutoCloseable
      +
      Specified by:
      +
      close in interface java.io.Closeable
      +
      Overrides:
      +
      close in class java.io.FilterOutputStream
      +
      Throws:
      +
      java.io.IOException
      +
      +
      +
    • +
    +
    +
  • +
+
+ +
+ +
+
+ + diff --git a/io-vector/build/docs/javadoc/org.xbib.graphics.io.vector/org/xbib/graphics/io/vector/util/DataUtils.html b/io-vector/build/docs/javadoc/org.xbib.graphics.io.vector/org/xbib/graphics/io/vector/util/DataUtils.html new file mode 100644 index 0000000..d1dc714 --- /dev/null +++ b/io-vector/build/docs/javadoc/org.xbib.graphics.io.vector/org/xbib/graphics/io/vector/util/DataUtils.html @@ -0,0 +1,497 @@ + + + + + +DataUtils (io-vector 3.0.0 API) + + + + + + + + + + + + + +
+ +
+
+ +
+ + +

Class DataUtils

+
+
java.lang.Object +
org.xbib.graphics.io.vector.util.DataUtils
+
+
+
+
public abstract class DataUtils
+extends java.lang.Object
+
Abstract class that contains utility functions for working with data + collections like maps or lists.
+
+
+
    + +
  • +
    +

    Constructor Summary

    +
    + + + + + + + + + + + + + + + + +
    Constructors
    ModifierConstructorDescription
    protected DataUtils() +
    Default constructor that prevents creation of class.
    +
    +
    +
    +
  • + +
  • +
    +

    Method Summary

    +
    +
    +
    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    Modifier and TypeMethodDescription
    static java.util.List<java.lang.Double>asList​(double[] elements) +
    Converts an array of double numbers to a list of Doubles.
    +
    static java.util.List<java.lang.Float>asList​(float[] elements) +
    Converts an array of float numbers to a list of Floats.
    +
    static java.lang.Stringformat​(java.lang.Number number) +
    Returns a formatted string of the specified number.
    +
    static java.lang.Stringformat​(java.lang.Object obj) +
    Returns a formatted string of the specified object.
    +
    static java.lang.Stringjoin​(java.lang.String separator, +double[] elements) +
    Returns a string containing all double numbers concatenated by a + specified separator.
    +
    static java.lang.Stringjoin​(java.lang.String separator, +float[] elements) +
    Returns a string containing all float numbers concatenated by a + specified separator.
    +
    static java.lang.Stringjoin​(java.lang.String separator, +java.lang.Object[] elements) +
    Returns a string containing all elements concatenated by a specified + separator.
    +
    static java.lang.Stringjoin​(java.lang.String separator, +java.util.List<?> elements) +
    Returns a string containing all elements concatenated by a specified + separator.
    +
    static <K,​ +V> java.util.Map<K,​V>map​(K[] keys, +V[] values) +
    Creates a mapping from two arrays, one with keys, one with values.
    +
    static intmax​(int... values) +
    Returns the largest of all specified values.
    +
    static java.lang.StringstripTrailing​(java.lang.String s, +java.lang.String substr) +
    Removes the specified trailing pattern from a string.
    +
    static voidtransfer​(java.io.InputStream in, +java.io.OutputStream out, +int bufferSize) +
    Copies data from an input stream to an output stream using a buffer of + specified size.
    +
    +
    +
    +
    +

    Methods inherited from class java.lang.Object

    +clone, equals, finalize, getClass, hashCode, notify, notifyAll, toString, wait, wait, wait
    +
    +
  • +
+
+
+
    + +
  • +
    +

    Constructor Details

    +
      +
    • +
      +

      DataUtils

      +
      protected DataUtils()
      +
      Default constructor that prevents creation of class.
      +
      +
    • +
    +
    +
  • + +
  • +
    +

    Method Details

    +
      +
    • +
      +

      map

      +
      public static <K,​ +V> java.util.Map<K,​V> map​(K[] keys, +V[] values)
      +
      Creates a mapping from two arrays, one with keys, one with values.
      +
      +
      Type Parameters:
      +
      K - Data type of the keys.
      +
      V - Data type of the values.
      +
      Parameters:
      +
      keys - Array containing the keys.
      +
      values - Array containing the values.
      +
      Returns:
      +
      Map with keys and values from the specified arrays.
      +
      +
      +
    • +
    • +
      +

      join

      +
      public static java.lang.String join​(java.lang.String separator, +java.util.List<?> elements)
      +
      Returns a string containing all elements concatenated by a specified + separator.
      +
      +
      Parameters:
      +
      separator - Separator string.
      +
      elements - List of elements that should be concatenated.
      +
      Returns:
      +
      a concatenated string.
      +
      +
      +
    • +
    • +
      +

      join

      +
      public static java.lang.String join​(java.lang.String separator, +java.lang.Object[] elements)
      +
      Returns a string containing all elements concatenated by a specified + separator.
      +
      +
      Parameters:
      +
      separator - Separator string.
      +
      elements - Array of elements that should be concatenated.
      +
      Returns:
      +
      a concatenated string.
      +
      +
      +
    • +
    • +
      +

      join

      +
      public static java.lang.String join​(java.lang.String separator, +double[] elements)
      +
      Returns a string containing all double numbers concatenated by a + specified separator.
      +
      +
      Parameters:
      +
      separator - Separator string.
      +
      elements - Array of double numbers that should be concatenated.
      +
      Returns:
      +
      a concatenated string.
      +
      +
      +
    • +
    • +
      +

      join

      +
      public static java.lang.String join​(java.lang.String separator, +float[] elements)
      +
      Returns a string containing all float numbers concatenated by a + specified separator.
      +
      +
      Parameters:
      +
      separator - Separator string.
      +
      elements - Array of float numbers that should be concatenated.
      +
      Returns:
      +
      a concatenated string.
      +
      +
      +
    • +
    • +
      +

      max

      +
      public static int max​(int... values)
      +
      Returns the largest of all specified values.
      +
      +
      Parameters:
      +
      values - Several integer values.
      +
      Returns:
      +
      largest value.
      +
      +
      +
    • +
    • +
      +

      transfer

      +
      public static void transfer​(java.io.InputStream in, +java.io.OutputStream out, +int bufferSize) + throws java.io.IOException
      +
      Copies data from an input stream to an output stream using a buffer of + specified size.
      +
      +
      Parameters:
      +
      in - Input stream.
      +
      out - Output stream.
      +
      bufferSize - Size of the copy buffer.
      +
      Throws:
      +
      java.io.IOException - when an error occurs while copying.
      +
      +
      +
    • +
    • +
      +

      format

      +
      public static java.lang.String format​(java.lang.Number number)
      +
      Returns a formatted string of the specified number. All trailing zeroes + or decimal points will be stripped.
      +
      +
      Parameters:
      +
      number - Number to convert to a string.
      +
      Returns:
      +
      A formatted string.
      +
      +
      +
    • +
    • +
      +

      format

      +
      public static java.lang.String format​(java.lang.Object obj)
      +
      Returns a formatted string of the specified object.
      +
      +
      Parameters:
      +
      obj - Object to convert to a string.
      +
      Returns:
      +
      A formatted string.
      +
      +
      +
    • +
    • +
      +

      asList

      +
      public static java.util.List<java.lang.Float> asList​(float[] elements)
      +
      Converts an array of float numbers to a list of Floats. + The list will be empty if the array is empty or null.
      +
      +
      Parameters:
      +
      elements - Array of float numbers.
      +
      Returns:
      +
      A list with all numbers as Float.
      +
      +
      +
    • +
    • +
      +

      asList

      +
      public static java.util.List<java.lang.Double> asList​(double[] elements)
      +
      Converts an array of double numbers to a list of Doubles. + The list will be empty if the array is empty or null.
      +
      +
      Parameters:
      +
      elements - Array of double numbers.
      +
      Returns:
      +
      A list with all numbers as Double.
      +
      +
      +
    • +
    • +
      +

      stripTrailing

      +
      public static java.lang.String stripTrailing​(java.lang.String s, +java.lang.String substr)
      +
      Removes the specified trailing pattern from a string.
      +
      +
      Parameters:
      +
      s - string.
      +
      substr - trailing pattern.
      +
      Returns:
      +
      A string without the trailing pattern.
      +
      +
      +
    • +
    +
    +
  • +
+
+ +
+ +
+
+ + diff --git a/io-vector/build/docs/javadoc/org.xbib.graphics.io.vector/org/xbib/graphics/io/vector/util/FlateEncodeStream.html b/io-vector/build/docs/javadoc/org.xbib.graphics.io.vector/org/xbib/graphics/io/vector/util/FlateEncodeStream.html new file mode 100644 index 0000000..57b9668 --- /dev/null +++ b/io-vector/build/docs/javadoc/org.xbib.graphics.io.vector/org/xbib/graphics/io/vector/util/FlateEncodeStream.html @@ -0,0 +1,208 @@ + + + + + +FlateEncodeStream (io-vector 3.0.0 API) + + + + + + + + + + + + + +
+ +
+
+ +
+ + +

Class FlateEncodeStream

+
+
java.lang.Object +
java.io.OutputStream +
java.io.FilterOutputStream +
java.util.zip.DeflaterOutputStream +
org.xbib.graphics.io.vector.util.FlateEncodeStream
+
+
+
+
+
+
+
All Implemented Interfaces:
+
java.io.Closeable, java.io.Flushable, java.lang.AutoCloseable
+
+
+
public class FlateEncodeStream
+extends java.util.zip.DeflaterOutputStream
+
+
+
    + +
  • +
    +

    Field Summary

    +
    +

    Fields inherited from class java.util.zip.DeflaterOutputStream

    +buf, def
    +
    +

    Fields inherited from class java.io.FilterOutputStream

    +out
    +
    +
  • + +
  • +
    +

    Constructor Summary

    +
    + + + + + + + + + + + + + + +
    Constructors
    ConstructorDescription
    FlateEncodeStream​(java.io.OutputStream out) 
    +
    +
    +
  • + +
  • +
    +

    Method Summary

    +
    +

    Methods inherited from class java.util.zip.DeflaterOutputStream

    +close, deflate, finish, flush, write, write
    +
    +

    Methods inherited from class java.io.FilterOutputStream

    +write
    +
    +

    Methods inherited from class java.io.OutputStream

    +nullOutputStream
    +
    +

    Methods inherited from class java.lang.Object

    +clone, equals, finalize, getClass, hashCode, notify, notifyAll, toString, wait, wait, wait
    +
    +
  • +
+
+
+
    + +
  • +
    +

    Constructor Details

    +
      +
    • +
      +

      FlateEncodeStream

      +
      public FlateEncodeStream​(java.io.OutputStream out)
      +
      +
    • +
    +
    +
  • +
+
+ +
+ +
+
+ + diff --git a/io-vector/build/docs/javadoc/org.xbib.graphics.io.vector/org/xbib/graphics/io/vector/util/FormattingWriter.html b/io-vector/build/docs/javadoc/org.xbib.graphics.io.vector/org/xbib/graphics/io/vector/util/FormattingWriter.html new file mode 100644 index 0000000..0c2e018 --- /dev/null +++ b/io-vector/build/docs/javadoc/org.xbib.graphics.io.vector/org/xbib/graphics/io/vector/util/FormattingWriter.html @@ -0,0 +1,363 @@ + + + + + +FormattingWriter (io-vector 3.0.0 API) + + + + + + + + + + + + + +
+ +
+
+ +
+ + +

Class FormattingWriter

+
+
java.lang.Object +
org.xbib.graphics.io.vector.util.FormattingWriter
+
+
+
+
All Implemented Interfaces:
+
java.io.Closeable, java.io.Flushable, java.lang.AutoCloseable
+
+
+
public class FormattingWriter
+extends java.lang.Object
+implements java.io.Closeable, java.io.Flushable
+
+
+
    + +
  • +
    +

    Constructor Summary

    +
    + + + + + + + + + + + + + + +
    Constructors
    ConstructorDescription
    FormattingWriter​(java.io.OutputStream out, +java.lang.String encoding, +java.lang.String eol) 
    +
    +
    +
  • + +
  • +
    +

    Method Summary

    +
    +
    +
    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    Modifier and TypeMethodDescription
    voidclose() 
    voidflush() 
    FormattingWriterformat​(java.lang.String format, +java.lang.Object... args) 
    longtell() 
    FormattingWriterwrite​(java.lang.Number number) 
    FormattingWriterwrite​(java.lang.String string) 
    FormattingWriterwriteln() 
    FormattingWriterwriteln​(java.lang.Number number) 
    FormattingWriterwriteln​(java.lang.String string) 
    +
    +
    +
    +

    Methods inherited from class java.lang.Object

    +clone, equals, finalize, getClass, hashCode, notify, notifyAll, toString, wait, wait, wait
    +
    +
  • +
+
+
+
    + +
  • +
    +

    Constructor Details

    +
      +
    • +
      +

      FormattingWriter

      +
      public FormattingWriter​(java.io.OutputStream out, +java.lang.String encoding, +java.lang.String eol)
      +
      +
    • +
    +
    +
  • + +
  • +
    +

    Method Details

    +
      +
    • +
      +

      write

      +
      public FormattingWriter write​(java.lang.String string) + throws java.io.IOException
      +
      +
      Throws:
      +
      java.io.IOException
      +
      +
      +
    • +
    • +
      +

      write

      +
      public FormattingWriter write​(java.lang.Number number) + throws java.io.IOException
      +
      +
      Throws:
      +
      java.io.IOException
      +
      +
      +
    • +
    • +
      +

      writeln

      +
      public FormattingWriter writeln() + throws java.io.IOException
      +
      +
      Throws:
      +
      java.io.IOException
      +
      +
      +
    • +
    • +
      +

      writeln

      +
      public FormattingWriter writeln​(java.lang.String string) + throws java.io.IOException
      +
      +
      Throws:
      +
      java.io.IOException
      +
      +
      +
    • +
    • +
      +

      writeln

      +
      public FormattingWriter writeln​(java.lang.Number number) + throws java.io.IOException
      +
      +
      Throws:
      +
      java.io.IOException
      +
      +
      +
    • +
    • +
      +

      format

      +
      public FormattingWriter format​(java.lang.String format, +java.lang.Object... args) + throws java.io.IOException
      +
      +
      Throws:
      +
      java.io.IOException
      +
      +
      +
    • +
    • +
      +

      flush

      +
      public void flush() + throws java.io.IOException
      +
      +
      Specified by:
      +
      flush in interface java.io.Flushable
      +
      Throws:
      +
      java.io.IOException
      +
      +
      +
    • +
    • +
      +

      close

      +
      public void close() + throws java.io.IOException
      +
      +
      Specified by:
      +
      close in interface java.lang.AutoCloseable
      +
      Specified by:
      +
      close in interface java.io.Closeable
      +
      Throws:
      +
      java.io.IOException
      +
      +
      +
    • +
    • +
      +

      tell

      +
      public long tell()
      +
      +
    • +
    +
    +
  • +
+
+ +
+ +
+
+ + diff --git a/io-vector/build/docs/javadoc/org.xbib.graphics.io.vector/org/xbib/graphics/io/vector/util/GraphicsUtils.html b/io-vector/build/docs/javadoc/org.xbib.graphics.io.vector/org/xbib/graphics/io/vector/util/GraphicsUtils.html new file mode 100644 index 0000000..935430b --- /dev/null +++ b/io-vector/build/docs/javadoc/org.xbib.graphics.io.vector/org/xbib/graphics/io/vector/util/GraphicsUtils.html @@ -0,0 +1,375 @@ + + + + + +GraphicsUtils (io-vector 3.0.0 API) + + + + + + + + + + + + + +
+ +
+
+ +
+ + +

Class GraphicsUtils

+
+
java.lang.Object +
org.xbib.graphics.io.vector.util.GraphicsUtils
+
+
+
+
public abstract class GraphicsUtils
+extends java.lang.Object
+
Abstract class that contains utility functions for working with graphics. + For example, this includes font handling.
+
+
+
    + +
  • +
    +

    Constructor Summary

    +
    + + + + + + + + + + + + + + + + +
    Constructors
    ModifierConstructorDescription
    protected GraphicsUtils() +
    Default constructor that prevents creation of class.
    +
    +
    +
    +
  • + +
  • +
    +

    Method Summary

    +
    +
    +
    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    Modifier and TypeMethodDescription
    static java.awt.Shapeclone​(java.awt.Shape shape) 
    static booleanequals​(java.awt.Shape shapeA, +java.awt.Shape shapeB) 
    static java.awt.image.BufferedImagegetAlphaImage​(java.awt.image.BufferedImage image) 
    static java.awt.FontgetPhysicalFont​(java.awt.Font logicalFont) 
    static java.awt.FontgetPhysicalFont​(java.awt.Font logicalFont, +java.lang.String testText) +
    Try to guess physical font from the properties of a logical font, like + "Dialog", "Serif", "Monospaced" etc.
    +
    static booleanhasAlpha​(java.awt.Image image) +
    This method returns true if the specified image has the + possibility to store transparent pixels.
    +
    static java.awt.image.BufferedImagetoBufferedImage​(java.awt.Image image) +
    This method returns a buffered image with the contents of an image.
    +
    static java.awt.image.BufferedImagetoBufferedImage​(java.awt.image.RenderedImage image) +
    Converts an arbitrary image to a BufferedImage.
    +
    static booleanusesAlpha​(java.awt.Image image) +
    This method returns true if the specified image has at least one + pixel that is not fully opaque.
    +
    +
    +
    +
    +

    Methods inherited from class java.lang.Object

    +clone, equals, finalize, getClass, hashCode, notify, notifyAll, toString, wait, wait, wait
    +
    +
  • +
+
+
+
    + +
  • +
    +

    Constructor Details

    +
      +
    • +
      +

      GraphicsUtils

      +
      protected GraphicsUtils()
      +
      Default constructor that prevents creation of class.
      +
      +
    • +
    +
    +
  • + +
  • +
    +

    Method Details

    +
      +
    • +
      +

      hasAlpha

      +
      public static boolean hasAlpha​(java.awt.Image image)
      +
      This method returns true if the specified image has the + possibility to store transparent pixels. + Inspired by http://www.exampledepot.com/egs/java.awt.image/HasAlpha.html
      +
      +
      Parameters:
      +
      image - Image that should be checked for alpha channel.
      +
      Returns:
      +
      true if the specified image can have transparent pixels, + false otherwise
      +
      +
      +
    • +
    • +
      +

      usesAlpha

      +
      public static boolean usesAlpha​(java.awt.Image image)
      +
      This method returns true if the specified image has at least one + pixel that is not fully opaque.
      +
      +
      Parameters:
      +
      image - Image that should be checked for non-opaque pixels.
      +
      Returns:
      +
      true if the specified image has transparent pixels, + false otherwise
      +
      +
      +
    • +
    • +
      +

      toBufferedImage

      +
      public static java.awt.image.BufferedImage toBufferedImage​(java.awt.image.RenderedImage image)
      +
      Converts an arbitrary image to a BufferedImage.
      +
      +
      Parameters:
      +
      image - Image that should be converted.
      +
      Returns:
      +
      a buffered image containing the image pixels, or the original + instance if the image already was of type BufferedImage.
      +
      +
      +
    • +
    • +
      +

      toBufferedImage

      +
      public static java.awt.image.BufferedImage toBufferedImage​(java.awt.Image image)
      +
      This method returns a buffered image with the contents of an image. + Taken from http://www.exampledepot.com/egs/java.awt.image/Image2Buf.html
      +
      +
      Parameters:
      +
      image - Image to be converted
      +
      Returns:
      +
      a buffered image with the contents of the specified image
      +
      +
      +
    • +
    • +
      +

      clone

      +
      public static java.awt.Shape clone​(java.awt.Shape shape)
      +
      +
    • +
    • +
      +

      getPhysicalFont

      +
      public static java.awt.Font getPhysicalFont​(java.awt.Font logicalFont, +java.lang.String testText)
      +
      Try to guess physical font from the properties of a logical font, like + "Dialog", "Serif", "Monospaced" etc.
      +
      +
      Parameters:
      +
      logicalFont - Logical font object.
      +
      testText - Text used to determine font properties.
      +
      Returns:
      +
      An object of the first matching physical font. The original font + object is returned if it was a physical font or no font matched.
      +
      +
      +
    • +
    • +
      +

      getPhysicalFont

      +
      public static java.awt.Font getPhysicalFont​(java.awt.Font logicalFont)
      +
      +
    • +
    • +
      +

      getAlphaImage

      +
      public static java.awt.image.BufferedImage getAlphaImage​(java.awt.image.BufferedImage image)
      +
      +
    • +
    • +
      +

      equals

      +
      public static boolean equals​(java.awt.Shape shapeA, +java.awt.Shape shapeB)
      +
      +
    • +
    +
    +
  • +
+
+ +
+ +
+
+ + diff --git a/io-vector/build/docs/javadoc/org.xbib.graphics.io.vector/org/xbib/graphics/io/vector/util/ImageDataStream.Interleaving.html b/io-vector/build/docs/javadoc/org.xbib.graphics.io.vector/org/xbib/graphics/io/vector/util/ImageDataStream.Interleaving.html new file mode 100644 index 0000000..e4f783e --- /dev/null +++ b/io-vector/build/docs/javadoc/org.xbib.graphics.io.vector/org/xbib/graphics/io/vector/util/ImageDataStream.Interleaving.html @@ -0,0 +1,305 @@ + + + + + +ImageDataStream.Interleaving (io-vector 3.0.0 API) + + + + + + + + + + + + + +
+ +
+
+ +
+ + +

Enum ImageDataStream.Interleaving

+
+
java.lang.Object +
java.lang.Enum<ImageDataStream.Interleaving> +
org.xbib.graphics.io.vector.util.ImageDataStream.Interleaving
+
+
+
+
+
All Implemented Interfaces:
+
java.io.Serializable, java.lang.Comparable<ImageDataStream.Interleaving>, java.lang.constant.Constable
+
+
+
Enclosing class:
+
ImageDataStream
+
+
+
public static enum ImageDataStream.Interleaving
+extends java.lang.Enum<ImageDataStream.Interleaving>
+
+
+
    + +
  • +
    +

    Nested Class Summary

    +
    +

    Nested classes/interfaces inherited from class java.lang.Enum

    +java.lang.Enum.EnumDesc<E extends java.lang.Enum<E>>
    +
    +
  • + +
  • +
    +

    Enum Constant Summary

    +
    + + + + + + + + + + + + + + + + + + + + + + + + + + +
    Enum Constants
    Enum ConstantDescription
    ALPHA_ONLY 
    ROW 
    SAMPLE 
    WITHOUT_ALPHA 
    +
    +
    +
  • + +
  • +
    +

    Method Summary

    +
    +
    +
    + + + + + + + + + + + + + + + + + + + + +
    Modifier and TypeMethodDescription
    static ImageDataStream.InterleavingvalueOf​(java.lang.String name) +
    Returns the enum constant of this type with the specified name.
    +
    static ImageDataStream.Interleaving[]values() +
    Returns an array containing the constants of this enum type, in +the order they are declared.
    +
    +
    +
    +
    +

    Methods inherited from class java.lang.Enum

    +clone, compareTo, describeConstable, equals, finalize, getDeclaringClass, hashCode, name, ordinal, toString, valueOf
    +
    +

    Methods inherited from class java.lang.Object

    +getClass, notify, notifyAll, wait, wait, wait
    +
    +
  • +
+
+
+
    + +
  • +
    +

    Enum Constant Details

    + +
    +
  • + +
  • +
    +

    Method Details

    +
      +
    • +
      +

      values

      +
      public static ImageDataStream.Interleaving[] values()
      +
      Returns an array containing the constants of this enum type, in +the order they are declared.
      +
      +
      Returns:
      +
      an array containing the constants of this enum type, in the order they are declared
      +
      +
      +
    • +
    • +
      +

      valueOf

      +
      public static ImageDataStream.Interleaving valueOf​(java.lang.String name)
      +
      Returns the enum constant of this type with the specified name. +The string must match exactly an identifier used to declare an +enum constant in this type. (Extraneous whitespace characters are +not permitted.)
      +
      +
      Parameters:
      +
      name - the name of the enum constant to be returned.
      +
      Returns:
      +
      the enum constant with the specified name
      +
      Throws:
      +
      java.lang.IllegalArgumentException - if this enum type has no constant with the specified name
      +
      java.lang.NullPointerException - if the argument is null
      +
      +
      +
    • +
    +
    +
  • +
+
+ +
+ +
+
+ + diff --git a/io-vector/build/docs/javadoc/org.xbib.graphics.io.vector/org/xbib/graphics/io/vector/util/ImageDataStream.html b/io-vector/build/docs/javadoc/org.xbib.graphics.io.vector/org/xbib/graphics/io/vector/util/ImageDataStream.html new file mode 100644 index 0000000..07b0551 --- /dev/null +++ b/io-vector/build/docs/javadoc/org.xbib.graphics.io.vector/org/xbib/graphics/io/vector/util/ImageDataStream.html @@ -0,0 +1,283 @@ + + + + + +ImageDataStream (io-vector 3.0.0 API) + + + + + + + + + + + + + +
+ +
+
+ +
+ + +

Class ImageDataStream

+
+
java.lang.Object +
java.io.InputStream +
org.xbib.graphics.io.vector.util.ImageDataStream
+
+
+
+
+
All Implemented Interfaces:
+
java.io.Closeable, java.lang.AutoCloseable
+
+
+
public class ImageDataStream
+extends java.io.InputStream
+
+
+
    + +
  • +
    +

    Nested Class Summary

    +
    + + + + + + + + + + + + + + + + +
    Nested Classes
    Modifier and TypeClassDescription
    static class ImageDataStream.Interleaving 
    +
    +
    +
  • + +
  • +
    +

    Constructor Summary

    +
    + + + + + + + + + + + + + + +
    Constructors
    ConstructorDescription
    ImageDataStream​(java.awt.image.BufferedImage image, +ImageDataStream.Interleaving interleaving) 
    +
    +
    +
  • + +
  • +
    +

    Method Summary

    +
    +
    +
    + + + + + + + + + + + + + + + + + + + + + + + + + +
    Modifier and TypeMethodDescription
    java.awt.image.BufferedImagegetImage() 
    ImageDataStream.InterleavinggetInterleaving() 
    intread() 
    +
    +
    +
    +

    Methods inherited from class java.io.InputStream

    +available, close, mark, markSupported, nullInputStream, read, read, readAllBytes, readNBytes, readNBytes, reset, skip, skipNBytes, transferTo
    +
    +

    Methods inherited from class java.lang.Object

    +clone, equals, finalize, getClass, hashCode, notify, notifyAll, toString, wait, wait, wait
    +
    +
  • +
+
+
+
    + +
  • +
    +

    Constructor Details

    + +
    +
  • + +
  • +
    +

    Method Details

    +
      +
    • +
      +

      getImage

      +
      public java.awt.image.BufferedImage getImage()
      +
      +
    • +
    • +
      +

      getInterleaving

      +
      public ImageDataStream.Interleaving getInterleaving()
      +
      +
    • +
    • +
      +

      read

      +
      public int read() + throws java.io.IOException
      +
      +
      Specified by:
      +
      read in class java.io.InputStream
      +
      Throws:
      +
      java.io.IOException
      +
      +
      +
    • +
    +
    +
  • +
+
+ +
+ +
+
+ + diff --git a/io-vector/build/docs/javadoc/org.xbib.graphics.io.vector/org/xbib/graphics/io/vector/util/LineWrapOutputStream.html b/io-vector/build/docs/javadoc/org.xbib.graphics.io.vector/org/xbib/graphics/io/vector/util/LineWrapOutputStream.html new file mode 100644 index 0000000..534ab23 --- /dev/null +++ b/io-vector/build/docs/javadoc/org.xbib.graphics.io.vector/org/xbib/graphics/io/vector/util/LineWrapOutputStream.html @@ -0,0 +1,301 @@ + + + + + +LineWrapOutputStream (io-vector 3.0.0 API) + + + + + + + + + + + + + +
+ +
+
+ +
+ + +

Class LineWrapOutputStream

+
+
java.lang.Object +
java.io.OutputStream +
java.io.FilterOutputStream +
org.xbib.graphics.io.vector.util.LineWrapOutputStream
+
+
+
+
+
+
All Implemented Interfaces:
+
java.io.Closeable, java.io.Flushable, java.lang.AutoCloseable
+
+
+
public class LineWrapOutputStream
+extends java.io.FilterOutputStream
+
+
+
    + +
  • +
    +

    Field Summary

    +
    + + + + + + + + + + + + + + + + +
    Fields
    Modifier and TypeFieldDescription
    static java.lang.StringSTANDARD_EOL 
    +
    +
    +

    Fields inherited from class java.io.FilterOutputStream

    +out
    +
    +
  • + +
  • +
    +

    Constructor Summary

    +
    + + + + + + + + + + + + + + + + + + +
    Constructors
    ConstructorDescription
    LineWrapOutputStream​(java.io.OutputStream sink, +int lineWidth) 
    LineWrapOutputStream​(java.io.OutputStream sink, +int lineWidth, +java.lang.String eol) 
    +
    +
    +
  • + +
  • +
    +

    Method Summary

    +
    +
    +
    + + + + + + + + + + + + + + + +
    Modifier and TypeMethodDescription
    voidwrite​(int b) 
    +
    +
    +
    +

    Methods inherited from class java.io.FilterOutputStream

    +close, flush, write, write
    +
    +

    Methods inherited from class java.io.OutputStream

    +nullOutputStream
    +
    +

    Methods inherited from class java.lang.Object

    +clone, equals, finalize, getClass, hashCode, notify, notifyAll, toString, wait, wait, wait
    +
    +
  • +
+
+
+
    + +
  • +
    +

    Field Details

    +
      +
    • +
      +

      STANDARD_EOL

      +
      public static final java.lang.String STANDARD_EOL
      +
      +
      See Also:
      +
      Constant Field Values
      +
      +
      +
    • +
    +
    +
  • + +
  • +
    +

    Constructor Details

    +
      +
    • +
      +

      LineWrapOutputStream

      +
      public LineWrapOutputStream​(java.io.OutputStream sink, +int lineWidth, +java.lang.String eol)
      +
      +
    • +
    • +
      +

      LineWrapOutputStream

      +
      public LineWrapOutputStream​(java.io.OutputStream sink, +int lineWidth)
      +
      +
    • +
    +
    +
  • + +
  • +
    +

    Method Details

    +
      +
    • +
      +

      write

      +
      public void write​(int b) + throws java.io.IOException
      +
      +
      Overrides:
      +
      write in class java.io.FilterOutputStream
      +
      Throws:
      +
      java.io.IOException
      +
      +
      +
    • +
    +
    +
  • +
+
+ +
+ +
+
+ + diff --git a/io-vector/build/docs/javadoc/org.xbib.graphics.io.vector/org/xbib/graphics/io/vector/util/VectorHints.Key.html b/io-vector/build/docs/javadoc/org.xbib.graphics.io.vector/org/xbib/graphics/io/vector/util/VectorHints.Key.html new file mode 100644 index 0000000..aee65f0 --- /dev/null +++ b/io-vector/build/docs/javadoc/org.xbib.graphics.io.vector/org/xbib/graphics/io/vector/util/VectorHints.Key.html @@ -0,0 +1,259 @@ + + + + + +VectorHints.Key (io-vector 3.0.0 API) + + + + + + + + + + + + + +
+ +
+
+ +
+ + +

Class VectorHints.Key

+
+
java.lang.Object +
java.awt.RenderingHints.Key +
org.xbib.graphics.io.vector.util.VectorHints.Key
+
+
+
+
+
Enclosing class:
+
VectorHints
+
+
+
public static class VectorHints.Key
+extends java.awt.RenderingHints.Key
+
+
+
    + +
  • +
    +

    Constructor Summary

    +
    + + + + + + + + + + + + + + +
    Constructors
    ConstructorDescription
    Key​(int privateKey, +java.lang.String description) 
    +
    +
    +
  • + +
  • +
    +

    Method Summary

    +
    +
    +
    + + + + + + + + + + + + + + + + + + + + + + + + + +
    Modifier and TypeMethodDescription
    intgetIndex() 
    booleanisCompatibleValue​(java.lang.Object val) 
    java.lang.StringtoString() 
    +
    +
    +
    +

    Methods inherited from class java.awt.RenderingHints.Key

    +equals, hashCode, intKey
    +
    +

    Methods inherited from class java.lang.Object

    +clone, finalize, getClass, notify, notifyAll, wait, wait, wait
    +
    +
  • +
+
+
+
    + +
  • +
    +

    Constructor Details

    +
      +
    • +
      +

      Key

      +
      public Key​(int privateKey, +java.lang.String description)
      +
      +
    • +
    +
    +
  • + +
  • +
    +

    Method Details

    +
      +
    • +
      +

      getIndex

      +
      public int getIndex()
      +
      +
    • +
    • +
      +

      isCompatibleValue

      +
      public boolean isCompatibleValue​(java.lang.Object val)
      +
      +
      Specified by:
      +
      isCompatibleValue in class java.awt.RenderingHints.Key
      +
      +
      +
    • +
    • +
      +

      toString

      +
      public java.lang.String toString()
      +
      +
      Overrides:
      +
      toString in class java.lang.Object
      +
      +
      +
    • +
    +
    +
  • +
+
+ +
+ +
+
+ + diff --git a/io-vector/build/docs/javadoc/org.xbib.graphics.io.vector/org/xbib/graphics/io/vector/util/VectorHints.Value.html b/io-vector/build/docs/javadoc/org.xbib.graphics.io.vector/org/xbib/graphics/io/vector/util/VectorHints.Value.html new file mode 100644 index 0000000..f187cab --- /dev/null +++ b/io-vector/build/docs/javadoc/org.xbib.graphics.io.vector/org/xbib/graphics/io/vector/util/VectorHints.Value.html @@ -0,0 +1,263 @@ + + + + + +VectorHints.Value (io-vector 3.0.0 API) + + + + + + + + + + + + + +
+ +
+
+ +
+ + +

Class VectorHints.Value

+
+
java.lang.Object +
org.xbib.graphics.io.vector.util.VectorHints.Value
+
+
+
+
Enclosing class:
+
VectorHints
+
+
+
public static class VectorHints.Value
+extends java.lang.Object
+
+
+
    + +
  • +
    +

    Constructor Summary

    +
    + + + + + + + + + + + + + + +
    Constructors
    ConstructorDescription
    Value​(VectorHints.Key key, +int index, +java.lang.String description) 
    +
    +
    +
  • + +
  • +
    +

    Method Summary

    +
    +
    +
    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    Modifier and TypeMethodDescription
    java.lang.StringgetId() 
    intgetIndex() 
    booleanisCompatibleKey​(java.awt.RenderingHints.Key key) 
    java.lang.StringtoString() 
    +
    +
    +
    +

    Methods inherited from class java.lang.Object

    +clone, equals, finalize, getClass, hashCode, notify, notifyAll, wait, wait, wait
    +
    +
  • +
+
+
+
    + +
  • +
    +

    Constructor Details

    +
      +
    • +
      +

      Value

      +
      public Value​(VectorHints.Key key, +int index, +java.lang.String description)
      +
      +
    • +
    +
    +
  • + +
  • +
    +

    Method Details

    +
      +
    • +
      +

      isCompatibleKey

      +
      public boolean isCompatibleKey​(java.awt.RenderingHints.Key key)
      +
      +
    • +
    • +
      +

      getIndex

      +
      public int getIndex()
      +
      +
    • +
    • +
      +

      getId

      +
      public java.lang.String getId()
      +
      +
    • +
    • +
      +

      toString

      +
      public java.lang.String toString()
      +
      +
      Overrides:
      +
      toString in class java.lang.Object
      +
      +
      +
    • +
    +
    +
  • +
+
+ +
+ +
+
+ + diff --git a/io-vector/build/docs/javadoc/org.xbib.graphics.io.vector/org/xbib/graphics/io/vector/util/VectorHints.html b/io-vector/build/docs/javadoc/org.xbib.graphics.io.vector/org/xbib/graphics/io/vector/util/VectorHints.html new file mode 100644 index 0000000..488799b --- /dev/null +++ b/io-vector/build/docs/javadoc/org.xbib.graphics.io.vector/org/xbib/graphics/io/vector/util/VectorHints.html @@ -0,0 +1,314 @@ + + + + + +VectorHints (io-vector 3.0.0 API) + + + + + + + + + + + + + +
+ +
+
+ +
+ + +

Class VectorHints

+
+
java.lang.Object +
org.xbib.graphics.io.vector.util.VectorHints
+
+
+
+
public abstract class VectorHints
+extends java.lang.Object
+
+
+
    + +
  • +
    +

    Nested Class Summary

    +
    + + + + + + + + + + + + + + + + + + + + + +
    Nested Classes
    Modifier and TypeClassDescription
    static class VectorHints.Key 
    static class VectorHints.Value 
    +
    +
    +
  • + +
  • +
    +

    Field Summary

    +
    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    Fields
    Modifier and TypeFieldDescription
    static VectorHints.KeyKEY_EXPORT 
    static VectorHints.KeyKEY_TEXT 
    static java.lang.ObjectVALUE_EXPORT_QUALITY 
    static java.lang.ObjectVALUE_EXPORT_READABILITY 
    static java.lang.ObjectVALUE_EXPORT_SIZE 
    static java.lang.ObjectVALUE_TEXT_DEFAULT 
    static java.lang.ObjectVALUE_TEXT_VECTOR 
    +
    +
    +
  • + +
  • +
    +

    Constructor Summary

    +
    + + + + + + + + + + + + + + + + +
    Constructors
    ModifierConstructorDescription
    protected VectorHints() 
    +
    +
    +
  • + +
  • +
    +

    Method Summary

    +
    +

    Methods inherited from class java.lang.Object

    +clone, equals, finalize, getClass, hashCode, notify, notifyAll, toString, wait, wait, wait
    +
    +
  • +
+
+
+
    + +
  • +
    +

    Field Details

    +
      +
    • +
      +

      KEY_EXPORT

      +
      public static final VectorHints.Key KEY_EXPORT
      +
      +
    • +
    • +
      +

      VALUE_EXPORT_READABILITY

      +
      public static final java.lang.Object VALUE_EXPORT_READABILITY
      +
      +
    • +
    • +
      +

      VALUE_EXPORT_QUALITY

      +
      public static final java.lang.Object VALUE_EXPORT_QUALITY
      +
      +
    • +
    • +
      +

      VALUE_EXPORT_SIZE

      +
      public static final java.lang.Object VALUE_EXPORT_SIZE
      +
      +
    • +
    • +
      +

      KEY_TEXT

      +
      public static final VectorHints.Key KEY_TEXT
      +
      +
    • +
    • +
      +

      VALUE_TEXT_DEFAULT

      +
      public static final java.lang.Object VALUE_TEXT_DEFAULT
      +
      +
    • +
    • +
      +

      VALUE_TEXT_VECTOR

      +
      public static final java.lang.Object VALUE_TEXT_VECTOR
      +
      +
    • +
    +
    +
  • + +
  • +
    +

    Constructor Details

    +
      +
    • +
      +

      VectorHints

      +
      protected VectorHints()
      +
      +
    • +
    +
    +
  • +
+
+ +
+ +
+
+ + diff --git a/io-vector/build/docs/javadoc/org.xbib.graphics.io.vector/org/xbib/graphics/io/vector/util/package-summary.html b/io-vector/build/docs/javadoc/org.xbib.graphics.io.vector/org/xbib/graphics/io/vector/util/package-summary.html new file mode 100644 index 0000000..acf8071 --- /dev/null +++ b/io-vector/build/docs/javadoc/org.xbib.graphics.io.vector/org/xbib/graphics/io/vector/util/package-summary.html @@ -0,0 +1,171 @@ + + + + + +org.xbib.graphics.io.vector.util (io-vector 3.0.0 API) + + + + + + + + + + + + + +
+ +
+
+
+ +

Package org.xbib.graphics.io.vector.util

+
+
+ +
+
+ +
+
+ + diff --git a/io-vector/build/docs/javadoc/org.xbib.graphics.io.vector/org/xbib/graphics/io/vector/util/package-tree.html b/io-vector/build/docs/javadoc/org.xbib.graphics.io.vector/org/xbib/graphics/io/vector/util/package-tree.html new file mode 100644 index 0000000..5578957 --- /dev/null +++ b/io-vector/build/docs/javadoc/org.xbib.graphics.io.vector/org/xbib/graphics/io/vector/util/package-tree.html @@ -0,0 +1,138 @@ + + + + + +org.xbib.graphics.io.vector.util Class Hierarchy (io-vector 3.0.0 API) + + + + + + + + + + + + + +
+ +
+
+
+

Hierarchy For Package org.xbib.graphics.io.vector.util

+Package Hierarchies: + +
+
+

Class Hierarchy

+
    +
  • java.lang.Object +
      +
    • org.xbib.graphics.io.vector.util.AlphaToMaskOp (implements java.awt.image.BufferedImageOp)
    • +
    • org.xbib.graphics.io.vector.util.DataUtils
    • +
    • org.xbib.graphics.io.vector.util.FormattingWriter (implements java.io.Closeable, java.io.Flushable)
    • +
    • org.xbib.graphics.io.vector.util.GraphicsUtils
    • +
    • java.io.InputStream (implements java.io.Closeable) + +
    • +
    • java.io.OutputStream (implements java.io.Closeable, java.io.Flushable) + +
    • +
    • java.awt.RenderingHints.Key + +
    • +
    • org.xbib.graphics.io.vector.util.VectorHints
    • +
    • org.xbib.graphics.io.vector.util.VectorHints.Value
    • +
    +
  • +
+
+
+

Enum Hierarchy

+
    +
  • java.lang.Object +
      +
    • java.lang.Enum<E> (implements java.lang.Comparable<T>, java.lang.constant.Constable, java.io.Serializable) + +
    • +
    +
  • +
+
+
+ +
+
+ + diff --git a/io-vector/build/docs/javadoc/overview-tree.html b/io-vector/build/docs/javadoc/overview-tree.html new file mode 100644 index 0000000..918c680 --- /dev/null +++ b/io-vector/build/docs/javadoc/overview-tree.html @@ -0,0 +1,235 @@ + + + + + +Class Hierarchy (io-vector 3.0.0 API) + + + + + + + + + + + + + +
+ +
+
+ +
+

Class Hierarchy

+ +
+
+

Interface Hierarchy

+ +
+
+

Enum Hierarchy

+
    +
  • java.lang.Object + +
  • +
+
+
+ +
+
+ + diff --git a/io-vector/build/docs/javadoc/package-search-index.js b/io-vector/build/docs/javadoc/package-search-index.js new file mode 100644 index 0000000..8baa428 --- /dev/null +++ b/io-vector/build/docs/javadoc/package-search-index.js @@ -0,0 +1 @@ +packageSearchIndex = [{"l":"All Packages","u":"allpackages-index.html"},{"m":"org.xbib.graphics.io.vector","l":"org.xbib.graphics.io.vector"},{"m":"org.xbib.graphics.io.vector","l":"org.xbib.graphics.io.vector.commands"},{"m":"org.xbib.graphics.io.vector","l":"org.xbib.graphics.io.vector.eps"},{"m":"org.xbib.graphics.io.vector","l":"org.xbib.graphics.io.vector.filters"},{"m":"org.xbib.graphics.io.vector","l":"org.xbib.graphics.io.vector.pdf"},{"m":"org.xbib.graphics.io.vector","l":"org.xbib.graphics.io.vector.svg"},{"m":"org.xbib.graphics.io.vector","l":"org.xbib.graphics.io.vector.util"}];updateSearchResults(); \ No newline at end of file diff --git a/io-vector/build/docs/javadoc/resources/glass.png b/io-vector/build/docs/javadoc/resources/glass.png new file mode 100644 index 0000000000000000000000000000000000000000..a7f591f467a1c0c949bbc510156a0c1afb860a6e GIT binary patch literal 499 zcmVJoRsvExf%rEN>jUL}qZ_~k#FbE+Q;{`;0FZwVNX2n-^JoI; zP;4#$8DIy*Yk-P>VN(DUKmPse7mx+ExD4O|;?E5D0Z5($mjO3`*anwQU^s{ZDK#Lz zj>~{qyaIx5K!t%=G&2IJNzg!ChRpyLkO7}Ry!QaotAHAMpbB3AF(}|_f!G-oI|uK6 z`id_dumai5K%C3Y$;tKS_iqMPHg<*|-@e`liWLAggVM!zAP#@l;=c>S03;{#04Z~5 zN_+ss=Yg6*hTr59mzMwZ@+l~q!+?ft!fF66AXT#wWavHt30bZWFCK%!BNk}LN?0Hg z1VF_nfs`Lm^DjYZ1(1uD0u4CSIr)XAaqW6IT{!St5~1{i=i}zAy76p%_|w8rh@@c0Axr!ns=D-X+|*sY6!@wacG9%)Qn*O zl0sa739kT-&_?#oVxXF6tOnqTD)cZ}2vi$`ZU8RLAlo8=_z#*P3xI~i!lEh+Pdu-L zx{d*wgjtXbnGX_Yf@Tc7Q3YhLhPvc8noGJs2DA~1DySiA&6V{5JzFt ojAY1KXm~va;tU{v7C?Xj0BHw!K;2aXV*mgE07*qoM6N<$f;4TDA^-pY literal 0 HcmV?d00001 diff --git a/io-vector/build/docs/javadoc/script-dir/images/ui-bg_glass_55_fbf9ee_1x400.png b/io-vector/build/docs/javadoc/script-dir/images/ui-bg_glass_55_fbf9ee_1x400.png new file mode 100644 index 0000000000000000000000000000000000000000..34abd18f32d3a55a297fdcf93409bd033ae573e7 GIT binary patch literal 335 zcmeAS@N?(olHy`uVBq!ia0vp^j6gI&fCnc6a#?2AmP!?*K(O3p^r= zfwTu0yPeFo12TF&T^vI^j=w#x$i?I+((tf;UXnmgbH|3oY>pC!)f}(GR!16S-u+#{ ze6YEqRkW=8vGl=5qArKM<9}TC-}iEvB{zdaTcX5$wyRTK&ALRXUCGx5b?-VBQkUm|IuXOmYJrBRJgj{Vx zMbNnqUkncy+qa2-mWYc>swkcIuvGK#>(0d)B7)5f`@$Ei28nH~0h*~=;u=wsl30>z zm0Xkxq!^403@vmGjdTsnLJUl-Obo4zO|=aStPBhe<(7X!(U6;;l9^VCTf=69^L{`L N44$rjF6*2UngDu&PXPb` literal 0 HcmV?d00001 diff --git a/io-vector/build/docs/javadoc/script-dir/images/ui-bg_glass_75_e6e6e6_1x400.png b/io-vector/build/docs/javadoc/script-dir/images/ui-bg_glass_75_e6e6e6_1x400.png new file mode 100644 index 0000000000000000000000000000000000000000..a90afb8bf8028404d206114965669b023dcb85ea GIT binary patch literal 262 zcmeAS@N?(olHy`uVBq!ia0vp^j6gI&0LWmFTHNUZq?nSt-Ch3w7g=q17Rci)@Q5r1 z(jH*!b~4)z#PD=+46!(!TrvH)L6@80)r*_cdCvDr%)6ghVL16=s@mbz7H!uRdGeDa z?kzLg)16i!f8fKx84s0>4hES%`s&m5HI1v5B^Uft7(lid2moiiX_$l+3hB+!{pPkNg5^ OVDNPHb6Mw<&;$T*0!_~V literal 0 HcmV?d00001 diff --git a/io-vector/build/docs/javadoc/script-dir/images/ui-bg_glass_95_fef1ec_1x400.png b/io-vector/build/docs/javadoc/script-dir/images/ui-bg_glass_95_fef1ec_1x400.png new file mode 100644 index 0000000000000000000000000000000000000000..dbe091f6dc036fc1dc11b005738e951e27a43f7a GIT binary patch literal 332 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�_j6WK+Gj+xJRxVvl#lh7y-4uY);t)n47k4ot z9YsU`HVk7fg4;r{;)FHk7ZHyZJ+W|$aKwj=g&_$VCVFn5%XzSA`|z}+4ItZ|`hB}R z>h-6Be`d>nmv8;kQHJg!HMr^cCGG=T5;3HhZ_JRq0_4a3TsY7Pz{V+}z>;!R^U4*c zJ>wRI59B-)92Vi?b&EWvH(`<(G5A?W)z>EuDMG@VENAb7aHa`I#tKw{0uUc3(#J8& z*_S%A_ZxCIY385{%qN-b1K)TWmCjUA4nWKx_ZnKLSvEf0($&_0@DS~ zN8JOXJXXcaFm^OCYrz(R7N5DQkXKGnnt}yzfw^8s%=A?7hxza;ylJ;XQ&XtC`pM%b z6$5Ff0{(ALcSlTKvIbr@mR`0Z)*iM`2EfO|E5OMk$jQsE!^mat*drqV literal 0 HcmV?d00001 diff --git a/io-vector/build/docs/javadoc/script-dir/images/ui-icons_2e83ff_256x240.png b/io-vector/build/docs/javadoc/script-dir/images/ui-icons_2e83ff_256x240.png new file mode 100644 index 0000000000000000000000000000000000000000..1f5f49756ca64bd20a1048bd7a3a584457d4bf00 GIT binary patch literal 4549 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 literal 0 HcmV?d00001 diff --git a/io-vector/build/docs/javadoc/script-dir/images/ui-icons_888888_256x240.png b/io-vector/build/docs/javadoc/script-dir/images/ui-icons_888888_256x240.png new file mode 100644 index 0000000000000000000000000000000000000000..ee5e33f27235d1b5cf0259f63f3894a6be33c679 GIT binary patch literal 6999 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 literal 0 HcmV?d00001 diff --git a/io-vector/build/docs/javadoc/script-dir/images/ui-icons_cd0a0a_256x240.png b/io-vector/build/docs/javadoc/script-dir/images/ui-icons_cd0a0a_256x240.png new file mode 100644 index 0000000000000000000000000000000000000000..7e8ebc180a2d2a74739059799407b8b7b57a9092 GIT binary patch literal 4549 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 Mq_Ix57W&cu0AIB!=l}o! literal 0 HcmV?d00001 diff --git a/io-vector/build/docs/javadoc/script-dir/jquery-3.5.1.min.js b/io-vector/build/docs/javadoc/script-dir/jquery-3.5.1.min.js new file mode 100644 index 0000000..b061403 --- /dev/null +++ b/io-vector/build/docs/javadoc/script-dir/jquery-3.5.1.min.js @@ -0,0 +1,2 @@ +/*! jQuery v3.5.1 | (c) JS Foundation and other contributors | jquery.org/license */ +!function(e,t){"use strict";"object"==typeof module&&"object"==typeof module.exports?module.exports=e.document?t(e,!0):function(e){if(!e.document)throw new Error("jQuery requires a window with a document");return t(e)}:t(e)}("undefined"!=typeof window?window:this,function(C,e){"use strict";var t=[],r=Object.getPrototypeOf,s=t.slice,g=t.flat?function(e){return t.flat.call(e)}:function(e){return t.concat.apply([],e)},u=t.push,i=t.indexOf,n={},o=n.toString,v=n.hasOwnProperty,a=v.toString,l=a.call(Object),y={},m=function(e){return"function"==typeof e&&"number"!=typeof e.nodeType},x=function(e){return null!=e&&e===e.window},E=C.document,c={type:!0,src:!0,nonce:!0,noModule:!0};function b(e,t,n){var r,i,o=(n=n||E).createElement("script");if(o.text=e,t)for(r in c)(i=t[r]||t.getAttribute&&t.getAttribute(r))&&o.setAttribute(r,i);n.head.appendChild(o).parentNode.removeChild(o)}function w(e){return null==e?e+"":"object"==typeof e||"function"==typeof e?n[o.call(e)]||"object":typeof e}var f="3.5.1",S=function(e,t){return new S.fn.init(e,t)};function p(e){var t=!!e&&"length"in e&&e.length,n=w(e);return!m(e)&&!x(e)&&("array"===n||0===t||"number"==typeof t&&0+~]|"+M+")"+M+"*"),U=new RegExp(M+"|>"),X=new RegExp(F),V=new RegExp("^"+I+"$"),G={ID:new RegExp("^#("+I+")"),CLASS:new RegExp("^\\.("+I+")"),TAG:new RegExp("^("+I+"|[*])"),ATTR:new RegExp("^"+W),PSEUDO:new RegExp("^"+F),CHILD:new RegExp("^:(only|first|last|nth|nth-last)-(child|of-type)(?:\\("+M+"*(even|odd|(([+-]|)(\\d*)n|)"+M+"*(?:([+-]|)"+M+"*(\\d+)|))"+M+"*\\)|)","i"),bool:new RegExp("^(?:"+R+")$","i"),needsContext:new RegExp("^"+M+"*[>+~]|:(even|odd|eq|gt|lt|nth|first|last)(?:\\("+M+"*((?:-\\d)?\\d*)"+M+"*\\)|)(?=[^-]|$)","i")},Y=/HTML$/i,Q=/^(?:input|select|textarea|button)$/i,J=/^h\d$/i,K=/^[^{]+\{\s*\[native \w/,Z=/^(?:#([\w-]+)|(\w+)|\.([\w-]+))$/,ee=/[+~]/,te=new RegExp("\\\\[\\da-fA-F]{1,6}"+M+"?|\\\\([^\\r\\n\\f])","g"),ne=function(e,t){var n="0x"+e.slice(1)-65536;return t||(n<0?String.fromCharCode(n+65536):String.fromCharCode(n>>10|55296,1023&n|56320))},re=/([\0-\x1f\x7f]|^-?\d)|^-$|[^\0-\x1f\x7f-\uFFFF\w-]/g,ie=function(e,t){return t?"\0"===e?"\ufffd":e.slice(0,-1)+"\\"+e.charCodeAt(e.length-1).toString(16)+" ":"\\"+e},oe=function(){T()},ae=be(function(e){return!0===e.disabled&&"fieldset"===e.nodeName.toLowerCase()},{dir:"parentNode",next:"legend"});try{H.apply(t=O.call(p.childNodes),p.childNodes),t[p.childNodes.length].nodeType}catch(e){H={apply:t.length?function(e,t){L.apply(e,O.call(t))}:function(e,t){var n=e.length,r=0;while(e[n++]=t[r++]);e.length=n-1}}}function se(t,e,n,r){var i,o,a,s,u,l,c,f=e&&e.ownerDocument,p=e?e.nodeType:9;if(n=n||[],"string"!=typeof t||!t||1!==p&&9!==p&&11!==p)return n;if(!r&&(T(e),e=e||C,E)){if(11!==p&&(u=Z.exec(t)))if(i=u[1]){if(9===p){if(!(a=e.getElementById(i)))return n;if(a.id===i)return n.push(a),n}else if(f&&(a=f.getElementById(i))&&y(e,a)&&a.id===i)return n.push(a),n}else{if(u[2])return H.apply(n,e.getElementsByTagName(t)),n;if((i=u[3])&&d.getElementsByClassName&&e.getElementsByClassName)return H.apply(n,e.getElementsByClassName(i)),n}if(d.qsa&&!N[t+" "]&&(!v||!v.test(t))&&(1!==p||"object"!==e.nodeName.toLowerCase())){if(c=t,f=e,1===p&&(U.test(t)||z.test(t))){(f=ee.test(t)&&ye(e.parentNode)||e)===e&&d.scope||((s=e.getAttribute("id"))?s=s.replace(re,ie):e.setAttribute("id",s=S)),o=(l=h(t)).length;while(o--)l[o]=(s?"#"+s:":scope")+" "+xe(l[o]);c=l.join(",")}try{return H.apply(n,f.querySelectorAll(c)),n}catch(e){N(t,!0)}finally{s===S&&e.removeAttribute("id")}}}return g(t.replace($,"$1"),e,n,r)}function ue(){var r=[];return function e(t,n){return r.push(t+" ")>b.cacheLength&&delete e[r.shift()],e[t+" "]=n}}function le(e){return e[S]=!0,e}function ce(e){var t=C.createElement("fieldset");try{return!!e(t)}catch(e){return!1}finally{t.parentNode&&t.parentNode.removeChild(t),t=null}}function fe(e,t){var n=e.split("|"),r=n.length;while(r--)b.attrHandle[n[r]]=t}function pe(e,t){var n=t&&e,r=n&&1===e.nodeType&&1===t.nodeType&&e.sourceIndex-t.sourceIndex;if(r)return r;if(n)while(n=n.nextSibling)if(n===t)return-1;return e?1:-1}function de(t){return function(e){return"input"===e.nodeName.toLowerCase()&&e.type===t}}function he(n){return function(e){var t=e.nodeName.toLowerCase();return("input"===t||"button"===t)&&e.type===n}}function ge(t){return function(e){return"form"in e?e.parentNode&&!1===e.disabled?"label"in e?"label"in e.parentNode?e.parentNode.disabled===t:e.disabled===t:e.isDisabled===t||e.isDisabled!==!t&&ae(e)===t:e.disabled===t:"label"in e&&e.disabled===t}}function ve(a){return le(function(o){return o=+o,le(function(e,t){var n,r=a([],e.length,o),i=r.length;while(i--)e[n=r[i]]&&(e[n]=!(t[n]=e[n]))})})}function ye(e){return e&&"undefined"!=typeof e.getElementsByTagName&&e}for(e in d=se.support={},i=se.isXML=function(e){var t=e.namespaceURI,n=(e.ownerDocument||e).documentElement;return!Y.test(t||n&&n.nodeName||"HTML")},T=se.setDocument=function(e){var t,n,r=e?e.ownerDocument||e:p;return r!=C&&9===r.nodeType&&r.documentElement&&(a=(C=r).documentElement,E=!i(C),p!=C&&(n=C.defaultView)&&n.top!==n&&(n.addEventListener?n.addEventListener("unload",oe,!1):n.attachEvent&&n.attachEvent("onunload",oe)),d.scope=ce(function(e){return a.appendChild(e).appendChild(C.createElement("div")),"undefined"!=typeof e.querySelectorAll&&!e.querySelectorAll(":scope fieldset div").length}),d.attributes=ce(function(e){return e.className="i",!e.getAttribute("className")}),d.getElementsByTagName=ce(function(e){return e.appendChild(C.createComment("")),!e.getElementsByTagName("*").length}),d.getElementsByClassName=K.test(C.getElementsByClassName),d.getById=ce(function(e){return a.appendChild(e).id=S,!C.getElementsByName||!C.getElementsByName(S).length}),d.getById?(b.filter.ID=function(e){var t=e.replace(te,ne);return function(e){return e.getAttribute("id")===t}},b.find.ID=function(e,t){if("undefined"!=typeof t.getElementById&&E){var n=t.getElementById(e);return n?[n]:[]}}):(b.filter.ID=function(e){var n=e.replace(te,ne);return function(e){var t="undefined"!=typeof e.getAttributeNode&&e.getAttributeNode("id");return t&&t.value===n}},b.find.ID=function(e,t){if("undefined"!=typeof t.getElementById&&E){var n,r,i,o=t.getElementById(e);if(o){if((n=o.getAttributeNode("id"))&&n.value===e)return[o];i=t.getElementsByName(e),r=0;while(o=i[r++])if((n=o.getAttributeNode("id"))&&n.value===e)return[o]}return[]}}),b.find.TAG=d.getElementsByTagName?function(e,t){return"undefined"!=typeof t.getElementsByTagName?t.getElementsByTagName(e):d.qsa?t.querySelectorAll(e):void 0}:function(e,t){var n,r=[],i=0,o=t.getElementsByTagName(e);if("*"===e){while(n=o[i++])1===n.nodeType&&r.push(n);return r}return o},b.find.CLASS=d.getElementsByClassName&&function(e,t){if("undefined"!=typeof t.getElementsByClassName&&E)return t.getElementsByClassName(e)},s=[],v=[],(d.qsa=K.test(C.querySelectorAll))&&(ce(function(e){var t;a.appendChild(e).innerHTML="",e.querySelectorAll("[msallowcapture^='']").length&&v.push("[*^$]="+M+"*(?:''|\"\")"),e.querySelectorAll("[selected]").length||v.push("\\["+M+"*(?:value|"+R+")"),e.querySelectorAll("[id~="+S+"-]").length||v.push("~="),(t=C.createElement("input")).setAttribute("name",""),e.appendChild(t),e.querySelectorAll("[name='']").length||v.push("\\["+M+"*name"+M+"*="+M+"*(?:''|\"\")"),e.querySelectorAll(":checked").length||v.push(":checked"),e.querySelectorAll("a#"+S+"+*").length||v.push(".#.+[+~]"),e.querySelectorAll("\\\f"),v.push("[\\r\\n\\f]")}),ce(function(e){e.innerHTML="";var t=C.createElement("input");t.setAttribute("type","hidden"),e.appendChild(t).setAttribute("name","D"),e.querySelectorAll("[name=d]").length&&v.push("name"+M+"*[*^$|!~]?="),2!==e.querySelectorAll(":enabled").length&&v.push(":enabled",":disabled"),a.appendChild(e).disabled=!0,2!==e.querySelectorAll(":disabled").length&&v.push(":enabled",":disabled"),e.querySelectorAll("*,:x"),v.push(",.*:")})),(d.matchesSelector=K.test(c=a.matches||a.webkitMatchesSelector||a.mozMatchesSelector||a.oMatchesSelector||a.msMatchesSelector))&&ce(function(e){d.disconnectedMatch=c.call(e,"*"),c.call(e,"[s!='']:x"),s.push("!=",F)}),v=v.length&&new RegExp(v.join("|")),s=s.length&&new RegExp(s.join("|")),t=K.test(a.compareDocumentPosition),y=t||K.test(a.contains)?function(e,t){var n=9===e.nodeType?e.documentElement:e,r=t&&t.parentNode;return e===r||!(!r||1!==r.nodeType||!(n.contains?n.contains(r):e.compareDocumentPosition&&16&e.compareDocumentPosition(r)))}:function(e,t){if(t)while(t=t.parentNode)if(t===e)return!0;return!1},D=t?function(e,t){if(e===t)return l=!0,0;var n=!e.compareDocumentPosition-!t.compareDocumentPosition;return n||(1&(n=(e.ownerDocument||e)==(t.ownerDocument||t)?e.compareDocumentPosition(t):1)||!d.sortDetached&&t.compareDocumentPosition(e)===n?e==C||e.ownerDocument==p&&y(p,e)?-1:t==C||t.ownerDocument==p&&y(p,t)?1:u?P(u,e)-P(u,t):0:4&n?-1:1)}:function(e,t){if(e===t)return l=!0,0;var n,r=0,i=e.parentNode,o=t.parentNode,a=[e],s=[t];if(!i||!o)return e==C?-1:t==C?1:i?-1:o?1:u?P(u,e)-P(u,t):0;if(i===o)return pe(e,t);n=e;while(n=n.parentNode)a.unshift(n);n=t;while(n=n.parentNode)s.unshift(n);while(a[r]===s[r])r++;return r?pe(a[r],s[r]):a[r]==p?-1:s[r]==p?1:0}),C},se.matches=function(e,t){return se(e,null,null,t)},se.matchesSelector=function(e,t){if(T(e),d.matchesSelector&&E&&!N[t+" "]&&(!s||!s.test(t))&&(!v||!v.test(t)))try{var n=c.call(e,t);if(n||d.disconnectedMatch||e.document&&11!==e.document.nodeType)return n}catch(e){N(t,!0)}return 0":{dir:"parentNode",first:!0}," ":{dir:"parentNode"},"+":{dir:"previousSibling",first:!0},"~":{dir:"previousSibling"}},preFilter:{ATTR:function(e){return e[1]=e[1].replace(te,ne),e[3]=(e[3]||e[4]||e[5]||"").replace(te,ne),"~="===e[2]&&(e[3]=" "+e[3]+" "),e.slice(0,4)},CHILD:function(e){return e[1]=e[1].toLowerCase(),"nth"===e[1].slice(0,3)?(e[3]||se.error(e[0]),e[4]=+(e[4]?e[5]+(e[6]||1):2*("even"===e[3]||"odd"===e[3])),e[5]=+(e[7]+e[8]||"odd"===e[3])):e[3]&&se.error(e[0]),e},PSEUDO:function(e){var t,n=!e[6]&&e[2];return G.CHILD.test(e[0])?null:(e[3]?e[2]=e[4]||e[5]||"":n&&X.test(n)&&(t=h(n,!0))&&(t=n.indexOf(")",n.length-t)-n.length)&&(e[0]=e[0].slice(0,t),e[2]=n.slice(0,t)),e.slice(0,3))}},filter:{TAG:function(e){var t=e.replace(te,ne).toLowerCase();return"*"===e?function(){return!0}:function(e){return e.nodeName&&e.nodeName.toLowerCase()===t}},CLASS:function(e){var t=m[e+" "];return t||(t=new RegExp("(^|"+M+")"+e+"("+M+"|$)"))&&m(e,function(e){return t.test("string"==typeof e.className&&e.className||"undefined"!=typeof e.getAttribute&&e.getAttribute("class")||"")})},ATTR:function(n,r,i){return function(e){var t=se.attr(e,n);return null==t?"!="===r:!r||(t+="","="===r?t===i:"!="===r?t!==i:"^="===r?i&&0===t.indexOf(i):"*="===r?i&&-1:\x20\t\r\n\f]*)[\x20\t\r\n\f]*\/?>(?:<\/\1>|)$/i;function D(e,n,r){return m(n)?S.grep(e,function(e,t){return!!n.call(e,t,e)!==r}):n.nodeType?S.grep(e,function(e){return e===n!==r}):"string"!=typeof n?S.grep(e,function(e){return-1)[^>]*|#([\w-]+))$/;(S.fn.init=function(e,t,n){var r,i;if(!e)return this;if(n=n||j,"string"==typeof e){if(!(r="<"===e[0]&&">"===e[e.length-1]&&3<=e.length?[null,e,null]:q.exec(e))||!r[1]&&t)return!t||t.jquery?(t||n).find(e):this.constructor(t).find(e);if(r[1]){if(t=t instanceof S?t[0]:t,S.merge(this,S.parseHTML(r[1],t&&t.nodeType?t.ownerDocument||t:E,!0)),N.test(r[1])&&S.isPlainObject(t))for(r in t)m(this[r])?this[r](t[r]):this.attr(r,t[r]);return this}return(i=E.getElementById(r[2]))&&(this[0]=i,this.length=1),this}return e.nodeType?(this[0]=e,this.length=1,this):m(e)?void 0!==n.ready?n.ready(e):e(S):S.makeArray(e,this)}).prototype=S.fn,j=S(E);var L=/^(?:parents|prev(?:Until|All))/,H={children:!0,contents:!0,next:!0,prev:!0};function O(e,t){while((e=e[t])&&1!==e.nodeType);return e}S.fn.extend({has:function(e){var t=S(e,this),n=t.length;return this.filter(function(){for(var e=0;e\x20\t\r\n\f]*)/i,he=/^$|^module$|\/(?:java|ecma)script/i;ce=E.createDocumentFragment().appendChild(E.createElement("div")),(fe=E.createElement("input")).setAttribute("type","radio"),fe.setAttribute("checked","checked"),fe.setAttribute("name","t"),ce.appendChild(fe),y.checkClone=ce.cloneNode(!0).cloneNode(!0).lastChild.checked,ce.innerHTML="",y.noCloneChecked=!!ce.cloneNode(!0).lastChild.defaultValue,ce.innerHTML="",y.option=!!ce.lastChild;var ge={thead:[1,"","
"],col:[2,"","
"],tr:[2,"","
"],td:[3,"","
"],_default:[0,"",""]};function ve(e,t){var n;return n="undefined"!=typeof e.getElementsByTagName?e.getElementsByTagName(t||"*"):"undefined"!=typeof e.querySelectorAll?e.querySelectorAll(t||"*"):[],void 0===t||t&&A(e,t)?S.merge([e],n):n}function ye(e,t){for(var n=0,r=e.length;n",""]);var me=/<|&#?\w+;/;function xe(e,t,n,r,i){for(var o,a,s,u,l,c,f=t.createDocumentFragment(),p=[],d=0,h=e.length;d\s*$/g;function qe(e,t){return A(e,"table")&&A(11!==t.nodeType?t:t.firstChild,"tr")&&S(e).children("tbody")[0]||e}function Le(e){return e.type=(null!==e.getAttribute("type"))+"/"+e.type,e}function He(e){return"true/"===(e.type||"").slice(0,5)?e.type=e.type.slice(5):e.removeAttribute("type"),e}function Oe(e,t){var n,r,i,o,a,s;if(1===t.nodeType){if(Y.hasData(e)&&(s=Y.get(e).events))for(i in Y.remove(t,"handle events"),s)for(n=0,r=s[i].length;n").attr(n.scriptAttrs||{}).prop({charset:n.scriptCharset,src:n.url}).on("load error",i=function(e){r.remove(),i=null,e&&t("error"===e.type?404:200,e.type)}),E.head.appendChild(r[0])},abort:function(){i&&i()}}});var Ut,Xt=[],Vt=/(=)\?(?=&|$)|\?\?/;S.ajaxSetup({jsonp:"callback",jsonpCallback:function(){var e=Xt.pop()||S.expando+"_"+Ct.guid++;return this[e]=!0,e}}),S.ajaxPrefilter("json jsonp",function(e,t,n){var r,i,o,a=!1!==e.jsonp&&(Vt.test(e.url)?"url":"string"==typeof e.data&&0===(e.contentType||"").indexOf("application/x-www-form-urlencoded")&&Vt.test(e.data)&&"data");if(a||"jsonp"===e.dataTypes[0])return r=e.jsonpCallback=m(e.jsonpCallback)?e.jsonpCallback():e.jsonpCallback,a?e[a]=e[a].replace(Vt,"$1"+r):!1!==e.jsonp&&(e.url+=(Et.test(e.url)?"&":"?")+e.jsonp+"="+r),e.converters["script json"]=function(){return o||S.error(r+" was not called"),o[0]},e.dataTypes[0]="json",i=C[r],C[r]=function(){o=arguments},n.always(function(){void 0===i?S(C).removeProp(r):C[r]=i,e[r]&&(e.jsonpCallback=t.jsonpCallback,Xt.push(r)),o&&m(i)&&i(o[0]),o=i=void 0}),"script"}),y.createHTMLDocument=((Ut=E.implementation.createHTMLDocument("").body).innerHTML="
",2===Ut.childNodes.length),S.parseHTML=function(e,t,n){return"string"!=typeof e?[]:("boolean"==typeof t&&(n=t,t=!1),t||(y.createHTMLDocument?((r=(t=E.implementation.createHTMLDocument("")).createElement("base")).href=E.location.href,t.head.appendChild(r)):t=E),o=!n&&[],(i=N.exec(e))?[t.createElement(i[1])]:(i=xe([e],t,o),o&&o.length&&S(o).remove(),S.merge([],i.childNodes)));var r,i,o},S.fn.load=function(e,t,n){var r,i,o,a=this,s=e.indexOf(" ");return-1").append(S.parseHTML(e)).find(r):e)}).always(n&&function(e,t){a.each(function(){n.apply(this,o||[e.responseText,t,e])})}),this},S.expr.pseudos.animated=function(t){return S.grep(S.timers,function(e){return t===e.elem}).length},S.offset={setOffset:function(e,t,n){var r,i,o,a,s,u,l=S.css(e,"position"),c=S(e),f={};"static"===l&&(e.style.position="relative"),s=c.offset(),o=S.css(e,"top"),u=S.css(e,"left"),("absolute"===l||"fixed"===l)&&-1<(o+u).indexOf("auto")?(a=(r=c.position()).top,i=r.left):(a=parseFloat(o)||0,i=parseFloat(u)||0),m(t)&&(t=t.call(e,n,S.extend({},s))),null!=t.top&&(f.top=t.top-s.top+a),null!=t.left&&(f.left=t.left-s.left+i),"using"in t?t.using.call(e,f):("number"==typeof f.top&&(f.top+="px"),"number"==typeof f.left&&(f.left+="px"),c.css(f))}},S.fn.extend({offset:function(t){if(arguments.length)return void 0===t?this:this.each(function(e){S.offset.setOffset(this,t,e)});var e,n,r=this[0];return r?r.getClientRects().length?(e=r.getBoundingClientRect(),n=r.ownerDocument.defaultView,{top:e.top+n.pageYOffset,left:e.left+n.pageXOffset}):{top:0,left:0}:void 0},position:function(){if(this[0]){var e,t,n,r=this[0],i={top:0,left:0};if("fixed"===S.css(r,"position"))t=r.getBoundingClientRect();else{t=this.offset(),n=r.ownerDocument,e=r.offsetParent||n.documentElement;while(e&&(e===n.body||e===n.documentElement)&&"static"===S.css(e,"position"))e=e.parentNode;e&&e!==r&&1===e.nodeType&&((i=S(e).offset()).top+=S.css(e,"borderTopWidth",!0),i.left+=S.css(e,"borderLeftWidth",!0))}return{top:t.top-i.top-S.css(r,"marginTop",!0),left:t.left-i.left-S.css(r,"marginLeft",!0)}}},offsetParent:function(){return this.map(function(){var e=this.offsetParent;while(e&&"static"===S.css(e,"position"))e=e.offsetParent;return e||re})}}),S.each({scrollLeft:"pageXOffset",scrollTop:"pageYOffset"},function(t,i){var o="pageYOffset"===i;S.fn[t]=function(e){return $(this,function(e,t,n){var r;if(x(e)?r=e:9===e.nodeType&&(r=e.defaultView),void 0===n)return r?r[i]:e[t];r?r.scrollTo(o?r.pageXOffset:n,o?n:r.pageYOffset):e[t]=n},t,e,arguments.length)}}),S.each(["top","left"],function(e,n){S.cssHooks[n]=$e(y.pixelPosition,function(e,t){if(t)return t=Be(e,n),Me.test(t)?S(e).position()[n]+"px":t})}),S.each({Height:"height",Width:"width"},function(a,s){S.each({padding:"inner"+a,content:s,"":"outer"+a},function(r,o){S.fn[o]=function(e,t){var n=arguments.length&&(r||"boolean"!=typeof e),i=r||(!0===e||!0===t?"margin":"border");return $(this,function(e,t,n){var r;return x(e)?0===o.indexOf("outer")?e["inner"+a]:e.document.documentElement["client"+a]:9===e.nodeType?(r=e.documentElement,Math.max(e.body["scroll"+a],r["scroll"+a],e.body["offset"+a],r["offset"+a],r["client"+a])):void 0===n?S.css(e,t,i):S.style(e,t,n,i)},s,n?e:void 0,n)}})}),S.each(["ajaxStart","ajaxStop","ajaxComplete","ajaxError","ajaxSuccess","ajaxSend"],function(e,t){S.fn[t]=function(e){return this.on(t,e)}}),S.fn.extend({bind:function(e,t,n){return this.on(e,null,t,n)},unbind:function(e,t){return this.off(e,null,t)},delegate:function(e,t,n,r){return this.on(t,e,n,r)},undelegate:function(e,t,n){return 1===arguments.length?this.off(e,"**"):this.off(t,e||"**",n)},hover:function(e,t){return this.mouseenter(e).mouseleave(t||e)}}),S.each("blur focus focusin focusout resize scroll click dblclick mousedown mouseup mousemove mouseover mouseout mouseenter mouseleave change select submit keydown keypress keyup contextmenu".split(" "),function(e,n){S.fn[n]=function(e,t){return 0a;a++)for(s in o[a])n=o[a][s],o[a].hasOwnProperty(s)&&void 0!==n&&(e[s]=t.isPlainObject(n)?t.isPlainObject(e[s])?t.widget.extend({},e[s],n):t.widget.extend({},n):n);return e},t.widget.bridge=function(e,s){var n=s.prototype.widgetFullName||e;t.fn[e]=function(o){var a="string"==typeof o,r=i.call(arguments,1),l=this;return a?this.length||"instance"!==o?this.each(function(){var i,s=t.data(this,n);return"instance"===o?(l=s,!1):s?t.isFunction(s[o])&&"_"!==o.charAt(0)?(i=s[o].apply(s,r),i!==s&&void 0!==i?(l=i&&i.jquery?l.pushStack(i.get()):i,!1):void 0):t.error("no such method '"+o+"' for "+e+" widget instance"):t.error("cannot call methods on "+e+" prior to initialization; "+"attempted to call method '"+o+"'")}):l=void 0:(r.length&&(o=t.widget.extend.apply(null,[o].concat(r))),this.each(function(){var e=t.data(this,n);e?(e.option(o||{}),e._init&&e._init()):t.data(this,n,new s(o,this))})),l}},t.Widget=function(){},t.Widget._childConstructors=[],t.Widget.prototype={widgetName:"widget",widgetEventPrefix:"",defaultElement:"
",options:{classes:{},disabled:!1,create:null},_createWidget:function(i,s){s=t(s||this.defaultElement||this)[0],this.element=t(s),this.uuid=e++,this.eventNamespace="."+this.widgetName+this.uuid,this.bindings=t(),this.hoverable=t(),this.focusable=t(),this.classesElementLookup={},s!==this&&(t.data(s,this.widgetFullName,this),this._on(!0,this.element,{remove:function(t){t.target===s&&this.destroy()}}),this.document=t(s.style?s.ownerDocument:s.document||s),this.window=t(this.document[0].defaultView||this.document[0].parentWindow)),this.options=t.widget.extend({},this.options,this._getCreateOptions(),i),this._create(),this.options.disabled&&this._setOptionDisabled(this.options.disabled),this._trigger("create",null,this._getCreateEventData()),this._init()},_getCreateOptions:function(){return{}},_getCreateEventData:t.noop,_create:t.noop,_init:t.noop,destroy:function(){var e=this;this._destroy(),t.each(this.classesElementLookup,function(t,i){e._removeClass(i,t)}),this.element.off(this.eventNamespace).removeData(this.widgetFullName),this.widget().off(this.eventNamespace).removeAttr("aria-disabled"),this.bindings.off(this.eventNamespace)},_destroy:t.noop,widget:function(){return this.element},option:function(e,i){var s,n,o,a=e;if(0===arguments.length)return t.widget.extend({},this.options);if("string"==typeof e)if(a={},s=e.split("."),e=s.shift(),s.length){for(n=a[e]=t.widget.extend({},this.options[e]),o=0;s.length-1>o;o++)n[s[o]]=n[s[o]]||{},n=n[s[o]];if(e=s.pop(),1===arguments.length)return void 0===n[e]?null:n[e];n[e]=i}else{if(1===arguments.length)return void 0===this.options[e]?null:this.options[e];a[e]=i}return this._setOptions(a),this},_setOptions:function(t){var e;for(e in t)this._setOption(e,t[e]);return this},_setOption:function(t,e){return"classes"===t&&this._setOptionClasses(e),this.options[t]=e,"disabled"===t&&this._setOptionDisabled(e),this},_setOptionClasses:function(e){var i,s,n;for(i in e)n=this.classesElementLookup[i],e[i]!==this.options.classes[i]&&n&&n.length&&(s=t(n.get()),this._removeClass(n,i),s.addClass(this._classes({element:s,keys:i,classes:e,add:!0})))},_setOptionDisabled:function(t){this._toggleClass(this.widget(),this.widgetFullName+"-disabled",null,!!t),t&&(this._removeClass(this.hoverable,null,"ui-state-hover"),this._removeClass(this.focusable,null,"ui-state-focus"))},enable:function(){return this._setOptions({disabled:!1})},disable:function(){return this._setOptions({disabled:!0})},_classes:function(e){function i(i,o){var a,r;for(r=0;i.length>r;r++)a=n.classesElementLookup[i[r]]||t(),a=e.add?t(t.unique(a.get().concat(e.element.get()))):t(a.not(e.element).get()),n.classesElementLookup[i[r]]=a,s.push(i[r]),o&&e.classes[i[r]]&&s.push(e.classes[i[r]])}var s=[],n=this;return e=t.extend({element:this.element,classes:this.options.classes||{}},e),this._on(e.element,{remove:"_untrackClassesElement"}),e.keys&&i(e.keys.match(/\S+/g)||[],!0),e.extra&&i(e.extra.match(/\S+/g)||[]),s.join(" ")},_untrackClassesElement:function(e){var i=this;t.each(i.classesElementLookup,function(s,n){-1!==t.inArray(e.target,n)&&(i.classesElementLookup[s]=t(n.not(e.target).get()))})},_removeClass:function(t,e,i){return this._toggleClass(t,e,i,!1)},_addClass:function(t,e,i){return this._toggleClass(t,e,i,!0)},_toggleClass:function(t,e,i,s){s="boolean"==typeof s?s:i;var n="string"==typeof t||null===t,o={extra:n?e:i,keys:n?t:e,element:n?this.element:t,add:s};return o.element.toggleClass(this._classes(o),s),this},_on:function(e,i,s){var n,o=this;"boolean"!=typeof e&&(s=i,i=e,e=!1),s?(i=n=t(i),this.bindings=this.bindings.add(i)):(s=i,i=this.element,n=this.widget()),t.each(s,function(s,a){function r(){return e||o.options.disabled!==!0&&!t(this).hasClass("ui-state-disabled")?("string"==typeof a?o[a]:a).apply(o,arguments):void 0}"string"!=typeof a&&(r.guid=a.guid=a.guid||r.guid||t.guid++);var l=s.match(/^([\w:-]*)\s*(.*)$/),h=l[1]+o.eventNamespace,c=l[2];c?n.on(h,c,r):i.on(h,r)})},_off:function(e,i){i=(i||"").split(" ").join(this.eventNamespace+" ")+this.eventNamespace,e.off(i).off(i),this.bindings=t(this.bindings.not(e).get()),this.focusable=t(this.focusable.not(e).get()),this.hoverable=t(this.hoverable.not(e).get())},_delay:function(t,e){function i(){return("string"==typeof t?s[t]:t).apply(s,arguments)}var s=this;return setTimeout(i,e||0)},_hoverable:function(e){this.hoverable=this.hoverable.add(e),this._on(e,{mouseenter:function(e){this._addClass(t(e.currentTarget),null,"ui-state-hover")},mouseleave:function(e){this._removeClass(t(e.currentTarget),null,"ui-state-hover")}})},_focusable:function(e){this.focusable=this.focusable.add(e),this._on(e,{focusin:function(e){this._addClass(t(e.currentTarget),null,"ui-state-focus")},focusout:function(e){this._removeClass(t(e.currentTarget),null,"ui-state-focus")}})},_trigger:function(e,i,s){var n,o,a=this.options[e];if(s=s||{},i=t.Event(i),i.type=(e===this.widgetEventPrefix?e:this.widgetEventPrefix+e).toLowerCase(),i.target=this.element[0],o=i.originalEvent)for(n in o)n in i||(i[n]=o[n]);return this.element.trigger(i,s),!(t.isFunction(a)&&a.apply(this.element[0],[i].concat(s))===!1||i.isDefaultPrevented())}},t.each({show:"fadeIn",hide:"fadeOut"},function(e,i){t.Widget.prototype["_"+e]=function(s,n,o){"string"==typeof n&&(n={effect:n});var a,r=n?n===!0||"number"==typeof n?i:n.effect||i:e;n=n||{},"number"==typeof n&&(n={duration:n}),a=!t.isEmptyObject(n),n.complete=o,n.delay&&s.delay(n.delay),a&&t.effects&&t.effects.effect[r]?s[e](n):r!==e&&s[r]?s[r](n.duration,n.easing,o):s.queue(function(i){t(this)[e](),o&&o.call(s[0]),i()})}}),t.widget,function(){function e(t,e,i){return[parseFloat(t[0])*(u.test(t[0])?e/100:1),parseFloat(t[1])*(u.test(t[1])?i/100:1)]}function i(e,i){return parseInt(t.css(e,i),10)||0}function s(e){var i=e[0];return 9===i.nodeType?{width:e.width(),height:e.height(),offset:{top:0,left:0}}:t.isWindow(i)?{width:e.width(),height:e.height(),offset:{top:e.scrollTop(),left:e.scrollLeft()}}:i.preventDefault?{width:0,height:0,offset:{top:i.pageY,left:i.pageX}}:{width:e.outerWidth(),height:e.outerHeight(),offset:e.offset()}}var n,o=Math.max,a=Math.abs,r=/left|center|right/,l=/top|center|bottom/,h=/[\+\-]\d+(\.[\d]+)?%?/,c=/^\w+/,u=/%$/,d=t.fn.position;t.position={scrollbarWidth:function(){if(void 0!==n)return n;var e,i,s=t("
"),o=s.children()[0];return t("body").append(s),e=o.offsetWidth,s.css("overflow","scroll"),i=o.offsetWidth,e===i&&(i=s[0].clientWidth),s.remove(),n=e-i},getScrollInfo:function(e){var i=e.isWindow||e.isDocument?"":e.element.css("overflow-x"),s=e.isWindow||e.isDocument?"":e.element.css("overflow-y"),n="scroll"===i||"auto"===i&&e.widthi?"left":e>0?"right":"center",vertical:0>r?"top":s>0?"bottom":"middle"};h>p&&p>a(e+i)&&(u.horizontal="center"),c>f&&f>a(s+r)&&(u.vertical="middle"),u.important=o(a(e),a(i))>o(a(s),a(r))?"horizontal":"vertical",n.using.call(this,t,u)}),l.offset(t.extend(D,{using:r}))})},t.ui.position={fit:{left:function(t,e){var i,s=e.within,n=s.isWindow?s.scrollLeft:s.offset.left,a=s.width,r=t.left-e.collisionPosition.marginLeft,l=n-r,h=r+e.collisionWidth-a-n;e.collisionWidth>a?l>0&&0>=h?(i=t.left+l+e.collisionWidth-a-n,t.left+=l-i):t.left=h>0&&0>=l?n:l>h?n+a-e.collisionWidth:n:l>0?t.left+=l:h>0?t.left-=h:t.left=o(t.left-r,t.left)},top:function(t,e){var i,s=e.within,n=s.isWindow?s.scrollTop:s.offset.top,a=e.within.height,r=t.top-e.collisionPosition.marginTop,l=n-r,h=r+e.collisionHeight-a-n;e.collisionHeight>a?l>0&&0>=h?(i=t.top+l+e.collisionHeight-a-n,t.top+=l-i):t.top=h>0&&0>=l?n:l>h?n+a-e.collisionHeight:n:l>0?t.top+=l:h>0?t.top-=h:t.top=o(t.top-r,t.top)}},flip:{left:function(t,e){var i,s,n=e.within,o=n.offset.left+n.scrollLeft,r=n.width,l=n.isWindow?n.scrollLeft:n.offset.left,h=t.left-e.collisionPosition.marginLeft,c=h-l,u=h+e.collisionWidth-r-l,d="left"===e.my[0]?-e.elemWidth:"right"===e.my[0]?e.elemWidth:0,p="left"===e.at[0]?e.targetWidth:"right"===e.at[0]?-e.targetWidth:0,f=-2*e.offset[0];0>c?(i=t.left+d+p+f+e.collisionWidth-r-o,(0>i||a(c)>i)&&(t.left+=d+p+f)):u>0&&(s=t.left-e.collisionPosition.marginLeft+d+p+f-l,(s>0||u>a(s))&&(t.left+=d+p+f))},top:function(t,e){var i,s,n=e.within,o=n.offset.top+n.scrollTop,r=n.height,l=n.isWindow?n.scrollTop:n.offset.top,h=t.top-e.collisionPosition.marginTop,c=h-l,u=h+e.collisionHeight-r-l,d="top"===e.my[1],p=d?-e.elemHeight:"bottom"===e.my[1]?e.elemHeight:0,f="top"===e.at[1]?e.targetHeight:"bottom"===e.at[1]?-e.targetHeight:0,g=-2*e.offset[1];0>c?(s=t.top+p+f+g+e.collisionHeight-r-o,(0>s||a(c)>s)&&(t.top+=p+f+g)):u>0&&(i=t.top-e.collisionPosition.marginTop+p+f+g-l,(i>0||u>a(i))&&(t.top+=p+f+g))}},flipfit:{left:function(){t.ui.position.flip.left.apply(this,arguments),t.ui.position.fit.left.apply(this,arguments)},top:function(){t.ui.position.flip.top.apply(this,arguments),t.ui.position.fit.top.apply(this,arguments)}}}}(),t.ui.position,t.ui.keyCode={BACKSPACE:8,COMMA:188,DELETE:46,DOWN:40,END:35,ENTER:13,ESCAPE:27,HOME:36,LEFT:37,PAGE_DOWN:34,PAGE_UP:33,PERIOD:190,RIGHT:39,SPACE:32,TAB:9,UP:38},t.fn.extend({uniqueId:function(){var t=0;return function(){return this.each(function(){this.id||(this.id="ui-id-"+ ++t)})}}(),removeUniqueId:function(){return this.each(function(){/^ui-id-\d+$/.test(this.id)&&t(this).removeAttr("id")})}}),t.ui.safeActiveElement=function(t){var e;try{e=t.activeElement}catch(i){e=t.body}return e||(e=t.body),e.nodeName||(e=t.body),e},t.widget("ui.menu",{version:"1.12.1",defaultElement:"
    ",delay:300,options:{icons:{submenu:"ui-icon-caret-1-e"},items:"> *",menus:"ul",position:{my:"left top",at:"right top"},role:"menu",blur:null,focus:null,select:null},_create:function(){this.activeMenu=this.element,this.mouseHandled=!1,this.element.uniqueId().attr({role:this.options.role,tabIndex:0}),this._addClass("ui-menu","ui-widget ui-widget-content"),this._on({"mousedown .ui-menu-item":function(t){t.preventDefault()},"click .ui-menu-item":function(e){var i=t(e.target),s=t(t.ui.safeActiveElement(this.document[0]));!this.mouseHandled&&i.not(".ui-state-disabled").length&&(this.select(e),e.isPropagationStopped()||(this.mouseHandled=!0),i.has(".ui-menu").length?this.expand(e):!this.element.is(":focus")&&s.closest(".ui-menu").length&&(this.element.trigger("focus",[!0]),this.active&&1===this.active.parents(".ui-menu").length&&clearTimeout(this.timer)))},"mouseenter .ui-menu-item":function(e){if(!this.previousFilter){var i=t(e.target).closest(".ui-menu-item"),s=t(e.currentTarget);i[0]===s[0]&&(this._removeClass(s.siblings().children(".ui-state-active"),null,"ui-state-active"),this.focus(e,s))}},mouseleave:"collapseAll","mouseleave .ui-menu":"collapseAll",focus:function(t,e){var i=this.active||this.element.find(this.options.items).eq(0);e||this.focus(t,i)},blur:function(e){this._delay(function(){var i=!t.contains(this.element[0],t.ui.safeActiveElement(this.document[0]));i&&this.collapseAll(e)})},keydown:"_keydown"}),this.refresh(),this._on(this.document,{click:function(t){this._closeOnDocumentClick(t)&&this.collapseAll(t),this.mouseHandled=!1}})},_destroy:function(){var e=this.element.find(".ui-menu-item").removeAttr("role aria-disabled"),i=e.children(".ui-menu-item-wrapper").removeUniqueId().removeAttr("tabIndex role aria-haspopup");this.element.removeAttr("aria-activedescendant").find(".ui-menu").addBack().removeAttr("role aria-labelledby aria-expanded aria-hidden aria-disabled tabIndex").removeUniqueId().show(),i.children().each(function(){var e=t(this);e.data("ui-menu-submenu-caret")&&e.remove()})},_keydown:function(e){var i,s,n,o,a=!0;switch(e.keyCode){case t.ui.keyCode.PAGE_UP:this.previousPage(e);break;case t.ui.keyCode.PAGE_DOWN:this.nextPage(e);break;case t.ui.keyCode.HOME:this._move("first","first",e);break;case t.ui.keyCode.END:this._move("last","last",e);break;case t.ui.keyCode.UP:this.previous(e);break;case t.ui.keyCode.DOWN:this.next(e);break;case t.ui.keyCode.LEFT:this.collapse(e);break;case t.ui.keyCode.RIGHT:this.active&&!this.active.is(".ui-state-disabled")&&this.expand(e);break;case t.ui.keyCode.ENTER:case t.ui.keyCode.SPACE:this._activate(e);break;case t.ui.keyCode.ESCAPE:this.collapse(e);break;default:a=!1,s=this.previousFilter||"",o=!1,n=e.keyCode>=96&&105>=e.keyCode?""+(e.keyCode-96):String.fromCharCode(e.keyCode),clearTimeout(this.filterTimer),n===s?o=!0:n=s+n,i=this._filterMenuItems(n),i=o&&-1!==i.index(this.active.next())?this.active.nextAll(".ui-menu-item"):i,i.length||(n=String.fromCharCode(e.keyCode),i=this._filterMenuItems(n)),i.length?(this.focus(e,i),this.previousFilter=n,this.filterTimer=this._delay(function(){delete this.previousFilter},1e3)):delete this.previousFilter}a&&e.preventDefault()},_activate:function(t){this.active&&!this.active.is(".ui-state-disabled")&&(this.active.children("[aria-haspopup='true']").length?this.expand(t):this.select(t))},refresh:function(){var e,i,s,n,o,a=this,r=this.options.icons.submenu,l=this.element.find(this.options.menus);this._toggleClass("ui-menu-icons",null,!!this.element.find(".ui-icon").length),s=l.filter(":not(.ui-menu)").hide().attr({role:this.options.role,"aria-hidden":"true","aria-expanded":"false"}).each(function(){var e=t(this),i=e.prev(),s=t("").data("ui-menu-submenu-caret",!0);a._addClass(s,"ui-menu-icon","ui-icon "+r),i.attr("aria-haspopup","true").prepend(s),e.attr("aria-labelledby",i.attr("id"))}),this._addClass(s,"ui-menu","ui-widget ui-widget-content ui-front"),e=l.add(this.element),i=e.find(this.options.items),i.not(".ui-menu-item").each(function(){var e=t(this);a._isDivider(e)&&a._addClass(e,"ui-menu-divider","ui-widget-content")}),n=i.not(".ui-menu-item, .ui-menu-divider"),o=n.children().not(".ui-menu").uniqueId().attr({tabIndex:-1,role:this._itemRole()}),this._addClass(n,"ui-menu-item")._addClass(o,"ui-menu-item-wrapper"),i.filter(".ui-state-disabled").attr("aria-disabled","true"),this.active&&!t.contains(this.element[0],this.active[0])&&this.blur()},_itemRole:function(){return{menu:"menuitem",listbox:"option"}[this.options.role]},_setOption:function(t,e){if("icons"===t){var i=this.element.find(".ui-menu-icon");this._removeClass(i,null,this.options.icons.submenu)._addClass(i,null,e.submenu)}this._super(t,e)},_setOptionDisabled:function(t){this._super(t),this.element.attr("aria-disabled",t+""),this._toggleClass(null,"ui-state-disabled",!!t)},focus:function(t,e){var i,s,n;this.blur(t,t&&"focus"===t.type),this._scrollIntoView(e),this.active=e.first(),s=this.active.children(".ui-menu-item-wrapper"),this._addClass(s,null,"ui-state-active"),this.options.role&&this.element.attr("aria-activedescendant",s.attr("id")),n=this.active.parent().closest(".ui-menu-item").children(".ui-menu-item-wrapper"),this._addClass(n,null,"ui-state-active"),t&&"keydown"===t.type?this._close():this.timer=this._delay(function(){this._close()},this.delay),i=e.children(".ui-menu"),i.length&&t&&/^mouse/.test(t.type)&&this._startOpening(i),this.activeMenu=e.parent(),this._trigger("focus",t,{item:e})},_scrollIntoView:function(e){var i,s,n,o,a,r;this._hasScroll()&&(i=parseFloat(t.css(this.activeMenu[0],"borderTopWidth"))||0,s=parseFloat(t.css(this.activeMenu[0],"paddingTop"))||0,n=e.offset().top-this.activeMenu.offset().top-i-s,o=this.activeMenu.scrollTop(),a=this.activeMenu.height(),r=e.outerHeight(),0>n?this.activeMenu.scrollTop(o+n):n+r>a&&this.activeMenu.scrollTop(o+n-a+r))},blur:function(t,e){e||clearTimeout(this.timer),this.active&&(this._removeClass(this.active.children(".ui-menu-item-wrapper"),null,"ui-state-active"),this._trigger("blur",t,{item:this.active}),this.active=null)},_startOpening:function(t){clearTimeout(this.timer),"true"===t.attr("aria-hidden")&&(this.timer=this._delay(function(){this._close(),this._open(t)},this.delay))},_open:function(e){var i=t.extend({of:this.active},this.options.position);clearTimeout(this.timer),this.element.find(".ui-menu").not(e.parents(".ui-menu")).hide().attr("aria-hidden","true"),e.show().removeAttr("aria-hidden").attr("aria-expanded","true").position(i)},collapseAll:function(e,i){clearTimeout(this.timer),this.timer=this._delay(function(){var s=i?this.element:t(e&&e.target).closest(this.element.find(".ui-menu"));s.length||(s=this.element),this._close(s),this.blur(e),this._removeClass(s.find(".ui-state-active"),null,"ui-state-active"),this.activeMenu=s},this.delay)},_close:function(t){t||(t=this.active?this.active.parent():this.element),t.find(".ui-menu").hide().attr("aria-hidden","true").attr("aria-expanded","false")},_closeOnDocumentClick:function(e){return!t(e.target).closest(".ui-menu").length},_isDivider:function(t){return!/[^\-\u2014\u2013\s]/.test(t.text())},collapse:function(t){var e=this.active&&this.active.parent().closest(".ui-menu-item",this.element);e&&e.length&&(this._close(),this.focus(t,e))},expand:function(t){var e=this.active&&this.active.children(".ui-menu ").find(this.options.items).first();e&&e.length&&(this._open(e.parent()),this._delay(function(){this.focus(t,e)}))},next:function(t){this._move("next","first",t)},previous:function(t){this._move("prev","last",t)},isFirstItem:function(){return this.active&&!this.active.prevAll(".ui-menu-item").length},isLastItem:function(){return this.active&&!this.active.nextAll(".ui-menu-item").length},_move:function(t,e,i){var s;this.active&&(s="first"===t||"last"===t?this.active["first"===t?"prevAll":"nextAll"](".ui-menu-item").eq(-1):this.active[t+"All"](".ui-menu-item").eq(0)),s&&s.length&&this.active||(s=this.activeMenu.find(this.options.items)[e]()),this.focus(i,s)},nextPage:function(e){var i,s,n;return this.active?(this.isLastItem()||(this._hasScroll()?(s=this.active.offset().top,n=this.element.height(),this.active.nextAll(".ui-menu-item").each(function(){return i=t(this),0>i.offset().top-s-n}),this.focus(e,i)):this.focus(e,this.activeMenu.find(this.options.items)[this.active?"last":"first"]())),void 0):(this.next(e),void 0)},previousPage:function(e){var i,s,n;return this.active?(this.isFirstItem()||(this._hasScroll()?(s=this.active.offset().top,n=this.element.height(),this.active.prevAll(".ui-menu-item").each(function(){return i=t(this),i.offset().top-s+n>0}),this.focus(e,i)):this.focus(e,this.activeMenu.find(this.options.items).first())),void 0):(this.next(e),void 0)},_hasScroll:function(){return this.element.outerHeight()",options:{appendTo:null,autoFocus:!1,delay:300,minLength:1,position:{my:"left top",at:"left bottom",collision:"none"},source:null,change:null,close:null,focus:null,open:null,response:null,search:null,select:null},requestIndex:0,pending:0,_create:function(){var e,i,s,n=this.element[0].nodeName.toLowerCase(),o="textarea"===n,a="input"===n;this.isMultiLine=o||!a&&this._isContentEditable(this.element),this.valueMethod=this.element[o||a?"val":"text"],this.isNewMenu=!0,this._addClass("ui-autocomplete-input"),this.element.attr("autocomplete","off"),this._on(this.element,{keydown:function(n){if(this.element.prop("readOnly"))return e=!0,s=!0,i=!0,void 0;e=!1,s=!1,i=!1;var o=t.ui.keyCode;switch(n.keyCode){case o.PAGE_UP:e=!0,this._move("previousPage",n);break;case o.PAGE_DOWN:e=!0,this._move("nextPage",n);break;case o.UP:e=!0,this._keyEvent("previous",n);break;case o.DOWN:e=!0,this._keyEvent("next",n);break;case o.ENTER:this.menu.active&&(e=!0,n.preventDefault(),this.menu.select(n));break;case o.TAB:this.menu.active&&this.menu.select(n);break;case o.ESCAPE:this.menu.element.is(":visible")&&(this.isMultiLine||this._value(this.term),this.close(n),n.preventDefault());break;default:i=!0,this._searchTimeout(n)}},keypress:function(s){if(e)return e=!1,(!this.isMultiLine||this.menu.element.is(":visible"))&&s.preventDefault(),void 0;if(!i){var n=t.ui.keyCode;switch(s.keyCode){case n.PAGE_UP:this._move("previousPage",s);break;case n.PAGE_DOWN:this._move("nextPage",s);break;case n.UP:this._keyEvent("previous",s);break;case n.DOWN:this._keyEvent("next",s)}}},input:function(t){return s?(s=!1,t.preventDefault(),void 0):(this._searchTimeout(t),void 0)},focus:function(){this.selectedItem=null,this.previous=this._value()},blur:function(t){return this.cancelBlur?(delete this.cancelBlur,void 0):(clearTimeout(this.searching),this.close(t),this._change(t),void 0)}}),this._initSource(),this.menu=t("
      ").appendTo(this._appendTo()).menu({role:null}).hide().menu("instance"),this._addClass(this.menu.element,"ui-autocomplete","ui-front"),this._on(this.menu.element,{mousedown:function(e){e.preventDefault(),this.cancelBlur=!0,this._delay(function(){delete this.cancelBlur,this.element[0]!==t.ui.safeActiveElement(this.document[0])&&this.element.trigger("focus")})},menufocus:function(e,i){var s,n;return this.isNewMenu&&(this.isNewMenu=!1,e.originalEvent&&/^mouse/.test(e.originalEvent.type))?(this.menu.blur(),this.document.one("mousemove",function(){t(e.target).trigger(e.originalEvent)}),void 0):(n=i.item.data("ui-autocomplete-item"),!1!==this._trigger("focus",e,{item:n})&&e.originalEvent&&/^key/.test(e.originalEvent.type)&&this._value(n.value),s=i.item.attr("aria-label")||n.value,s&&t.trim(s).length&&(this.liveRegion.children().hide(),t("
      ").text(s).appendTo(this.liveRegion)),void 0)},menuselect:function(e,i){var s=i.item.data("ui-autocomplete-item"),n=this.previous;this.element[0]!==t.ui.safeActiveElement(this.document[0])&&(this.element.trigger("focus"),this.previous=n,this._delay(function(){this.previous=n,this.selectedItem=s})),!1!==this._trigger("select",e,{item:s})&&this._value(s.value),this.term=this._value(),this.close(e),this.selectedItem=s}}),this.liveRegion=t("
      ",{role:"status","aria-live":"assertive","aria-relevant":"additions"}).appendTo(this.document[0].body),this._addClass(this.liveRegion,null,"ui-helper-hidden-accessible"),this._on(this.window,{beforeunload:function(){this.element.removeAttr("autocomplete")}})},_destroy:function(){clearTimeout(this.searching),this.element.removeAttr("autocomplete"),this.menu.element.remove(),this.liveRegion.remove()},_setOption:function(t,e){this._super(t,e),"source"===t&&this._initSource(),"appendTo"===t&&this.menu.element.appendTo(this._appendTo()),"disabled"===t&&e&&this.xhr&&this.xhr.abort()},_isEventTargetInWidget:function(e){var i=this.menu.element[0];return e.target===this.element[0]||e.target===i||t.contains(i,e.target)},_closeOnClickOutside:function(t){this._isEventTargetInWidget(t)||this.close()},_appendTo:function(){var e=this.options.appendTo;return e&&(e=e.jquery||e.nodeType?t(e):this.document.find(e).eq(0)),e&&e[0]||(e=this.element.closest(".ui-front, dialog")),e.length||(e=this.document[0].body),e},_initSource:function(){var e,i,s=this;t.isArray(this.options.source)?(e=this.options.source,this.source=function(i,s){s(t.ui.autocomplete.filter(e,i.term))}):"string"==typeof this.options.source?(i=this.options.source,this.source=function(e,n){s.xhr&&s.xhr.abort(),s.xhr=t.ajax({url:i,data:e,dataType:"json",success:function(t){n(t)},error:function(){n([])}})}):this.source=this.options.source},_searchTimeout:function(t){clearTimeout(this.searching),this.searching=this._delay(function(){var e=this.term===this._value(),i=this.menu.element.is(":visible"),s=t.altKey||t.ctrlKey||t.metaKey||t.shiftKey;(!e||e&&!i&&!s)&&(this.selectedItem=null,this.search(null,t))},this.options.delay)},search:function(t,e){return t=null!=t?t:this._value(),this.term=this._value(),t.length").append(t("
      ").text(i.label)).appendTo(e)},_move:function(t,e){return this.menu.element.is(":visible")?this.menu.isFirstItem()&&/^previous/.test(t)||this.menu.isLastItem()&&/^next/.test(t)?(this.isMultiLine||this._value(this.term),this.menu.blur(),void 0):(this.menu[t](e),void 0):(this.search(null,e),void 0)},widget:function(){return this.menu.element},_value:function(){return this.valueMethod.apply(this.element,arguments)},_keyEvent:function(t,e){(!this.isMultiLine||this.menu.element.is(":visible"))&&(this._move(t,e),e.preventDefault())},_isContentEditable:function(t){if(!t.length)return!1;var e=t.prop("contentEditable");return"inherit"===e?this._isContentEditable(t.parent()):"true"===e}}),t.extend(t.ui.autocomplete,{escapeRegex:function(t){return t.replace(/[\-\[\]{}()*+?.,\\\^$|#\s]/g,"\\$&")},filter:function(e,i){var s=RegExp(t.ui.autocomplete.escapeRegex(i),"i");return t.grep(e,function(t){return s.test(t.label||t.value||t)})}}),t.widget("ui.autocomplete",t.ui.autocomplete,{options:{messages:{noResults:"No search results.",results:function(t){return t+(t>1?" results are":" result is")+" available, use up and down arrow keys to navigate."}}},__response:function(e){var i;this._superApply(arguments),this.options.disabled||this.cancelSearch||(i=e&&e.length?this.options.messages.results(e.length):this.options.messages.noResults,this.liveRegion.children().hide(),t("
      ").text(i).appendTo(this.liveRegion))}}),t.ui.autocomplete}); \ No newline at end of file diff --git a/io-vector/build/docs/javadoc/script-dir/jquery-ui.structure.min.css b/io-vector/build/docs/javadoc/script-dir/jquery-ui.structure.min.css new file mode 100644 index 0000000..e880892 --- /dev/null +++ b/io-vector/build/docs/javadoc/script-dir/jquery-ui.structure.min.css @@ -0,0 +1,5 @@ +/*! jQuery UI - v1.12.1 - 2018-12-06 +* http://jqueryui.com +* Copyright jQuery Foundation and other contributors; Licensed MIT */ + +.ui-helper-hidden{display:none}.ui-helper-hidden-accessible{border:0;clip:rect(0 0 0 0);height:1px;margin:-1px;overflow:hidden;padding:0;position:absolute;width:1px}.ui-helper-reset{margin:0;padding:0;border:0;outline:0;line-height:1.3;text-decoration:none;font-size:100%;list-style:none}.ui-helper-clearfix:before,.ui-helper-clearfix:after{content:"";display:table;border-collapse:collapse}.ui-helper-clearfix:after{clear:both}.ui-helper-zfix{width:100%;height:100%;top:0;left:0;position:absolute;opacity:0;filter:Alpha(Opacity=0)}.ui-front{z-index:100}.ui-state-disabled{cursor:default!important;pointer-events:none}.ui-icon{display:inline-block;vertical-align:middle;margin-top:-.25em;position:relative;text-indent:-99999px;overflow:hidden;background-repeat:no-repeat}.ui-widget-icon-block{left:50%;margin-left:-8px;display:block}.ui-widget-overlay{position:fixed;top:0;left:0;width:100%;height:100%}.ui-autocomplete{position:absolute;top:0;left:0;cursor:default}.ui-menu{list-style:none;padding:0;margin:0;display:block;outline:0}.ui-menu .ui-menu{position:absolute}.ui-menu .ui-menu-item{margin:0;cursor:pointer;list-style-image:url("data:image/gif;base64,R0lGODlhAQABAIAAAAAAAP///yH5BAEAAAAALAAAAAABAAEAAAIBRAA7")}.ui-menu .ui-menu-item-wrapper{position:relative;padding:3px 1em 3px .4em}.ui-menu .ui-menu-divider{margin:5px 0;height:0;font-size:0;line-height:0;border-width:1px 0 0 0}.ui-menu .ui-state-focus,.ui-menu .ui-state-active{margin:-1px}.ui-menu-icons{position:relative}.ui-menu-icons .ui-menu-item-wrapper{padding-left:2em}.ui-menu .ui-icon{position:absolute;top:0;bottom:0;left:.2em;margin:auto 0}.ui-menu .ui-menu-icon{left:auto;right:0} \ No newline at end of file diff --git a/io-vector/build/docs/javadoc/script.js b/io-vector/build/docs/javadoc/script.js new file mode 100644 index 0000000..4428408 --- /dev/null +++ b/io-vector/build/docs/javadoc/script.js @@ -0,0 +1,103 @@ +/* + * Copyright (c) 2013, 2020, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +var moduleSearchIndex; +var packageSearchIndex; +var typeSearchIndex; +var memberSearchIndex; +var tagSearchIndex; +function loadScripts(doc, tag) { + createElem(doc, tag, 'search.js'); + + createElem(doc, tag, 'module-search-index.js'); + createElem(doc, tag, 'package-search-index.js'); + createElem(doc, tag, 'type-search-index.js'); + createElem(doc, tag, 'member-search-index.js'); + createElem(doc, tag, 'tag-search-index.js'); +} + +function createElem(doc, tag, path) { + var script = doc.createElement(tag); + var scriptElement = doc.getElementsByTagName(tag)[0]; + script.src = pathtoroot + path; + scriptElement.parentNode.insertBefore(script, scriptElement); +} + +function show(type) { + count = 0; + for (var key in data) { + var row = document.getElementById(key); + if ((data[key] & type) !== 0) { + row.style.display = ''; + row.className = (count++ % 2) ? rowColor : altColor; + } + else + row.style.display = 'none'; + } + updateTabs(type); +} + +function updateTabs(type) { + var firstRow = document.getElementById(Object.keys(data)[0]); + var table = firstRow.closest('table'); + for (var value in tabs) { + var tab = document.getElementById(tabs[value][0]); + if (value == type) { + tab.className = activeTableTab; + tab.innerHTML = tabs[value][1]; + tab.setAttribute('aria-selected', true); + tab.setAttribute('tabindex',0); + table.setAttribute('aria-labelledby', tabs[value][0]); + } + else { + tab.className = tableTab; + tab.setAttribute('aria-selected', false); + tab.setAttribute('tabindex',-1); + tab.setAttribute('onclick', "show("+ value + ")"); + tab.innerHTML = tabs[value][1]; + } + } +} + +function switchTab(e) { + if (e.keyCode == 37 || e.keyCode == 38) { + $("[aria-selected=true]").prev().click().focus(); + e.preventDefault(); + } + if (e.keyCode == 39 || e.keyCode == 40) { + $("[aria-selected=true]").next().click().focus(); + e.preventDefault(); + } +} + +var updateSearchResults = function() {}; + +function indexFilesLoaded() { + return moduleSearchIndex + && packageSearchIndex + && typeSearchIndex + && memberSearchIndex + && tagSearchIndex; +} diff --git a/io-vector/build/docs/javadoc/search.js b/io-vector/build/docs/javadoc/search.js new file mode 100644 index 0000000..9d19ba1 --- /dev/null +++ b/io-vector/build/docs/javadoc/search.js @@ -0,0 +1,378 @@ +/* + * Copyright (c) 2015, 2020, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +var noResult = {l: "No results found"}; +var loading = {l: "Loading search index..."}; +var catModules = "Modules"; +var catPackages = "Packages"; +var catTypes = "Types"; +var catMembers = "Members"; +var catSearchTags = "SearchTags"; +var highlight = "$&"; +var searchPattern = ""; +var RANKING_THRESHOLD = 2; +var NO_MATCH = 0xffff; +var MAX_RESULTS_PER_CATEGORY = 500; +var UNNAMED = ""; +function escapeHtml(str) { + return str.replace(//g, ">"); +} +function getHighlightedText(item, matcher) { + var escapedItem = escapeHtml(item); + return escapedItem.replace(matcher, highlight); +} +function getURLPrefix(ui) { + var urlPrefix=""; + var slash = "/"; + if (ui.item.category === catModules) { + return ui.item.l + slash; + } else if (ui.item.category === catPackages && ui.item.m) { + return ui.item.m + slash; + } else if (ui.item.category === catTypes || ui.item.category === catMembers) { + if (ui.item.m) { + urlPrefix = ui.item.m + slash; + } else { + $.each(packageSearchIndex, function(index, item) { + if (item.m && ui.item.p === item.l) { + urlPrefix = item.m + slash; + } + }); + } + return urlPrefix; + } + return urlPrefix; +} +function makeCamelCaseRegex(term) { + var pattern = ""; + var isWordToken = false; + term.replace(/,\s*/g, ", ").trim().split(/\s+/).forEach(function(w, index) { + if (index > 0) { + // whitespace between identifiers is significant + pattern += (isWordToken && /^\w/.test(w)) ? "\\s+" : "\\s*"; + } + var tokens = w.split(/(?=[A-Z,.()<>[\/])/); + for (var i = 0; i < tokens.length; i++) { + var s = tokens[i]; + if (s === "") { + continue; + } + pattern += $.ui.autocomplete.escapeRegex(s); + isWordToken = /\w$/.test(s); + if (isWordToken) { + pattern += "([a-z0-9_$<>\\[\\]]*?)"; + } + } + }); + return pattern; +} +function createMatcher(pattern, flags) { + var isCamelCase = /[A-Z]/.test(pattern); + return new RegExp(pattern, flags + (isCamelCase ? "" : "i")); +} +var watermark = 'Search'; +$(function() { + $("#search").val(''); + $("#search").prop("disabled", false); + $("#reset").prop("disabled", false); + $("#search").val(watermark).addClass('watermark'); + $("#search").blur(function() { + if ($(this).val().length == 0) { + $(this).val(watermark).addClass('watermark'); + } + }); + $("#search").on('click keydown paste', function() { + if ($(this).val() == watermark) { + $(this).val('').removeClass('watermark'); + } + }); + $("#reset").click(function() { + $("#search").val(''); + $("#search").focus(); + }); + $("#search").focus(); + $("#search")[0].setSelectionRange(0, 0); +}); +$.widget("custom.catcomplete", $.ui.autocomplete, { + _create: function() { + this._super(); + this.widget().menu("option", "items", "> :not(.ui-autocomplete-category)"); + }, + _renderMenu: function(ul, items) { + var rMenu = this; + var currentCategory = ""; + rMenu.menu.bindings = $(); + $.each(items, function(index, item) { + var li; + if (item.category && item.category !== currentCategory) { + ul.append("
    • " + item.category + "
    • "); + currentCategory = item.category; + } + li = rMenu._renderItemData(ul, item); + if (item.category) { + li.attr("aria-label", item.category + " : " + item.l); + li.attr("class", "result-item"); + } else { + li.attr("aria-label", item.l); + li.attr("class", "result-item"); + } + }); + }, + _renderItem: function(ul, item) { + var label = ""; + var matcher = createMatcher(escapeHtml(searchPattern), "g"); + if (item.category === catModules) { + label = getHighlightedText(item.l, matcher); + } else if (item.category === catPackages) { + label = getHighlightedText(item.l, matcher); + } else if (item.category === catTypes) { + label = (item.p && item.p !== UNNAMED) + ? getHighlightedText(item.p + "." + item.l, matcher) + : getHighlightedText(item.l, matcher); + } else if (item.category === catMembers) { + label = (item.p && item.p !== UNNAMED) + ? getHighlightedText(item.p + "." + item.c + "." + item.l, matcher) + : getHighlightedText(item.c + "." + item.l, matcher); + } else if (item.category === catSearchTags) { + label = getHighlightedText(item.l, matcher); + } else { + label = item.l; + } + var li = $("
    • ").appendTo(ul); + var div = $("
      ").appendTo(li); + if (item.category === catSearchTags) { + if (item.d) { + div.html(label + " (" + item.h + ")
      " + + item.d + "
      "); + } else { + div.html(label + " (" + item.h + ")"); + } + } else { + if (item.m) { + div.html(item.m + "/" + label); + } else { + div.html(label); + } + } + return li; + } +}); +function rankMatch(match, category) { + if (!match) { + return NO_MATCH; + } + var index = match.index; + var input = match.input; + var leftBoundaryMatch = 2; + var periferalMatch = 0; + var delta = 0; + // make sure match is anchored on a left word boundary + if (index === 0 || /\W/.test(input[index - 1]) || "_" === input[index - 1] || "_" === input[index]) { + leftBoundaryMatch = 0; + } else if (input[index] === input[index].toUpperCase() && !/^[A-Z0-9_$]+$/.test(input)) { + leftBoundaryMatch = 1; + } + var matchEnd = index + match[0].length; + var leftParen = input.indexOf("("); + // exclude peripheral matches + if (category !== catModules && category !== catSearchTags) { + var endOfName = leftParen > -1 ? leftParen : input.length; + var delim = category === catPackages ? "/" : "."; + if (leftParen > -1 && leftParen < index) { + periferalMatch += 2; + } else if (input.lastIndexOf(delim, endOfName) >= matchEnd) { + periferalMatch += 2; + } + } + for (var i = 1; i < match.length; i++) { + // lower ranking if parts of the name are missing + if (match[i]) + delta += match[i].length; + } + if (category === catTypes) { + // lower ranking if a type name contains unmatched camel-case parts + if (/[A-Z]/.test(input.substring(matchEnd))) + delta += 5; + if (/[A-Z]/.test(input.substring(0, index))) + delta += 5; + } + return leftBoundaryMatch + periferalMatch + (delta / 200); + +} +function doSearch(request, response) { + var result = []; + var newResults = []; + + searchPattern = makeCamelCaseRegex(request.term); + if (searchPattern === "") { + return this.close(); + } + var camelCaseMatcher = createMatcher(searchPattern, ""); + var boundaryMatcher = createMatcher("\\b" + searchPattern, ""); + + function concatResults(a1, a2) { + a2.sort(function(e1, e2) { + return e1.ranking - e2.ranking; + }); + a1 = a1.concat(a2.map(function(e) { return e.item; })); + a2.length = 0; + return a1; + } + + if (moduleSearchIndex) { + $.each(moduleSearchIndex, function(index, item) { + item.category = catModules; + var ranking = rankMatch(boundaryMatcher.exec(item.l), catModules); + if (ranking < RANKING_THRESHOLD) { + newResults.push({ ranking: ranking, item: item }); + } + return newResults.length < MAX_RESULTS_PER_CATEGORY; + }); + result = concatResults(result, newResults); + } + if (packageSearchIndex) { + $.each(packageSearchIndex, function(index, item) { + item.category = catPackages; + var name = (item.m && request.term.indexOf("/") > -1) + ? (item.m + "/" + item.l) + : item.l; + var ranking = rankMatch(boundaryMatcher.exec(name), catPackages); + if (ranking < RANKING_THRESHOLD) { + newResults.push({ ranking: ranking, item: item }); + } + return newResults.length < MAX_RESULTS_PER_CATEGORY; + }); + result = concatResults(result, newResults); + } + if (typeSearchIndex) { + $.each(typeSearchIndex, function(index, item) { + item.category = catTypes; + var name = request.term.indexOf(".") > -1 + ? item.p + "." + item.l + : item.l; + var ranking = rankMatch(camelCaseMatcher.exec(name), catTypes); + if (ranking < RANKING_THRESHOLD) { + newResults.push({ ranking: ranking, item: item }); + } + return newResults.length < MAX_RESULTS_PER_CATEGORY; + }); + result = concatResults(result, newResults); + } + if (memberSearchIndex) { + $.each(memberSearchIndex, function(index, item) { + item.category = catMembers; + var name = request.term.indexOf(".") > -1 + ? item.p + "." + item.c + "." + item.l + : item.l; + var ranking = rankMatch(camelCaseMatcher.exec(name), catMembers); + if (ranking < RANKING_THRESHOLD) { + newResults.push({ ranking: ranking, item: item }); + } + return newResults.length < MAX_RESULTS_PER_CATEGORY; + }); + result = concatResults(result, newResults); + } + if (tagSearchIndex) { + $.each(tagSearchIndex, function(index, item) { + item.category = catSearchTags; + var ranking = rankMatch(boundaryMatcher.exec(item.l), catSearchTags); + if (ranking < RANKING_THRESHOLD) { + newResults.push({ ranking: ranking, item: item }); + } + return newResults.length < MAX_RESULTS_PER_CATEGORY; + }); + result = concatResults(result, newResults); + } + if (!indexFilesLoaded()) { + updateSearchResults = function() { + doSearch(request, response); + } + result.unshift(loading); + } else { + updateSearchResults = function() {}; + } + response(result); +} +$(function() { + $("#search").catcomplete({ + minLength: 1, + delay: 300, + source: doSearch, + response: function(event, ui) { + if (!ui.content.length) { + ui.content.push(noResult); + } else { + $("#search").empty(); + } + }, + autoFocus: true, + focus: function(event, ui) { + return false; + }, + position: { + collision: "flip" + }, + select: function(event, ui) { + if (ui.item.category) { + var url = getURLPrefix(ui); + if (ui.item.category === catModules) { + url += "module-summary.html"; + } else if (ui.item.category === catPackages) { + if (ui.item.u) { + url = ui.item.u; + } else { + url += ui.item.l.replace(/\./g, '/') + "/package-summary.html"; + } + } else if (ui.item.category === catTypes) { + if (ui.item.u) { + url = ui.item.u; + } else if (ui.item.p === UNNAMED) { + url += ui.item.l + ".html"; + } else { + url += ui.item.p.replace(/\./g, '/') + "/" + ui.item.l + ".html"; + } + } else if (ui.item.category === catMembers) { + if (ui.item.p === UNNAMED) { + url += ui.item.c + ".html" + "#"; + } else { + url += ui.item.p.replace(/\./g, '/') + "/" + ui.item.c + ".html" + "#"; + } + if (ui.item.u) { + url += ui.item.u; + } else { + url += ui.item.l; + } + } else if (ui.item.category === catSearchTags) { + url += ui.item.u; + } + if (top !== window) { + parent.classFrame.location = pathtoroot + url; + } else { + window.location.href = pathtoroot + url; + } + $("#search").focus(); + } + } + }); +}); diff --git a/io-vector/build/docs/javadoc/stylesheet.css b/io-vector/build/docs/javadoc/stylesheet.css new file mode 100644 index 0000000..79a9d97 --- /dev/null +++ b/io-vector/build/docs/javadoc/stylesheet.css @@ -0,0 +1,792 @@ +/* + * Javadoc style sheet + */ + +@import url('resources/fonts/dejavu.css'); + +/* + * Styles for individual HTML elements. + * + * These are styles that are specific to individual HTML elements. Changing them affects the style of a particular + * HTML element throughout the page. + */ + +body { + background-color:#ffffff; + color:#353833; + font-family:'DejaVu Sans', Arial, Helvetica, sans-serif; + font-size:14px; + margin:0; + padding:0; + height:100%; + width:100%; +} +iframe { + margin:0; + padding:0; + height:100%; + width:100%; + overflow-y:scroll; + border:none; +} +a:link, a:visited { + text-decoration:none; + color:#4A6782; +} +a[href]:hover, a[href]:focus { + text-decoration:none; + color:#bb7a2a; +} +a[name] { + color:#353833; +} +pre { + font-family:'DejaVu Sans Mono', monospace; + font-size:14px; +} +h1 { + font-size:20px; +} +h2 { + font-size:18px; +} +h3 { + font-size:16px; +} +h4 { + font-size:13px; +} +h5 { + font-size:12px; +} +h6 { + font-size:11px; +} +ul { + list-style-type:disc; +} +code, tt { + font-family:'DejaVu Sans Mono', monospace; + font-size:14px; + padding-top:4px; + margin-top:8px; + line-height:1.4em; +} +dt code { + font-family:'DejaVu Sans Mono', monospace; + font-size:14px; + padding-top:4px; +} +table tr td dt code { + font-family:'DejaVu Sans Mono', monospace; + font-size:14px; + vertical-align:top; + padding-top:4px; +} +sup { + font-size:8px; +} +button { + font-family: 'DejaVu Sans', Arial, Helvetica, sans-serif; + font-size: 14px; +} +/* + * Styles for HTML generated by javadoc. + * + * These are style classes that are used by the standard doclet to generate HTML documentation. + */ + +/* + * Styles for document title and copyright. + */ +.clear { + clear:both; + height:0px; + overflow:hidden; +} +.about-language { + float:right; + padding:0px 21px; + font-size:11px; + z-index:200; + margin-top:-9px; +} +.legal-copy { + margin-left:.5em; +} +.tab { + background-color:#0066FF; + color:#ffffff; + padding:8px; + width:5em; + font-weight:bold; +} +/* + * Styles for navigation bar. + */ +@media screen { + .flex-box { + position:fixed; + display:flex; + flex-direction:column; + height: 100%; + width: 100%; + } + .flex-header { + flex: 0 0 auto; + } + .flex-content { + flex: 1 1 auto; + overflow-y: auto; + } +} +.top-nav { + background-color:#4D7A97; + color:#FFFFFF; + float:left; + padding:0; + width:100%; + clear:right; + height:2.8em; + padding-top:10px; + overflow:hidden; + font-size:12px; +} +.bottom-nav { + margin-top:10px; + background-color:#4D7A97; + color:#FFFFFF; + float:left; + padding:0; + width:100%; + clear:right; + height:2.8em; + padding-top:10px; + overflow:hidden; + font-size:12px; +} +.sub-nav { + background-color:#dee3e9; + float:left; + width:100%; + overflow:hidden; + font-size:12px; +} +.sub-nav div { + clear:left; + float:left; + padding:0 0 5px 6px; + text-transform:uppercase; +} +.sub-nav .nav-list { + padding-top:5px; +} +ul.nav-list, ul.sub-nav-list { + float:left; + margin:0 25px 0 0; + padding:0; +} +ul.nav-list li{ + list-style:none; + float:left; + padding: 5px 6px; + text-transform:uppercase; +} +.sub-nav .nav-list-search { + float:right; + margin:0 0 0 0; + padding:5px 6px; + clear:none; +} +.nav-list-search label { + position:relative; + right:-16px; +} +ul.sub-nav-list li { + list-style:none; + float:left; + padding-top:10px; +} +.top-nav a:link, .top-nav a:active, .top-nav a:visited, .bottom-nav a:link, .bottom-nav a:active, .bottom-nav a:visited { + color:#FFFFFF; + text-decoration:none; + text-transform:uppercase; +} +.top-nav a:hover, .bottom-nav a:hover { + text-decoration:none; + color:#bb7a2a; + text-transform:uppercase; +} +.nav-bar-cell1-rev { + background-color:#F8981D; + color:#253441; + margin: auto 5px; +} +.skip-nav { + position:absolute; + top:auto; + left:-9999px; + overflow:hidden; +} +/* + * Hide navigation links and search box in print layout + */ +@media print { + ul.nav-list, div.sub-nav { + display:none; + } +} +/* + * Styles for page header and footer. + */ +.title { + color:#2c4557; + margin:10px 0; +} +.sub-title { + margin:5px 0 0 0; +} +.header ul { + margin:0 0 15px 0; + padding:0; +} +.header ul li, .footer ul li { + list-style:none; + font-size:13px; +} +/* + * Styles for headings. + */ +body.class-declaration-page .summary h2, +body.class-declaration-page .details h2, +body.class-use-page h2, +body.module-declaration-page .block-list h2 { + font-style: italic; + padding:0; + margin:15px 0; +} +body.class-declaration-page .summary h3, +body.class-declaration-page .details h3, +body.class-declaration-page .summary .inherited-list h2 { + background-color:#dee3e9; + border:1px solid #d0d9e0; + margin:0 0 6px -8px; + padding:7px 5px; +} +/* + * Styles for page layout containers. + */ +main { + clear:both; + padding:10px 20px; + position:relative; +} +dl.notes > dt { + font-family: 'DejaVu Sans', Arial, Helvetica, sans-serif; + font-size:12px; + font-weight:bold; + margin:10px 0 0 0; + color:#4E4E4E; +} +dl.notes > dd { + margin:5px 0 10px 0px; + font-size:14px; + font-family:'DejaVu Serif', Georgia, "Times New Roman", Times, serif; +} +dl.name-value > dt { + margin-left:1px; + font-size:1.1em; + display:inline; + font-weight:bold; +} +dl.name-value > dd { + margin:0 0 0 1px; + font-size:1.1em; + display:inline; +} +/* + * Styles for lists. + */ +li.circle { + list-style:circle; +} +ul.horizontal li { + display:inline; + font-size:0.9em; +} +div.inheritance { + margin:0; + padding:0; +} +div.inheritance div.inheritance { + margin-left:2em; +} +ul.block-list, +ul.details-list, +ul.member-list, +ul.summary-list { + margin:10px 0 10px 0; + padding:0; +} +ul.block-list > li, +ul.details-list > li, +ul.member-list > li, +ul.summary-list > li { + list-style:none; + margin-bottom:15px; + line-height:1.4; +} +table tr td dl, table tr td dl dt, table tr td dl dd { + margin-top:0; + margin-bottom:1px; +} +/* + * Styles for tables. + */ +.overview-summary table, .member-summary table, .type-summary table, .use-summary table, .constants-summary table, .deprecated-summary table, +.requires-summary table, .packages-summary table, .provides-summary table, .uses-summary table, .system-properties-summary table { + width:100%; + border-spacing:0; + border-left:1px solid #EEE; + border-right:1px solid #EEE; + border-bottom:1px solid #EEE; +} +.overview-summary table, .member-summary table, .requires-summary table, .packages-summary table, +.provides-summary table, .uses-summary table, .system-properties-summary table { + padding:0px; +} +.overview-summary caption, .member-summary caption, .type-summary caption, +.use-summary caption, .constants-summary caption, .deprecated-summary caption, +.requires-summary caption, .packages-summary caption, .provides-summary caption, +.uses-summary caption, .system-properties-summary caption { + position:relative; + text-align:left; + background-repeat:no-repeat; + color:#253441; + font-weight:bold; + clear:none; + overflow:hidden; + padding:0px; + padding-top:10px; + padding-left:1px; + margin:0px; + white-space:pre; +} +.constants-summary caption a:link, .constants-summary caption a:visited, +.use-summary caption a:link, .use-summary caption a:visited { + color:#1f389c; +} +.overview-summary caption a:link, .member-summary caption a:link, .type-summary caption a:link, +.deprecated-summary caption a:link, +.requires-summary caption a:link, .packages-summary caption a:link, .provides-summary caption a:link, +.uses-summary caption a:link, +.overview-summary caption a:hover, .member-summary caption a:hover, .type-summary caption a:hover, +.use-summary caption a:hover, .constants-summary caption a:hover, .deprecated-summary caption a:hover, +.requires-summary caption a:hover, .packages-summary caption a:hover, .provides-summary caption a:hover, +.uses-summary caption a:hover, +.overview-summary caption a:active, .member-summary caption a:active, .type-summary caption a:active, +.use-summary caption a:active, .constants-summary caption a:active, .deprecated-summary caption a:active, +.requires-summary caption a:active, .packages-summary caption a:active, .provides-summary caption a:active, +.uses-summary caption a:active, +.overview-summary caption a:visited, .member-summary caption a:visited, .type-summary caption a:visited, +.deprecated-summary caption a:visited, +.requires-summary caption a:visited, .packages-summary caption a:visited, .provides-summary caption a:visited, +.uses-summary caption a:visited { + color:#FFFFFF; +} +.overview-summary caption span, .member-summary caption span, .type-summary caption span, +.use-summary caption span, .constants-summary caption span, .deprecated-summary caption span, +.requires-summary caption span, .packages-summary caption span, .provides-summary caption span, +.uses-summary caption span, .system-properties-summary caption span { + white-space:nowrap; + padding-top:5px; + padding-left:12px; + padding-right:12px; + padding-bottom:7px; + display:inline-block; + float:left; + background-color:#F8981D; + border: none; + height:16px; +} + +div.table-tabs > button { + border: none; + cursor: pointer; + padding: 5px 12px 7px 12px; + font-weight: bold; + margin-right: 3px; +} +div.table-tabs > button.active-table-tab { + background: #F8981D; + color: #253441; +} +div.table-tabs > button.table-tab { + background: #4D7A97; + color: #FFFFFF; +} + +.row-color th, +.alt-color th { + font-weight:normal; +} +.overview-summary td, .member-summary td, .type-summary td, +.use-summary td, .constants-summary td, .deprecated-summary td, +.requires-summary td, .packages-summary td, .provides-summary td, +.uses-summary td, .system-properties-summary td { + text-align:left; + padding:0px 0px 12px 10px; +} +th.col-first, th.col-second, th.col-last, th.col-constructor-name, th.col-deprecated-item-name, .use-summary th, +.constants-summary th, .packages-summary th, td.col-first, td.col-second, td.col-last, .use-summary td, +.constants-summary td, .system-properties-summary th { + vertical-align:top; + padding-right:0px; + padding-top:8px; + padding-bottom:3px; +} +th.col-first, th.col-second, th.col-last, th.col-constructor-name, th.col-deprecated-item-name, .constants-summary th, +.packages-summary th { + background:#dee3e9; + text-align:left; + padding:8px 3px 3px 7px; +} +td.col-first, th.col-first { + font-size:13px; +} +td.col-second, th.col-second, td.col-last, th.col-constructor-name, th.col-deprecated-item-name, th.col-last { + font-size:13px; +} +.constants-summary th, .packages-summary th { + font-size:13px; +} +.provides-summary th.col-first, .provides-summary th.col-last, .provides-summary td.col-first, +.provides-summary td.col-last { + white-space:normal; + font-size:13px; +} +.overview-summary td.col-first, .overview-summary th.col-first, +.requires-summary td.col-first, .requires-summary th.col-first, +.packages-summary td.col-first, .packages-summary td.col-second, .packages-summary th.col-first, .packages-summary th, +.uses-summary td.col-first, .uses-summary th.col-first, +.provides-summary td.col-first, .provides-summary th.col-first, +.member-summary td.col-first, .member-summary th.col-first, +.member-summary td.col-second, .member-summary th.col-second, .member-summary th.col-constructor-name, +.type-summary td.col-first, .type-summary th.col-first { + vertical-align:top; +} +.packages-summary th.col-last, .packages-summary td.col-last { + white-space:normal; +} +td.col-first a:link, td.col-first a:visited, +td.col-second a:link, td.col-second a:visited, +th.col-first a:link, th.col-first a:visited, +th.col-second a:link, th.col-second a:visited, +th.col-constructor-name a:link, th.col-constructor-name a:visited, +th.col-deprecated-item-name a:link, th.col-deprecated-item-name a:visited, +.constant-values-container td a:link, .constant-values-container td a:visited, +.all-classes-container td a:link, .all-classes-container td a:visited, +.all-packages-container td a:link, .all-packages-container td a:visited { + font-weight:bold; +} +.table-sub-heading-color { + background-color:#EEEEFF; +} +.alt-color, .alt-color th { + background-color:#FFFFFF; +} +.row-color, .row-color th { + background-color:#EEEEEF; +} +/* + * Styles for contents. + */ +.description pre { + margin-top:0; +} +.deprecated-content { + margin:0; + padding:10px 0; +} +div.block { + font-size:14px; + font-family:'DejaVu Serif', Georgia, "Times New Roman", Times, serif; +} +td.col-last div { + padding-top:0px; +} +td.col-last a { + padding-bottom:3px; +} +div.member-signature { + font-family:'DejaVu Sans Mono', monospace; + font-size:14px; + margin:14px 0; + white-space: pre-wrap; +} +div.member-signature span.annotations { + white-space: pre-wrap; +} +div.member-signature span.type-parameters-long, +div.member-signature span.parameters, +div.member-signature span.exceptions { + display: inline-block; + vertical-align: top; + white-space: pre; +} +div.member-signature span.type-parameters { + white-space: normal; +} +/* + * Styles for formatting effect. + */ +.source-line-no { + color:green; + padding:0 30px 0 0; +} +h1.hidden { + visibility:hidden; + overflow:hidden; + font-size:10px; +} +.block { + display:block; + margin:0 10px 5px 0; + color:#474747; +} +.deprecated-label, .descfrm-type-label, .implementation-label, .member-name-label, .member-name-link, +.module-label-in-package, .module-label-in-type, .override-specify-label, .package-label-in-type, +.package-hierarchy-label, .type-name-label, .type-name-link, .search-tag-link { + font-weight:bold; +} +.deprecation-comment, .help-footnote, .interface-name { + font-style:italic; +} +.deprecation-block { + font-size:14px; + font-family:'DejaVu Serif', Georgia, "Times New Roman", Times, serif; + border-style:solid; + border-width:thin; + border-radius:10px; + padding:10px; + margin-bottom:10px; + margin-right:10px; + display:inline-block; +} +div.block div.deprecation-comment, div.block div.block span.emphasized-phrase, +div.block div.block span.interface-name { + font-style:normal; +} +/* + * Styles specific to HTML5 elements. + */ +main, nav, header, footer, section { + display:block; +} +/* + * Styles for javadoc search. + */ +.ui-autocomplete-category { + font-weight:bold; + font-size:15px; + padding:7px 0 7px 3px; + background-color:#4D7A97; + color:#FFFFFF; +} +.result-item { + font-size:13px; +} +.ui-autocomplete { + max-height:85%; + max-width:65%; + overflow-y:scroll; + overflow-x:scroll; + white-space:nowrap; + box-shadow: 0 3px 6px rgba(0,0,0,0.16), 0 3px 6px rgba(0,0,0,0.23); +} +ul.ui-autocomplete { + position:fixed; + z-index:999999; +} +ul.ui-autocomplete li { + float:left; + clear:both; + width:100%; +} +.result-highlight { + font-weight:bold; +} +#search { + background-image:url('resources/glass.png'); + background-size:13px; + background-repeat:no-repeat; + background-position:2px 3px; + padding-left:20px; + position:relative; + right:-18px; + width:400px; +} +#reset { + background-color: rgb(255,255,255); + background-image:url('resources/x.png'); + background-position:center; + background-repeat:no-repeat; + background-size:12px; + border:0 none; + width:16px; + height:16px; + position:relative; + left:-4px; + top:-4px; + font-size:0px; +} +.watermark { + color:#545454; +} +.search-tag-desc-result { + font-style:italic; + font-size:11px; +} +.search-tag-holder-result { + font-style:italic; + font-size:12px; +} +.search-tag-result:target { + background-color:yellow; +} +.module-graph span { + display:none; + position:absolute; +} +.module-graph:hover span { + display:block; + margin: -100px 0 0 100px; + z-index: 1; +} +.inherited-list { + margin: 10px 0 10px 0; +} +section.description { + line-height: 1.4; +} +.summary section[class$="-summary"], .details section[class$="-details"], +.class-uses .detail, .serialized-class-details { + padding: 0px 20px 5px 10px; + border: 1px solid #ededed; + background-color: #f8f8f8; +} +.inherited-list, section[class$="-details"] .detail { + padding:0 0 5px 8px; + background-color:#ffffff; + border:none; +} +.vertical-separator { + padding: 0 5px; +} +ul.help-section-list { + margin: 0; +} +/* + * Indicator icon for external links. + */ +main a[href*="://"]::after { + content:""; + display:inline-block; + background-image:url('data:image/svg+xml; utf8, \ + \ + \ + '); + background-size:100% 100%; + width:7px; + height:7px; + margin-left:2px; + margin-bottom:4px; +} +main a[href*="://"]:hover::after, +main a[href*="://"]:focus::after { + background-image:url('data:image/svg+xml; utf8, \ + \ + \ + '); +} + +/* + * Styles for user-provided tables. + * + * borderless: + * No borders, vertical margins, styled caption. + * This style is provided for use with existing doc comments. + * In general, borderless tables should not be used for layout purposes. + * + * plain: + * Plain borders around table and cells, vertical margins, styled caption. + * Best for small tables or for complex tables for tables with cells that span + * rows and columns, when the "striped" style does not work well. + * + * striped: + * Borders around the table and vertical borders between cells, striped rows, + * vertical margins, styled caption. + * Best for tables that have a header row, and a body containing a series of simple rows. + */ + +table.borderless, +table.plain, +table.striped { + margin-top: 10px; + margin-bottom: 10px; +} +table.borderless > caption, +table.plain > caption, +table.striped > caption { + font-weight: bold; + font-size: smaller; +} +table.borderless th, table.borderless td, +table.plain th, table.plain td, +table.striped th, table.striped td { + padding: 2px 5px; +} +table.borderless, +table.borderless > thead > tr > th, table.borderless > tbody > tr > th, table.borderless > tr > th, +table.borderless > thead > tr > td, table.borderless > tbody > tr > td, table.borderless > tr > td { + border: none; +} +table.borderless > thead > tr, table.borderless > tbody > tr, table.borderless > tr { + background-color: transparent; +} +table.plain { + border-collapse: collapse; + border: 1px solid black; +} +table.plain > thead > tr, table.plain > tbody tr, table.plain > tr { + background-color: transparent; +} +table.plain > thead > tr > th, table.plain > tbody > tr > th, table.plain > tr > th, +table.plain > thead > tr > td, table.plain > tbody > tr > td, table.plain > tr > td { + border: 1px solid black; +} +table.striped { + border-collapse: collapse; + border: 1px solid black; +} +table.striped > thead { + background-color: #E3E3E3; +} +table.striped > thead > tr > th, table.striped > thead > tr > td { + border: 1px solid black; +} +table.striped > tbody > tr:nth-child(even) { + background-color: #EEE +} +table.striped > tbody > tr:nth-child(odd) { + background-color: #FFF +} +table.striped > tbody > tr > th, table.striped > tbody > tr > td { + border-left: 1px solid black; + border-right: 1px solid black; +} +table.striped > tbody > tr > th { + font-weight: normal; +} diff --git a/io-vector/build/docs/javadoc/tag-search-index.js b/io-vector/build/docs/javadoc/tag-search-index.js new file mode 100644 index 0000000..0367dae --- /dev/null +++ b/io-vector/build/docs/javadoc/tag-search-index.js @@ -0,0 +1 @@ +tagSearchIndex = [];updateSearchResults(); \ No newline at end of file diff --git a/io-vector/build/docs/javadoc/type-search-index.js b/io-vector/build/docs/javadoc/type-search-index.js new file mode 100644 index 0000000..a9a7afd --- /dev/null +++ b/io-vector/build/docs/javadoc/type-search-index.js @@ -0,0 +1 @@ +typeSearchIndex = [{"p":"org.xbib.graphics.io.vector.filters","l":"AbsoluteToRelativeTransformsFilter"},{"p":"org.xbib.graphics.io.vector.commands","l":"AffineTransformCommand"},{"l":"All Classes","u":"allclasses-index.html"},{"p":"org.xbib.graphics.io.vector.util","l":"AlphaToMaskOp"},{"p":"org.xbib.graphics.io.vector.util","l":"ASCII85EncodeStream"},{"p":"org.xbib.graphics.io.vector.util","l":"Base64EncodeStream"},{"p":"org.xbib.graphics.io.vector","l":"Command"},{"p":"org.xbib.graphics.io.vector.commands","l":"CreateCommand"},{"p":"org.xbib.graphics.io.vector.util","l":"DataUtils"},{"p":"org.xbib.graphics.io.vector.commands","l":"DisposeCommand"},{"p":"org.xbib.graphics.io.vector.commands","l":"DrawImageCommand"},{"p":"org.xbib.graphics.io.vector.commands","l":"DrawShapeCommand"},{"p":"org.xbib.graphics.io.vector.commands","l":"DrawStringCommand"},{"p":"org.xbib.graphics.io.vector.eps","l":"EPSGraphics2D"},{"p":"org.xbib.graphics.io.vector.eps","l":"EPSProcessor"},{"p":"org.xbib.graphics.io.vector.eps","l":"EPSProcessorResult"},{"p":"org.xbib.graphics.io.vector.filters","l":"FillPaintedShapeAsImageFilter"},{"p":"org.xbib.graphics.io.vector.commands","l":"FillShapeCommand"},{"p":"org.xbib.graphics.io.vector.filters","l":"Filter"},{"p":"org.xbib.graphics.io.vector.util","l":"FlateEncodeStream"},{"p":"org.xbib.graphics.io.vector.util","l":"FormattingWriter"},{"p":"org.xbib.graphics.io.vector.pdf","l":"GeneratedPayload"},{"p":"org.xbib.graphics.io.vector","l":"GraphicsState"},{"p":"org.xbib.graphics.io.vector.util","l":"GraphicsUtils"},{"p":"org.xbib.graphics.io.vector.commands","l":"Group"},{"p":"org.xbib.graphics.io.vector.filters","l":"GroupingFilter"},{"p":"org.xbib.graphics.io.vector.util","l":"ImageDataStream"},{"p":"org.xbib.graphics.io.vector.util","l":"ImageDataStream.Interleaving"},{"p":"org.xbib.graphics.io.vector.util","l":"VectorHints.Key"},{"p":"org.xbib.graphics.io.vector.util","l":"LineWrapOutputStream"},{"p":"org.xbib.graphics.io.vector.filters","l":"OptimizeFilter"},{"p":"org.xbib.graphics.io.vector","l":"PageSize"},{"p":"org.xbib.graphics.io.vector.pdf","l":"Payload"},{"p":"org.xbib.graphics.io.vector.pdf","l":"PDFGraphics2D"},{"p":"org.xbib.graphics.io.vector.pdf","l":"PDFObject"},{"p":"org.xbib.graphics.io.vector.pdf","l":"PDFProcessor"},{"p":"org.xbib.graphics.io.vector.pdf","l":"PDFProcessorResult"},{"p":"org.xbib.graphics.io.vector","l":"Processor"},{"p":"org.xbib.graphics.io.vector","l":"ProcessorResult"},{"p":"org.xbib.graphics.io.vector.pdf","l":"Resources"},{"p":"org.xbib.graphics.io.vector.commands","l":"RotateCommand"},{"p":"org.xbib.graphics.io.vector.commands","l":"ScaleCommand"},{"p":"org.xbib.graphics.io.vector.commands","l":"SetBackgroundCommand"},{"p":"org.xbib.graphics.io.vector.commands","l":"SetClipCommand"},{"p":"org.xbib.graphics.io.vector.commands","l":"SetColorCommand"},{"p":"org.xbib.graphics.io.vector.commands","l":"SetCompositeCommand"},{"p":"org.xbib.graphics.io.vector.commands","l":"SetFontCommand"},{"p":"org.xbib.graphics.io.vector.commands","l":"SetHintCommand"},{"p":"org.xbib.graphics.io.vector.commands","l":"SetPaintCommand"},{"p":"org.xbib.graphics.io.vector.commands","l":"SetStrokeCommand"},{"p":"org.xbib.graphics.io.vector.commands","l":"SetTransformCommand"},{"p":"org.xbib.graphics.io.vector.commands","l":"SetXORModeCommand"},{"p":"org.xbib.graphics.io.vector.commands","l":"ShearCommand"},{"p":"org.xbib.graphics.io.vector.pdf","l":"SizePayload"},{"p":"org.xbib.graphics.io.vector.filters","l":"StateChangeGroupingFilter"},{"p":"org.xbib.graphics.io.vector.commands","l":"StateCommand"},{"p":"org.xbib.graphics.io.vector.svg","l":"SVGGraphics2D"},{"p":"org.xbib.graphics.io.vector.svg","l":"SVGProcessor"},{"p":"org.xbib.graphics.io.vector.svg","l":"SVGProcessorResult"},{"p":"org.xbib.graphics.io.vector.commands","l":"TransformCommand"},{"p":"org.xbib.graphics.io.vector.commands","l":"TranslateCommand"},{"p":"org.xbib.graphics.io.vector.util","l":"VectorHints.Value"},{"p":"org.xbib.graphics.io.vector","l":"VectorGraphics2D"},{"p":"org.xbib.graphics.io.vector","l":"VectorGraphicsFormat"},{"p":"org.xbib.graphics.io.vector.util","l":"VectorHints"}];updateSearchResults(); \ No newline at end of file diff --git a/io-vector/build/libs/io-vector-3.0.0-javadoc.jar b/io-vector/build/libs/io-vector-3.0.0-javadoc.jar new file mode 100644 index 0000000000000000000000000000000000000000..25cd4f6638e9482fd021130573c9db7a509a0840 GIT binary patch literal 261 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 literal 0 HcmV?d00001 diff --git a/io-vector/build/libs/io-vector-3.0.0.jar b/io-vector/build/libs/io-vector-3.0.0.jar new file mode 100644 index 0000000000000000000000000000000000000000..ae846a1f6109e9364fd1c482ccb752cb6767f412 GIT binary patch literal 109577 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

      5Z?czpZHH)6&9mM4(*mRP%Sairwzk3d}LL*&N zt_GM({<{v};m`OL9Ox~JFgUdurg=hF|7%mP4Z+v!rILqpUklf+==aE#hqu}89wp0- z=o!E*4>L$Cd`-^a89nf!fp!y)PGgFgVDDhhPOltIt8w|A2Q zo+KTujC}3WR4kz#F*MFwpHhk7Ff?1A#EyL-FJnO>mep|^=O_8!2wuWLWp#*&fnxS* zdGl0)bV2W__^lkt_j&0&QR$?A>1pTEguv8pvwY1=N91Jl8B;1ji2lwk6y3p=Iqijm zIQDOTSHGBMr1Vs;maSYwgt1%*u0GXvEXy}tH5pN;$`5}3FVd!Y6NgXf52)k$XKf+y zZ)WKf{t1pU)w4D+`u|#D6e&np{B(x-T7oB6)-18Z;_yPZCwl=@i+wP5h1efvxd_hej3$kn?;LQd_)-x2O!#Z`ioSlAR+GrCK`=^8`OA!Y9#%MB zue!wZ)tehz{ByQNpX>=pMG}mkBB44w&GavS@c5QbS(7P=USUxxePUBoU|vU=H&?sM z%<9~>_so>J`lWEmBL0!4j$2pgQTm9smBt89%_8_fN#D!$L1><`gum4y58X;d5`Bkc zApTup7g~ClM-*}i?nEPn84F|lnNG8_zHpGi!HIY(DX1|uXjXfoSMK~mrSLu6MRGpl z-GlRfW8m=-HCrwD&pu4yaS z6ZSqS{yN_^hy&pYN-$vesMgynT}DQA1g${>~T0CFWgv1M``s92uZ&fw%!p* zuPL}rw4q(UhG9i*7W?t>5#+NrT(?l)*F-oU`8bt8W&;)8bmaWkJBGN{JeC&m(D51B z5NEcQ%C>Ch)M9N>aa+?VmdkltGP^r-Ya{dTaKXjsSN7<5>?i3Q9}gGCIozw?x3lcg zzwGq-WUVSH7O5zpohajzIJ;L6M(x<6haoW@3act=sV-S%VybC!qdDY;#qup~L;Sr6 zUP9r(#~3HFIZInB7w1>xs`FTpZEbXjP$Dsv$DAnRq(Hv{RSU-*6?YPmuwW~xurZ9K z)XhQz_s4pmuuHFHDMqw06&o@yn5(8LUcd>uQx4#aa0!&FDEL?S1O!7A^5+wNCCf)B!N(J416lLkQd24m!xDX(UGmv5#5z>#q zE$mR2ixX(F$S7w^Vcub7M@)*z-W#w+c~S~K>~(qN4g%=Y6R9!J08BxY+1V6=`!P4U zFzU`44(3IsJDmc@WN+AEsffFQHc=mwP`W5WmA`OjD}E7b#I?K~_-E&^jDm!nPJB8p zL>JJvv?AEwiIpk+)Qfg$11$r%ur(P(HtaJ+dxU>*m+e5S3<1&pMD6 zn~F>`UGP^Ib<80wZ=a*>!-l#=$FoA)aq^-4O`W{DQOF+Psb;;x^i^s^<%R? z?MOTOjSdgiOfgV#b*aKvuhx=}uSNmgYp{1wEt*0Poq4?PaX^KdR7bZfiB#L&HX@92 zu?Hs|bTnEHfUbo6!n~TN3$I;hu4Z3Y-j{GT#&{I`tn5f`Q2C=(Ca$?rz2DxFQ+o#W zvP#TIDIJCUPPE;yASi3K{?lb8Lrg7-2IT>hYB3tJEEL0S5Z*ME!>nAHJurr?Z)Tx9 z!MHWmit4j7%g;2VmUbBV_MQLSYc_X6Dvx*z(U5ao>-!#BYfwsV5GC49N~2G)?v-$s zRzgBxill2a{b!0dPyH21j7+{I%F`ED6Ur}m5#bEO{I0*=IybUF=ZFYBfx>@dLpn^m zyY3%$xA>y4`_^3^f_FJ8HW7Txn3?)(E?IPAetQ`;YzSCOOsQC6g6z%nskISIi;bkw z%Mbx&uUKCy-UY(w&-ekEVt5h7q?6%UVkOIzB7=7%F^~9gCGq?St(7AR_3-a;hZ1fA z#&U{+(GO~|kQ7m~0?fjbNf$!}!~lPhy0pXd5lLI%=vwySxY|_P+DSZovn@2nftz(r z8K+@Y6kt$B-NZ=KM5#QIg-|JTB#vS-BK>$tjAEz~%*f^J1l2Jjmbq4`F$-~0eP!WL zW%nexM9^MUE!~Q09>IfGlX!Ft%k`_U2a!*|;M_m+4dxmo!eK>p6YAx#ulltB36tg> zv4#af7sVJjLz;*U|jjFq7jXVY5_(?QYRL^g84(K7j#(6Hp|QVuc@oxpd8=(%>9{6#2U9oJETZ{ zm#h9vxkOts>_H&7;b?ujkOYz4gBnMqakfn6aB(=L0@eOWrY)~h%;7aTM!6OG@F$PO z-tGNsHq14H}$7xJ}=X>wR#n%eytq znMP)$8!bR7mX3a0Oi$jAGH2y9J9%dL0+}@)_p^{9Y)nOFvE==a}teS(W21>NP zWVd8|OGliZBPF*caLcWYxehwV)=m_3kBu!(^yc=UtG!EjyUM z`EbPJ5|t=l2e-p*o-n9MN3baz!?vTec87nuF&7RWXRo2rAJ9zf5_NsW?0$W2`x&*M zCA9>m_>iB8CbQeJ75o^ZyW`b4?6A4I097hf{}y#u%tjCM#*{UJ#O#MOjG^s7Cc)gD zifOaF<63<~$doyv_r%;X-clNLqxVq zWXxjsA5KmYzt=jns9_dID-|6s7N>|%AT7_`Iej<+8}A5LZ3VKmsNuWV8-b~Ki(9fK z9_^@oM0T6;gmtV4Qmd^ zxj#xPIgF3UWJVuV<<6As{|yGW666_Q{2Qj0#HHTcS_w}A=G?ga5b@#lqj2{)Q!MGj zpU2R^*jHIW;{p0$g9K9Y4xwUfbzMFZB~|!v{{A5)OO$Mjv6wr-?Cgm!^^MruD7{?P z0Z)cM!R%-%F?@_+($p2Od#bLL$l+c~jpZAxn~5dIh+WSqx!y(+rAtffdN+F)iRP~u z&EN9s(Ow#3&^kQC5m9A48YA$3j98y9a==Ci5J#?WJm#0m%Ci_2q{XV}Hc}yt2|y+J zeou_Q1ep15$YL9~u&eR2Ft9KHa!q)F%LA+?_+FIvL+A4B$*2+RPk`K;E{$~ftJDh( zr9le1kL}M33TLCSy-dGIpx})&_;IEok664|F#mHr$u%trEV#wW_G0tmfr6Km#+y9} zb>P$y2PdLk;Ao=P?eZvOZL2!7aUE0;6kA1e~+8PcljuXI==i``y5TQ2(Z@un@S>p75_717T zdc>LbpG@{aV_B6orJ3T=(030NoNCD#mxMbduj2{DvZ)fB-ES0q;nZI2a> zE9Bhrgp3>3Aev*s651G$JGjImK1cC*+CTT0R}iW%E(|A4`x7z>4@)MK_UT^I3)96G zhz%5=sT{;gq&s4|C}=GVQ3+L|;TyM9j`7?{LX2K^{A9AEUHw{(q0=S}cJ;u{+ z)vt-wkn6}?Ma7^$FPY{XI4G8Is9uTp!0K(FE6x!$zVcC+V#*GRfk{W5_ zJn>0YNZBuw&d{F(ei8Jj2TH;pY9n!_I~%wJVd`BkzR~n~QC$`WKPc}1)h`D_i|+01 zCoGKnNjd-d7ZHK%Pse$MpUl>O1_O!`KOPN)uc3Hl_!z_CWPj{{eWD?EBG&-KkdRA5 zWU;U;(WcrUf48)x4&q8VBEnIz8%Vf~R$|b>Fph&mB2XpGvORd)qlq9L-dV18vP227 zpvv^qQ=8)V_hJ)cpZE6?9e@r%ia>7Hj~nrqTu2z}3>L{wYv%GdakuqyN00`$c0HFV zXxHDP@(6$+pAcdX0#K678cLtatJLR<$*l_Y6jqzoXDnn@t8*%o<&DN=+Vfas;ESHtiQE{b$k-sX9R~OYu1!p8Etps~=M&?o|gKR+zygMjwp@u+( zll1S6MXJ>&&)+cY65iUF5|D3{E2lifU6Tt)h@$Nv`Y~ZLV^aJoL@+j50RVa^^7zsbm;b%K}P~5Dg^|OyXUpEdNvz0PaS^xxiQZ7THkFih~cN#WXK55OYm#td)-3orCspwLx}M{dV(e36|?Z zXT&+IS90EJL|@jny9~F87=ydzP;l;P6sU?K$0UGr$6m0BeJ18q^w6bzL?b}n4&h1K z5MgE7TOZanALx$R^$?$VU%>Sf{*-g2F*TTFHx z(C0KCu+Ma&9Qi3kXt%GD*UpA6CWv#rql4oihA(3uEYpS*V$PcbZ8zjgQZ1E}jI-N| z*%$!jXX8$0e}yPmh?eJ&zFfp1A~jPfK^oN~EtUA)KfF?y2$a)ftB_G%*rRxcpLBsA zeC-LYb9OZiVHtZ|f?}L#5_V2q`9M|`y5=h_FEj5de9MNJYX2(Ps4NK4abwxGzWoK? z=CI=<1Y*OL6aAGyoh6ODBg8HJWzZ~}4utxZFnDbI>tp!h7Zx{K<`p{DH9Ds!E$cFa zYn{o>PVY_;PA_2MBIVCU{1scxq)-&WFVN0tg`YyaFLeJzp7qCV#jo|}Jf6Nj;KHmh zOQe*Af&6_PV$oO7;eQ-0o)bNlS*2PJg#TV~iD2T10F%9s4yZ#>*GN#Td680Y`bJ~vzG5;Iaix^oO+5g~}4Q2J* zEN%1*|5J?=q-^GSkus$+S((HX<_dY&eW3%73Kk%USdr`@fE3A#MEyh?N}}oup{uOe zq%$^Ni8WjLf<*C>IKG28O8PVqTSKAy=Da68I2dePJ})2cA$>hs%<%jQ;iw{z=3vgX zX={eKR_nn4pWzF+8VXIYAvqp^#;?2UvTwuoZ@C5Dx*UyG3}?zc8ms(ZjvUd{G6j-% zm%2@}Ayhy(kv>ytcvAW3@|7&!sSa(pBREme4^V})1D^+RV7+#s#?Dk#uM{0~W32b7B*0M9T235H82lf`-my!xaM{vK+qP}n zwymAE?aZCFZQHhO+qTW#r>eT^R)48G)?b(-Vnw_$pLxz1YlX zFpzU8!v>4uAep_yBwW71w|E}(*BM;9nJ(Sy?0MX%hgSFA*?A4Q-)YAQg0hMC;O0S~ z;u&UX0GQLbZTn{zc(VW-BgFVR?(enYrJ@z%5f@?W?$tk}`Ib&BT>^VINv-@q_w@wwvuYEvO+Fci5}cDb(1Ah#htT{3DirxDP)zB zorrk|)2Twq`ob-Z&PZ|?DJ-(pQ2&yT(m&Lg}POzv8f+J+tob#hLL-PqtIezi+17 z$ZLzk5JRUPXY!9pSkZzBs4&cG zN5rK~ZmmboJe+o!0s`g;1Pf>()&Yi*hJCl^i|BHN>1ED5N8MBj1OG|Mt>?O!4gcqq z`DTrxkwRtAx(n~0FLim3eG?&JAa#l=;rpZzi>^V0-B#;dudbEhEV`cPk|0m@ z!7BfV`@)c2G@csEwvbC0;-1Y&f5Hr@+v><8Qr4|LStj=zO+NGhp$;05R@N;<%INf# z%_AtZut;YsY&l--IvYLqs|35ZuW{%^cJE1-kT_EfYfiD{6Ukj!tY)nSWUqxa^ujv> zP8UKe=Z4|~I;H~mSBw&+VJGmRaF)jcomb3}8Bpw&dFu*|p@e^X1c~!xx~JfIz^h1N4kXT9*;I=Sdn;phwI-aNWv&WGJ8|O#LPclUK|1Ul(wBaNCH}e$ znDYa_E9nPjk#ryV}%gE1XC?|#mbv*3}KeMLQCu-%uziI6+ma_os~ z`|%3yklly2v$fwz4Py=3cG}q^5^@Myibo#o&|p1!Ez%)m;YWXWv?b6fcEUuy z6%T+pg|glu3v2CL6C|<)f0buN{6-B)6EG;UiKH)&r9|3_x4rxf{43s-i1<=yk%}F{ zf0*&~fhM0OgE3A#VVvHOL?3$BBjvKJ=Pff=5bGc@OQh^)?^sFQEpP4q z_m=%X9Z~-eFyrHYlZ*bJj;J6a_Wv&1|2G%iMt)J@UnSXYt4cx#zd%4xNOA>IFA2vt zmC=`mt!I=r&7kqU=;+YP)>%kF|3>{m{q94O`~v);7_eZ6HD~IUoaKGZ^2#~QDt$d2 ze#HmK*@yhcMK6i?$3>_8SL!`N^Ph|elR4nNL_`nj9_3grT#1!@5k{#N`s0}6MmYxG zoID5;(P1(y+HsI!E(`Op^w`ZOX8*#$@dl?E@0n(&9ABgju#C!J<$Q}v+qQJ$kWsmW zgq%l_{49IBJT)8msZP@7g>lAs0JA$p8hmuST|wTGAhIqiBMSS@u2**(rn%IZRd7jZ z?6n!`BTB<){}Ns=3z8iWU@A?e`x%yE?V61Rk2Ux_cI|ed#Jpn9s#CW_ z$*BQDY7mnt0&()U)dCeTcX6zkX|BW!^17sur-A`Nr-9Hm`?BH@c*~S~ZL)1CuTivV zhEN9tp^ua?``HsH7*vz`#HoHYUz5|_N{H%2sylu#U(^Hp7RR8C6n!gf6MlF&JvEMdXD(A;!%s`x-`T z5s6~)&a%>;qjI;w7;tc+abV(1!7$D-RWUBs3`<{icD`p5HS6%A3~?`WNj;ooCj2ac zJKT@YkAa$`)#S@92HBU1mdcbTE-3clgoIu&RmEkKTx_VrqjDTcJ*92bTvBaPIs|e$ ze-vz|JLdXWn$ra_S2=1f+x}+4taBnSh3KR3y6A=ox%B_39*nb`>>)2uJY(R=;NNYW z@!E(HI)BbwF-*<;lno(YR(~%f5>~>li|z*+sJz4^kR7 zD?)=kiZggUKxzahip+eJBJY=>-HZwHkx4mBJLDUjN!Y=>@}ui9p8cuhJ#XQMvN;u? z>b>-{TUO|36CF&68>On4eu15hbN%~!WyAW zyGQ-Hrc^>MioS+)6!QouP+TBetMC(Fc?)v>W^O@#!%**%wgR+(bZK|Q@ zg8zxJ5dmW3P|QL=lCb0kVW;d~4@yc%o8#IuOkKJmC7v9hXr_%OVQ%W3FZC7tvzd-2 zKOs{(`yKrwil1;rypEuCkrB#AU?X{1)%~)ud3rY*`Nt3VjxI-x0SG_zj}U-hh{vF% z@r#uw?P48@le+qKl&$d#%Q9nCl;-X@3jR@KO1@=_jRs6VX?HdEB+QR7YcsAOgh5Q$ z9+nA)U&$3^ddY&VF`LzFSU`WOj7=!zVOo$A$1H+kbczLHx|*53Y7At^7Xc)<=i)pK zccW5cwCU*l#7N4D3XYFFwYJpe4r5hHwi_d26fs$n43mxoT1<1*qImJjl4H3|49DOE z!+Thxrpi-G;j(mRM0xevC2ev3v$8DV2qE(pc1YzBMNCVFv{n=ASXa&RX)%t=eg&7^8RtSA}#HX=#g9(ePUdmdo4uu7pdMd>+oV{GEjyK}|?b#yP-d zi#4oK-VkiyZ{DB3Z|c!p#oT}9HaEmdRTrxo%ygUd%cPvN>z(hnyw2TRT`~;}@>+UZ z`NiQfc7OwYFN)wfVFL}wM`evwc~i%t^QVeD&Z`OX6HCuYg4UG>?VUK5VOEoWw_q^v zV2!bTwe!6j-tDjf2QL&Xz;4I}K;bFaME`6nV4}^kAd2Hg)(xHPGHSL!F;a8JkXCFllX*b}KE*%{7QDw&6Hs8ci4N0pEio47d|AR3=+C8t9 z6(dUB!)^05oA>hjo-ZOLHmW4*C3!G(nXQFj=@2b9gn*H9Mo$GN#+BiNviOhVW8$er z0#fM3aD5r@Eiw;rv#6T2(+5IW_~uVEE^Gjbzo5$i;sxU&1Z7RK4JuFW``3BOF{S}9 z@ote5O>D~~!s`q#F)WCVtEfH#JcwK_rPM2}XBm!BuYFn6`4B-lc~e(s>;vARp6Ii) zEeRHfNHP8PK#Gk5e34(6t1k-2Jw`KxCiUH^2%{&IrT}K})}0zVA<*#*!0z|%L7vmy z|Mhq;Hfi|QQZ^B359wHzAY?La1eN666I@+!2qq^kQ=7(rIw|$v$;-`Hxk)GmbA-c!&znFpl zQ(_pYsDg+7Eg6lp{_odx|Nj#6Un2>cy&=6-mY@Gc63Cs%lF=K3BCQZ;s|}C|1yD$8 z6lf9{F!~?F$qYy&Kx%3<(KWYOHLt+h=vJ1i(JmkXwfV1fM^-mCudMi3RcobEMLhiG z{EsvO!ne<_LXTUz(;TmxcC+r+V6&d}Zw5r%Q>oVkBAfWh5)4OX(wzOxy-B+#R`w(H z8qUE#n=?l_xaM$8vjz+4nv;g(!u4n#HW9>`7M69*gno+|eHmXNYJVm-1Z=c@3DSB2 z4+E;-XMKIj%oPaAl!r9M zya*WiYUX)l_twzNt=ts5F9@k5$c`rgS#EClT>r3@6gA+zA^m>9^)sbWUfLtmL9nVq z!_gNf#1yhfwVJ2N4qAXH3*l$U)TG3;G|L1sPkuPo%1-h%j#QZvdFerK*^dstbk=|Y1Iq=aLKd`MAn?M;ZiT`#b>AL)}3*b ztzr+qabkbY?d6FV1}AFNiYlml>oT}9DroX6}1!0uPz+e zfzc8(NjFaPn9K;lqXurkU~7R%3m0AoOOM69tDLa$h(j{nPS z!xh;RT7NBQ#377+7Ngeos|~k{TYBj$T)mzvT=I)7wUaY^trjy1HjNPv6^=fAAxH&$C3Z8=JQY zHKTzkrlv5{`UCP?g$*HOGBlV8c05W<3Cof`s!;n(Ez-8}+;#o_NLY~I!OF)5^+)y^ z=#x3~rl^y^ecJr2vzgG6_sI#tu_E*au+?-HO}2ZrPibgoDbBpG~c8bqa`R4tJjStd79u(zO{{`Kr^o>U!(a$HeSMw1N-gPdfr}+HHxeF z^vW-n8|1*+gfI7VOwW~q-Hj*i4JtBiw@AH~bNr`SbIZ!r4=n~a zpb)x;=GyD^okQUZQUTZ>edV96j}L)HKgr74c3BRDlpYDcCwuFS%BKv`+RdjBh`BmD zaMavYH6-ho$+%W)&&(Y_pJv?ZYJ><&KVf7&8O8$nVmwcH?i9ew2RjdT&olmq8kgL= z6{W{RzJ1Wry?l8BYb)B1C<|wJa%pVrZ`d9&Wd5m5l}kGdcD-Y#we;fY%ZxzmOlB~UFX(~+eQ~Smwq%Swit0-v58;9J*adfYinqAiedqZ8 z5VkgLJljVIJV<#!m1Nv&THLiwhjgFSwOX)}2lGs3cbLTJend`Vti*M7l+TC?2KTDz zLw6n+nY4cCiW4=J+1e^00>%%B!)~7VZZtEdXdarpDh6$}Fni+-fag;7(T5<_X4!@f z?@aPXlu*?(_?p0s5)t}k3zo=C(?@4MG*4tbo#%v#iNsUUf-4Gpca0aQeCFUe%>ymC zzwc;2=o>tlv2Ir~4n}#!u?*PChGU%lC&-xj1-OVO4GW^(#_NAknIeLt5_XjKo?a&N z-VN7N%vhj#p>61n6LCCGz%vWGL-pgygyqtwk9815XTByPNaHzpfG&~d|@-IW52MjY^kTMKQb1$OE^lsj_%sug1~%#<-CWQWoun5jL4G8{aYg95`M?NvE# z!=%fzF-gio3C#k!!MjCBJQ)vlFP6zJ8J4>j_I%N>5aB_tnxf|ZF2gr@a0@AG7OKoJ zI!DZIb(aZHn#vE~Jjkzl-_Djh3c=_S2!baL^)aXjBh8WS5Q5nz&P=gbrtnf zD`+j!rbi?U7lp`GPgL@LOLQ48g@W;r1k*iun*WFz7c7-PlSMHHxn(x>2Y=n$#T~fF z4^fD=H?$TJ#09ShF2(P-95a#mVu)c||52@HKEGfq^m`k{F?CYJ+1Kz<{@rws4|vOT z;!505E#;~57Ow}jLh>=ufWcPWeaMaB*eoTwJH^ z1TYx9m~e6sJ`)V(#c(3|en*-2ivl_25nCT%3)$TyT#lcV>zic}GF@D5ePG?*FH(kV zdKN0sH?j!`<|0(|8#c0#<)?evkHNtya<_M8&`-o-`~=PV`LWJ&SV90R5Vj$be(ekW zhZ=2#MDxnwn-o}l4E`uj@a4<#`;TwCU~QVgRy-zY%`VUwI9Xq!V$*;|*V9XZ&vm z8+*E(XazB=Ma#M zLY{I|I-NuAS&!Xt46|OFI)jh-qj-SWhgH$OQ6_&pSa`Wm%RTPT)XA@8VcEWrU!DQ& z$_prpOT0XgCOon4Q_|-pP*1@g89Y7!S30Hzrk~NHVE7433y4RHVNj-2IH!g|Oj**T z8lzN z*7Uu)0 zgNX>XZQ?7qj0zrTGML_zK=a2Xc0GiE9mERO9BGeQ{R}T~$g|ib35b)`F$4<`sHh^b zHLFszBU9ml0vnMoIS?nIOa_^VfjOu!@(B0nLz6@h=w_9yoH)y ziH88VVqH1aBw6nSn%`>dy{0BfXR1{t4=d!5wKeP**B(hkLyD#)lZHEq1rjVIWw-iq zWjRs7V%iB_^f^yb9V!teg!Z<{Ng1S=CN6oJ8I*&>Q1lb}T7|-N$XxS_F_Adhuf#?G zY+7J{yeCupQh418)cyI8Z`!iF3>X-CnIU17$iuiZ-bDt8ljcNr#yBt>m zUPv&dGg9 ?eBr?Q84DrPOgznuVi^h-F3rvXfIr)~=OEzIt-GqqJ&YnB*PPfqfrP zCqzhefKo++YO@dmk3@jhA7$ynUYv`qF&3)FIL^Wk#zUs&Ev3@63bE=iJ{S&=B}ky` zkT^v#85Jtah6%OOip3~sXP~TwDkVYhj%x4yvW9}f^08GI@Ir(gPw6jgpk`kN`$lm< zw9r-->jjW>S*6xFyzirdV1Z4 zI>+;bWv59r+8~-7dkso1A|Zx$Jy20GM1rgw4|l{jemN5&>1E4GHquqmYqDb0KXLdO z=wniu`vcki!SQ0`hw#lQP#LV2fz@mix?(amMhJp6LO9l{ZdDek3=-MFu7?J^Q1{*3 zN9xP%Yr0NK3flp9^MwdQ_8@^=Pn$qLe3Pqvi)vg=nWG7(vz>x; zxf1XO+4KU<<#abc>*#pg_d+z6&O8-outj$B)bU8)g9KG>th7&6`4#z_m!>5emuR~e zstWWGgyv~%%|x5FVVb2giftR0uMx@JSnuX@KI}jHSjMCC|1*=t~(Wf+xENx>MK1L)a1jTU7Gu~ zq*s{-T1I)G+7-Uu#+AudO`>wWt#!qGLddw>6$pOx7x|uX79;+SaPLNs@Iw+x6vprh zHLdW2#d_wy0%b7sKtZN0(}mI_E&aeoqM;uvj+N-B_Um`v9pba)mi|zJHxz&4;+3f% zm(*{sZqTXs<2`e+S$(~EBtOzVpCH~8Sq93=9&92Yf^chfUBF(XZ2OI>-B!G&CHT7Y2pN+#GiW!9|fuWwk&@-Hsh?~@kKwaLN>t1-BrS;ui)}kUE=QHiBhx_^cGS) z|FK^1SJPX#*ffs;6Q+Kuh%MtzH$2mk_F+>Xk&wdjB7RqiYXcqkEx`mX9h$@Me*k?E z_UXT!I21NGVIPMaR?mAYuSs8kdoq~t=(|4etFC8oL z!KTm=;oW?Rl{Yx;5`kNNluGy{*WyyMqtE*Qv0Xy)`g8#pWr`2#J>l?o>?;^fTE6Z%9IL-;8LcN? zg70VmU=f9R8t0599=b3LX-fEDqfkUTbCXL;E1>%Nr(@!-+GKXQM9-hZXI#q<425m5 zy=}%HbIcshl!Z{hY7%0_%wO%^8}k|}=yl^~=T>jw#X~WKfq$(JgsB>0TJxIEE z4@rLMxTR(;*`B}oq!Z4skyDNHBn;hjGH5u`Mwc}3x`kp#!*g8mDr-wu)V#vg)waD_ zex7w^hSN=(GE`5(;8N`$Nr;9?p0Qvyj6MZZuKC%*afLcuu5ovvlsP(*k5kx4w62@0 zE1g$d&2w7d-^gHYC5VKY!4k9JoL&9RRBj8|;?7ba(Y%%^gO-)ecL*`c7wU~(cB+~p zmo^sP>gd{SZ>##;MVin!ovF=KD)KhgkwR>(n8FU;_;|KuF$pv)! z^lkWXNAeQ003x`u|Lu9?G(%(DN(YH@6oW!_W(+OueL|gl)SzEo>yTc{O7N^uSm|Q} z0O#@{ptdAlBaq5YbZK9_3-7l?914*fk2gwX=h|{EeNHSbSGl#{;O!69Y`O-~3@N8(lQLRTu9PrZVxcrZ zTr>TiA_f8Ioq0c60y>Ns`k4X_rG*XCveJy|rr)xbLh$ump!N%n9{^HN99G-x@W&e`s0~fFD>jD7TYmK$C93s zMD>NOXnXsvKeUa~LADG4AWBhxW#7k{8oo|JE(f4(6_Y8lkd?`y*|nuIj*HeOqj_^P zRTgx2EOQh%wWJW~)P`#jbGsZBce)8XoDYtM=%jxxo=*1Utis8T{}=X0xDbGb6rA~t+QG!B80~Z`{w9ax;UembA+Gsk(k+O@Q7wQ(c&B6J(3JQMAjjf zBNz;|w)0gV+B|pQ2u&zEQ0TNhqvwdfxcF&>I4c7eA?};$^E4@RY>2QTXI{9#FZ?>< z2t1Q5#`BvZWWuF-HI1})NA z4LsO%D{#(wb96qx-h*1EKHvvbAoJ54gG0g;#VQX5RPD8uLl3&SwEZQp-gooQ$O5pmjpUi=PFwb4TcUlvx^4$x>eqg+P6Rc zJ{o^e!5X+SY8*Y74iuv{pO+3@8}-ay6jkgbEPFejQN();ft^%H(+Zmj3T?mFI40&} ziZRVg8&4h9*CMK@lmx8DHfGTthIa@lJeQgI!I zf~$p7WAC!}hCvTd&M8Qr>r-~#F&4}s8-cG@O}@tDzTj~p0I-bFm~ey!I7N8Kxl0G) zF&R-)a{dmEs+|DunzH(9!48tDAMEpU2dmEMt3{f3S*T~`k_-1mrss0vaqO9bTuLuO zWRqK74lZab;IpLSb95QGvQ$wIWK(ppLr4h5G25*W>t@$(X?e&C-Oye5b327F;IqdY@1OCSajp{pt-bB_K}N6HDZRyx9K$kJs7mgD zWt+BuG0B=%oSeJ*zS?=WQM`=5tkX1;fW)RQ^I1m)bX0;Kf0N{eINM=jkIl>1L?hE} zi&Oxs&q|Kxv#E3QCU~1V#3FQ$q;1zE|2}NdS$3tzIBjUPC<)0qY8xxa*}9jVKc0}d zrJX3)J%~MC6ijall(0e57Kre06O3UKKww)C*%VkbrAwXBaHaQ%SDsl{6g&0pIn96g z@XVGI$qb4m&gJ8e$~H^QwSGj zhMRMaRD*vHVj^pu;S>p-*}O}^O>WoNc2 z4p-^`mSrSvHSbC9YBz;XJuMmK$ZYC*+=Ff(&n}UDgM3oeKF7(cjS%v4o1M8T_``5r z@P|qDD42|LoZ0EB_dmqc6AP@BZcaW z>ul+{DZw_6HLnHE(35Uk1bsoC;luQ4DR%49TUi5OzfKCeJsti#tb54+wXgnrtrtN&y zKa+TcqqH%2z(3AD+4B!Fb!VPQEF(F2b^_xjE7}KD>il7flxa-l4SGAGe#$C2XTEb4 zVIFf-_UD+4?VV?+YXZgKqgym&lZq^7BbgDVZQz}^h+dPkiI0Z^w;EqL0c@>^#s{TN z4^JqeqKIg11z0;SOA^2pRNKcwe^kl;cn1moUf3x-BZ8%er1ikw^uVg^g2}54e(s0U zni;~Am88xwkS6o>Jt~A^=gpVrJ)aXTA|1n8NT>_S=N!k!?i9WEI$O**`n@iq$6H4l zT>Gs}ka<=z_rQW zT{L+D*Qh_$@BR^aoqI&M;vekl0XXXgco6jr{t?5Um#&laJ2m#iY&pZ{N&XpcB=}3d zP^lwR`^e-O2>e8rD`|Ic^2GK>^!Bd#iS9RR=g{^8onQ3#1pVB{H}z*QcM<%H)u)Wb z2W0c1KB81!g~+Ewpfp3^GaAb#-+gbZq?M?n(f_^lLoCVg@2{f|vXn#GG0it}(gEWt z*tc>L62qaV53`h{%2@50w!Zroi_$RGCps}zvE6ZdL5iid1TFHiy-#Kjt|g}i?7W*l zdPYb&$v1P7&Pc>@{3}5;NvkM5CiZiMXzGDq+Rzek(&%ETpJ--Jfw)mG$rnC(Min27 z$D!gJ_x;GCaE1=!ftb{o^bglT)I$RNOAwvz)Fx-izYw9KVx{u~cjegL;F8dbk&0=@ zd$kq7IJi#CySmUA1qiAr>h9(b_fZ0MJx~p9Af^{87B>qRGcAdeHG0E z2R{Hc*Hkfuaxvoi3}JYuA|Jryf&|KE$a2NsJZb5nbziuL7;=Z!c(y|}s6q=;Se3Uy zcWnr=#pj*+soPDtC1-Lq1nB?Rjr{C0i^8L4ozJ{y!!s9u&b*?Fvu%+8*lb<}r%^mO zRw2^ihV54Xr2;{GL|Ma)#7(_Lhes}E9gRUJ@`t4!$HJC(N3B{KqQBQBttJ}p%f>Db zI&{ADI+MjtB#JY!aVFyjq7`Y5POvGVYo7u1@WJ%(LAQ3GF%+g-=iaiey96t?yFAcy z3n^f{eOd0%0PGz6=@Tsf>`52?r3b$|c3Tjl5Qz6Ai5akWyFGY4Y)<= zX(30uGC7S>xHD(q1oq?)3Atlr!AY?oiY-j%!3$uFPa!aTa6)5sg0nf%>Bx9IBNI_@ zyEpa>DxFj2iOvy^dPH)J^LvE(NQgCJpAFE!u?r%?#%2mi?)meJw8@#(v$CnM&>&ey zJQ{(!->+!!q3`G?>@+NKhxL`4nd$ZAn?|&LoTWgcj31Wyb`{OX-bJ*_|Z#HQ(M*4vL?wJbQg_bb99nAtTy5#GO&26C|T2X`~O< z_|Y+Aq;$@@Cf}ngK*|HP`e?C0!^zT|5l;(?=}PbYhO#N>F>gBo30^WbSktqiFB2`% zRCY@0_2$H^Al-ODy=HJL8thdF`4)CwmyA47DNRvWC@Nt=F3C`)Tstc{DT+4E@6IHy zp16>0drAx?xt;`Z3st3JYpGo={Dt z@>{!QQc}{7%J#Hhh;_d~mj)h}aGAY$pQR4vm!pfU4AgJq1}@eEmDc0jNdT94G@bo1 zQLh+#0;R8R;?bBIdT;i`ZEYs%p$Z=Z>SX!Xs)}LuL}>}qs@KWv^wcLYvtRIvGdfl^ zcr{iwnzHJlRAYsu;euhM<(7qm>ip8sY4PaoK`konuCSG*4mT<)%*4#eli=A_zO!c+ zLZv{&0;1z%tCH(S9t!Ga@j=m`KS_{AaY6-dpIii`1OUdZbQH7hIJls?Bj(w}jnW{l zx|Z%?3L5lez-x8QeNox`GHdva3hT%cay1Go8hvayfMQs%-SA|j;jfETu#r5R$P=IO0P|DS61*?w};=I%{tUBehQHZ5|^AgHdQkut_w?9Ze{D zz6t(qQ<_1@5h$qSLQ)P19EYZ!v{^|Jyp%j%MuT=XKqyMVDIiB{Jx?rpMbey?{TVOI zlN|hUUn{W-;f=qRZQgtrVv8%XDgjaDfEQ-2XzBfHS@>)T^E*yQ*le-Y8&5}cZ)}-8 zG*K_~8FNn@+=J-VaijqfJ>bhEM=1R$j{1X8Pk{UZetoRp6s|YyZtw&Bn^q70E%+PP z&gkkRvo~<>l=eGvk0RqPh^JqHSQ>v*ho-_8UAO&e(j`c!p9=l?69weEwgc7nv8zgSRGKmyxzHr2N!LT#t#gm?bnfU^4 zzP$lZd0`B~Pz?HVMbCa2;6v-qt2b$&ob zJQulJDHRpMH*Pu}GEzYr-YS{k59>_OF8j?|pP3YcqgD`kSFXF>;Gu--U0K%tfRjV2)<(4&5g^;~a>8YYZSfjPb4lVBWkpTjzflRk#mq&p$_PxX$P0%V zp&?D-CqP5d`xa02PhzKT^>%p%bl3TgvwT8i$MT1>eCkDCpf~(%nDmoKCS$YYR2mmD z8KI~TJR6~Cwh_#Lhao~AV03kL{Q@lv4vrC3_)Wiq zT)$K->ZH29S?MN$U=;p}mqBWW^GIHQV2hHnZEFWo3vh|>cN>ACUgUmB4KO*4I{A}} z*uq{>H48ECJVt1Bjjc6KFJ3q~yW09t9Hl8~%hNan&}s{!=nxVQ@y=@TA~ zCp>KTAliWtR-nx%s9~ztzW;5ZNDuzOqcqm+ra2hD0QrM!#%ynaDao1(e)E@gf9N^!8gj8y;4r(7kxJVNvHQ# z9a#E{!(AtC{!#5W58?{lq^PwzIsTele+;t)s_&vR(%F*W4@!_TNg{m%L9udaep<6< z?;c;VNyduQ*%j_+0`dp3?Q008+Qr!n9Y>zp`CiudV;H?EKk)08x(?8fR|O#W6%O@Z zL4NSg`H}LE*bjLKKsr&V#VAAv30=CmSz$oymiWORO>0Q_618Vx1y+hGA~+P4A-0#i zWFBw}C{06neIOOg1&lJqc+pCDB|Z*pKkD?zyns0R1E9gRK`T;7q zIhr3JDr}dR+J~pe7{`GOO|o*`E^(KYBNBF!)N2*Df74KzSLf3Afunea(f*jn_0pY( zjo@GC?)3A5;muCfRARmM^SEhVCX{J-7d&q@&q#mUT8qWGP`YOL_kfS+{^}fk$81}z zTs^otGoz>wi$ZpV`d!I?IT1b5-}RvwBzcU4j;0cnBC*(qzgJQ0N$s*!z${!)lBfOT zK}`NX!wM+KO!)K}=Xw<}QLzQEx(fhyk4$%h@uZ{i{gfA|k9dL4p=ftn?$_`lG zd#&ewUvU5ORI-9!S$uP$-}!Y0@a-}E#vI@?t{A1@iloPsK9Yh$>h;x3h zQHXP-pZ=vKfVDrEeVBa5#uZ8tudcFeC}p2Ky|mR@luss^zIJeZl=_0+5%HZ0p&EzG zL$pjdy_B=JEHq_xUqnY-b4nmI)T~bISGeSp zGw1-?xZ|@tthWgC3pMBf;Ocy}u6TD2?Ci^9%!E!shMhvRm0C8NDx}50D^~%Hx02_n zE27x#-%)Fo^@rAQ{~pYNMsgn|RlIn4(b%;Rk;#SCb7fs1GzDVuszL>O@v346K>bwn z2W%3T1*nu=K~ppuw$()edD+|=J0yjexQJzwuXHbLyyLySPbE&c6HNknc#*Tbzkc2h zq{#|-_;aQRozW1|kY_pjkr3hXK~dm9xgPGuIl!L-v1qJE*lcTy2AQoV(BjuH6N)Ey z^zrr1i|#m=Po$?vmrHxP)(I!v#(voLnB_T0RF6*twMg*=%nnYSZ%R@>`|%M!|{jtF=0$DXYx~_F#ACR ze?7@Tp2L4wiiC{J=Kvw`Z8-%-1y#kyZAe_L<#3qxYS_I)K8C z(uk==S5w1zQ$FvvJ!us+$9$7dPlBS+x-`UN(V1IKE<_y5+fhm*;(n6!@lWSDSaX?{ z1Ih}^s^%vw9J#-S~&h#0&w+ z`(haLpIcq~*ncDFKVlBq#theKUN90adkksXK_W9kC2G=47sj1|Kn_qu#?A7op3y@F zKc7?G<%f}ah+OMEEFYs7gVRu}=9{uBbjEGpSD`O-DalJZm2`!5 zERufy{~p?Rkr$=Pe~v>v#Q&b#C*3v zNnJyPs3K!!1&gAd1e!z(Ai_kDyM$RF7zshdjkc~l&*=C}x$)|_)n?n|sB!X?^Bd#* z&i|GC4a~{$wKHuZ+>cu$AANy?+AeHEakLuloD75v8?U*Qf9CuvXD z)KsR)3%S#9h7dG#I15gdN`ak}4%-&S_ZFLzVSmKgr}m%-0!wz`YiJmckwFmN*=?yq@fm z^ku}pZf4#hZQfM+Poty=zS5mC&5YUzg&;mP9`UIe)(9^9 zysR0jHvnS8)G?75ovpE~O*CucC-sdu%BfEnWm=zcv85|WE2_jXtV?r|EWPv^Zq#b2 zngtqv2%@?@VyZ^%aX@~c1#QAD1|>IVa@fqXR(HGE8Mh6sQurLEKih zhKfDtY`rW-X~Xl79&=!(e3bZeB=nc=)ht@m`@MnA^S%ctRSH$K_PCZ%sX&HQg;hon zc>t}2+0OQ32O?vH=hPUQqibOORgSZVZesm_nnZF6jdnYkV2!L+v8x=dr6DCISaXx+ zLQp3DPYEZjF@rP7@#kl@fI44c`L@D)0N#P3J>`qG%6?6 zp)@KdonSl4b$`xR*9dqoSFl9CnpTW5;|mY)-7rrH$$K80O6*(^4BhkyZ$f zB_?REm3JWv3)Rhm*-3Z@e7AOTtuc#(#(Zq8hNhOHbcklzMrmRWf$wwu=$vNJHg~>UwSiG)sPD%~geM z8S+5nYkPKAG_dYkDE%RI+?}eW4TRFKR<>Tr&odXV;CC@sw@CIY=fvk-G}kB3v)#%$ zd%0hwoF5>d!)?)zjrK3x^PtOzTRFdKsb4G?{dN-Y0g-j@>)I{c%>BWq_mBSu*s*I5 z`sXWtT#;AY2Yz2_d*}?ef~8>|*-b%P>Ol|p+8^ZHs&Mg--M)H(JJzS&(QwzPUdz)* zVZPYH#M!|(2LN8SjKH%&#H=C3ERz>>2N=wm4@5Iu$A*E}d*r?py%G8J#yDTG@@RfN zy*Ct!AJQMfdtRJ>Z7|X1jXOlVt9z&Q-dTU>6Tv9`rC()a@v=Sg9yUwbW^W*jon`LQ zh3V?KN9a#%_Mxp9&__OjNDJ5-&JetyMIciBX`&CH+5!r)p-5HH*YB?<9-4O@onT_fBP`~hH|r{Bn9}6O z668*}1A9^}BDUo`tVe+&4fKe4)uz7a!CO#s#DiOTXZOMJ$ul()IzQG5!vD72kOvTm zjcrlP=treKImk2&&?{BkyLV0;+I(U95l-0N#PlZ2vw6xMpk|Iz5VF8q3 zbHvRf&hLZvzIFs~Sz7)+EV#*)ap54PUqmS&U20enPeY0`eWqhx<`l{$e*zWyLy~qH zH~Q!@_e|xC`+8pjYgZ5TolbTZNoz%U2x#G$m)mmq01}T2kc$SuM-1St#&UD2jQM9T zvV3)(J_cu8u6=OF7?AJR)UG`Azve^y#}RWAOh94(&*i!L&v?i9-=GcrhraWF5(iW^ z<^Ew&l>=XpCnI!#meFe0tLqbyf#_G8b@pKhr)BUmLz#)aNhiU z6&F>BpiAN~K6P%cFH`A_hZw(K&xg=`N>yj#d168f{B$4aDS>m%a1jmWodmTh#Y&AP z`fPuZK=O5HuGepa=>&1Z7f|`00Cf$|v{HJabQy>nJ5Oc+J{a-@*z7?-g=(;NTfPWq z$^#F0aTL_^wYF9^K1`u!Dx-uzL*fnRvKZm40?|dGKD+vDVn1uTae^1zAlM@qU=Be^ z<4o!4vIE^K{fINRlUedTa8~Vg24R5M;tkR|jj@Gl4?$S^=Mq`mkjH6&S{a7tZX?W} z=@D5}EFT{$Vz}BW_j3X;rWm8L^xOwy|56DqZ13snSl=)dHDa0O0|^i|(zB?8I;NS` z{g5=^O3dmWBK!)2hM#Rh4M3J&OqtTr`<1Ch2wS_Z=#3Iu!$@6lzG5i)0VR#ikav&| zO1|)679<9JBv`(%k}If$LM@=l#N&C)BUPvXhI(;vCt2<~k`^`;anU+Yd*uf_Dko|p zL+TpncP35Pz({>ATFUYVzm}VRbOMPf6Jfluz%X+g)teXWtXb#6TXL0!#;B)CDqaXy zh=7bTH~R*junT0%9Ez@#WdyAW`TPZY@)hqyyiRN?sT#l4SG_^cVDXFUw_|uvz%HEu zqc8>MbRbU|J*v-ea2NcA97>rOG?X$xtbc}{ofjHIOCyY(7_(3@V_HFIB8tk|v)s(! z<{EyYIDzDFgvrt0x6&Gcp9eT3HUPvZGJ-fKRge+{1sYS53^CatvG9?Yy-eGm{x6{W ze;{SD+KQR;e_^}&4|b>eZ^HNghDGHCB!L9rt91WO&w>fGgI@}Ada5HpQve4hyn!+u zHO;D9*Dy5|y2JVz34*&*I3VtKkMCYDy{Ngkx%m0=`~|R&jOQ0^v)>tWv5>Opved1D zUEpjt#K}&L-LZh}_Xxn;UazN6+%y5Rjy*qgS7MG$abUcb($ij!WUah z_I}iRL#}Y#Vp+#URz=KkvZj~}P zGd9VLn`D_e?E}5ZA6N_vXom&xKjhweA02%kKEw2Zz#<7lGZ{(s&^q@^QEddL#8?fg zNqQQ+2eBw}m|Yb*=08A$Kra@nL+FN=9BI@O#}Md-owO@LTX6nLxfxw#j7=_*g^XWo zR;*YpwgDT36zWP{`LvYNM>*-R4uDBLtoI(NnXPvFJsCEa)+9Rr*vtgl1Bqwldli{ z%>I38Duv_yFSs&VL-zI$jtG-PCP3ZpTdnHw||CdLD zf{oP@KfG_r>g9`}r%$b!&$(A7vWoyPUlzFMurK{se=iyh{Tv*M8UkcRe8iCn=+c41!d1-D5+g?n`pFrNTgTma9lLfPEtr8YT_l1n*!_4s;(_GjMlke z1Bu9mSQ(y#6Z`^e=JQinqtl?5r|9*h%91MEMG3`TMj~ubpa^rT|0=*AnVY0lpNy{0 zR+L{S(`+uljMiVBhA1&_;|rRnqI02-5RO~Iwvg8+xl<{y_^FY?%PtE<#DCU2wBh~D4iq!^UomIMXln(ScWi(n~x9J+_N=E(x2|_o#JMO-o ziMVAtj&g{ihgOjyHMqXEc#o3)EeQl4DFX@Hj1tBzpAlM5GLKt9f+RJOdv{rMgpRQY z8J|oFI7A&3SJ|PeNOBsOfbWx6Jc^z^(A}uhOB%D4)9f&{wQ7mGd?=Znh6pw2uw?Z+ z3`aN(UI-M>UTbD?m2hc(v>`9NAkhtv&NwTDrP6|*n~Ahf5Y3MYF=%34f~JZ2iqC$? zmF_`Op|R?KAWljs5O`lUqs}(CQ8am%QF?us?4?ky&{0|(8ATf#9RRT&wqi+z%{XUV z(6ciKp<^AT9JHr-Vq`86X02@($vkpHb6F*^|H|csE~zT0o*Y0JYH4IiOEG8gr|^_P zSN7CAlBR=$?6kCieX4k=_7`mYrCLG0ym43OMXj)2-Snx>Hr= zQgs+Huub1ar^CXgxkXdUitV~&MH4M)_jT*llu-f{{Fe`p$!WLybjNF^-OSJJ>wZ;^ zS4ddQgVCo*Tb^%i}@cO#Hr3B^2+{r^Kk7W)O z1cC@jysrrnZ#pj>b-IBf)G^)B*ja17WhJG+)sGz!Tr6Tj5C(_Q!$PdCm;t`#4oV|i z+QKHFI@v~FMhnQEc5J*F^Fq7PqDKZqd5oDGF|6jL6&UMV0UzqXY2~;E)t@l)WKO%p z`HD#&rvyW29k$d7c=4*=o@&a?qRhsquAC?;TFB;j6BF=yq3%q)WbgxBZ+!vL{Gm1N zFcQe)oV-M$I6o$3gu*8OEGfhuU{GmARZV7n?}}$r?9n3Lod4cGu_`h|z&sq@gBizY zzMhq)jLV1$8fKOJsGBjk?mrX84Eu8#JGE;TlGE3B`EWpCrv5#3(>KV1V z@!VJ;Vak}lu4*p3xtcK3Ci*8uUaPM_$pOH}opv2QJS5+FIl$bhtezViHn_Vfr@W1K z34!(CB5WgRL>}Lem}6mn2N&{?tEc3TmGf)V6zbF$s|`MWF73ftOdBca01H3*_8%dy zzLIESNnF@q08UbhJUD-WOxEz*yNKK}kh&?``f?T1Qs!VGoH};`%hogK%mLs*R@=Sv zp=8qK-5p$WD`QIOl8~Sd!;wKL9BJ*V!s@2!0@=)&qQifkbR z)xPkYOz9MOQBkQg{V61Z=`Zu|&H*B58_ozi*j&oLE9MGH09~354n}n}b&OYbkz=^@ zh3t@zYk(ZOR@heQ)b}u%w8`(^BbO#ej+r%iR5+Q4c;`%Kp{Gg7?c_IhtB4vWmYFsA zRsvMssd{0u7cDCu=bWT^%q?;;v9@_wG#%q5nde&HhJPF6M?+BY^=Md%eOfFds%R*vFv>%-o3cqxQ3mN2)|O;;1Fmc#K@3J56Tlb8O(a;87rp07LeC7soS2WE0Sg%yb4eBTRZt108>=i&Jflonsg4ARSF3DB5PPuW+Oatxo$h{HD=Sksf30SLAO<#acpncE0A+7}8Gxo4#Im?|y; zyUmSGO$aC__89Ht}l- zl`-tqXrwspk%Sy@!E7Yj@J5yTPvXS$8%s!eHRe(n(GOg9am!R$cmas>%r4sG5I{@W zKs2$4WYXlIIC;h;6lc%(CD^a+ws#S2?EDEK)V6xQmo7@r8g(8L5V0!fLsX~l-I~M7 z%$@`Zo5k2yNr}WEC9%b4;6%H}jy&Dqj*XX!axior#09{Tq)Zh`ZVd@ixT|x7hqFTP zzzz%jTzAjTb1RyL5|&ZI)D(>Gqt_jaq^50Y{~PPN7+kN6Ed}Lu2E$C>o~5edw5Ke(a^g>dnYyco{?0LbDC_;n@~;S1RLBM_e^U$*++YD zl1A6yQ34TO22U~>fJ)GBbz+G#Q*DC#yeu_lnYA_^cK(i~f5g1ed1bRpmUDQ3n(5-| zSSin9>|%p(cvCabCbQccQ~GPK%lM{1Br*b87&CXqHPjS+h+ftvAuL zzjk{+9An|MxkMQG0gIFPJ+m;dZ9G>uHLqQz{luhJ-q|#*Ukij_f8R=Rz6~#L;%vj4 zb*3YF{@qkh5L(cT+BZ&R@ze5@VAMpv-87J&eL^?51 zfP?a%Q3KP#u@o_)|3zy+pH%LUuOp2eezV^t`QF_Ak6c75YXL>>Dbxw|_| zQwJT};{)6N)`p;NPY%T^wOU!-k+olEZ$8J~+m^W06E;|?pr2KlkbDOM8RP6En~--j zQXhxMJ8IT!MKVe-RCiRGC$M7=x#RkP>)*|O&?6u3&S^J#L$ln(R}q;DUI+bq0tPtQ z!_Ify$lK=kO+<#MT|9hc;jZrW@`m}zSF$U-^C)4U>ITKu>D_n};Fg&uRaVU}{oV}; zJi6@{E67yul~>LzQ>Axk*Oq~K90c*qDg7zp_6 z$B*>ZzA%3o3BqWqE3^Z&jq$7S-#Vu4?rI7CMV-Tg*)cS*oNQOBAJ8-uYecp0f;32-vFEmkmK2Fhti477K zmw)B6(m)QDad%P<{1|WT%k3xhFdzFc@XaD@UEx<;?!MRhrxn66^BaLZWP~5nZwOE8 z3onyz|91rOuX;6xD!dnN7y5x~xcm3)$*gpD8ABa{=rhy)ODgN_acx$Fs0clFcnT{K zlVjEoesc=Mr!|On|GmBzu&A>I0I>if8fiVC#MQtaZS4ZS2CNEWud^a0NR47~gh9FV z1V!z`hGp7VCV4rPpl+};!SE4apCU}_GI%*kEHs#=kQSf=M?$?q3ubj3X%R;?*$s&^5i4U1@WGBkgTmq7v$`a-W-)qI74U5inTq}-v-WR)m zV=Wimd6NsHO-i++vvuzsm5FZ5?k)H5080`g&M{5r)3W<3d?p z)k(2#YhPevfoZb|ydu;5Y(5qr-_f0W4e=G74_&a7td}h^XpF zr6zHc$(T|6aFx*r?OG|+)XKv&RA?VCCaRucL+gB0s36oJfwd~_HO2YSl%eXE*%ayK zDb?f);88wylfZcnpi968-(+|t8V215LP5WU6u4v$l>U^IpQ%cPbqmXaie{B;O$w4L zcN=qSXa=HKpJS3{&FcCkO|4}q4+~zkm-Xd15-obY|@3ea@Ms90TxcXtB<^o)0dJj^CG)LfrW=5o?)O-Hz@W_ z5SwNRg49MrhSpq$H5Mbhx0!dL9kIGdyQKr`CEH4-ztna^Aa%&lGXwdx>l?Tcz=b}i zc&JWwB(5>AYTD@QcA3?j%jvR!Xn1j}q$)xCFu`VcfMgxxnUSyo@l^VjpD*q5;6I`T-%N)MuO=-W+B*C6tIjWcqKhW3y_RJks$|bi_U2A_ zy|qc#wb9AvQO@W9>%W{@I<>a^O-SJ~iTwGN07d$oNvYG9p-$g+0N)}%_B$&hQFcI`kjzjWWK@dzDDYtREWN7-bE z9;(zBF3V~T&JJ6^~QvXeS`3JWv|z6J@zVE3!X!RPV_5x=P*F5Sl(s{u}4ov}9+v z>R9yeMHkW9ep#cte5YY~Q?W1gs2q}0|H4-HIfgu|vACQL;tbFFxyD~IL!O4VjdBe$ z$}@&b5B-IVqJ|ww)1*pd#CS!|`dMwiAx{Gq?ac1A;`I~UvOfpr$F+#`;rX{R#%GdHCyn=CFqokst zMzr3N1E0$@TS+#K&E%L?T{?6+)%e6*QuWieBnabg8!2Gkv8B%+Dkmob5E;w) zu1mw;hD4t=L^yX@pI>yCx{#I1!#a25tY1Hpz)A;%Mw z<)`bD1mFs}N2rAcm3IxmiDD*&mu+qc1A0sYo=*tsG4!a~=ka7Id{^$h5pq#JaLYzxB zUv=j%>|*J~B6au9N~sl}Qon#6K{cz*>hHTOb*-LWkL&P*rtsS%tol4Ay9ehNf%1*P zkGi~hlM6qQDsH!@#BznB+xW9f@@3!0Q-8=>WoCW5hm`ylHmo(Ws2!rHT?&^2(BrtV zMy47O?!EO{m}oaAFl{>iPUS2-l&@$gUsRn%Wy~691=rrHX0f%CvTOnx%cq|kQm@w8 zvQ~WnL!B;6XL18U9m$;Gd1KrW&fWE7-)45 zXvi{1CVaVzxP_&FpD1qjk?Dy3MJ&koC~Q}r=Mw?4mPvq!&>DvZA+3{>Q;6#$s~Zct zqUDoEQJVW{&x~Ym`*WggO-R*CXwbuz>j$O~%>AWyDBS)5nL4H2cp^XU*@c-zS6D~y zq!Wo@QAQ9wz$2Wv)q~Xp2`q_^U3G}B5o!aF>!gZg|9zhu4~%utqbiBlZ>?z_4f|`z zG`nf)w8m#N0zuACk6Sc_7WTEIjfk6;f=RAz4e0jX8Z1(I!c5v=cmD8 znizl6*j&Ot4VP*=HH){cBiWOQ2l9lR;&ihgL%c^_t}xUW#v2(OX&7c*Nn5)IeWE(@0sXI$aq(C`r)hU!eM zvH21Sm1hdJbmNOs4>iK+_pf5i(9nTU+FI|4yZ9Pu5;z>h=1|7&oQRoz`%T;#w)(-q zkx>XMhevqTknux;!c+1I;~}V-iMfTogqN3;ymtTGdwUh0mLlPd*Ugs@c-N5j@UC+- z=G%xQ`bzI*pMXvvV%P*W(;dnh!4xXV$8Sb2tU-T>_!bUy*;&$%pF*-cK!VmIJ)0w_ z1!+`RcTvlhCE}IiFA9<+2x2Zs)N(GpJ~zQ9=v-1jArRtIf;&Akdr-Sh)()wa~jU+4i53>D*1D!?u9_Ah95G__j?3-vrehx&x0IeoshMoKz5VW)w*(& zF_lDH2~O;%o;cBuI|s4CfC!UJ*!3UzC39<#xhZDI+;een z1|ADW!RgG5J%{SACDmpcLyF(J|^BCO(#+&h;nKf@6-<86MbjTAA}^E7LQLO z?|-cBnj%y)f1;O+drC>BI#JtNGJHwgJf~jGPB`#54yf^ zjE}EN_Mf>dj$kp9Uwtr}Ga2uhUD2YI{^m=#vs;77oEM)jgLH&GKzf?H%CJRKzg(5P zKc7RnE)1AKd6gBdB_2!YCFo}8_C&_K$~#En=2R}G8W&#`9OG^XxTtr_-WIS#7<*Qv z`({YbCdTVHa{^T`)P`mh7dn91_+ksw6d9edXNVbaX3i8UiC6+(X%?opzEh4yxMfLh*jSrSA*5=^$p4l{tq666rh}`9#F^*?UnL8XY|%t z7IAu9>}5Xfw2ui(hcR^gLU9*mqn|v_4y2x_Xqvb_NNkAuUx?o ztf_$`z@X?QuogLk+PH$pA*BQp75NU=lla@KTrRO<6@TwucSA$%R^XCiQBvSi!{T`X z4i~HM1k9ZMFLx*|{n^@#=SSy4_j&m3N!gj;sD%=P#O(T^>=HFx^SHUlvo8i*}x; z%%|4k^LSF(z5cH!w-+X78{ZGS1Q-lxNTm-?rBBc59^Kl`sfMp7K3E?NY4p_GA2KoS zf3DQ`XbkXZUg%M8SW2&}c+aU3GI1aUq4o`Mi{zevN9GFSqHYB=aSH`EWTDO=&K_VR z6yc~v=$O6HbmTe-xa8&!pP0MFXXmh67XKdPbMk#l)dLoY>OqkGsh{n;=`4>VJvFOyMRwGqD`kgF@8<=J zyH0ZIB!{6VGQz=aHG0yI&Vqps*9E!t`{Sq^wDO&BGIzN*kEJYUw*YGIg48X75O%8Z zB&*F-I!FEud=+JW^8WGpL|2%;iO9?p&Fy6Yo0iCAnKYj|O^f8JuL#)_`RK_}*nuB3 z`s}DGBS0tDk!&h+Rxmivf}(Q}<@t8UMX~LTc5tgJIsLGN{F2YN{?^ZD*!`MU?FR_q z7jpI$98=Ug!0x9%mmND_&MbQi&{3c@+=07Aw73D`?ykzI_&QKYQKe9p3!LRDu~%!J zU+kv!#4VoIhopn(8%iNKaInqY=u390e0#F>5tyB&mzb)zHrcoeu>SrJ%`U^`Em=$K zA1y`)N`hNVJTAXHE{>JBy^l4xo$qr(NbP)*210_EdRpjZctALNPa|FM+f(Pw7kSd7 z%$GYP_y%C1ZsN^N!YOGmH+ z7OBpo-97bQ!_)t++e|)eQKe%nu@u)&W_DNJ>E2Cx$`IEt0}15|^j1LDo=k3V z6}9ypD>71g`i7zBRpDow}3#HD~MF#Op`Kw=x*(wpf<2t*r}J zzuTUe>;2+@ejO9x z=gX&yKs55=z51@($5J}QoXO%@9NPO*LvUm99=Ch60wg^vO{2UdM)?rEhO@*8BqqFf zo{2z0%3WF$D#4{vQOfk;T-2cq2jRKI01~K=FD-^J0^Srx4+g;GL-Nej1!daciKKbP znmiS6ZkBsS%VWv1F$ibcT=7&Vl63m+Cpz# z2`BgFe#fdtckIdb4^#ak@n+I7=*!&mTrN{tk8!iG5B6UUmYPV%hu1QP1pl%s9!ta6 zjIGtImEfT^vFWRlRDjG)5fJ}rmO69fJuB9qh&whgKPRuHu8gh$To+@uZNN7X9ri9B z%lH7gbs@T8SVp6-=H&Js}4j|Ja!#``^8JzD$42!r>HW%KoZ#nC7cU?@>dWA z?_J^+645c+rK$}t4kG8&L)S#I6^XzUt|+`^vsXkkg1we3hAA|1s*-SBK5#d73ct3s zL;L-i(8FL@)154CPC(GD9YD1VgKSJq%Q743(Op3Y>-*1em*)#xTo-krbB>{NP_Veo z;C$8!XRb?jjqU`^?grNOhP{?ZcrF%fKmLH$I)m0m!RU62=`0rZtjoL_-HDprjjZiW zd!2pZK3CgzKnJzDDDZ;XB?_ot9ViF10l^Qes#>Nj7v2mNDFf-k-FbDK(fjWj^)0u8 zv}y6;UMav46eE4$i69c*=MgQ&5)d*Mm)K5F%AS`<^Ix4g5{Wh^TFAVvnh_G~^ND#> zdJdo*Tr2wrZ9rzWM3>l;2?j8g$ITWP@@7uFVRHt|7PENczQ22~wL#&xp$+tm|3(}e zg5QwTIqc?ny(tpArYg|s!T#w9^c_%Jxb#Np9#$h-?h4^Ou{vk^AuYXg;ZCSMW$*@) zJHqlH-5m<{W|}>3k>b39UV`GfMu9BHW5h ztAf8dL;2QPYeJH~q-n~KzO-qgWu=ioykV$zoAVrL@{L{5DtQKL;$^CKqX6lTEdzc) zFG4?j_OdrAx8Emv%AIS_zht8SFq=vnsQ#e)sHOK`$7ZlB5Lq*-2wK^F`mDJKQ*p`L zanAoK;j>RR2aVp6N-0+L6A{zAK#pW2`)9N#$2F`d=gwQIpvPZ`4fDyxY~7KuZ;sAm zQ9xM$^34)u`fQO_He1cCdGw(Az=4={;L+x_i#670#5&~wzRiEnr91@JU(6%;_&)F6 zml&NiO%jJXWHT5T0E}72rc=fX$VBpq4EVZ04gUt>fXDO8FHA3pbD3hd5`cL`+O;%2 zUWt?M+fY)c#skDPUzpR$tn^LaL=Mt^9u)}vESOTN)=m6fXtCSPjG)7ZlS}9k z0wo;s!GTMO&X zEm2adn{aZ=w1l^v|2pNgJD1x@joTYol!^Ha+7weR{|?}1JKwTo5;KbTwE zYm?)-oO;u@YBA%RF`d|$D|A^2TMhDE0eQM$uy^JP+$e{C^2rOhjsvdcxmW1AV0{zp zw;?k6sN{^u7X*Kz!5RJ9$HoJO;+HeCkGDYbMbr_Y=Wpj3e{-68?(o9^*p#~<^~LnT z(<|tAtafhY8?E%7${pi3tn}`ZGuXRN^w#PP-D_i9hU&XV*&+9r_i$K&CYSq+BC5w-H$xlN*<_;9(hb#eF6i}?;+A#S&lNA5y7koLKr zejB;y2Y~C2HtvH+W{-~~$%||l#V~=QhrZy;0e>Hx4fqA|wirUOMgE~u3=D(^L%xAh zkc_7)67%u(%z%Z|f7zlQKoySeYMv;YdhIXnnWUJS>2Hn0*~G;z3xZA}Qb)(T-f%?EXDJ2lySvx9T3#980608n8~|hnq_tRp~Bos1e>@w^gIut)kB(xSdk{JKo@+ z*Yxe&AwuWr>7_@Z?@qM5e_jSRwjG%2$<7K;b(9YEoTM|y!)Xmgzmz6t73f`hCJ*1T z=uNxCX0b?zUdlpsDO^q;M;DM8pb+kOy2n6&WBf{~f51@g_$(NHBQ4)|k@w&zhVTSX z_)4JO_?(IS0AYX8F!oBpVSdGWPiD_-ezJB#1-2*k1}blkExu6j#`T;~ZVy34wkpyF zCGR~XAD<@%Un`M)B?UB+hwaGedco4a;1@Q~R97y4&*Y?|joN=|$E6BNZk3*N;BUP$ zX+?66>Tef0Wbpu%`964wqW`^rLV-cVR=$mv6it5NMmvk@yv99{u=q*H=qp?M+3bdp3^6TJTwaY0BaVLE`YLx&!~pZt5`~I1qLOtbJef}YZ;6- zJGg3JHVA1dVoR~mP{erj7eGNrdONcMl2bgFqz}GIBy5HQ@xi9(`Qg&VZRxMcHE}#! zTvV@ac&HsY=pq-7p@HVqu3~h6d}DOLY-4nE*QkZ&%Hv@>FQojH*vyi^&_DDtQbB{S&cPIBgd3-ZXVeFPCwTIbn3E#6x7BW>#d@ zL>AQW1NXnhlK)Yw{}ZVHuNk`p|BYJR#o5C8|B$=?htzmU8ac)R2LLF82LNFH|MCL? zYkPA8Wjh%ICo4Jo|DkxMOXN0zu%H7Z4vtEdvC5G^J$avymQEEKwE53OAe@8?8~Kvl}s`AwuGc zrzmQEd?%=OKeiUY*FUPcC@Cfb88QwVX_<(|L%L&;Yl#Y$kK_b-rfeagzWaL}#cDPj zRh6<}L}q4Y>Z~L;a-7NUBH8Ns`0tvQBzJ)kpfgPqgn|i|+IeisrOX`@#!qZ;kx~45 z(g-rmSi@-H&S3qE}5(XeECS?1}*P!D~xg-2`K$O?$11kSrxY z3v(t)X+yC_RrhKjdXOHKv%p9;wPdY2t5JG_o&<^kdg^e>kn*qU7ahxzuJjmdf zp&hxAgIs8FU4biu*}}&WNu){c*zHdLmxmp{agG??HK?@}t67>T^}-edDla%~JwUXP zalw!|eU?RIzRHwjfq5>(6$FP8mQb``3~;D!QFGq(FV0I5^yeWZ?4bnN6+*T7c}ZVR zb2>H~jFeFcHiDrCa3_lM!ZDeUVjY9^YLl+?nC#kpcHztUsq zHebvsN|wUEd&7dakTJSNJ>vq-r5I<)UKPLdu(sr^nGun8fxIZ+R%%wo`d5-eSST@eNbM!A*I(!X^yRD3B z$}5``HV~*|vmlvdCl4aCWCd4U!EZ+1$~_d6+5=6GTyiyi)Un=v?*ZEsKrSkdvx)U( z$LyiavcXB9nN_JV^}j))vUoYuvX>_w=Nb4Sz9+d1 zjI4`vzC(|e&ljX(s60zkOU!VWZT!h6&bAD*cuMfp8R5sCn+g`A(raA<7@NXW71(HW?}e-neBz)k^)*%>dyY)ywVz zd$fnMpYo#4%{|?;sPTHkv+M?&-?&2~w|krp!t%B~76B*=h}~uo|E9aM=_HD#JCO3{ zNl*>8S-jowp4dUUYxTU%|H$u-tHvrha@|b15N~h!+ZEY{YS(>_2Z`8+TxN_=W|9I)0A45xX7h9stSw6VcivdA##;o zw&@0k_lZSj#ku2bmT?TtqGh*FN&@HdzH!_*v3U;nNz{Xw(-9QdssusxaD2|ywuEpW zocs@guy6Wnl>JW}cygc06|}EEU1VV_s?s%f@ID~SEvy?_y> zEP;fx4^W*&X|NYUctgA0vw!;7{KB`<$`n3~9`LVF1#C;pB0o#JLcJVFTnVwg93p49 zLs*S~o6+5UmHJaG1H;z9g6oOpWL~`d^P#O?DnxqBtW@C><+Y^Bh>2=jQMzj?KV5P;O!oc4L7*i?@lN3+)>M6X2QdYV*)F?77pUdL3dlK)yceC7`1ZH3YhgtM%zi+=r zzlajiIucllUUDpE7o5KSS4a3-*h_2;t-K>t&h4EPqPy=;ZV$5A7bT?~L|Q*)Npkn*>oQa(1(xqYSAFT~|MeV*Kvz zACTs$8tIBa3=lVvRuBFL$S=%ZhewXXlg1o+JB=Z30q=3q4p7=r=;;Qas8ZYT*!NU*br*c#axn=L0v>`gFf1kmm;j#-Sb$l02>_AAACT#Sq`hxBPQT4z zu4NMk*?zKP?KH{ zHLO0~;#lv_887|f3U|sBT-EN9&6h6w{NnoR0-xffmov&JCF-!;WCSt#0wy1g2_t~h zsZg1xX*-4SK8${-{w^}2!F-+X)zV(E$N#n+Kf7F?skx?!{3D73_E&EjvxNW~%!L^$)N~@L!57;!4lL+T4Gw}fo{Iek4t0zVTd)mx zyafV96;WVWAdag6Wv?a8$+R`29g?U!G2?+rK>avhK&gxv^fk2sshT=VBV>}sK5u@Z zQX7zL!zrYPQd_6X(_RsvT>g;xzi~#;d2pr7PnYO@79h==Hi~=Sb>{1&Htx3Mj5L9{ z^A4yEDI)gCEjLaI5)syjoHGV#`bY6YiWGjC@B^|HXwKTg38iV2PFLLh+USfJ7zmYx zoFn$(E)Hj|vz~)Q9ByrG zwdZO5)Ck11BV@MP5qhW<;$g|JF5do^TizTl&!Tp%cVr}=Uzvz7thGc$jE|Bp-})1O zB05#dI-807MW`+6oVNfKZhcY(A^h^?vDs2Qx@fwhNq4GkVoUeuJHV1xf6v7P`jsE^ z2XApWJgKq6Td?hHBAeZ^&aN(QIMz4aD*X1I_R<^g^M~5^AJJ$@#Y9JGqf2S^(#7Ru zW%RmPnYdnmz*gp&L#NvjUfiFw+SJRT&Mx`bnO&u1}X6KQxsf?qxniQIbwp=P+ z5{9)rM}Bfsi*u75pJ)qy(W13oIx@u8aRj60&cRUc5R48=x#vPZ+qV5_= z9gBO|v&^DHR~b(tY#4N4aw*ojmkGy}JwyU}42_3{63CN;Z#xiYIJ5Wh#*Vhc*y3oD zA}|u+n7+MQ$17$2o;DI8gDL0hcJ*K{4~k+#N@A<+(Vi+-J}J#M3EaC%bdENE>TFWm z1iNzb%ZtI$+V3ompP%`9C|fOgAN6W5F0-E zeDO2V`-5S7+{GO_lw}eNEyK^vmF8#s%Cm1C<+paDII8*_`_W8H`PiNa4gKTJg|z}T z@8+PoT4^;9J=mMsg==7?wp?ar7;pR|x$t$_*mmsDwYc%FlO5z@F%_HX?AeQMegUJw z3JIDUK{MgJ30SnQ^)$V{6u#msyxZSjgXkQtE*ZUC9=;TIT%cLN55aAqI5rJsm9Imq ziudYp@Wv@4=SLOEP|OEo@4{a46Z>25Dkgf-N$`DbwUe%GS9*Y&%{|^+IE-?u1T(v& zQjPF%nK0RL`(oRrvVMRV!WIkX`3jy}-S?!V$^I6{8dG6ZdqA+Hkv*W+)csxA4L^J zCq*!Z<%kgoedxG48Y8t7*rJC_(1@;9C+?G*9_sOlwATWQ2{__e*jlA=rZ|7QCQv&LcwQ{A2~G@CEM=G*(zLaO}D7wX8s?=sS6;yN=%J(aTsJ?gc1VB!mUWiHgSdrB<_K7y~BqjO;1mG?BVB|iCg|I2 zEbq6DhIVFMWYj~lkS2PDyO>m;+aM-CFlHeNOIK{t!3yhLimCUgY$2+oG|hlMVXJ?L zSyeTz>%KV*B)TXuE$gDkI=Vi(s5j#Ce(3w_zot{7kA@cm&|kkQeopY5|DALyXy6E7 zWBLEeraDSWYA8DFt*->SBtsxnp*amu0%3nb@d@IDXM`CjLVq`U#B0Zu6ZRg6&W5;f zg!JCN`?Y{uOVZUwQ&!$Q)b3)cO^06|Ha++q>KkIs4I?MWaKA&s- zBDY%y#=f8{!NRelGV89~o)K%r4ZmqMHLg(PRBoIQq$pC06nCpjp#(%_ z@>r-gJApPGra&}n2}Oh+G9Bbrgf#JDdHrO>RVp1?)d}s{5wdVJUe9=M32^FuHvYbL znSSyWv5Qo}QbS`@3aP%0TmybC)E1F4&imZnsKC&T!dz@{RJW^Sneou$@i%xH7 z!+Np);+IHW>2a@qp~&@i9H|G=!7D`A&J(4Yat*r;?Nd5!G4Jh#hpamxNWo63jUIRsi^hxCB0FL zYb<33FsAC#AA%2Klc^^a`aEt~wR;E~ryWM)TpK`*C%#4rL_@hvyK#*! zcV83H_O1D5O~|>AdPmh3NXAHGFIet~6hS2X)2%vW)vr6$At_}!i4NUD_IQl$8XC*H zN4CVwlV2UbudlSQ8vEFGkJuNHvkJq+_um$)W*_@Trb&m`<;GS?} zk8<~u)&Sz#?gqWV2i!Tph`YP0nO^|fX7A6;SD6U}or9@F=O7?tXK(al2)q#db$xmY z*XR`ZfKT`)0K=r|SqFl>4I!8g8f~!lcQd{@r_#S&3BDZXK>j(hl5hy)?PY|v0Watf zG7`nwk!bLCE_Qp4JO|W+=2bj-cE%gRA;d4y&|4D5EJH`x_yYyaVUnR7W%+{W*%+ZZ zRH*63UII;Sxn}*sSrl%R=by!w+p1ARRyJ+>x@#ZjEdQge13E$Np#34K`kXX?oM* zNnC^drFzH733|YHI)7=MyYUHTVsnyJD!A&p0G-%(-qNgjA1g(i;n6Ebz+DSl%$64E z8g956Ade<`Iq?P9 zJG`}-8bUbvFji3rkr1qr1TP~VrYx)Q9;}ius+{Hlmy=R=C_7~^P82!0bKS~LP3X7~ z8*~e|*U5nUL^Y-!K|KpN<|bW-SidZOTdgG_I8x7(v0^sj>|;XX+<8F2@K1FQA<-}# z0)3-P=4;D?q%?E#uT+~E+ziVDH6spKoWj$dP_pA>7!UUP-ryHHy+%j0n+L^M+=B7; zV`Mnp`N)$k<;YmqK!}t*=C~+6=IH4Vs6V==J*N|_5Yb$Xe_{kx=4=aHL^<<%X95#E zm?&%HD?{|iZ}=*Ewa8EL;9$N{klVhNtQ~zVnPhb2P>YlY!eEY0I`xNwNgCv7REZ{- z)!Cwm&Feuv64nAQhFIOWkWB|9N(!76pEAEg#e zr%@F?F9bE}8?q>2Wqh_Kv3yQqz-V1o3+c&6|Hb|e>0?0p*;FeFOG|1uYh*%Y z%jz`li4vceCRgSHdE}Ful{)*g@Pz$Vp){{ao!r9H3xs#oK@A&Cq<33|kguzkdd$=L zXsBCLP#Gs)u7dURD7?Q3-Bv-$iB<;IEv`qrmS4?r7RfJKC4g*E#hbi(g(Nyq_~noA z@lG;x@7f&5`UV0(YITYP4Ws?t1q#Ib^Fsyv5SV_i)T=Zett-DH(Kg&e#@o%GXXRjsw*LzQ~L$sA&d4S|No$FO{dMX7pNB8lAy9Dcf)Xo>Wvg z;i|rn23Kx6bKeEw#zd_YyrCtvbAw?`3x`u8XS!oFT;ZTbjT&C&T?M#bX%s34ixE~S zbSu3Xhvei$D|6xQ>V;c8G(zI535U*WVYGl1!4tvE`lnT%N(}S8>OhlP(B4(iTAaXWcEgl<1 zIk^j_iQ5-(3jm((-5%gWqMzDOwtI|78wM*(W{@1FJ)vTDvozv$OuvsOvAiVt`M=@0L^K6|^+?>FuyQ;v2XZ?~t0kT4Hrp zgf#v@C6`t$wUDFhp|qJZ=JW*yU!F!(i=bnMv5ghSN-a*ii^R2nhupCh>>mrEi@W#$ zd=WxrqDW@97x=tr-*bDmY^_>}Jqy4ELHI4{hTu6DiXs0s4-arww3a<~Eh1)DR*EF znofBaXmWeSE#HyW+6D~T=w-F0q_vwWRNlgEcd@#r;Y4G?Fz^V>ZxvpX*fB-gV67f< z>1EL`+km_Q2XmL>hB+0lfr7ab&e|e(6s>R1kN2-54_fcvB;1WbfKua3DZVf}F4N8Z3!tIiCd@8WtNX$KhsLg?3;`ek3 zbzPNitGpSNr6^bG!A^B9$(w#`r=X;3EwW_*)23dCr+R9ctBIYQc+5I?j6e*FoGc)n z|B-=f6ck;@M2ut_+6KGuLWh?WG!rYZAuv!spf;4oH$cB^fbWil z#fr03lhzG|{B28wK!g99tznba|1K8|NQ*5BwMjcM899-x%OD&tcTmT(*MB{MRQH)q zJ?t>%?C!Y}{ZSpTPFfEAX5MqQ9?{v(v#CVNFC58`nfx)MR@{j`Jby-!eg0A>6Pflv4=b){jBid4KU+ z+;hqst12z?VDWV=u|XpSjw(Bst$D;;Xn9>px*@lH;s;e35P!+vHN*3050E) z3TsEo?tr&Ymy52bKIzMU?CIBv*WE+!UI|c^a;e@>*`St9RL3T5bHUbuukI@gta-W= zGdQuoNbhe12Myn#?5PZYY(To#cvpn<85{-0?1u%AX1eY#Ki%<|vWsYi${?ok|61H6 z^2k$c00HdINHmF9+eqO6WOxR5GJH^ZBW0%GY-jGVW z(u5v&VHQ{8^SF+MA8YEI1~im?$A3-lTeKTQ4V50QA{z2cduD z^(uZYj{i>OD{5un1o(e9EE45qq<|SwG8)cp+EvSWY1|g#@%8_Jz$fZ1WTEjYdrVi= zMp>~-rJ>yFxg&X;z~6|4(QAPU(39P`F;ArLc0R`(LiK=pFk+dBLU6788L_(|LG$M> zhx*E+@}b3tHYjX$Y;tfhjzVD$zEMmZgTr`&o7b#n`7S-dk0Nfsm}*G9IhF=^849zi zylzmVGc~VOFb8ja7EL?`e6Yb+K`=Nesf=l{j~BF}YY0fwFSb$)^-;uhkMzhcX6*8O z`YLDVmSzIPzjXZx@ytyfIFI(5`+w^P+d5bqI60Zy zn5sFLI{_U27rm0OtAs3uF_aA}0oMmA{#U)c$*&26&aYfi1bYDu1scpeVz0rgZGbt$ z@gQY3O}m{eDq@eZ_D4&G)+|bQqI_ zA&0P(IH;j##qm4{(56~$ZnKoV9`%Qy^PnMPr>W=#-6_W?ItMQB!)u+{WT{cdC{&@Sss z6;fZ}VTvITuuIi`&#~0suWQ^o6UNejBj?ER3Bq;9emk&u3Nw!4ec}xM+Ke0SQWp%1$nNh%uS7{o!@j8Ed~3?UQ`T-lSnnTkxCP z*1oqG1~ZbS>tj|E?a_`%s>Fq=pm;)4sOpbcX_l+9RJ)YUuP0s}^?KB}szz01c^$bg zbucv+?1eQEw?3idxE! z!_j)8_}^R9t|wiOJX55=xZv7+^kGUL&hByQ&o|_&$}RTu$z9Mu8((!=t-RM!u465M z48@=~Hm!d4wbB;e9E3%%rFB+$Yyq|!=QNshc8*huA~)CSd*YL}4;+S#{?4hdJ1>$p z$uc)}L~qxq;hMi1^IPc34x_yD^=`{3#|rD}x^*z!-`Obcw_qXP4LDBx_1Q=J{$bMr zJfrq~FVKAy_#uIQdoX(-n8g@{*B~4Uj&5P|MLF=d4Kd~A>bm6R z6a@r%D{svID&V<^_Ki`B{ynX`#ab1Twz6BG0#3{q^0NAPz#1Hzy@KFZ}_A z*&q7#KXYTY4yN>OhUSL!rVa*nX68nY^yaqoE&wAZTL=38F)H@IWljG_-+@Hb*2YQ1 z&CUVf=xFW&u=#li*;?BfI2ioA|34w4QhC!6O9kayXXUW3ZUsR~^DQ^36zwor()U~#?>`erzZxU%{PgClU;Q(HBg&r*x5o!Bdb<7>H6 zb;4I`!Kcu9Ub&s@WviXmuI?7Athu5aEt|qnxYl+0*+#O;N~)G6$5p_#6Z3*8hB&j9 z>@gUFLurOzdL}~=VAg`ol>&PKJPOiD!7vVepP|wDq0E74Mei<+R#xdSA;?*UfqKr? z<(m^h_BiAmvS)5!(^toFi^x`IVYRir2vP~6?@3a+XjSoCS^X#hd+PXUbwPD+$IcxD zbB>bqahBo)ql}frK@T%1U;z1C*i{(EPUg6&a-{)SBULdldPVK&Gy^6^f1iUi8ix+y z6?i+|HRr;a)f|ElN!4|lYpZ1$&&`r%aVFg}mdGX2{-%LVWU8y8LxzgPui?>S4CUcY zvc7tv+w0iNj1n`9;9^3OSRDmrt)@lJnhksnn*o0wvfx_n^Q-+-ESJge`tt}P?L}6s z>uOA$)4{>BZ1)L19upx5IfiXBci(FHMT;g_S25|!kM7qPX$t;1FohV^%Zn9Q$H6#9 zth0xTnxX>0yuBKC^tggy%BStT;Y`G3lR3I(@P3LD?`^^D?hmmC598<*9Twu3g&Rcj zu=dY5%LX$qvV_YU{Q{1WFQZT|7wpH$By1TRY-BD%M6h-r*wh8oCs4d8S#mr0?E3S! zZhM6MYCiN>%naH>iw8gUjj4O&@NafTbTuO{HtYf@${f*F!UNG#n6(ZHSugmB{lDVEql+5S{ZAj#f%d;S0s3EX;eUm`CZwD4 z;iAtpO)GQ!IFexxkOt`FZ_yc1X@pgOu=u}_U`SS{B&j3;@u~JEzlGN=P_>#hEiF+k zV+}1e2g9Y18LAo=+nkygEVQ)BbQabutaYI7x{tR~jhT=SPVYbX7@oDJT)XP&@%ip| z17d!0OzuAS0u=)#L$cq2?6ZTifsgh3iJQKjEy?sY6%g1eP#{Utp>ae=rNOv?ki!9I zqDwps7|yyet#omrMS{oWniY6hYB3Z6U%~_ zdEFR9hSg$_W6Wbp)5Qem-?o`~GSH-GYAdEYyZrbT{PEM5T>46zS<-*0aLZ|A#dtR0 zfl6#N0vSewH>$g;EZg;^C6$3JX281+i`|9c z8#aoBS7My(aYANBiTw^$hUU@n8`K)P9UAwH%e8yeD1E%=AoSahn?;O6$;jw8j{XqMC{f%+Pp1$l)u#!von1r_ z|0mTup?z8%5fbOOWH$r0d)t~~x*4Zq#-L36{cdrHymgMt$_^h@*EhStImO9rN(6;vyht=Ey=YskpIwXZ0nuFEpACc zMYk%?ZB-gI>P9gqfv-VK(U3%o%*Flhq|WBifHfn$ZgxO3gM4KA4<$z~2fCVswToMc zBF(vUg&4C6!ugL6sd@%jeoh1hOWS%HN6XJA;=3yJeO zTGV41A!O2lc4E-AnFsSsTHQzGVHhnTEE1IJd{HdnUl%|am}bg(noQfac^)+3`i7$J zkCY$poDUZ?x|tPZOnB*F2t*#3AsL-8&-}`~5=7{MfY>J8LZkXH+7+sl5?%ZYh?&3Grv$wt;X6n2*stkKYoJeL zn4J*l*e?lTa{J8w5@LV%rx>0NXT`-z>W5|d{WbD)@qzla=mt$;wTr5JIElB#-7OBr zRofbC+gK4JZB^4t6kycMHX({1YSjlV#>HdMpiFb^T`Wq=vJ?GdZ47#VtDQu)4lZt2`+W|MhnV1Zu{o3w|1)GHSJ&L7g-Y^CWLrvYE@Ur()@caX^ozVjn}h` zF)*n|*FcBYco(d-@bGINtm=S`2CFY7S*<|61oJjF;RfmPtA4 zN&YFseg^HdmE(k!;u~gU{$cuykxr&5k_?rVgHR24UfVsjGE6xl=aHW3c}S0wnK`%g zxsP<5^;)*MhpwE5FScP?s}z<>ddkZ8+qy9RofsIXj-+42d{5PWOnf%5Y$=#`49|Qb=9uSplnc6Jf3w;n zgjngQ6F3c7)f#6t7dUc=Q64{mtj!OEA8zafcDMGW=khX1mjqS>ygErP+oGy{q2SJ{B2Nff?LR9bvt5DBW7*Pil~~1mpT+U zKf;vHhym~HW*pp;i8W=otco5btl`%Hy6&w=P%o28Ouo|iHulGrV#47mWiM{SBS|C? zC0&uo$_Gj)Y|YFxlr^?4o==rU6LrIIM4M{{;TxAnzKgAe^+zo$>WQRtz$1?GurIj8 z?(pob5gWVgTuqtXN_)!w@vV_%b}iQQQM4V_p!6R}9#wY2=_2+nn|p7r;m(~vyqXEk~1$qNvF-^TyUYK z8au?9#dkBGb5opBCo7t1F47uKZ~ZYl;M<9^HyEt zYUr3scsxDSL~~rIq-##8Bkb&G@OC7aLt8kENxy^Cq1$m#qR4GDvS8QaqXZJtlrdLI z#%8Axk?KBRn%S!QBcR09(90qic6Oy+r?%WC4LM52HdbrKk#R{yS^NOuidJ1P-{cI6 z%$(KolUGw~Ik8e}yV!A}oNF0RBm(JD>NVU5(URWXtOfGUJIcmAxq3mWFNi>F3r)?6 zGHtq8DcIKv%H+~S*~vDd_s>6JRoADwX!26~v(`o0gxU2`G@sgs^B&opjmE&pm2y=q zhg2e!ad@k_uLoF4Rg38Oyf%8d;?>ND5q(3w=@_DCGQ`$n$=CJl2AHS;lc<75{BUi)yORg8-UY+cveU{q38s5=>@u0q%_ z>b??laXk|zeBOq)1Gv{{i?6BJH2lQfBsAE8cF^Mw{vVU`4N&s=3e@IhRradQm6oR5 zqAYtu!76A*+jzUiFJmQwirC)93^3_O^KKzzOW2|1vcpqzZ$dMu0U<6q+O{|y!nDEB zZzH&!PWMW}c8!duSz5aCu_hZdLj2ob1DbJeCdPK`YxNa|;k7F%X;GB-K`DQ(P;~a| zJ=Mp`7%|P-bLbGH?5{5+KrTZ#@t{}qhGY-GnX+Zb0eUGO6muJKw=Svu21CdcM%Gi% z1EC>f`JLxXdwK{LxFRdWweqyLD2wwkcN2q#6bUuM#JZ%EerOJ8^?GnZOHv0V^mhJX z#I$AXNlj&Cvv4$AimUOd$!N^N{L?(VW8>uQBgUc|`K(THY~P4_StvQyBNfpfE+iZ_ z7$nD_MUvhHYQTBK<11pG68Se~xk0UeERwBvOS+^d#wzFQ(M2(AJtXEY0(TFUiHk## zT?VGk#Z_93j83I9I`!2WBD*wDkL()D4WpyP5BBToST>1yv^DC9q`;6RME-nB+7?{N zOwTVzj}Ay5q8hJ+sjN^O``Nioir3t(f)WO$csnSdg<{QhKU^|f^0pt0aI`ht`qd?o7wt! z!J!_jEkirs92^T{g+Z!F92!nBNiMoO&>SzTECdiI-O+ezrs2^jb)L5Feq`)2w1)A4 zc3EL;OmwK~VX77^%{^nm#k(|=&}O_9OXwLjkZ+-BW7gd*g-$reZArT?%%7b3r)^PC z)srC^fWde+Z=`b@w!_~A88()5Nq|t0AdJ@!y z7=>u}aHi8l%d|Y`X}3DknE*g`DWn?Y!il2!7!C z;qizMs_3R05+dJYUKrEgjJq#|Z)D5UjBjF%ZvtoNc9!kAfOhp^hj8})tcT< z*$DXLuwh4>?hB&`%0>;+^+vjT)dB`Cce~`I_(L1rYay5^)zTPf^#>D+5ne|9?<1Of z#Ap(l@F&xsKIl;M>I>o{rn-BAR>!*sH+ONorWGz3JVbyqkes^R9foQB4%ZPw_=Ci; zqAnkvVb3*(^bw9WBZCTN@^A=-Z&b_Oi;m_S%Er`Yq35bmd_MB|k*1@Ay6Sa>9ct^* zqtO?gbgJ6H*|0h46_G0t+_nFDqi4F*8yvpfmFlHNg_=H$@47qSUkNv5DBT<)(R(^W zEV`Tky9j~+XC4PE<5@AbDG?)yzr)h%_Hg>b9m_-lIfvpTjZAI&bebxta7drmtKY{z z80lWfy8@oUcNLKMohj}>doVAsBRqcteLv~1zb73j#Qb%yNiMnvq&KWheY|BiyoK+7 zipyyCle>EL+D!OW7ZeCg>60YU)mD!A!bk(uWu&;-I~7l#+VM+A991zyL>0E^CT`jz zB0&@Jy2r06A3dLPdj)u8&YNl(_&)9kye0vT#cw*mW~ENaQ=1_%+%;3zs#2~RNZO)X zf2ZYrlV=F%gb3-1C7Wk;>4&Yf{gAxi9B1)potKWm;w6EwzdFnyqFSpk~$vy{_gjtK^knuo)8$XKh|Rtec-(-ebp+$fiRHmA>e|k`#7rZ#tNW+knVe?tyaCXi`1K@~eMdwYA z`TIC)TSX$CvpUyhb~3Bc9UV}tZFs0Q{2tWzAMS;7F;Wkv>6*L8XK(t-ZNf6nCTEfD zeT93IAUHkFXZ3nzPOmx-hICskQ`7r;>FpsfKceMJyK4!jHhsolTasMevM#QB>KA## zUg`3#3d-#Yk?p5Zo(%ZUhJ;uBL>n=QA60$7YI**AUlsda54yV{oHL5=@&`t_1xDXc zAieN;+ya-~gecF%C;1z)9Ivu$>1l}a;rp!8cNb-#8Txk+bmvA@5dmCy)U8GQ&>@@g zlnx3Lw6rrFB1)a|FqN}!zUGz4O^8Uk6Sox#J1PEckB?I}X8Q4zSz~QPLI~;-FDw}d zw$C}jVlCqxaas;K6hPP^7##qwMXP-g_%S0tzOJ_78iCcu z>(-UScL{7v%{E(%gS{0*>Ibkr^rJoW2f8o#`$XnnQ$G;og1_Gi#Fe|p}3_BOt> z$61L!)#i|A4+Wld#P6a0cvt)K_Wt#)PuH44V7m^nU5n5q#Pa=fAD?{>zp>8pZO6xd z$c@&=*PQtsgvJ6NDwI1o8mpDdPx27*8Mi#_iI0Bm5{ZpM?=5-fp26{=?c>v~UaZ;0A(Pld848vs{c;_#tDb;HhyT`hsE zn;f$b8@$s!Zqh-yegoeM_MMP^({wSwon&N1tU|6&Lwos4=8Z!ECLrdk9g`8@uK`F=i5fPgcot5L_$rM84nL zgEu|4kz)66SKdY+jyiTuIj)mNrI3aup>NKIvJDgv#59OENt00eWTu!a-7IDV1t^Mc z>bHCBO^nqd*BX7}jU0T3k;P}&{7zh_VBwRYJgM+H{YI5kwhN@iw3vc&Ky z=(AIG3+E9TqTAS;roN*od3fs-RynNxFJQdRP3?nWZ;X^PoNOsgCMo+Roml zZ@hnL)>E|=>y%x#Ydad!s!=A7%3yJDD3eX&FyA{6&SY_%n;13D%$Od1&4@H8d%TK| zk}g>1k**Wyw#;rQU;amrTd(0I4Hc|p1y0Qxnk+Cr?9Y%5#+anw{5!l5f_z-KB2I!s z?ZYf-ltZtG@lr+%yc>N4Mw_HQRq}YpgcE2x4%t2%WnY1^msv6R*!@Wat)_a!EA6W^ zNpZ>)j|V+@T(1P>v-?Lcqxz0r^h+gjt9tZY756TwqZhGacDwPWF#Kmyo^$Tbr28X) zJY#}iZk+#+w(V-vO?xZJ7f@j0d7Y^oS^Z~AAd=O1&mbv9R|txu;VS|2PD1Ewl0U5g z-k`1XnXjn>+_eip_AQZm>7TIbAqjox-#OQ5T2eDrk7HsGJnF9a;CZOyPM>(24VcX} zS<)NxuS-h$k+}Z|qo=d$>QeKdy4Qw(P0QTpEvJqWIJJH{euXU`N{V1r!DmlQR4de~ z=X5^*IZ{gDi6}17XP0pJBi!Jji%Qh{oPv+8zxf;~g0! zEHpBfY8$jC(`x;m>d?*)cp4Zx1_8X*1(<5$&|L)BcErPa@lh@5@6S@@q>T|d-`P_v zXgJ*nw4k<4@>fj#W%N`aW2Y+-@KP$7_V0hIzYM&#glJ|f^w`;L zQ8)Zq9Ty309dn3mv$m~c)Y;H6mmsO@UUZU%v_-OBX$pDoZP%)_l1#fLS(>$6Zw?#NmS) zQxD~lBw9nMI#L#=i%(jzdm9}ftKz5OS{;##I5U(3!KG>jcK5`k8lghciD@0GT@TFZ zYDI$+^>|w5cJ19t2X0#ycK1g{d2k#KxEt~_L)eBfLfCxEGB|H=`lN+yjU9y8Iug?LJtKWmaEWY+*G#t@XRlLr- zjivDqS5N8n_*i}+b#wWG+iKmrz8+>AJ?2ggj#f`~Mu#nV(R6clL)YUB_dWam*Cu*s zSAtUKk7MBY&oYM4e}`O?ur@FS{Aa57f9^DrNZ9;P?^XZ+ivrFgEEAOKc_#szMFvkDBr3ZmRE83djVF zh&aS9LIMI2j}%2p4@q{6ouZK&hMW@@;oV*QWcNWw&aSXSk#)sVIcdMnvJ~2-nJ;7N zi8_0d<5-)@j81%cL%~OrF00#R0>NU3b-YzDYrWyQ?j~sleHO*cq*P+%5>Iwkibt7H zI&=ZGrR*jxc8rWo%`=Es_<3xQO8!wL2dkW5xWSvR2xdZsHFi~t^ao9X3;HQ0*QdbS zZ;(0Qk3mq1U@ZmnL5P@HfiEa~l0{qnxHlU`Hp$rU1F}5)SO_yk7jSuB)Boj!OW z>)q=dsf#g2Hv3RNVutAJH(-95a)-!17@IJzO>0fgei*NZUvmQoGeC6LpZPj{);5{K zoF;5k_gY&_Owip7d^Fxb)Ng4w<|4kxajGyZr`qAYWNnS}-=u-<3m(!6HVP#C$U+xo z*T0P3Z^91J^hn~wtH6bcB8+(`;;w`So)I_SNoV8&K(}(RZBPGf8Q@%xKGR9yv}gD8 zB3|$Ng-T3B05N*Rsf)vZ{6E%|+}H}i@qVloxnY0(V*CFQ9sesJRJ9zHeq=_P=#u`$ zv|^-7S>fPd*Th7LKtP%sr(@XFAie(n&HV?<%NSP*+*m?fZ0H|bXX`1c^}Hw6iBP}v zEi^oBKt|D}F<0h<6WMpp^e1R0AFI&0kEK+XzP1SykGf@c_w4D?Y1fjeW9oIFmE-DVT+wu2)#vs9W|8 zOU=)_^vYU9glade_Mlm*Fl1C%qBAwIo*OGc4IAn`EIplIfyW}8)$;xY1CCnBo%v|t zK=G$s&$NrP(F8_FY;nO{i7HMRYYf*F83fe(@$T6o!v~sYzL8u*#-kgkftb$Vj8CnAYSB_&zK-wO`9hCm2V&KtlS9@;iUq_6p(Ij60X8-B0DU8tliaw?9?-Iey@aq=GfSeaDE_#ckDD0Gf zGZqol?Sq;b-~#PtJFs3F2IE{PD8FUx?Z~cmmyJ6KhUqa=0!6xCwS$*gX~eQ>>q>9T z_Lv|MA@##ks>*^ODccbQe>eG=1F?+YEt~{mXs-Pweh#U7(q?rt*hU4$%+=i#ez=%9 zX{}FY2^t`2O42zMi#7fDrkE{)X=*72fO zI|wlfH>POOT_CT`V23uWjEs+g2Ms%+&1bP^{YA5|c{qcUQmBR)s}qCt!Pt630zliW ztZ(IQ6jBK zvNA&A2SJ97m62X|Ull)&2K=(R25#*H2zgADP$bQEDBC7?D5Pmc4?mU+qI^dcL$X#! z7mjHm<|1~P#ceA5)iEDxQtz@WJVb?dId*K3V@CSS>t$)liSnxNph7PflH^$)Czp|g zQ?2PBYB5?09#rjk*~|Vi$yxY_tm$z8JL}M$#L9C%%>MdL`-c~t-fLkh6<;7#2LLj0 z#dJ1NC1OQwR6J6|q$w?jSy|K^=|Tk;>9j4hjJuEqKF-nbN8%8XZZg1EsHgcCK0F@l zci_lt%#507xB1p^t_aSzV@agaV$3ocqvlZS_Pq`L(W|GnX>M)VHmLmn=9>a@AV_73++k8!-~?)rni>Rm7Q3tWzxfAJi(z>E-m^=|vW ziEgpW`WxEWT>8KIzaD{{z*n9jU~WCb*5S{2KZww8ygRgj{>hchO?^sJ-*89Wd!Yhy z#Ke?aH}#^#cfntF!G@gAqu_1LK8XGIW(z&DA>B&f6NWzV_nm{%S9CSHOK;Fg&|*6* z>4=<>eWrD6GTsW*u3AIJy8%Iu;VKtDw%BHyv*{(NYEoNWM@8!^R&4Z!CR_PvNneeB zfmXkMwX?0Qt0^?GY!@Vx>BVV5L!?f66vBV*+beinRn6?kDE|36WVs=V_(qb#@8x@_ zZTaL7EsKezut)p;4Q3z46h+vDp)e75VXH88Pw0c5?-oPO--G!PyzROI^?bwiB}v`; z0CCGBM%6JKe6w>lc8051QFoSi8Pke%!Cz@eZ*1P5bhHC}p}LO$r*yh@@cwULiFo0e zC0|H4BtPQX%tNu$z;K1;xi6Cd{~<{{|MEgTs~fhQltP+ElfmgFpI^Z%e9If7B%U-4 zL7f-Pyco*+^M;`5;X_ts?*lBOdoMX#R2OQ#`}JJk+_oC`n6UU2@i%Q8S#MTq9}vl? zTY9}w+TX$#zO}&Hy5Pd>f%V?5_txMY6_(kib?zAscETo>wnaejQP>BxUqt|z{N13t6*6ME;pHL4>_R6|>I_}Fl zM`+Jv_zh8Fk#}08+paCER|KExnGgBLPzpKPJ!<4G6_?9nGa!?hzcgZ!(Tqo_b1Dv# zhXj67JCr7}e(F(*pL&%0zmsvL&20c`KZ9U$&Q5mDPX9${L&;hWSpdPOiMQp62SjVDR9&|a zyx}DrDi4LhTv$AC0fFzN(Q0heUaZP9H2VYHhxwNN9|TF-iK}k{S9?OZX4i{>tBI*; zmb;0`^Lxy+9+0%r=pPUT+_W$<7UqYprsvCy{o^c-s-0d7iYLKZvfnn>Of1t}S;R*e z9z#jfA5)$a#^Lq@2+Ia3Egu2&QDA*KBDC9baZPsUvrSI9OP(l6JE3Iyy$~<%ajzvuY*-$>24cn=jfUx zvd6Ga1SOv?sD#8Sv5zqn$PHUWA+=xvN?^pN1%kt%-&D;&2rMz!7>V9Xx*>50Tga`K zWg-dt5gvtlXI3$0dpO9Pq|-)-hG(`$!g3}09O8`9U!@g>HvR#z1JI#Ui2_=i_`2&j zzP)Y_=gydf_Vlt~Xra5*P@BlZo3N2mns#hs%@X5vAYG++)SIQ~l4pHZT_Rj|MjR(g z!mLBcm^(l8=!51gRfgAdZDta}#?xxXxu-8Wu{^FtV3P-HkpdL(-BiS~2;Gl#)hoCE zEvMmw0c3ss89cENN5?o|X55G#s#zX(&};(ueq|D!hzWjy&mrH*P_2h@iH(M2fnB$FV{&iK7mLwowQDYhlCgMDc+wIml1bgy0{vA(7tNumJ&!*VyN;~ zIqw_fR7fTIz>uPW&q7f0P&S)u$#M9+b-!^#x{ECi=>^w){H5X%QmEk}Mm!FqFp(!1 zAQF?{-f)efv;_8QRy}g(N zvz8c$SwR6f&r9cdzEtoj@ZdT<{{2>{jjD)HDGao(dmWJQ8Nu1G^{xEbO)KNvI#&w9 zlW?9(zVPab@d{Ur6<9M~Fi{mKkA$$HQM$n?{&kpgYLjuKi+1j-#B5HyG;e{ihOudl zuw|I6+{43sR&~Yy)!A3T1-UGJ1Cjy)l9H0r9n#$?-QC?C(%pzicSv`4gGfqCNq4vK zJ)HBN(0i|k^Q}nyVCTQH6Fa-lvooHMh2|c9-%}&VD973KO2klF;Gif@L3TQ=i&hA< zi`EWr=POAhGYNhf%^L2?)*n?a7?K!$;sfCrz@Ua<~kS6PT zo?t!ovs*@~IfGPG{g%^vSc2KKLc0;B9eg{r>DehSii&3}G-adbvkWzTaHs8jLIXFx z`sEGofMKb2=sD_fo;7Ssj@MIEpP|t}#r9{mtv_wF+?K0*Ljf%f0ABuo)a{DDYpSA- zxr4#)x|*UeCyOPF$e9I8(Pb+~TT^aByQKVFdj~uHi)a#E?HPr= z2d;}s}&7?SeBmFW$G@A-9nhk%B>+ zLT^E5d1qLBou-$~)p;ieTe#Ci`U11ZpLi9VpEi}0k-$d?{tb@g$cq4ryyRI;)WU)a zJ^4w2QArr~YC)Tt*pFg5xlb23C@8ni(cvlwV2peZWHxs{=o(#0seW$~6sDNP=;$Cn zvVe@2)6!#G;D_Ru3vLt?md`Gu3YgyIWp>$uE=-PyvCE5iIE1iqR;i0&hs!#uEI*^M2x{GmP5LaU$D&8IaIlg4;fHJ!1j6M#qgJJS>E<@o-(~f%O!89vYde<8$Hc9 zDt<+r=LUMcg0OlY+`x!isq$A+K`J03d?#}2vo7od0)ZBi{~`UhPy){_zNp?qtYgqUyuVP%$^1K& zxp$cPmcB*>Aj^`oHTJ~ypKa<5-_K7%QVN&*7-4cLtz7wJ+`shDv6#VXQiRaD91rlx z0=uDAHj&1y2S=0QcI9uBCvJnj3iM%P|rjjl1hDe}?cBm{L z2w4!#!IA1WWrWbTAJO&o!gz;O(46RVmm5Bw_$}7x&Z5IF(yFr=d}Azn^U68UH{gtr zb0e<7nD#|U^1j&DQKue-Z5dEgL45my8d0O~NX1~6h;$AYBOJMwU{=`+(pf@WIuc{- z{1l=s_?R?A7i}?58b@N@JB=n|&kDL7J0-hqWpL&?p<`}(gf<0^2F)3_L*p-Gh_6uR z_Qjmq(1u5XSmoXgH+|4WJTwg9k8-Ug~qL>IfnfM!`rOAqP80 zaAo>5;`;1_iO&utLsvDXl^5$0Ms?H3&u94JetLG41i8&< z^(g1!4zuvI_`N%!K21#S{-|g{xR(AgaqZt6 z+#k!ZhTJ$03a2=%BtJ@!v7lfkfe#*d>$??UDqeC36tTeT4)ZxwMR6c7DY!%R^1p5i z`?T4}J#b{1h-BXrxQ>6Z@YU@zr`rVyx0lDwvn8(7olRZ>|7>r~Q#!boHMe<7Yo|HQ z;uh5Es0=Hq2732TQ3=W9dFgcOtP-=KjtTlG?nB#8r~(X#X3;TTd| zEouuZOs#I3>Iu!fsW{ytFF6-2{VR0d6MDUA(4lQ1;xc~SqwR=BHL8eOq)|$3zlkBE z3iU!t5-lW9Y?5EAVQ!|t7L!COo7Q}ZNQzGVAeYg%WmUc=dFXwi8F7GGx21!T+-u3Y z5^aM8V;FcNjGlNGwf%v}j$??@laJ z>)Navk|``-A6?Z>CeOR?t7IsXDs0k++(!s0b`l4CLK?EvxSYH}1D5_hgD1!#j+ZpE zN?5Fd1v)Xx?@j6m>{9~gpZ3YM6+)JfEsbckopqQAcfu!*#TW+)_wwnOKVjJJU~=#! zHmb~CM&oxC4a!B8Z5gQ7TPj&$Vs9chGh0yUvp^xc;p#MAu)6?{CVz|Ss+gCgSk{?D zAOnX0{|WaE8eLq9yNN?M{)bm&a8CrcLf%bUDqIq4T0nncFYUoH*cV6b4ehrU9&3|y zatK#pTU0Mb@`}E0m^wDo=f7}O`#b*^e|wDP^th68H`m*@)(IGjp|A(^3mc;SsUl!(H4 z^c|AH(5PCZDg1L?=tz0RSLl&PenkO!Ud~)dd9DjMJf%budU(C9x|<*$2GHS%KSg^a ze-AmaNoJ%GI&#LoNL0#n7W0l>KZEA@I+HAwz>+mI2mJCt@W^*qK0=UwSdKj|Sx-bS zPGFITyPxUMM^W?J>*sxf!ma@vGhD~?aZJ)Jrnyp9fl)6iU`6gB|9WKckb~p{x`<5+ zB?!of=|7v&?^g83i~sZjznfCA2aJ1M_tiZELsC=HkT~C4f4@QNS2G|!K2ab~@kMCC ziI$;p>qO)Ane;pNN!}_|*T^1ZX*$=`pcPLNh=IwXT7(KCRDW5RRJ8bldtOnKdfPhO zbkK6MZ^+-#%FSKIe$d1)%sEWQ5K7~IeiFu0DJ5>3l5#g_L>j=7#1@lcqb%q&S)}#{WEO!M1Ys@0*WU#Ir7;!VRg60!T@Q0zWZ`@V%_G?TCGA#<6nZ3D7b&Rz={n2P$1-I(IJ1RYLEXMML--cZSS}+Hu@QMHK0;uNVi*Q z!D0o4HBr5b-U6$84}0`@*MlYlQy`B3y`{6IKXF=|y;HhD(~`E>F#sS&?fP+Ff7 z10S(S!`CpL+uvw4@f6g=vwE^o?Gw|BFq~kYVt=`m)dtxRtDW($)fWA%K345wM~Z1$ zu>1<|%NRLYm+9+|F^P$oHM6R!da|5M&;$Nx$S)zT7)+!n%u+;mOr8i;`6V`}PC?9d zQnei<&N$&JDm}BvW`JWG&#v4wuccFofpw~)MsVtyK~GlvT4!RT!9TPzo7jEE(@(YS9T0VSJ+GQc6!Y)bJ}o|URXpp==bTUnOF`o~;5{3fyUTq+WOV+OfLk?o$?rC(*EjHg-o_tSGc&ZsB1gZ!jUv@=@ZuZ8NGn>~1R9vPx*ynT`A10*K zz+cQ|E<5Mr6Xf>^1r_YSm@Tc6?aG#NyjB_7i&pMSH}1GMdyYAq8Ew&?>$vGe(Ji^1 zY01m)Lq&C_k&^PJ4oFy@-Vrkk$6$UrCppWz(FwMK6lm?-7Bj{WPg{?kXpf{}{zBy4 zw@P6WI04_H;J{2wU1|;Bq57#yEOizQbRvCz`ja=)(PA=nM99;;0sZaWbxau;>Zl^k z$&Q$(#IYsX7T^&&)b<$;Mk5L`njghgd|#wDb27RWM3!WmNi@WlJPC|i7_jbz+)F-M zhin*4r%AF${z^$o(|uimpvv!>F!pVxQUWp8D&F zca}?0evDUl^+{#iB#V7EIZ1Df>d^Yk+{`}QcHkRZe64GB)J zk=*(=%mS^NP4bYt{=htDBOXX<9y;8$wNNN}=)`h2ai41OGDNJ$ zR@ETmJ5u5DI64}eDMew$LZa;m`EosHG?Gy{A2YU2N?j+K#T(V;3m>zGgs z$Voov(r;+Q7B^A&M@Tw+37wLkVp(cjpI8%DEcU)Ylhc{jRZTp56_XzxKx%Xg*+};uCR@_1)VXX%js`H$&O)T{G+a zvnjI8Pcd22Y}a$3wCf>qU10o_XpDEp-xM9zL&(b~5WnOqlSE~{&kc_-9bB0hVy3s8 z)y0@85_fFWwKu0N-mhL#wu&{A3&E5tLd=hT>a3E}gk~>>H=6w!uaCj_d$Ioa40B~h zkTTHkK`4bY5XmJ@z7a#h@T;I0&dp?)U8>HqXnv?XF&|>tKepADoCKfg`r>0smcL1k zj$tJ>bUkb)=Cl#(qWZ)&h+=dy+h>X3P}MUBXY=f1h&ND>?^>~BiGrc; zu&jf32a=s3olNj8m<0=Y2G~1r-z!>3+e&ZeEu05SuBdC~ZV+n}CEYO=J?YSMu|UEJ z2ujG5GnYhh{9Z7asli>-Sr$7-8~fS|kLQboTi6rBL@QiUM^;{0`y}0@XYGfGdjULK z%ua`N8nC2D6WxwBprJ<{p8~oHxO7|cjf&FrP8tPd>N%qzSnO=D36mODGU7khXfsa3 z1+Ed*n+F73x@Vp{fohJSS_YnatjG@ZqiUhN$I{IsYauG(`Tm-8y6=W1Fsr;SVp<_& zvkwUbSt)4+(}qfdd3pxsI{`?X{8_anmK(8N3vO_AozKRCaR|+c*ijGRBrX3u$F#o5 zi_@qW%HSmX3ZjNL-a#6D^q|a@MAPn?W{hLqT{-%n6$#pGTLw{_POM8yT1Iz} zwUFfCkG=?e*@R;OH*LJ~%`968pUpVdw81mG$13Pf6ntvdqB?IC0(JR4Dgg_m(vf?- zAylogX+?(@Sym`64$VmoOHiaOlm)7DF9R*0wihKbsM$xt%nbCMa>7NkP;N-azA(Wt z5KeoH-E@ymQZJx6(7Tsz^1hQp-Q5i83gs2HDXYsWY;LY&1?_)@o8yJC@;2SY7xg{A zKVk9rm6zf>>;-G4F{aC%W|2zn2<^k1>D(?j%-xkMtb-Ve zP!3%0b(tHalZIe(FG0-)u*4JK*$}W+dVQp@DTgr=I}ake#6KMj7EJ2sR5^byAk$pF z2$Q>DaN=Hz?9@DhrEC@OGCbErm19-AsV4L6oS8l3a&sAsKe|~=Tygm9{yq7MS>DQ6 zWQJ-Lsg(be&4>wjH$6d}xLIGbH*{Ns*xa@bc{#~u#qdG2k|9awB9DkI4 zo=%5rS%vscxnQFXS0F}LE?@O0mNl7xM09BZ_3zY5&tf~_Sv%r9S;zJqEjGD~r>yr$}A0-DaiG}TGfi*6nLLgo&vLseS5(k&eZ4nJB zq@4P3{f^$9$WacS^#nt`mEo(7O#mG2P^F{`SeC)!KAbMZaDeEb$$aAayAw*_=e~Sa z+BZ4_V)btkyk^Ua_J$T60tEJ0%&fDIIRbn9k5S?Wz;ZJ?C<@CCnU?NGl#*;eG`~_E z_c6wdI9S>T9+&Ij+9jP)tK>iNf_ItK{w|<3Q>;DXVvs_nL^qFZRNz8Po?~;A6#}DM z%wq5)v_G-$iaA!rs_(2xH?rv8Ro)frq26+Y!L+y^n%xu6@!J}sdYE&>nO%+M-;frUln)h<&@rQ+*R&5Q! z7phq<87l(9zw=$N`h0`;I0kc}3te$vK)HqY;xH76dmbCRkR8Huw?kmVbrvG~d?Yk*4|Dm^j&vD0~)Obb2E}Cq{r% z++PQJeP0Q)orSgfo#ZPOp0*OqPQh|2l;l;E09BEXB%P*TL!+YST%6k~;Utowhmzd% z^Dp{?yAdssdVRuQg-KMotD(vJJL&6{_P9v`xduliqFL z#AX|7$wGA`N9a2!6G?lk@-F#}T|hNa>b4$Nq3;nhi25WhOpnL0=)h6G(W(>t6{nnM z+SGB7M5oI}fS!&Y6t~&HhO_>8ya-DJ+!bHugj6n-dM$9NHEwtik3O2)2ezhIR@mD0 zEAT=v|K~Q*FgTq1ImVPHpYhC74zL8;0p`L@Xdu zhn^M#i+xi?*sd@qxZ;KUvGp(V=AB*~JN3$lnIt(*duYt}(rPZPWpM8w4{)oVMdMCu zT}`dV9@w5A=XK%h>530Vd9gx{p1=jha}Qo9r+9r6qqj)=H1O$8XTa;&jLYPkrK9b= zc4ku3@e>OCwf!*0!TSLO_^W6#?)WsK&xrHr{7bF78m;f}^L^NJ&oT(kW^k4J%Swl$ zg;8FJC5Vx)wJ227HCIw?&c31D&KfFgaJgy9uSw%=Fc(tTSD4AApY~4*c6Bhw_NUp( z$CkTtrU^hOWfsVsw;;7EyRnG64Nx$1mgT!7SHkicVEH0LJ?TLQMLkaUNxn11(#){r zxG*{ZxE068CDbpDdvn~(dhSL=6@Zyy%PSx(wBQwJGZm;7-R>YbS|ZYDU#m^a@)a$P z>Z7_m%&;niu8e2-tD>FRF_aKyWsHC=Gm9OcQCjY~lvHOmjE^Zc=IF=Mug5GC>*L-# zs)R>EF?`Lz@;Xhjw~kLb<;<0=ISOFLXRFHnG>C#)2D0<5ef>lclAS3sfZVqd^Aj;D z1vGJ*m>6VTbxhs7?jG$S1Wu5wU|kdY=X$2NqYk9p%VbJY!~7YT$~M15h0j=*^`B}Z z!TUEUiw?6;syM*4*Brx;gXnB~DLq$8a4u<629&?Ohz81^1@suQI`PDMCf_Ay>g-v@ooGUL&?w5NrSt2)4LI${l20ZfK{g#g(k34$d2PtW<% z!P}fT?rE`FnvRkMMjsXK2Te#{+D5#y9cMhD6{&#idNqu#vGIZ8CTiq*;)5s z^qH0`1zjGMjSZ+cc}oh@yrG9rThXQCo{fEZRRq+g)B@mv8dJDOVzZpy6*q90VQJZT zGgWP=QC)xLA}Y{Gc(0Mb1lOFxKKtD@C7f5B%ylwxI_Ohzr3v)H=6ZM@f1fSu6bcpTotVy-! z!(R{vEy|5pW+3`+dEk{}b(gRoxpjweF*wb9XRL=~Tj<{} z=DMgDp*t#K^vcS^S;eUOn)u+k;QhcBn$BOw@SDt4Y?VH#Ne%?ndP&5Ft zlxS2gRV09OcAM^5?*ndM=E6-dp59xbE8XwRf15A>r{$4NF^jE=wr+4BU5aQ(YhSlU z94&&KK{*Z6UL~lido{CN%XQXpos!{cHRiS*9v9wWEzFX<`KfCNYT=6}ntq$Ld~Lm6 z4(AA11Gap1pl+-gVf0A@8^uk}r#lkc*;t*+7sA{D<-GC`b8|baY%X8a^3xJ;ST74m zC#?ir%1Nm->++RRq?MH^o3KErjUADux?}>n3>@=>sqs|<4W`VRQ=vk@vA$RaXrCQ@! zpk24R3y-|hl*T%2jt9TbMPTS%HhD(If&9XN?=;3);0acw^uj5LX|B2Ho8Alq)lT{H z#J5(E(H7o_;v)lmi|-7YL6E0~4OOnVFjXC+gc#3(lNiRQLv-QJ>?MIA7vrl9Tb%Na z>{mxjSsikUt6Lc}H^vK=$MG=kdZ#`@863@3#&}dca!hT>4UzG9*|R-ht|f1|2DWq9 z+}5`O`x+xB<2i-QE@Z!H&Q9s_H_3gIgFTo8)lf)YgXj-`vkDVV-R;Xx)(AFz1$37jb49Ykutz70xD zG5fF+86iam98=2YUeeTCxTs3{b?j`#AS!h%Wfio|47))DIU5%Qhuz#vwyHptz4@oZ z_Gr51*K!zD76>FaZT-fxV!fk8i6K_v5n0};o4N_h8?-CW!|G8y(tK~ZN?|YyiVY$p zqu%EhzIf_xoN5%a0S!qpWJhrqMQgl+l#t%L#TU5G#MPDkt^avHrS;4+2$kgG0NNL# z_+7g3XCn}lsZUN>1-+k~3W{#!y0M05rckdu#c@FOsY8}Myu6$Ot6}v*3ZMtKvZQcJ zESVQXhd5Pon!#_dH2}eLK>YfJ_G{tA_}K}ka~%Xi?IAxiuH&-KlxKro3p&EZ(lV~+ zAx^eiMtG@Gg8u^Fu!WP1WkxCOO$|F)Yu~3%FHZYKOH5ULG}ot5{u`rE@7ZK)MG(xkL%gjp;gu9j3vxZ8 z^>Rbyt5qRqrK(-Zgs5tT6$&xcib%)~Ud5S>oZ!6)#(Ad5<5>zCYA}Xuf#8AaCQ5m# zeoTcm(+3vmXJAz1N%w2Q{QnYSUcjRl& z%n7=2OJu$lg*G3ICLU2&BAZv91KQL55seB)gng5#Y6*W6yZSlRAPmB*;XHYBmDyMF ze#4@mz+DD>nl7-&nh!AMPikY4oY5TL@|@z+bVTwdQuRv5sE18VxFbmI-7IaW6lHI; zwVzNwNnVm+YtNJ)2NS5@ZQq$7$8y0qr7yZ|TP5SP?|N!SK(VzziFJ>IV0;J zqW;#Vau`?i;U;@2nmLLP*RE;)%sR``gxAf&5;KDJsoyh;ELp-AC=91LRTLvxl3Ec$ zVtQ_M7d zU<_WoBX<;3!nSFB;5-SP^IO{zN%i9v>$WBz*u+!d*W-EilO|Z(%CYPD5zP%vx^F~0 z_`botQBOZhPc*M-)!)$p%dAqtMTF|=4=JPVz2XGjd>JLvex1Fi!gh(ywHSEj1=4sE zDGp`Xs0Mq=SIb;rI7dE}k7gl*tc^=`{*+hhHTbFDgkjQ0Hv6NOm6X;zlt|#nDyg{E z+>ldguesp$aJonkYfZaysqoslKbGmF!FPP{=EyHVyQ{+9Emlo4JV;3P_wx#h!sP$J z)Y%>d1!sZDpBj!2J7$;#Mv=+uZ=t?puHI77Ha*y@u|PPPZG&GyCX7Li%>@-Gol^LD z{I$EPv1~17)g|~nbH{m2eN)F_N&hh}1gK*;ii>l4RIB&Mlv7rU9qzPfUH=|#fe zM=TQ*9ke4w{ZkV4hONd2NTVUU6`8xZqt!`0?{HA&u%6t0R7y^><~oF5$;^h4=Mf8= zp^?BOJI)tb9e1XS6B7W%p$QmOr%PV{<`#{?EAF2rg-U4Bj3LNNr+U(Wje;jlDBm~> z*5BSz9jHxVWqbMChr3wpo?J-2kVE=>*{4^59G=(UL7`QLgx_KASL~M~0}mqyDc#wM z*dhsTfmG_3d(*xUmZ(PF?bLZ)L_1Xl zHhT*;kdVpZ)O8cdFuqtx7DMhrAx9^+k0EtP<^c_VqoyEVTIuqv_yk*ZgViU|2$nq1 z@MA*AoDaRIrx-*)oxFQjHTHeU$|kut)vCOATT{^ci z{2(u4ZNfAQrI(s1hQV`VA&7KS3VzPWg3yZ-wHw^V$gQ&ko*0TexJ9>8BW}agp^2tFu{cQ)G$l#_@4!QHV_>-3v+sFtq<>3xgyVLXW!d9!d`XFc z80q8f(VTffM%$A1!i%H-5}Em1VB6(5+PC4C>8xR1Ebor!i@={#^dmZ~q84Usj^n>| zg-!K%?wObDzdlE$+48;ZAXNNFSi~?VHPZ7^ilQDSXS4 z2E;eBbcs#)$=K>dce=66!-^WI;bp?N-w-V%l$O3wL#(s1qocF?Pa*7en7Qcgy&k!{ z;*Qrw8)VTQnh#>KLxx9Y8K<;?wYQ}JTegrIpofK++}NczukXA)uA={hSv`;YPBY+Y ze314oN5ByI-l@TF|C9d8EpmW&^I%|&(^Uf1wOnr|u?iZMf=XnSDeZ@~3gZtc{f+LZ zF%9T3*K9@CT+Z*m9#UU)Ho7A(tnrHM;XA(%yg8!ID-a)Rg1tX)GhC9SZTm19a{P3- zPJ=NnnEV(!N>db7;;U_!&WGM=qouc$QP7_gZ;x#_Z71fWvhHX$K&w*K5Go@t?yxk5pHZT(kjJov`N)Ne>GlZSA4tj2KazzQ@E-hAR5~plC ze_i2g%cqy#q$K6jG4}1!slEH+GEbs!D>Wy0g;mLrvp>ToNAGmqb7C-3<2Cd-7#xsj=>$-WtX+S1@jU$>nhc# zQ9209)Z7zTIhfW&>Set1_m#XE9ASMGu#>y?)s_89>e+)SD8VOE@U`5Wl>&T)QP?a*ShyX_P|5!Y0XHz=i$81iR}(t+R=Oe zE_MCTJ~0QgC{8dD`NI5{xhX2uF7K~ZH;%$#-b~T-ERd%4n2SNU9-p-u&LW9_eBp&u0vU^u! z*{HVA#qX}ZE4NAYf|cO*fqjk-4)>L7OC1|EP}S6o*3WFs8OKl4AzY|$ zgn34Ax1Yz?LBvVYF1UY$9!oQ!Tgxz-u2?v=QjJ9n7C+xGs5B>fuG!X(?=2*Fe%?@7 zuH^XGZnVW#N3tFa!>5iaqR+I$JHqRG1@u~c{O!8<27?4;tdO>Nupc6`M2}ySzemoO zmCDYseV#)7N-iszZ)2cf0^-6sm@*4X|LuMpgSr}t6KiJuAR2t_q_Ku|At=>me94q5 zU6tZ7>ENYb4-dscq{)>$bR~oD+M3zm;DMeC4&qlu3zu?6F6-Cy?6Bq$#NQ;=OfW z&0tR$&W140q9UYblso%*{*z>NI0LE-4i&+yRawkeg&JyR-*94j*u`C&nzQR2e4CA! zG{5$_DJIC}w}f?2!=ij$9r-fwfw2Yl zL3w!lwzLV+Obl(KL6L3kb~wa+^et516XW#r<#`SL`={`?YPFf~a@1-wAQEn239+_7 z=Wd3}kHaX-g(o`nsh2BbzQ#4n?v4tVf$l&vYpaYIO!k{-BaCeDjT5frkU37geODE4 zyXrddF1##8E|!+Pvf^>tHbslO2-`KJuTD7LG^q zJP(Hgn&LQ59X{*WV-3daN+Pf))isqDDR~B?`x~V-b!6TqR%u|)fXVVh!xl60)p$42 zq7K?=^oKPcEsI4=;6(IL2OenWghABoV6kq7&23%mo*M0)8Xd?tALqt>bsE$qU=*@+ zAhR#Z8Yz$9>t%b2vqSpZ58F~|8G2td;`zEs9cm@V72VLCp=3lYqy8lA*>YY z&lk_u^LIOJ4+26O)P*<(@J-?>FSrI~*s%-_Qfzi`t4G=3WVFghwV+R%p21R`s$g<* z9jtPm!d=pU--W)sfz>dZ_E<=4)7o63u|GurSgO0`Y9C70KNdG{CO2kamAlSB>>0@| zepr(q3SwrXu|DR1gF@u}dXYEz;N|Bj)uYYH@EYo4CKy%p%#mmQX230LWOM0#N_`jH zr`Gz74kb!ni>xex8exL??h^i`q&KwTqj$7QR;yfEimN+F8U}*(^IW6{mT;HNcgfks zX9J}~j%Sp#f-`VeW;>kyn8H@C`zXqAOa0nP@mrhFB2L%@>adLCZ|PHrtTO$N4L`=z z%7o7g4jn(+j(0MdNk+hx%GB95Lv*CJ`zB)2NSL!15m&=5JG=k;52^+;4c*7|ebPhQ zJI*^fHueDx2*udsd8%jd6ACQ?F3A*JSz_J>UfzmFmTKJ>%Nmv)lOOE7Y8JJZp@Sqm zIW@jaW?1Gi5od)j9!y_8i3s-^H`I-D9eMa9Lc?z90ZYYSi#sV{y)%~>+;x3O*FUzf zB!Idrh(fqsV-5=Lb}{(XPI4rrGaRT=+-T5xyFSn16|p!{d|U2`(2B+OIjwsU!gE;p zyKVxvS+XVE)P_7^h|s0vJ)&1CcdIa$`Z5j0@lD`0>}}_5XQ;b5*PXqua}>uhzQDMZ zT+2Ma|1nY`=|u-AZVvLAUKP)Nmgx+?!!Za`U#|`dR5iGlnaz?&*<7@CEn&N*u+P^B z@J1XMNA~BtjzrUYzyE?6J96(a+giND_^$~k_|h%g+wY9)i;v<^-iYgm|P!C&f#k^lQg&5OPah^QHc z^HC{WEqYq`t1yhDi)f&bIJ=DQtWr#?uY2 zJ@dyvCyi)_FkjEVK4RCx-YfzSlZ0?;DzMLXm*`6=Q(PQ!#!qqSOIz=?j5N3Fd8sD2 zx|$;WuMh4`GPu%QGS1si@zJ)0pHNP0Kq=>Yy!8z5cU_%EC%<=l`b625jXcLV|M*rq z{23dy$spX-!T@Vy?EFSG4P-peQYuoPFEs^xeMg>Uvq})N+f`sz; z@qNaLMLjY+;eskG5oo&l;pDmD+B%hqB5#;x4BpWR7twi~klUo@G~tbt5j)>{yARdB z(G@kX>xLIow8V~TeICbAH1-8T8{=b~t$Z7C*Kz@>#`S-;Z)&!!!n`_k4{m6P-;_e__c1I_@;=UM=N^$et+eoM zy^}fq?FawEscghMONM(Ik77ZFdoCgNPxtJdQmv}b*3H^(O|)CkK9z!>I8@0OXDV!; zdP;rp%=e6Q3>t2|bh4~4P(e?-Y^m#w{VMK2G3d5=+OQ>|7I3N!^ilOc6IoiIInzH7}$d1i2i+#G)NnMW!H zcmj~~8S+^2J~oyyxjC-8sWX$mP4h}6vf|mj{52Eb7r2mUyF!`VEWERE&-l3GTs1j0 z60;RAskT%vZwtwsf(sRSY6%f|8wrs$6_V_yV>QkbXry_*5?@69O({?dNn`F4206rV zRNeu*a0i{Q7SXr&)8&yLre<^|!86}@KQZ+qno?jBN63%*!i;WyAz7pJWq7w!d%R`z z{Npt%v1h;$taeinb&X9ujX;>#d*y(nTjnS&us+_zg$qUhCW2xNMRcP&g^Y>P(|oPM z#i#`bxm~-(i8QZ~G^;|VBSQ%H;X>kwqm-aHHx#c%_J`f?^NiY z<72|7ea{NJ;QS~hEF#3+Gwa_Dlk|ryZccHJ2_Pyd6xmE@hllL8^%yox=wCO5T5n@iA2-8lEPk?0 zZ2pu*A=^RUIu!gCLcYl}Fb+X0{{pPn?7h${Yp{0i(r?WB zv;u<>8>h?|hLzt^Xl%nVP{Jh=BJpv3KWpJ_ItLN&2S$yqk=wqv_A|UEiK@MI((kb! zI@(qG4pb@8Orm;k`Ees?K*LbJVqGnFj`m3)a^P--k)ydLRuXUZLJR-EibLMshh|Tn z*JMd%BMos>ELG+=+= z>lonjn(ApK2v9)jTz{`)w@h(Hav;`Q=L9ZF1Y02@A3d)mkeQ-=1T7cn z^_nS$Uo#dXc;WMEaWo4@$wSQ~P%xAyK!H$bXb|*DoriKAAb@iI)_|*h|JM*f_IMw@ zl@yfcr4p4AqWOEb7gZW>KxGa+sDVFtxC)5!@R$GBvc1WG@BZv2$txu)Bq%3OEh+SO z3J+!>Rf(562K=oRP_7B-kM6+NZ0}&;vxSwugSi2fiKU^{@B069_lF*Tj2kel|7#E9 z4gm;0R3Q0Pj~_WL`M(~&)hzi{x95N0fII+A{k@yfAEi!y)gSeT{xHD!|7)p~zms^l zPw~*}4_>iA+2J&U?FB;f-Qh?@%~BjK|SgoOOn^n5J){B zZ>wWzX9#%GUvdzaN>NV*z;qFo3Vw-b#SsKMuxL$LY63 zV*it$-_&XE4Tm(qk2}E6k3hE1{soYnv5xhxIg*m|+Wi24Pyn*$_<1Zwn+r z`z1@O>ISU;oh5kWMqaHx+GlCZ6ngY|z} z;YR%J!Eye!B4TlmHGtfW!3zP9FZh z!La~*4p71Gmsop&9s{octe5{Wbu@&J$9gCR_DdMnChvYJ03rggn;#`qO!Rn|hYT9O zgh|V%hX3ypl701f7#W>^!Vwv@W_-Y*23Y#hg^rUx9tX&(V`cVh@$f5OF-ZVG3ICxA z3gnLm`sda3!7tz%YT`bq9VLMEqr3i2@pvp{X<11t{a6V~qg5kUD*59A9JoqCj? zlQTBZ`Lz`p#z&~}1MEBm2S2(wAHZ`z4&)zwDbi~zf;Rw2G63?UB47g7(H{l!IN`K6 z8eZ1`AgX}V{t>8);okr~R8Re%CiL^x0`O}8*B&D0aUfOWuQ)QW{xKkYpw>@sd3FrU zk0TJ+0OS0#WqTt7GWjQJf--VH-!cAsZ2SP=wY;|d6dUq288=t1Hk^T zJxn^Y&aY7nP0WEru|H1ygE2Rk3gprQy5b850s;*E&j#!n9!Zf`*Urk^!QMdLO4b0- z8lcGIpPFh11WSKbhW-1DJs_+3^*b~GH6`f~KtP25#C=%#(TpC)%R?ykn-4b7|1Bcp zwfilo{S_!F+tJ7}pa$*09`VPxjLGuRp#PJlhLvGsc7S>}pkzPBW%{;{qyAga2Sl;I z;-MU*vEK$*A2Wb(|3{QFhsU8vTicshn7A7J3dOXB-76ok$P0iU{vX8CZImc}xT-nrnbuBtZDbaSKg6lJNhU^IwNTkzamI-p=vIlgtMR zHox%9$_57d9hm4JL!seEP?J*>{*@Iy4EJ5rMF0oj0lori@}r`6P5tw50f1l3_5=NJ z#mc)WV0(kA{IjCV%>6t1|7+5J_PqFis>*}s^#BL`Wx(}Yk?Nma%XwVS8V)oFNIv54 z)olNunC;#27;?Pk*2X&WR+2h)X43!T&?c~s%Krk0clv-e?#ELBD)>hXm{*QpRP;5I zprszLYx;Yh)t@;bxUIU>0V290fSVt;(C#RYz~R%eGhk-?HO#~OL*>xp6#)@Q8F1qC zV(w{^I);s%tzg@fDse{|9^~esEHpzUFc!^^3Ob^<9dDV0xSUu zaQQ!O{US*ogYw%GJbNHqQ?dolPyVl*`OotBxqkXznw1ZnMg3}L@c&g!{b%^IpUO1< z1>Z*fZ{h#DtbVRx@fQMw&?6B3xuV6-Ed5*-;4eTz(SHy4-E)1&;q==X&F8~T5LurDJH0l0h+5NY6FG;pA;8v!^p GkpBlda(QI{ literal 0 HcmV?d00001 diff --git a/io-vector/build/reports/tests/test/classes/org.xbib.graphics.io.filters.AbsoluteToRelativeTransformsFilterTest.html b/io-vector/build/reports/tests/test/classes/org.xbib.graphics.io.filters.AbsoluteToRelativeTransformsFilterTest.html new file mode 100644 index 0000000..f14f528 --- /dev/null +++ b/io-vector/build/reports/tests/test/classes/org.xbib.graphics.io.filters.AbsoluteToRelativeTransformsFilterTest.html @@ -0,0 +1,106 @@ + + + + + +Test results - Class org.xbib.graphics.io.filters.AbsoluteToRelativeTransformsFilterTest + + + + + +

      +

      Class org.xbib.graphics.io.filters.AbsoluteToRelativeTransformsFilterTest

      + +
      + + + + + +
      +
      + + + + + + + +
      +
      +
      3
      +

      tests

      +
      +
      +
      +
      0
      +

      failures

      +
      +
      +
      +
      0
      +

      ignored

      +
      +
      +
      +
      0.016s
      +

      duration

      +
      +
      +
      +
      +
      +
      100%
      +

      successful

      +
      +
      +
      +
      + +
      +

      Tests

      + + + + + + + + + + + + + + + + + + + + + + + +
      TestDurationResult
      testAbsoluteAndRelativeTransformsIdentical()0.001spassed
      testRelativeTransformAfterDispose()0spassed
      testTranslateCorrect()0.015spassed
      +
      +
      + +
      + + diff --git a/io-vector/build/reports/tests/test/classes/org.xbib.graphics.io.filters.FillPaintedShapeAsImageFilterTest.html b/io-vector/build/reports/tests/test/classes/org.xbib.graphics.io.filters.FillPaintedShapeAsImageFilterTest.html new file mode 100644 index 0000000..4e96fc9 --- /dev/null +++ b/io-vector/build/reports/tests/test/classes/org.xbib.graphics.io.filters.FillPaintedShapeAsImageFilterTest.html @@ -0,0 +1,101 @@ + + + + + +Test results - Class org.xbib.graphics.io.filters.FillPaintedShapeAsImageFilterTest + + + + + +
      +

      Class org.xbib.graphics.io.filters.FillPaintedShapeAsImageFilterTest

      + +
      + + + + + +
      +
      + + + + + + + +
      +
      +
      2
      +

      tests

      +
      +
      +
      +
      0
      +

      failures

      +
      +
      +
      +
      0
      +

      ignored

      +
      +
      +
      +
      1.956s
      +

      duration

      +
      +
      +
      +
      +
      +
      100%
      +

      successful

      +
      +
      +
      +
      + +
      +

      Tests

      + + + + + + + + + + + + + + + + + + +
      TestDurationResult
      testFillShapeNotReplacedWithoutPaintCommand()0.013spassed
      testFillShapeReplacedWithDrawImage()1.943spassed
      +
      +
      + +
      + + diff --git a/io-vector/build/reports/tests/test/classes/org.xbib.graphics.io.filters.FilterTest.html b/io-vector/build/reports/tests/test/classes/org.xbib.graphics.io.filters.FilterTest.html new file mode 100644 index 0000000..390144f --- /dev/null +++ b/io-vector/build/reports/tests/test/classes/org.xbib.graphics.io.filters.FilterTest.html @@ -0,0 +1,106 @@ + + + + + +Test results - Class org.xbib.graphics.io.filters.FilterTest + + + + + +
      +

      Class org.xbib.graphics.io.filters.FilterTest

      + +
      + + + + + +
      +
      + + + + + + + +
      +
      +
      3
      +

      tests

      +
      +
      +
      +
      0
      +

      failures

      +
      +
      +
      +
      0
      +

      ignored

      +
      +
      +
      +
      0.004s
      +

      duration

      +
      +
      +
      +
      +
      +
      100%
      +

      successful

      +
      +
      +
      +
      + +
      +

      Tests

      + + + + + + + + + + + + + + + + + + + + + + + +
      TestDurationResult
      duplicate()0.001spassed
      filterAll()0.001spassed
      filterNone()0.002spassed
      +
      +
      + +
      + + diff --git a/io-vector/build/reports/tests/test/classes/org.xbib.graphics.io.filters.GroupingFilterTest.html b/io-vector/build/reports/tests/test/classes/org.xbib.graphics.io.filters.GroupingFilterTest.html new file mode 100644 index 0000000..1cf1308 --- /dev/null +++ b/io-vector/build/reports/tests/test/classes/org.xbib.graphics.io.filters.GroupingFilterTest.html @@ -0,0 +1,96 @@ + + + + + +Test results - Class org.xbib.graphics.io.filters.GroupingFilterTest + + + + + +
      +

      Class org.xbib.graphics.io.filters.GroupingFilterTest

      + +
      + + + + + +
      +
      + + + + + + + +
      +
      +
      1
      +

      tests

      +
      +
      +
      +
      0
      +

      failures

      +
      +
      +
      +
      0
      +

      ignored

      +
      +
      +
      +
      0.001s
      +

      duration

      +
      +
      +
      +
      +
      +
      100%
      +

      successful

      +
      +
      +
      +
      + +
      +

      Tests

      + + + + + + + + + + + + + +
      TestDurationResult
      filtered()0.001spassed
      +
      +
      + +
      + + diff --git a/io-vector/build/reports/tests/test/classes/org.xbib.graphics.io.vector.GraphicsStateTest.html b/io-vector/build/reports/tests/test/classes/org.xbib.graphics.io.vector.GraphicsStateTest.html new file mode 100644 index 0000000..899fc52 --- /dev/null +++ b/io-vector/build/reports/tests/test/classes/org.xbib.graphics.io.vector.GraphicsStateTest.html @@ -0,0 +1,106 @@ + + + + + +Test results - Class org.xbib.graphics.io.vector.GraphicsStateTest + + + + + +
      +

      Class org.xbib.graphics.io.vector.GraphicsStateTest

      + +
      + + + + + +
      +
      + + + + + + + +
      +
      +
      3
      +

      tests

      +
      +
      +
      +
      0
      +

      failures

      +
      +
      +
      +
      0
      +

      ignored

      +
      +
      +
      +
      0.003s
      +

      duration

      +
      +
      +
      +
      +
      +
      100%
      +

      successful

      +
      +
      +
      +
      + +
      +

      Tests

      + + + + + + + + + + + + + + + + + + + + + + + +
      TestDurationResult
      testClone()0.002spassed
      testEquals()0.001spassed
      testInitialStateIsEqualToGraphics2D()0spassed
      +
      +
      + +
      + + diff --git a/io-vector/build/reports/tests/test/classes/org.xbib.graphics.io.vector.TestUtilsTest.html b/io-vector/build/reports/tests/test/classes/org.xbib.graphics.io.vector.TestUtilsTest.html new file mode 100644 index 0000000..403186a --- /dev/null +++ b/io-vector/build/reports/tests/test/classes/org.xbib.graphics.io.vector.TestUtilsTest.html @@ -0,0 +1,131 @@ + + + + + +Test results - Class org.xbib.graphics.io.vector.TestUtilsTest + + + + + +
      +

      Class org.xbib.graphics.io.vector.TestUtilsTest

      + +
      + + + + + +
      +
      + + + + + + + +
      +
      +
      8
      +

      tests

      +
      +
      +
      +
      0
      +

      failures

      +
      +
      +
      +
      0
      +

      ignored

      +
      +
      +
      +
      0.005s
      +

      duration

      +
      +
      +
      +
      +
      +
      100%
      +

      successful

      +
      +
      +
      +
      + +
      +

      Tests

      + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
      TestDurationResult
      testParseXMLAttributesTag()0.001spassed
      testParseXmlCDATA()0spassed
      testParseXmlComment()0spassed
      testParseXmlDeclaration()0spassed
      testParseXmlDoctype()0.001spassed
      testParseXmlEmptyElement()0.001spassed
      testParseXmlEndTag()0.001spassed
      testParseXmlStartTag()0.001spassed
      +
      +
      + +
      + + diff --git a/io-vector/build/reports/tests/test/classes/org.xbib.graphics.io.vector.VectorGraphics2DTest.html b/io-vector/build/reports/tests/test/classes/org.xbib.graphics.io.vector.VectorGraphics2DTest.html new file mode 100644 index 0000000..ebf146f --- /dev/null +++ b/io-vector/build/reports/tests/test/classes/org.xbib.graphics.io.vector.VectorGraphics2DTest.html @@ -0,0 +1,106 @@ + + + + + +Test results - Class org.xbib.graphics.io.vector.VectorGraphics2DTest + + + + + +
      +

      Class org.xbib.graphics.io.vector.VectorGraphics2DTest

      + +
      + + + + + +
      +
      + + + + + + + +
      +
      +
      3
      +

      tests

      +
      +
      +
      +
      0
      +

      failures

      +
      +
      +
      +
      0
      +

      ignored

      +
      +
      +
      +
      0.006s
      +

      duration

      +
      +
      +
      +
      +
      +
      100%
      +

      successful

      +
      +
      +
      +
      + +
      +

      Tests

      + + + + + + + + + + + + + + + + + + + + + + + +
      TestDurationResult
      testCreateEmitsCreateCommand()0spassed
      testDisposeCommandEmitted()0.001spassed
      testEmptyVectorGraphics2DStartsWithCreateCommand()0.005spassed
      +
      +
      + +
      + + diff --git a/io-vector/build/reports/tests/test/classes/org.xbib.graphics.io.vector.eps.EPSProcessorTest.html b/io-vector/build/reports/tests/test/classes/org.xbib.graphics.io.vector.eps.EPSProcessorTest.html new file mode 100644 index 0000000..5f6a3f7 --- /dev/null +++ b/io-vector/build/reports/tests/test/classes/org.xbib.graphics.io.vector.eps.EPSProcessorTest.html @@ -0,0 +1,96 @@ + + + + + +Test results - Class org.xbib.graphics.io.vector.eps.EPSProcessorTest + + + + + +
      +

      Class org.xbib.graphics.io.vector.eps.EPSProcessorTest

      + +
      + + + + + +
      +
      + + + + + + + +
      +
      +
      1
      +

      tests

      +
      +
      +
      +
      0
      +

      failures

      +
      +
      +
      +
      0
      +

      ignored

      +
      +
      +
      +
      3.546s
      +

      duration

      +
      +
      +
      +
      +
      +
      100%
      +

      successful

      +
      +
      +
      +
      + +
      +

      Tests

      + + + + + + + + + + + + + +
      TestDurationResult
      envelopeForEmptyDocument()3.546spassed
      +
      +
      + +
      + + diff --git a/io-vector/build/reports/tests/test/classes/org.xbib.graphics.io.vector.pdf.PDFProcessorTest.html b/io-vector/build/reports/tests/test/classes/org.xbib.graphics.io.vector.pdf.PDFProcessorTest.html new file mode 100644 index 0000000..1b4711a --- /dev/null +++ b/io-vector/build/reports/tests/test/classes/org.xbib.graphics.io.vector.pdf.PDFProcessorTest.html @@ -0,0 +1,96 @@ + + + + + +Test results - Class org.xbib.graphics.io.vector.pdf.PDFProcessorTest + + + + + +
      +

      Class org.xbib.graphics.io.vector.pdf.PDFProcessorTest

      + +
      + + + + + +
      +
      + + + + + + + +
      +
      +
      1
      +

      tests

      +
      +
      +
      +
      0
      +

      failures

      +
      +
      +
      +
      0
      +

      ignored

      +
      +
      +
      +
      0.312s
      +

      duration

      +
      +
      +
      +
      +
      +
      100%
      +

      successful

      +
      +
      +
      +
      + +
      +

      Tests

      + + + + + + + + + + + + + +
      TestDurationResult
      envelopeForEmptyDocument()0.312spassed
      +
      +
      + +
      + + diff --git a/io-vector/build/reports/tests/test/classes/org.xbib.graphics.io.vector.svg.SVGProcessorTest.html b/io-vector/build/reports/tests/test/classes/org.xbib.graphics.io.vector.svg.SVGProcessorTest.html new file mode 100644 index 0000000..f8513e6 --- /dev/null +++ b/io-vector/build/reports/tests/test/classes/org.xbib.graphics.io.vector.svg.SVGProcessorTest.html @@ -0,0 +1,106 @@ + + + + + +Test results - Class org.xbib.graphics.io.vector.svg.SVGProcessorTest + + + + + +
      +

      Class org.xbib.graphics.io.vector.svg.SVGProcessorTest

      + +
      + + + + + +
      +
      + + + + + + + +
      +
      +
      3
      +

      tests

      +
      +
      +
      +
      0
      +

      failures

      +
      +
      +
      +
      0
      +

      ignored

      +
      +
      +
      +
      0.103s
      +

      duration

      +
      +
      +
      +
      +
      +
      100%
      +

      successful

      +
      +
      +
      +
      + +
      +

      Tests

      + + + + + + + + + + + + + + + + + + + + + + + +
      TestDurationResult
      drawShapeBlack()0.004spassed
      envelopeForEmptyDocument()0.005spassed
      fillShapeBlack()0.094spassed
      +
      +
      + +
      + + diff --git a/io-vector/build/reports/tests/test/classes/org.xbib.graphics.io.vector.util.ASCII85EncodeStreamTest.html b/io-vector/build/reports/tests/test/classes/org.xbib.graphics.io.vector.util.ASCII85EncodeStreamTest.html new file mode 100644 index 0000000..781e7e4 --- /dev/null +++ b/io-vector/build/reports/tests/test/classes/org.xbib.graphics.io.vector.util.ASCII85EncodeStreamTest.html @@ -0,0 +1,106 @@ + + + + + +Test results - Class org.xbib.graphics.io.vector.util.ASCII85EncodeStreamTest + + + + + +
      +

      Class org.xbib.graphics.io.vector.util.ASCII85EncodeStreamTest

      + +
      + + + + + +
      +
      + + + + + + + +
      +
      +
      3
      +

      tests

      +
      +
      +
      +
      0
      +

      failures

      +
      +
      +
      +
      0
      +

      ignored

      +
      +
      +
      +
      0.002s
      +

      duration

      +
      +
      +
      +
      +
      +
      100%
      +

      successful

      +
      +
      +
      +
      + +
      +

      Tests

      + + + + + + + + + + + + + + + + + + + + + + + +
      TestDurationResult
      testEmpty()0.001spassed
      testEncoding()0.001spassed
      testPadding()0spassed
      +
      +
      + +
      + + diff --git a/io-vector/build/reports/tests/test/classes/org.xbib.graphics.io.vector.util.Base64EncodeStreamTest.html b/io-vector/build/reports/tests/test/classes/org.xbib.graphics.io.vector.util.Base64EncodeStreamTest.html new file mode 100644 index 0000000..10f3724 --- /dev/null +++ b/io-vector/build/reports/tests/test/classes/org.xbib.graphics.io.vector.util.Base64EncodeStreamTest.html @@ -0,0 +1,106 @@ + + + + + +Test results - Class org.xbib.graphics.io.vector.util.Base64EncodeStreamTest + + + + + +
      +

      Class org.xbib.graphics.io.vector.util.Base64EncodeStreamTest

      + +
      + + + + + +
      +
      + + + + + + + +
      +
      +
      3
      +

      tests

      +
      +
      +
      +
      0
      +

      failures

      +
      +
      +
      +
      0
      +

      ignored

      +
      +
      +
      +
      0.002s
      +

      duration

      +
      +
      +
      +
      +
      +
      100%
      +

      successful

      +
      +
      +
      +
      + +
      +

      Tests

      + + + + + + + + + + + + + + + + + + + + + + + +
      TestDurationResult
      testEmpty()0spassed
      testEncoding()0.001spassed
      testPadding()0.001spassed
      +
      +
      + +
      + + diff --git a/io-vector/build/reports/tests/test/classes/org.xbib.graphics.io.vector.util.DataUtilsTest.html b/io-vector/build/reports/tests/test/classes/org.xbib.graphics.io.vector.util.DataUtilsTest.html new file mode 100644 index 0000000..55192d3 --- /dev/null +++ b/io-vector/build/reports/tests/test/classes/org.xbib.graphics.io.vector.util.DataUtilsTest.html @@ -0,0 +1,106 @@ + + + + + +Test results - Class org.xbib.graphics.io.vector.util.DataUtilsTest + + + + + +
      +

      Class org.xbib.graphics.io.vector.util.DataUtilsTest

      + +
      + + + + + +
      +
      + + + + + + + +
      +
      +
      3
      +

      tests

      +
      +
      +
      +
      0
      +

      failures

      +
      +
      +
      +
      0
      +

      ignored

      +
      +
      +
      +
      0.002s
      +

      duration

      +
      +
      +
      +
      +
      +
      100%
      +

      successful

      +
      +
      +
      +
      + +
      +

      Tests

      + + + + + + + + + + + + + + + + + + + + + + + +
      TestDurationResult
      stripComplexSubstring()0.001spassed
      stripTrailingSpaces()0.001spassed
      stripTrailingSpacesInMultilineString()0spassed
      +
      +
      + +
      + + diff --git a/io-vector/build/reports/tests/test/classes/org.xbib.graphics.io.vector.util.GraphicsUtilsTest.html b/io-vector/build/reports/tests/test/classes/org.xbib.graphics.io.vector.util.GraphicsUtilsTest.html new file mode 100644 index 0000000..39a7f0b --- /dev/null +++ b/io-vector/build/reports/tests/test/classes/org.xbib.graphics.io.vector.util.GraphicsUtilsTest.html @@ -0,0 +1,111 @@ + + + + + +Test results - Class org.xbib.graphics.io.vector.util.GraphicsUtilsTest + + + + + +
      +

      Class org.xbib.graphics.io.vector.util.GraphicsUtilsTest

      + +
      + + + + + +
      +
      + + + + + + + +
      +
      +
      4
      +

      tests

      +
      +
      +
      +
      0
      +

      failures

      +
      +
      +
      +
      0
      +

      ignored

      +
      +
      +
      +
      0.397s
      +

      duration

      +
      +
      +
      +
      +
      +
      100%
      +

      successful

      +
      +
      +
      +
      + +
      +

      Tests

      + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
      TestDurationResult
      testCloneShape()0.003spassed
      testHasAlpha()0spassed
      testPhysicalFont()0.370spassed
      testToBufferedImage()0.024spassed
      +
      +
      + +
      + + diff --git a/io-vector/build/reports/tests/test/css/base-style.css b/io-vector/build/reports/tests/test/css/base-style.css new file mode 100644 index 0000000..4afa73e --- /dev/null +++ b/io-vector/build/reports/tests/test/css/base-style.css @@ -0,0 +1,179 @@ + +body { + margin: 0; + padding: 0; + font-family: sans-serif; + font-size: 12pt; +} + +body, a, a:visited { + color: #303030; +} + +#content { + padding-left: 50px; + padding-right: 50px; + padding-top: 30px; + padding-bottom: 30px; +} + +#content h1 { + font-size: 160%; + margin-bottom: 10px; +} + +#footer { + margin-top: 100px; + font-size: 80%; + white-space: nowrap; +} + +#footer, #footer a { + color: #a0a0a0; +} + +#line-wrapping-toggle { + vertical-align: middle; +} + +#label-for-line-wrapping-toggle { + vertical-align: middle; +} + +ul { + margin-left: 0; +} + +h1, h2, h3 { + white-space: nowrap; +} + +h2 { + font-size: 120%; +} + +ul.tabLinks { + padding-left: 0; + padding-top: 10px; + padding-bottom: 10px; + overflow: auto; + min-width: 800px; + width: auto !important; + width: 800px; +} + +ul.tabLinks li { + float: left; + height: 100%; + list-style: none; + padding-left: 10px; + padding-right: 10px; + padding-top: 5px; + padding-bottom: 5px; + margin-bottom: 0; + -moz-border-radius: 7px; + border-radius: 7px; + margin-right: 25px; + border: solid 1px #d4d4d4; + background-color: #f0f0f0; +} + +ul.tabLinks li:hover { + background-color: #fafafa; +} + +ul.tabLinks li.selected { + background-color: #c5f0f5; + border-color: #c5f0f5; +} + +ul.tabLinks a { + font-size: 120%; + display: block; + outline: none; + text-decoration: none; + margin: 0; + padding: 0; +} + +ul.tabLinks li h2 { + margin: 0; + padding: 0; +} + +div.tab { +} + +div.selected { + display: block; +} + +div.deselected { + display: none; +} + +div.tab table { + min-width: 350px; + width: auto !important; + width: 350px; + border-collapse: collapse; +} + +div.tab th, div.tab table { + border-bottom: solid #d0d0d0 1px; +} + +div.tab th { + text-align: left; + white-space: nowrap; + padding-left: 6em; +} + +div.tab th:first-child { + padding-left: 0; +} + +div.tab td { + white-space: nowrap; + padding-left: 6em; + padding-top: 5px; + padding-bottom: 5px; +} + +div.tab td:first-child { + padding-left: 0; +} + +div.tab td.numeric, div.tab th.numeric { + text-align: right; +} + +span.code { + display: inline-block; + margin-top: 0em; + margin-bottom: 1em; +} + +span.code pre { + font-size: 11pt; + padding-top: 10px; + padding-bottom: 10px; + padding-left: 10px; + padding-right: 10px; + margin: 0; + background-color: #f7f7f7; + border: solid 1px #d0d0d0; + min-width: 700px; + width: auto !important; + width: 700px; +} + +span.wrapped pre { + word-wrap: break-word; + white-space: pre-wrap; + word-break: break-all; +} + +label.hidden { + display: none; +} \ No newline at end of file diff --git a/io-vector/build/reports/tests/test/css/style.css b/io-vector/build/reports/tests/test/css/style.css new file mode 100644 index 0000000..3dc4913 --- /dev/null +++ b/io-vector/build/reports/tests/test/css/style.css @@ -0,0 +1,84 @@ + +#summary { + margin-top: 30px; + margin-bottom: 40px; +} + +#summary table { + border-collapse: collapse; +} + +#summary td { + vertical-align: top; +} + +.breadcrumbs, .breadcrumbs a { + color: #606060; +} + +.infoBox { + width: 110px; + padding-top: 15px; + padding-bottom: 15px; + text-align: center; +} + +.infoBox p { + margin: 0; +} + +.counter, .percent { + font-size: 120%; + font-weight: bold; + margin-bottom: 8px; +} + +#duration { + width: 125px; +} + +#successRate, .summaryGroup { + border: solid 2px #d0d0d0; + -moz-border-radius: 10px; + border-radius: 10px; +} + +#successRate { + width: 140px; + margin-left: 35px; +} + +#successRate .percent { + font-size: 180%; +} + +.success, .success a { + color: #008000; +} + +div.success, #successRate.success { + background-color: #bbd9bb; + border-color: #008000; +} + +.failures, .failures a { + color: #b60808; +} + +.skipped, .skipped a { + color: #c09853; +} + +div.failures, #successRate.failures { + background-color: #ecdada; + border-color: #b60808; +} + +ul.linkList { + padding-left: 0; +} + +ul.linkList li { + list-style: none; + margin-bottom: 5px; +} diff --git a/io-vector/build/reports/tests/test/index.html b/io-vector/build/reports/tests/test/index.html new file mode 100644 index 0000000..6b4ccca --- /dev/null +++ b/io-vector/build/reports/tests/test/index.html @@ -0,0 +1,313 @@ + + + + + +Test results - Test Summary + + + + + +
      +

      Test Summary

      +
      + + + + + +
      +
      + + + + + + + +
      +
      +
      41
      +

      tests

      +
      +
      +
      +
      0
      +

      failures

      +
      +
      +
      +
      0
      +

      ignored

      +
      +
      +
      +
      6.355s
      +

      duration

      +
      +
      +
      +
      +
      +
      100%
      +

      successful

      +
      +
      +
      +
      + +
      +

      Packages

      + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
      PackageTestsFailuresIgnoredDurationSuccess rate
      +org.xbib.graphics.io.filters +9001.977s100%
      +org.xbib.graphics.io.vector +14000.014s100%
      +org.xbib.graphics.io.vector.eps +1003.546s100%
      +org.xbib.graphics.io.vector.pdf +1000.312s100%
      +org.xbib.graphics.io.vector.svg +3000.103s100%
      +org.xbib.graphics.io.vector.util +13000.403s100%
      +
      +
      +

      Classes

      + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
      ClassTestsFailuresIgnoredDurationSuccess rate
      +org.xbib.graphics.io.filters.AbsoluteToRelativeTransformsFilterTest +3000.016s100%
      +org.xbib.graphics.io.filters.FillPaintedShapeAsImageFilterTest +2001.956s100%
      +org.xbib.graphics.io.filters.FilterTest +3000.004s100%
      +org.xbib.graphics.io.filters.GroupingFilterTest +1000.001s100%
      +org.xbib.graphics.io.vector.GraphicsStateTest +3000.003s100%
      +org.xbib.graphics.io.vector.TestUtilsTest +8000.005s100%
      +org.xbib.graphics.io.vector.VectorGraphics2DTest +3000.006s100%
      +org.xbib.graphics.io.vector.eps.EPSProcessorTest +1003.546s100%
      +org.xbib.graphics.io.vector.pdf.PDFProcessorTest +1000.312s100%
      +org.xbib.graphics.io.vector.svg.SVGProcessorTest +3000.103s100%
      +org.xbib.graphics.io.vector.util.ASCII85EncodeStreamTest +3000.002s100%
      +org.xbib.graphics.io.vector.util.Base64EncodeStreamTest +3000.002s100%
      +org.xbib.graphics.io.vector.util.DataUtilsTest +3000.002s100%
      +org.xbib.graphics.io.vector.util.GraphicsUtilsTest +4000.397s100%
      +
      +
      + +
      + + diff --git a/io-vector/build/reports/tests/test/js/report.js b/io-vector/build/reports/tests/test/js/report.js new file mode 100644 index 0000000..83bab4a --- /dev/null +++ b/io-vector/build/reports/tests/test/js/report.js @@ -0,0 +1,194 @@ +(function (window, document) { + "use strict"; + + var tabs = {}; + + function changeElementClass(element, classValue) { + if (element.getAttribute("className")) { + element.setAttribute("className", classValue); + } else { + element.setAttribute("class", classValue); + } + } + + function getClassAttribute(element) { + if (element.getAttribute("className")) { + return element.getAttribute("className"); + } else { + return element.getAttribute("class"); + } + } + + function addClass(element, classValue) { + changeElementClass(element, getClassAttribute(element) + " " + classValue); + } + + function removeClass(element, classValue) { + changeElementClass(element, getClassAttribute(element).replace(classValue, "")); + } + + function initTabs() { + var container = document.getElementById("tabs"); + + tabs.tabs = findTabs(container); + tabs.titles = findTitles(tabs.tabs); + tabs.headers = findHeaders(container); + tabs.select = select; + tabs.deselectAll = deselectAll; + tabs.select(0); + + return true; + } + + function getCheckBox() { + return document.getElementById("line-wrapping-toggle"); + } + + function getLabelForCheckBox() { + return document.getElementById("label-for-line-wrapping-toggle"); + } + + function findCodeBlocks() { + var spans = document.getElementById("tabs").getElementsByTagName("span"); + var codeBlocks = []; + for (var i = 0; i < spans.length; ++i) { + if (spans[i].className.indexOf("code") >= 0) { + codeBlocks.push(spans[i]); + } + } + return codeBlocks; + } + + function forAllCodeBlocks(operation) { + var codeBlocks = findCodeBlocks(); + + for (var i = 0; i < codeBlocks.length; ++i) { + operation(codeBlocks[i], "wrapped"); + } + } + + function toggleLineWrapping() { + var checkBox = getCheckBox(); + + if (checkBox.checked) { + forAllCodeBlocks(addClass); + } else { + forAllCodeBlocks(removeClass); + } + } + + function initControls() { + if (findCodeBlocks().length > 0) { + var checkBox = getCheckBox(); + var label = getLabelForCheckBox(); + + checkBox.onclick = toggleLineWrapping; + checkBox.checked = false; + + removeClass(label, "hidden"); + } + } + + function switchTab() { + var id = this.id.substr(1); + + for (var i = 0; i < tabs.tabs.length; i++) { + if (tabs.tabs[i].id === id) { + tabs.select(i); + break; + } + } + + return false; + } + + function select(i) { + this.deselectAll(); + + changeElementClass(this.tabs[i], "tab selected"); + changeElementClass(this.headers[i], "selected"); + + while (this.headers[i].firstChild) { + this.headers[i].removeChild(this.headers[i].firstChild); + } + + var h2 = document.createElement("H2"); + + h2.appendChild(document.createTextNode(this.titles[i])); + this.headers[i].appendChild(h2); + } + + function deselectAll() { + for (var i = 0; i < this.tabs.length; i++) { + changeElementClass(this.tabs[i], "tab deselected"); + changeElementClass(this.headers[i], "deselected"); + + while (this.headers[i].firstChild) { + this.headers[i].removeChild(this.headers[i].firstChild); + } + + var a = document.createElement("A"); + + a.setAttribute("id", "ltab" + i); + a.setAttribute("href", "#tab" + i); + a.onclick = switchTab; + a.appendChild(document.createTextNode(this.titles[i])); + + this.headers[i].appendChild(a); + } + } + + function findTabs(container) { + return findChildElements(container, "DIV", "tab"); + } + + function findHeaders(container) { + var owner = findChildElements(container, "UL", "tabLinks"); + return findChildElements(owner[0], "LI", null); + } + + function findTitles(tabs) { + var titles = []; + + for (var i = 0; i < tabs.length; i++) { + var tab = tabs[i]; + var header = findChildElements(tab, "H2", null)[0]; + + header.parentNode.removeChild(header); + + if (header.innerText) { + titles.push(header.innerText); + } else { + titles.push(header.textContent); + } + } + + return titles; + } + + function findChildElements(container, name, targetClass) { + var elements = []; + var children = container.childNodes; + + for (var i = 0; i < children.length; i++) { + var child = children.item(i); + + if (child.nodeType === 1 && child.nodeName === name) { + if (targetClass && child.className.indexOf(targetClass) < 0) { + continue; + } + + elements.push(child); + } + } + + return elements; + } + + // Entry point. + + window.onload = function() { + initTabs(); + initControls(); + }; +} (window, window.document)); \ No newline at end of file diff --git a/io-vector/build/reports/tests/test/packages/org.xbib.graphics.io.filters.html b/io-vector/build/reports/tests/test/packages/org.xbib.graphics.io.filters.html new file mode 100644 index 0000000..46414bc --- /dev/null +++ b/io-vector/build/reports/tests/test/packages/org.xbib.graphics.io.filters.html @@ -0,0 +1,133 @@ + + + + + +Test results - Package org.xbib.graphics.io.filters + + + + + +
      +

      Package org.xbib.graphics.io.filters

      + +
      + + + + + +
      +
      + + + + + + + +
      +
      +
      9
      +

      tests

      +
      +
      +
      +
      0
      +

      failures

      +
      +
      +
      +
      0
      +

      ignored

      +
      +
      +
      +
      1.977s
      +

      duration

      +
      +
      +
      +
      +
      +
      100%
      +

      successful

      +
      +
      +
      +
      + +
      +

      Classes

      + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
      ClassTestsFailuresIgnoredDurationSuccess rate
      +AbsoluteToRelativeTransformsFilterTest +3000.016s100%
      +FillPaintedShapeAsImageFilterTest +2001.956s100%
      +FilterTest +3000.004s100%
      +GroupingFilterTest +1000.001s100%
      +
      +
      + +
      + + diff --git a/io-vector/build/reports/tests/test/packages/org.xbib.graphics.io.vector.eps.html b/io-vector/build/reports/tests/test/packages/org.xbib.graphics.io.vector.eps.html new file mode 100644 index 0000000..ed2fa13 --- /dev/null +++ b/io-vector/build/reports/tests/test/packages/org.xbib.graphics.io.vector.eps.html @@ -0,0 +1,103 @@ + + + + + +Test results - Package org.xbib.graphics.io.vector.eps + + + + + +
      +

      Package org.xbib.graphics.io.vector.eps

      + +
      + + + + + +
      +
      + + + + + + + +
      +
      +
      1
      +

      tests

      +
      +
      +
      +
      0
      +

      failures

      +
      +
      +
      +
      0
      +

      ignored

      +
      +
      +
      +
      3.546s
      +

      duration

      +
      +
      +
      +
      +
      +
      100%
      +

      successful

      +
      +
      +
      +
      + +
      +

      Classes

      + + + + + + + + + + + + + + + + + + + +
      ClassTestsFailuresIgnoredDurationSuccess rate
      +EPSProcessorTest +1003.546s100%
      +
      +
      + +
      + + diff --git a/io-vector/build/reports/tests/test/packages/org.xbib.graphics.io.vector.html b/io-vector/build/reports/tests/test/packages/org.xbib.graphics.io.vector.html new file mode 100644 index 0000000..11db431 --- /dev/null +++ b/io-vector/build/reports/tests/test/packages/org.xbib.graphics.io.vector.html @@ -0,0 +1,123 @@ + + + + + +Test results - Package org.xbib.graphics.io.vector + + + + + +
      +

      Package org.xbib.graphics.io.vector

      + +
      + + + + + +
      +
      + + + + + + + +
      +
      +
      14
      +

      tests

      +
      +
      +
      +
      0
      +

      failures

      +
      +
      +
      +
      0
      +

      ignored

      +
      +
      +
      +
      0.014s
      +

      duration

      +
      +
      +
      +
      +
      +
      100%
      +

      successful

      +
      +
      +
      +
      + +
      +

      Classes

      + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
      ClassTestsFailuresIgnoredDurationSuccess rate
      +GraphicsStateTest +3000.003s100%
      +TestUtilsTest +8000.005s100%
      +VectorGraphics2DTest +3000.006s100%
      +
      +
      + +
      + + diff --git a/io-vector/build/reports/tests/test/packages/org.xbib.graphics.io.vector.pdf.html b/io-vector/build/reports/tests/test/packages/org.xbib.graphics.io.vector.pdf.html new file mode 100644 index 0000000..4797191 --- /dev/null +++ b/io-vector/build/reports/tests/test/packages/org.xbib.graphics.io.vector.pdf.html @@ -0,0 +1,103 @@ + + + + + +Test results - Package org.xbib.graphics.io.vector.pdf + + + + + +
      +

      Package org.xbib.graphics.io.vector.pdf

      + +
      + + + + + +
      +
      + + + + + + + +
      +
      +
      1
      +

      tests

      +
      +
      +
      +
      0
      +

      failures

      +
      +
      +
      +
      0
      +

      ignored

      +
      +
      +
      +
      0.312s
      +

      duration

      +
      +
      +
      +
      +
      +
      100%
      +

      successful

      +
      +
      +
      +
      + +
      +

      Classes

      + + + + + + + + + + + + + + + + + + + +
      ClassTestsFailuresIgnoredDurationSuccess rate
      +PDFProcessorTest +1000.312s100%
      +
      +
      + +
      + + diff --git a/io-vector/build/reports/tests/test/packages/org.xbib.graphics.io.vector.svg.html b/io-vector/build/reports/tests/test/packages/org.xbib.graphics.io.vector.svg.html new file mode 100644 index 0000000..790872c --- /dev/null +++ b/io-vector/build/reports/tests/test/packages/org.xbib.graphics.io.vector.svg.html @@ -0,0 +1,103 @@ + + + + + +Test results - Package org.xbib.graphics.io.vector.svg + + + + + +
      +

      Package org.xbib.graphics.io.vector.svg

      + +
      + + + + + +
      +
      + + + + + + + +
      +
      +
      3
      +

      tests

      +
      +
      +
      +
      0
      +

      failures

      +
      +
      +
      +
      0
      +

      ignored

      +
      +
      +
      +
      0.103s
      +

      duration

      +
      +
      +
      +
      +
      +
      100%
      +

      successful

      +
      +
      +
      +
      + +
      +

      Classes

      + + + + + + + + + + + + + + + + + + + +
      ClassTestsFailuresIgnoredDurationSuccess rate
      +SVGProcessorTest +3000.103s100%
      +
      +
      + +
      + + diff --git a/io-vector/build/reports/tests/test/packages/org.xbib.graphics.io.vector.util.html b/io-vector/build/reports/tests/test/packages/org.xbib.graphics.io.vector.util.html new file mode 100644 index 0000000..3c61dde --- /dev/null +++ b/io-vector/build/reports/tests/test/packages/org.xbib.graphics.io.vector.util.html @@ -0,0 +1,133 @@ + + + + + +Test results - Package org.xbib.graphics.io.vector.util + + + + + +
      +

      Package org.xbib.graphics.io.vector.util

      + +
      + + + + + +
      +
      + + + + + + + +
      +
      +
      13
      +

      tests

      +
      +
      +
      +
      0
      +

      failures

      +
      +
      +
      +
      0
      +

      ignored

      +
      +
      +
      +
      0.403s
      +

      duration

      +
      +
      +
      +
      +
      +
      100%
      +

      successful

      +
      +
      +
      +
      + +
      +

      Classes

      + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
      ClassTestsFailuresIgnoredDurationSuccess rate
      +ASCII85EncodeStreamTest +3000.002s100%
      +Base64EncodeStreamTest +3000.002s100%
      +DataUtilsTest +3000.002s100%
      +GraphicsUtilsTest +4000.397s100%
      +
      +
      + +
      + + diff --git a/io-vector/build/test-results/test/TEST-org.xbib.graphics.io.filters.AbsoluteToRelativeTransformsFilterTest.xml b/io-vector/build/test-results/test/TEST-org.xbib.graphics.io.filters.AbsoluteToRelativeTransformsFilterTest.xml new file mode 100644 index 0000000..7048c5c --- /dev/null +++ b/io-vector/build/test-results/test/TEST-org.xbib.graphics.io.filters.AbsoluteToRelativeTransformsFilterTest.xml @@ -0,0 +1,9 @@ + + + + + + + + + diff --git a/io-vector/build/test-results/test/TEST-org.xbib.graphics.io.filters.FillPaintedShapeAsImageFilterTest.xml b/io-vector/build/test-results/test/TEST-org.xbib.graphics.io.filters.FillPaintedShapeAsImageFilterTest.xml new file mode 100644 index 0000000..29d9978 --- /dev/null +++ b/io-vector/build/test-results/test/TEST-org.xbib.graphics.io.filters.FillPaintedShapeAsImageFilterTest.xml @@ -0,0 +1,8 @@ + + + + + + + + diff --git a/io-vector/build/test-results/test/TEST-org.xbib.graphics.io.filters.FilterTest.xml b/io-vector/build/test-results/test/TEST-org.xbib.graphics.io.filters.FilterTest.xml new file mode 100644 index 0000000..fd9eb15 --- /dev/null +++ b/io-vector/build/test-results/test/TEST-org.xbib.graphics.io.filters.FilterTest.xml @@ -0,0 +1,9 @@ + + + + + + + + + diff --git a/io-vector/build/test-results/test/TEST-org.xbib.graphics.io.filters.GroupingFilterTest.xml b/io-vector/build/test-results/test/TEST-org.xbib.graphics.io.filters.GroupingFilterTest.xml new file mode 100644 index 0000000..9ac5228 --- /dev/null +++ b/io-vector/build/test-results/test/TEST-org.xbib.graphics.io.filters.GroupingFilterTest.xml @@ -0,0 +1,7 @@ + + + + + + + diff --git a/io-vector/build/test-results/test/TEST-org.xbib.graphics.io.vector.GraphicsStateTest.xml b/io-vector/build/test-results/test/TEST-org.xbib.graphics.io.vector.GraphicsStateTest.xml new file mode 100644 index 0000000..3cfa68e --- /dev/null +++ b/io-vector/build/test-results/test/TEST-org.xbib.graphics.io.vector.GraphicsStateTest.xml @@ -0,0 +1,9 @@ + + + + + + + + + diff --git a/io-vector/build/test-results/test/TEST-org.xbib.graphics.io.vector.TestUtilsTest.xml b/io-vector/build/test-results/test/TEST-org.xbib.graphics.io.vector.TestUtilsTest.xml new file mode 100644 index 0000000..807a49b --- /dev/null +++ b/io-vector/build/test-results/test/TEST-org.xbib.graphics.io.vector.TestUtilsTest.xml @@ -0,0 +1,14 @@ + + + + + + + + + + + + + + diff --git a/io-vector/build/test-results/test/TEST-org.xbib.graphics.io.vector.VectorGraphics2DTest.xml b/io-vector/build/test-results/test/TEST-org.xbib.graphics.io.vector.VectorGraphics2DTest.xml new file mode 100644 index 0000000..0c0968b --- /dev/null +++ b/io-vector/build/test-results/test/TEST-org.xbib.graphics.io.vector.VectorGraphics2DTest.xml @@ -0,0 +1,9 @@ + + + + + + + + + diff --git a/io-vector/build/test-results/test/TEST-org.xbib.graphics.io.vector.eps.EPSProcessorTest.xml b/io-vector/build/test-results/test/TEST-org.xbib.graphics.io.vector.eps.EPSProcessorTest.xml new file mode 100644 index 0000000..63b28f7 --- /dev/null +++ b/io-vector/build/test-results/test/TEST-org.xbib.graphics.io.vector.eps.EPSProcessorTest.xml @@ -0,0 +1,7 @@ + + + + + + + diff --git a/io-vector/build/test-results/test/TEST-org.xbib.graphics.io.vector.pdf.PDFProcessorTest.xml b/io-vector/build/test-results/test/TEST-org.xbib.graphics.io.vector.pdf.PDFProcessorTest.xml new file mode 100644 index 0000000..884ab97 --- /dev/null +++ b/io-vector/build/test-results/test/TEST-org.xbib.graphics.io.vector.pdf.PDFProcessorTest.xml @@ -0,0 +1,7 @@ + + + + + + + diff --git a/io-vector/build/test-results/test/TEST-org.xbib.graphics.io.vector.svg.SVGProcessorTest.xml b/io-vector/build/test-results/test/TEST-org.xbib.graphics.io.vector.svg.SVGProcessorTest.xml new file mode 100644 index 0000000..d1295dd --- /dev/null +++ b/io-vector/build/test-results/test/TEST-org.xbib.graphics.io.vector.svg.SVGProcessorTest.xml @@ -0,0 +1,9 @@ + + + + + + + + + diff --git a/io-vector/build/test-results/test/TEST-org.xbib.graphics.io.vector.util.ASCII85EncodeStreamTest.xml b/io-vector/build/test-results/test/TEST-org.xbib.graphics.io.vector.util.ASCII85EncodeStreamTest.xml new file mode 100644 index 0000000..b4fdfc9 --- /dev/null +++ b/io-vector/build/test-results/test/TEST-org.xbib.graphics.io.vector.util.ASCII85EncodeStreamTest.xml @@ -0,0 +1,9 @@ + + + + + + + + + diff --git a/io-vector/build/test-results/test/TEST-org.xbib.graphics.io.vector.util.Base64EncodeStreamTest.xml b/io-vector/build/test-results/test/TEST-org.xbib.graphics.io.vector.util.Base64EncodeStreamTest.xml new file mode 100644 index 0000000..6ef4051 --- /dev/null +++ b/io-vector/build/test-results/test/TEST-org.xbib.graphics.io.vector.util.Base64EncodeStreamTest.xml @@ -0,0 +1,9 @@ + + + + + + + + + diff --git a/io-vector/build/test-results/test/TEST-org.xbib.graphics.io.vector.util.DataUtilsTest.xml b/io-vector/build/test-results/test/TEST-org.xbib.graphics.io.vector.util.DataUtilsTest.xml new file mode 100644 index 0000000..0f97322 --- /dev/null +++ b/io-vector/build/test-results/test/TEST-org.xbib.graphics.io.vector.util.DataUtilsTest.xml @@ -0,0 +1,9 @@ + + + + + + + + + diff --git a/io-vector/build/test-results/test/TEST-org.xbib.graphics.io.vector.util.GraphicsUtilsTest.xml b/io-vector/build/test-results/test/TEST-org.xbib.graphics.io.vector.util.GraphicsUtilsTest.xml new file mode 100644 index 0000000..9a5a447 --- /dev/null +++ b/io-vector/build/test-results/test/TEST-org.xbib.graphics.io.vector.util.GraphicsUtilsTest.xml @@ -0,0 +1,10 @@ + + + + + + + + + + diff --git a/io-vector/build/test-results/test/binary/output.bin b/io-vector/build/test-results/test/binary/output.bin new file mode 100644 index 0000000..e69de29 diff --git a/io-vector/build/test-results/test/binary/output.bin.idx b/io-vector/build/test-results/test/binary/output.bin.idx new file mode 100644 index 0000000000000000000000000000000000000000..f76dd238ade08917e6712764a16a22005a50573d GIT binary patch literal 1 IcmZPo000310RR91 literal 0 HcmV?d00001 diff --git a/io-vector/build/test-results/test/binary/results.bin b/io-vector/build/test-results/test/binary/results.bin new file mode 100644 index 0000000000000000000000000000000000000000..214794e9d88e68267c6d1eb55716143a92944379 GIT binary patch literal 3972 zcmb_fTW=dh6h6)-B^26nr>%Pf7ue;pG{!BwI(G_ z3W|UhQ6wY;6oDFAA|T<`3h^uYGx!0BGqabhoz2D$o z>kBS%7R?sdOk}Gp(POueX`C9oD^`Ak@>nB1{>q_f&FJ&&U5th?wzOBdng&xMY_Eg{SMJlx?r0uXA- zf13pjk=r*slX3AeIA;)R@y7V!kAX3iBnzsRN{pf_uELQkNhy(biJo+7 z6bs57y_S@d*sC%cLT~%4*z!#{)?)*K>n%ChUec+t+$}i%X;OWKwit1VXWEvkZGRJc z{sWx&C@DWxDL2cimPU(Z*5HY&Nf`-NncZ#3v9~|MlM-dYtIVD)d!B2~`>0HcXWhj* zeu4v;HA)?>SL=$OESUQUalPD_s?}aOUA0=a zK^q>He5Z-v-L+s?1V1Z<55z-By{3im?>ojxU5BUS^pL>CnI@$~V^X!4X9J#oU(oBs zFr>2HQw)y{*mFBI;=~MRbz)lBbsCGrp=Dm{kOjIq+W(_?5PjYo8CwM+uxhUl!ekD;I!ac>=t8}@~E-i3R<>amkFQgO*UGCEZohzYJ92-Fr1!Kt;>G=?jL z1Wq)H;zqd(kq|6@j=m^+P1BzA+imJnBlJ6+3I{{EyD;&ggvH_#$CZdp+hW|Nkl^od z%$CkVQ3CN*!pqEABq1(0j}ewiv+&Z2P%bwd`G~5vg#b$6O6he_*LO_WB;oW-slU{A zD;Fl=7v-70(&{_LRrD+LR98=>Vc24~D*gt~_SRPKDc1EnJpcWU@m7dOVt5S69{Tf# z^4upJJGFzQ;xKxt;m?bsXeis!V;^tB3s8rv;xme}Z~{jD z%4IrJp652h3E8ygD8sqlrI<*Iw{5q>Hy!+6lmuSqiIS6ZT-rgCxG1_VF21U^4H?3g zm3Kh7A*waoSzwdsYnwDtEr+qwt}b#X3`{%qqnf9^%LODkoyZbn6YPMTt#bt@j4t@F>nmY P_8HmhC%|ZE?>qPx)aQh{ literal 0 HcmV?d00001 diff --git a/io-vector/build/tmp/compileJava/source-classes-mapping.txt b/io-vector/build/tmp/compileJava/source-classes-mapping.txt new file mode 100644 index 0000000..6dbfeb6 --- /dev/null +++ b/io-vector/build/tmp/compileJava/source-classes-mapping.txt @@ -0,0 +1,128 @@ +org/xbib/graphics/io/vector/commands/SetTransformCommand.java + org.xbib.graphics.io.vector.commands.SetTransformCommand +org/xbib/graphics/io/vector/commands/SetStrokeCommand.java + org.xbib.graphics.io.vector.commands.SetStrokeCommand +org/xbib/graphics/io/vector/filters/StateChangeGroupingFilter.java + org.xbib.graphics.io.vector.filters.StateChangeGroupingFilter +org/xbib/graphics/io/vector/commands/TransformCommand.java + org.xbib.graphics.io.vector.commands.TransformCommand +org/xbib/graphics/io/vector/PageSize.java + org.xbib.graphics.io.vector.PageSize +org/xbib/graphics/io/vector/util/AlphaToMaskOp.java + org.xbib.graphics.io.vector.util.AlphaToMaskOp +org/xbib/graphics/io/vector/commands/SetFontCommand.java + org.xbib.graphics.io.vector.commands.SetFontCommand +org/xbib/graphics/io/vector/commands/ShearCommand.java + org.xbib.graphics.io.vector.commands.ShearCommand +org/xbib/graphics/io/vector/commands/SetColorCommand.java + org.xbib.graphics.io.vector.commands.SetColorCommand +org/xbib/graphics/io/vector/commands/SetPaintCommand.java + org.xbib.graphics.io.vector.commands.SetPaintCommand +org/xbib/graphics/io/vector/util/FlateEncodeStream.java + org.xbib.graphics.io.vector.util.FlateEncodeStream +org/xbib/graphics/io/vector/filters/AbsoluteToRelativeTransformsFilter.java + org.xbib.graphics.io.vector.filters.AbsoluteToRelativeTransformsFilter +org/xbib/graphics/io/vector/commands/RotateCommand.java + org.xbib.graphics.io.vector.commands.RotateCommand +org/xbib/graphics/io/vector/pdf/GeneratedPayload.java + org.xbib.graphics.io.vector.pdf.GeneratedPayload +org/xbib/graphics/io/vector/commands/AffineTransformCommand.java + org.xbib.graphics.io.vector.commands.AffineTransformCommand +org/xbib/graphics/io/vector/ProcessorResult.java + org.xbib.graphics.io.vector.ProcessorResult +org/xbib/graphics/io/vector/util/GraphicsUtils.java + org.xbib.graphics.io.vector.util.GraphicsUtils + org.xbib.graphics.io.vector.util.GraphicsUtils$FontExpressivenessComparator +org/xbib/graphics/io/vector/util/VectorHints.java + org.xbib.graphics.io.vector.util.VectorHints + org.xbib.graphics.io.vector.util.VectorHints$Key + org.xbib.graphics.io.vector.util.VectorHints$Value +org/xbib/graphics/io/vector/Command.java + org.xbib.graphics.io.vector.Command +org/xbib/graphics/io/vector/commands/StateCommand.java + org.xbib.graphics.io.vector.commands.StateCommand +org/xbib/graphics/io/vector/GraphicsState.java + org.xbib.graphics.io.vector.GraphicsState +org/xbib/graphics/io/vector/util/ImageDataStream.java + org.xbib.graphics.io.vector.util.ImageDataStream + org.xbib.graphics.io.vector.util.ImageDataStream$Interleaving +org/xbib/graphics/io/vector/util/Base64EncodeStream.java + org.xbib.graphics.io.vector.util.Base64EncodeStream +org/xbib/graphics/io/vector/commands/ScaleCommand.java + org.xbib.graphics.io.vector.commands.ScaleCommand +org/xbib/graphics/io/vector/commands/FillShapeCommand.java + org.xbib.graphics.io.vector.commands.FillShapeCommand +org/xbib/graphics/io/vector/commands/SetCompositeCommand.java + org.xbib.graphics.io.vector.commands.SetCompositeCommand +org/xbib/graphics/io/vector/commands/CreateCommand.java + org.xbib.graphics.io.vector.commands.CreateCommand +module-info.java + module-info +org/xbib/graphics/io/vector/pdf/Resources.java + org.xbib.graphics.io.vector.pdf.Resources +org/xbib/graphics/io/vector/filters/Filter.java + org.xbib.graphics.io.vector.filters.Filter +org/xbib/graphics/io/vector/filters/GroupingFilter.java + org.xbib.graphics.io.vector.filters.GroupingFilter +org/xbib/graphics/io/vector/Processor.java + org.xbib.graphics.io.vector.Processor +org/xbib/graphics/io/vector/commands/SetBackgroundCommand.java + org.xbib.graphics.io.vector.commands.SetBackgroundCommand +org/xbib/graphics/io/vector/pdf/PDFObject.java + org.xbib.graphics.io.vector.pdf.PDFObject +org/xbib/graphics/io/vector/util/ASCII85EncodeStream.java + org.xbib.graphics.io.vector.util.ASCII85EncodeStream +org/xbib/graphics/io/vector/util/LineWrapOutputStream.java + org.xbib.graphics.io.vector.util.LineWrapOutputStream +org/xbib/graphics/io/vector/commands/SetHintCommand.java + org.xbib.graphics.io.vector.commands.SetHintCommand +org/xbib/graphics/io/vector/commands/TranslateCommand.java + org.xbib.graphics.io.vector.commands.TranslateCommand +org/xbib/graphics/io/vector/commands/Group.java + org.xbib.graphics.io.vector.commands.Group +org/xbib/graphics/io/vector/VectorGraphicsFormat.java + org.xbib.graphics.io.vector.VectorGraphicsFormat +org/xbib/graphics/io/vector/commands/DrawShapeCommand.java + org.xbib.graphics.io.vector.commands.DrawShapeCommand +org/xbib/graphics/io/vector/filters/FillPaintedShapeAsImageFilter.java + org.xbib.graphics.io.vector.filters.FillPaintedShapeAsImageFilter +org/xbib/graphics/io/vector/commands/SetXORModeCommand.java + org.xbib.graphics.io.vector.commands.SetXORModeCommand +org/xbib/graphics/io/vector/commands/DrawImageCommand.java + org.xbib.graphics.io.vector.commands.DrawImageCommand +org/xbib/graphics/io/vector/filters/OptimizeFilter.java + org.xbib.graphics.io.vector.filters.OptimizeFilter +org/xbib/graphics/io/vector/commands/SetClipCommand.java + org.xbib.graphics.io.vector.commands.SetClipCommand +org/xbib/graphics/io/vector/pdf/Payload.java + org.xbib.graphics.io.vector.pdf.Payload +org/xbib/graphics/io/vector/commands/DrawStringCommand.java + org.xbib.graphics.io.vector.commands.DrawStringCommand +org/xbib/graphics/io/vector/util/FormattingWriter.java + org.xbib.graphics.io.vector.util.FormattingWriter +org/xbib/graphics/io/vector/pdf/SizePayload.java + org.xbib.graphics.io.vector.pdf.SizePayload +org/xbib/graphics/io/vector/eps/EPSProcessorResult.java + org.xbib.graphics.io.vector.eps.EPSProcessorResult +org/xbib/graphics/io/vector/commands/DisposeCommand.java + org.xbib.graphics.io.vector.commands.DisposeCommand +org/xbib/graphics/io/vector/VectorGraphics2D.java + org.xbib.graphics.io.vector.VectorGraphics2D +org/xbib/graphics/io/vector/svg/SVGGraphics2D.java + org.xbib.graphics.io.vector.svg.SVGGraphics2D +org/xbib/graphics/io/vector/pdf/PDFGraphics2D.java + org.xbib.graphics.io.vector.pdf.PDFGraphics2D +org/xbib/graphics/io/vector/pdf/PDFProcessor.java + org.xbib.graphics.io.vector.pdf.PDFProcessor +org/xbib/graphics/io/vector/pdf/PDFProcessorResult.java + org.xbib.graphics.io.vector.pdf.PDFProcessorResult +org/xbib/graphics/io/vector/util/DataUtils.java + org.xbib.graphics.io.vector.util.DataUtils +org/xbib/graphics/io/vector/svg/SVGProcessorResult.java + org.xbib.graphics.io.vector.svg.SVGProcessorResult +org/xbib/graphics/io/vector/eps/EPSGraphics2D.java + org.xbib.graphics.io.vector.eps.EPSGraphics2D +org/xbib/graphics/io/vector/eps/EPSProcessor.java + org.xbib.graphics.io.vector.eps.EPSProcessor +org/xbib/graphics/io/vector/svg/SVGProcessor.java + org.xbib.graphics.io.vector.svg.SVGProcessor diff --git a/io-vector/build/tmp/compileTestJava/source-classes-mapping.txt b/io-vector/build/tmp/compileTestJava/source-classes-mapping.txt new file mode 100644 index 0000000..a68b65b --- /dev/null +++ b/io-vector/build/tmp/compileTestJava/source-classes-mapping.txt @@ -0,0 +1,70 @@ +org/xbib/graphics/io/filters/GroupingFilterTest.java + org.xbib.graphics.io.filters.GroupingFilterTest + org.xbib.graphics.io.filters.GroupingFilterTest$1 +org/xbib/graphics/io/visual/PaintTest.java + org.xbib.graphics.io.visual.PaintTest +org/xbib/graphics/io/visual/FontTest.java + org.xbib.graphics.io.visual.FontTest +org/xbib/graphics/io/vector/util/Base64EncodeStreamTest.java + org.xbib.graphics.io.vector.util.Base64EncodeStreamTest +org/xbib/graphics/io/vector/util/DataUtilsTest.java + org.xbib.graphics.io.vector.util.DataUtilsTest +org/xbib/graphics/io/visual/StrokeTest.java + org.xbib.graphics.io.visual.StrokeTest +org/xbib/graphics/io/visual/TransformTest.java + org.xbib.graphics.io.visual.TransformTest +org/xbib/graphics/io/vector/TestUtils.java + org.xbib.graphics.io.vector.TestUtils + org.xbib.graphics.io.vector.TestUtils$Template + org.xbib.graphics.io.vector.TestUtils$XMLFragment + org.xbib.graphics.io.vector.TestUtils$XMLFragment$FragmentType +org/xbib/graphics/io/filters/FilterTest.java + org.xbib.graphics.io.filters.FilterTest + org.xbib.graphics.io.filters.FilterTest$1 + org.xbib.graphics.io.filters.FilterTest$2 + org.xbib.graphics.io.filters.FilterTest$3 +org/xbib/graphics/io/vector/TestUtilsTest.java + org.xbib.graphics.io.vector.TestUtilsTest +org/xbib/graphics/io/vector/GraphicsStateTest.java + org.xbib.graphics.io.vector.GraphicsStateTest +org/xbib/graphics/io/visual/ColorTest.java + org.xbib.graphics.io.visual.ColorTest +org/xbib/graphics/io/visual/ClippingTest.java + org.xbib.graphics.io.visual.ClippingTest +org/xbib/graphics/io/visual/EmptyFileTest.java + org.xbib.graphics.io.visual.EmptyFileTest +org/xbib/graphics/io/vector/util/ASCII85EncodeStreamTest.java + org.xbib.graphics.io.vector.util.ASCII85EncodeStreamTest +org/xbib/graphics/io/vector/util/GraphicsUtilsTest.java + org.xbib.graphics.io.vector.util.GraphicsUtilsTest + org.xbib.graphics.io.vector.util.GraphicsUtilsTest$1 +org/xbib/graphics/io/visual/ImageTest.java + org.xbib.graphics.io.visual.ImageTest +org/xbib/graphics/io/visual/SwingExportTest.java + org.xbib.graphics.io.visual.SwingExportTest +org/xbib/graphics/io/visual/ShapesTest.java + org.xbib.graphics.io.visual.ShapesTest +org/xbib/graphics/io/visual/TestBrowser.java + org.xbib.graphics.io.visual.TestBrowser + org.xbib.graphics.io.visual.TestBrowser$1 + org.xbib.graphics.io.visual.TestBrowser$2 + org.xbib.graphics.io.visual.TestBrowser$3 + org.xbib.graphics.io.visual.TestBrowser$ImageComparisonPanel + org.xbib.graphics.io.visual.TestBrowser$ImageDisplayPanel + org.xbib.graphics.io.visual.TestBrowser$ImageFormat +org/xbib/graphics/io/visual/CharacterTest.java + org.xbib.graphics.io.visual.CharacterTest +org/xbib/graphics/io/filters/FillPaintedShapeAsImageFilterTest.java + org.xbib.graphics.io.filters.FillPaintedShapeAsImageFilterTest +org/xbib/graphics/io/vector/pdf/PDFProcessorTest.java + org.xbib.graphics.io.vector.pdf.PDFProcessorTest +org/xbib/graphics/io/vector/VectorGraphics2DTest.java + org.xbib.graphics.io.vector.VectorGraphics2DTest +org/xbib/graphics/io/vector/eps/EPSProcessorTest.java + org.xbib.graphics.io.vector.eps.EPSProcessorTest +org/xbib/graphics/io/vector/svg/SVGProcessorTest.java + org.xbib.graphics.io.vector.svg.SVGProcessorTest +org/xbib/graphics/io/visual/AbstractTest.java + org.xbib.graphics.io.visual.AbstractTest +org/xbib/graphics/io/filters/AbsoluteToRelativeTransformsFilterTest.java + org.xbib.graphics.io.filters.AbsoluteToRelativeTransformsFilterTest diff --git a/io-vector/build/tmp/jar/MANIFEST.MF b/io-vector/build/tmp/jar/MANIFEST.MF new file mode 100644 index 0000000..0ea94c1 --- /dev/null +++ b/io-vector/build/tmp/jar/MANIFEST.MF @@ -0,0 +1,3 @@ +Manifest-Version: 1.0 +Implementation-Version: 3.0.0 + diff --git a/io-vector/build/tmp/javadoc/javadoc.options b/io-vector/build/tmp/javadoc/javadoc.options new file mode 100644 index 0000000..b5a992b --- /dev/null +++ b/io-vector/build/tmp/javadoc/javadoc.options @@ -0,0 +1,69 @@ +--module-path '/Users/joerg/Projects/github/jprante/graphics/io-vector/build/classes/java/main' +-Xdoclint:none '-quiet' +-d '/Users/joerg/Projects/github/jprante/graphics/io-vector/build/docs/javadoc' +-doctitle 'io-vector 3.0.0 API' +-notimestamp +-quiet +-windowtitle 'io-vector 3.0.0 API' +'/Users/joerg/Projects/github/jprante/graphics/io-vector/src/main/java/module-info.java' +'/Users/joerg/Projects/github/jprante/graphics/io-vector/src/main/java/org/xbib/graphics/io/vector/Command.java' +'/Users/joerg/Projects/github/jprante/graphics/io-vector/src/main/java/org/xbib/graphics/io/vector/commands/AffineTransformCommand.java' +'/Users/joerg/Projects/github/jprante/graphics/io-vector/src/main/java/org/xbib/graphics/io/vector/commands/CreateCommand.java' +'/Users/joerg/Projects/github/jprante/graphics/io-vector/src/main/java/org/xbib/graphics/io/vector/commands/DisposeCommand.java' +'/Users/joerg/Projects/github/jprante/graphics/io-vector/src/main/java/org/xbib/graphics/io/vector/commands/DrawImageCommand.java' +'/Users/joerg/Projects/github/jprante/graphics/io-vector/src/main/java/org/xbib/graphics/io/vector/commands/DrawShapeCommand.java' +'/Users/joerg/Projects/github/jprante/graphics/io-vector/src/main/java/org/xbib/graphics/io/vector/commands/DrawStringCommand.java' +'/Users/joerg/Projects/github/jprante/graphics/io-vector/src/main/java/org/xbib/graphics/io/vector/commands/FillShapeCommand.java' +'/Users/joerg/Projects/github/jprante/graphics/io-vector/src/main/java/org/xbib/graphics/io/vector/commands/Group.java' +'/Users/joerg/Projects/github/jprante/graphics/io-vector/src/main/java/org/xbib/graphics/io/vector/commands/RotateCommand.java' +'/Users/joerg/Projects/github/jprante/graphics/io-vector/src/main/java/org/xbib/graphics/io/vector/commands/ScaleCommand.java' +'/Users/joerg/Projects/github/jprante/graphics/io-vector/src/main/java/org/xbib/graphics/io/vector/commands/SetBackgroundCommand.java' +'/Users/joerg/Projects/github/jprante/graphics/io-vector/src/main/java/org/xbib/graphics/io/vector/commands/SetClipCommand.java' +'/Users/joerg/Projects/github/jprante/graphics/io-vector/src/main/java/org/xbib/graphics/io/vector/commands/SetColorCommand.java' +'/Users/joerg/Projects/github/jprante/graphics/io-vector/src/main/java/org/xbib/graphics/io/vector/commands/SetCompositeCommand.java' +'/Users/joerg/Projects/github/jprante/graphics/io-vector/src/main/java/org/xbib/graphics/io/vector/commands/SetFontCommand.java' +'/Users/joerg/Projects/github/jprante/graphics/io-vector/src/main/java/org/xbib/graphics/io/vector/commands/SetHintCommand.java' +'/Users/joerg/Projects/github/jprante/graphics/io-vector/src/main/java/org/xbib/graphics/io/vector/commands/SetPaintCommand.java' +'/Users/joerg/Projects/github/jprante/graphics/io-vector/src/main/java/org/xbib/graphics/io/vector/commands/SetStrokeCommand.java' +'/Users/joerg/Projects/github/jprante/graphics/io-vector/src/main/java/org/xbib/graphics/io/vector/commands/SetTransformCommand.java' +'/Users/joerg/Projects/github/jprante/graphics/io-vector/src/main/java/org/xbib/graphics/io/vector/commands/SetXORModeCommand.java' +'/Users/joerg/Projects/github/jprante/graphics/io-vector/src/main/java/org/xbib/graphics/io/vector/commands/ShearCommand.java' +'/Users/joerg/Projects/github/jprante/graphics/io-vector/src/main/java/org/xbib/graphics/io/vector/commands/StateCommand.java' +'/Users/joerg/Projects/github/jprante/graphics/io-vector/src/main/java/org/xbib/graphics/io/vector/commands/TransformCommand.java' +'/Users/joerg/Projects/github/jprante/graphics/io-vector/src/main/java/org/xbib/graphics/io/vector/commands/TranslateCommand.java' +'/Users/joerg/Projects/github/jprante/graphics/io-vector/src/main/java/org/xbib/graphics/io/vector/eps/EPSGraphics2D.java' +'/Users/joerg/Projects/github/jprante/graphics/io-vector/src/main/java/org/xbib/graphics/io/vector/eps/EPSProcessor.java' +'/Users/joerg/Projects/github/jprante/graphics/io-vector/src/main/java/org/xbib/graphics/io/vector/eps/EPSProcessorResult.java' +'/Users/joerg/Projects/github/jprante/graphics/io-vector/src/main/java/org/xbib/graphics/io/vector/filters/AbsoluteToRelativeTransformsFilter.java' +'/Users/joerg/Projects/github/jprante/graphics/io-vector/src/main/java/org/xbib/graphics/io/vector/filters/FillPaintedShapeAsImageFilter.java' +'/Users/joerg/Projects/github/jprante/graphics/io-vector/src/main/java/org/xbib/graphics/io/vector/filters/Filter.java' +'/Users/joerg/Projects/github/jprante/graphics/io-vector/src/main/java/org/xbib/graphics/io/vector/filters/GroupingFilter.java' +'/Users/joerg/Projects/github/jprante/graphics/io-vector/src/main/java/org/xbib/graphics/io/vector/filters/OptimizeFilter.java' +'/Users/joerg/Projects/github/jprante/graphics/io-vector/src/main/java/org/xbib/graphics/io/vector/filters/StateChangeGroupingFilter.java' +'/Users/joerg/Projects/github/jprante/graphics/io-vector/src/main/java/org/xbib/graphics/io/vector/GraphicsState.java' +'/Users/joerg/Projects/github/jprante/graphics/io-vector/src/main/java/org/xbib/graphics/io/vector/PageSize.java' +'/Users/joerg/Projects/github/jprante/graphics/io-vector/src/main/java/org/xbib/graphics/io/vector/pdf/GeneratedPayload.java' +'/Users/joerg/Projects/github/jprante/graphics/io-vector/src/main/java/org/xbib/graphics/io/vector/pdf/Payload.java' +'/Users/joerg/Projects/github/jprante/graphics/io-vector/src/main/java/org/xbib/graphics/io/vector/pdf/PDFGraphics2D.java' +'/Users/joerg/Projects/github/jprante/graphics/io-vector/src/main/java/org/xbib/graphics/io/vector/pdf/PDFObject.java' +'/Users/joerg/Projects/github/jprante/graphics/io-vector/src/main/java/org/xbib/graphics/io/vector/pdf/PDFProcessor.java' +'/Users/joerg/Projects/github/jprante/graphics/io-vector/src/main/java/org/xbib/graphics/io/vector/pdf/PDFProcessorResult.java' +'/Users/joerg/Projects/github/jprante/graphics/io-vector/src/main/java/org/xbib/graphics/io/vector/pdf/Resources.java' +'/Users/joerg/Projects/github/jprante/graphics/io-vector/src/main/java/org/xbib/graphics/io/vector/pdf/SizePayload.java' +'/Users/joerg/Projects/github/jprante/graphics/io-vector/src/main/java/org/xbib/graphics/io/vector/Processor.java' +'/Users/joerg/Projects/github/jprante/graphics/io-vector/src/main/java/org/xbib/graphics/io/vector/ProcessorResult.java' +'/Users/joerg/Projects/github/jprante/graphics/io-vector/src/main/java/org/xbib/graphics/io/vector/svg/SVGGraphics2D.java' +'/Users/joerg/Projects/github/jprante/graphics/io-vector/src/main/java/org/xbib/graphics/io/vector/svg/SVGProcessor.java' +'/Users/joerg/Projects/github/jprante/graphics/io-vector/src/main/java/org/xbib/graphics/io/vector/svg/SVGProcessorResult.java' +'/Users/joerg/Projects/github/jprante/graphics/io-vector/src/main/java/org/xbib/graphics/io/vector/util/AlphaToMaskOp.java' +'/Users/joerg/Projects/github/jprante/graphics/io-vector/src/main/java/org/xbib/graphics/io/vector/util/ASCII85EncodeStream.java' +'/Users/joerg/Projects/github/jprante/graphics/io-vector/src/main/java/org/xbib/graphics/io/vector/util/Base64EncodeStream.java' +'/Users/joerg/Projects/github/jprante/graphics/io-vector/src/main/java/org/xbib/graphics/io/vector/util/DataUtils.java' +'/Users/joerg/Projects/github/jprante/graphics/io-vector/src/main/java/org/xbib/graphics/io/vector/util/FlateEncodeStream.java' +'/Users/joerg/Projects/github/jprante/graphics/io-vector/src/main/java/org/xbib/graphics/io/vector/util/FormattingWriter.java' +'/Users/joerg/Projects/github/jprante/graphics/io-vector/src/main/java/org/xbib/graphics/io/vector/util/GraphicsUtils.java' +'/Users/joerg/Projects/github/jprante/graphics/io-vector/src/main/java/org/xbib/graphics/io/vector/util/ImageDataStream.java' +'/Users/joerg/Projects/github/jprante/graphics/io-vector/src/main/java/org/xbib/graphics/io/vector/util/LineWrapOutputStream.java' +'/Users/joerg/Projects/github/jprante/graphics/io-vector/src/main/java/org/xbib/graphics/io/vector/util/VectorHints.java' +'/Users/joerg/Projects/github/jprante/graphics/io-vector/src/main/java/org/xbib/graphics/io/vector/VectorGraphics2D.java' +'/Users/joerg/Projects/github/jprante/graphics/io-vector/src/main/java/org/xbib/graphics/io/vector/VectorGraphicsFormat.java' diff --git a/io-vector/build/tmp/javadocJar/MANIFEST.MF b/io-vector/build/tmp/javadocJar/MANIFEST.MF new file mode 100644 index 0000000..58630c0 --- /dev/null +++ b/io-vector/build/tmp/javadocJar/MANIFEST.MF @@ -0,0 +1,2 @@ +Manifest-Version: 1.0 + diff --git a/io-vector/build/tmp/sourcesJar/MANIFEST.MF b/io-vector/build/tmp/sourcesJar/MANIFEST.MF new file mode 100644 index 0000000..58630c0 --- /dev/null +++ b/io-vector/build/tmp/sourcesJar/MANIFEST.MF @@ -0,0 +1,2 @@ +Manifest-Version: 1.0 + diff --git a/io-vector/src/main/java/module-info.java b/io-vector/src/main/java/module-info.java new file mode 100644 index 0000000..d07255e --- /dev/null +++ b/io-vector/src/main/java/module-info.java @@ -0,0 +1,8 @@ +module org.xbib.graphics.io.vector { + exports org.xbib.graphics.io.vector; + exports org.xbib.graphics.io.vector.eps; + exports org.xbib.graphics.io.vector.pdf; + exports org.xbib.graphics.io.vector.svg; + + requires transitive java.desktop; +} \ No newline at end of file diff --git a/io-vector/src/main/java/org/xbib/graphics/io/vector/Command.java b/io-vector/src/main/java/org/xbib/graphics/io/vector/Command.java new file mode 100644 index 0000000..b452536 --- /dev/null +++ b/io-vector/src/main/java/org/xbib/graphics/io/vector/Command.java @@ -0,0 +1,29 @@ +package org.xbib.graphics.io.vector; + +public abstract class Command { + + private final T value; + + public Command(T value) { + this.value = value; + } + + public T getValue() { + return value; + } + + @Override + public String toString() { + return String.format("%s[value=%s]", getClass().getName(), getValue()); + } + + @Override + public boolean equals(Object obj) { + if (obj == null || !getClass().equals(obj.getClass())) { + return false; + } + Command o = (Command) obj; + return value == o.value || value.equals(o.value); + } +} + diff --git a/io-vector/src/main/java/org/xbib/graphics/io/vector/GraphicsState.java b/io-vector/src/main/java/org/xbib/graphics/io/vector/GraphicsState.java new file mode 100644 index 0000000..c85ec8d --- /dev/null +++ b/io-vector/src/main/java/org/xbib/graphics/io/vector/GraphicsState.java @@ -0,0 +1,274 @@ +package org.xbib.graphics.io.vector; + +import org.xbib.graphics.io.vector.util.GraphicsUtils; +import java.awt.AlphaComposite; +import java.awt.BasicStroke; +import java.awt.Color; +import java.awt.Composite; +import java.awt.Font; +import java.awt.Paint; +import java.awt.RenderingHints; +import java.awt.Shape; +import java.awt.Stroke; +import java.awt.geom.AffineTransform; +import java.awt.geom.NoninvertibleTransformException; +import java.awt.geom.Rectangle2D; +import java.util.Objects; + +public class GraphicsState implements Cloneable { + /** + * Default background color. + */ + public static final Color DEFAULT_BACKGROUND = Color.BLACK; + /** + * Default color. + */ + public static final Color DEFAULT_COLOR = Color.WHITE; + /** + * Default clipping shape. + */ + public static final Shape DEFAULT_CLIP = null; + /** + * Default composite mode. + */ + public static final Composite DEFAULT_COMPOSITE = AlphaComposite.SrcOver; + /** + * Default font. + */ + public static final Font DEFAULT_FONT = Font.decode(null); + /** + * Default paint. + */ + public static final Color DEFAULT_PAINT = DEFAULT_COLOR; + /** + * Default stroke. + */ + public static final Stroke DEFAULT_STROKE = new BasicStroke(); + /** + * Default transformation. + */ + public static final AffineTransform DEFAULT_TRANSFORM = + new AffineTransform(); + /** + * Default XOR mode. + */ + public static final Color DEFAULT_XOR_MODE = Color.BLACK; + + /** + * Rendering hints. + */ + private RenderingHints hints; + /** + * Current background color. + */ + private Color background; + /** + * Current foreground color. + */ + private Color color; + /** + * Shape used for clipping paint operations. + */ + private Shape clip; + /** + * Method used for compositing. + */ + private Composite composite; + /** + * Current font. + */ + private Font font; + /** + * Paint used to fill shapes. + */ + private Paint paint; + /** + * Stroke used for drawing shapes. + */ + private Stroke stroke; + /** + * Current transformation matrix. + */ + private AffineTransform transform; + /** + * XOR mode used for rendering. + */ + private Color xorMode; + + public GraphicsState() { + hints = new RenderingHints(null); + background = DEFAULT_BACKGROUND; + color = DEFAULT_COLOR; + clip = DEFAULT_CLIP; + composite = DEFAULT_COMPOSITE; + font = DEFAULT_FONT; + paint = DEFAULT_PAINT; + stroke = DEFAULT_STROKE; + transform = new AffineTransform(DEFAULT_TRANSFORM); + xorMode = DEFAULT_XOR_MODE; + } + + private static Shape transformShape(Shape s, AffineTransform tx) { + if (s == null) { + return null; + } + if (tx == null || tx.isIdentity()) { + return GraphicsUtils.clone(s); + } + boolean isRectangle = s instanceof Rectangle2D; + int nonRectlinearTxMask = AffineTransform.TYPE_GENERAL_TRANSFORM | + AffineTransform.TYPE_GENERAL_ROTATION; + boolean isRectlinearTx = (tx.getType() & nonRectlinearTxMask) == 0; + if (isRectangle && isRectlinearTx) { + Rectangle2D rect = (Rectangle2D) s; + double[] corners = new double[]{ + rect.getMinX(), rect.getMinY(), + rect.getMaxX(), rect.getMaxY() + }; + tx.transform(corners, 0, corners, 0, 2); + rect = new Rectangle2D.Double(); + rect.setFrameFromDiagonal(corners[0], corners[1], corners[2], + corners[3]); + return rect; + } + return tx.createTransformedShape(s); + } + + private static Shape untransformShape(Shape s, AffineTransform tx) { + if (s == null) { + return null; + } + try { + AffineTransform inverse = tx.createInverse(); + return transformShape(s, inverse); + } catch (NoninvertibleTransformException e) { + return null; + } + } + + @Override + public Object clone() throws CloneNotSupportedException { + GraphicsState clone = (GraphicsState) super.clone(); + clone.hints = (RenderingHints) hints.clone(); + clone.clip = GraphicsUtils.clone(clip); + clone.transform = new AffineTransform(transform); + return clone; + } + + public Shape transformShape(Shape shape) { + return transformShape(shape, transform); + } + + public Shape untransformShape(Shape shape) { + return untransformShape(shape, transform); + } + + public RenderingHints getHints() { + return hints; + } + + public Color getBackground() { + return background; + } + + public void setBackground(Color background) { + this.background = background; + } + + public Color getColor() { + return color; + } + + public void setColor(Color color) { + this.color = color; + } + + public Shape getClip() { + return untransformShape(clip); + } + + public void setClip(Shape clip) { + this.clip = transformShape(clip); + } + + public Composite getComposite() { + return composite; + } + + public void setComposite(Composite composite) { + this.composite = composite; + } + + public Font getFont() { + return font; + } + + public void setFont(Font font) { + this.font = font; + } + + public Paint getPaint() { + return paint; + } + + public void setPaint(Paint paint) { + this.paint = paint; + } + + public Stroke getStroke() { + return stroke; + } + + public void setStroke(Stroke stroke) { + this.stroke = stroke; + } + + public AffineTransform getTransform() { + return new AffineTransform(transform); + } + + public void setTransform(AffineTransform tx) { + transform.setTransform(tx); + } + + public Color getXorMode() { + return xorMode; + } + + public void setXorMode(Color xorMode) { + this.xorMode = xorMode; + } + + @Override + public boolean equals(Object obj) { + if (this == obj) { + return true; + } + if (!(obj instanceof GraphicsState)) { + return false; + } + GraphicsState o = (GraphicsState) obj; + return !(!hints.equals(o.hints) || !background.equals(o.background) || + !color.equals(o.color) || !composite.equals(o.composite) || + !font.equals(o.font) || !paint.equals(o.paint) || + !stroke.equals(o.stroke) || !transform.equals(o.transform) || + !xorMode.equals(o.xorMode) || + ((clip == null || o.clip == null) && clip != o.clip) || + (clip != null && !clip.equals(o.clip))); + } + + @Override + public int hashCode() { + return Objects.hash(hints, background, color, composite, font, paint, + stroke, transform, xorMode, clip); + } + + public boolean isDefault() { + return hints.isEmpty() && background.equals(DEFAULT_BACKGROUND) && + color.equals(DEFAULT_COLOR) && composite.equals(DEFAULT_COMPOSITE) && + font.equals(DEFAULT_FONT) && paint.equals(DEFAULT_PAINT) && + stroke.equals(DEFAULT_STROKE) && transform.equals(DEFAULT_TRANSFORM) && + xorMode.equals(DEFAULT_XOR_MODE) && clip == DEFAULT_CLIP; + } +} + diff --git a/io-vector/src/main/java/org/xbib/graphics/io/vector/PageSize.java b/io-vector/src/main/java/org/xbib/graphics/io/vector/PageSize.java new file mode 100644 index 0000000..8e452fa --- /dev/null +++ b/io-vector/src/main/java/org/xbib/graphics/io/vector/PageSize.java @@ -0,0 +1,75 @@ +package org.xbib.graphics.io.vector; + +import java.awt.geom.Rectangle2D; + +public class PageSize { + + private static final double MM_PER_INCH = 2.54; + + public static final PageSize TABLOID = new PageSize(11.0 * MM_PER_INCH, 17.0 * MM_PER_INCH); + + public static final PageSize LETTER = new PageSize(8.5 * MM_PER_INCH, 11.0 * MM_PER_INCH); + + public static final PageSize LEGAL = new PageSize(8.5 * MM_PER_INCH, 14.0 * MM_PER_INCH); + + public static final PageSize LEDGER = TABLOID.getLandscape(); + + public static final PageSize A3 = new PageSize(297.0, 420.0); + + public static final PageSize A4 = new PageSize(210.0, 297.0); + + public static final PageSize A5 = new PageSize(148.0, 210.0); + + private final double x; + + private final double y; + + private final double width; + + private final double height; + + public PageSize(double x, double y, double width, double height) { + this.x = x; + this.y = y; + this.width = width; + this.height = height; + } + + public PageSize(double width, double height) { + this(0.0, 0.0, width, height); + } + + public PageSize(Rectangle2D size) { + this(size.getX(), size.getY(), size.getWidth(), size.getHeight()); + } + + public PageSize getPortrait() { + if (width <= height) { + return this; + } + return new PageSize(x, y, height, width); + } + + public PageSize getLandscape() { + if (width >= height) { + return this; + } + return new PageSize(x, y, height, width); + } + + public double getX() { + return x; + } + + public double getY() { + return y; + } + + public double getHeight() { + return height; + } + + public double getWidth() { + return width; + } +} diff --git a/io-vector/src/main/java/org/xbib/graphics/io/vector/Processor.java b/io-vector/src/main/java/org/xbib/graphics/io/vector/Processor.java new file mode 100644 index 0000000..3496bb8 --- /dev/null +++ b/io-vector/src/main/java/org/xbib/graphics/io/vector/Processor.java @@ -0,0 +1,8 @@ +package org.xbib.graphics.io.vector; + +import java.io.IOException; + +public interface Processor { + + ProcessorResult process(Iterable> commands, PageSize pageSize) throws IOException; +} diff --git a/io-vector/src/main/java/org/xbib/graphics/io/vector/ProcessorResult.java b/io-vector/src/main/java/org/xbib/graphics/io/vector/ProcessorResult.java new file mode 100644 index 0000000..cbee656 --- /dev/null +++ b/io-vector/src/main/java/org/xbib/graphics/io/vector/ProcessorResult.java @@ -0,0 +1,13 @@ +package org.xbib.graphics.io.vector; + +import java.io.IOException; +import java.io.OutputStream; + +public interface ProcessorResult { + + void handle(Command command) throws IOException; + + void write(OutputStream out) throws IOException; + + void close() throws IOException; +} diff --git a/io-vector/src/main/java/org/xbib/graphics/io/vector/VectorGraphics2D.java b/io-vector/src/main/java/org/xbib/graphics/io/vector/VectorGraphics2D.java new file mode 100644 index 0000000..63751bf --- /dev/null +++ b/io-vector/src/main/java/org/xbib/graphics/io/vector/VectorGraphics2D.java @@ -0,0 +1,896 @@ +package org.xbib.graphics.io.vector; + +import org.xbib.graphics.io.vector.commands.CreateCommand; +import org.xbib.graphics.io.vector.commands.DisposeCommand; +import org.xbib.graphics.io.vector.commands.DrawImageCommand; +import org.xbib.graphics.io.vector.commands.DrawShapeCommand; +import org.xbib.graphics.io.vector.commands.DrawStringCommand; +import org.xbib.graphics.io.vector.commands.FillShapeCommand; +import org.xbib.graphics.io.vector.commands.RotateCommand; +import org.xbib.graphics.io.vector.commands.ScaleCommand; +import org.xbib.graphics.io.vector.commands.SetBackgroundCommand; +import org.xbib.graphics.io.vector.commands.SetClipCommand; +import org.xbib.graphics.io.vector.commands.SetColorCommand; +import org.xbib.graphics.io.vector.commands.SetCompositeCommand; +import org.xbib.graphics.io.vector.commands.SetFontCommand; +import org.xbib.graphics.io.vector.commands.SetHintCommand; +import org.xbib.graphics.io.vector.commands.SetPaintCommand; +import org.xbib.graphics.io.vector.commands.SetStrokeCommand; +import org.xbib.graphics.io.vector.commands.SetTransformCommand; +import org.xbib.graphics.io.vector.commands.SetXORModeCommand; +import org.xbib.graphics.io.vector.commands.ShearCommand; +import org.xbib.graphics.io.vector.commands.TransformCommand; +import org.xbib.graphics.io.vector.commands.TranslateCommand; +import org.xbib.graphics.io.vector.util.GraphicsUtils; +import java.awt.AlphaComposite; +import java.awt.Color; +import java.awt.Composite; +import java.awt.Font; +import java.awt.FontMetrics; +import java.awt.Graphics; +import java.awt.Graphics2D; +import java.awt.GraphicsConfiguration; +import java.awt.GraphicsDevice; +import java.awt.GraphicsEnvironment; +import java.awt.Image; +import java.awt.Paint; +import java.awt.Polygon; +import java.awt.Rectangle; +import java.awt.RenderingHints; +import java.awt.RenderingHints.Key; +import java.awt.Shape; +import java.awt.Stroke; +import java.awt.font.FontRenderContext; +import java.awt.font.GlyphVector; +import java.awt.font.TextLayout; +import java.awt.geom.AffineTransform; +import java.awt.geom.Arc2D; +import java.awt.geom.Area; +import java.awt.geom.Ellipse2D; +import java.awt.geom.Line2D; +import java.awt.geom.Path2D; +import java.awt.geom.Rectangle2D; +import java.awt.geom.RoundRectangle2D; +import java.awt.image.AffineTransformOp; +import java.awt.image.BufferedImage; +import java.awt.image.BufferedImageOp; +import java.awt.image.ImageObserver; +import java.awt.image.RenderedImage; +import java.awt.image.renderable.RenderableImage; +import java.io.ByteArrayOutputStream; +import java.io.IOException; +import java.io.OutputStream; +import java.text.AttributedCharacterIterator; +import java.util.LinkedList; +import java.util.List; +import java.util.Map; +import java.util.Map.Entry; + +/** + * Base for classes that want to implement vector export. + */ +public class VectorGraphics2D extends Graphics2D implements Cloneable { + + private final Processor processor; + + private final PageSize pageSize; + /** + * List of operations that were performed on this graphics object and its + * derived objects. + */ + private final List> commands; + + private final GraphicsConfiguration deviceConfig; + + /** + * Context settings used to render fonts. + */ + private final FontRenderContext fontRenderContext; + /** + * Flag that tells whether this graphics object has been disposed. + */ + private boolean disposed; + + private GraphicsState state; + + private Graphics2D debugValidateGraphics; + + public VectorGraphics2D() { + this(null, null); + } + + public VectorGraphics2D(Processor processor, PageSize pageSize) { + this.processor = processor; + this.pageSize = pageSize; + this.commands = new LinkedList<>(); + emit(new CreateCommand(this)); + GraphicsEnvironment graphicsEnvironment = GraphicsEnvironment.getLocalGraphicsEnvironment(); + if (!graphicsEnvironment.isHeadlessInstance()) { + GraphicsDevice graphicsDevice = graphicsEnvironment.getDefaultScreenDevice(); + deviceConfig = graphicsDevice.getDefaultConfiguration(); + } else { + deviceConfig = null; + } + fontRenderContext = new FontRenderContext(null, false, true); + state = new GraphicsState(); + BufferedImage _debug_validate_bimg = new BufferedImage(200, 250, BufferedImage.TYPE_INT_ARGB); + debugValidateGraphics = (Graphics2D) _debug_validate_bimg.getGraphics(); + debugValidateGraphics.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON); + } + + public PageSize getPageSize() { + return pageSize; + } + + public byte[] getBytes() throws IOException { + ByteArrayOutputStream out = new ByteArrayOutputStream(); + try (out) { + writeTo(out); + } + return out.toByteArray(); + } + + public void writeTo(OutputStream out) throws IOException { + processor.process(getCommands(), pageSize).write(out); + } + + private static Shape intersectShapes(Shape s1, Shape s2) { + if (s1 instanceof Rectangle2D && s2 instanceof Rectangle2D) { + Rectangle2D r1 = (Rectangle2D) s1; + Rectangle2D r2 = (Rectangle2D) s2; + double x1 = Math.max(r1.getMinX(), r2.getMinX()); + double y1 = Math.max(r1.getMinY(), r2.getMinY()); + double x2 = Math.min(r1.getMaxX(), r2.getMaxX()); + double y2 = Math.min(r1.getMaxY(), r2.getMaxY()); + Rectangle2D intersection = new Rectangle2D.Double(); + if ((x2 < x1) || (y2 < y1)) { + intersection.setFrameFromDiagonal(0, 0, 0, 0); + } else { + intersection.setFrameFromDiagonal(x1, y1, x2, y2); + } + return intersection; + } else { + Area intersection = new Area(s1); + intersection.intersect(new Area(s2)); + return intersection; + } + } + + @Override + public Object clone() throws CloneNotSupportedException { + VectorGraphics2D clone = (VectorGraphics2D) super.clone(); + clone.state = (GraphicsState) state.clone(); + return clone; + } + + @Override + public void addRenderingHints(Map hints) { + if (isDisposed()) { + return; + } + for (Entry entry : hints.entrySet()) { + setRenderingHint((Key) entry.getKey(), entry.getValue()); + } + } + + @Override + public void clip(Shape s) { + debugValidateGraphics.clip(s); + Shape clipOld = getClip(); + Shape clip = getClip(); + if ((clip != null) && (s != null)) { + s = intersectShapes(clip, s); + } + setClip(s); + Shape clipNew = getClip(); + if ((clipNew == null || debugValidateGraphics.getClip() == null) && clipNew != debugValidateGraphics.getClip()) { + throw new IllegalStateException("clip() validation failed: clip(" + clipOld + ", " + s + ") => " + clipNew + " != " + debugValidateGraphics.getClip()); + } + if (clipNew != null && !GraphicsUtils.equals(clipNew, debugValidateGraphics.getClip())) { + throw new IllegalStateException("clip() validation failed: clip(" + clipOld + ", " + s + ") => " + clipNew + " != " + debugValidateGraphics.getClip()); + } + } + + @Override + public void draw(Shape s) { + if (isDisposed() || s == null) { + return; + } + emit(new DrawShapeCommand(s)); + debugValidateGraphics.draw(s); + } + + @Override + public void drawGlyphVector(GlyphVector g, float x, float y) { + Shape s = g.getOutline(x, y); + draw(s); + } + + @Override + public boolean drawImage(Image img, AffineTransform xform, ImageObserver obs) { + BufferedImage bimg = getTransformedImage(img, xform); + return drawImage(bimg, bimg.getMinX(), bimg.getMinY(), + bimg.getWidth(), bimg.getHeight(), null, null); + } + + /** + * Returns a transformed version of an image. + * + * @param image Image to be transformed + * @param xform Affine transform to be applied + * @return Image with transformed content + */ + private BufferedImage getTransformedImage(Image image, + AffineTransform xform) { + Integer interpolationType = + (Integer) getRenderingHint(RenderingHints.KEY_INTERPOLATION); + if (RenderingHints.VALUE_INTERPOLATION_NEAREST_NEIGHBOR + .equals(interpolationType)) { + interpolationType = AffineTransformOp.TYPE_NEAREST_NEIGHBOR; + } else if (RenderingHints.VALUE_INTERPOLATION_BILINEAR + .equals(interpolationType)) { + interpolationType = AffineTransformOp.TYPE_BILINEAR; + } else { + interpolationType = AffineTransformOp.TYPE_BICUBIC; + } + AffineTransformOp op = new AffineTransformOp(xform, interpolationType); + BufferedImage bufferedImage = GraphicsUtils.toBufferedImage(image); + return op.filter(bufferedImage, null); + } + + @Override + public void drawImage(BufferedImage img, BufferedImageOp op, int x, int y) { + if (op != null) { + img = op.filter(img, null); + } + drawImage(img, x, y, img.getWidth(), img.getHeight(), null, null); + } + + @Override + public void drawRenderableImage(RenderableImage img, AffineTransform xform) { + drawRenderedImage(img.createDefaultRendering(), xform); + } + + @Override + public void drawRenderedImage(RenderedImage img, AffineTransform xform) { + BufferedImage bimg = GraphicsUtils.toBufferedImage(img); + drawImage(bimg, xform, null); + } + + @Override + public void drawString(String str, int x, int y) { + drawString(str, (float) x, (float) y); + } + + @Override + public void drawString(String str, float x, float y) { + if (isDisposed() || str == null || str.trim().length() == 0) { + return; + } + boolean isTextAsVectors = false; + if (isTextAsVectors) { + TextLayout layout = new TextLayout(str, getFont(), + getFontRenderContext()); + Shape s = layout.getOutline( + AffineTransform.getTranslateInstance(x, y)); + fill(s); + } else { + emit(new DrawStringCommand(str, x, y)); + debugValidateGraphics.drawString(str, x, y); + } + + } + + @Override + public void drawString(AttributedCharacterIterator iterator, int x, int y) { + drawString(iterator, (float) x, (float) y); + } + + @Override + public void drawString(AttributedCharacterIterator iterator, float x, float y) { + StringBuilder buf = new StringBuilder(); + for (char c = iterator.first(); c != AttributedCharacterIterator.DONE; + c = iterator.next()) { + buf.append(c); + } + drawString(buf.toString(), x, y); + } + + @Override + public void fill(Shape s) { + if (isDisposed() || s == null) { + return; + } + emit(new FillShapeCommand(s)); + debugValidateGraphics.fill(s); + } + + @Override + public Color getBackground() { + return state.getBackground(); + } + + @Override + public void setBackground(Color color) { + if (isDisposed() || color == null || getColor().equals(color)) { + return; + } + emit(new SetBackgroundCommand(color)); + state.setBackground(color); + debugValidateGraphics.setBackground(color); + if (!getBackground().equals(debugValidateGraphics.getBackground())) { + throw new IllegalStateException("setBackground() validation failed"); + } + } + + @Override + public Composite getComposite() { + return state.getComposite(); + } + + @Override + public void setComposite(Composite comp) { + if (isDisposed()) { + return; + } + if (comp == null) { + throw new IllegalArgumentException("Cannot set a null composite"); + } + emit(new SetCompositeCommand(comp)); + state.setComposite(comp); + debugValidateGraphics.setComposite(comp); + if (!getComposite().equals(debugValidateGraphics.getComposite())) { + throw new IllegalStateException("setComposite() validation failed"); + } + } + + @Override + public GraphicsConfiguration getDeviceConfiguration() { + return deviceConfig; + } + + @Override + public FontRenderContext getFontRenderContext() { + return fontRenderContext; + } + + @Override + public Paint getPaint() { + return state.getPaint(); + } + + @Override + public void setPaint(Paint paint) { + if (isDisposed() || paint == null) { + return; + } + if (paint instanceof Color) { + setColor((Color) paint); + return; + } + if (getPaint().equals(paint)) { + return; + } + emit(new SetPaintCommand(paint)); + state.setPaint(paint); + debugValidateGraphics.setPaint(paint); + if (!getPaint().equals(debugValidateGraphics.getPaint())) { + throw new IllegalStateException("setPaint() validation failed"); + } + } + + @Override + public Object getRenderingHint(Key hintKey) { + if (RenderingHints.KEY_ANTIALIASING.equals(hintKey)) { + return RenderingHints.VALUE_ANTIALIAS_OFF; + } else if (RenderingHints.KEY_TEXT_ANTIALIASING.equals(hintKey)) { + return RenderingHints.VALUE_TEXT_ANTIALIAS_OFF; + } else if (RenderingHints.KEY_FRACTIONALMETRICS.equals(hintKey)) { + return RenderingHints.VALUE_FRACTIONALMETRICS_ON; + } + return state.getHints().get(hintKey); + } + + @Override + public RenderingHints getRenderingHints() { + return (RenderingHints) state.getHints().clone(); + } + + @Override + public void setRenderingHints(Map hints) { + if (isDisposed()) { + return; + } + state.getHints().clear(); + for (Entry hint : hints.entrySet()) { + setRenderingHint((Key) hint.getKey(), hint.getValue()); + } + } + + @Override + public Stroke getStroke() { + return state.getStroke(); + } + + @Override + public void setStroke(Stroke s) { + if (isDisposed()) { + return; + } + if (s == null) { + throw new IllegalArgumentException("Cannot set a null stroke."); + } + emit(new SetStrokeCommand(s)); + state.setStroke(s); + + debugValidateGraphics.setStroke(s); + if (!getStroke().equals(debugValidateGraphics.getStroke())) { + throw new IllegalStateException("setStroke() validation failed"); + } + } + + @Override + public boolean hit(Rectangle rect, Shape s, boolean onStroke) { + Shape hitShape = s; + if (onStroke) { + hitShape = getStroke().createStrokedShape(hitShape); + } + hitShape = state.transformShape(hitShape); + boolean hit = hitShape.intersects(rect); + + boolean _debug_hit = debugValidateGraphics.hit(rect, s, onStroke); + if (hit != _debug_hit) { + throw new IllegalStateException("setClip() validation failed"); + } + + return hit; + } + + @Override + public void setRenderingHint(Key hintKey, Object hintValue) { + if (isDisposed()) { + return; + } + state.getHints().put(hintKey, hintValue); + emit(new SetHintCommand(hintKey, hintValue)); + } + + @Override + public AffineTransform getTransform() { + return new AffineTransform(state.getTransform()); + } + + @Override + public void setTransform(AffineTransform tx) { + if (isDisposed() || tx == null || state.getTransform().equals(tx)) { + return; + } + emit(new SetTransformCommand(tx)); + state.setTransform(tx); + + debugValidateGraphics.setTransform(tx); + if (!getTransform().equals(debugValidateGraphics.getTransform())) { + throw new IllegalStateException("setTransform() validation failed"); + } + } + + @Override + public void shear(double shx, double shy) { + if (shx == 0.0 && shy == 0.0) { + return; + } + AffineTransform txNew = getTransform(); + txNew.shear(shx, shy); + emit(new ShearCommand(shx, shy)); + state.setTransform(txNew); + + debugValidateGraphics.shear(shx, shy); + if (!getTransform().equals(debugValidateGraphics.getTransform())) { + throw new IllegalStateException("shear() validation failed"); + } + } + + @Override + public void transform(AffineTransform tx) { + if (tx.isIdentity()) { + return; + } + AffineTransform txNew = getTransform(); + txNew.concatenate(tx); + emit(new TransformCommand(tx)); + state.setTransform(txNew); + + debugValidateGraphics.transform(tx); + if (!getTransform().equals(debugValidateGraphics.getTransform())) { + throw new IllegalStateException("transform() validation failed"); + } + } + + @Override + public void translate(int x, int y) { + translate((double) x, (double) y); + } + + @Override + public void translate(double tx, double ty) { + if (tx == 0.0 && ty == 0.0) { + return; + } + AffineTransform txNew = getTransform(); + txNew.translate(tx, ty); + emit(new TranslateCommand(tx, ty)); + state.setTransform(txNew); + debugValidateGraphics.translate(tx, ty); + if (!getTransform().equals(debugValidateGraphics.getTransform())) { + throw new IllegalStateException("translate() validation failed"); + } + } + + @Override + public void rotate(double theta) { + rotate(theta, 0.0, 0.0); + } + + @Override + public void rotate(double theta, double x, double y) { + if (theta == 0.0) { + return; + } + AffineTransform txNew = getTransform(); + if (x == 0.0 && y == 0.0) { + txNew.rotate(theta); + } else { + txNew.rotate(theta, x, y); + } + emit(new RotateCommand(theta, x, y)); + state.setTransform(txNew); + if (x == 0.0 && y == 0.0) { + debugValidateGraphics.rotate(theta); + if (!getTransform().equals(debugValidateGraphics.getTransform())) { + throw new IllegalStateException("rotate(theta) validation failed"); + } + } else { + debugValidateGraphics.rotate(theta, x, y); + if (!getTransform().equals(debugValidateGraphics.getTransform())) { + throw new IllegalStateException("rotate(theta,x,y) validation failed"); + } + } + } + + @Override + public void scale(double sx, double sy) { + if (sx == 1.0 && sy == 1.0) { + return; + } + AffineTransform txNew = getTransform(); + txNew.scale(sx, sy); + emit(new ScaleCommand(sx, sy)); + state.setTransform(txNew); + debugValidateGraphics.scale(sx, sy); + if (!getTransform().equals(debugValidateGraphics.getTransform())) { + throw new IllegalStateException("scale() validation failed"); + } + } + + @Override + public void clearRect(int x, int y, int width, int height) { + Color colorOld = getColor(); + setColor(getBackground()); + fillRect(x, y, width, height); + setColor(colorOld); + } + + @Override + public void clipRect(int x, int y, int width, int height) { + clip(new Rectangle(x, y, width, height)); + } + + @Override + public void copyArea(int x, int y, int width, int height, int dx, int dy) { + // TODO Implement + //throw new UnsupportedOperationException("copyArea() isn't supported by VectorGraphics2D."); + } + + @Override + public Graphics create() { + if (isDisposed()) { + return null; + } + VectorGraphics2D clone = null; + try { + clone = (VectorGraphics2D) this.clone(); + } catch (CloneNotSupportedException e) { + return null; + } + emit(new CreateCommand(clone)); + clone.debugValidateGraphics = (Graphics2D) debugValidateGraphics.create(); + return clone; + } + + @Override + public void dispose() { + if (isDisposed()) { + return; + } + emit(new DisposeCommand(this)); + disposed = true; + debugValidateGraphics.dispose(); + } + + @Override + public void drawArc(int x, int y, int width, int height, int startAngle, + int arcAngle) { + draw(new Arc2D.Double(x, y, width, height, + startAngle, arcAngle, Arc2D.OPEN)); + } + + @Override + public boolean drawImage(Image img, int x, int y, ImageObserver observer) { + return drawImage(img, x, y, img.getWidth(observer), + img.getHeight(observer), null, observer); + } + + @Override + public boolean drawImage(Image img, int x, int y, Color bgcolor, + ImageObserver observer) { + return drawImage(img, x, y, img.getWidth(observer), + img.getHeight(observer), bgcolor, observer); + } + + @Override + public boolean drawImage(Image img, int x, int y, int width, int height, + ImageObserver observer) { + return drawImage(img, x, y, width, height, null, observer); + } + + @Override + public boolean drawImage(Image img, int x, int y, int width, int height, + Color bgcolor, ImageObserver observer) { + if (isDisposed() || img == null) { + return true; + } + + int imageWidth = img.getWidth(observer); + int imageHeight = img.getHeight(observer); + Rectangle bounds = new Rectangle(x, y, width, height); + + if (bgcolor != null) { + // Fill rectangle with bgcolor + Color bgcolorOld = getColor(); + setColor(bgcolor); + fill(bounds); + setColor(bgcolorOld); + } + + emit(new DrawImageCommand(img, imageWidth, imageHeight, x, y, width, height)); + + debugValidateGraphics.drawImage(img, x, y, width, height, bgcolor, observer); + + return true; + } + + @Override + public boolean drawImage(Image img, int dx1, int dy1, int dx2, int dy2, + int sx1, int sy1, int sx2, int sy2, ImageObserver observer) { + return drawImage(img, dx1, dy1, dx2, dy2, sx1, sy1, sx2, sy2, null, + observer); + } + + @Override + public boolean drawImage(Image img, int dx1, int dy1, int dx2, int dy2, + int sx1, int sy1, int sx2, int sy2, Color bgcolor, + ImageObserver observer) { + if (img == null) { + return true; + } + + int sx = Math.min(sx1, sx2); + int sy = Math.min(sy1, sy2); + int sw = Math.abs(sx2 - sx1); + int sh = Math.abs(sy2 - sy1); + int dx = Math.min(dx1, dx2); + int dy = Math.min(dy1, dy2); + int dw = Math.abs(dx2 - dx1); + int dh = Math.abs(dy2 - dy1); + + // Draw image on rectangle + BufferedImage bufferedImg = GraphicsUtils.toBufferedImage(img); + Image cropped = bufferedImg.getSubimage(sx, sy, sw, sh); + return drawImage(cropped, dx, dy, dw, dh, bgcolor, observer); + } + + @Override + public void drawLine(int x1, int y1, int x2, int y2) { + draw(new Line2D.Double(x1, y1, x2, y2)); + } + + @Override + public void drawOval(int x, int y, int width, int height) { + draw(new Ellipse2D.Double(x, y, width, height)); + } + + @Override + public void drawPolygon(Polygon p) { + draw(p); + } + + @Override + public void drawPolygon(int[] xPoints, int[] yPoints, int nPoints) { + draw(new Polygon(xPoints, yPoints, nPoints)); + } + + @Override + public void drawPolyline(int[] xPoints, int[] yPoints, int nPoints) { + Path2D p = new Path2D.Float(); + for (int i = 0; i < nPoints; i++) { + if (i > 0) { + p.lineTo(xPoints[i], yPoints[i]); + } else { + p.moveTo(xPoints[i], yPoints[i]); + } + } + draw(p); + } + + @Override + public void drawRect(int x, int y, int width, int height) { + draw(new Rectangle(x, y, width, height)); + } + + @Override + public void drawRoundRect(int x, int y, int width, int height, + int arcWidth, int arcHeight) { + draw(new RoundRectangle2D.Double(x, y, width, height, + arcWidth, arcHeight)); + } + + @Override + public void fillArc(int x, int y, int width, int height, + int startAngle, int arcAngle) { + fill(new Arc2D.Double(x, y, width, height, + startAngle, arcAngle, Arc2D.PIE)); + } + + @Override + public void fillOval(int x, int y, int width, int height) { + fill(new Ellipse2D.Double(x, y, width, height)); + } + + @Override + public void fillPolygon(Polygon p) { + fill(p); + } + + @Override + public void fillPolygon(int[] xPoints, int[] yPoints, int nPoints) { + fill(new Polygon(xPoints, yPoints, nPoints)); + } + + @Override + public void fillRect(int x, int y, int width, int height) { + fill(new Rectangle(x, y, width, height)); + } + + @Override + public void fillRoundRect(int x, int y, int width, int height, + int arcWidth, int arcHeight) { + fill(new RoundRectangle2D.Double(x, y, width, height, + arcWidth, arcHeight)); + } + + @Override + public Shape getClip() { + return state.getClip(); + } + + @Override + public void setClip(Shape clip) { + if (isDisposed()) { + return; + } + emit(new SetClipCommand(clip)); + state.setClip(clip); + + debugValidateGraphics.setClip(clip); + if (getClip() == null) { + if (debugValidateGraphics.getClip() != null) { + throw new IllegalStateException("setClip() validation failed: clip=null, validation=" + + debugValidateGraphics.getClip()); + } + } else if (!GraphicsUtils.equals(getClip(), debugValidateGraphics.getClip())) { + throw new IllegalStateException("setClip() validation failed: clip=" + getClip() + ", validation=" + + debugValidateGraphics.getClip()); + } + } + + @Override + public Rectangle getClipBounds() { + if (getClip() == null) { + return null; + } + return getClip().getBounds(); + } + + @Override + public Color getColor() { + return state.getColor(); + } + + @Override + public void setColor(Color c) { + if (isDisposed() || c == null || getColor().equals(c)) { + return; + } + emit(new SetColorCommand(c)); + state.setColor(c); + state.setPaint(c); + debugValidateGraphics.setColor(c); + if (!getColor().equals(debugValidateGraphics.getColor())) { + throw new IllegalStateException("setColor() validation failed"); + } + } + + @Override + public Font getFont() { + return state.getFont(); + } + + @Override + public void setFont(Font font) { + if (isDisposed() || (font != null && getFont().equals(font))) { + return; + } + emit(new SetFontCommand(font)); + state.setFont(font); + debugValidateGraphics.setFont(font); + if (!getFont().equals(debugValidateGraphics.getFont())) { + throw new IllegalStateException("setFont() validation failed"); + } + } + + @Override + public FontMetrics getFontMetrics(Font f) { + BufferedImage bi = new BufferedImage(1, 1, BufferedImage.TYPE_INT_ARGB_PRE); + Graphics g = bi.getGraphics(); + FontMetrics fontMetrics = g.getFontMetrics(getFont()); + g.dispose(); + return fontMetrics; + } + + @Override + public void setClip(int x, int y, int width, int height) { + setClip(new Rectangle(x, y, width, height)); + } + + @Override + public void setPaintMode() { + setComposite(AlphaComposite.SrcOver); + debugValidateGraphics.setPaintMode(); + } + + public Color getXORMode() { + return state.getXorMode(); + } + + @Override + public void setXORMode(Color c1) { + if (isDisposed() || c1 == null) { + return; + } + emit(new SetXORModeCommand(c1)); + state.setXorMode(c1); + debugValidateGraphics.setXORMode(c1); + } + + private void emit(Command command) { + commands.add(command); + } + + protected Iterable> getCommands() { + return commands; + } + + protected boolean isDisposed() { + return disposed; + } +} diff --git a/io-vector/src/main/java/org/xbib/graphics/io/vector/VectorGraphicsFormat.java b/io-vector/src/main/java/org/xbib/graphics/io/vector/VectorGraphicsFormat.java new file mode 100644 index 0000000..887abcb --- /dev/null +++ b/io-vector/src/main/java/org/xbib/graphics/io/vector/VectorGraphicsFormat.java @@ -0,0 +1,5 @@ +package org.xbib.graphics.io.vector; + +public enum VectorGraphicsFormat { + EPS, PDF, SVG +} diff --git a/io-vector/src/main/java/org/xbib/graphics/io/vector/commands/AffineTransformCommand.java b/io-vector/src/main/java/org/xbib/graphics/io/vector/commands/AffineTransformCommand.java new file mode 100644 index 0000000..b2d726f --- /dev/null +++ b/io-vector/src/main/java/org/xbib/graphics/io/vector/commands/AffineTransformCommand.java @@ -0,0 +1,10 @@ +package org.xbib.graphics.io.vector.commands; + +import java.awt.geom.AffineTransform; + +public abstract class AffineTransformCommand extends StateCommand { + public AffineTransformCommand(AffineTransform transform) { + super(transform); + } +} + diff --git a/io-vector/src/main/java/org/xbib/graphics/io/vector/commands/CreateCommand.java b/io-vector/src/main/java/org/xbib/graphics/io/vector/commands/CreateCommand.java new file mode 100644 index 0000000..73323d3 --- /dev/null +++ b/io-vector/src/main/java/org/xbib/graphics/io/vector/commands/CreateCommand.java @@ -0,0 +1,10 @@ +package org.xbib.graphics.io.vector.commands; + +import org.xbib.graphics.io.vector.VectorGraphics2D; + +public class CreateCommand extends StateCommand { + public CreateCommand(VectorGraphics2D graphics) { + super(graphics); + } +} + diff --git a/io-vector/src/main/java/org/xbib/graphics/io/vector/commands/DisposeCommand.java b/io-vector/src/main/java/org/xbib/graphics/io/vector/commands/DisposeCommand.java new file mode 100644 index 0000000..d11e178 --- /dev/null +++ b/io-vector/src/main/java/org/xbib/graphics/io/vector/commands/DisposeCommand.java @@ -0,0 +1,10 @@ +package org.xbib.graphics.io.vector.commands; + +import org.xbib.graphics.io.vector.VectorGraphics2D; + +public class DisposeCommand extends StateCommand { + public DisposeCommand(VectorGraphics2D graphics) { + super(graphics); + } +} + diff --git a/io-vector/src/main/java/org/xbib/graphics/io/vector/commands/DrawImageCommand.java b/io-vector/src/main/java/org/xbib/graphics/io/vector/commands/DrawImageCommand.java new file mode 100644 index 0000000..613e994 --- /dev/null +++ b/io-vector/src/main/java/org/xbib/graphics/io/vector/commands/DrawImageCommand.java @@ -0,0 +1,59 @@ +package org.xbib.graphics.io.vector.commands; + +import org.xbib.graphics.io.vector.Command; +import java.awt.Image; +import java.util.Locale; + +public class DrawImageCommand extends Command { + private final int imageWidth; + private final int imageHeight; + private final double x; + private final double y; + private final double width; + private final double height; + + public DrawImageCommand(Image image, int imageWidth, int imageHeight, + double x, double y, double width, double height) { + super(image); + this.imageWidth = imageWidth; + this.imageHeight = imageHeight; + this.x = x; + this.y = y; + this.width = width; + this.height = height; + } + + public int getImageWidth() { + return imageWidth; + } + + public int getImageHeight() { + return imageHeight; + } + + public double getX() { + return x; + } + + public double getY() { + return y; + } + + public double getWidth() { + return width; + } + + public double getHeight() { + return height; + } + + @Override + public String toString() { + return String.format((Locale) null, + "%s[value=%s, imageWidth=%d, imageHeight=%d, x=%f, y=%f, width=%f, height=%f]", + getClass().getName(), getValue(), + getImageWidth(), getImageHeight(), + getX(), getY(), getWidth(), getHeight()); + } +} + diff --git a/io-vector/src/main/java/org/xbib/graphics/io/vector/commands/DrawShapeCommand.java b/io-vector/src/main/java/org/xbib/graphics/io/vector/commands/DrawShapeCommand.java new file mode 100644 index 0000000..cbfff32 --- /dev/null +++ b/io-vector/src/main/java/org/xbib/graphics/io/vector/commands/DrawShapeCommand.java @@ -0,0 +1,13 @@ +package org.xbib.graphics.io.vector.commands; + +import org.xbib.graphics.io.vector.Command; +import org.xbib.graphics.io.vector.util.GraphicsUtils; +import java.awt.Shape; + +public class DrawShapeCommand extends Command { + + public DrawShapeCommand(Shape shape) { + super(GraphicsUtils.clone(shape)); + } +} + diff --git a/io-vector/src/main/java/org/xbib/graphics/io/vector/commands/DrawStringCommand.java b/io-vector/src/main/java/org/xbib/graphics/io/vector/commands/DrawStringCommand.java new file mode 100644 index 0000000..7a23ebb --- /dev/null +++ b/io-vector/src/main/java/org/xbib/graphics/io/vector/commands/DrawStringCommand.java @@ -0,0 +1,31 @@ +package org.xbib.graphics.io.vector.commands; + +import org.xbib.graphics.io.vector.Command; +import java.util.Locale; + + +public class DrawStringCommand extends Command { + private final double x; + private final double y; + + public DrawStringCommand(String string, double x, double y) { + super(string); + this.x = x; + this.y = y; + } + + public double getX() { + return x; + } + + public double getY() { + return y; + } + + @Override + public String toString() { + return String.format((Locale) null, "%s[value=%s, x=%f, y=%f]", + getClass().getName(), getValue(), getX(), getY()); + } +} + diff --git a/io-vector/src/main/java/org/xbib/graphics/io/vector/commands/FillShapeCommand.java b/io-vector/src/main/java/org/xbib/graphics/io/vector/commands/FillShapeCommand.java new file mode 100644 index 0000000..ce91250 --- /dev/null +++ b/io-vector/src/main/java/org/xbib/graphics/io/vector/commands/FillShapeCommand.java @@ -0,0 +1,13 @@ +package org.xbib.graphics.io.vector.commands; + +import org.xbib.graphics.io.vector.Command; +import org.xbib.graphics.io.vector.util.GraphicsUtils; +import java.awt.Shape; + +public class FillShapeCommand extends Command { + + public FillShapeCommand(Shape shape) { + super(GraphicsUtils.clone(shape)); + } +} + diff --git a/io-vector/src/main/java/org/xbib/graphics/io/vector/commands/Group.java b/io-vector/src/main/java/org/xbib/graphics/io/vector/commands/Group.java new file mode 100644 index 0000000..9143e3c --- /dev/null +++ b/io-vector/src/main/java/org/xbib/graphics/io/vector/commands/Group.java @@ -0,0 +1,17 @@ +package org.xbib.graphics.io.vector.commands; + +import org.xbib.graphics.io.vector.Command; +import java.util.LinkedList; +import java.util.List; + +public class Group extends Command>> { + public Group() { + super(new LinkedList>()); + } + + public void add(Command command) { + List> group = getValue(); + group.add(command); + } +} + diff --git a/io-vector/src/main/java/org/xbib/graphics/io/vector/commands/RotateCommand.java b/io-vector/src/main/java/org/xbib/graphics/io/vector/commands/RotateCommand.java new file mode 100644 index 0000000..433d22b --- /dev/null +++ b/io-vector/src/main/java/org/xbib/graphics/io/vector/commands/RotateCommand.java @@ -0,0 +1,38 @@ +package org.xbib.graphics.io.vector.commands; + +import java.awt.geom.AffineTransform; +import java.util.Locale; + +public class RotateCommand extends AffineTransformCommand { + private final double theta; + private final double centerX; + private final double centerY; + + public RotateCommand(double theta, double centerX, double centerY) { + super(AffineTransform.getRotateInstance(theta, centerX, centerY)); + this.theta = theta; + this.centerX = centerX; + this.centerY = centerY; + } + + public double getTheta() { + return theta; + } + + public double getCenterX() { + return centerX; + } + + public double getCenterY() { + return centerY; + } + + @Override + public String toString() { + return String.format((Locale) null, + "%s[theta=%f, centerX=%f, centerY=%f, value=%s]", + getClass().getName(), getTheta(), getCenterX(), getCenterY(), + getValue()); + } +} + diff --git a/io-vector/src/main/java/org/xbib/graphics/io/vector/commands/ScaleCommand.java b/io-vector/src/main/java/org/xbib/graphics/io/vector/commands/ScaleCommand.java new file mode 100644 index 0000000..6d436cb --- /dev/null +++ b/io-vector/src/main/java/org/xbib/graphics/io/vector/commands/ScaleCommand.java @@ -0,0 +1,31 @@ +package org.xbib.graphics.io.vector.commands; + +import java.awt.geom.AffineTransform; +import java.util.Locale; + +public class ScaleCommand extends AffineTransformCommand { + private final double scaleX; + private final double scaleY; + + public ScaleCommand(double scaleX, double scaleY) { + super(AffineTransform.getScaleInstance(scaleX, scaleY)); + this.scaleX = scaleX; + this.scaleY = scaleY; + } + + public double getScaleX() { + return scaleX; + } + + public double getScaleY() { + return scaleY; + } + + @Override + public String toString() { + return String.format((Locale) null, + "%s[scaleX=%f, scaleY=%f, value=%s]", getClass().getName(), + getScaleX(), getScaleY(), getValue()); + } +} + diff --git a/io-vector/src/main/java/org/xbib/graphics/io/vector/commands/SetBackgroundCommand.java b/io-vector/src/main/java/org/xbib/graphics/io/vector/commands/SetBackgroundCommand.java new file mode 100644 index 0000000..2a0c497 --- /dev/null +++ b/io-vector/src/main/java/org/xbib/graphics/io/vector/commands/SetBackgroundCommand.java @@ -0,0 +1,10 @@ +package org.xbib.graphics.io.vector.commands; + +import java.awt.Color; + +public class SetBackgroundCommand extends StateCommand { + public SetBackgroundCommand(Color color) { + super(color); + } +} + diff --git a/io-vector/src/main/java/org/xbib/graphics/io/vector/commands/SetClipCommand.java b/io-vector/src/main/java/org/xbib/graphics/io/vector/commands/SetClipCommand.java new file mode 100644 index 0000000..8bc0d28 --- /dev/null +++ b/io-vector/src/main/java/org/xbib/graphics/io/vector/commands/SetClipCommand.java @@ -0,0 +1,10 @@ +package org.xbib.graphics.io.vector.commands; + +import java.awt.Shape; + +public class SetClipCommand extends StateCommand { + public SetClipCommand(Shape shape) { + super(shape); + } +} + diff --git a/io-vector/src/main/java/org/xbib/graphics/io/vector/commands/SetColorCommand.java b/io-vector/src/main/java/org/xbib/graphics/io/vector/commands/SetColorCommand.java new file mode 100644 index 0000000..eb41bcf --- /dev/null +++ b/io-vector/src/main/java/org/xbib/graphics/io/vector/commands/SetColorCommand.java @@ -0,0 +1,10 @@ +package org.xbib.graphics.io.vector.commands; + +import java.awt.Color; + +public class SetColorCommand extends StateCommand { + public SetColorCommand(Color color) { + super(color); + } +} + diff --git a/io-vector/src/main/java/org/xbib/graphics/io/vector/commands/SetCompositeCommand.java b/io-vector/src/main/java/org/xbib/graphics/io/vector/commands/SetCompositeCommand.java new file mode 100644 index 0000000..143ec4a --- /dev/null +++ b/io-vector/src/main/java/org/xbib/graphics/io/vector/commands/SetCompositeCommand.java @@ -0,0 +1,10 @@ +package org.xbib.graphics.io.vector.commands; + +import java.awt.Composite; + +public class SetCompositeCommand extends StateCommand { + public SetCompositeCommand(Composite composite) { + super(composite); + } +} + diff --git a/io-vector/src/main/java/org/xbib/graphics/io/vector/commands/SetFontCommand.java b/io-vector/src/main/java/org/xbib/graphics/io/vector/commands/SetFontCommand.java new file mode 100644 index 0000000..996bbdc --- /dev/null +++ b/io-vector/src/main/java/org/xbib/graphics/io/vector/commands/SetFontCommand.java @@ -0,0 +1,10 @@ +package org.xbib.graphics.io.vector.commands; + +import java.awt.Font; + +public class SetFontCommand extends StateCommand { + public SetFontCommand(Font font) { + super(font); + } +} + diff --git a/io-vector/src/main/java/org/xbib/graphics/io/vector/commands/SetHintCommand.java b/io-vector/src/main/java/org/xbib/graphics/io/vector/commands/SetHintCommand.java new file mode 100644 index 0000000..a056790 --- /dev/null +++ b/io-vector/src/main/java/org/xbib/graphics/io/vector/commands/SetHintCommand.java @@ -0,0 +1,24 @@ +package org.xbib.graphics.io.vector.commands; + +import java.util.Locale; + +public class SetHintCommand extends StateCommand { + private final Object key; + + public SetHintCommand(Object hintKey, Object hintValue) { + super(hintValue); + key = hintKey; + } + + public Object getKey() { + return key; + } + + @Override + public String toString() { + return String.format((Locale) null, + "%s[key=%s, value=%s]", getClass().getName(), + getKey(), getValue()); + } +} + diff --git a/io-vector/src/main/java/org/xbib/graphics/io/vector/commands/SetPaintCommand.java b/io-vector/src/main/java/org/xbib/graphics/io/vector/commands/SetPaintCommand.java new file mode 100644 index 0000000..f3d3ab8 --- /dev/null +++ b/io-vector/src/main/java/org/xbib/graphics/io/vector/commands/SetPaintCommand.java @@ -0,0 +1,10 @@ +package org.xbib.graphics.io.vector.commands; + +import java.awt.Paint; + +public class SetPaintCommand extends StateCommand { + public SetPaintCommand(Paint paint) { + super(paint); + } +} + diff --git a/io-vector/src/main/java/org/xbib/graphics/io/vector/commands/SetStrokeCommand.java b/io-vector/src/main/java/org/xbib/graphics/io/vector/commands/SetStrokeCommand.java new file mode 100644 index 0000000..da91cef --- /dev/null +++ b/io-vector/src/main/java/org/xbib/graphics/io/vector/commands/SetStrokeCommand.java @@ -0,0 +1,10 @@ +package org.xbib.graphics.io.vector.commands; + +import java.awt.Stroke; + +public class SetStrokeCommand extends StateCommand { + public SetStrokeCommand(Stroke stroke) { + super(stroke); + } +} + diff --git a/io-vector/src/main/java/org/xbib/graphics/io/vector/commands/SetTransformCommand.java b/io-vector/src/main/java/org/xbib/graphics/io/vector/commands/SetTransformCommand.java new file mode 100644 index 0000000..180ecdf --- /dev/null +++ b/io-vector/src/main/java/org/xbib/graphics/io/vector/commands/SetTransformCommand.java @@ -0,0 +1,10 @@ +package org.xbib.graphics.io.vector.commands; + +import java.awt.geom.AffineTransform; + +public class SetTransformCommand extends StateCommand { + public SetTransformCommand(AffineTransform transform) { + super(new AffineTransform(transform)); + } +} + diff --git a/io-vector/src/main/java/org/xbib/graphics/io/vector/commands/SetXORModeCommand.java b/io-vector/src/main/java/org/xbib/graphics/io/vector/commands/SetXORModeCommand.java new file mode 100644 index 0000000..e472bdf --- /dev/null +++ b/io-vector/src/main/java/org/xbib/graphics/io/vector/commands/SetXORModeCommand.java @@ -0,0 +1,10 @@ +package org.xbib.graphics.io.vector.commands; + +import java.awt.Color; + +public class SetXORModeCommand extends StateCommand { + public SetXORModeCommand(Color mode) { + super(mode); + } +} + diff --git a/io-vector/src/main/java/org/xbib/graphics/io/vector/commands/ShearCommand.java b/io-vector/src/main/java/org/xbib/graphics/io/vector/commands/ShearCommand.java new file mode 100644 index 0000000..25c9d60 --- /dev/null +++ b/io-vector/src/main/java/org/xbib/graphics/io/vector/commands/ShearCommand.java @@ -0,0 +1,31 @@ +package org.xbib.graphics.io.vector.commands; + +import java.awt.geom.AffineTransform; +import java.util.Locale; + +public class ShearCommand extends AffineTransformCommand { + private final double shearX; + private final double shearY; + + public ShearCommand(double shearX, double shearY) { + super(AffineTransform.getShearInstance(shearX, shearY)); + this.shearX = shearX; + this.shearY = shearY; + } + + public double getShearX() { + return shearX; + } + + public double getShearY() { + return shearY; + } + + @Override + public String toString() { + return String.format((Locale) null, + "%s[shearX=%f, shearY=%f, value=%s]", getClass().getName(), + getShearX(), getShearY(), getValue()); + } +} + diff --git a/io-vector/src/main/java/org/xbib/graphics/io/vector/commands/StateCommand.java b/io-vector/src/main/java/org/xbib/graphics/io/vector/commands/StateCommand.java new file mode 100644 index 0000000..cc4b0b8 --- /dev/null +++ b/io-vector/src/main/java/org/xbib/graphics/io/vector/commands/StateCommand.java @@ -0,0 +1,10 @@ +package org.xbib.graphics.io.vector.commands; + +import org.xbib.graphics.io.vector.Command; + +public abstract class StateCommand extends Command { + public StateCommand(T value) { + super(value); + } +} + diff --git a/io-vector/src/main/java/org/xbib/graphics/io/vector/commands/TransformCommand.java b/io-vector/src/main/java/org/xbib/graphics/io/vector/commands/TransformCommand.java new file mode 100644 index 0000000..0e7ba54 --- /dev/null +++ b/io-vector/src/main/java/org/xbib/graphics/io/vector/commands/TransformCommand.java @@ -0,0 +1,17 @@ +package org.xbib.graphics.io.vector.commands; + +import java.awt.geom.AffineTransform; + +public class TransformCommand extends AffineTransformCommand { + private final AffineTransform transform; + + public TransformCommand(AffineTransform transform) { + super(transform); + this.transform = new AffineTransform(transform); + } + + public AffineTransform getTransform() { + return transform; + } +} + diff --git a/io-vector/src/main/java/org/xbib/graphics/io/vector/commands/TranslateCommand.java b/io-vector/src/main/java/org/xbib/graphics/io/vector/commands/TranslateCommand.java new file mode 100644 index 0000000..6b48654 --- /dev/null +++ b/io-vector/src/main/java/org/xbib/graphics/io/vector/commands/TranslateCommand.java @@ -0,0 +1,31 @@ +package org.xbib.graphics.io.vector.commands; + +import java.awt.geom.AffineTransform; +import java.util.Locale; + +public class TranslateCommand extends AffineTransformCommand { + private final double deltaX; + private final double deltaY; + + public TranslateCommand(double x, double y) { + super(AffineTransform.getTranslateInstance(x, y)); + this.deltaX = x; + this.deltaY = y; + } + + public double getDeltaX() { + return deltaX; + } + + public double getDeltaY() { + return deltaY; + } + + @Override + public String toString() { + return String.format((Locale) null, + "%s[deltaX=%f, deltaY=%f, value=%s]", getClass().getName(), + getDeltaX(), getDeltaY(), getValue()); + } +} + diff --git a/io-vector/src/main/java/org/xbib/graphics/io/vector/eps/EPSGraphics2D.java b/io-vector/src/main/java/org/xbib/graphics/io/vector/eps/EPSGraphics2D.java new file mode 100644 index 0000000..1bd4542 --- /dev/null +++ b/io-vector/src/main/java/org/xbib/graphics/io/vector/eps/EPSGraphics2D.java @@ -0,0 +1,34 @@ +package org.xbib.graphics.io.vector.eps; + +import org.xbib.graphics.io.vector.VectorGraphics2D; +import org.xbib.graphics.io.vector.PageSize; +import java.awt.BasicStroke; +import java.awt.Color; + +/** + * {@code Graphics2D} implementation that saves all operations to a string + * in the Encapsulated PostScript® (EPS) format. + */ +public class EPSGraphics2D extends VectorGraphics2D { + + /** + * Initializes a new VectorGraphics2D pipeline for translating Graphics2D + * commands to EPS data. The document dimensions must be specified as + * parameters. + * + * @param x Left offset. + * @param y Top offset + * @param width Width. + * @param height Height. + */ + public EPSGraphics2D(double x, double y, double width, double height) { + super(new EPSProcessor(), new PageSize(x, y, width, height)); + /* + * The following are the default settings for the graphics state in an EPS file. + * Although they currently appear in the document output, they do not have to be set explicitly. + */ + // TODO: Default graphics state does not need to be printed in the document + setColor(Color.BLACK); + setStroke(new BasicStroke(1f, BasicStroke.CAP_BUTT, BasicStroke.JOIN_MITER, 10f, null, 0f)); + } +} diff --git a/io-vector/src/main/java/org/xbib/graphics/io/vector/eps/EPSProcessor.java b/io-vector/src/main/java/org/xbib/graphics/io/vector/eps/EPSProcessor.java new file mode 100644 index 0000000..775f653 --- /dev/null +++ b/io-vector/src/main/java/org/xbib/graphics/io/vector/eps/EPSProcessor.java @@ -0,0 +1,22 @@ +package org.xbib.graphics.io.vector.eps; + +import org.xbib.graphics.io.vector.ProcessorResult; +import org.xbib.graphics.io.vector.Processor; +import org.xbib.graphics.io.vector.Command; +import org.xbib.graphics.io.vector.filters.FillPaintedShapeAsImageFilter; +import org.xbib.graphics.io.vector.PageSize; +import java.io.IOException; + +public class EPSProcessor implements Processor { + + @Override + public ProcessorResult process(Iterable> commands, PageSize pageSize) throws IOException { + FillPaintedShapeAsImageFilter paintedShapeAsImageFilter = new FillPaintedShapeAsImageFilter(commands); + EPSProcessorResult epsDocument = new EPSProcessorResult(pageSize); + for (Command command : paintedShapeAsImageFilter) { + epsDocument.handle(command); + } + epsDocument.close(); + return epsDocument; + } +} diff --git a/io-vector/src/main/java/org/xbib/graphics/io/vector/eps/EPSProcessorResult.java b/io-vector/src/main/java/org/xbib/graphics/io/vector/eps/EPSProcessorResult.java new file mode 100644 index 0000000..2158b6a --- /dev/null +++ b/io-vector/src/main/java/org/xbib/graphics/io/vector/eps/EPSProcessorResult.java @@ -0,0 +1,470 @@ +package org.xbib.graphics.io.vector.eps; + +import org.xbib.graphics.io.vector.ProcessorResult; +import org.xbib.graphics.io.vector.GraphicsState; +import org.xbib.graphics.io.vector.Command; +import org.xbib.graphics.io.vector.commands.CreateCommand; +import org.xbib.graphics.io.vector.commands.DisposeCommand; +import org.xbib.graphics.io.vector.commands.DrawImageCommand; +import org.xbib.graphics.io.vector.commands.DrawShapeCommand; +import org.xbib.graphics.io.vector.commands.DrawStringCommand; +import org.xbib.graphics.io.vector.commands.FillShapeCommand; +import org.xbib.graphics.io.vector.commands.RotateCommand; +import org.xbib.graphics.io.vector.commands.ScaleCommand; +import org.xbib.graphics.io.vector.commands.SetClipCommand; +import org.xbib.graphics.io.vector.commands.SetColorCommand; +import org.xbib.graphics.io.vector.commands.SetCompositeCommand; +import org.xbib.graphics.io.vector.commands.SetFontCommand; +import org.xbib.graphics.io.vector.commands.SetPaintCommand; +import org.xbib.graphics.io.vector.commands.SetStrokeCommand; +import org.xbib.graphics.io.vector.commands.SetTransformCommand; +import org.xbib.graphics.io.vector.commands.ShearCommand; +import org.xbib.graphics.io.vector.commands.TransformCommand; +import org.xbib.graphics.io.vector.commands.TranslateCommand; +import org.xbib.graphics.io.vector.util.ASCII85EncodeStream; +import org.xbib.graphics.io.vector.util.AlphaToMaskOp; +import org.xbib.graphics.io.vector.util.DataUtils; +import org.xbib.graphics.io.vector.util.FlateEncodeStream; +import org.xbib.graphics.io.vector.util.GraphicsUtils; +import org.xbib.graphics.io.vector.util.ImageDataStream; +import org.xbib.graphics.io.vector.util.ImageDataStream.Interleaving; +import org.xbib.graphics.io.vector.util.LineWrapOutputStream; +import org.xbib.graphics.io.vector.PageSize; +import java.awt.BasicStroke; +import java.awt.Color; +import java.awt.Font; +import java.awt.Image; +import java.awt.Shape; +import java.awt.Stroke; +import java.awt.geom.Arc2D; +import java.awt.geom.Ellipse2D; +import java.awt.geom.Line2D; +import java.awt.geom.PathIterator; +import java.awt.geom.Rectangle2D; +import java.awt.image.BufferedImage; +import java.io.ByteArrayOutputStream; +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; +import java.io.OutputStreamWriter; +import java.util.Arrays; +import java.util.LinkedList; +import java.util.List; +import java.util.Map; +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +public class EPSProcessorResult implements ProcessorResult { + /** + * Constant to convert values from millimeters to PostScript® units + * (1/72th inch). + */ + private static final double UNITS_PER_MM = 72.0 / 25.4; + + private static final String CHARSET = "ISO-8859-1"; + + private static final String EOL = "\n"; + + private static final int MAX_LINE_WIDTH = 255; + + private static final Pattern ELEMENT_SEPARATION_PATTERN = Pattern.compile("(.{1," + MAX_LINE_WIDTH + "})(\\s+|$)"); + + /** + * Mapping of stroke endcap values from Java to PostScript®. + */ + private static final Map STROKE_ENDCAPS = DataUtils.map( + new Integer[]{BasicStroke.CAP_BUTT, BasicStroke.CAP_ROUND, BasicStroke.CAP_SQUARE}, + new Integer[]{0, 1, 2} + ); + + /** + * Mapping of line join values for path drawing from Java to + * PostScript®. + */ + private static final Map STROKE_LINEJOIN = DataUtils.map( + new Integer[]{BasicStroke.JOIN_MITER, BasicStroke.JOIN_ROUND, BasicStroke.JOIN_BEVEL}, + new Integer[]{0, 1, 2} + ); + + private static final String FONT_LATIN1_SUFFIX = "Lat"; + + private final PageSize pageSize; + + private final List elements; + + public EPSProcessorResult(PageSize pageSize) { + this.pageSize = pageSize; + this.elements = new LinkedList<>(); + addHeader(); + } + + private static String getOutput(Color c) { + // TODO Handle transparency + return String.valueOf(c.getRed() / 255.0) + " " + c.getGreen() / 255.0 + " " + c.getBlue() / 255.0 + " rgb"; + } + + private static String getOutput(Shape s) { + StringBuilder out = new StringBuilder(); + out.append("newpath "); + if (s instanceof Line2D) { + Line2D l = (Line2D) s; + out.append(l.getX1()).append(" ").append(l.getY1()).append(" M ") + .append(l.getX2()).append(" ").append(l.getY2()).append(" L"); + } else if (s instanceof Rectangle2D) { + Rectangle2D r = (Rectangle2D) s; + out.append(r.getX()).append(" ").append(r.getY()).append(" ") + .append(r.getWidth()).append(" ").append(r.getHeight()) + .append(" rect Z"); + } else if (s instanceof Ellipse2D) { + Ellipse2D e = (Ellipse2D) s; + double x = e.getX() + e.getWidth() / 2.0; + double y = e.getY() + e.getHeight() / 2.0; + double rx = e.getWidth() / 2.0; + double ry = e.getHeight() / 2.0; + out.append(x).append(" ").append(y).append(" ") + .append(rx).append(" ").append(ry).append(" ") + .append(360.0).append(" ").append(0.0) + .append(" ellipse Z"); + } else if (s instanceof Arc2D) { + Arc2D e = (Arc2D) s; + double x = (e.getX() + e.getWidth() / 2.0); + double y = (e.getY() + e.getHeight() / 2.0); + double rx = e.getWidth() / 2.0; + double ry = e.getHeight() / 2.0; + double startAngle = -e.getAngleStart(); + double endAngle = -(e.getAngleStart() + e.getAngleExtent()); + out.append(x).append(" ").append(y).append(" ") + .append(rx).append(" ").append(ry).append(" ") + .append(startAngle).append(" ").append(endAngle) + .append(" ellipse"); + if (e.getArcType() == Arc2D.CHORD) { + out.append(" Z"); + } else if (e.getArcType() == Arc2D.PIE) { + out.append(" ").append(x).append(" ").append(y).append(" L Z"); + } + } else { + PathIterator segments = s.getPathIterator(null); + double[] coordsCur = new double[6]; + double[] pointPrev = new double[2]; + for (int i = 0; !segments.isDone(); i++, segments.next()) { + if (i > 0) { + out.append(" "); + } + int segmentType = segments.currentSegment(coordsCur); + switch (segmentType) { + case PathIterator.SEG_MOVETO: + out.append(coordsCur[0]).append(" ").append(coordsCur[1]) + .append(" M"); + pointPrev[0] = coordsCur[0]; + pointPrev[1] = coordsCur[1]; + break; + case PathIterator.SEG_LINETO: + out.append(coordsCur[0]).append(" ").append(coordsCur[1]) + .append(" L"); + pointPrev[0] = coordsCur[0]; + pointPrev[1] = coordsCur[1]; + break; + case PathIterator.SEG_CUBICTO: + out.append(coordsCur[0]).append(" ").append(coordsCur[1]) + .append(" ").append(coordsCur[2]).append(" ") + .append(coordsCur[3]).append(" ").append(coordsCur[4]) + .append(" ").append(coordsCur[5]).append(" C"); + pointPrev[0] = coordsCur[4]; + pointPrev[1] = coordsCur[5]; + break; + case PathIterator.SEG_QUADTO: + double x1 = pointPrev[0] + 2.0 / 3.0 * (coordsCur[0] - pointPrev[0]); + double y1 = pointPrev[1] + 2.0 / 3.0 * (coordsCur[1] - pointPrev[1]); + double x2 = coordsCur[0] + 1.0 / 3.0 * (coordsCur[2] - coordsCur[0]); + double y2 = coordsCur[1] + 1.0 / 3.0 * (coordsCur[3] - coordsCur[1]); + double x3 = coordsCur[2]; + double y3 = coordsCur[3]; + out.append(x1).append(" ").append(y1).append(" ") + .append(x2).append(" ").append(y2).append(" ") + .append(x3).append(" ").append(y3).append(" C"); + pointPrev[0] = x3; + pointPrev[1] = y3; + break; + case PathIterator.SEG_CLOSE: + out.append("Z"); + break; + default: + throw new IllegalStateException("Unknown path operation."); + } + } + } + return out.toString(); + } + + private static String getOutput(Image image, int imageWidth, int imageHeight, + double x, double y, double width, double height) throws IOException { + StringBuilder out = new StringBuilder(); + BufferedImage bufferedImage = GraphicsUtils.toBufferedImage(image); + int bands = bufferedImage.getSampleModel().getNumBands(); + int bitsPerSample = DataUtils.max(bufferedImage.getSampleModel().getSampleSize()); + bitsPerSample = (int) (Math.ceil(bitsPerSample / 8.0) * 8.0); + if (bands > 3) { + bands = 3; + } + out.append("gsave").append(EOL); + if (x != 0.0 || y != 0.0) { + out.append(x).append(" ").append(y).append(" translate").append(EOL); + } + if (width != 1.0 || height != 1.0) { + out.append(width).append(" ").append(height).append(" scale").append(EOL); + } + int decodeScale = 1; + if (bufferedImage.getColorModel().hasAlpha()) { + // TODO Use different InterleaveType (2 or 3) for more efficient compression + out.append("<< /ImageType 3 /InterleaveType 1 ") + .append("/MaskDict ") + .append(imageWidth).append(" ").append(imageHeight).append(" ") + .append(1).append(" ").append(bitsPerSample).append(" ").append(decodeScale).append(" ") + .append(false).append(" ").append(0).append(" imgdict ") + .append("/DataDict ") + .append(imageWidth).append(" ").append(imageHeight).append(" ") + .append(bands).append(" ").append(bitsPerSample).append(" ").append(decodeScale).append(" ") + .append(true).append(" currentfile /ASCII85Decode filter ") + .append("<< /BitsPerComponent ").append(bitsPerSample).append(" >> ") + .append("/FlateDecode filter ") + .append("imgdict ") + .append(">> image").append(EOL); + + // Convert alpha values to binary mask + // FIXME Do alpha conversion in a preprocessing step on commands + bufferedImage = new AlphaToMaskOp(true).filter(bufferedImage, null); + output(bufferedImage, out); + } else { + if (bands == 1) { + out.append("/DeviceGray setcolorspace").append(EOL); + } + if (bufferedImage.getType() == BufferedImage.TYPE_BYTE_BINARY) { + decodeScale = 255; + } + out.append(imageWidth).append(" ").append(imageHeight).append(" ") + .append(bands).append(" ").append(bitsPerSample).append(" ").append(decodeScale).append(" ") + .append(true).append(" currentfile /ASCII85Decode filter ") + .append("<< /BitsPerComponent ").append(bitsPerSample).append(" >> ") + .append("/FlateDecode filter ") + .append("imgdict ") + .append("image").append(EOL); + output(bufferedImage, out); + } + out.append("grestore"); + return out.toString(); + } + + private static void output(BufferedImage image, StringBuilder out) throws IOException { + InputStream imageDataStream = new ImageDataStream(image, Interleaving.SAMPLE); + ByteArrayOutputStream outBytes = new ByteArrayOutputStream(); + OutputStream compressionStream = new FlateEncodeStream( + new ASCII85EncodeStream(new LineWrapOutputStream(outBytes, 80))); + DataUtils.transfer(imageDataStream, compressionStream, 4096); + compressionStream.close(); + String compressed = outBytes.toString(CHARSET); + out.append(compressed).append(EOL); + } + + private static String getOutput(String str, double x, double y) { + return "gsave 1 -1 scale " + x + " " + -y + " M " + getOutput(str) + " show " + "grestore"; + } + + private static StringBuilder getOutput(String str) { + StringBuilder out = new StringBuilder(); + // Escape text + str = str.replaceAll("\\\\", "\\\\\\\\") + .replaceAll("\t", "\\\\t") + .replaceAll("\b", "\\\\b") + .replaceAll("\f", "\\\\f") + .replaceAll("\\(", "\\\\(") + .replaceAll("\\)", "\\\\)") + .replaceAll("[\r\n]", ""); + out.append("(").append(str).append(")"); + return out; + } + + private static String getOutput(Stroke s) { + StringBuilder out = new StringBuilder(); + if (s instanceof BasicStroke) { + BasicStroke bs = (BasicStroke) s; + out.append(bs.getLineWidth()).append(" setlinewidth ") + .append(STROKE_LINEJOIN.get(bs.getLineJoin())).append(" setlinejoin ") + .append(STROKE_ENDCAPS.get(bs.getEndCap())).append(" setlinecap ") + .append("[").append(DataUtils.join(" ", bs.getDashArray())).append("] ") + .append(bs.getDashPhase()).append(" setdash"); + } else { + out.append("% Custom strokes aren't supported at the moment"); + } + return out.toString(); + } + + private static String getOutput(Font font) { + StringBuilder out = new StringBuilder(); + font = GraphicsUtils.getPhysicalFont(font); + String fontName = font.getPSName(); + + // Convert font to ISO-8859-1 encoding + String fontNameLatin1 = fontName + FONT_LATIN1_SUFFIX; + out.append("/").append(fontNameLatin1).append(" ") + .append("/").append(font.getPSName()).append(" latinize "); + + // Use encoded font + out.append("/").append(fontNameLatin1).append(" ") + .append(font.getSize2D()).append(" selectfont"); + + return out.toString(); + } + + private void addHeader() { + double x = pageSize.getX() * UNITS_PER_MM, + y = pageSize.getY() * UNITS_PER_MM, + width = pageSize.getWidth() * UNITS_PER_MM, + height = pageSize.getHeight() * UNITS_PER_MM; + elements.addAll(Arrays.asList( + "%!PS-Adobe-3.0 EPSF-3.0", + "%%BoundingBox: " + ((int) Math.floor(x)) + " " + ((int) Math.floor(y)) + " " + ((int) Math.ceil(x + width)) + " " + ((int) Math.ceil(y + height)), + "%%HiResBoundingBox: " + x + " " + y + " " + (x + width) + " " + (y + height), + "%%LanguageLevel: 3", + "%%Pages: 1", + "%%EndComments", + "%%Page: 1 1", + "/M /moveto load def", + "/L /lineto load def", + "/C /curveto load def", + "/Z /closepath load def", + "/RL /rlineto load def", + "/rgb /setrgbcolor load def", + "/rect { /height exch def /width exch def /y exch def /x exch def x y M width 0 RL 0 height RL width neg 0 RL } bind def", + "/ellipse { /endangle exch def /startangle exch def /ry exch def /rx exch def /y exch def /x exch def /savematrix matrix currentmatrix def x y translate rx ry scale 0 0 1 startangle endangle arcn savematrix setmatrix } bind def", + "/imgdict { /datastream exch def /hasdata exch def /decodeScale exch def /bits exch def /bands exch def /imgheight exch def /imgwidth exch def << /ImageType 1 /Width imgwidth /Height imgheight /BitsPerComponent bits /Decode [bands {0 decodeScale} repeat] /ImageMatrix [imgwidth 0 0 imgheight 0 0] hasdata { /DataSource datastream } if >> } bind def", + "/latinize { /fontName exch def /fontNameNew exch def fontName findfont 0 dict copy begin /Encoding ISOLatin1Encoding def fontNameNew /FontName def currentdict end dup /FID undef fontNameNew exch definefont pop } bind def", + getOutput(GraphicsState.DEFAULT_FONT), + "gsave", + "clipsave", + "/DeviceRGB setcolorspace", + "0 " + height + " translate", + UNITS_PER_MM + " " + (-UNITS_PER_MM) + " scale", + "/basematrix matrix currentmatrix def" + )); + } + + public void write(OutputStream out) throws IOException { + OutputStreamWriter o = new OutputStreamWriter(out, CHARSET); + for (String element : elements) { + if (element == null) { + continue; + } + // Write current element in lines of 255 bytes (excluding line terminators) + // Numbers must not be separated by line breaks or errors will occur + // TODO: Integrate functionality into LineWrapOutputStream + Matcher chunkMatcher = ELEMENT_SEPARATION_PATTERN.matcher(element); + boolean chunkFound = false; + while (chunkMatcher.find()) { + chunkFound = true; + String chunk = chunkMatcher.group(); + o.write(chunk, 0, chunk.length()); + o.append(EOL); + } + if (!chunkFound) { + // TODO: Exception, if no whitespace can be found in the chunk + throw new IllegalStateException("Unable to divide eps element into lines: " + element); + } + } + o.append("%%EOF"); + o.flush(); + } + + @Override + public void close() { + // nothing to do + } + + @Override + public void handle(Command command) throws IOException { + if (command instanceof SetClipCommand) { + SetClipCommand c = (SetClipCommand) command; + Shape clip = c.getValue(); + elements.add("cliprestore"); + if (clip != null) { + elements.add(getOutput(clip) + " clip"); + } + } else if (command instanceof SetColorCommand) { + SetColorCommand c = (SetColorCommand) command; + elements.add(getOutput(c.getValue())); + } else if (command instanceof SetCompositeCommand) { + SetCompositeCommand c = (SetCompositeCommand) command; + // TODO Implement composite rendering for EPS + elements.add("% composite not yet implemented: " + c.getValue()); + } else if (command instanceof SetFontCommand) { + SetFontCommand c = (SetFontCommand) command; + elements.add(getOutput(c.getValue())); + } else if (command instanceof SetPaintCommand) { + SetPaintCommand c = (SetPaintCommand) command; + // TODO Implement paint rendering for EPS + elements.add("% paint not yet implemented: " + c.getValue()); + } else if (command instanceof SetStrokeCommand) { + SetStrokeCommand c = (SetStrokeCommand) command; + elements.add(getOutput(c.getValue())); + } else if (command instanceof SetTransformCommand) { + SetTransformCommand c = (SetTransformCommand) command; + StringBuilder e = new StringBuilder(); + double[] matrix = new double[6]; + c.getValue().getMatrix(matrix); + e.append("basematrix setmatrix [") + .append(DataUtils.join(" ", matrix)).append("] concat"); + elements.add(e.toString()); + } else if (command instanceof RotateCommand) { + RotateCommand c = (RotateCommand) command; + StringBuilder e = new StringBuilder(); + double x = c.getCenterX(); + double y = c.getCenterY(); + boolean translated = x != 0.0 || y != 0.0; + if (translated) { + e.append(x).append(" ").append(y).append(" translate "); + } + e.append(Math.toDegrees(c.getTheta())).append(" rotate"); + if (translated) { + e.append(" "); + e.append(-x).append(" ").append(-y).append(" translate"); + } + elements.add(e.toString()); + } else if (command instanceof ScaleCommand) { + ScaleCommand c = (ScaleCommand) command; + elements.add(DataUtils.format(c.getScaleX()) + " " + DataUtils.format(c.getScaleY()) + " scale"); + } else if (command instanceof ShearCommand) { + ShearCommand c = (ShearCommand) command; + elements.add("[1 " + DataUtils.format(c.getShearY()) + " " + DataUtils.format(c.getShearX()) + " 1 0 0] concat"); + } else if (command instanceof TransformCommand) { + TransformCommand c = (TransformCommand) command; + StringBuilder e = new StringBuilder(); + double[] matrix = new double[6]; + c.getValue().getMatrix(matrix); + e.append("[").append(DataUtils.join(" ", matrix)) + .append("] concat"); + elements.add(e.toString()); + } else if (command instanceof TranslateCommand) { + TranslateCommand c = (TranslateCommand) command; + elements.add(String.valueOf(c.getDeltaX()) + " " + c.getDeltaY() + " translate"); + } else if (command instanceof DrawImageCommand) { + DrawImageCommand c = (DrawImageCommand) command; + String e = getOutput(c.getValue(), + c.getImageWidth(), c.getImageHeight(), + c.getX(), c.getY(), c.getWidth(), c.getHeight()); + elements.add(e); + } else if (command instanceof DrawShapeCommand) { + DrawShapeCommand c = (DrawShapeCommand) command; + elements.add(getOutput(c.getValue()) + " stroke"); + } else if (command instanceof DrawStringCommand) { + DrawStringCommand c = (DrawStringCommand) command; + elements.add(getOutput(c.getValue(), c.getX(), c.getY())); + } else if (command instanceof FillShapeCommand) { + FillShapeCommand c = (FillShapeCommand) command; + elements.add(getOutput(c.getValue()) + " fill"); + } else if (command instanceof CreateCommand) { + elements.add("gsave"); + } else if (command instanceof DisposeCommand) { + elements.add("grestore"); + } + } +} + diff --git a/io-vector/src/main/java/org/xbib/graphics/io/vector/filters/AbsoluteToRelativeTransformsFilter.java b/io-vector/src/main/java/org/xbib/graphics/io/vector/filters/AbsoluteToRelativeTransformsFilter.java new file mode 100644 index 0000000..1e30f38 --- /dev/null +++ b/io-vector/src/main/java/org/xbib/graphics/io/vector/filters/AbsoluteToRelativeTransformsFilter.java @@ -0,0 +1,63 @@ +package org.xbib.graphics.io.vector.filters; + +import org.xbib.graphics.io.vector.commands.AffineTransformCommand; +import org.xbib.graphics.io.vector.Command; +import org.xbib.graphics.io.vector.commands.CreateCommand; +import org.xbib.graphics.io.vector.commands.DisposeCommand; +import org.xbib.graphics.io.vector.commands.SetTransformCommand; +import org.xbib.graphics.io.vector.commands.TransformCommand; +import java.awt.geom.AffineTransform; +import java.awt.geom.NoninvertibleTransformException; +import java.util.Collections; +import java.util.Deque; +import java.util.LinkedList; +import java.util.List; + +public class AbsoluteToRelativeTransformsFilter extends Filter { + + private final Deque transforms; + + public AbsoluteToRelativeTransformsFilter(Iterable> stream) { + super(stream); + transforms = new LinkedList<>(); + } + + @Override + public Command next() { + Command nextCommand = super.next(); + if (nextCommand instanceof AffineTransformCommand) { + AffineTransformCommand affineTransformCommand = (AffineTransformCommand) nextCommand; + getCurrentTransform().concatenate(affineTransformCommand.getValue()); + } else if (nextCommand instanceof CreateCommand) { + AffineTransform newTransform = transforms.isEmpty() ? new AffineTransform() : new AffineTransform(getCurrentTransform()); + transforms.push(newTransform); + } else if (nextCommand instanceof DisposeCommand) { + transforms.pop(); + } + return nextCommand; + } + + @Override + protected List> filter(Command command) { + if (command instanceof SetTransformCommand) { + SetTransformCommand setTransformCommand = (SetTransformCommand) command; + AffineTransform absoluteTransform = setTransformCommand.getValue(); + AffineTransform relativeTransform = new AffineTransform(); + try { + AffineTransform invertedOldTransformation = getCurrentTransform().createInverse(); + relativeTransform.concatenate(invertedOldTransformation); + } catch (NoninvertibleTransformException e) { + // ignore + } + relativeTransform.concatenate(absoluteTransform); + TransformCommand transformCommand = new TransformCommand(relativeTransform); + return Collections.singletonList(transformCommand); + } + return Collections.singletonList(command); + } + + private AffineTransform getCurrentTransform() { + return transforms.peek(); + } +} + diff --git a/io-vector/src/main/java/org/xbib/graphics/io/vector/filters/FillPaintedShapeAsImageFilter.java b/io-vector/src/main/java/org/xbib/graphics/io/vector/filters/FillPaintedShapeAsImageFilter.java new file mode 100644 index 0000000..653bc69 --- /dev/null +++ b/io-vector/src/main/java/org/xbib/graphics/io/vector/filters/FillPaintedShapeAsImageFilter.java @@ -0,0 +1,69 @@ +package org.xbib.graphics.io.vector.filters; + +import org.xbib.graphics.io.vector.Command; +import org.xbib.graphics.io.vector.commands.DisposeCommand; +import org.xbib.graphics.io.vector.commands.DrawImageCommand; +import org.xbib.graphics.io.vector.commands.FillShapeCommand; +import org.xbib.graphics.io.vector.commands.SetPaintCommand; +import java.awt.Graphics2D; +import java.awt.RenderingHints; +import java.awt.Shape; +import java.awt.geom.Rectangle2D; +import java.awt.image.BufferedImage; +import java.util.Arrays; +import java.util.List; + +public class FillPaintedShapeAsImageFilter extends Filter { + private SetPaintCommand lastSetPaintCommand; + + public FillPaintedShapeAsImageFilter(Iterable> stream) { + super(stream); + } + + @Override + public Command next() { + Command nextCommand = super.next(); + + if (nextCommand instanceof SetPaintCommand) { + lastSetPaintCommand = (SetPaintCommand) nextCommand; + } else if (nextCommand instanceof DisposeCommand) { + lastSetPaintCommand = null; + } + + return nextCommand; + } + + private DrawImageCommand getDrawImageCommand(FillShapeCommand shapeCommand, SetPaintCommand paintCommand) { + Shape shape = shapeCommand.getValue(); + Rectangle2D shapeBounds = shape.getBounds2D(); + double x = shapeBounds.getX(); + double y = shapeBounds.getY(); + double width = shapeBounds.getWidth(); + double height = shapeBounds.getHeight(); + int imageWidth = (int) Math.round(width); + int imageHeight = (int) Math.round(height); + BufferedImage image = new BufferedImage(imageWidth, imageHeight, BufferedImage.TYPE_INT_ARGB); + Graphics2D imageGraphics = (Graphics2D) image.getGraphics(); + imageGraphics.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON); + imageGraphics.scale(imageWidth / width, imageHeight / height); + imageGraphics.translate(-shapeBounds.getX(), -shapeBounds.getY()); + imageGraphics.setPaint(paintCommand.getValue()); + imageGraphics.fill(shape); + imageGraphics.dispose(); + + DrawImageCommand drawImageCommand = new DrawImageCommand(image, imageWidth, imageHeight, x, y, width, height); + return drawImageCommand; + } + + @Override + protected List> filter(Command command) { + if (lastSetPaintCommand != null && command instanceof FillShapeCommand) { + FillShapeCommand fillShapeCommand = (FillShapeCommand) command; + DrawImageCommand drawImageCommand = getDrawImageCommand(fillShapeCommand, lastSetPaintCommand); + return Arrays.>asList(drawImageCommand); + } + + return Arrays.>asList(command); + } +} + diff --git a/io-vector/src/main/java/org/xbib/graphics/io/vector/filters/Filter.java b/io-vector/src/main/java/org/xbib/graphics/io/vector/filters/Filter.java new file mode 100644 index 0000000..87e772a --- /dev/null +++ b/io-vector/src/main/java/org/xbib/graphics/io/vector/filters/Filter.java @@ -0,0 +1,47 @@ +package org.xbib.graphics.io.vector.filters; + +import org.xbib.graphics.io.vector.Command; +import java.util.Iterator; +import java.util.LinkedList; +import java.util.List; +import java.util.Queue; + +public abstract class Filter implements Iterable>, Iterator> { + private final Queue> buffer; + private final Iterator> iterator; + + public Filter(Iterable> stream) { + buffer = new LinkedList>(); + iterator = stream.iterator(); + } + + public Iterator> iterator() { + return this; + } + + public boolean hasNext() { + findNextCommand(); + return !buffer.isEmpty(); + } + + private void findNextCommand() { + while (buffer.isEmpty() && iterator.hasNext()) { + Command command = iterator.next(); + List> commands = filter(command); + if (commands != null) { + buffer.addAll(commands); + } + } + } + + public Command next() { + findNextCommand(); + return buffer.poll(); + } + + public void remove() { + } + + protected abstract List> filter(Command command); +} + diff --git a/io-vector/src/main/java/org/xbib/graphics/io/vector/filters/GroupingFilter.java b/io-vector/src/main/java/org/xbib/graphics/io/vector/filters/GroupingFilter.java new file mode 100644 index 0000000..d3e871c --- /dev/null +++ b/io-vector/src/main/java/org/xbib/graphics/io/vector/filters/GroupingFilter.java @@ -0,0 +1,46 @@ +package org.xbib.graphics.io.vector.filters; + +import org.xbib.graphics.io.vector.Command; +import org.xbib.graphics.io.vector.commands.Group; +import java.util.Arrays; +import java.util.List; + + +public abstract class GroupingFilter extends Filter { + private Group group; + + public GroupingFilter(Iterable> stream) { + super(stream); + } + + @Override + public boolean hasNext() { + return group != null || super.hasNext(); + } + + @Override + public Command next() { + if (group == null) { + return super.next(); + } + Group g = group; + group = null; + return g; + } + + @Override + protected List> filter(Command command) { + boolean grouped = isGrouped(command); + if (grouped) { + if (group == null) { + group = new Group(); + } + group.add(command); + return null; + } + return Arrays.>asList(command); + } + + protected abstract boolean isGrouped(Command command); +} + diff --git a/io-vector/src/main/java/org/xbib/graphics/io/vector/filters/OptimizeFilter.java b/io-vector/src/main/java/org/xbib/graphics/io/vector/filters/OptimizeFilter.java new file mode 100644 index 0000000..49323d4 --- /dev/null +++ b/io-vector/src/main/java/org/xbib/graphics/io/vector/filters/OptimizeFilter.java @@ -0,0 +1,56 @@ +package org.xbib.graphics.io.vector.filters; + +import org.xbib.graphics.io.vector.commands.AffineTransformCommand; +import org.xbib.graphics.io.vector.Command; +import org.xbib.graphics.io.vector.commands.SetHintCommand; +import org.xbib.graphics.io.vector.commands.StateCommand; +import java.util.Arrays; +import java.util.Iterator; +import java.util.LinkedList; +import java.util.List; +import java.util.Queue; + +public class OptimizeFilter extends Filter { + private final Queue> buffer; + + public OptimizeFilter(Iterable> stream) { + super(stream); + buffer = new LinkedList>(); + } + + private static boolean isStateChange(Command command) { + return (command instanceof StateCommand) && + !(command instanceof AffineTransformCommand) && + !(command instanceof SetHintCommand); + } + + @Override + public boolean hasNext() { + return super.hasNext(); + } + + @Override + public Command next() { + if (buffer.isEmpty()) { + return super.next(); + } + return buffer.poll(); + } + + @Override + protected List> filter(Command command) { + if (!isStateChange(command)) { + return Arrays.>asList(command); + } + Iterator> i = buffer.iterator(); + Class cls = command.getClass(); + while (i.hasNext()) { + if (cls.equals(i.next().getClass())) { + i.remove(); + } + } + buffer.add(command); + return null; + } +} + diff --git a/io-vector/src/main/java/org/xbib/graphics/io/vector/filters/StateChangeGroupingFilter.java b/io-vector/src/main/java/org/xbib/graphics/io/vector/filters/StateChangeGroupingFilter.java new file mode 100644 index 0000000..097e749 --- /dev/null +++ b/io-vector/src/main/java/org/xbib/graphics/io/vector/filters/StateChangeGroupingFilter.java @@ -0,0 +1,17 @@ +package org.xbib.graphics.io.vector.filters; + +import org.xbib.graphics.io.vector.Command; +import org.xbib.graphics.io.vector.commands.StateCommand; + +public class StateChangeGroupingFilter extends GroupingFilter { + + public StateChangeGroupingFilter(Iterable> stream) { + super(stream); + } + + @Override + protected boolean isGrouped(Command command) { + return command instanceof StateCommand; + } +} + diff --git a/io-vector/src/main/java/org/xbib/graphics/io/vector/pdf/GeneratedPayload.java b/io-vector/src/main/java/org/xbib/graphics/io/vector/pdf/GeneratedPayload.java new file mode 100644 index 0000000..2cf8229 --- /dev/null +++ b/io-vector/src/main/java/org/xbib/graphics/io/vector/pdf/GeneratedPayload.java @@ -0,0 +1,26 @@ +package org.xbib.graphics.io.vector.pdf; + +import java.io.IOException; + +public abstract class GeneratedPayload extends Payload { + + public GeneratedPayload(boolean stream) { + super(stream); + } + + @Override + public byte[] getBytes() throws IOException { + for (byte b : generatePayload()) { + super.write(b); + } + return super.getBytes(); + } + + @Override + public void write(int b) throws IOException { + throw new UnsupportedOperationException("Payload will be calculated and is read only."); + } + + protected abstract byte[] generatePayload() throws IOException; +} + diff --git a/io-vector/src/main/java/org/xbib/graphics/io/vector/pdf/PDFGraphics2D.java b/io-vector/src/main/java/org/xbib/graphics/io/vector/pdf/PDFGraphics2D.java new file mode 100644 index 0000000..b41ba8c --- /dev/null +++ b/io-vector/src/main/java/org/xbib/graphics/io/vector/pdf/PDFGraphics2D.java @@ -0,0 +1,30 @@ +package org.xbib.graphics.io.vector.pdf; + +import org.xbib.graphics.io.vector.VectorGraphics2D; +import org.xbib.graphics.io.vector.PageSize; +import java.awt.BasicStroke; +import java.awt.Color; + +/** + * {@code Graphics2D} implementation that saves all operations to a string + * in the Portable Document Format (PDF). + */ +public class PDFGraphics2D extends VectorGraphics2D { + + /** + * Initializes a new VectorGraphics2D pipeline for translating Graphics2D + * commands to PDF data. The document dimensions must be specified as + * parameters. + * + * @param x Left offset. + * @param y Top offset + * @param width Width. + * @param height Height. + */ + public PDFGraphics2D(double x, double y, double width, double height) { + super(new PDFProcessor(), new PageSize(x, y, width, height)); + // TODO: Default graphics state does not need to be printed in the document + setColor(Color.BLACK); + setStroke(new BasicStroke(1f, BasicStroke.CAP_BUTT, BasicStroke.JOIN_MITER, 10f, null, 0f)); + } +} diff --git a/io-vector/src/main/java/org/xbib/graphics/io/vector/pdf/PDFObject.java b/io-vector/src/main/java/org/xbib/graphics/io/vector/pdf/PDFObject.java new file mode 100644 index 0000000..d831a64 --- /dev/null +++ b/io-vector/src/main/java/org/xbib/graphics/io/vector/pdf/PDFObject.java @@ -0,0 +1,22 @@ +package org.xbib.graphics.io.vector.pdf; + +import java.util.LinkedHashMap; +import java.util.Map; + +public class PDFObject { + public final int id; + public final int version; + public final Map dict; + public final Payload payload; + + public PDFObject(int id, int version, Map dict, Payload payload) { + this.dict = new LinkedHashMap<>(); + this.id = id; + this.version = version; + this.payload = payload; + if (dict != null) { + this.dict.putAll(dict); + } + } +} + diff --git a/io-vector/src/main/java/org/xbib/graphics/io/vector/pdf/PDFProcessor.java b/io-vector/src/main/java/org/xbib/graphics/io/vector/pdf/PDFProcessor.java new file mode 100644 index 0000000..e3589f8 --- /dev/null +++ b/io-vector/src/main/java/org/xbib/graphics/io/vector/pdf/PDFProcessor.java @@ -0,0 +1,37 @@ +package org.xbib.graphics.io.vector.pdf; + +import org.xbib.graphics.io.vector.ProcessorResult; +import org.xbib.graphics.io.vector.Processor; +import org.xbib.graphics.io.vector.Command; +import org.xbib.graphics.io.vector.filters.AbsoluteToRelativeTransformsFilter; +import org.xbib.graphics.io.vector.filters.FillPaintedShapeAsImageFilter; +import org.xbib.graphics.io.vector.filters.StateChangeGroupingFilter; +import org.xbib.graphics.io.vector.PageSize; +import java.io.IOException; + +public class PDFProcessor implements Processor { + + private final boolean compressed; + + public PDFProcessor() { + this(false); + } + + public PDFProcessor(boolean compressed) { + this.compressed = compressed; + } + + @Override + public ProcessorResult process(Iterable> commands, PageSize pageSize) throws IOException { + AbsoluteToRelativeTransformsFilter absoluteToRelativeTransformsFilter = new AbsoluteToRelativeTransformsFilter(commands); + FillPaintedShapeAsImageFilter paintedShapeAsImageFilter = new FillPaintedShapeAsImageFilter(absoluteToRelativeTransformsFilter); + Iterable> filtered = new StateChangeGroupingFilter(paintedShapeAsImageFilter); + PDFProcessorResult doc = new PDFProcessorResult(pageSize); + doc.setCompressed(compressed); + for (Command command : filtered) { + doc.handle(command); + } + doc.close(); + return doc; + } +} diff --git a/io-vector/src/main/java/org/xbib/graphics/io/vector/pdf/PDFProcessorResult.java b/io-vector/src/main/java/org/xbib/graphics/io/vector/pdf/PDFProcessorResult.java new file mode 100644 index 0000000..bd37ad1 --- /dev/null +++ b/io-vector/src/main/java/org/xbib/graphics/io/vector/pdf/PDFProcessorResult.java @@ -0,0 +1,585 @@ +package org.xbib.graphics.io.vector.pdf; + +import org.xbib.graphics.io.vector.ProcessorResult; +import org.xbib.graphics.io.vector.GraphicsState; +import org.xbib.graphics.io.vector.commands.AffineTransformCommand; +import org.xbib.graphics.io.vector.Command; +import org.xbib.graphics.io.vector.commands.CreateCommand; +import org.xbib.graphics.io.vector.commands.DisposeCommand; +import org.xbib.graphics.io.vector.commands.DrawImageCommand; +import org.xbib.graphics.io.vector.commands.DrawShapeCommand; +import org.xbib.graphics.io.vector.commands.DrawStringCommand; +import org.xbib.graphics.io.vector.commands.FillShapeCommand; +import org.xbib.graphics.io.vector.commands.Group; +import org.xbib.graphics.io.vector.commands.SetBackgroundCommand; +import org.xbib.graphics.io.vector.commands.SetClipCommand; +import org.xbib.graphics.io.vector.commands.SetColorCommand; +import org.xbib.graphics.io.vector.commands.SetFontCommand; +import org.xbib.graphics.io.vector.commands.SetHintCommand; +import org.xbib.graphics.io.vector.commands.SetPaintCommand; +import org.xbib.graphics.io.vector.commands.SetStrokeCommand; +import org.xbib.graphics.io.vector.commands.SetTransformCommand; +import org.xbib.graphics.io.vector.util.DataUtils; +import org.xbib.graphics.io.vector.util.FlateEncodeStream; +import org.xbib.graphics.io.vector.util.FormattingWriter; +import org.xbib.graphics.io.vector.util.GraphicsUtils; +import org.xbib.graphics.io.vector.util.ImageDataStream; +import org.xbib.graphics.io.vector.util.ImageDataStream.Interleaving; +import org.xbib.graphics.io.vector.PageSize; +import java.awt.BasicStroke; +import java.awt.Color; +import java.awt.Font; +import java.awt.Image; +import java.awt.Shape; +import java.awt.Stroke; +import java.awt.geom.AffineTransform; +import java.awt.geom.PathIterator; +import java.awt.image.BufferedImage; +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; +import java.lang.reflect.InvocationTargetException; +import java.util.Arrays; +import java.util.Deque; +import java.util.HashMap; +import java.util.LinkedList; +import java.util.List; +import java.util.Map; + +public class PDFProcessorResult implements ProcessorResult { + + private static final String EOL = "\n"; + + private static final String CHARSET = "ISO-8859-1"; + + private static final String HEADER = "%PDF-1.4"; + + private static final String FOOTER = "%%EOF"; + + /** + * Constant to convert values from millimeters to PDF units (1/72th inch). + */ + private static final double MM_IN_UNITS = 72.0 / 25.4; + + /** + * Mapping of stroke endcap values from Java to PDF. + */ + private static final Map STROKE_ENDCAPS = DataUtils.map( + new Integer[]{BasicStroke.CAP_BUTT, BasicStroke.CAP_ROUND, BasicStroke.CAP_SQUARE}, + new Integer[]{0, 1, 2} + ); + + /** + * Mapping of line join values for path drawing from Java to PDF. + */ + private static final Map STROKE_LINEJOIN = DataUtils.map( + new Integer[]{BasicStroke.JOIN_MITER, BasicStroke.JOIN_ROUND, BasicStroke.JOIN_BEVEL}, + new Integer[]{0, 1, 2} + ); + + private final PageSize pageSize; + + private final List objects; + + private final Map xref; + + private final Map images; + + private final Deque states; + + private int objectIdCounter; + + private PDFObject contents; + + private Resources resources; + + private boolean transformed; + + private boolean compressed; + + public PDFProcessorResult(PageSize pageSize) throws IOException { + this.pageSize = pageSize; + states = new LinkedList<>(); + states.push(new GraphicsState()); + objects = new LinkedList<>(); + objectIdCounter = 1; + xref = new HashMap<>(); + images = new HashMap<>(); + compressed = false; // disable compress by default + initPage(); + } + + public void setCompressed(boolean compressed) { + this.compressed = compressed; + } + + public static String toString(PDFObject obj) throws IOException { + StringBuilder out = new StringBuilder(); + out.append(obj.id).append(" ").append(obj.version).append(" obj") + .append(EOL); + if (!obj.dict.isEmpty()) { + out.append(serialize(obj.dict)).append(EOL); + } + if (obj.payload != null) { + String content = new String(obj.payload.getBytes(), CHARSET); + if (content.length() > 0) { + if (obj.payload.isStream()) { + out.append("stream").append(EOL); + } + out.append(content); + if (obj.payload.isStream()) { + out.append("endstream"); + } + out.append(EOL); + } + } + out.append("endobj"); + return out.toString(); + } + + private static String serialize(Object obj) { + if (obj instanceof String) { + return "/" + obj.toString(); + } else if (obj instanceof float[]) { + return serialize(DataUtils.asList((float[]) obj)); + } else if (obj instanceof double[]) { + return serialize(DataUtils.asList((double[]) obj)); + } else if (obj instanceof Object[]) { + return serialize(Arrays.asList((Object[]) obj)); + } else if (obj instanceof List) { + List list = (List) obj; + StringBuilder out = new StringBuilder(); + out.append("["); + int i = 0; + for (Object elem : list) { + if (i++ > 0) { + out.append(" "); + } + out.append(serialize(elem)); + } + out.append("]"); + return out.toString(); + } else if (obj instanceof Map) { + Map dict = (Map) obj; + StringBuilder out = new StringBuilder(); + out.append("<<").append(EOL); + for (Map.Entry entry : dict.entrySet()) { + String key = entry.getKey().toString(); + out.append(serialize(key)).append(" "); + Object value = entry.getValue(); + out.append(serialize(value)).append(EOL); + } + out.append(">>"); + return out.toString(); + } else if (obj instanceof PDFObject) { + PDFObject pdfObj = (PDFObject) obj; + return pdfObj.id + " " + pdfObj.version + " R"; + } else { + return DataUtils.format(obj); + } + } + + private static String getOutput(Color c) { + StringBuilder out = new StringBuilder(); + String r = serialize(c.getRed() / 255.0); + String g = serialize(c.getGreen() / 255.0); + String b = serialize(c.getBlue() / 255.0); + out.append(r).append(" ").append(g).append(" ").append(b).append(" rg ") + .append(r).append(" ").append(g).append(" ").append(b).append(" RG"); + return out.toString(); + } + + private static String getOutput(Shape s) { + StringBuilder out = new StringBuilder(); + PathIterator segments = s.getPathIterator(null); + double[] coordsCur = new double[6]; + double[] pointPrev = new double[2]; + for (int i = 0; !segments.isDone(); i++, segments.next()) { + if (i > 0) { + out.append(" "); + } + int segmentType = segments.currentSegment(coordsCur); + switch (segmentType) { + case PathIterator.SEG_MOVETO: + out.append(serialize(coordsCur[0])).append(" ") + .append(serialize(coordsCur[1])).append(" m"); + pointPrev[0] = coordsCur[0]; + pointPrev[1] = coordsCur[1]; + break; + case PathIterator.SEG_LINETO: + out.append(serialize(coordsCur[0])).append(" ") + .append(serialize(coordsCur[1])).append(" l"); + pointPrev[0] = coordsCur[0]; + pointPrev[1] = coordsCur[1]; + break; + case PathIterator.SEG_CUBICTO: + out.append(serialize(coordsCur[0])).append(" ") + .append(serialize(coordsCur[1])).append(" ") + .append(serialize(coordsCur[2])).append(" ") + .append(serialize(coordsCur[3])).append(" ") + .append(serialize(coordsCur[4])).append(" ") + .append(serialize(coordsCur[5])).append(" c"); + pointPrev[0] = coordsCur[4]; + pointPrev[1] = coordsCur[5]; + break; + case PathIterator.SEG_QUADTO: + double x1 = pointPrev[0] + 2.0 / 3.0 * (coordsCur[0] - pointPrev[0]); + double y1 = pointPrev[1] + 2.0 / 3.0 * (coordsCur[1] - pointPrev[1]); + double x2 = coordsCur[0] + 1.0 / 3.0 * (coordsCur[2] - coordsCur[0]); + double y2 = coordsCur[1] + 1.0 / 3.0 * (coordsCur[3] - coordsCur[1]); + double x3 = coordsCur[2]; + double y3 = coordsCur[3]; + out.append(serialize(x1)).append(" ") + .append(serialize(y1)).append(" ") + .append(serialize(x2)).append(" ") + .append(serialize(y2)).append(" ") + .append(serialize(x3)).append(" ") + .append(serialize(y3)).append(" c"); + pointPrev[0] = x3; + pointPrev[1] = y3; + break; + case PathIterator.SEG_CLOSE: + out.append("h"); + break; + default: + throw new IllegalStateException("Unknown path operation."); + } + } + + return out.toString(); + } + + private static String getOutput(GraphicsState state, Resources resources, boolean first) { + StringBuilder out = new StringBuilder(); + if (!first) { + out.append("Q").append(EOL); + } + out.append("q").append(EOL); + if (!state.getColor().equals(GraphicsState.DEFAULT_COLOR)) { + if (state.getColor().getAlpha() != GraphicsState.DEFAULT_COLOR.getAlpha()) { + double a = state.getColor().getAlpha() / 255.0; + String resourceId = resources.getId(a); + out.append("/").append(resourceId).append(" gs").append(EOL); + } + out.append(getOutput(state.getColor())).append(EOL); + } + if (!state.getTransform().equals(GraphicsState.DEFAULT_TRANSFORM)) { + out.append(getOutput(state.getTransform())).append(" cm").append(EOL); + } + if (!state.getStroke().equals(GraphicsState.DEFAULT_STROKE)) { + out.append(getOutput(state.getStroke())).append(EOL); + } + if (state.getClip() != GraphicsState.DEFAULT_CLIP) { + out.append(getOutput(state.getClip())).append(" W n").append(EOL); + } + if (!state.getFont().equals(GraphicsState.DEFAULT_FONT)) { + Font font = state.getFont(); + String fontResourceId = resources.getId(font); + float fontSize = font.getSize2D(); + out.append("/").append(fontResourceId).append(" ").append(fontSize) + .append(" Tf").append(EOL); + } + + return DataUtils.stripTrailing(out.toString(), EOL); + } + + private static String getOutput(Stroke s) { + StringBuilder out = new StringBuilder(); + if (s instanceof BasicStroke) { + BasicStroke strokeDefault = (BasicStroke) GraphicsState.DEFAULT_STROKE; + BasicStroke strokeNew = (BasicStroke) s; + if (strokeNew.getLineWidth() != strokeDefault.getLineWidth()) { + out.append(serialize(strokeNew.getLineWidth())) + .append(" w").append(EOL); + } + if (strokeNew.getLineJoin() == BasicStroke.JOIN_MITER && strokeNew.getMiterLimit() != strokeDefault.getMiterLimit()) { + out.append(serialize(strokeNew.getMiterLimit())) + .append(" M").append(EOL); + } + if (strokeNew.getLineJoin() != strokeDefault.getLineJoin()) { + out.append(serialize(STROKE_LINEJOIN.get(strokeNew.getLineJoin()))) + .append(" j").append(EOL); + } + if (strokeNew.getEndCap() != strokeDefault.getEndCap()) { + out.append(serialize(STROKE_ENDCAPS.get(strokeNew.getEndCap()))) + .append(" J").append(EOL); + } + if (strokeNew.getDashArray() != strokeDefault.getDashArray()) { + if (strokeNew.getDashArray() != null) { + out.append(serialize(strokeNew.getDashArray())).append(" ") + .append(serialize(strokeNew.getDashPhase())) + .append(" d").append(EOL); + } else { + out.append(EOL).append("[] 0 d").append(EOL); + } + } + } + return out.toString(); + } + + private static String getOutput(AffineTransform transform) { + double[] matrix = new double[6]; + transform.getMatrix(matrix); + return DataUtils.join(" ", matrix); + } + + private static String getOutput(String str, double x, double y) { + + // Save current graphics state + // Undo swapping of y axis + // Render text + // Restore previous graphics state + + return "q " + "1 0 0 -1 " + x + " " + y + " cm " + "BT " + getOutput(str) + " Tj ET " + "Q"; + } + + private static StringBuilder getOutput(String str) { + StringBuilder out = new StringBuilder(); + + // Escape string + str = str.replaceAll("\\\\", "\\\\\\\\") + .replaceAll("\t", "\\\\t") + .replaceAll("\b", "\\\\b") + .replaceAll("\f", "\\\\f") + .replaceAll("\\(", "\\\\(") + .replaceAll("\\)", "\\\\)") + .replaceAll("[\r\n]", ""); + + out.append("(").append(str).append(")"); + + return out; + } + + private static String getOutput(PDFObject image, double x, double y, + double width, double height, Resources resources) { + // Query image resource id + String resourceId = resources.getId(image); + + // Save graphics state + // Move image to correct position and scale it to (width, height) + // Swap y axis + // Draw image + // Restore old graphics state + + return "q " + width + " 0 0 " + height + " " + x + " " + y + " cm " + "1 0 0 -1 0 1 cm " + "/" + resourceId + " Do " + "Q"; + } + + private GraphicsState getCurrentState() { + return states.peek(); + } + + private void initPage() throws IOException { + Map dict; + dict = DataUtils.map(new String[]{"Type"}, new Object[]{"Catalog"}); + PDFObject catalog = addObject(dict, null); + List pagesKids = new LinkedList<>(); + dict = DataUtils.map( + new String[]{"Type", "Kids", "Count"}, + new Object[]{"Pages", pagesKids, 1}); + PDFObject pages = addObject(dict, null); + catalog.dict.put("Pages", pages); + double x = pageSize.getX() * MM_IN_UNITS; + double y = pageSize.getY() * MM_IN_UNITS; + double width = pageSize.getWidth() * MM_IN_UNITS; + double height = pageSize.getHeight() * MM_IN_UNITS; + dict = DataUtils.map( + new String[]{"Type", "Parent", "MediaBox"}, + new Object[]{"Page", pages, new double[]{x, y, width, height}}); + PDFObject page = addObject(dict, null); + pagesKids.add(page); + Payload contentsPayload = new Payload(true); + contents = addObject(null, contentsPayload); + page.dict.put("Contents", contents); + if (compressed) { + try { + contentsPayload.addFilter(FlateEncodeStream.class); + contents.dict.put("Filter", new Object[]{"FlateDecode"}); + } catch (NoSuchMethodException | InstantiationException | InvocationTargetException | IllegalAccessException e) { + // ignore + } + } + contentsPayload.write(DataUtils.join("", new Object[]{ + "q", EOL, + getOutput(getCurrentState().getColor()), EOL, + MM_IN_UNITS, " 0 0 ", -MM_IN_UNITS, " 0 ", height, " cm", EOL + }).getBytes(CHARSET)); + Payload contentLengthPayload = new SizePayload(contents, CHARSET, false); + PDFObject contentLength = addObject(null, contentLengthPayload); + contents.dict.put("Length", contentLength); + resources = new Resources(objectIdCounter++, 0); + objects.add(resources); + page.dict.put("Resources", resources); + Font font = getCurrentState().getFont(); + String fontResourceId = resources.getId(font); + float fontSize = font.getSize2D(); + contentsPayload.write(("/" + fontResourceId + " " + fontSize + " Tf" + EOL).getBytes(CHARSET)); + } + + private PDFObject addObject(Map dict, Payload payload) { + final int id = objectIdCounter++; + final int version = 0; + PDFObject object = new PDFObject(id, version, dict, payload); + objects.add(object); + return object; + } + + private PDFObject addObject(Image image) throws IOException { + BufferedImage bufferedImage = GraphicsUtils.toBufferedImage(image); + int width = bufferedImage.getWidth(); + int height = bufferedImage.getHeight(); + int bitsPerSample = DataUtils.max(bufferedImage.getSampleModel().getSampleSize()); + int bands = bufferedImage.getSampleModel().getNumBands(); + String colorSpaceName = (bands == 1) ? "DeviceGray" : "DeviceRGB"; + Payload imagePayload = new Payload(true); + String[] imageFilters = {}; + if (compressed) { + try { + imagePayload.addFilter(FlateEncodeStream.class); + imageFilters = new String[]{"FlateDecode"}; + } catch (NoSuchMethodException | InstantiationException | InvocationTargetException | IllegalAccessException e) { + // ignore + } + } + InputStream imageDataStream = new ImageDataStream(bufferedImage, Interleaving.WITHOUT_ALPHA); + DataUtils.transfer(imageDataStream, imagePayload, 1024); + imagePayload.close(); + int length = imagePayload.getBytes().length; + Map imageDict = DataUtils.map( + new String[]{"Type", "Subtype", "Width", "Height", "ColorSpace", + "BitsPerComponent", "Length", "Filter"}, + new Object[]{"XObject", "Image", width, height, colorSpaceName, + bitsPerSample, length, imageFilters} + ); + PDFObject imageObject = addObject(imageDict, imagePayload); + boolean hasAlpha = bufferedImage.getColorModel().hasAlpha(); + if (hasAlpha) { + BufferedImage mask = GraphicsUtils.getAlphaImage(bufferedImage); + PDFObject maskObject = addObject(mask); + boolean isBitmask = mask.getSampleModel().getSampleSize(0) == 1; + if (isBitmask) { + maskObject.dict.put("ImageMask", true); + maskObject.dict.remove("ColorSpace"); + imageObject.dict.put("Mask", maskObject); + } else { + imageObject.dict.put("SMask", maskObject); + } + } + return imageObject; + } + + public void write(OutputStream out) throws IOException { + FormattingWriter o = new FormattingWriter(out, CHARSET, EOL); + o.writeln(HEADER); + for (PDFObject obj : objects) { + xref.put(obj, o.tell()); + o.writeln(toString(obj)); + o.flush(); + } + long xrefPos = o.tell(); + o.writeln("xref"); + o.write(0).write(" ").writeln(objects.size() + 1); + o.format("%010d %05d f ", 0, 65535).writeln(); + for (PDFObject obj : objects) { + o.format("%010d %05d n ", xref.get(obj), 0).writeln(); + } + o.flush(); + o.writeln("trailer"); + o.writeln(serialize(DataUtils.map( + new String[]{"Size", "Root"}, + new Object[]{objects.size() + 1, objects.get(0)} + ))); + + o.writeln("startxref"); + o.writeln(xrefPos); + o.writeln(FOOTER); + o.flush(); + } + + public void handle(Command command) throws IOException { + String s = ""; + if (command instanceof Group) { + Group c = (Group) command; + applyStateCommands(c.getValue()); + s = getOutput(getCurrentState(), resources, !transformed); + transformed = true; + } else if (command instanceof DrawShapeCommand) { + DrawShapeCommand c = (DrawShapeCommand) command; + s = getOutput(c.getValue()) + " S"; + } else if (command instanceof FillShapeCommand) { + FillShapeCommand c = (FillShapeCommand) command; + s = getOutput(c.getValue()) + " f"; + } else if (command instanceof DrawStringCommand) { + DrawStringCommand c = (DrawStringCommand) command; + s = getOutput(c.getValue(), c.getX(), c.getY()); + } else if (command instanceof DrawImageCommand) { + DrawImageCommand c = (DrawImageCommand) command; + // Create object for image data + Image image = c.getValue(); + PDFObject imageObject = images.get(image.hashCode()); + if (imageObject == null) { + imageObject = addObject(image); + images.put(image.hashCode(), imageObject); + } + s = getOutput(imageObject, c.getX(), c.getY(), + c.getWidth(), c.getHeight(), resources); + } + Payload contentsPayload = contents.payload; + contentsPayload.write(s.getBytes(CHARSET)); + contentsPayload.write(EOL.getBytes(CHARSET)); + } + + private void applyStateCommands(List> commands) { + for (Command command : commands) { + if (command instanceof SetHintCommand) { + SetHintCommand c = (SetHintCommand) command; + getCurrentState().getHints().put(c.getKey(), c.getValue()); + } else if (command instanceof SetBackgroundCommand) { + SetBackgroundCommand c = (SetBackgroundCommand) command; + getCurrentState().setBackground(c.getValue()); + } else if (command instanceof SetColorCommand) { + SetColorCommand c = (SetColorCommand) command; + getCurrentState().setColor(c.getValue()); + } else if (command instanceof SetPaintCommand) { + SetPaintCommand c = (SetPaintCommand) command; + getCurrentState().setPaint(c.getValue()); + } else if (command instanceof SetStrokeCommand) { + SetStrokeCommand c = (SetStrokeCommand) command; + getCurrentState().setStroke(c.getValue()); + } else if (command instanceof SetFontCommand) { + SetFontCommand c = (SetFontCommand) command; + getCurrentState().setFont(c.getValue()); + } else if (command instanceof SetTransformCommand) { + throw new UnsupportedOperationException("The PDF format has no means of setting the transformation matrix."); + } else if (command instanceof AffineTransformCommand) { + AffineTransformCommand c = (AffineTransformCommand) command; + AffineTransform stateTransform = getCurrentState().getTransform(); + AffineTransform transformToBeApplied = c.getValue(); + stateTransform.concatenate(transformToBeApplied); + getCurrentState().setTransform(stateTransform); + } else if (command instanceof SetClipCommand) { + SetClipCommand c = (SetClipCommand) command; + getCurrentState().setClip(c.getValue()); + } else if (command instanceof CreateCommand) { + try { + states.push((GraphicsState) getCurrentState().clone()); + } catch (CloneNotSupportedException e) { + // do nothing + } + } else if (command instanceof DisposeCommand) { + states.pop(); + } + } + } + + @Override + public void close() throws IOException { + String footer = "Q" + EOL; + if (transformed) { + footer += "Q" + EOL; + } + Payload contentsPayload = contents.payload; + contentsPayload.write(footer.getBytes(CHARSET)); + contentsPayload.close(); + } +} + diff --git a/io-vector/src/main/java/org/xbib/graphics/io/vector/pdf/Payload.java b/io-vector/src/main/java/org/xbib/graphics/io/vector/pdf/Payload.java new file mode 100644 index 0000000..3daed17 --- /dev/null +++ b/io-vector/src/main/java/org/xbib/graphics/io/vector/pdf/Payload.java @@ -0,0 +1,54 @@ +package org.xbib.graphics.io.vector.pdf; + +import java.io.ByteArrayOutputStream; +import java.io.FilterOutputStream; +import java.io.IOException; +import java.io.OutputStream; +import java.lang.reflect.InvocationTargetException; + +public class Payload extends OutputStream { + + private final ByteArrayOutputStream byteStream; + + private final boolean stream; + + private OutputStream filteredStream; + + private boolean empty; + + public Payload(boolean stream) { + this.byteStream = new ByteArrayOutputStream(); + this.stream = stream; + this.filteredStream = byteStream; + this.empty = true; + } + + public byte[] getBytes() throws IOException { + return byteStream.toByteArray(); + } + + public boolean isStream() { + return stream; + } + + @Override + public void write(int b) throws IOException { + filteredStream.write(b); + empty = false; + } + + @Override + public void close() throws IOException { + super.close(); + filteredStream.close(); + } + + public void addFilter(Class filterClass) + throws NoSuchMethodException, IllegalAccessException, InvocationTargetException, InstantiationException { + if (!empty) { + throw new IllegalStateException("unable to add filter after writing to payload"); + } + filteredStream = filterClass.getConstructor(OutputStream.class).newInstance(filteredStream); + } +} + diff --git a/io-vector/src/main/java/org/xbib/graphics/io/vector/pdf/Resources.java b/io-vector/src/main/java/org/xbib/graphics/io/vector/pdf/Resources.java new file mode 100644 index 0000000..82ffaa8 --- /dev/null +++ b/io-vector/src/main/java/org/xbib/graphics/io/vector/pdf/Resources.java @@ -0,0 +1,98 @@ +package org.xbib.graphics.io.vector.pdf; + +import org.xbib.graphics.io.vector.util.DataUtils; +import org.xbib.graphics.io.vector.util.GraphicsUtils; +import java.awt.Font; +import java.util.HashMap; +import java.util.LinkedHashMap; +import java.util.Map; +import java.util.concurrent.atomic.AtomicInteger; + +public class Resources extends PDFObject { + + private static final String KEY_PROC_SET = "ProcSet"; + private static final String KEY_TRANSPARENCY = "ExtGState"; + private static final String KEY_FONT = "Font"; + private static final String KEY_IMAGE = "XObject"; + + private static final String[] VALUE_PROC_SET = {"PDF", "Text", "ImageB", "ImageC", "ImageI"}; + + private static final String PREFIX_FONT = "Fnt"; + private static final String PREFIX_IMAGE = "Img"; + private static final String PREFIX_TRANSPARENCY = "Trp"; + + private final Map fonts; + private final Map images; + private final Map transparencies; + + private final AtomicInteger currentFontId = new AtomicInteger(); + private final AtomicInteger currentImageId = new AtomicInteger(); + private final AtomicInteger currentTransparencyId = new AtomicInteger(); + + public Resources(int id, int version) { + super(id, version, null, null); + fonts = new HashMap<>(); + images = new HashMap<>(); + transparencies = new HashMap<>(); + dict.put(KEY_PROC_SET, VALUE_PROC_SET); + } + + private String getResourceId(Map resources, T resource, + String idPrefix, AtomicInteger idCounter) { + String id = resources.get(resource); + if (id == null) { + id = String.format("%s%d", idPrefix, idCounter.getAndIncrement()); + resources.put(resource, id); + } + return id; + } + + @SuppressWarnings("unchecked") + public String getId(Font font) { + Map> dictEntry = + (Map>) dict.get(KEY_FONT); + if (dictEntry == null) { + dictEntry = new LinkedHashMap<>(); + dict.put(KEY_FONT, dictEntry); + } + font = GraphicsUtils.getPhysicalFont(font); + String resourceId = getResourceId(fonts, font, PREFIX_FONT, currentFontId); + String fontName = font.getPSName(); + // TODO: Determine font encoding (e.g. MacRomanEncoding, MacExpertEncoding, WinAnsiEncoding) + String fontEncoding = "WinAnsiEncoding"; + dictEntry.put(resourceId, DataUtils.map( + new String[]{"Type", "Subtype", "Encoding", "BaseFont"}, + new Object[]{"Font", "TrueType", fontEncoding, fontName} + )); + return resourceId; + } + + @SuppressWarnings("unchecked") + public String getId(PDFObject image) { + Map dictEntry = (Map) dict.get(KEY_IMAGE); + if (dictEntry == null) { + dictEntry = new LinkedHashMap<>(); + dict.put(KEY_IMAGE, dictEntry); + } + String resourceId = getResourceId(images, image, PREFIX_IMAGE, currentImageId); + dictEntry.put(resourceId, image); + return resourceId; + } + + @SuppressWarnings("unchecked") + public String getId(Double transparency) { + Map> dictEntry = + (Map>) dict.get(KEY_TRANSPARENCY); + if (dictEntry == null) { + dictEntry = new LinkedHashMap<>(); + dict.put(KEY_TRANSPARENCY, dictEntry); + } + String resourceId = getResourceId(transparencies, transparency, + PREFIX_TRANSPARENCY, currentTransparencyId); + dictEntry.put(resourceId, DataUtils.map( + new String[]{"Type", "ca", "CA"}, + new Object[]{"ExtGState", transparency, transparency} + )); + return resourceId; + } +} diff --git a/io-vector/src/main/java/org/xbib/graphics/io/vector/pdf/SizePayload.java b/io-vector/src/main/java/org/xbib/graphics/io/vector/pdf/SizePayload.java new file mode 100644 index 0000000..907963e --- /dev/null +++ b/io-vector/src/main/java/org/xbib/graphics/io/vector/pdf/SizePayload.java @@ -0,0 +1,25 @@ +package org.xbib.graphics.io.vector.pdf; + +import org.xbib.graphics.io.vector.util.DataUtils; +import java.io.IOException; + +public class SizePayload extends GeneratedPayload { + + private final PDFObject object; + + private final String charset; + + public SizePayload(PDFObject object, String charset, boolean stream) { + super(stream); + this.object = object; + this.charset = charset; + } + + @Override + protected byte[] generatePayload() throws IOException { + object.payload.close(); + String content = DataUtils.format(object.payload.getBytes().length); + return content.getBytes(charset); + } +} + diff --git a/io-vector/src/main/java/org/xbib/graphics/io/vector/svg/SVGGraphics2D.java b/io-vector/src/main/java/org/xbib/graphics/io/vector/svg/SVGGraphics2D.java new file mode 100644 index 0000000..4ea3c04 --- /dev/null +++ b/io-vector/src/main/java/org/xbib/graphics/io/vector/svg/SVGGraphics2D.java @@ -0,0 +1,27 @@ +package org.xbib.graphics.io.vector.svg; + +import org.xbib.graphics.io.vector.VectorGraphics2D; +import org.xbib.graphics.io.vector.PageSize; +import java.awt.Color; + +/** + * {@code Graphics2D} implementation that saves all operations to a string + * in the Scaled Vector Graphics (SVG) format. + */ +public class SVGGraphics2D extends VectorGraphics2D { + + /** + * Initializes a new VectorGraphics2D pipeline for translating Graphics2D + * commands to SVG data. The document dimensions must be specified as + * parameters. + * + * @param x Left offset. + * @param y Top offset + * @param width Width. + * @param height Height. + */ + public SVGGraphics2D(double x, double y, double width, double height) { + super(new SVGProcessor(), new PageSize(x, y, width, height)); + setColor(Color.BLACK); + } +} diff --git a/io-vector/src/main/java/org/xbib/graphics/io/vector/svg/SVGProcessor.java b/io-vector/src/main/java/org/xbib/graphics/io/vector/svg/SVGProcessor.java new file mode 100644 index 0000000..6ebc4ad --- /dev/null +++ b/io-vector/src/main/java/org/xbib/graphics/io/vector/svg/SVGProcessor.java @@ -0,0 +1,21 @@ +package org.xbib.graphics.io.vector.svg; + +import org.xbib.graphics.io.vector.ProcessorResult; +import org.xbib.graphics.io.vector.Processor; +import org.xbib.graphics.io.vector.Command; +import org.xbib.graphics.io.vector.filters.FillPaintedShapeAsImageFilter; +import org.xbib.graphics.io.vector.filters.StateChangeGroupingFilter; +import org.xbib.graphics.io.vector.PageSize; + +public class SVGProcessor implements Processor { + public ProcessorResult process(Iterable> commands, PageSize pageSize) { + FillPaintedShapeAsImageFilter shapesAsImages = new FillPaintedShapeAsImageFilter(commands); + Iterable> filtered = new StateChangeGroupingFilter(shapesAsImages); + SVGProcessorResult doc = new SVGProcessorResult(pageSize); + for (Command command : filtered) { + doc.handle(command); + } + doc.close(); + return doc; + } +} diff --git a/io-vector/src/main/java/org/xbib/graphics/io/vector/svg/SVGProcessorResult.java b/io-vector/src/main/java/org/xbib/graphics/io/vector/svg/SVGProcessorResult.java new file mode 100644 index 0000000..c9d6353 --- /dev/null +++ b/io-vector/src/main/java/org/xbib/graphics/io/vector/svg/SVGProcessorResult.java @@ -0,0 +1,547 @@ +package org.xbib.graphics.io.vector.svg; + +import org.w3c.dom.DOMImplementation; +import org.w3c.dom.Document; +import org.w3c.dom.DocumentType; +import org.w3c.dom.Element; +import org.xbib.graphics.io.vector.GraphicsState; +import org.xbib.graphics.io.vector.ProcessorResult; +import org.xbib.graphics.io.vector.util.VectorHints; +import org.xbib.graphics.io.vector.commands.AffineTransformCommand; +import org.xbib.graphics.io.vector.Command; +import org.xbib.graphics.io.vector.commands.CreateCommand; +import org.xbib.graphics.io.vector.commands.DisposeCommand; +import org.xbib.graphics.io.vector.commands.DrawImageCommand; +import org.xbib.graphics.io.vector.commands.DrawShapeCommand; +import org.xbib.graphics.io.vector.commands.DrawStringCommand; +import org.xbib.graphics.io.vector.commands.FillShapeCommand; +import org.xbib.graphics.io.vector.commands.Group; +import org.xbib.graphics.io.vector.commands.SetBackgroundCommand; +import org.xbib.graphics.io.vector.commands.SetClipCommand; +import org.xbib.graphics.io.vector.commands.SetColorCommand; +import org.xbib.graphics.io.vector.commands.SetCompositeCommand; +import org.xbib.graphics.io.vector.commands.SetFontCommand; +import org.xbib.graphics.io.vector.commands.SetHintCommand; +import org.xbib.graphics.io.vector.commands.SetPaintCommand; +import org.xbib.graphics.io.vector.commands.SetStrokeCommand; +import org.xbib.graphics.io.vector.commands.SetTransformCommand; +import org.xbib.graphics.io.vector.util.Base64EncodeStream; +import org.xbib.graphics.io.vector.util.DataUtils; +import org.xbib.graphics.io.vector.util.GraphicsUtils; +import org.xbib.graphics.io.vector.PageSize; +import java.awt.BasicStroke; +import java.awt.Color; +import java.awt.Font; +import java.awt.Image; +import java.awt.Shape; +import java.awt.Stroke; +import java.awt.geom.AffineTransform; +import java.awt.geom.Ellipse2D; +import java.awt.geom.Line2D; +import java.awt.geom.PathIterator; +import java.awt.geom.Rectangle2D; +import java.awt.geom.RoundRectangle2D; +import java.awt.image.BufferedImage; +import java.io.ByteArrayOutputStream; +import java.io.IOException; +import java.io.OutputStream; +import java.nio.charset.StandardCharsets; +import java.util.Deque; +import java.util.HashMap; +import java.util.LinkedList; +import java.util.List; +import java.util.Locale; +import java.util.Map; +import javax.imageio.ImageIO; +import javax.xml.parsers.DocumentBuilder; +import javax.xml.parsers.DocumentBuilderFactory; +import javax.xml.parsers.ParserConfigurationException; +import javax.xml.transform.OutputKeys; +import javax.xml.transform.Transformer; +import javax.xml.transform.TransformerException; +import javax.xml.transform.TransformerFactory; +import javax.xml.transform.dom.DOMSource; +import javax.xml.transform.stream.StreamResult; + +public class SVGProcessorResult implements ProcessorResult { + + private static final String SVG_DOCTYPE_QNAME = "svg"; + + private static final String SVG_DOCTYPE_PUBLIC_ID = "-//W3C//DTD SVG 1.1//EN"; + + private static final String SVG_DOCTYPE_SYSTEM_ID = "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"; + + private static final String SVG_NAMESPACE_URI = "http://www.w3.org/2000/svg"; + + private static final String XLINK_NAMESPACE = "xlink"; + + private static final String XLINK_NAMESPACE_URI = "http://www.w3.org/1999/xlink"; + + private static final String PREFIX_CLIP = "clip"; + + private static final String CHARSET = "UTF-8"; + + private static final double DOTS_PER_MM = 2.834646; // 72 dpi + //private static final double DOTS_PER_MM = 11.811024; // 300 dpi + + /** + * Mapping of stroke endcap values from Java to SVG. + */ + private static final Map STROKE_ENDCAPS = + DataUtils.map(new Integer[]{BasicStroke.CAP_BUTT, BasicStroke.CAP_ROUND, BasicStroke.CAP_SQUARE}, + new String[]{"butt", "round", "square"} + ); + /** + * Mapping of line join values for path drawing from Java to SVG. + */ + private static final Map STROKE_LINEJOIN = + DataUtils.map(new Integer[]{BasicStroke.JOIN_MITER, BasicStroke.JOIN_ROUND, BasicStroke.JOIN_BEVEL}, + new String[]{"miter", "round", "bevel"} + ); + + private final PageSize pageSize; + + private final Deque states; + + private final Document doc; + + private final Element root; + + private final Map clippingPathElements; + + private Element group; + + private boolean groupAdded; + + private Element defs; + + public SVGProcessorResult(PageSize pageSize) { + this.pageSize = pageSize; + states = new LinkedList<>(); + states.push(new GraphicsState()); + clippingPathElements = new HashMap<>(); + DocumentBuilderFactory docFactory = DocumentBuilderFactory.newInstance(); + docFactory.setValidating(false); + DocumentBuilder docBuilder; + try { + docBuilder = docFactory.newDocumentBuilder(); + } catch (ParserConfigurationException e) { + throw new IllegalStateException("Could not create XML builder."); + } + DOMImplementation domImpl = docBuilder.getDOMImplementation(); + DocumentType docType = domImpl.createDocumentType(SVG_DOCTYPE_QNAME, SVG_DOCTYPE_PUBLIC_ID, SVG_DOCTYPE_SYSTEM_ID); + doc = domImpl.createDocument(SVG_NAMESPACE_URI, "svg", docType); + // FIXME: Some XML parsers don't support setting standalone to "false" + try { + doc.setXmlStandalone(false); + } catch (AbstractMethodError e) { + throw new IllegalStateException("Your XML parser does not support standalone XML documents."); + } + root = doc.getDocumentElement(); + initRoot(); + + group = root; + } + + private static void appendStyle(StringBuilder style, String attribute, Object value) { + style.append(attribute).append(":") + .append(DataUtils.format(value)).append(";"); + } + + private static String getOutput(AffineTransform tx) { + StringBuilder out = new StringBuilder(); + if (AffineTransform.getTranslateInstance(tx.getTranslateX(), tx.getTranslateY()).equals(tx)) { + out.append("translate(") + .append(DataUtils.format(tx.getTranslateX())).append(" ") + .append(DataUtils.format(tx.getTranslateY())).append(")"); + } else { + double[] matrix = new double[6]; + tx.getMatrix(matrix); + out.append("matrix(").append(DataUtils.join(" ", matrix)).append(")"); + } + return out.toString(); + } + + private static String getOutput(Color color) { + return String.format((Locale) null, "rgb(%d,%d,%d)", + color.getRed(), color.getGreen(), color.getBlue()); + } + + private static String getOutput(Shape shape) { + StringBuilder out = new StringBuilder(); + PathIterator segments = shape.getPathIterator(null); + double[] coords = new double[6]; + for (int i = 0; !segments.isDone(); i++, segments.next()) { + if (i > 0) { + out.append(" "); + } + int segmentType = segments.currentSegment(coords); + switch (segmentType) { + case PathIterator.SEG_MOVETO: + out.append("M").append(coords[0]).append(",").append(coords[1]); + break; + case PathIterator.SEG_LINETO: + out.append("L").append(coords[0]).append(",").append(coords[1]); + break; + case PathIterator.SEG_CUBICTO: + out.append("C") + .append(coords[0]).append(",").append(coords[1]).append(" ") + .append(coords[2]).append(",").append(coords[3]).append(" ") + .append(coords[4]).append(",").append(coords[5]); + break; + case PathIterator.SEG_QUADTO: + out.append("Q") + .append(coords[0]).append(",").append(coords[1]).append(" ") + .append(coords[2]).append(",").append(coords[3]); + break; + case PathIterator.SEG_CLOSE: + out.append("Z"); + break; + default: + throw new IllegalStateException("Unknown path operation."); + } + } + return out.toString(); + } + + private static String getOutput(Font font) { + StringBuilder out = new StringBuilder(); + if (!GraphicsState.DEFAULT_FONT.getFamily().equals(font.getFamily())) { + String physicalFamily = GraphicsUtils.getPhysicalFont(font).getFamily(); + out.append("font-family:\"").append(physicalFamily).append("\";"); + } + if (font.getSize2D() != GraphicsState.DEFAULT_FONT.getSize2D()) { + out.append("font-size:").append(DataUtils.format(font.getSize2D())).append("px;"); + } + if ((font.getStyle() & Font.ITALIC) != 0) { + out.append("font-style:italic;"); + } + if ((font.getStyle() & Font.BOLD) != 0) { + out.append("font-weight:bold;"); + } + return out.toString(); + } + + private static String getOutput(Image image, boolean lossyAllowed) { + BufferedImage bufferedImage = GraphicsUtils.toBufferedImage(image); + String encoded = encodeImage(bufferedImage, "png"); + if (!GraphicsUtils.usesAlpha(bufferedImage) && lossyAllowed) { + String encodedLossy = encodeImage(bufferedImage, "jpeg"); + if (encodedLossy.length() > 0 && encodedLossy.length() < encoded.length()) { + encoded = encodedLossy; + } + } + return encoded; + } + + private static String encodeImage(BufferedImage bufferedImage, String format) { + ByteArrayOutputStream byteStream = new ByteArrayOutputStream(); + Base64EncodeStream encodeStream = new Base64EncodeStream(byteStream); + try { + ImageIO.write(bufferedImage, format, encodeStream); + encodeStream.close(); + String encoded = byteStream.toString(StandardCharsets.ISO_8859_1); + return String.format("data:image/%s;base64,%s", format, encoded); + } catch (IOException e) { + return ""; + } + } + + private GraphicsState getCurrentState() { + return states.peek(); + } + + private void initRoot() { + double x = pageSize.getX(); + double y = pageSize.getY(); + double width = pageSize.getWidth(); + double height = pageSize.getHeight(); + root.setAttribute("xmlns:" + XLINK_NAMESPACE, XLINK_NAMESPACE_URI); + root.setAttribute("version", "1.1"); + root.setAttribute("x", DataUtils.format(x / DOTS_PER_MM) + "mm"); + root.setAttribute("y", DataUtils.format(y / DOTS_PER_MM) + "mm"); + root.setAttribute("width", DataUtils.format(width / DOTS_PER_MM) + "mm"); + root.setAttribute("height", DataUtils.format(height / DOTS_PER_MM) + "mm"); + root.setAttribute("viewBox", DataUtils.join(" ", new double[]{x, y, width, height})); + } + + public void write(OutputStream out) throws IOException { + TransformerFactory transformerFactory = TransformerFactory.newInstance(); + try { + Transformer transformer = transformerFactory.newTransformer(); + transformer.setOutputProperty(OutputKeys.INDENT, "yes"); + transformer.setOutputProperty(OutputKeys.STANDALONE, "no"); + transformer.setOutputProperty("{http://xml.apache.org/xslt}indent-amount", "2"); + transformer.setOutputProperty(OutputKeys.ENCODING, CHARSET); + transformer.setOutputProperty(OutputKeys.DOCTYPE_PUBLIC, + doc.getDoctype().getPublicId()); + transformer.setOutputProperty(OutputKeys.DOCTYPE_SYSTEM, + doc.getDoctype().getSystemId()); + transformer.transform(new DOMSource(doc), new StreamResult(out)); + } catch (TransformerException e) { + throw new IOException(e.getMessage()); + } + } + + @Override + public void close() { + // nothing to do + } + + @Override + public String toString() { + ByteArrayOutputStream out = new ByteArrayOutputStream(); + try { + write(out); + return out.toString(CHARSET); + } catch (IOException e) { + return ""; + } + } + + private void newGroup() { + group = doc.createElement("g"); + groupAdded = false; + Shape clip = getCurrentState().getClip(); + if (clip != GraphicsState.DEFAULT_CLIP) { + Element clipElem = getClipElement(clip); + String ref = "url(#" + clipElem.getAttribute("id") + ")"; + group.setAttribute("clip-path", ref); + } + AffineTransform tx = getCurrentState().getTransform(); + if (!GraphicsState.DEFAULT_TRANSFORM.equals(tx)) { + group.setAttribute("transform", getOutput(tx)); + } + } + + private Element getClipElement(Shape clip) { + Element path = clippingPathElements.get(clip.hashCode()); + if (path != null) { + return path; + } + if (defs == null) { + defs = doc.createElement("defs"); + root.insertBefore(defs, root.getFirstChild()); + } + path = doc.createElement("clipPath"); + path.setAttribute("id", PREFIX_CLIP + clip.hashCode()); + Element shape = getElement(clip); + shape.removeAttribute("style"); + path.appendChild(shape); + defs.appendChild(path); + clippingPathElements.put(clip.hashCode(), path); + return path; + } + + private void addToGroup(Element e) { + group.appendChild(e); + if (!groupAdded && group != root) { + root.appendChild(group); + groupAdded = true; + } + } + + public void handle(Command command) { + if (command instanceof Group) { + Group c = (Group) command; + applyStateCommands(c.getValue()); + if (containsGroupCommand(c.getValue())) { + newGroup(); + } + } else if (command instanceof DrawImageCommand) { + DrawImageCommand c = (DrawImageCommand) command; + Element e = getElement(c.getValue(), + c.getX(), c.getY(), c.getWidth(), c.getHeight()); + addToGroup(e); + } else if (command instanceof DrawShapeCommand) { + DrawShapeCommand c = (DrawShapeCommand) command; + Element e = getElement(c.getValue()); + e.setAttribute("style", getStyle(false)); + addToGroup(e); + } else if (command instanceof DrawStringCommand) { + DrawStringCommand c = (DrawStringCommand) command; + Element e = getElement(c.getValue(), c.getX(), c.getY()); + e.setAttribute("style", getStyle(getCurrentState().getFont())); + addToGroup(e); + } else if (command instanceof FillShapeCommand) { + FillShapeCommand c = (FillShapeCommand) command; + Element e = getElement(c.getValue()); + e.setAttribute("style", getStyle(true)); + addToGroup(e); + } + } + + private void applyStateCommands(List> commands) { + for (Command command : commands) { + GraphicsState state = getCurrentState(); + if (command instanceof SetBackgroundCommand) { + SetBackgroundCommand c = (SetBackgroundCommand) command; + state.setBackground(c.getValue()); + } else if (command instanceof SetClipCommand) { + SetClipCommand c = (SetClipCommand) command; + state.setClip(c.getValue()); + } else if (command instanceof SetColorCommand) { + SetColorCommand c = (SetColorCommand) command; + state.setColor(c.getValue()); + } else if (command instanceof SetCompositeCommand) { + SetCompositeCommand c = (SetCompositeCommand) command; + state.setComposite(c.getValue()); + } else if (command instanceof SetFontCommand) { + SetFontCommand c = (SetFontCommand) command; + state.setFont(c.getValue()); + } else if (command instanceof SetPaintCommand) { + SetPaintCommand c = (SetPaintCommand) command; + state.setPaint(c.getValue()); + } else if (command instanceof SetStrokeCommand) { + SetStrokeCommand c = (SetStrokeCommand) command; + state.setStroke(c.getValue()); + } else if (command instanceof SetTransformCommand) { + SetTransformCommand c = (SetTransformCommand) command; + state.setTransform(c.getValue()); + } else if (command instanceof AffineTransformCommand) { + AffineTransformCommand c = (AffineTransformCommand) command; + AffineTransform stateTransform = state.getTransform(); + AffineTransform transformToBeApplied = c.getValue(); + stateTransform.concatenate(transformToBeApplied); + state.setTransform(stateTransform); + } else if (command instanceof SetHintCommand) { + SetHintCommand c = (SetHintCommand) command; + state.getHints().put(c.getKey(), c.getValue()); + } else if (command instanceof CreateCommand) { + try { + states.push((GraphicsState) getCurrentState().clone()); + } catch (CloneNotSupportedException e) { + // ignore + } + } else if (command instanceof DisposeCommand) { + states.pop(); + } + } + } + + private boolean containsGroupCommand(List> commands) { + for (Command command : commands) { + if ((command instanceof SetClipCommand) || + (command instanceof SetTransformCommand) || + (command instanceof AffineTransformCommand)) { + return true; + } + } + return false; + } + + private String getStyle(boolean filled) { + StringBuilder style = new StringBuilder(); + Color color = getCurrentState().getColor(); + String colorOutput = getOutput(color); + double opacity = color.getAlpha() / 255.0; + if (filled) { + appendStyle(style, "fill", colorOutput); + if (color.getAlpha() < 255) { + appendStyle(style, "fill-opacity", opacity); + } + } else { + appendStyle(style, "fill", "none"); + } + if (!filled) { + appendStyle(style, "stroke", colorOutput); + if (color.getAlpha() < 255) { + appendStyle(style, "stroke-opacity", opacity); + } + Stroke stroke = getCurrentState().getStroke(); + if (stroke instanceof BasicStroke) { + BasicStroke bs = (BasicStroke) stroke; + if (bs.getLineWidth() != 1f) { + appendStyle(style, "stroke-width", bs.getLineWidth()); + } + if (bs.getMiterLimit() != 4f) { + appendStyle(style, "stroke-miterlimit", bs.getMiterLimit()); + } + if (bs.getEndCap() != BasicStroke.CAP_BUTT) { + appendStyle(style, "stroke-linecap", STROKE_ENDCAPS.get(bs.getEndCap())); + } + if (bs.getLineJoin() != BasicStroke.JOIN_MITER) { + appendStyle(style, "stroke-linejoin", STROKE_LINEJOIN.get(bs.getLineJoin())); + } + if (bs.getDashArray() != null) { + appendStyle(style, "stroke-dasharray", DataUtils.join(",", bs.getDashArray())); + if (bs.getDashPhase() != 0f) { + appendStyle(style, "stroke-dashoffset", bs.getDashPhase()); + } + } + } + } else { + appendStyle(style, "stroke", "none"); + } + return style.toString(); + } + + private String getStyle(Font font) { + String style = getStyle(true); + if (!GraphicsState.DEFAULT_FONT.equals(font)) { + style += getOutput(font); + } + return style; + } + + private Element getElement(Shape shape) { + Element elem; + if (shape instanceof Line2D) { + Line2D s = (Line2D) shape; + elem = doc.createElement("line"); + elem.setAttribute("x1", DataUtils.format(s.getX1())); + elem.setAttribute("y1", DataUtils.format(s.getY1())); + elem.setAttribute("x2", DataUtils.format(s.getX2())); + elem.setAttribute("y2", DataUtils.format(s.getY2())); + } else if (shape instanceof Rectangle2D) { + Rectangle2D s = (Rectangle2D) shape; + elem = doc.createElement("rect"); + elem.setAttribute("x", DataUtils.format(s.getX())); + elem.setAttribute("y", DataUtils.format(s.getY())); + elem.setAttribute("width", DataUtils.format(s.getWidth())); + elem.setAttribute("height", DataUtils.format(s.getHeight())); + } else if (shape instanceof RoundRectangle2D) { + RoundRectangle2D s = (RoundRectangle2D) shape; + elem = doc.createElement("rect"); + elem.setAttribute("x", DataUtils.format(s.getX())); + elem.setAttribute("y", DataUtils.format(s.getY())); + elem.setAttribute("width", DataUtils.format(s.getWidth())); + elem.setAttribute("height", DataUtils.format(s.getHeight())); + elem.setAttribute("rx", DataUtils.format(s.getArcWidth() / 2.0)); + elem.setAttribute("ry", DataUtils.format(s.getArcHeight() / 2.0)); + } else if (shape instanceof Ellipse2D) { + Ellipse2D s = (Ellipse2D) shape; + elem = doc.createElement("ellipse"); + elem.setAttribute("cx", DataUtils.format(s.getCenterX())); + elem.setAttribute("cy", DataUtils.format(s.getCenterY())); + elem.setAttribute("rx", DataUtils.format(s.getWidth() / 2.0)); + elem.setAttribute("ry", DataUtils.format(s.getHeight() / 2.0)); + } else { + elem = doc.createElement("path"); + elem.setAttribute("d", getOutput(shape)); + } + return elem; + } + + private Element getElement(String text, double x, double y) { + Element elem = doc.createElement("text"); + elem.appendChild(doc.createTextNode(text)); + elem.setAttribute("x", DataUtils.format(x)); + elem.setAttribute("y", DataUtils.format(y)); + return elem; + } + + private Element getElement(Image image, double x, double y, double width, double height) { + Element elem = doc.createElement("image"); + elem.setAttribute("x", DataUtils.format(x)); + elem.setAttribute("y", DataUtils.format(y)); + elem.setAttribute("width", DataUtils.format(width)); + elem.setAttribute("height", DataUtils.format(height)); + elem.setAttribute("preserveAspectRatio", "none"); + boolean lossyAllowed = getCurrentState().getHints().get(VectorHints.KEY_EXPORT) == + VectorHints.VALUE_EXPORT_SIZE; + elem.setAttribute("xlink:href", getOutput(image, lossyAllowed)); + return elem; + } +} + diff --git a/io-vector/src/main/java/org/xbib/graphics/io/vector/util/ASCII85EncodeStream.java b/io-vector/src/main/java/org/xbib/graphics/io/vector/util/ASCII85EncodeStream.java new file mode 100644 index 0000000..7bb0976 --- /dev/null +++ b/io-vector/src/main/java/org/xbib/graphics/io/vector/util/ASCII85EncodeStream.java @@ -0,0 +1,102 @@ +package org.xbib.graphics.io.vector.util; + +import java.io.FilterOutputStream; +import java.io.IOException; +import java.io.OutputStream; +import java.nio.charset.Charset; +import java.util.Arrays; + +public class ASCII85EncodeStream extends FilterOutputStream { + private static final Charset ISO88591 = Charset.forName("ISO-8859-1"); + private static final int BASE = 85; + private static final int[] POW_85 = + {BASE * BASE * BASE * BASE, BASE * BASE * BASE, BASE * BASE, BASE, 1}; + private static final char[] CHAR_MAP = + "!\"#$%&\'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\\]^_`abcdefghijklmnopqrstu" + .toCharArray(); + private final byte[] data; + private final byte[] prefixBytes; + private final byte[] suffixBytes; + private final byte[] encoded; + private boolean closed; + private int dataSize; + private boolean prefixDone; + + public ASCII85EncodeStream(OutputStream out, String prefix, String suffix) { + super(out); + prefixBytes = (prefix != null ? prefix : "").getBytes(ISO88591); + suffixBytes = (suffix != null ? suffix : "").getBytes(ISO88591); + data = new byte[4]; + encoded = new byte[5]; + } + + public ASCII85EncodeStream(OutputStream out) { + this(out, "", "~>"); + } + + private static long toUInt32(byte[] bytes, int size) { + long uint32 = 0L; + for (int i = 0; i < 4 && i < size; i++) { + uint32 |= (bytes[i] & 0xff) << (3 - i) * 8; + } + return toUnsignedInt(uint32); + } + + private static long toUnsignedInt(long x) { + return x & 0x00000000ffffffffL; + } + + @Override + public void write(int b) throws IOException { + if (closed) { + return; + } + if (!prefixDone) { + out.write(prefixBytes); + prefixDone = true; + } + if (dataSize == data.length) { + writeChunk(); + dataSize = 0; + } + data[dataSize++] = (byte) (b & 0xff); + } + + private void writeChunk() throws IOException { + if (dataSize == 0) { + return; + } + long uint32 = toUInt32(data, dataSize); + int padByteCount = data.length - dataSize; + int encodedSize = encodeChunk(uint32, padByteCount); + out.write(encoded, 0, encodedSize); + } + + private int encodeChunk(long uint32, int padByteCount) { + Arrays.fill(encoded, (byte) 0); + if (uint32 == 0L && padByteCount == 0) { + encoded[0] = 'z'; + return 1; + } + int size = encoded.length - padByteCount; + for (int i = 0; i < size; i++) { + encoded[i] = (byte) CHAR_MAP[(int) (uint32 / POW_85[i] % BASE)]; + } + return size; + } + + @Override + public void close() throws IOException { + if (closed) { + return; + } + + writeChunk(); + + out.write(suffixBytes); + + super.close(); + closed = true; + } +} + diff --git a/io-vector/src/main/java/org/xbib/graphics/io/vector/util/AlphaToMaskOp.java b/io-vector/src/main/java/org/xbib/graphics/io/vector/util/AlphaToMaskOp.java new file mode 100644 index 0000000..98f4979 --- /dev/null +++ b/io-vector/src/main/java/org/xbib/graphics/io/vector/util/AlphaToMaskOp.java @@ -0,0 +1,101 @@ +package org.xbib.graphics.io.vector.util; + +import java.awt.RenderingHints; +import java.awt.geom.Point2D; +import java.awt.geom.Rectangle2D; +import java.awt.image.BufferedImage; +import java.awt.image.BufferedImageOp; +import java.awt.image.ColorModel; +import java.awt.image.Raster; +import java.awt.image.WritableRaster; +import java.util.Hashtable; + +public class AlphaToMaskOp implements BufferedImageOp { + private final boolean inverted; + + public AlphaToMaskOp(boolean inverted) { + this.inverted = inverted; + } + + public AlphaToMaskOp() { + this(false); + } + + public boolean isInverted() { + return inverted; + } + + public BufferedImage filter(BufferedImage src, BufferedImage dest) { + ColorModel cm = src.getColorModel(); + + if (dest == null) { + dest = createCompatibleDestImage(src, cm); + } else if (dest.getWidth() != src.getWidth() || dest.getHeight() != src.getHeight()) { + throw new IllegalArgumentException("Source and destination images have different dimensions."); + } else if (dest.getColorModel() != cm) { + throw new IllegalArgumentException("Color models don't match."); + } + + if (cm.hasAlpha()) { + Raster srcRaster = src.getRaster(); + WritableRaster destRaster = dest.getRaster(); + + for (int y = 0; y < srcRaster.getHeight(); y++) { + for (int x = 0; x < srcRaster.getWidth(); x++) { + int argb = cm.getRGB(srcRaster.getDataElements(x, y, null)); + int alpha = argb >>> 24; + if (alpha >= 127 && !isInverted() || alpha < 127 && isInverted()) { + argb |= 0xff000000; + } else { + argb &= 0x00ffffff; + } + destRaster.setDataElements(x, y, cm.getDataElements(argb, null)); + } + } + } + + return dest; + } + + public Rectangle2D getBounds2D(BufferedImage src) { + Rectangle2D bounds = new Rectangle2D.Double(); + bounds.setRect(src.getRaster().getBounds()); + return bounds; + } + + public BufferedImage createCompatibleDestImage(BufferedImage src, + ColorModel destCM) { + if (destCM == null) { + destCM = src.getColorModel(); + } + WritableRaster raster = destCM.createCompatibleWritableRaster( + src.getWidth(), src.getHeight()); + boolean isRasterPremultiplied = destCM.isAlphaPremultiplied(); + Hashtable properties = null; + if (src.getPropertyNames() != null) { + properties = new Hashtable(); + for (String key : src.getPropertyNames()) { + properties.put(key, src.getProperty(key)); + } + } + + BufferedImage bimage = new BufferedImage(destCM, raster, + isRasterPremultiplied, properties); + src.copyData(raster); + return bimage; + } + + public Point2D getPoint2D(Point2D srcPt, Point2D dstPt) { + if (dstPt == null) { + dstPt = new Point2D.Double(); + } + dstPt.setLocation(srcPt); + return dstPt; + } + + public RenderingHints getRenderingHints() { + return null; + } + +} + diff --git a/io-vector/src/main/java/org/xbib/graphics/io/vector/util/Base64EncodeStream.java b/io-vector/src/main/java/org/xbib/graphics/io/vector/util/Base64EncodeStream.java new file mode 100644 index 0000000..4c4ae4e --- /dev/null +++ b/io-vector/src/main/java/org/xbib/graphics/io/vector/util/Base64EncodeStream.java @@ -0,0 +1,83 @@ +package org.xbib.graphics.io.vector.util; + +import java.io.FilterOutputStream; +import java.io.IOException; +import java.io.OutputStream; +import java.util.Arrays; + +public class Base64EncodeStream extends FilterOutputStream { + private static final int BASE = 64; + private static final int[] POW_64 = + {BASE * BASE * BASE, BASE * BASE, BASE, 1}; + private static final char[] CHAR_MAP = + "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/" + .toCharArray(); + private final byte[] data; + private final byte[] encoded; + private boolean closed; + private int dataSize; + + public Base64EncodeStream(OutputStream out) { + super(out); + data = new byte[3]; + encoded = new byte[4]; + } + + private static long toUInt32(byte[] bytes, int size) { + long uint32 = 0L; + int offset = (3 - size) * 8; + for (int i = size - 1; i >= 0; i--) { + uint32 |= (bytes[i] & 0xff) << offset; + offset += 8; + } + return toUnsignedInt(uint32); + } + + private static long toUnsignedInt(long x) { + return x & 0x00000000ffffffffL; + } + + @Override + public void write(int b) throws IOException { + if (closed) { + return; + } + if (dataSize == data.length) { + writeChunk(); + dataSize = 0; + } + data[dataSize++] = (byte) (b & 0xff); + } + + private void writeChunk() throws IOException { + if (dataSize == 0) { + return; + } + long uint32 = toUInt32(data, dataSize); + int padByteCount = data.length - dataSize; + int encodedSize = encodeChunk(uint32, padByteCount); + out.write(encoded, 0, encodedSize); + } + + private int encodeChunk(long uint32, int padByteCount) { + Arrays.fill(encoded, (byte) '='); + int size = encoded.length - padByteCount; + for (int i = 0; i < size; i++) { + encoded[i] = (byte) CHAR_MAP[(int) (uint32 / POW_64[i] % BASE)]; + } + return encoded.length; + } + + @Override + public void close() throws IOException { + if (closed) { + return; + } + + writeChunk(); + + super.close(); + closed = true; + } +} + diff --git a/io-vector/src/main/java/org/xbib/graphics/io/vector/util/DataUtils.java b/io-vector/src/main/java/org/xbib/graphics/io/vector/util/DataUtils.java new file mode 100644 index 0000000..357aa01 --- /dev/null +++ b/io-vector/src/main/java/org/xbib/graphics/io/vector/util/DataUtils.java @@ -0,0 +1,240 @@ +package org.xbib.graphics.io.vector.util; + +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.LinkedHashMap; +import java.util.List; +import java.util.Map; +import java.util.regex.Pattern; + +/** + * Abstract class that contains utility functions for working with data + * collections like maps or lists. + */ +public abstract class DataUtils { + /** + * Default constructor that prevents creation of class. + */ + protected DataUtils() { + throw new UnsupportedOperationException(); + } + + /** + * Creates a mapping from two arrays, one with keys, one with values. + * + * @param Data type of the keys. + * @param Data type of the values. + * @param keys Array containing the keys. + * @param values Array containing the values. + * @return Map with keys and values from the specified arrays. + */ + public static Map map(K[] keys, V[] values) { + // Check for valid parameters + if (keys.length != values.length) { + throw new IllegalArgumentException( + "Cannot create a Map: " + + "The number of keys and values differs."); + } + // Fill map with keys and values + Map map = new LinkedHashMap(keys.length); + for (int i = 0; i < keys.length; i++) { + K key = keys[i]; + V value = values[i]; + map.put(key, value); + } + return map; + } + + /** + * Returns a string containing all elements concatenated by a specified + * separator. + * + * @param separator Separator string. + * @param elements List of elements that should be concatenated. + * @return a concatenated string. + */ + public static String join(String separator, List elements) { + if (elements == null || elements.size() == 0) { + return ""; + } + StringBuilder sb = new StringBuilder(elements.size() * 3); + int i = 0; + for (Object elem : elements) { + if (separator.length() > 0 && i++ > 0) { + sb.append(separator); + } + sb.append(format(elem)); + } + return sb.toString(); + } + + /** + * Returns a string containing all elements concatenated by a specified + * separator. + * + * @param separator Separator string. + * @param elements Array of elements that should be concatenated. + * @return a concatenated string. + */ + public static String join(String separator, Object[] elements) { + if (elements == null || elements.length == 0) { + return ""; + } + return join(separator, Arrays.asList(elements)); + } + + /** + * Returns a string containing all double numbers concatenated by a + * specified separator. + * + * @param separator Separator string. + * @param elements Array of double numbers that should be concatenated. + * @return a concatenated string. + */ + public static String join(String separator, double[] elements) { + if (elements == null || elements.length == 0) { + return ""; + } + List list = new ArrayList(elements.length); + for (Double element : elements) { + list.add(element); + } + return join(separator, list); + } + + /** + * Returns a string containing all float numbers concatenated by a + * specified separator. + * + * @param separator Separator string. + * @param elements Array of float numbers that should be concatenated. + * @return a concatenated string. + */ + public static String join(String separator, float[] elements) { + if (elements == null || elements.length == 0) { + return ""; + } + List list = new ArrayList(elements.length); + for (Float element : elements) { + list.add(element); + } + return join(separator, list); + } + + /** + * Returns the largest of all specified values. + * + * @param values Several integer values. + * @return largest value. + */ + public static int max(int... values) { + int max = values[0]; + for (int i = 1; i < values.length; i++) { + if (values[i] > max) { + max = values[i]; + } + } + return max; + } + + /** + * Copies data from an input stream to an output stream using a buffer of + * specified size. + * + * @param in Input stream. + * @param out Output stream. + * @param bufferSize Size of the copy buffer. + * @throws IOException when an error occurs while copying. + */ + public static void transfer(InputStream in, OutputStream out, int bufferSize) + throws IOException { + byte[] buffer = new byte[bufferSize]; + int bytesRead; + while ((bytesRead = in.read(buffer)) != -1) { + out.write(buffer, 0, bytesRead); + } + } + + /** + * Returns a formatted string of the specified number. All trailing zeroes + * or decimal points will be stripped. + * + * @param number Number to convert to a string. + * @return A formatted string. + */ + public static String format(Number number) { + String formatted; + if (number instanceof Double || number instanceof Float) { + formatted = Double.toString(number.doubleValue()) + .replaceAll("\\.0+$", "") + .replaceAll("(\\.[0-9]*[1-9])0+$", "$1"); + } else { + formatted = number.toString(); + } + return formatted; + } + + /** + * Returns a formatted string of the specified object. + * + * @param obj Object to convert to a string. + * @return A formatted string. + */ + public static String format(Object obj) { + if (obj instanceof Number) { + return format((Number) obj); + } else { + return obj.toString(); + } + } + + /** + * Converts an array of {@code float} numbers to a list of {@code Float}s. + * The list will be empty if the array is empty or {@code null}. + * + * @param elements Array of float numbers. + * @return A list with all numbers as {@code Float}. + */ + public static List asList(float[] elements) { + int size = (elements != null) ? elements.length : 0; + List list = new ArrayList<>(size); + if (elements != null) { + for (Float elem : elements) { + list.add(elem); + } + } + return list; + } + + /** + * Converts an array of {@code double} numbers to a list of {@code Double}s. + * The list will be empty if the array is empty or {@code null}. + * + * @param elements Array of double numbers. + * @return A list with all numbers as {@code Double}. + */ + public static List asList(double[] elements) { + int size = (elements != null) ? elements.length : 0; + List list = new ArrayList<>(size); + if (elements != null) { + for (Double elem : elements) { + list.add(elem); + } + } + return list; + } + + /** + * Removes the specified trailing pattern from a string. + * + * @param s string. + * @param substr trailing pattern. + * @return A string without the trailing pattern. + */ + public static String stripTrailing(String s, String substr) { + return s.replaceAll("(" + Pattern.quote(substr) + ")+$", ""); + } +} diff --git a/io-vector/src/main/java/org/xbib/graphics/io/vector/util/FlateEncodeStream.java b/io-vector/src/main/java/org/xbib/graphics/io/vector/util/FlateEncodeStream.java new file mode 100644 index 0000000..7877982 --- /dev/null +++ b/io-vector/src/main/java/org/xbib/graphics/io/vector/util/FlateEncodeStream.java @@ -0,0 +1,11 @@ +package org.xbib.graphics.io.vector.util; + +import java.io.OutputStream; +import java.util.zip.DeflaterOutputStream; + +public class FlateEncodeStream extends DeflaterOutputStream { + public FlateEncodeStream(OutputStream out) { + super(out); + } +} + diff --git a/io-vector/src/main/java/org/xbib/graphics/io/vector/util/FormattingWriter.java b/io-vector/src/main/java/org/xbib/graphics/io/vector/util/FormattingWriter.java new file mode 100644 index 0000000..5ca14d0 --- /dev/null +++ b/io-vector/src/main/java/org/xbib/graphics/io/vector/util/FormattingWriter.java @@ -0,0 +1,66 @@ +package org.xbib.graphics.io.vector.util; + +import java.io.Closeable; +import java.io.Flushable; +import java.io.IOException; +import java.io.OutputStream; + +public class FormattingWriter implements Closeable, Flushable { + private final OutputStream out; + private final String encoding; + private final String eolString; + private long position; + + public FormattingWriter(OutputStream out, String encoding, String eol) { + this.out = out; + this.encoding = encoding; + this.eolString = eol; + } + + public FormattingWriter write(String string) throws IOException { + byte[] bytes = string.getBytes(encoding); + out.write(bytes, 0, bytes.length); + position += bytes.length; + return this; + } + + public FormattingWriter write(Number number) throws IOException { + write(DataUtils.format(number)); + return this; + } + + public FormattingWriter writeln() throws IOException { + write(eolString); + return this; + } + + public FormattingWriter writeln(String string) throws IOException { + write(string); + write(eolString); + return this; + } + + public FormattingWriter writeln(Number number) throws IOException { + write(number); + write(eolString); + return this; + } + + public FormattingWriter format(String format, Object... args) throws IOException { + write(String.format(null, format, args)); + return this; + } + + public void flush() throws IOException { + out.flush(); + } + + public void close() throws IOException { + out.close(); + } + + public long tell() { + return position; + } +} + diff --git a/io-vector/src/main/java/org/xbib/graphics/io/vector/util/GraphicsUtils.java b/io-vector/src/main/java/org/xbib/graphics/io/vector/util/GraphicsUtils.java new file mode 100644 index 0000000..35a0e67 --- /dev/null +++ b/io-vector/src/main/java/org/xbib/graphics/io/vector/util/GraphicsUtils.java @@ -0,0 +1,411 @@ +package org.xbib.graphics.io.vector.util; + +import java.awt.Font; +import java.awt.Graphics; +import java.awt.GraphicsConfiguration; +import java.awt.GraphicsDevice; +import java.awt.GraphicsEnvironment; +import java.awt.HeadlessException; +import java.awt.Image; +import java.awt.Polygon; +import java.awt.Rectangle; +import java.awt.Shape; +import java.awt.Transparency; +import java.awt.color.ColorSpace; +import java.awt.font.FontRenderContext; +import java.awt.font.TextLayout; +import java.awt.geom.Arc2D; +import java.awt.geom.CubicCurve2D; +import java.awt.geom.Ellipse2D; +import java.awt.geom.Line2D; +import java.awt.geom.Path2D; +import java.awt.geom.PathIterator; +import java.awt.geom.QuadCurve2D; +import java.awt.geom.Rectangle2D; +import java.awt.geom.RoundRectangle2D; +import java.awt.image.BufferedImage; +import java.awt.image.ColorModel; +import java.awt.image.ComponentColorModel; +import java.awt.image.DataBuffer; +import java.awt.image.PixelGrabber; +import java.awt.image.Raster; +import java.awt.image.RenderedImage; +import java.awt.image.WritableRaster; +import java.util.Comparator; +import java.util.HashSet; +import java.util.Hashtable; +import java.util.PriorityQueue; +import java.util.Queue; +import java.util.Set; +import javax.swing.ImageIcon; + +/** + * Abstract class that contains utility functions for working with graphics. + * For example, this includes font handling. + */ +public abstract class GraphicsUtils { + private static final FontRenderContext FONT_RENDER_CONTEXT = + new FontRenderContext(null, false, true); + private static final String FONT_TEST_STRING = + "Falsches Üben von Xylophonmusik quält jeden größeren Zwerg"; + private static final FontExpressivenessComparator FONT_EXPRESSIVENESS_COMPARATOR = + new FontExpressivenessComparator(); + + /** + * Default constructor that prevents creation of class. + */ + protected GraphicsUtils() { + throw new UnsupportedOperationException(); + } + + /** + * This method returns {@code true} if the specified image has the + * possibility to store transparent pixels. + * Inspired by http://www.exampledepot.com/egs/java.awt.image/HasAlpha.html + * + * @param image Image that should be checked for alpha channel. + * @return {@code true} if the specified image can have transparent pixels, + * {@code false} otherwise + */ + public static boolean hasAlpha(Image image) { + ColorModel cm; + // If buffered image, the color model is readily available + if (image instanceof BufferedImage) { + BufferedImage bimage = (BufferedImage) image; + cm = bimage.getColorModel(); + } else { + // Use a pixel grabber to retrieve the image's color model; + // grabbing a single pixel is usually sufficient + PixelGrabber pg = new PixelGrabber(image, 0, 0, 1, 1, false); + try { + pg.grabPixels(); + } catch (InterruptedException e) { + return false; + } + // Get the image's color model + cm = pg.getColorModel(); + } + return cm.hasAlpha(); + } + + /** + * This method returns {@code true} if the specified image has at least one + * pixel that is not fully opaque. + * + * @param image Image that should be checked for non-opaque pixels. + * @return {@code true} if the specified image has transparent pixels, + * {@code false} otherwise + */ + public static boolean usesAlpha(Image image) { + if (image == null) { + return false; + } + BufferedImage bimage = toBufferedImage(image); + Raster alphaRaster = bimage.getAlphaRaster(); + if (alphaRaster == null) { + return false; + } + DataBuffer dataBuffer = alphaRaster.getDataBuffer(); + for (int i = 0; i < dataBuffer.getSize(); i++) { + int alpha = dataBuffer.getElem(i); + if (alpha < 255) { + return true; + } + } + return false; + } + + /** + * Converts an arbitrary image to a {@code BufferedImage}. + * + * @param image Image that should be converted. + * @return a buffered image containing the image pixels, or the original + * instance if the image already was of type {@code BufferedImage}. + */ + public static BufferedImage toBufferedImage(RenderedImage image) { + if (image instanceof BufferedImage) { + return (BufferedImage) image; + } + + ColorModel cm = image.getColorModel(); + WritableRaster raster = cm.createCompatibleWritableRaster( + image.getWidth(), image.getHeight()); + boolean isRasterPremultiplied = cm.isAlphaPremultiplied(); + Hashtable properties = null; + if (image.getPropertyNames() != null) { + properties = new Hashtable(); + for (String key : image.getPropertyNames()) { + properties.put(key, image.getProperty(key)); + } + } + + BufferedImage bimage = new BufferedImage(cm, raster, + isRasterPremultiplied, properties); + image.copyData(raster); + return bimage; + } + + /** + * This method returns a buffered image with the contents of an image. + * Taken from http://www.exampledepot.com/egs/java.awt.image/Image2Buf.html + * + * @param image Image to be converted + * @return a buffered image with the contents of the specified image + */ + public static BufferedImage toBufferedImage(Image image) { + if (image instanceof BufferedImage) { + return (BufferedImage) image; + } + // This code ensures that all the pixels in the image are loaded + image = new ImageIcon(image).getImage(); + // Determine if the image has transparent pixels + boolean hasAlpha = hasAlpha(image); + + // Create a buffered image with a format that's compatible with the + // screen + BufferedImage bimage; + GraphicsEnvironment ge = GraphicsEnvironment + .getLocalGraphicsEnvironment(); + try { + // Determine the type of transparency of the new buffered image + int transparency = Transparency.OPAQUE; + if (hasAlpha) { + transparency = Transparency.TRANSLUCENT; + } + // Create the buffered image + GraphicsDevice gs = ge.getDefaultScreenDevice(); + GraphicsConfiguration gc = gs.getDefaultConfiguration(); + bimage = gc.createCompatibleImage( + image.getWidth(null), image.getHeight(null), transparency); + } catch (HeadlessException e) { + // The system does not have a screen + bimage = null; + } + if (bimage == null) { + // Create a buffered image using the default color model + int type = BufferedImage.TYPE_INT_RGB; + if (hasAlpha) { + type = BufferedImage.TYPE_INT_ARGB; + } + bimage = new BufferedImage( + image.getWidth(null), image.getHeight(null), type); + } + // Copy image to buffered image + Graphics g = bimage.createGraphics(); + // Paint the image onto the buffered image + g.drawImage(image, 0, 0, null); + g.dispose(); + return bimage; + } + + public static Shape clone(Shape shape) { + if (shape == null) { + return null; + } + Shape clone; + if (shape instanceof Line2D) { + clone = (shape instanceof Line2D.Float) ? + new Line2D.Float() : new Line2D.Double(); + ((Line2D) clone).setLine((Line2D) shape); + } else if (shape instanceof Rectangle) { + clone = new Rectangle((Rectangle) shape); + } else if (shape instanceof Rectangle2D) { + clone = (shape instanceof Rectangle2D.Float) ? + new Rectangle2D.Float() : new Rectangle2D.Double(); + ((Rectangle2D) clone).setRect((Rectangle2D) shape); + } else if (shape instanceof RoundRectangle2D) { + clone = (shape instanceof RoundRectangle2D.Float) ? + new RoundRectangle2D.Float() : new RoundRectangle2D.Double(); + ((RoundRectangle2D) clone).setRoundRect((RoundRectangle2D) shape); + } else if (shape instanceof Ellipse2D) { + clone = (shape instanceof Ellipse2D.Float) ? + new Ellipse2D.Float() : new Ellipse2D.Double(); + ((Ellipse2D) clone).setFrame(((Ellipse2D) shape).getFrame()); + } else if (shape instanceof Arc2D) { + clone = (shape instanceof Arc2D.Float) ? + new Arc2D.Float() : new Arc2D.Double(); + ((Arc2D) clone).setArc((Arc2D) shape); + } else if (shape instanceof Polygon) { + Polygon p = (Polygon) shape; + clone = new Polygon(p.xpoints, p.ypoints, p.npoints); + } else if (shape instanceof CubicCurve2D) { + clone = (shape instanceof CubicCurve2D.Float) ? + new CubicCurve2D.Float() : new CubicCurve2D.Double(); + ((CubicCurve2D) clone).setCurve((CubicCurve2D) shape); + } else if (shape instanceof QuadCurve2D) { + clone = (shape instanceof QuadCurve2D.Float) ? + new QuadCurve2D.Float() : new QuadCurve2D.Double(); + ((QuadCurve2D) clone).setCurve((QuadCurve2D) shape); + } else if (shape instanceof Path2D.Float) { + clone = new Path2D.Float(shape); + } else { + clone = new Path2D.Double(shape); + } + return clone; + } + + private static boolean isLogicalFontFamily(String family) { + return (Font.DIALOG.equals(family) || + Font.DIALOG_INPUT.equals(family) || + Font.SANS_SERIF.equals(family) || + Font.SERIF.equals(family) || + Font.MONOSPACED.equals(family)); + } + + /** + * Try to guess physical font from the properties of a logical font, like + * "Dialog", "Serif", "Monospaced" etc. + * + * @param logicalFont Logical font object. + * @param testText Text used to determine font properties. + * @return An object of the first matching physical font. The original font + * object is returned if it was a physical font or no font matched. + */ + public static Font getPhysicalFont(Font logicalFont, String testText) { + String logicalFamily = logicalFont.getFamily(); + if (!isLogicalFontFamily(logicalFamily)) { + return logicalFont; + } + + final TextLayout logicalLayout = + new TextLayout(testText, logicalFont, FONT_RENDER_CONTEXT); + + // Create a list of matches sorted by font expressiveness (in descending order) + Queue physicalFonts = + new PriorityQueue(1, FONT_EXPRESSIVENESS_COMPARATOR); + + Font[] allPhysicalFonts = GraphicsEnvironment.getLocalGraphicsEnvironment().getAllFonts(); + for (Font physicalFont : allPhysicalFonts) { + String physicalFamily = physicalFont.getFamily(); + // Skip logical fonts + if (isLogicalFontFamily(physicalFamily)) { + continue; + } + + // Derive identical variant of physical font + physicalFont = physicalFont.deriveFont( + logicalFont.getStyle(), logicalFont.getSize2D()); + TextLayout physicalLayout = + new TextLayout(testText, physicalFont, FONT_RENDER_CONTEXT); + + // Compare various properties of physical and logical font + if (physicalLayout.getBounds().equals(logicalLayout.getBounds()) && + physicalLayout.getAscent() == logicalLayout.getAscent() && + physicalLayout.getDescent() == logicalLayout.getDescent() && + physicalLayout.getLeading() == logicalLayout.getLeading() && + physicalLayout.getAdvance() == logicalLayout.getAdvance() && + physicalLayout.getVisibleAdvance() == logicalLayout.getVisibleAdvance()) { + // Store matching font in list + physicalFonts.add(physicalFont); + } + } + + // Return a valid font even when no matching font could be found + if (physicalFonts.isEmpty()) { + return logicalFont; + } + + return physicalFonts.poll(); + } + + public static Font getPhysicalFont(Font logicalFont) { + return getPhysicalFont(logicalFont, FONT_TEST_STRING); + } + + public static BufferedImage getAlphaImage(BufferedImage image) { + WritableRaster alphaRaster = image.getAlphaRaster(); + int width = image.getWidth(); + int height = image.getHeight(); + + ColorModel cm; + WritableRaster raster; + // TODO Handle bitmap masks (work on ImageDataStream is necessary) + /* + if (image.getTransparency() == BufferedImage.BITMASK) { + byte[] arr = {(byte) 0, (byte) 255}; + + cm = new IndexColorModel(1, 2, arr, arr, arr); + raster = Raster.createPackedRaster(DataBuffer.TYPE_BYTE, + width, height, 1, 1, null); + } else {*/ + ColorSpace colorSpace = ColorSpace.getInstance(ColorSpace.CS_GRAY); + int[] bits = {8}; + cm = new ComponentColorModel(colorSpace, bits, false, true, + Transparency.OPAQUE, DataBuffer.TYPE_BYTE); + raster = cm.createCompatibleWritableRaster(width, height); + //} + + BufferedImage alphaImage = new BufferedImage(cm, raster, false, null); + + int[] alphaValues = new int[image.getWidth() * alphaRaster.getNumBands()]; + for (int y = 0; y < image.getHeight(); y++) { + alphaRaster.getPixels(0, y, image.getWidth(), 1, alphaValues); + // FIXME Don't force 8-bit alpha channel (see TODO above) + if (image.getTransparency() == BufferedImage.BITMASK) { + for (int i = 0; i < alphaValues.length; i++) { + if (alphaValues[i] > 0) { + alphaValues[i] = 255; + } + } + } + alphaImage.getRaster().setPixels(0, y, image.getWidth(), 1, alphaValues); + } + + return alphaImage; + } + + public static boolean equals(Shape shapeA, Shape shapeB) { + PathIterator pathAIterator = shapeA.getPathIterator(null); + PathIterator pathBIterator = shapeB.getPathIterator(null); + + if (pathAIterator.getWindingRule() != pathBIterator.getWindingRule()) { + return false; + } + double[] pathASegment = new double[6]; + double[] pathBSegment = new double[6]; + while (!pathAIterator.isDone()) { + int pathASegmentType = pathAIterator.currentSegment(pathASegment); + int pathBSegmentType = pathBIterator.currentSegment(pathBSegment); + if (pathASegmentType != pathBSegmentType) { + return false; + } + for (int segmentIndex = 0; segmentIndex < pathASegment.length; segmentIndex++) { + if (pathASegment[segmentIndex] != pathBSegment[segmentIndex]) { + return false; + } + } + + pathAIterator.next(); + pathBIterator.next(); + } + // When the iterator of shapeA is done and shapeA equals shapeB, the iterator of shapeB must also be done + if (!pathBIterator.isDone()) { + return false; + } + return true; + } + + private static class FontExpressivenessComparator implements Comparator { + private static final int[] STYLES = { + Font.PLAIN, Font.ITALIC, Font.BOLD, Font.BOLD | Font.ITALIC + }; + + public int compare(Font font1, Font font2) { + if (font1 == font2) { + return 0; + } + Set variantNames1 = new HashSet(); + Set variantNames2 = new HashSet(); + for (int style : STYLES) { + variantNames1.add(font1.deriveFont(style).getPSName()); + variantNames2.add(font2.deriveFont(style).getPSName()); + } + if (variantNames1.size() < variantNames2.size()) { + return 1; + } else if (variantNames1.size() > variantNames2.size()) { + return -1; + } + return font1.getName().compareTo(font2.getName()); + } + } +} diff --git a/io-vector/src/main/java/org/xbib/graphics/io/vector/util/ImageDataStream.java b/io-vector/src/main/java/org/xbib/graphics/io/vector/util/ImageDataStream.java new file mode 100644 index 0000000..6e98d56 --- /dev/null +++ b/io-vector/src/main/java/org/xbib/graphics/io/vector/util/ImageDataStream.java @@ -0,0 +1,131 @@ +package org.xbib.graphics.io.vector.util; + +import java.awt.image.BufferedImage; +import java.awt.image.Raster; +import java.io.IOException; +import java.io.InputStream; +import java.util.LinkedList; +import java.util.Queue; + +public class ImageDataStream extends InputStream { + private final BufferedImage image; + private final int width; + private final int height; + private final Interleaving interleaving; + private final Raster raster; + private final boolean opaque; + private final Queue byteBuffer; + private final int[] sampleValues; + private final int[] sampleSizes; + private int x; + private int y; + + public ImageDataStream(BufferedImage image, Interleaving interleaving) { + this.image = image; + this.interleaving = interleaving; + + width = image.getWidth(); + height = image.getHeight(); + x = -1; + y = 0; + + Raster alphaRaster = image.getAlphaRaster(); + if (interleaving == Interleaving.ALPHA_ONLY) { + raster = alphaRaster; + } else { + raster = image.getRaster(); + } + opaque = alphaRaster == null; + + byteBuffer = new LinkedList(); + sampleValues = new int[raster.getNumBands()]; + sampleSizes = raster.getSampleModel().getSampleSize(); + } + + public BufferedImage getImage() { + return image; + } + + public Interleaving getInterleaving() { + return interleaving; + } + + @Override + public int read() throws IOException { + if (!byteBuffer.isEmpty()) { + return byteBuffer.poll(); + } else { + if (!nextSample()) { + return -1; + } + int bands = sampleValues.length; + if (interleaving == Interleaving.WITHOUT_ALPHA || + interleaving == Interleaving.ALPHA_ONLY) { + if (interleaving == Interleaving.WITHOUT_ALPHA && !opaque) { + // Ignore alpha band + bands--; + } + for (int band = 0; band < bands; band++) { + bufferSampleValue(band); + } + } else { + if (opaque) { + for (int band = 0; band < bands; band++) { + bufferSampleValue(band); + } + } else { + for (int band = 0; band < bands; band++) { + // Fix order to be ARGB instead of RGBA + if (band == 0) { + bufferSampleValue(bands - 1); + } else { + bufferSampleValue(band - 1); + } + } + } + } + if (!byteBuffer.isEmpty()) { + return byteBuffer.poll(); + } else { + return -1; + } + } + } + + private void bufferSampleValue(int band) { + if (sampleSizes[band] < 8) { + int byteValue = sampleValues[band] & 0xFF; + byteBuffer.offer(byteValue); + } else { + int byteCount = sampleSizes[band] / 8; + for (int i = byteCount - 1; i >= 0; i--) { + int byteValue = (sampleValues[band] >> i * 8) & 0xFF; + byteBuffer.offer(byteValue); + } + } + } + + private boolean nextSample() { + if (interleaving == Interleaving.SAMPLE || interleaving == Interleaving.WITHOUT_ALPHA) { + x++; + if (x >= width) { + x = 0; + y++; + } + } + if (x < 0 || x >= width || y < 0 || y >= height) { + return false; + } else { + raster.getPixel(x, y, sampleValues); + return true; + } + } + + public enum Interleaving { + SAMPLE, + ROW, + WITHOUT_ALPHA, + ALPHA_ONLY + } +} + diff --git a/io-vector/src/main/java/org/xbib/graphics/io/vector/util/LineWrapOutputStream.java b/io-vector/src/main/java/org/xbib/graphics/io/vector/util/LineWrapOutputStream.java new file mode 100644 index 0000000..2518e5d --- /dev/null +++ b/io-vector/src/main/java/org/xbib/graphics/io/vector/util/LineWrapOutputStream.java @@ -0,0 +1,40 @@ +package org.xbib.graphics.io.vector.util; + +import java.io.FilterOutputStream; +import java.io.IOException; +import java.io.OutputStream; + +public class LineWrapOutputStream extends FilterOutputStream { + + public static final String STANDARD_EOL = "\r\n"; + + private final int lineWidth; + + private final byte[] eolBytes; + + private int written; + + public LineWrapOutputStream(OutputStream sink, int lineWidth, String eol) { + super(sink); + this.lineWidth = lineWidth; + this.eolBytes = eol.getBytes(); + if (lineWidth <= 0) { + throw new IllegalArgumentException("Width must be at least 0."); + } + } + + public LineWrapOutputStream(OutputStream sink, int lineWidth) { + this(sink, lineWidth, STANDARD_EOL); + } + + @Override + public void write(int b) throws IOException { + if (written == lineWidth) { + out.write(eolBytes); + written = 0; + } + out.write(b); + written++; + } +} + diff --git a/io-vector/src/main/java/org/xbib/graphics/io/vector/util/VectorHints.java b/io-vector/src/main/java/org/xbib/graphics/io/vector/util/VectorHints.java new file mode 100644 index 0000000..6792789 --- /dev/null +++ b/io-vector/src/main/java/org/xbib/graphics/io/vector/util/VectorHints.java @@ -0,0 +1,83 @@ +package org.xbib.graphics.io.vector.util; + +import java.awt.RenderingHints; +import java.util.HashSet; +import java.util.Set; + +public abstract class VectorHints { + public static final Key KEY_EXPORT = new Key(0, "Vector export mode"); + public static final Object VALUE_EXPORT_READABILITY = new Value(KEY_EXPORT, 0, "Maximize readability for humans"); + public static final Object VALUE_EXPORT_QUALITY = new Value(KEY_EXPORT, 1, "Maximize render quality"); + public static final Object VALUE_EXPORT_SIZE = new Value(KEY_EXPORT, 2, "Minimize data size"); + public static final Key KEY_TEXT = new Key(1, "Text export mode"); + public static final Object VALUE_TEXT_DEFAULT = new Value(KEY_TEXT, 0, "Keep text"); + public static final Object VALUE_TEXT_VECTOR = new Value(KEY_TEXT, 1, "Convert text to vector shapes"); + + protected VectorHints() { + throw new UnsupportedOperationException(); + } + + public static class Key extends RenderingHints.Key { + private final String description; + + public Key(int privateKey, String description) { + super(privateKey); + this.description = description; + } + + public int getIndex() { + return intKey(); + } + + @Override + public boolean isCompatibleValue(Object val) { + return val instanceof Value && ((Value) val).isCompatibleKey(this); + } + + @Override + public String toString() { + return description; + } + } + + public static class Value { + private static final Set values = new HashSet(); + private final Key key; + private final int index; + private final String description; + + public Value(Key key, int index, String description) { + this.key = key; + this.index = index; + this.description = description; + register(this); + } + + private synchronized static void register(Value value) { + String id = value.getId(); + if (values.contains(id)) { + throw new ExceptionInInitializerError( + "Duplicate index: " + value.getIndex()); + } + values.add(id); + } + + public boolean isCompatibleKey(RenderingHints.Key key) { + return this.key == key; + } + + public int getIndex() { + return index; + } + + public String getId() { + return key.getIndex() + ":" + getIndex(); + } + + @Override + public String toString() { + return description; + } + } +} + diff --git a/io-vector/src/test/java/org/xbib/graphics/io/filters/AbsoluteToRelativeTransformsFilterTest.java b/io-vector/src/test/java/org/xbib/graphics/io/filters/AbsoluteToRelativeTransformsFilterTest.java new file mode 100644 index 0000000..77d4d1f --- /dev/null +++ b/io-vector/src/test/java/org/xbib/graphics/io/filters/AbsoluteToRelativeTransformsFilterTest.java @@ -0,0 +1,90 @@ +package org.xbib.graphics.io.filters; + +import static org.hamcrest.CoreMatchers.is; +import static org.hamcrest.MatcherAssert.assertThat; +import org.junit.jupiter.api.Test; +import org.xbib.graphics.io.vector.Command; +import org.xbib.graphics.io.vector.commands.CreateCommand; +import org.xbib.graphics.io.vector.commands.DisposeCommand; +import org.xbib.graphics.io.vector.commands.SetTransformCommand; +import org.xbib.graphics.io.vector.commands.TransformCommand; +import org.xbib.graphics.io.vector.commands.TranslateCommand; +import org.xbib.graphics.io.vector.filters.AbsoluteToRelativeTransformsFilter; +import java.awt.geom.AffineTransform; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; + +public class AbsoluteToRelativeTransformsFilterTest { + + @Test + public void testAbsoluteAndRelativeTransformsIdentical() { + AffineTransform absoluteTransform = new AffineTransform(); + absoluteTransform.rotate(42.0); + absoluteTransform.translate(4.0, 2.0); + List> commands = wrapCommands( + new SetTransformCommand(absoluteTransform) + ); + + AbsoluteToRelativeTransformsFilter filter = new AbsoluteToRelativeTransformsFilter(commands); + + filter.next(); + AffineTransform relativeTransform = ((TransformCommand) filter.next()).getValue(); + assertThat(relativeTransform, is(absoluteTransform)); + } + + @Test + public void testTranslateCorrect() { + AffineTransform absoluteTransform = new AffineTransform(); + absoluteTransform.scale(2.0, 2.0); + absoluteTransform.translate(4.2, 4.2); // (8.4, 8.4) + List> commands = wrapCommands( + new TranslateCommand(4.0, 2.0), + new SetTransformCommand(absoluteTransform) + ); + + AbsoluteToRelativeTransformsFilter filter = new AbsoluteToRelativeTransformsFilter(commands); + + TransformCommand transformCommand = null; + while (filter.hasNext()) { + Command filteredCommand = filter.next(); + if (filteredCommand instanceof TransformCommand) { + transformCommand = (TransformCommand) filteredCommand; + } + } + AffineTransform relativeTransform = transformCommand.getValue(); + assertThat(relativeTransform.getTranslateX(), is(4.4)); + assertThat(relativeTransform.getTranslateY(), is(6.4)); + } + + @Test + public void testRelativeTransformAfterDispose() { + AffineTransform absoluteTransform = new AffineTransform(); + absoluteTransform.rotate(42.0); + absoluteTransform.translate(4.0, 2.0); + List> commands = wrapCommands( + new CreateCommand(null), + new TransformCommand(absoluteTransform), + new DisposeCommand(null), + new SetTransformCommand(absoluteTransform) + ); + + AbsoluteToRelativeTransformsFilter filter = new AbsoluteToRelativeTransformsFilter(commands); + TransformCommand lastTransformCommand = null; + for (Command filteredCommand : filter) { + if (filteredCommand instanceof TransformCommand) { + lastTransformCommand = (TransformCommand) filteredCommand; + } + } + assertThat(lastTransformCommand.getValue(), is(absoluteTransform)); + } + + private List> wrapCommands(Command... commands) { + List> commandList = new ArrayList>(commands.length + 2); + commandList.add(new CreateCommand(null)); + commandList.addAll(Arrays.asList(commands)); + commandList.add(new DisposeCommand(null)); + return commandList; + } +} + diff --git a/io-vector/src/test/java/org/xbib/graphics/io/filters/FillPaintedShapeAsImageFilterTest.java b/io-vector/src/test/java/org/xbib/graphics/io/filters/FillPaintedShapeAsImageFilterTest.java new file mode 100644 index 0000000..b442ad0 --- /dev/null +++ b/io-vector/src/test/java/org/xbib/graphics/io/filters/FillPaintedShapeAsImageFilterTest.java @@ -0,0 +1,53 @@ +package org.xbib.graphics.io.filters; + +import static org.hamcrest.CoreMatchers.any; +import static org.hamcrest.MatcherAssert.assertThat; +import static org.hamcrest.Matchers.hasItem; +import static org.hamcrest.Matchers.not; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertFalse; +import org.junit.jupiter.api.Test; +import org.xbib.graphics.io.vector.Command; +import org.xbib.graphics.io.vector.commands.DrawImageCommand; +import org.xbib.graphics.io.vector.commands.FillShapeCommand; +import org.xbib.graphics.io.vector.commands.RotateCommand; +import org.xbib.graphics.io.vector.commands.SetPaintCommand; +import org.xbib.graphics.io.vector.filters.FillPaintedShapeAsImageFilter; +import java.awt.Color; +import java.awt.GradientPaint; +import java.awt.geom.Rectangle2D; +import java.util.Iterator; +import java.util.LinkedList; +import java.util.List; + +public class FillPaintedShapeAsImageFilterTest { + + @Test + public void testFillShapeReplacedWithDrawImage() { + List> commands = new LinkedList>(); + commands.add(new SetPaintCommand(new GradientPaint(0.0f, 0.0f, Color.BLACK, 100.0f, 100.0f, Color.WHITE))); + commands.add(new RotateCommand(10.0, 4.0, 2.0)); + commands.add(new FillShapeCommand(new Rectangle2D.Double(10.0, 10.0, 100.0, 100.0))); + + FillPaintedShapeAsImageFilter filter = new FillPaintedShapeAsImageFilter(commands); + + assertThat(filter, hasItem(any(DrawImageCommand.class))); + assertThat(filter, not(hasItem(any(FillShapeCommand.class)))); + } + + @Test + public void testFillShapeNotReplacedWithoutPaintCommand() { + List> commands = new LinkedList>(); + commands.add(new RotateCommand(10.0, 4.0, 2.0)); + commands.add(new FillShapeCommand(new Rectangle2D.Double(10.0, 10.0, 100.0, 100.0))); + + FillPaintedShapeAsImageFilter filter = new FillPaintedShapeAsImageFilter(commands); + + Iterator> filterIterator = filter.iterator(); + for (Command command : commands) { + assertEquals(command, filterIterator.next()); + } + assertFalse(filterIterator.hasNext()); + } +} + diff --git a/io-vector/src/test/java/org/xbib/graphics/io/filters/FilterTest.java b/io-vector/src/test/java/org/xbib/graphics/io/filters/FilterTest.java new file mode 100644 index 0000000..f0f7281 --- /dev/null +++ b/io-vector/src/test/java/org/xbib/graphics/io/filters/FilterTest.java @@ -0,0 +1,99 @@ +package org.xbib.graphics.io.filters; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertTrue; +import org.junit.jupiter.api.Test; +import org.xbib.graphics.io.vector.Command; +import org.xbib.graphics.io.vector.commands.DrawShapeCommand; +import org.xbib.graphics.io.vector.commands.SetColorCommand; +import org.xbib.graphics.io.vector.commands.SetStrokeCommand; +import org.xbib.graphics.io.vector.commands.SetTransformCommand; +import org.xbib.graphics.io.vector.filters.Filter; +import java.awt.BasicStroke; +import java.awt.Color; +import java.awt.geom.AffineTransform; +import java.awt.geom.Line2D; +import java.util.Arrays; +import java.util.Collections; +import java.util.Iterator; +import java.util.LinkedList; +import java.util.List; + + +public class FilterTest { + + @Test + public void filterNone() { + List> stream = new LinkedList>(); + stream.add(new SetColorCommand(Color.BLACK)); + stream.add(new SetStrokeCommand(new BasicStroke(1f))); + stream.add(new DrawShapeCommand(new Line2D.Double(0.0, 1.0, 10.0, 11.0))); + stream.add(new SetTransformCommand(AffineTransform.getTranslateInstance(5.0, 5.0))); + stream.add(new DrawShapeCommand(new Line2D.Double(0.0, 1.0, 5.0, 6.0))); + + Iterator> unfiltered = stream.iterator(); + + Filter filtered = new Filter(stream) { + @Override + protected List> filter(Command command) { + return Collections.singletonList(command); + } + }; + + while (filtered.hasNext() || unfiltered.hasNext()) { + Command expected = unfiltered.next(); + Command result = filtered.next(); + assertEquals(expected, result); + } + } + + @Test + public void filterAll() { + List> stream = new LinkedList>(); + stream.add(new SetColorCommand(Color.BLACK)); + stream.add(new SetStrokeCommand(new BasicStroke(1f))); + stream.add(new DrawShapeCommand(new Line2D.Double(0.0, 1.0, 10.0, 11.0))); + stream.add(new SetTransformCommand(AffineTransform.getTranslateInstance(5.0, 5.0))); + stream.add(new DrawShapeCommand(new Line2D.Double(0.0, 1.0, 5.0, 6.0))); + + Iterator> unfiltered = stream.iterator(); + + Filter filtered = new Filter(stream) { + @Override + protected List> filter(Command command) { + return null; + } + }; + assertTrue(unfiltered.hasNext()); + assertFalse(filtered.hasNext()); + } + + @Test + public void duplicate() { + List> stream = new LinkedList>(); + stream.add(new SetColorCommand(Color.BLACK)); + stream.add(new SetStrokeCommand(new BasicStroke(1f))); + stream.add(new DrawShapeCommand(new Line2D.Double(0.0, 1.0, 10.0, 11.0))); + stream.add(new SetTransformCommand(AffineTransform.getTranslateInstance(5.0, 5.0))); + stream.add(new DrawShapeCommand(new Line2D.Double(0.0, 1.0, 5.0, 6.0))); + + Iterator> unfiltered = stream.iterator(); + + Filter filtered = new Filter(stream) { + @Override + protected List> filter(Command command) { + return Arrays.asList(command, command); + } + }; + + while (filtered.hasNext() || unfiltered.hasNext()) { + Command expected = unfiltered.next(); + Command result1 = filtered.next(); + Command result2 = filtered.next(); + assertEquals(expected, result1); + assertEquals(expected, result2); + } + } +} + diff --git a/io-vector/src/test/java/org/xbib/graphics/io/filters/GroupingFilterTest.java b/io-vector/src/test/java/org/xbib/graphics/io/filters/GroupingFilterTest.java new file mode 100644 index 0000000..16edfd5 --- /dev/null +++ b/io-vector/src/test/java/org/xbib/graphics/io/filters/GroupingFilterTest.java @@ -0,0 +1,56 @@ +package org.xbib.graphics.io.filters; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import org.junit.jupiter.api.Test; +import org.xbib.graphics.io.vector.Command; +import org.xbib.graphics.io.vector.commands.DrawShapeCommand; +import org.xbib.graphics.io.vector.commands.Group; +import org.xbib.graphics.io.vector.commands.SetColorCommand; +import org.xbib.graphics.io.vector.commands.SetStrokeCommand; +import org.xbib.graphics.io.vector.commands.SetTransformCommand; +import org.xbib.graphics.io.vector.commands.StateCommand; +import org.xbib.graphics.io.vector.filters.Filter; +import org.xbib.graphics.io.vector.filters.GroupingFilter; +import java.awt.BasicStroke; +import java.awt.Color; +import java.awt.geom.AffineTransform; +import java.awt.geom.Line2D; +import java.util.Iterator; +import java.util.LinkedList; +import java.util.List; + +public class GroupingFilterTest { + + @Test + public void filtered() { + List> resultStream = new LinkedList>(); + resultStream.add(new SetColorCommand(Color.BLACK)); + resultStream.add(new SetStrokeCommand(new BasicStroke(1f))); + resultStream.add(new DrawShapeCommand(new Line2D.Double(0.0, 1.0, 10.0, 11.0))); + resultStream.add(new SetTransformCommand(AffineTransform.getTranslateInstance(5.0, 5.0))); + resultStream.add(new DrawShapeCommand(new Line2D.Double(0.0, 1.0, 5.0, 6.0))); + List> expectedStream = new LinkedList>(); + Iterator> resultCloneIterator = resultStream.iterator(); + Group group1 = new Group(); + group1.add(resultCloneIterator.next()); + group1.add(resultCloneIterator.next()); + expectedStream.add(group1); + expectedStream.add(resultCloneIterator.next()); + Group group2 = new Group(); + group2.add(resultCloneIterator.next()); + expectedStream.add(group2); + expectedStream.add(resultCloneIterator.next()); + Iterator> expectedIterator = expectedStream.iterator(); + Filter resultIterator = new GroupingFilter(resultStream) { + @Override + protected boolean isGrouped(Command command) { + return command instanceof StateCommand; + } + }; + while (resultIterator.hasNext() || expectedIterator.hasNext()) { + Command result = resultIterator.next(); + Command expected = expectedIterator.next(); + assertEquals(expected, result); + } + } +} diff --git a/io-vector/src/test/java/org/xbib/graphics/io/vector/GraphicsStateTest.java b/io-vector/src/test/java/org/xbib/graphics/io/vector/GraphicsStateTest.java new file mode 100644 index 0000000..0155651 --- /dev/null +++ b/io-vector/src/test/java/org/xbib/graphics/io/vector/GraphicsStateTest.java @@ -0,0 +1,55 @@ +package org.xbib.graphics.io.vector; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertNotEquals; +import static org.junit.jupiter.api.Assertions.assertNotSame; +import org.junit.jupiter.api.Test; +import java.awt.Color; +import java.awt.Graphics2D; +import java.awt.geom.AffineTransform; +import java.awt.geom.Rectangle2D; +import java.awt.image.BufferedImage; + +public class GraphicsStateTest { + + @Test + public void testInitialStateIsEqualToGraphics2D() { + BufferedImage image = new BufferedImage(1, 1, BufferedImage.TYPE_INT_ARGB); + Graphics2D g2d = (Graphics2D) image.getGraphics(); + GraphicsState state = new GraphicsState(); + assertEquals(state.getBackground(), g2d.getBackground()); + assertEquals(state.getColor(), g2d.getColor()); + assertEquals(state.getClip(), g2d.getClip()); + assertEquals(state.getComposite(), g2d.getComposite()); + assertEquals(state.getFont(), g2d.getFont()); + assertEquals(state.getPaint(), g2d.getPaint()); + assertEquals(state.getStroke(), g2d.getStroke()); + assertEquals(state.getTransform(), g2d.getTransform()); + } + + @Test + public void testEquals() { + GraphicsState state1 = new GraphicsState(); + state1.setBackground(Color.WHITE); + state1.setColor(Color.BLACK); + state1.setClip(new Rectangle2D.Double(0, 0, 10, 10)); + GraphicsState state2 = new GraphicsState(); + state2.setBackground(Color.WHITE); + state2.setColor(Color.BLACK); + state2.setClip(new Rectangle2D.Double(0, 0, 10, 10)); + assertEquals(state1, state2); + state2.setTransform(AffineTransform.getTranslateInstance(5, 5)); + assertNotEquals(state2, state1); + } + + @Test + public void testClone() throws CloneNotSupportedException { + GraphicsState state = new GraphicsState(); + state.setBackground(Color.BLUE); + state.setColor(Color.GREEN); + state.setClip(new Rectangle2D.Double(2, 3, 4, 2)); + GraphicsState clone = (GraphicsState) state.clone(); + assertNotSame(state, clone); + assertEquals(state, clone); + } +} diff --git a/io-vector/src/test/java/org/xbib/graphics/io/vector/TestUtils.java b/io-vector/src/test/java/org/xbib/graphics/io/vector/TestUtils.java new file mode 100644 index 0000000..12840f5 --- /dev/null +++ b/io-vector/src/test/java/org/xbib/graphics/io/vector/TestUtils.java @@ -0,0 +1,265 @@ +package org.xbib.graphics.io.vector; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertTrue; +import java.util.Collections; +import java.util.IdentityHashMap; +import java.util.Iterator; +import java.util.LinkedList; +import java.util.List; +import java.util.Map; +import java.util.TreeMap; +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +public abstract class TestUtils { + + protected TestUtils() { + throw new UnsupportedOperationException(); + } + + public static void assertTemplateEquals(Template expected, Template actual) { + Iterator itExpected = expected.iterator(); + Iterator itActual = actual.iterator(); + while (itExpected.hasNext() && itActual.hasNext()) { + Object lineExpected = itExpected.next(); + Object lineActual = itActual.next(); + if (lineExpected == null) { + continue; + } + assertTrue(lineActual instanceof String, + String.format("Line is of type %s, expected String.", lineActual.getClass())); + if (lineExpected instanceof String) { + assertEquals(lineExpected, lineActual); + } else if (lineExpected instanceof Pattern) { + Pattern expectedPattern = (Pattern) lineExpected; + Matcher matcher = expectedPattern.matcher((String) lineActual); + assertTrue(matcher.matches(), + String.format("Line didn't match pattern.\nExpected: \"%s\"\nActual: \"%s\"", matcher.pattern(), lineActual)); + } + } + assertEquals(expected.size(), actual.size(), "Wrong number of lines in template."); + } + + private static List parseXML(String xmlString) { + XMLFragment frag; + List fragments = new LinkedList(); + int startPos = 0; + while ((frag = XMLFragment.parse(xmlString, startPos)) != null) { + fragments.add(frag); + startPos = frag.matchEnd; + } + return fragments; + } + + public static void assertXMLEquals(String expected, String actual) { + List expectedFrags = parseXML(expected); + List actualFrags = parseXML(actual); + + Iterator itExpected = expectedFrags.iterator(); + Iterator itActual = actualFrags.iterator(); + while (itExpected.hasNext() && itActual.hasNext()) { + XMLFragment expectedFrag = itExpected.next(); + XMLFragment actualFrag = itActual.next(); + assertEquals(expectedFrag, actualFrag); + } + + assertEquals(expectedFrags.size(), actualFrags.size()); + } + + @SuppressWarnings("serial") + public static class Template extends LinkedList { + public Template(Object[] lines) { + Collections.addAll(this, lines); + } + + public Template(Template[] templates) { + for (Template template : templates) { + addAll(template); + } + } + } + + public static class XMLFragment { + + private static final Pattern CDATA = Pattern.compile("\\s*"); + + private static final Pattern COMMENT = Pattern.compile("\\s*"); + + private static final Pattern TAG_BEGIN = Pattern.compile("\\s*<(/|\\?|!)?\\s*([^\\s>/\\?]+)"); + + private static final Pattern TAG_END = Pattern.compile("\\s*(/|\\?)?>"); + + private static final Pattern TAG_ATTRIBUTE = Pattern.compile("\\s*([^\\s>=]+)=(\"[^\"]*\"|'[^']*')"); + + private static final Pattern DOCTYPE_PART = Pattern.compile("\\s*(\"[^\"]*\"|'[^']*'|[^\\s>]+)"); + + public final String name; + + public final FragmentType type; + + public final Map attributes; + + public final int matchStart; + + public final int matchEnd; + + public XMLFragment(String name, FragmentType type, Map attributes, + int matchStart, int matchEnd) { + this.name = name; + this.type = type; + this.attributes = Collections.unmodifiableMap( + new TreeMap(attributes)); + this.matchStart = matchStart; + this.matchEnd = matchEnd; + } + + public static XMLFragment parse(String xmlString, int matchStart) { + Map attrs = new IdentityHashMap(); + + Matcher cdataMatch = CDATA.matcher(xmlString); + cdataMatch.region(matchStart, xmlString.length()); + if (cdataMatch.lookingAt()) { + attrs.put("value", cdataMatch.group(1)); + return new XMLFragment("", FragmentType.CDATA, attrs, matchStart, cdataMatch.end()); + } + + Matcher commentMatch = COMMENT.matcher(xmlString); + commentMatch.region(matchStart, xmlString.length()); + if (commentMatch.lookingAt()) { + attrs.put("value", commentMatch.group(1).trim()); + return new XMLFragment("", FragmentType.COMMENT, attrs, matchStart, commentMatch.end()); + } + + Matcher beginMatch = TAG_BEGIN.matcher(xmlString); + beginMatch.region(matchStart, xmlString.length()); + if (!beginMatch.lookingAt()) { + return null; + } + int matchEndPrev = beginMatch.end(); + + String modifiers = beginMatch.group(1); + String name = beginMatch.group(2); + boolean endTag = "/".equals(modifiers); + boolean declarationStart = "?".equals(modifiers); + boolean doctype = "!".equals(modifiers) && "DOCTYPE".equals(name); + + if (doctype) { + int partNo = 0; + while (true) { + Matcher attrMatch = DOCTYPE_PART.matcher(xmlString); + attrMatch.region(matchEndPrev, xmlString.length()); + if (!attrMatch.lookingAt()) { + break; + } + matchEndPrev = attrMatch.end(); + + String partValue = attrMatch.group(1); + if (partValue.startsWith("\"") || partValue.startsWith("'")) { + partValue = partValue.substring(1, partValue.length() - 1); + } + + String partId = String.format("doctype %02d", partNo++); + attrs.put(partId, partValue); + } + } else { + while (true) { + Matcher attrMatch = TAG_ATTRIBUTE.matcher(xmlString); + attrMatch.region(matchEndPrev, xmlString.length()); + if (!attrMatch.lookingAt()) { + break; + } + matchEndPrev = attrMatch.end(); + + String attrName = attrMatch.group(1); + String attrValue = attrMatch.group(2); + attrValue = attrValue.substring(1, attrValue.length() - 1); + attrs.put(attrName, attrValue); + } + } + + Matcher endMatch = TAG_END.matcher(xmlString); + endMatch.region(matchEndPrev, xmlString.length()); + if (!endMatch.lookingAt()) { + throw new AssertionError(String.format("No tag end found: %s", xmlString.substring(0, matchEndPrev))); + } + matchEndPrev = endMatch.end(); + + modifiers = endMatch.group(1); + boolean emptyElement = "/".equals(modifiers); + boolean declarationEnd = "?".equals(modifiers); + + FragmentType type = FragmentType.START_TAG; + if (endTag) { + type = FragmentType.END_TAG; + } else if (emptyElement) { + type = FragmentType.EMPTY_ELEMENT; + } else if (declarationStart && declarationEnd) { + type = FragmentType.DECLARATION; + } else if (doctype) { + type = FragmentType.DOCTYPE; + } + + return new XMLFragment(name, type, attrs, matchStart, matchEndPrev); + } + + @Override + public boolean equals(Object o) { + if (!(o instanceof XMLFragment)) { + return false; + } + XMLFragment frag = (XMLFragment) o; + if (!type.equals(frag.type) || !name.equals(frag.name)) { + return false; + } + Iterator> itThis = attributes.entrySet().iterator(); + Iterator> itFrag = frag.attributes.entrySet().iterator(); + while (itThis.hasNext() && itFrag.hasNext()) { + Map.Entry attrThis = itThis.next(); + Map.Entry attrFrag = itFrag.next(); + if (!attrThis.getKey().equals(attrFrag.getKey()) || + !attrThis.getValue().equals(attrFrag.getValue())) { + return false; + } + } + return true; + } + + @Override + public int hashCode() { + return type.hashCode() ^ attributes.hashCode(); + } + + @Override + public String toString() { + StringBuilder s = new StringBuilder("<"); + if (FragmentType.END_TAG.equals(type)) { + s.append("/"); + } else if (FragmentType.DECLARATION.equals(type)) { + s.append("?"); + } + + if (FragmentType.DOCTYPE.equals(type)) { + s.append("!").append(name); + for (String partValue : attributes.values()) { + s.append(" ").append(partValue); + } + } else { + s.append(name); + for (Map.Entry attr : attributes.entrySet()) { + s.append(" ").append(attr.getKey()).append("=\"").append(attr.getValue()).append("\""); + } + } + if (FragmentType.DECLARATION.equals(type)) { + s.append("?"); + } + s.append(">"); + return s.toString(); + } + + public enum FragmentType { + START_TAG, END_TAG, EMPTY_ELEMENT, CDATA, + DECLARATION, DOCTYPE, COMMENT + } + } +} diff --git a/io-vector/src/test/java/org/xbib/graphics/io/vector/TestUtilsTest.java b/io-vector/src/test/java/org/xbib/graphics/io/vector/TestUtilsTest.java new file mode 100644 index 0000000..c22f4f6 --- /dev/null +++ b/io-vector/src/test/java/org/xbib/graphics/io/vector/TestUtilsTest.java @@ -0,0 +1,189 @@ +package org.xbib.graphics.io.vector; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertTrue; +import org.junit.jupiter.api.Test; +import org.xbib.graphics.io.vector.TestUtils.XMLFragment; + + +public class TestUtilsTest { + + @Test + public void testParseXmlStartTag() throws Exception { + String xmlTagName = "foo:bar.baz_tag"; + String xmlString; + XMLFragment frag; + xmlString = "<" + xmlTagName + ">"; + frag = XMLFragment.parse(xmlString, 0); + assertEquals(xmlTagName, frag.name); + assertEquals(XMLFragment.FragmentType.START_TAG, frag.type); + assertTrue(frag.attributes.isEmpty()); + assertEquals(0, frag.matchStart); + assertEquals(xmlString.length(), frag.matchEnd); + xmlString = "< " + xmlTagName + " >"; + frag = XMLFragment.parse(xmlString, 0); + assertEquals(xmlTagName, frag.name); + assertEquals(XMLFragment.FragmentType.START_TAG, frag.type); + assertTrue(frag.attributes.isEmpty()); + assertEquals(0, frag.matchStart); + assertEquals(xmlString.length(), frag.matchEnd); + } + + @Test + public void testParseXmlEndTag() throws Exception { + String xmlTagName = "foo:bar.baz_tag"; + String xmlString; + XMLFragment frag; + + xmlString = ""; + frag = XMLFragment.parse(xmlString, 0); + assertEquals(xmlTagName, frag.name); + assertEquals(XMLFragment.FragmentType.END_TAG, frag.type); + assertTrue(frag.attributes.isEmpty()); + assertEquals(0, frag.matchStart); + assertEquals(xmlString.length(), frag.matchEnd); + + xmlString = ""; + frag = XMLFragment.parse(xmlString, 0); + assertEquals(xmlTagName, frag.name); + assertEquals(XMLFragment.FragmentType.END_TAG, frag.type); + assertTrue(frag.attributes.isEmpty()); + assertEquals(0, frag.matchStart); + assertEquals(xmlString.length(), frag.matchEnd); + } + + @Test + public void testParseXmlEmptyElement() throws Exception { + String xmlTagName = "foo:bar.baz_tag"; + String xmlString; + XMLFragment frag; + + xmlString = "<" + xmlTagName + "/>"; + frag = XMLFragment.parse(xmlString, 0); + assertEquals(xmlTagName, frag.name); + assertEquals(XMLFragment.FragmentType.EMPTY_ELEMENT, frag.type); + assertTrue(frag.attributes.isEmpty()); + assertEquals(0, frag.matchStart); + assertEquals(xmlString.length(), frag.matchEnd); + + xmlString = "< " + xmlTagName + " />"; + frag = XMLFragment.parse(xmlString, 0); + assertEquals(xmlTagName, frag.name); + assertEquals(XMLFragment.FragmentType.EMPTY_ELEMENT, frag.type); + assertTrue(frag.attributes.isEmpty()); + assertEquals(0, frag.matchStart); + assertEquals(xmlString.length(), frag.matchEnd); + } + + @Test + public void testParseXmlCDATA() throws Exception { + String xmlString; + XMLFragment frag; + + xmlString = ""; + frag = XMLFragment.parse(xmlString, 0); + assertEquals("", frag.name); + assertEquals(XMLFragment.FragmentType.CDATA, frag.type); + assertEquals("foo bar", frag.attributes.get("value")); + assertEquals(0, frag.matchStart); + assertEquals(xmlString.length(), frag.matchEnd); + + xmlString = "foo bar]]>"; + frag = XMLFragment.parse(xmlString, 0); + assertEquals("", frag.name); + assertEquals(XMLFragment.FragmentType.CDATA, frag.type); + assertEquals("foo bar", frag.attributes.get("value")); + assertEquals(0, frag.matchStart); + assertEquals(xmlString.length(), frag.matchEnd); + } + + @Test + public void testParseXmlDeclaration() throws Exception { + String xmlString; + XMLFragment frag; + + xmlString = ""; + frag = XMLFragment.parse(xmlString, 0); + assertEquals("xml", frag.name); + assertEquals(XMLFragment.FragmentType.DECLARATION, frag.type); + assertEquals("1.0", frag.attributes.get("version")); + assertEquals("UTF-8", frag.attributes.get("encoding")); + assertEquals(0, frag.matchStart); + assertEquals(xmlString.length(), frag.matchEnd); + } + + @Test + public void testParseXmlDoctype() throws Exception { + String xmlString; + XMLFragment frag; + + xmlString = ""; + frag = XMLFragment.parse(xmlString, 0); + assertEquals(XMLFragment.FragmentType.DOCTYPE, frag.type); + assertEquals("html", frag.attributes.get("doctype 00")); + assertEquals(0, frag.matchStart); + assertEquals(xmlString.length(), frag.matchEnd); + + xmlString = ""; + frag = XMLFragment.parse(xmlString, 0); + assertEquals(XMLFragment.FragmentType.DOCTYPE, frag.type); + assertEquals("svg", frag.attributes.get("doctype 00")); + assertEquals("PUBLIC", frag.attributes.get("doctype 01")); + assertEquals("-//W3C//DTD SVG 1.1 Tiny//EN", frag.attributes.get("doctype 02")); + assertEquals("http://www.w3.org/Graphics/SVG/1.1/DTD/svg11-tiny.dtd", frag.attributes.get("doctype 03")); + assertEquals(0, frag.matchStart); + assertEquals(xmlString.length(), frag.matchEnd); + } + + @Test + public void testParseXmlComment() throws Exception { + String xmlString; + XMLFragment frag; + + xmlString = ""; + frag = XMLFragment.parse(xmlString, 0); + assertEquals("", frag.name); + assertEquals(XMLFragment.FragmentType.COMMENT, frag.type); + assertEquals("foo bar", frag.attributes.get("value")); + assertEquals(0, frag.matchStart); + assertEquals(xmlString.length(), frag.matchEnd); + + xmlString = ""; + frag = XMLFragment.parse(xmlString, 0); + assertEquals("", frag.name); + assertEquals(XMLFragment.FragmentType.COMMENT, frag.type); + assertEquals("foo bar", frag.attributes.get("value")); + assertEquals(0, frag.matchStart); + assertEquals(xmlString.length(), frag.matchEnd); + } + + @Test + public void testParseXMLAttributesTag() throws Exception { + String xmlTagName = "foo:bar.baz_tag"; + String xmlString; + XMLFragment frag; + + xmlString = "<" + xmlTagName + " foo='bar'>"; + frag = XMLFragment.parse(xmlString, 0); + assertEquals(xmlTagName, frag.name); + assertEquals("bar", frag.attributes.get("foo")); + assertEquals(0, frag.matchStart); + assertEquals(xmlString.length(), frag.matchEnd); + + xmlString = "<" + xmlTagName + " foo=\"bar\">"; + frag = XMLFragment.parse(xmlString, 0); + assertEquals(xmlTagName, frag.name); + assertEquals("bar", frag.attributes.get("foo")); + assertEquals(0, frag.matchStart); + assertEquals(xmlString.length(), frag.matchEnd); + + xmlString = "<" + xmlTagName + " foo=\"bar\" baz='qux'>"; + frag = XMLFragment.parse(xmlString, 0); + assertEquals(xmlTagName, frag.name); + assertEquals("bar", frag.attributes.get("foo")); + assertEquals("qux", frag.attributes.get("baz")); + assertEquals(0, frag.matchStart); + assertEquals(xmlString.length(), frag.matchEnd); + } +} diff --git a/io-vector/src/test/java/org/xbib/graphics/io/vector/VectorGraphics2DTest.java b/io-vector/src/test/java/org/xbib/graphics/io/vector/VectorGraphics2DTest.java new file mode 100644 index 0000000..cd6c93d --- /dev/null +++ b/io-vector/src/test/java/org/xbib/graphics/io/vector/VectorGraphics2DTest.java @@ -0,0 +1,56 @@ +package org.xbib.graphics.io.vector; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertNotNull; +import static org.junit.jupiter.api.Assertions.assertTrue; +import org.junit.jupiter.api.Test; +import org.xbib.graphics.io.vector.commands.CreateCommand; +import org.xbib.graphics.io.vector.commands.DisposeCommand; +import java.awt.Color; +import java.awt.Graphics2D; +import java.util.Iterator; + +public class VectorGraphics2DTest { + + @Test + public void testEmptyVectorGraphics2DStartsWithCreateCommand() { + VectorGraphics2D g = new VectorGraphics2D(); + Iterable> commands = g.getCommands(); + Iterator> commandIterator = commands.iterator(); + assertTrue(commandIterator.hasNext()); + Command firstCommand = commandIterator.next(); + assertTrue(firstCommand instanceof CreateCommand); + assertEquals(g, ((CreateCommand) firstCommand).getValue()); + } + + @Test + public void testCreateEmitsCreateCommand() { + VectorGraphics2D g = new VectorGraphics2D(); + VectorGraphics2D g2 = (VectorGraphics2D) g.create(); + assertNotNull(g2); + CreateCommand g2CreateCommand = null; + for (Command g2Command : g2.getCommands()) { + if (g2Command instanceof CreateCommand) { + g2CreateCommand = (CreateCommand) g2Command; + } + } + assertNotNull(g2CreateCommand); + assertEquals(g2, g2CreateCommand.getValue()); + } + + @Test + public void testDisposeCommandEmitted() { + VectorGraphics2D g = new VectorGraphics2D(); + g.setColor(Color.RED); + Graphics2D g2 = (Graphics2D) g.create(); + g2.setColor(Color.BLUE); + g2.dispose(); + Iterable> commands = g.getCommands(); + Command lastCommand = null; + for (Command command : commands) { + lastCommand = command; + } + assertTrue(lastCommand instanceof DisposeCommand); + assertEquals(Color.BLUE, ((DisposeCommand) lastCommand).getValue().getColor()); + } +} diff --git a/io-vector/src/test/java/org/xbib/graphics/io/vector/eps/EPSProcessorTest.java b/io-vector/src/test/java/org/xbib/graphics/io/vector/eps/EPSProcessorTest.java new file mode 100644 index 0000000..29bb7da --- /dev/null +++ b/io-vector/src/test/java/org/xbib/graphics/io/vector/eps/EPSProcessorTest.java @@ -0,0 +1,71 @@ +package org.xbib.graphics.io.vector.eps; + +import static org.xbib.graphics.io.vector.TestUtils.Template; +import static org.xbib.graphics.io.vector.TestUtils.assertTemplateEquals; +import org.junit.jupiter.api.Test; +import org.xbib.graphics.io.vector.ProcessorResult; +import org.xbib.graphics.io.vector.Command; +import org.xbib.graphics.io.vector.PageSize; +import java.io.ByteArrayOutputStream; +import java.io.IOException; +import java.nio.charset.StandardCharsets; +import java.util.Collections; +import java.util.LinkedList; +import java.util.List; +import java.util.regex.Pattern; + +public class EPSProcessorTest { + + private static final String EOL = "\n"; + + private static final Object[] HEADER = { + "%!PS-Adobe-3.0 EPSF-3.0", + "%%BoundingBox: 0 28 57 114", + "%%HiResBoundingBox: 0.0 28.34645669291339 56.69291338582678 113.38582677165356", + "%%LanguageLevel: 3", + "%%Pages: 1", + "%%EndComments", + "%%Page: 1 1", + "/M /moveto load def", + "/L /lineto load def", + "/C /curveto load def", + "/Z /closepath load def", + "/RL /rlineto load def", + "/rgb /setrgbcolor load def", + "/rect { /height exch def /width exch def /y exch def /x exch def x y M width 0 RL 0 height RL width neg 0 RL } bind def", + "/ellipse { /endangle exch def /startangle exch def /ry exch def /rx exch def /y exch def /x exch def /savematrix matrix currentmatrix def x y translate rx ry scale 0 0 1 startangle endangle arcn savematrix setmatrix } bind def", + "/imgdict { /datastream exch def /hasdata exch def /decodeScale exch def /bits exch def /bands exch def /imgheight exch def /imgwidth exch def << /ImageType 1 /Width imgwidth /Height imgheight /BitsPerComponent bits /Decode [bands {0 decodeScale} repeat] ", + "/ImageMatrix [imgwidth 0 0 imgheight 0 0] hasdata { /DataSource datastream } if >> } bind def", + "/latinize { /fontName exch def /fontNameNew exch def fontName findfont 0 dict copy begin /Encoding ISOLatin1Encoding def fontNameNew /FontName def currentdict end dup /FID undef fontNameNew exch definefont pop } bind def", + Pattern.compile("/\\S+?Lat /\\S+ latinize /\\S+?Lat 12.0 selectfont"), + "gsave", + "clipsave", + "/DeviceRGB setcolorspace", + "0 85.03937007874016 translate", + "2.834645669291339 -2.834645669291339 scale", + "/basematrix matrix currentmatrix def", + "%%EOF" + }; + private static final PageSize PAGE_SIZE = new PageSize(0.0, 10.0, 20.0, 30.0); + + private final EPSProcessor processor = new EPSProcessor(); + + private final List> commands = new LinkedList<>(); + + private final ByteArrayOutputStream bytes = new ByteArrayOutputStream(); + + private String process(Command... commands) throws IOException { + Collections.addAll(this.commands, commands); + ProcessorResult processed = processor.process(this.commands, PAGE_SIZE); + processed.write(bytes); + return bytes.toString(StandardCharsets.ISO_8859_1); + } + + @Test + public void envelopeForEmptyDocument() throws IOException { + String result = process(); + Template actual = new Template(result.split(EOL)); + Template expected = new Template(HEADER); + assertTemplateEquals(expected, actual); + } +} diff --git a/io-vector/src/test/java/org/xbib/graphics/io/vector/pdf/PDFProcessorTest.java b/io-vector/src/test/java/org/xbib/graphics/io/vector/pdf/PDFProcessorTest.java new file mode 100644 index 0000000..f8d1e3e --- /dev/null +++ b/io-vector/src/test/java/org/xbib/graphics/io/vector/pdf/PDFProcessorTest.java @@ -0,0 +1,109 @@ +package org.xbib.graphics.io.vector.pdf; + +import static org.xbib.graphics.io.vector.TestUtils.Template; +import static org.xbib.graphics.io.vector.TestUtils.assertTemplateEquals; +import org.junit.jupiter.api.Test; +import org.xbib.graphics.io.vector.Command; +import org.xbib.graphics.io.vector.PageSize; +import java.io.ByteArrayOutputStream; +import java.io.IOException; +import java.nio.charset.StandardCharsets; +import java.util.Collections; +import java.util.LinkedList; +import java.util.List; +import java.util.regex.Pattern; + +public class PDFProcessorTest { + private static final String EOL = "\n"; + private static final String HEADER = "%PDF-1.4"; + private static final String FOOTER = "%%EOF"; + private static final PageSize PAGE_SIZE = new PageSize(0.0, 10.0, 20.0, 30.0); + + private final PDFProcessor processor = new PDFProcessor(false); + private final List> commands = new LinkedList<>(); + private final ByteArrayOutputStream bytes = new ByteArrayOutputStream(); + + private String process(Command... commands) throws IOException { + Collections.addAll(this.commands, commands); + processor.process(this.commands, PAGE_SIZE).write(bytes); + return bytes.toString(StandardCharsets.ISO_8859_1); + } + + @Test + public void envelopeForEmptyDocument() throws IOException { + String result = process(); + Template actual = new Template(result.split(EOL)); + Template expected = new Template(new Object[]{ + HEADER, + "1 0 obj", + "<<", + "/Type /Catalog", + "/Pages 2 0 R", + ">>", + "endobj", + "2 0 obj", + "<<", + "/Type /Pages", + "/Kids [3 0 R]", + "/Count 1", + ">>", + "endobj", + "3 0 obj", + "<<", + "/Type /Page", + "/Parent 2 0 R", + "/MediaBox [0 28.34645669291339 56.69291338582678 85.03937007874016]", + "/Contents 4 0 R", + "/Resources 6 0 R", + ">>", + "endobj", + "4 0 obj", + "<<", + "/Length 5 0 R", + ">>", + "stream", + "q", + "1 1 1 rg 1 1 1 RG", + "2.834645669291339 0 0 -2.834645669291339 0 85.03937007874016 cm", + "/Fnt0 12.0 Tf", + "Q", + "endstream", + "endobj", + "5 0 obj", + "100", + "endobj", + "6 0 obj", + "<<", + "/ProcSet [/PDF /Text /ImageB /ImageC /ImageI]", + "/Font <<", + "/Fnt0 <<", + "/Type /Font", + "/Subtype /TrueType", + "/Encoding /WinAnsiEncoding", + Pattern.compile("/BaseFont /\\S+"), + ">>", + ">>", + ">>", + "endobj", + "xref", + "0 7", + "0000000000 65535 f ", + Pattern.compile("\\d{10} 00000 n "), + Pattern.compile("\\d{10} 00000 n "), + Pattern.compile("\\d{10} 00000 n "), + Pattern.compile("\\d{10} 00000 n "), + Pattern.compile("\\d{10} 00000 n "), + Pattern.compile("\\d{10} 00000 n "), + "trailer", + "<<", + "/Size 7", + "/Root 1 0 R", + ">>", + "startxref", + Pattern.compile("[1-9]\\d*"), + FOOTER + }); + assertTemplateEquals(expected, actual); + } +} + diff --git a/io-vector/src/test/java/org/xbib/graphics/io/vector/svg/SVGProcessorTest.java b/io-vector/src/test/java/org/xbib/graphics/io/vector/svg/SVGProcessorTest.java new file mode 100644 index 0000000..83f7be8 --- /dev/null +++ b/io-vector/src/test/java/org/xbib/graphics/io/vector/svg/SVGProcessorTest.java @@ -0,0 +1,68 @@ +package org.xbib.graphics.io.vector.svg; + +import static org.xbib.graphics.io.vector.TestUtils.assertXMLEquals; +import org.junit.jupiter.api.Test; +import org.xbib.graphics.io.vector.ProcessorResult; +import org.xbib.graphics.io.vector.Command; +import org.xbib.graphics.io.vector.commands.DrawShapeCommand; +import org.xbib.graphics.io.vector.commands.FillShapeCommand; +import org.xbib.graphics.io.vector.PageSize; +import java.awt.geom.Rectangle2D; +import java.io.ByteArrayOutputStream; +import java.io.IOException; +import java.nio.charset.StandardCharsets; +import java.util.Collections; +import java.util.LinkedList; +import java.util.List; + +public class SVGProcessorTest { + private static final String EOL = "\n"; + private static final String HEADER = + "" + EOL + + "" + EOL + + "" + EOL; + private static final String FOOTER = ""; + private static final PageSize PAGE_SIZE = new PageSize(0.0, 10.0, 20.0, 30.0); + + private final SVGProcessor processor = new SVGProcessor(); + private final List> commands = new LinkedList<>(); + private final ByteArrayOutputStream bytes = new ByteArrayOutputStream(); + + private String process(Command... commands) throws IOException { + Collections.addAll(this.commands, commands); + ProcessorResult processed = processor.process(this.commands, PAGE_SIZE); + processed.write(bytes); + return bytes.toString(StandardCharsets.UTF_8); + } + + @Test + public void envelopeForEmptyDocument() throws Exception { + String result = process(); + String expected = HEADER.replaceAll(">$", "/>"); + assertXMLEquals(expected, result); + } + + @Test + public void drawShapeBlack() throws Exception { + String result = process( + new DrawShapeCommand(new Rectangle2D.Double(1, 2, 3, 4)) + ); + String expected = + HEADER + EOL + + " " + EOL + + FOOTER; + assertXMLEquals(expected, result); + } + + @Test + public void fillShapeBlack() throws Exception { + String result = process( + new FillShapeCommand(new Rectangle2D.Double(1, 2, 3, 4)) + ); + String expected = + HEADER + EOL + + " " + EOL + + FOOTER; + assertXMLEquals(expected, result); + } +} diff --git a/io-vector/src/test/java/org/xbib/graphics/io/vector/util/ASCII85EncodeStreamTest.java b/io-vector/src/test/java/org/xbib/graphics/io/vector/util/ASCII85EncodeStreamTest.java new file mode 100644 index 0000000..958de3b --- /dev/null +++ b/io-vector/src/test/java/org/xbib/graphics/io/vector/util/ASCII85EncodeStreamTest.java @@ -0,0 +1,53 @@ +package org.xbib.graphics.io.vector.util; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import org.junit.jupiter.api.Test; +import java.io.ByteArrayInputStream; +import java.io.ByteArrayOutputStream; +import java.io.IOException; +import java.io.OutputStream; +import java.nio.charset.StandardCharsets; + +public class ASCII85EncodeStreamTest { + + private static void assertEncodedString(String expected, String input) throws IOException { + ByteArrayInputStream inputStream = new ByteArrayInputStream(input.getBytes()); + ByteArrayOutputStream outStream = new ByteArrayOutputStream(); + OutputStream encodeStream = new ASCII85EncodeStream(outStream); + byte[] buffer = new byte[1024]; + for (int count = inputStream.read(buffer); count >= 0; count = inputStream.read(buffer)) { + encodeStream.write(buffer, 0, count); + } + encodeStream.close(); + String encoded = outStream.toString(StandardCharsets.ISO_8859_1); + assertEquals(expected, encoded); + } + + @Test + public void testEncoding() throws IOException { + String input = + "Man is distinguished, not only by his reason, but by this singular passion " + + "from other animals, which is a lust of the mind, that by a perseverance of " + + "delight in the continued and indefatigable generation of knowledge, exceeds " + + "the short vehemence of any carnal pleasure."; + + String expected = + "9jqo^BlbD-BleB1DJ+*+F(f,q/0JhKFCj@.4Gp$d7F!,L7@<6@)/0JDEF@3BB/F*&OCAfu2/AKYi(DIb:@FD,*)" + + "+C]U=@3BN#EcYf8ATD3s@q?d$AftVqCh[NqF-FD5W8ARlolDIal(DIduD.RTpAKYo'+CT/5+Cei#" + + "DII?(E,9)oF*2M7/c~>"; + + assertEncodedString(expected, input); + } + + @Test + public void testPadding() throws IOException { + assertEncodedString("/c~>", "."); + } + + @Test + public void testEmpty() throws IOException { + assertEncodedString("~>", ""); + } +} diff --git a/io-vector/src/test/java/org/xbib/graphics/io/vector/util/Base64EncodeStreamTest.java b/io-vector/src/test/java/org/xbib/graphics/io/vector/util/Base64EncodeStreamTest.java new file mode 100644 index 0000000..8edea6a --- /dev/null +++ b/io-vector/src/test/java/org/xbib/graphics/io/vector/util/Base64EncodeStreamTest.java @@ -0,0 +1,63 @@ +package org.xbib.graphics.io.vector.util; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import org.junit.jupiter.api.Test; +import java.io.ByteArrayInputStream; +import java.io.ByteArrayOutputStream; +import java.io.IOException; +import java.io.OutputStream; +import java.nio.charset.StandardCharsets; + +public class Base64EncodeStreamTest { + + private static void assertEncodedString(String expected, String input) throws IOException { + ByteArrayInputStream inputStream = new ByteArrayInputStream(input.getBytes()); + ByteArrayOutputStream outStream = new ByteArrayOutputStream(); + OutputStream encodeStream = new Base64EncodeStream(outStream); + byte[] buffer = new byte[1024]; + for (int count = inputStream.read(buffer); count >= 0; count = inputStream.read(buffer)) { + encodeStream.write(buffer, 0, count); + } + encodeStream.close(); + String encoded = outStream.toString(StandardCharsets.ISO_8859_1); + assertEquals(expected, encoded); + } + + @Test + public void testEncoding() throws IOException { + String input = + "Man is distinguished, not only by his reason, but by this singular passion " + + "from other animals, which is a lust of the mind, that by a perseverance of " + + "delight in the continued and indefatigable generation of knowledge, exceeds " + + "the short vehemence of any carnal pleasure."; + + String expected = + "TWFuIGlzIGRpc3Rpbmd1aXNoZWQsIG5vdCBvbmx5IGJ5IGhpcyByZWFzb24sIGJ1dCBieSB0aGlz" + + "IHNpbmd1bGFyIHBhc3Npb24gZnJvbSBvdGhlciBhbmltYWxzLCB3aGljaCBpcyBhIGx1c3Qgb2Yg" + + "dGhlIG1pbmQsIHRoYXQgYnkgYSBwZXJzZXZlcmFuY2Ugb2YgZGVsaWdodCBpbiB0aGUgY29udGlu" + + "dWVkIGFuZCBpbmRlZmF0aWdhYmxlIGdlbmVyYXRpb24gb2Yga25vd2xlZGdlLCBleGNlZWRzIHRo" + + "ZSBzaG9ydCB2ZWhlbWVuY2Ugb2YgYW55IGNhcm5hbCBwbGVhc3VyZS4="; + + assertEncodedString(expected, input); + } + + @Test + public void testPadding() throws IOException { + assertEncodedString("YW55IGNhcm5hbCBwbGVhc3VyZS4=", "any carnal pleasure."); + assertEncodedString("YW55IGNhcm5hbCBwbGVhc3VyZQ==", "any carnal pleasure"); + assertEncodedString("YW55IGNhcm5hbCBwbGVhc3Vy", "any carnal pleasur"); + assertEncodedString("YW55IGNhcm5hbCBwbGVhc3U=", "any carnal pleasu"); + assertEncodedString("YW55IGNhcm5hbCBwbGVhcw==", "any carnal pleas"); + + assertEncodedString("cGxlYXN1cmUu", "pleasure."); + assertEncodedString("bGVhc3VyZS4=", "leasure."); + assertEncodedString("ZWFzdXJlLg==", "easure."); + assertEncodedString("YXN1cmUu", "asure."); + assertEncodedString("c3VyZS4=", "sure."); + } + + @Test + public void testEmpty() throws IOException { + assertEncodedString("", ""); + } +} diff --git a/io-vector/src/test/java/org/xbib/graphics/io/vector/util/DataUtilsTest.java b/io-vector/src/test/java/org/xbib/graphics/io/vector/util/DataUtilsTest.java new file mode 100644 index 0000000..cb8ce55 --- /dev/null +++ b/io-vector/src/test/java/org/xbib/graphics/io/vector/util/DataUtilsTest.java @@ -0,0 +1,28 @@ +package org.xbib.graphics.io.vector.util; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import org.junit.jupiter.api.Test; + +public class DataUtilsTest { + + @Test + public void stripTrailingSpaces() { + String result = DataUtils.stripTrailing(" foo bar! ", " "); + String expected = " foo bar!"; + assertEquals(expected, result); + } + + @Test + public void stripTrailingSpacesInMultilineString() { + String result = DataUtils.stripTrailing(" foo bar! \n ", " "); + String expected = " foo bar! \n"; + assertEquals(expected, result); + } + + @Test + public void stripComplexSubstring() { + String result = DataUtils.stripTrailing("+bar foo+bar+bar+bar", "+bar"); + String expected = "+bar foo"; + assertEquals(expected, result); + } +} diff --git a/io-vector/src/test/java/org/xbib/graphics/io/vector/util/GraphicsUtilsTest.java b/io-vector/src/test/java/org/xbib/graphics/io/vector/util/GraphicsUtilsTest.java new file mode 100644 index 0000000..3672382 --- /dev/null +++ b/io-vector/src/test/java/org/xbib/graphics/io/vector/util/GraphicsUtilsTest.java @@ -0,0 +1,142 @@ +package org.xbib.graphics.io.vector.util; + +import static org.junit.jupiter.api.Assertions.assertArrayEquals; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertNotNull; +import static org.junit.jupiter.api.Assertions.assertNotSame; +import static org.junit.jupiter.api.Assertions.assertTrue; +import org.junit.jupiter.api.Test; +import java.awt.Font; +import java.awt.Image; +import java.awt.Polygon; +import java.awt.Rectangle; +import java.awt.Shape; +import java.awt.Toolkit; +import java.awt.geom.Arc2D; +import java.awt.geom.CubicCurve2D; +import java.awt.geom.Ellipse2D; +import java.awt.geom.Line2D; +import java.awt.geom.Path2D; +import java.awt.geom.PathIterator; +import java.awt.geom.QuadCurve2D; +import java.awt.geom.Rectangle2D; +import java.awt.geom.RoundRectangle2D; +import java.awt.image.BufferedImage; +import java.awt.image.FilteredImageSource; +import java.awt.image.RGBImageFilter; +import java.lang.reflect.InvocationTargetException; + +/** + * On Linux, the package msttcorefonts need to be installed. + */ +public class GraphicsUtilsTest { + private static final double DELTA = 1e-15; + + private static void assertShapeEquals(Shape expected, Shape actual) { + if ((expected instanceof Line2D) && (actual instanceof Line2D)) { + assertEquals(((Line2D) expected).getP1(), ((Line2D) actual).getP1()); + assertEquals(((Line2D) expected).getP2(), ((Line2D) actual).getP2()); + } else if ((expected instanceof Polygon) && (actual instanceof Polygon)) { + int n = ((Polygon) actual).npoints; + assertEquals(((Polygon) expected).npoints, n); + if (n > 0) { + assertArrayEquals(((Polygon) expected).xpoints, ((Polygon) actual).xpoints); + assertArrayEquals(((Polygon) expected).ypoints, ((Polygon) actual).ypoints); + } + } else if ((expected instanceof QuadCurve2D) && (actual instanceof QuadCurve2D)) { + assertEquals(((QuadCurve2D) expected).getP1(), ((QuadCurve2D) actual).getP1()); + assertEquals(((QuadCurve2D) expected).getCtrlPt(), ((QuadCurve2D) actual).getCtrlPt()); + assertEquals(((QuadCurve2D) expected).getP2(), ((QuadCurve2D) actual).getP2()); + } else if ((expected instanceof CubicCurve2D) && (actual instanceof CubicCurve2D)) { + assertEquals(((CubicCurve2D) expected).getP1(), ((CubicCurve2D) actual).getP1()); + assertEquals(((CubicCurve2D) expected).getCtrlP1(), ((CubicCurve2D) actual).getCtrlP1()); + assertEquals(((CubicCurve2D) expected).getCtrlP2(), ((CubicCurve2D) actual).getCtrlP2()); + assertEquals(((CubicCurve2D) expected).getP2(), ((CubicCurve2D) actual).getP2()); + } else if ((expected instanceof Path2D) && (actual instanceof Path2D)) { + PathIterator itExpected = expected.getPathIterator(null); + PathIterator itActual = actual.getPathIterator(null); + double[] segmentExpected = new double[6]; + double[] segmentActual = new double[6]; + for (; !itExpected.isDone() || !itActual.isDone(); itExpected.next(), itActual.next()) { + assertEquals(itExpected.getWindingRule(), itActual.getWindingRule()); + itExpected.currentSegment(segmentExpected); + itActual.currentSegment(segmentActual); + assertArrayEquals(segmentExpected, segmentActual, DELTA); + } + } else { + assertEquals(expected, actual); + } + } + + @Test + public void testToBufferedImage() { + Image[] images = { + new BufferedImage(320, 240, BufferedImage.TYPE_INT_ARGB), + new BufferedImage(320, 240, BufferedImage.TYPE_INT_RGB), + Toolkit.getDefaultToolkit().createImage(new FilteredImageSource( + new BufferedImage(320, 240, BufferedImage.TYPE_INT_RGB).getSource(), + new RGBImageFilter() { + @Override + public int filterRGB(int x, int y, int rgb) { + return rgb & 0xff; + } + } + )) + }; + + for (Image image : images) { + BufferedImage bimage = GraphicsUtils.toBufferedImage(image); + assertNotNull(bimage); + assertEquals(BufferedImage.class, bimage.getClass()); + assertEquals(image.getWidth(null), bimage.getWidth()); + assertEquals(image.getHeight(null), bimage.getHeight()); + } + } + + @Test + public void testHasAlpha() { + Image image; + image = new BufferedImage(320, 240, BufferedImage.TYPE_INT_ARGB); + assertTrue(GraphicsUtils.hasAlpha(image)); + image = new BufferedImage(320, 240, BufferedImage.TYPE_INT_RGB); + assertFalse(GraphicsUtils.hasAlpha(image)); + } + + @Test + public void testPhysicalFont() { + Font font = new Font("Monospaced", Font.PLAIN, 12); + assertNotSame(font, GraphicsUtils.getPhysicalFont(font)); + } + + @Test + public void testCloneShape() + throws InstantiationException, IllegalAccessException, NoSuchMethodException, InvocationTargetException { + Class[] shapeClasses = { + Line2D.Float.class, + Line2D.Double.class, + Rectangle.class, + Rectangle2D.Float.class, + Rectangle2D.Double.class, + RoundRectangle2D.Float.class, + RoundRectangle2D.Double.class, + Ellipse2D.Float.class, + Ellipse2D.Double.class, + Arc2D.Float.class, + Arc2D.Double.class, + Polygon.class, + CubicCurve2D.Float.class, + CubicCurve2D.Double.class, + QuadCurve2D.Float.class, + QuadCurve2D.Double.class, + Path2D.Float.class, + Path2D.Double.class + }; + for (Class shapeClass : shapeClasses) { + Shape shape = (Shape) shapeClass.getDeclaredConstructor().newInstance(); + Shape clone = GraphicsUtils.clone(shape); + assertNotNull(clone); + assertShapeEquals(shape, clone); + } + } +} diff --git a/io-vector/src/test/java/org/xbib/graphics/io/visual/AbstractTest.java b/io-vector/src/test/java/org/xbib/graphics/io/visual/AbstractTest.java new file mode 100644 index 0000000..2dc575a --- /dev/null +++ b/io-vector/src/test/java/org/xbib/graphics/io/visual/AbstractTest.java @@ -0,0 +1,86 @@ +package org.xbib.graphics.io.visual; + +import org.xbib.graphics.io.vector.eps.EPSGraphics2D; +import org.xbib.graphics.io.vector.pdf.PDFGraphics2D; +import org.xbib.graphics.io.vector.svg.SVGGraphics2D; +import org.xbib.graphics.io.vector.PageSize; +import java.awt.Color; +import java.awt.Graphics2D; +import java.awt.RenderingHints; +import java.awt.image.BufferedImage; +import java.io.ByteArrayInputStream; +import java.io.File; +import java.io.IOException; +import java.io.InputStream; +import javax.imageio.ImageIO; + +public abstract class AbstractTest { + + private final PageSize pageSize; + + private final BufferedImage reference; + + private final EPSGraphics2D epsGraphics; + + private final PDFGraphics2D pdfGraphics; + + private final SVGGraphics2D svgGraphics; + + public AbstractTest() throws IOException { + int width = 150; + int height = 150; + pageSize = new PageSize(0.0, 0.0, width, height); + epsGraphics = new EPSGraphics2D(0, 0, width, height); + draw(epsGraphics); + pdfGraphics = new PDFGraphics2D(0, 0, width, height); + draw(pdfGraphics); + svgGraphics = new SVGGraphics2D(0, 0, width, height); + draw(svgGraphics); + reference = new BufferedImage(width, height, BufferedImage.TYPE_INT_ARGB); + Graphics2D referenceGraphics = reference.createGraphics(); + referenceGraphics.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON); + referenceGraphics.setRenderingHint(RenderingHints.KEY_RENDERING, RenderingHints.VALUE_RENDER_QUALITY); + referenceGraphics.setBackground(new Color(1f, 1f, 1f, 0f)); + referenceGraphics.clearRect(0, 0, reference.getWidth(), reference.getHeight()); + referenceGraphics.setColor(Color.BLACK); + draw(referenceGraphics); + File referenceImage = File.createTempFile(getClass().getName() + ".reference", ".png"); + referenceImage.deleteOnExit(); + ImageIO.write(reference, "png", referenceImage); + } + + public abstract void draw(Graphics2D g); + + public PageSize getPageSize() { + return pageSize; + } + + public BufferedImage getReference() { + return reference; + } + + public InputStream getEPS() { + try { + return new ByteArrayInputStream(epsGraphics.getBytes()); + } catch (IOException e) { + return null; + } + } + + public InputStream getPDF() { + try { + return new ByteArrayInputStream(pdfGraphics.getBytes()); + } catch (IOException e) { + return null; + } + } + + public InputStream getSVG() { + try { + return new ByteArrayInputStream(svgGraphics.getBytes()); + } catch (IOException e) { + return null; + } + } + +} diff --git a/io-vector/src/test/java/org/xbib/graphics/io/visual/CharacterTest.java b/io-vector/src/test/java/org/xbib/graphics/io/visual/CharacterTest.java new file mode 100644 index 0000000..8f8849f --- /dev/null +++ b/io-vector/src/test/java/org/xbib/graphics/io/visual/CharacterTest.java @@ -0,0 +1,44 @@ +package org.xbib.graphics.io.visual; + +import java.awt.Graphics2D; +import java.io.IOException; +import java.nio.charset.Charset; +import java.nio.charset.CharsetEncoder; +import java.nio.charset.StandardCharsets; +import java.util.ArrayList; +import java.util.List; + +public class CharacterTest extends AbstractTest { + + public CharacterTest() throws IOException { + } + + @Override + public void draw(Graphics2D g) { + double w = getPageSize().getWidth(); + double h = getPageSize().getHeight(); + Charset latin1 = StandardCharsets.ISO_8859_1; + CharsetEncoder latin1Encoder = latin1.newEncoder(); + List charactersInCharset = new ArrayList<>(); + for (char char_ = Character.MIN_VALUE; char_ < Character.MAX_VALUE; char_++) { + String javaString = String.valueOf(char_); + if (latin1Encoder.canEncode(char_)) { + charactersInCharset.add(javaString); + } + } + final int colCount = (int) Math.ceil(Math.sqrt(charactersInCharset.size())); + final int rowCount = colCount; + double tileWidth = w / colCount; + double tileHeight = h / rowCount; + int charIndex = 0; + for (double y = 0.0; y < h; y += tileHeight) { + for (double x = 0.0; x < w; x += tileWidth) { + String c = charactersInCharset.get(charIndex); + double tileCenterX = x + tileWidth / 2.0; + double tileCenterY = y + tileHeight / 2.0; + g.drawString(c, (float) tileCenterX, (float) tileCenterY); + charIndex++; + } + } + } +} diff --git a/io-vector/src/test/java/org/xbib/graphics/io/visual/ClippingTest.java b/io-vector/src/test/java/org/xbib/graphics/io/visual/ClippingTest.java new file mode 100644 index 0000000..8589b81 --- /dev/null +++ b/io-vector/src/test/java/org/xbib/graphics/io/visual/ClippingTest.java @@ -0,0 +1,37 @@ +package org.xbib.graphics.io.visual; + +import java.awt.Graphics2D; +import java.awt.geom.AffineTransform; +import java.awt.geom.Ellipse2D; +import java.awt.geom.Line2D; +import java.io.IOException; + +public class ClippingTest extends AbstractTest { + + public ClippingTest() throws IOException { + } + + @Override + public void draw(Graphics2D g) { + double w = getPageSize().getWidth(); + double h = getPageSize().getHeight(); + + AffineTransform txOrig = g.getTransform(); + g.translate(w / 2.0, h / 2.0); + + g.setClip(new Ellipse2D.Double(-0.6 * w / 2.0, -h / 2.0, 0.6 * w, h)); + for (double x = -w / 2.0; x < w / 2.0; x += 4.0) { + g.draw(new Line2D.Double(x, -h / 2.0, x, h / 2.0)); + } + + g.rotate(Math.toRadians(-90.0)); + g.clip(new Ellipse2D.Double(-0.6 * w / 2.0, -h / 2.0, 0.6 * w, h)); + for (double x = -h / 2.0; x < h / 2.0; x += 4.0) { + g.draw(new Line2D.Double(x, -w / 2.0, x, w / 2.0)); + } + + g.setTransform(txOrig); + g.setClip(null); + g.draw(new Line2D.Double(0.0, 0.0, w, h)); + } +} diff --git a/io-vector/src/test/java/org/xbib/graphics/io/visual/ColorTest.java b/io-vector/src/test/java/org/xbib/graphics/io/visual/ColorTest.java new file mode 100644 index 0000000..f8e1630 --- /dev/null +++ b/io-vector/src/test/java/org/xbib/graphics/io/visual/ColorTest.java @@ -0,0 +1,32 @@ +package org.xbib.graphics.io.visual; + +import java.awt.Color; +import java.awt.Graphics2D; +import java.awt.geom.Rectangle2D; +import java.io.IOException; + +public class ColorTest extends AbstractTest { + + public ColorTest() throws IOException { + } + + @Override + public void draw(Graphics2D g) { + final float wPage = (float) getPageSize().getWidth(); + final float hPage = (float) getPageSize().getHeight(); + final float wTile = Math.min(wPage / 15f, hPage / 15f); + final float hTile = wTile; + float w = wPage - wTile; + float h = hPage - hTile; + for (float y = (hPage - h) / 2f; y < h; y += hTile) { + float yRel = y / h; + for (float x = (wPage - w) / 2f; x < w; x += wTile) { + float xRel = x / w; + Color c = Color.getHSBColor(yRel, 1f, 1f); + int alpha = 255 - (int) (xRel * 255f); + g.setColor(new Color(c.getRed(), c.getGreen(), c.getBlue(), alpha)); + g.fill(new Rectangle2D.Float(x, y, wTile, hTile)); + } + } + } +} diff --git a/io-vector/src/test/java/org/xbib/graphics/io/visual/EmptyFileTest.java b/io-vector/src/test/java/org/xbib/graphics/io/visual/EmptyFileTest.java new file mode 100644 index 0000000..c984b07 --- /dev/null +++ b/io-vector/src/test/java/org/xbib/graphics/io/visual/EmptyFileTest.java @@ -0,0 +1,14 @@ +package org.xbib.graphics.io.visual; + +import java.awt.Graphics2D; +import java.io.IOException; + +public class EmptyFileTest extends AbstractTest { + + public EmptyFileTest() throws IOException { + } + + @Override + public void draw(Graphics2D g) { + } +} diff --git a/io-vector/src/test/java/org/xbib/graphics/io/visual/FontTest.java b/io-vector/src/test/java/org/xbib/graphics/io/visual/FontTest.java new file mode 100644 index 0000000..3af641c --- /dev/null +++ b/io-vector/src/test/java/org/xbib/graphics/io/visual/FontTest.java @@ -0,0 +1,50 @@ +package org.xbib.graphics.io.visual; + +import org.xbib.graphics.io.vector.GraphicsState; +import java.awt.Font; +import java.awt.Graphics2D; +import java.io.IOException; + +public class FontTest extends AbstractTest { + + public FontTest() throws IOException { + } + + @Override + public void draw(Graphics2D g) { + final int tileCountH = 4; + final int tileCountV = 8; + final double wTile = getPageSize().getWidth() / tileCountH; + final double hTile = getPageSize().getHeight() / tileCountV; + final double xOrigin = (getPageSize().getWidth() - tileCountH * wTile) / 2.0; + final double yOrigin = (getPageSize().getHeight() - tileCountV * hTile) / 2.0; + double x = xOrigin; + double y = yOrigin; + + final float[] sizes = { + GraphicsState.DEFAULT_FONT.getSize2D(), GraphicsState.DEFAULT_FONT.getSize2D() / 2f + }; + final String[] names = { + GraphicsState.DEFAULT_FONT.getName(), Font.SERIF, Font.MONOSPACED, "Monospaced" + }; + final int[] styles = { + Font.PLAIN, Font.ITALIC, Font.BOLD, Font.BOLD | Font.ITALIC + }; + + for (float size : sizes) { + for (String name : names) { + for (int style : styles) { + Font font = new Font(name, style, 10).deriveFont(size); + g.setFont(font); + g.drawString("vg2d", (float) x, (float) y); + + x += wTile; + if (x >= tileCountH * wTile) { + x = xOrigin; + y += hTile; + } + } + } + } + } +} diff --git a/io-vector/src/test/java/org/xbib/graphics/io/visual/ImageTest.java b/io-vector/src/test/java/org/xbib/graphics/io/visual/ImageTest.java new file mode 100644 index 0000000..21d9ae6 --- /dev/null +++ b/io-vector/src/test/java/org/xbib/graphics/io/visual/ImageTest.java @@ -0,0 +1,30 @@ +package org.xbib.graphics.io.visual; + +import java.awt.Color; +import java.awt.GradientPaint; +import java.awt.Graphics2D; +import java.awt.geom.Point2D; +import java.awt.geom.Rectangle2D; +import java.awt.image.BufferedImage; +import java.io.IOException; + +public class ImageTest extends AbstractTest { + + public ImageTest() throws IOException { + } + + @Override + public void draw(Graphics2D g) { + BufferedImage image = new BufferedImage(4, 3, BufferedImage.TYPE_INT_ARGB); + Graphics2D gImage = (Graphics2D) image.getGraphics(); + gImage.setPaint(new GradientPaint( + new Point2D.Double(0.0, 0.0), Color.RED, + new Point2D.Double(3.0, 2.0), Color.BLUE) + ); + gImage.fill(new Rectangle2D.Double(0.0, 0.0, 4.0, 3.0)); + g.drawImage(image, 0, 0, (int) getPageSize().getWidth(), (int) (0.5 * getPageSize().getHeight()), null); + g.rotate(-10.0 / 180.0 * Math.PI, 2.0, 1.5); + g.drawImage(image, (int) (0.1 * getPageSize().getWidth()), (int) (0.6 * getPageSize().getHeight()), + (int) (0.33 * getPageSize().getWidth()), (int) (0.33 * getPageSize().getHeight()), null); + } +} diff --git a/io-vector/src/test/java/org/xbib/graphics/io/visual/PaintTest.java b/io-vector/src/test/java/org/xbib/graphics/io/visual/PaintTest.java new file mode 100644 index 0000000..2271cba --- /dev/null +++ b/io-vector/src/test/java/org/xbib/graphics/io/visual/PaintTest.java @@ -0,0 +1,40 @@ +package org.xbib.graphics.io.visual; + +import java.awt.Color; +import java.awt.GradientPaint; +import java.awt.Graphics2D; +import java.awt.geom.AffineTransform; +import java.awt.geom.Rectangle2D; +import java.io.IOException; + +public class PaintTest extends AbstractTest { + + public PaintTest() throws IOException { + } + + @Override + public void draw(Graphics2D g) { + // Draw multiple rotated rectangles + final int steps = 25; + final int cols = 5; + final int rows = steps / cols; + final double tileWidth = getPageSize().getWidth() / cols; + final double tileHeight = getPageSize().getHeight() / rows; + g.translate(tileWidth / 2, tileHeight / 2); + final double rectWidth = tileWidth * 0.8; + final double rectHeight = tileHeight * 0.8; + Rectangle2D rect = new Rectangle2D.Double(-rectWidth / 2, -rectHeight / 2, rectWidth, rectHeight); + g.setPaint(new GradientPaint(0f, (float) (-rectHeight / 2), Color.RED, 0f, (float) (rectHeight / 2), Color.BLUE)); + for (int i = 0; i < steps; i++) { + AffineTransform txOld = g.getTransform(); + AffineTransform tx = new AffineTransform(txOld); + int col = i % 5; + int row = i / 5; + tx.translate(col * tileWidth, row * tileHeight); + tx.rotate(i * Math.toRadians(360.0 / steps)); + g.setTransform(tx); + g.fill(rect); + g.setTransform(txOld); + } + } +} diff --git a/io-vector/src/test/java/org/xbib/graphics/io/visual/ShapesTest.java b/io-vector/src/test/java/org/xbib/graphics/io/visual/ShapesTest.java new file mode 100644 index 0000000..446790b --- /dev/null +++ b/io-vector/src/test/java/org/xbib/graphics/io/visual/ShapesTest.java @@ -0,0 +1,131 @@ +package org.xbib.graphics.io.visual; + +import java.awt.Graphics2D; +import java.awt.Polygon; +import java.awt.geom.AffineTransform; +import java.awt.geom.Arc2D; +import java.awt.geom.CubicCurve2D; +import java.awt.geom.Ellipse2D; +import java.awt.geom.Line2D; +import java.awt.geom.Path2D; +import java.awt.geom.QuadCurve2D; +import java.awt.geom.Rectangle2D; +import java.awt.geom.RoundRectangle2D; +import java.io.IOException; + +public class ShapesTest extends AbstractTest { + + public ShapesTest() throws IOException { + } + + @Override + public void draw(Graphics2D g) { + final int tileCountH = 4; + final int tileCountV = 6; + final double wTile = getPageSize().getWidth() / tileCountH; + final double hTile = getPageSize().getHeight() / tileCountV; + final double xOrigin = (getPageSize().getWidth() - tileCountH * wTile) / 2.0; + final double yOrigin = (getPageSize().getHeight() - tileCountV * hTile) / 2.0; + double x = xOrigin; + double y = yOrigin; + + g.draw(new Line2D.Double(x, y, x + 0.8 * wTile, y + 0.6 * hTile)); + x += wTile; + g.draw(new QuadCurve2D.Double(x, y, x + 0.8 * wTile, y, x + 0.8 * wTile, y + 0.6 * hTile)); + x += wTile; + g.draw(new CubicCurve2D.Double(x, y, x + 0.8 * wTile, y, x, y + 0.6 * hTile, x + 0.8 * wTile, y + 0.6 * hTile)); + + x = xOrigin; + y += hTile; + g.fill(new Rectangle2D.Double(x, y, 0.8 * wTile, 0.6 * hTile)); + x += wTile; + g.draw(new Rectangle2D.Double(x, y, 0.8 * wTile, 0.6 * hTile)); + x += wTile; + + g.fill(new RoundRectangle2D.Double(x, y, 0.8 * wTile, 0.6 * hTile, 0.2 * wTile, 0.2 * hTile)); + x += wTile; + g.draw(new RoundRectangle2D.Double(x, y, 0.8 * wTile, 0.6 * hTile, 0.2 * wTile, 0.2 * hTile)); + x += wTile; + + + x = xOrigin; + y += hTile; + g.fill(new Ellipse2D.Double(x, y, 0.8 * wTile, 0.6 * hTile)); + x += wTile; + g.draw(new Ellipse2D.Double(x, y, 0.8 * wTile, 0.6 * hTile)); + x += wTile; + + g.fill(new Polygon( + new int[]{(int) (x), (int) (x + 0.8 * wTile / 2.0), (int) (x + 0.8 * wTile)}, + new int[]{(int) (y + 0.6 * hTile), (int) (y), (int) (y + 0.6 * hTile)}, + 3 + )); + x += wTile; + g.draw(new Polygon( + new int[]{(int) (x), (int) (x + 0.8 * wTile / 2.0), (int) (x + 0.8 * wTile)}, + new int[]{(int) (y + 0.6 * hTile), (int) (y), (int) (y + 0.6 * hTile)}, + 3 + )); + + + x = xOrigin; + y += hTile; + g.fill(new Arc2D.Double(x, y, 0.8 * wTile, 0.6 * hTile, 110, 320, Arc2D.PIE)); + x += wTile; + g.draw(new Arc2D.Double(x, y, 0.8 * wTile, 0.6 * hTile, 110, 320, Arc2D.PIE)); + x += wTile; + g.fill(new Arc2D.Double(x, y, 0.6 * hTile, 0.8 * wTile, 10, 320, Arc2D.CHORD)); + x += wTile; + g.draw(new Arc2D.Double(x, y, 0.6 * hTile, 0.8 * wTile, 10, 320, Arc2D.CHORD)); + + + x = xOrigin; + y += hTile; + g.fill(new Arc2D.Double(x, y, 0.6 * hTile, 0.8 * wTile, 10, 320, Arc2D.OPEN)); + x += wTile; + g.draw(new Arc2D.Double(x, y, 0.6 * hTile, 0.8 * wTile, 10, 320, Arc2D.OPEN)); + x += wTile; + g.fill(new Arc2D.Double(x, y, 0.6 * hTile, 0.8 * wTile, 10, 320, Arc2D.PIE)); + x += wTile; + g.draw(new Arc2D.Double(x, y, 0.6 * hTile, 0.8 * wTile, 10, 320, Arc2D.PIE)); + + + x = xOrigin; + y += hTile; + + final Path2D path1 = new Path2D.Double(); + path1.moveTo(0.00, 0.00); + path1.lineTo(0.33, 1.00); + path1.lineTo(0.67, 0.00); + path1.quadTo(0.33, 0.00, 0.33, 0.50); + path1.quadTo(0.33, 1.00, 0.67, 1.00); + path1.quadTo(1.00, 1.00, 1.00, 0.50); + path1.lineTo(0.67, 0.50); + path1.transform(AffineTransform.getScaleInstance(0.8 * wTile, 0.6 * hTile)); + + path1.transform(AffineTransform.getTranslateInstance(x, y)); + g.fill(path1); + x += wTile; + path1.transform(AffineTransform.getTranslateInstance(wTile, 0.0)); + g.draw(path1); + x += wTile; + + final Path2D path2 = new Path2D.Double(); + path2.moveTo(0.0, 0.4); + path2.curveTo(0.0, 0.3, 0.0, 0.0, 0.2, 0.0); + path2.curveTo(0.3, 0.0, 0.4, 0.1, 0.4, 0.3); + path2.curveTo(0.4, 0.5, 0.2, 0.8, 0.0, 1.0); + path2.lineTo(0.6, 1.0); + path2.lineTo(0.6, 0.0); + path2.curveTo(0.8, 0.0, 1.0, 0.2, 1.0, 0.5); + path2.curveTo(1.0, 0.6, 1.0, 0.8, 0.9, 0.9); + path2.closePath(); + path2.transform(AffineTransform.getScaleInstance(0.8 * wTile, 0.6 * hTile)); + + path2.transform(AffineTransform.getTranslateInstance(x, y)); + g.fill(path2); + x += wTile; + path2.transform(AffineTransform.getTranslateInstance(wTile, 0.0)); + g.draw(path2); + } +} diff --git a/io-vector/src/test/java/org/xbib/graphics/io/visual/StrokeTest.java b/io-vector/src/test/java/org/xbib/graphics/io/visual/StrokeTest.java new file mode 100644 index 0000000..df0be49 --- /dev/null +++ b/io-vector/src/test/java/org/xbib/graphics/io/visual/StrokeTest.java @@ -0,0 +1,94 @@ +package org.xbib.graphics.io.visual; + +import java.awt.BasicStroke; +import java.awt.Graphics2D; +import java.awt.Stroke; +import java.awt.geom.AffineTransform; +import java.awt.geom.Path2D; +import java.io.IOException; + +public class StrokeTest extends AbstractTest { + + private static final Stroke[] strokes = { + // Width + new BasicStroke(0.0f), + new BasicStroke(0.5f), + new BasicStroke(1.0f), + new BasicStroke(2.0f), + // Cap + new BasicStroke(1f, BasicStroke.CAP_BUTT, BasicStroke.JOIN_MITER), + new BasicStroke(1f, BasicStroke.CAP_ROUND, BasicStroke.JOIN_MITER), + new BasicStroke(1f, BasicStroke.CAP_SQUARE, BasicStroke.JOIN_MITER), + null, + // Join + new BasicStroke(1f, BasicStroke.CAP_BUTT, BasicStroke.JOIN_BEVEL), + new BasicStroke(1f, BasicStroke.CAP_BUTT, BasicStroke.JOIN_MITER), + new BasicStroke(1f, BasicStroke.CAP_BUTT, BasicStroke.JOIN_ROUND), + null, + // Miter limit + new BasicStroke(1f, BasicStroke.CAP_BUTT, BasicStroke.JOIN_MITER, 1f), + new BasicStroke(1f, BasicStroke.CAP_BUTT, BasicStroke.JOIN_MITER, 2f), + new BasicStroke(1f, BasicStroke.CAP_BUTT, BasicStroke.JOIN_MITER, 3f), + new BasicStroke(1f, BasicStroke.CAP_BUTT, BasicStroke.JOIN_MITER, 10f), + // Dash pattern + new BasicStroke(1f, BasicStroke.CAP_BUTT, BasicStroke.JOIN_MITER, 10f, new float[]{1f}, 0f), + new BasicStroke(1f, BasicStroke.CAP_BUTT, BasicStroke.JOIN_MITER, 10f, new float[]{1f, 1f}, 0f), + new BasicStroke(1f, BasicStroke.CAP_BUTT, BasicStroke.JOIN_MITER, 10f, new float[]{3f, 1f, 1f}, 0f), + new BasicStroke(1f, BasicStroke.CAP_BUTT, BasicStroke.JOIN_MITER, 10f, new float[]{3f, 1f, 4f, 1f}, 0f), + // Dash phase + new BasicStroke(1f, BasicStroke.CAP_BUTT, BasicStroke.JOIN_MITER, 10f, new float[]{3f, 1f}, 0.5f), + new BasicStroke(1f, BasicStroke.CAP_BUTT, BasicStroke.JOIN_MITER, 10f, new float[]{3f, 1f}, 1.0f), + new BasicStroke(1f, BasicStroke.CAP_BUTT, BasicStroke.JOIN_MITER, 10f, new float[]{3f, 1f}, 1.5f), + new BasicStroke(1f, BasicStroke.CAP_BUTT, BasicStroke.JOIN_MITER, 10f, new float[]{3f, 1f}, 2.5f), + }; + + public StrokeTest() throws IOException { + } + + @Override + public void draw(Graphics2D g) { + final int tileCountH = 4; + final int tileCountV = 6; + final double wTile = getPageSize().getWidth() / tileCountH; + final double hTile = getPageSize().getHeight() / tileCountV; + final double xOrigin = (getPageSize().getWidth() - tileCountH * wTile) / 2.0; + final double yOrigin = (getPageSize().getHeight() - tileCountV * hTile) / 2.0; + + final Path2D path = new Path2D.Double(); + path.moveTo(0.00, 0.00); + path.lineTo(0.33, 1.00); + path.lineTo(0.67, 0.00); + path.quadTo(0.33, 0.00, 0.33, 0.50); + path.quadTo(0.33, 1.00, 0.67, 1.00); + path.quadTo(1.00, 1.00, 1.00, 0.50); + path.lineTo(0.67, 0.50); + path.moveTo(1.0, 0.4); + path.curveTo(1.0, 0.3, 1.0, 0.0, 1.2, 0.0); + path.curveTo(1.3, 0.0, 1.4, 0.1, 1.4, 0.3); + path.curveTo(1.4, 0.5, 1.2, 0.8, 1.0, 1.0); + path.lineTo(1.6, 1.0); + path.lineTo(1.6, 0.0); + path.curveTo(1.8, 0.0, 2.0, 0.2, 2.0, 0.5); + path.curveTo(2.0, 0.6, 2.0, 0.8, 1.9, 0.9); + + path.transform(AffineTransform.getScaleInstance(0.8 * wTile / 2.0, 0.6 * hTile)); + + double x = xOrigin; + double y = yOrigin; + for (Stroke stroke : strokes) { + if (stroke != null) { + Path2D p = new Path2D.Double(path); + p.transform(AffineTransform.getTranslateInstance(x, y)); + + g.setStroke(stroke); + g.draw(p); + } + + x += wTile; + if (x >= tileCountH * wTile) { + x = xOrigin; + y += hTile; + } + } + } +} diff --git a/io-vector/src/test/java/org/xbib/graphics/io/visual/SwingExportTest.java b/io-vector/src/test/java/org/xbib/graphics/io/visual/SwingExportTest.java new file mode 100644 index 0000000..f23f5a3 --- /dev/null +++ b/io-vector/src/test/java/org/xbib/graphics/io/visual/SwingExportTest.java @@ -0,0 +1,27 @@ +package org.xbib.graphics.io.visual; + +import java.awt.BorderLayout; +import java.awt.Graphics2D; +import java.io.IOException; +import javax.swing.JButton; +import javax.swing.JFrame; +import javax.swing.JSlider; + +public class SwingExportTest extends AbstractTest { + + public SwingExportTest() throws IOException { + } + + @Override + public void draw(Graphics2D g) { + JFrame frame = new JFrame(); + frame.getContentPane().add(new JButton("Hello Swing!"), BorderLayout.CENTER); + frame.getContentPane().add(new JSlider(), BorderLayout.NORTH); + frame.setSize(200, 250); + //g.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON); + frame.setVisible(true); + frame.printAll(g); + frame.setVisible(false); + frame.dispose(); + } +} diff --git a/io-vector/src/test/java/org/xbib/graphics/io/visual/TestBrowser.java b/io-vector/src/test/java/org/xbib/graphics/io/visual/TestBrowser.java new file mode 100644 index 0000000..0a8ab8b --- /dev/null +++ b/io-vector/src/test/java/org/xbib/graphics/io/visual/TestBrowser.java @@ -0,0 +1,253 @@ +package org.xbib.graphics.io.visual; + +import java.awt.BorderLayout; +import java.awt.Component; +import java.awt.event.ItemEvent; +import java.awt.event.ItemListener; +import java.awt.image.BufferedImage; +import java.io.File; +import java.io.FileOutputStream; +import java.io.IOException; +import java.io.InputStream; +import java.util.ArrayList; +import java.util.List; +import javax.swing.Box; +import javax.swing.BoxLayout; +import javax.swing.DefaultListCellRenderer; +import javax.swing.ImageIcon; +import javax.swing.JButton; +import javax.swing.JComboBox; +import javax.swing.JComponent; +import javax.swing.JFileChooser; +import javax.swing.JFrame; +import javax.swing.JLabel; +import javax.swing.JList; +import javax.swing.JPanel; +import javax.swing.JSplitPane; +import javax.swing.ListSelectionModel; +import javax.swing.WindowConstants; + +@SuppressWarnings("serial") +public class TestBrowser extends JFrame { + + private final List testCases; + + private final ImageComparisonPanel imageComparisonPanel; + + private AbstractTest testCase; + + public TestBrowser() throws IOException { + super("Test browser"); + setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE); + setSize(1024, 768); + testCases = new ArrayList<>(); + testCases.add(new ColorTest()); + testCases.add(new StrokeTest()); + testCases.add(new ShapesTest()); + testCases.add(new FontTest()); + testCases.add(new CharacterTest()); + testCases.add(new EmptyFileTest()); + testCases.add(new ImageTest()); + testCases.add(new ClippingTest()); + testCases.add(new PaintTest()); + testCases.add(new SwingExportTest()); + testCases.add(new TransformTest()); + final JList testList = new JList<>(testCases.toArray()); + testList.setSelectionMode(ListSelectionModel.SINGLE_SELECTION); + testList.setCellRenderer(new DefaultListCellRenderer() { + @Override + public Component getListCellRendererComponent(JList list, Object value, int index, boolean isSelected, boolean cellHasFocus) { + String testName = value.getClass().getSimpleName(); + return super.getListCellRendererComponent(list, testName, index, isSelected, cellHasFocus); + } + }); + testList.addListSelectionListener(e -> { + if (!e.getValueIsAdjusting()) { + int index = testList.getSelectedIndex(); + if (index < 0) { + return; + } + AbstractTest test = testCases.get(index); + testCase = test; + setTestCase(test); + } + }); + getContentPane().add(testList, BorderLayout.WEST); + + JPanel configurableImageComparisonPanel = new JPanel(new BorderLayout()); + getContentPane().add(configurableImageComparisonPanel, BorderLayout.CENTER); + + ImageFormat startingImageFormat = ImageFormat.EPS; + JComboBox imageFormatSelector = new JComboBox<>(ImageFormat.values()); + configurableImageComparisonPanel.add(imageFormatSelector, BorderLayout.NORTH); + imageFormatSelector.setSelectedItem(startingImageFormat); + imageFormatSelector.addItemListener(new ItemListener() { + @Override + public void itemStateChanged(ItemEvent itemEvent) { + ImageFormat format = (ImageFormat) itemEvent.getItem(); + imageComparisonPanel.setImageFormat(format); + AbstractTest test = getTestCase(); + if (test != null) { + setTestCase(test); + } + } + }); + + imageComparisonPanel = new ImageComparisonPanel(startingImageFormat); + configurableImageComparisonPanel.add(imageComparisonPanel, BorderLayout.CENTER); + } + + public static void main(String[] args) throws Exception { + new TestBrowser().setVisible(true); + } + + public AbstractTest getTestCase() { + return testCase; + } + + public void setTestCase(AbstractTest test) { + BufferedImage reference = test.getReference(); + imageComparisonPanel.setLeftComponent(new ImageDisplayPanel(reference, null)); + ImageDisplayPanel imageDisplayPanel; + switch (imageComparisonPanel.getImageFormat()) { + case EPS: + imageDisplayPanel = new ImageDisplayPanel(null, test.getEPS()); + imageComparisonPanel.setRightComponent(imageDisplayPanel); + break; + case PDF: + imageDisplayPanel = new ImageDisplayPanel(null, test.getPDF()); + imageComparisonPanel.setRightComponent(imageDisplayPanel); + break; + case SVG: + imageDisplayPanel = new ImageDisplayPanel(null, test.getSVG()); + imageComparisonPanel.setRightComponent(imageDisplayPanel); + break; + default: + throw new IllegalArgumentException("Unknown image format: " + imageComparisonPanel.getImageFormat()); + } + } + + private enum ImageFormat { + EPS("EPS"), + PDF("PDF"), + SVG("SVG"); + + private final String name; + + ImageFormat(String name) { + this.name = name; + } + + public String getName() { + return name; + } + } + + private static class ImageComparisonPanel extends Box { + + private final Box leftPanel; + + private final Box rightPanel; + + private ImageFormat imageFormat; + + private JComponent leftComponent; + + private JComponent rightComponent; + + public ImageComparisonPanel(ImageFormat imageFormat) { + super(BoxLayout.PAGE_AXIS); + + this.imageFormat = imageFormat; + + JSplitPane splitPane = new JSplitPane(JSplitPane.HORIZONTAL_SPLIT); + splitPane.setResizeWeight(0.5); + add(splitPane); + + leftPanel = new Box(BoxLayout.PAGE_AXIS); + leftPanel.add(new JLabel("Graphics2D")); + splitPane.setTopComponent(leftPanel); + + rightPanel = new Box(BoxLayout.PAGE_AXIS); + rightPanel.add(new JLabel(imageFormat.getName())); + splitPane.setBottomComponent(rightPanel); + } + + public void setLeftComponent(JComponent leftComponent) { + if (this.leftComponent != null) { + leftPanel.remove(this.leftComponent); + } + this.leftComponent = leftComponent; + leftPanel.add(leftComponent); + leftPanel.revalidate(); + leftPanel.repaint(); + } + + public void setRightComponent(JComponent rightComponent) { + if (this.rightComponent != null) { + rightPanel.remove(this.rightComponent); + } + this.rightComponent = rightComponent; + rightPanel.add(rightComponent); + rightPanel.revalidate(); + rightPanel.repaint(); + } + + public ImageFormat getImageFormat() { + return imageFormat; + } + + public void setImageFormat(ImageFormat imageFormat) { + this.imageFormat = imageFormat; + JLabel imageFormatLabel = (JLabel) rightPanel.getComponent(0); + imageFormatLabel.setText(imageFormat.getName()); + imageFormatLabel.repaint(); + } + } + + private static class ImageDisplayPanel extends JPanel { + private final InputStream imageData; + + public ImageDisplayPanel(BufferedImage renderedImage, InputStream imageData) { + super(new BorderLayout()); + this.imageData = imageData; + if (renderedImage != null) { + JLabel imageLabel = new JLabel(new ImageIcon(renderedImage)); + add(imageLabel, BorderLayout.CENTER); + } + JButton saveToFileButton = new JButton("Save as..."); + if (imageData == null) { + saveToFileButton.setEnabled(false); + } + saveToFileButton.addActionListener(e -> { + JFileChooser saveFileDialog = new JFileChooser(); + saveFileDialog.setFileSelectionMode(JFileChooser.FILES_ONLY); + saveFileDialog.setMultiSelectionEnabled(false); + int userChoice = saveFileDialog.showSaveDialog(ImageDisplayPanel.this); + if (userChoice != JFileChooser.APPROVE_OPTION) { + return; + } + File dest = saveFileDialog.getSelectedFile(); + FileOutputStream destStream = null; + try { + destStream = new FileOutputStream(dest); + int imageDataChunk; + while ((imageDataChunk = ImageDisplayPanel.this.imageData.read()) != -1) { + destStream.write(imageDataChunk); + } + } catch (IOException e1) { + e1.printStackTrace(); + } finally { + if (destStream != null) { + try { + destStream.close(); + } catch (IOException e1) { + e1.printStackTrace(); + } + } + } + }); + add(saveToFileButton, BorderLayout.SOUTH); + } + } +} diff --git a/io-vector/src/test/java/org/xbib/graphics/io/visual/TransformTest.java b/io-vector/src/test/java/org/xbib/graphics/io/visual/TransformTest.java new file mode 100644 index 0000000..ea4b19a --- /dev/null +++ b/io-vector/src/test/java/org/xbib/graphics/io/visual/TransformTest.java @@ -0,0 +1,63 @@ +package org.xbib.graphics.io.visual; + +import java.awt.Graphics2D; +import java.awt.Shape; +import java.awt.geom.AffineTransform; +import java.awt.geom.Rectangle2D; +import java.io.IOException; + +public class TransformTest extends AbstractTest { + + public TransformTest() throws IOException { + } + + @Override + public void draw(Graphics2D g) { + final int rowCount = 2; + final int colCount = 4; + double wTile = getPageSize().getWidth() / colCount; + double hTile = wTile; + + g.translate(0.5 * wTile, 0.5 * hTile); + AffineTransform txOrig = g.getTransform(); + + Shape s = new Rectangle2D.Double(0.0, 0.0, 0.5 * wTile, 0.75 * hTile); + + // Row 1 + + g.draw(s); + + g.translate(wTile, 0.0); + g.draw(s); + + g.translate(wTile, 0.0); + { + Graphics2D g2 = (Graphics2D) g.create(); + g2.scale(0.5, 0.5); + g2.draw(s); + g2.dispose(); + } + + g.translate(wTile, 0.0); + { + Graphics2D g2 = (Graphics2D) g.create(); + g2.rotate(Math.toRadians(30.0)); + g2.draw(s); + g2.dispose(); + } + + // Row 2 + + g.setTransform(txOrig); + g.translate(0.0, hTile); + + g.shear(0.5, 0.0); + g.draw(s); + g.shear(-0.5, 0.0); + g.translate(wTile, 0.0); + + g.shear(0.0, 0.5); + g.draw(s); + g.shear(0.0, -0.5); + } +} diff --git a/layout-pdfbox/NOTICE.txt b/layout-pdfbox/NOTICE.txt new file mode 100644 index 0000000..076cb25 --- /dev/null +++ b/layout-pdfbox/NOTICE.txt @@ -0,0 +1,7 @@ +This work is based on + +https://github.com/ralfstuckert/pdfbox-layout + +(MIT License) + +as of October, 2020 \ No newline at end of file diff --git a/layout-pdfbox/build.gradle b/layout-pdfbox/build.gradle new file mode 100644 index 0000000..7a11b87 --- /dev/null +++ b/layout-pdfbox/build.gradle @@ -0,0 +1,3 @@ +dependencies { + implementation "org.apache.pdfbox:pdfbox:${project.property('pdfbox.version')}" +} \ No newline at end of file diff --git a/layout-pdfbox/src/main/java/org/xbib/graphics/layout/pdfbox/elements/ControlElement.java b/layout-pdfbox/src/main/java/org/xbib/graphics/layout/pdfbox/elements/ControlElement.java new file mode 100644 index 0000000..fc52e3b --- /dev/null +++ b/layout-pdfbox/src/main/java/org/xbib/graphics/layout/pdfbox/elements/ControlElement.java @@ -0,0 +1,25 @@ +package org.xbib.graphics.layout.pdfbox.elements; + +/** + * ControlElements do not have a drawable representation, but control page flow, + * rendering etc. + */ +public class ControlElement implements Element { + + /** + * Triggers a new page in a document. + */ + public final static ControlElement NEWPAGE = new ControlElement("NEWPAGE"); + + private final String name; + + public ControlElement(final String name) { + this.name = name; + } + + @Override + public String toString() { + return "ControlElement [NEWPAGE=" + NEWPAGE + ", name=" + name + "]"; + } + +} diff --git a/layout-pdfbox/src/main/java/org/xbib/graphics/layout/pdfbox/elements/Cutter.java b/layout-pdfbox/src/main/java/org/xbib/graphics/layout/pdfbox/elements/Cutter.java new file mode 100644 index 0000000..6a2d2b3 --- /dev/null +++ b/layout-pdfbox/src/main/java/org/xbib/graphics/layout/pdfbox/elements/Cutter.java @@ -0,0 +1,63 @@ +package org.xbib.graphics.layout.pdfbox.elements; + +import org.apache.pdfbox.pdmodel.PDDocument; +import org.apache.pdfbox.pdmodel.PDPageContentStream; +import org.xbib.graphics.layout.pdfbox.text.DrawListener; +import org.xbib.graphics.layout.pdfbox.text.Position; +import java.io.IOException; + +/** + * A cutter transforms any Drawable element into a {@link Dividable}. It simply + * cuts the drawable vertically into pieces matching the target height. + */ +public class Cutter implements Dividable, Drawable { + + private final Drawable undividable; + private final float viewPortY; + private final float viewPortHeight; + + public Cutter(Drawable undividableElement) throws IOException { + this(undividableElement, 0, undividableElement.getHeight()); + } + + protected Cutter(Drawable undividable, float viewPortY, float viewPortHeight) { + this.undividable = undividable; + this.viewPortY = viewPortY; + this.viewPortHeight = viewPortHeight; + } + + @Override + public Divided divide(float remainingHeight, final float pageHeight) { + return new Divided(new Cutter(undividable, viewPortY, remainingHeight), + new Cutter(undividable, viewPortY - remainingHeight, + viewPortHeight - remainingHeight)); + } + + @Override + public float getWidth() throws IOException { + return undividable.getWidth(); + } + + @Override + public float getHeight() throws IOException { + return viewPortHeight; + } + + @Override + public Position getAbsolutePosition() { + return null; + } + + @Override + public void draw(PDDocument pdDocument, PDPageContentStream contentStream, + Position upperLeft, DrawListener drawListener) throws IOException { + Position viewPortOrigin = upperLeft.add(0, -viewPortY); + undividable.draw(pdDocument, contentStream, viewPortOrigin, drawListener); + } + + @Override + public Drawable removeLeadingEmptyVerticalSpace() throws IOException { + return new Cutter(undividable.removeLeadingEmptyVerticalSpace()); + } + +} diff --git a/layout-pdfbox/src/main/java/org/xbib/graphics/layout/pdfbox/elements/Dimension.java b/layout-pdfbox/src/main/java/org/xbib/graphics/layout/pdfbox/elements/Dimension.java new file mode 100644 index 0000000..27a2b49 --- /dev/null +++ b/layout-pdfbox/src/main/java/org/xbib/graphics/layout/pdfbox/elements/Dimension.java @@ -0,0 +1,57 @@ +package org.xbib.graphics.layout.pdfbox.elements; + +/** + * In order to avoid dependencies to AWT, we use our own Dimension class here. + */ +public class Dimension { + + private final float width; + private final float height; + + public Dimension(float width, float height) { + super(); + this.width = width; + this.height = height; + } + + public float getWidth() { + return width; + } + + public float getHeight() { + return height; + } + + @Override + public String toString() { + return "Dimension [width=" + width + ", height=" + height + "]"; + } + + @Override + public int hashCode() { + final int prime = 31; + int result = 1; + result = prime * result + Float.floatToIntBits(height); + result = prime * result + Float.floatToIntBits(width); + return result; + } + + @Override + public boolean equals(Object obj) { + if (this == obj) { + return true; + } + if (obj == null) { + return false; + } + if (getClass() != obj.getClass()) { + return false; + } + Dimension other = (Dimension) obj; + if (Float.floatToIntBits(height) != Float.floatToIntBits(other.height)) { + return false; + } + return Float.floatToIntBits(width) == Float.floatToIntBits(other.width); + } + +} diff --git a/layout-pdfbox/src/main/java/org/xbib/graphics/layout/pdfbox/elements/Dividable.java b/layout-pdfbox/src/main/java/org/xbib/graphics/layout/pdfbox/elements/Dividable.java new file mode 100644 index 0000000..5f6c9b6 --- /dev/null +++ b/layout-pdfbox/src/main/java/org/xbib/graphics/layout/pdfbox/elements/Dividable.java @@ -0,0 +1,51 @@ +package org.xbib.graphics.layout.pdfbox.elements; + +import java.io.IOException; + +/** + * If a drawable is marked as {@link Dividable}, it can be (vertically) divided + * in case it does not fit on the (remaining) page. + */ +public interface Dividable { + + /** + * Divides the drawable vetically into pieces where the first part is to + * respect the given remaining height. The page height allows to make better + * decisions on how to divide best. + * + * @param remainingHeight the remaining height on the page dictating the height of the + * first part. + * @param nextPageHeight the height of the next page allows to make better decisions on + * how to divide best, e.g. maybe the element fits completely on + * the next page. + * @return the Divided containing the first part and the tail. + * @throws IOException by pdfbox. + */ + Divided divide(final float remainingHeight, final float nextPageHeight) + throws IOException; + + /** + * A container for the result of a {@link Dividable#divide(float, float)} + * operation. + */ + class Divided { + + private final Drawable first; + private final Drawable tail; + + public Divided(Drawable first, Drawable tail) { + this.first = first; + this.tail = tail; + } + + public Drawable getFirst() { + return first; + } + + public Drawable getTail() { + return tail; + } + + } + +} diff --git a/layout-pdfbox/src/main/java/org/xbib/graphics/layout/pdfbox/elements/Document.java b/layout-pdfbox/src/main/java/org/xbib/graphics/layout/pdfbox/elements/Document.java new file mode 100644 index 0000000..83fb1a1 --- /dev/null +++ b/layout-pdfbox/src/main/java/org/xbib/graphics/layout/pdfbox/elements/Document.java @@ -0,0 +1,364 @@ +package org.xbib.graphics.layout.pdfbox.elements; + +import org.apache.pdfbox.pdmodel.PDDocument; +import org.apache.pdfbox.pdmodel.common.PDRectangle; +import org.xbib.graphics.layout.pdfbox.elements.render.Layout; +import org.xbib.graphics.layout.pdfbox.elements.render.LayoutHint; +import org.xbib.graphics.layout.pdfbox.elements.render.RenderContext; +import org.xbib.graphics.layout.pdfbox.elements.render.RenderListener; +import org.xbib.graphics.layout.pdfbox.elements.render.Renderer; +import org.xbib.graphics.layout.pdfbox.elements.render.VerticalLayout; +import org.xbib.graphics.layout.pdfbox.elements.render.VerticalLayoutHint; +import java.io.File; +import java.io.FileOutputStream; +import java.io.IOException; +import java.io.OutputStream; +import java.util.AbstractMap.SimpleEntry; +import java.util.ArrayList; +import java.util.Iterator; +import java.util.List; +import java.util.Map.Entry; +import java.util.concurrent.CopyOnWriteArrayList; + +/** + * The central class for creating a document. + */ +public class Document implements RenderListener { + + /** + * A4 portrait without margins. + */ + public final static PageFormat DEFAULT_PAGE_FORMAT = new PageFormat(); + + private final List> elements = new ArrayList<>(); + private final List customRenderer = new CopyOnWriteArrayList(); + private final List renderListener = new CopyOnWriteArrayList(); + + private PDDocument pdDocument; + private final PageFormat pageFormat; + + /** + * Creates a Document using the {@link #DEFAULT_PAGE_FORMAT}. + */ + public Document() { + this(DEFAULT_PAGE_FORMAT); + } + + /** + * Creates a Document in A4 with orientation portrait and the given margins. + * By default, a {@link VerticalLayout} is used. + * + * @param marginLeft the left margin + * @param marginRight the right margin + * @param marginTop the top margin + * @param marginBottom the bottom margin + */ + public Document(float marginLeft, float marginRight, float marginTop, + float marginBottom) { + this(PageFormat.with() + .margins(marginLeft, marginRight, marginTop, marginBottom) + .build()); + } + + /** + * Creates a Document based on the given media box. By default, a + * {@link VerticalLayout} is used. + * + * @param mediaBox the media box to use. + * @deprecated use {@link #Document(PageFormat)} instead. + */ + @Deprecated + public Document(PDRectangle mediaBox) { + this(mediaBox, 0, 0, 0, 0); + } + + /** + * Creates a Document based on the given media box and margins. By default, + * a {@link VerticalLayout} is used. + * + * @param mediaBox the media box to use. + * @param marginLeft the left margin + * @param marginRight the right margin + * @param marginTop the top margin + * @param marginBottom the bottom margin + * @deprecated use {@link #Document(PageFormat)} instead. + */ + @Deprecated + public Document(PDRectangle mediaBox, float marginLeft, float marginRight, + float marginTop, float marginBottom) { + this(new PageFormat(mediaBox, Orientation.Portrait, marginLeft, + marginRight, marginTop, marginBottom)); + } + + /** + * Creates a Document based on the given page format. By default, a + * {@link VerticalLayout} is used. + * + * @param pageFormat the page format box to use. + */ + public Document(final PageFormat pageFormat) { + this.pageFormat = pageFormat; + } + + /** + * Adds an element to the document using a {@link VerticalLayoutHint}. + * + * @param element the element to add + */ + public void add(final Element element) { + add(element, new VerticalLayoutHint()); + } + + /** + * Adds an element with the given layout hint. + * + * @param element the element to add + * @param layoutHint the hint for the {@link Layout}. + */ + public void add(final Element element, final LayoutHint layoutHint) { + elements.add(createEntry(element, layoutHint)); + } + + private Entry createEntry(final Element element, + final LayoutHint layoutHint) { + return new SimpleEntry(element, layoutHint); + } + + /** + * Removes the given element. + * + * @param element the element to remove. + */ + public void remove(final Element element) { + elements.remove(element); + } + + /** + * @return the page format to use as default. + */ + public PageFormat getPageFormat() { + return pageFormat; + } + + /** + * @return the left document margin. + * @deprecated use {@link #getPageFormat()} instead. + */ + @Deprecated + public float getMarginLeft() { + return getPageFormat().getMarginLeft(); + } + + /** + * @return the right document margin. + * @deprecated use {@link #getPageFormat()} instead. + */ + @Deprecated + public float getMarginRight() { + return getPageFormat().getMarginRight(); + } + + /** + * @return the top document margin. + * @deprecated use {@link #getPageFormat()} instead. + */ + @Deprecated + public float getMarginTop() { + return getPageFormat().getMarginTop(); + } + + /** + * @return the bottom document margin. + * @deprecated use {@link #getPageFormat()} instead. + */ + @Deprecated + public float getMarginBottom() { + return getPageFormat().getMarginBottom(); + } + + /** + * @return the media box to use. + * @deprecated use {@link #getPageFormat()} instead. + */ + @Deprecated + public PDRectangle getMediaBox() { + return getPageFormat().getMediaBox(); + } + + /** + * @return the orientation to use. + * @deprecated use {@link #getPageFormat()} instead. + */ + @Deprecated + public Orientation getOrientation() { + return getPageFormat().getOrientation(); + } + + /** + * @return the media box width minus margins. + */ + public float getPageWidth() { + return getMediaBox().getWidth() - getMarginLeft() - getMarginRight(); + } + + /** + * @return the media box height minus margins. + */ + public float getPageHeight() { + return getMediaBox().getHeight() - getMarginTop() - getMarginBottom(); + } + + /** + * Returns the {@link PDDocument} to be created by method {@link #render()}. + * Beware that this PDDocument is released after rendering. This means each + * rendering process creates a new PDDocument. + * + * @return the PDDocument to be used on the next call to {@link #render()}. + */ + public PDDocument getPDDocument() { + if (pdDocument == null) { + pdDocument = new PDDocument(); + } + return pdDocument; + } + + /** + * Called after {@link #render()} in order to release the current document. + */ + protected void resetPDDocument() { + this.pdDocument = null; + } + + /** + * Adds a (custom) {@link Renderer} that may handle the rendering of an + * element. All renderers will be asked to render the current element in the + * order they have been added. If no renderer is capable, the default + * renderer will be asked. + * + * @param renderer the renderer to add. + */ + public void addRenderer(final Renderer renderer) { + if (renderer != null) { + customRenderer.add(renderer); + } + } + + /** + * Removes a {@link Renderer} . + * + * @param renderer the renderer to remove. + */ + public void removeRenderer(final Renderer renderer) { + customRenderer.remove(renderer); + } + + /** + * Renders all elements and returns the resulting {@link PDDocument}. + * + * @return the resulting {@link PDDocument} + * @throws IOException by pdfbox + */ + public PDDocument render() throws IOException { + PDDocument document = getPDDocument(); + RenderContext renderContext = new RenderContext(this, document); + for (Entry entry : elements) { + Element element = entry.getKey(); + LayoutHint layoutHint = entry.getValue(); + boolean success = false; + + // first ask custom renderer to render the element + Iterator customRendererIterator = customRenderer + .iterator(); + while (!success && customRendererIterator.hasNext()) { + success = customRendererIterator.next().render(renderContext, + element, layoutHint); + } + + // if none of them felt responsible, let the default renderer do the job. + if (!success) { + success = renderContext.render(renderContext, element, + layoutHint); + } + + if (!success) { + throw new IllegalArgumentException( + String.format( + "neither layout %s nor the render context knows what to do with %s", + renderContext.getLayout(), element)); + + } + } + renderContext.close(); + + resetPDDocument(); + return document; + } + + /** + * {@link #render() Renders} the document and saves it to the given file. + * + * @param file the file to save to. + * @throws IOException by pdfbox + */ + public void save(final File file) throws IOException { + try (OutputStream out = new FileOutputStream(file)) { + save(out); + } + } + + /** + * {@link #render() Renders} the document and saves it to the given output + * stream. + * + * @param output the stream to save to. + * @throws IOException by pdfbox + */ + public void save(final OutputStream output) throws IOException { + try (PDDocument document = render()) { + try { + document.save(output); + } catch (IOException ioe) { + throw ioe; + } catch (Exception e) { + throw new IOException(e); + } + } + } + + /** + * Adds a {@link RenderListener} that will be notified during + * {@link #render() rendering}. + * + * @param listener the listener to add. + */ + public void addRenderListener(final RenderListener listener) { + if (listener != null) { + renderListener.add(listener); + } + } + + /** + * Removes a {@link RenderListener} . + * + * @param listener the listener to remove. + */ + public void removeRenderListener(final RenderListener listener) { + renderListener.remove(listener); + } + + @Override + public void beforePage(final RenderContext renderContext) + throws IOException { + for (RenderListener listener : renderListener) { + listener.beforePage(renderContext); + } + } + + @Override + public void afterPage(final RenderContext renderContext) throws IOException { + for (RenderListener listener : renderListener) { + listener.afterPage(renderContext); + } + } + +} diff --git a/layout-pdfbox/src/main/java/org/xbib/graphics/layout/pdfbox/elements/Drawable.java b/layout-pdfbox/src/main/java/org/xbib/graphics/layout/pdfbox/elements/Drawable.java new file mode 100644 index 0000000..3119ca3 --- /dev/null +++ b/layout-pdfbox/src/main/java/org/xbib/graphics/layout/pdfbox/elements/Drawable.java @@ -0,0 +1,57 @@ +package org.xbib.graphics.layout.pdfbox.elements; + +import org.apache.pdfbox.pdmodel.PDDocument; +import org.apache.pdfbox.pdmodel.PDPageContentStream; +import org.xbib.graphics.layout.pdfbox.elements.render.Layout; +import org.xbib.graphics.layout.pdfbox.text.DrawListener; +import org.xbib.graphics.layout.pdfbox.text.Position; +import java.io.IOException; + +/** + * Common interface for drawable objects. + */ +public interface Drawable { + + /** + * @return the width of the drawable. + * @throws IOException by pdfbox + */ + float getWidth() throws IOException; + + /** + * @return the height of the drawable. + * @throws IOException by pdfbox + */ + float getHeight() throws IOException; + + /** + * If an absolute position is given, the drawable will be drawn at this + * position ignoring any {@link Layout}. + * + * @return the absolute position. + * @throws IOException by pdfbox + */ + Position getAbsolutePosition() throws IOException; + + /** + * Draws the object at the given position. + * + * @param pdDocument the underlying pdfbox document. + * @param contentStream the stream to draw to. + * @param upperLeft the upper left position to start drawing. + * @param drawListener the listener to + * {@link DrawListener#drawn(Object, Position, float, float) notify} on + * drawn objects. + * @throws IOException by pdfbox + */ + void draw(PDDocument pdDocument, PDPageContentStream contentStream, + Position upperLeft, DrawListener drawListener) throws IOException; + + /** + * @return a copy of this drawable where any leading empty vertical space is + * removed, if possible. This is useful for avoiding leading empty + * space on a new page. + * @throws IOException by pdfbox + */ + Drawable removeLeadingEmptyVerticalSpace() throws IOException; +} diff --git a/layout-pdfbox/src/main/java/org/xbib/graphics/layout/pdfbox/elements/Element.java b/layout-pdfbox/src/main/java/org/xbib/graphics/layout/pdfbox/elements/Element.java new file mode 100644 index 0000000..dc67d17 --- /dev/null +++ b/layout-pdfbox/src/main/java/org/xbib/graphics/layout/pdfbox/elements/Element.java @@ -0,0 +1,8 @@ +package org.xbib.graphics.layout.pdfbox.elements; + +/** + * Base (tagging) interface for elements in a {@link Document}. + */ +public interface Element { + +} diff --git a/layout-pdfbox/src/main/java/org/xbib/graphics/layout/pdfbox/elements/Frame.java b/layout-pdfbox/src/main/java/org/xbib/graphics/layout/pdfbox/elements/Frame.java new file mode 100644 index 0000000..7ed2348 --- /dev/null +++ b/layout-pdfbox/src/main/java/org/xbib/graphics/layout/pdfbox/elements/Frame.java @@ -0,0 +1,680 @@ +package org.xbib.graphics.layout.pdfbox.elements; + +import org.apache.pdfbox.pdmodel.PDDocument; +import org.apache.pdfbox.pdmodel.PDPageContentStream; +import org.xbib.graphics.layout.pdfbox.shape.Rect; +import org.xbib.graphics.layout.pdfbox.shape.Shape; +import org.xbib.graphics.layout.pdfbox.shape.Stroke; +import org.xbib.graphics.layout.pdfbox.text.DrawListener; +import org.xbib.graphics.layout.pdfbox.text.Position; +import org.xbib.graphics.layout.pdfbox.text.WidthRespecting; +import java.awt.Color; +import java.io.IOException; +import java.util.Collection; +import java.util.List; +import java.util.concurrent.CopyOnWriteArrayList; + +/** + * The frame is a container for a {@link Drawable}, that allows to add margin, + * padding, border and background to the contained drawable. The size (width and + * height) is either given, or calculated based on the dimensions of the + * contained item. The size available for the inner element is reduced by the + * margin, padding and border width. + */ +public class Frame implements Element, Drawable, WidthRespecting, Dividable { + + private final List innerList = new CopyOnWriteArrayList(); + + private float paddingLeft; + private float paddingRight; + private float paddingTop; + private float paddingBottom; + + private float marginLeft; + private float marginRight; + private float marginTop; + private float marginBottom; + + private Shape shape = new Rect(); + private Stroke borderStroke = new Stroke(); + private Color borderColor; + private Color backgroundColor; + + private float maxWidth = -1; + + private final Float givenWidth; + private final Float givenHeight; + + private Position absolutePosition; + + /** + * Creates an empty frame. + */ + public Frame() { + this(null, null); + } + + /** + * Creates a frame containing the inner element. + * + * @param inner the item to contain. + */ + public Frame(final Drawable inner) { + this(inner, null, null); + } + + /** + * Creates a frame containing the inner element, optionally constraint by + * the given dimensions. These contraints target the border-box of the + * frame, means: the inner element plus padding plus border width, but not + * the margin. + * + * @param inner the item to contain. + * @param width the width to constrain the border-box of the frame to, or + * null. + * @param height the height to constrain the border-box of the frame to, or + * null. + */ + public Frame(final Drawable inner, final Float width, final Float height) { + this(width, height); + add(inner); + } + + /** + * Creates a frame constraint by the given dimensions. These contraints + * target the border-box of the frame, means: the inner element plus padding + * plus border width, but not the margin. + * + * @param width the width to constrain the border-box of the frame to, or + * null. + * @param height the height to constrain the border-box of the frame to, or + * null. + */ + public Frame(final Float width, final Float height) { + this.givenWidth = width; + this.givenHeight = height; + } + + /** + * Adds a drawable to the frame. + * + * @param drawable + */ + public void add(final Drawable drawable) { + innerList.add(drawable); + } + + protected void addAll(final Collection drawable) { + innerList.addAll(drawable); + } + + /** + * @return the shape to use as border and/or background. + */ + public Shape getShape() { + return shape; + } + + /** + * Sets the shape to use as border and/or background. + * + * @param shape the shape to use. + */ + public void setShape(Shape shape) { + this.shape = shape; + } + + /** + * The stroke to use to draw the border. + * + * @return the stroke to use. + */ + public Stroke getBorderStroke() { + return borderStroke; + } + + /** + * Sets the stroke to use to draw the border. + * + * @param borderStroke the stroke to use. + */ + public void setBorderStroke(Stroke borderStroke) { + this.borderStroke = borderStroke; + } + + /** + * @return the widht of the {@link #getBorderStroke()} or 0. + */ + protected float getBorderWidth() { + return hasBorder() ? getBorderStroke().getLineWidth() : 0; + } + + /** + * @return if a {@link #getShape() shape}, a {@link #getBorderStroke() + * stroke} and {@link #getBorderColor() color} is set. + */ + protected boolean hasBorder() { + return getShape() != null && getBorderStroke() != null + && getBorderColor() != null; + } + + /** + * @return the color to use to draw the border. + */ + public Color getBorderColor() { + return borderColor; + } + + /** + * Sets the color to use to draw the border. + * + * @param borderColor the border color. + */ + public void setBorderColor(Color borderColor) { + this.borderColor = borderColor; + } + + /** + * Convenience method for setting both border color and stroke. + * + * @param borderColor the border color. + * @param borderStroke the stroke to use. + */ + public void setBorder(Color borderColor, Stroke borderStroke) { + setBorderColor(borderColor); + setBorderStroke(borderStroke); + } + + /** + * @return the color to use to draw the background. + */ + public Color getBackgroundColor() { + return backgroundColor; + } + + /** + * Sets the color to use to draw the background. + * + * @param backgroundColor the background color. + */ + public void setBackgroundColor(Color backgroundColor) { + this.backgroundColor = backgroundColor; + } + + /** + * Copies all attributes but the inner drawable and size to the given frame. + * + * @param other the frame to copy the attributes to. + */ + protected void copyAllButInnerAndSizeTo(final Frame other) { + other.setShape(this.getShape()); + other.setBorderStroke(this.getBorderStroke()); + other.setBorderColor(this.getBorderColor()); + other.setBackgroundColor(this.getBackgroundColor()); + + other.setPaddingBottom(this.getPaddingBottom()); + other.setPaddingLeft(this.getPaddingLeft()); + other.setPaddingRight(this.getPaddingRight()); + other.setPaddingTop(this.getPaddingTop()); + + other.setMarginBottom(this.getMarginBottom()); + other.setMarginLeft(this.getMarginLeft()); + other.setMarginRight(this.getMarginRight()); + other.setMarginTop(this.getMarginTop()); + } + + /** + * @return the left padding + */ + public float getPaddingLeft() { + return paddingLeft; + } + + /** + * Sets the left padding. + * + * @param paddingLeft left padding. + */ + public void setPaddingLeft(float paddingLeft) { + this.paddingLeft = paddingLeft; + } + + /** + * @return the right padding + */ + public float getPaddingRight() { + return paddingRight; + } + + /** + * Sets the right padding. + * + * @param paddingRight right padding. + */ + public void setPaddingRight(float paddingRight) { + this.paddingRight = paddingRight; + } + + /** + * @return the top padding + */ + public float getPaddingTop() { + return paddingTop; + } + + /** + * Sets the top padding. + * + * @param paddingTop top padding. + */ + public void setPaddingTop(float paddingTop) { + this.paddingTop = paddingTop; + } + + /** + * @return the bottom padding + */ + public float getPaddingBottom() { + return paddingBottom; + } + + /** + * Sets the bottom padding. + * + * @param paddingBottom bottom padding. + */ + public void setPaddingBottom(float paddingBottom) { + this.paddingBottom = paddingBottom; + } + + /** + * Sets the padding. + * + * @param left left padding. + * @param right right padding. + * @param top top padding. + * @param bottom bottom padding. + */ + public void setPadding(float left, float right, float top, float bottom) { + setPaddingLeft(left); + setPaddingRight(right); + setPaddingTop(top); + setPaddingBottom(bottom); + } + + /** + * @return the left margin + */ + public float getMarginLeft() { + return marginLeft; + } + + /** + * Sets the left margin. + * + * @param marginLeft left margin. + */ + public void setMarginLeft(float marginLeft) { + this.marginLeft = marginLeft; + } + + /** + * @return the right margin + */ + public float getMarginRight() { + return marginRight; + } + + /** + * Sets the right margin. + * + * @param marginRight right margin. + */ + public void setMarginRight(float marginRight) { + this.marginRight = marginRight; + } + + /** + * @return the top margin + */ + public float getMarginTop() { + return marginTop; + } + + /** + * Sets the top margin. + * + * @param marginTop top margin. + */ + public void setMarginTop(float marginTop) { + this.marginTop = marginTop; + } + + /** + * @return the bottom margin + */ + public float getMarginBottom() { + return marginBottom; + } + + /** + * Sets the bottom margin. + * + * @param marginBottom bottom margin. + */ + public void setMarginBottom(float marginBottom) { + this.marginBottom = marginBottom; + } + + /** + * Sets the margin. + * + * @param left left margin. + * @param right right margin. + * @param top top margin. + * @param bottom bottom margin. + */ + public void setMargin(float left, float right, float top, float bottom) { + setMarginLeft(left); + setMarginRight(right); + setMarginTop(top); + setMarginBottom(bottom); + } + + /** + * @return the sum of left/right padding and border width. + */ + protected float getHorizontalShapeSpacing() { + return 2 * getBorderWidth() + getPaddingLeft() + getPaddingRight(); + } + + /** + * @return the sum of top/bottom padding and border width. + */ + protected float getVerticalShapeSpacing() { + return 2 * getBorderWidth() + getPaddingTop() + getPaddingBottom(); + } + + /** + * @return the sum of left/right margin, padding and border width. + */ + protected float getHorizontalSpacing() { + return getMarginLeft() + getMarginRight() + getHorizontalShapeSpacing(); + } + + /** + * @return the sum of top/bottom margin, padding and border width. + */ + protected float getVerticalSpacing() { + return getMarginTop() + getMarginBottom() + getVerticalShapeSpacing(); + } + + /** + * @return the height given to constrain the size of the shape. + */ + protected Float getGivenHeight() { + return givenHeight; + } + + /** + * @return the width given to constrain the size of the shape. + */ + protected Float getGivenWidth() { + return givenWidth; + } + + @Override + public float getWidth() throws IOException { + if (getGivenWidth() != null) { + return getGivenWidth() + getMarginLeft() + getMarginRight(); + } + return getMaxWidth(innerList) + getHorizontalSpacing(); + } + + protected float getMaxWidth(List drawableList) throws IOException { + float max = 0; + if (drawableList != null) { + for (Drawable inner : drawableList) { + max = Math.max(max, inner.getWidth()); + } + } + return max; + } + + @Override + public float getHeight() throws IOException { + if (getGivenHeight() != null) { + return getGivenHeight() + getMarginTop() + getMarginBottom(); + } + return getHeight(innerList) + getVerticalSpacing(); + } + + protected float getHeight(List drawableList) throws IOException { + float height = 0; + if (drawableList != null) { + for (Drawable inner : drawableList) { + height += inner.getHeight(); + } + } + return height; + } + + @Override + public Position getAbsolutePosition() throws IOException { + return absolutePosition; + } + + /** + * Sets th absolute position. + * + * @param absolutePosition the absolute position to use, or null. + */ + public void setAbsolutePosition(Position absolutePosition) { + this.absolutePosition = absolutePosition; + } + + @Override + public float getMaxWidth() { + return maxWidth; + } + + @Override + public void setMaxWidth(float maxWidth) { + this.maxWidth = maxWidth; + + for (Drawable inner : innerList) { + setMaxWidth(inner, maxWidth); + } + } + + private void setMaxWidth(final Drawable inner, float maxWidth) { + if (inner instanceof WidthRespecting) { + if (getGivenWidth() != null) { + ((WidthRespecting) inner).setMaxWidth(getGivenWidth() + - getHorizontalShapeSpacing()); + } else if (maxWidth >= 0) { + ((WidthRespecting) inner).setMaxWidth(maxWidth + - getHorizontalSpacing()); + } + } + } + + /** + * Propagates the max width to the inner item if there is a given size, but + * no absolute position. + * + * @throws IOException by pdfbox. + */ + protected void setInnerMaxWidthIfNecessary() throws IOException { + if (getAbsolutePosition() == null && getGivenWidth() != null) { + setMaxWidth(getGivenWidth() - getHorizontalShapeSpacing()); + } + } + + @Override + public void draw(PDDocument pdDocument, PDPageContentStream contentStream, + Position upperLeft, DrawListener drawListener) throws IOException { + + setInnerMaxWidthIfNecessary(); + + float halfBorderWidth = 0; + if (getBorderWidth() > 0) { + halfBorderWidth = getBorderWidth() / 2f; + } + upperLeft = upperLeft.add(getMarginLeft() + halfBorderWidth, + -getMarginTop() - halfBorderWidth); + + if (getShape() != null) { + float shapeWidth = getWidth() - getMarginLeft() - getMarginRight() + - getBorderWidth(); + float shapeHeight = getHeight() - getMarginTop() + - getMarginBottom() - getBorderWidth(); + + if (getBackgroundColor() != null) { + getShape().fill(pdDocument, contentStream, upperLeft, + shapeWidth, shapeHeight, getBackgroundColor(), + drawListener); + } + if (hasBorder()) { + getShape().draw(pdDocument, contentStream, upperLeft, + shapeWidth, shapeHeight, getBorderColor(), + getBorderStroke(), drawListener); + } + } + + Position innerUpperLeft = upperLeft.add(getPaddingLeft() + + halfBorderWidth, -getPaddingTop() - halfBorderWidth); + + for (Drawable inner : innerList) { + inner.draw(pdDocument, contentStream, innerUpperLeft, drawListener); + innerUpperLeft = innerUpperLeft.add(0, -inner.getHeight()); + } + } + + @Override + public Drawable removeLeadingEmptyVerticalSpace() throws IOException { + if (innerList.size() > 0) { + Drawable drawableWithoutLeadingVerticalSpace = innerList.get(0) + .removeLeadingEmptyVerticalSpace(); + innerList.set(0, drawableWithoutLeadingVerticalSpace); + } + return this; + } + + @Override + public Divided divide(float remainingHeight, float nextPageHeight) + throws IOException { + setInnerMaxWidthIfNecessary(); + + if (remainingHeight - getVerticalSpacing() <= 0) { + return new Divided(new VerticalSpacer(remainingHeight), this); + } + + // find first inner that does not fit on page + float spaceLeft = remainingHeight - getVerticalSpacing(); + + DividedList dividedList = divideList(innerList, spaceLeft); + + float spaceLeftForDivided = spaceLeft + - getHeight(dividedList.getHead()); + Divided divided = null; + + if (dividedList.getDrawableToDivide() != null) { + Dividable innerDividable = null; + if (dividedList.getDrawableToDivide() instanceof Dividable) { + innerDividable = (Dividable) dividedList.getDrawableToDivide(); + } else { + innerDividable = new Cutter(dividedList.getDrawableToDivide()); + } + // some space left on this page for the inner element + divided = innerDividable.divide(spaceLeftForDivided, nextPageHeight + - getVerticalSpacing()); + } + + Float firstHeight = getGivenHeight() == null ? null : remainingHeight; + Float tailHeight = getGivenHeight() == null ? null : getGivenHeight() + - spaceLeft; + + // create head sub frame + Frame first = new Frame(getGivenWidth(), firstHeight); + copyAllButInnerAndSizeTo(first); + if (dividedList.getHead() != null) { + first.addAll(dividedList.getHead()); + } + if (divided != null) { + first.add(divided.getFirst()); + } + + // create tail sub frame + Frame tail = new Frame(getGivenWidth(), tailHeight); + copyAllButInnerAndSizeTo(tail); + if (divided != null) { + tail.add(divided.getTail()); + } + if (dividedList.getTail() != null) { + tail.addAll(dividedList.getTail()); + } + + return new Divided(first, tail); + } + + private DividedList divideList(List items, float spaceLeft) + throws IOException { + List head = null; + List tail = null; + Drawable toDivide = null; + + float tmpHeight = 0; + int index = 0; + while (tmpHeight < spaceLeft) { + tmpHeight += items.get(index).getHeight(); + + if (tmpHeight == spaceLeft) { + // we can split between two drawables + head = items.subList(0, index + 1); + if (index + 1 < items.size()) { + tail = items.subList(index + 1, items.size()); + } + } + + if (tmpHeight > spaceLeft) { + head = items.subList(0, index); + toDivide = items.get(index); + if (index + 1 < items.size()) { + tail = items.subList(index + 1, items.size()); + } + } + + ++index; + } + + return new DividedList(head, toDivide, tail); + } + + public static class DividedList { + private final List head; + private final Drawable drawableToDivide; + private final List tail; + + public DividedList(List head, Drawable drawableToDivide, + List tail) { + this.head = head; + this.drawableToDivide = drawableToDivide; + this.tail = tail; + } + + public List getHead() { + return head; + } + + public Drawable getDrawableToDivide() { + return drawableToDivide; + } + + public List getTail() { + return tail; + } + + } + +} diff --git a/layout-pdfbox/src/main/java/org/xbib/graphics/layout/pdfbox/elements/HorizontalRuler.java b/layout-pdfbox/src/main/java/org/xbib/graphics/layout/pdfbox/elements/HorizontalRuler.java new file mode 100644 index 0000000..4646510 --- /dev/null +++ b/layout-pdfbox/src/main/java/org/xbib/graphics/layout/pdfbox/elements/HorizontalRuler.java @@ -0,0 +1,96 @@ +package org.xbib.graphics.layout.pdfbox.elements; + +import org.apache.pdfbox.pdmodel.PDDocument; +import org.apache.pdfbox.pdmodel.PDPageContentStream; +import org.xbib.graphics.layout.pdfbox.shape.Stroke; +import org.xbib.graphics.layout.pdfbox.text.DrawListener; +import org.xbib.graphics.layout.pdfbox.text.Position; +import org.xbib.graphics.layout.pdfbox.text.WidthRespecting; +import java.awt.Color; +import java.io.IOException; + +/** + * A horizontal ruler that adjust its width to the given + * {@link WidthRespecting#getMaxWidth() max width}. + */ +public class HorizontalRuler implements Drawable, Element, WidthRespecting { + + private final Stroke stroke; + + private final Color color; + + private float maxWidth = -1f; + + public HorizontalRuler(Stroke stroke, Color color) { + super(); + this.stroke = stroke; + this.color = color; + } + + /** + * @return the stroke to draw the ruler with. + */ + public Stroke getStroke() { + return stroke; + } + + /** + * @return the color to draw the ruler with. + */ + public Color getColor() { + return color; + } + + @Override + public float getMaxWidth() { + return maxWidth; + } + + @Override + public void setMaxWidth(float maxWidth) { + this.maxWidth = maxWidth; + } + + @Override + public float getWidth() throws IOException { + return getMaxWidth(); + } + + @Override + public float getHeight() throws IOException { + if (getStroke() == null) { + return 0f; + } + return getStroke().getLineWidth(); + } + + @Override + public Position getAbsolutePosition() { + return null; + } + + @Override + public void draw(PDDocument pdDocument, PDPageContentStream contentStream, + Position upperLeft, DrawListener drawListener) throws IOException { + if (getColor() != null) { + contentStream.setStrokingColor(getColor()); + } + if (getStroke() != null) { + getStroke().applyTo(contentStream); + float x = upperLeft.getX(); + float y = upperLeft.getY() - getStroke().getLineWidth() / 2; + contentStream.moveTo(x, y); + contentStream.lineTo(x + getWidth(), y); + contentStream.stroke(); + } + if (drawListener != null) { + drawListener.drawn(this, upperLeft, getWidth(), getHeight()); + } + } + + @Override + public Drawable removeLeadingEmptyVerticalSpace() { + return this; + } + +} diff --git a/layout-pdfbox/src/main/java/org/xbib/graphics/layout/pdfbox/elements/ImageElement.java b/layout-pdfbox/src/main/java/org/xbib/graphics/layout/pdfbox/elements/ImageElement.java new file mode 100644 index 0000000..fd47698 --- /dev/null +++ b/layout-pdfbox/src/main/java/org/xbib/graphics/layout/pdfbox/elements/ImageElement.java @@ -0,0 +1,137 @@ +package org.xbib.graphics.layout.pdfbox.elements; + +import org.apache.pdfbox.pdmodel.PDDocument; +import org.apache.pdfbox.pdmodel.PDPageContentStream; +import org.xbib.graphics.layout.pdfbox.text.DrawListener; +import org.xbib.graphics.layout.pdfbox.text.Position; +import org.xbib.graphics.layout.pdfbox.text.WidthRespecting; +import org.xbib.graphics.layout.pdfbox.util.CompatibilityHelper; +import java.awt.image.BufferedImage; +import java.io.File; +import java.io.IOException; +import java.io.InputStream; +import javax.imageio.ImageIO; + +public class ImageElement implements Element, Drawable, Dividable, + WidthRespecting { + + /** + * Set this to {@link #setWidth(float)} resp. {@link #setHeight(float)} + * (usually both) in order to respect the {@link WidthRespecting width}. + */ + public final static float SCALE_TO_RESPECT_WIDTH = -1f; + + private final BufferedImage image; + private float width; + private float height; + private float maxWidth = -1; + private Position absolutePosition; + + public ImageElement(final BufferedImage image) { + this.image = image; + this.width = image.getWidth(); + this.height = image.getHeight(); + } + + public ImageElement(final InputStream inputStream) throws IOException { + this(ImageIO.read(inputStream)); + } + + public ImageElement(final String filePath) throws IOException { + this(ImageIO.read(new File(filePath))); + } + + @Override + public float getWidth() throws IOException { + if (width == SCALE_TO_RESPECT_WIDTH) { + if (getMaxWidth() > 0 && image.getWidth() > getMaxWidth()) { + return getMaxWidth(); + } + return image.getWidth(); + } + return width; + } + + /** + * Sets the width. Default is the image width. Set to + * {@link #SCALE_TO_RESPECT_WIDTH} in order to let the image + * {@link WidthRespecting respect any given width}. + * + * @param width the width to use. + */ + public void setWidth(float width) { + this.width = width; + } + + @Override + public float getHeight() throws IOException { + if (height == SCALE_TO_RESPECT_WIDTH) { + if (getMaxWidth() > 0 && image.getWidth() > getMaxWidth()) { + return getMaxWidth() / (float) image.getWidth() + * (float) image.getHeight(); + } + return image.getHeight(); + } + return height; + } + + /** + * Sets the height. Default is the image height. Set to + * {@link #SCALE_TO_RESPECT_WIDTH} in order to let the image + * {@link WidthRespecting respect any given width}. Usually this makes only + * sense if you also set the width to {@link #SCALE_TO_RESPECT_WIDTH}. + * + * @param height the height to use. + */ + public void setHeight(float height) { + this.height = height; + } + + @Override + public Divided divide(float remainingHeight, float nextPageHeight) + throws IOException { + if (getHeight() <= nextPageHeight) { + return new Divided(new VerticalSpacer(remainingHeight), this); + } + return new Cutter(this).divide(remainingHeight, nextPageHeight); + } + + @Override + public float getMaxWidth() { + return maxWidth; + } + + @Override + public void setMaxWidth(float maxWidth) { + this.maxWidth = maxWidth; + } + + @Override + public Position getAbsolutePosition() { + return absolutePosition; + } + + /** + * Sets the absolute position to render at. + * + * @param absolutePosition the absolute position. + */ + public void setAbsolutePosition(Position absolutePosition) { + this.absolutePosition = absolutePosition; + } + + @Override + public void draw(PDDocument pdDocument, PDPageContentStream contentStream, + Position upperLeft, DrawListener drawListener) throws IOException { + CompatibilityHelper.drawImage(image, pdDocument, contentStream, + upperLeft, getWidth(), getHeight()); + if (drawListener != null) { + drawListener.drawn(this, upperLeft, getWidth(), getHeight()); + } + } + + @Override + public Drawable removeLeadingEmptyVerticalSpace() { + return this; + } +} diff --git a/layout-pdfbox/src/main/java/org/xbib/graphics/layout/pdfbox/elements/Orientation.java b/layout-pdfbox/src/main/java/org/xbib/graphics/layout/pdfbox/elements/Orientation.java new file mode 100644 index 0000000..91db239 --- /dev/null +++ b/layout-pdfbox/src/main/java/org/xbib/graphics/layout/pdfbox/elements/Orientation.java @@ -0,0 +1,7 @@ +package org.xbib.graphics.layout.pdfbox.elements; + + +public enum Orientation { + + Portrait, Landscape +} diff --git a/layout-pdfbox/src/main/java/org/xbib/graphics/layout/pdfbox/elements/PageFormat.java b/layout-pdfbox/src/main/java/org/xbib/graphics/layout/pdfbox/elements/PageFormat.java new file mode 100644 index 0000000..ec4a1b1 --- /dev/null +++ b/layout-pdfbox/src/main/java/org/xbib/graphics/layout/pdfbox/elements/PageFormat.java @@ -0,0 +1,369 @@ +package org.xbib.graphics.layout.pdfbox.elements; + +import org.apache.pdfbox.pdmodel.common.PDRectangle; +import org.xbib.graphics.layout.pdfbox.elements.render.VerticalLayout; +import org.xbib.graphics.layout.pdfbox.text.Constants; + +/** + * Defines the size and orientation of a page. The default is A4 portrait + * without margins. + */ +public class PageFormat implements Element { + + private final float marginLeft; + private final float marginRight; + private final float marginTop; + private final float marginBottom; + private final PDRectangle mediaBox; + private final Orientation orientation; + private final int rotation; + + /** + * Creates a PageFormat with A4 portrait without margins. + */ + public PageFormat() { + this(Constants.A4); + } + + /** + * Creates a PageFormat with a given size and orientation portrait. + * + * @param mediaBox the size. + */ + public PageFormat(final PDRectangle mediaBox) { + this(mediaBox, Orientation.Portrait); + } + + /** + * Creates a PageFormat with a given size and orientation. + * + * @param mediaBox the size. + * @param orientation the orientation. + */ + public PageFormat(final PDRectangle mediaBox, final Orientation orientation) { + this(mediaBox, orientation, 0, 0, 0, 0); + } + + /** + * Creates a Document based on the given media box and margins. By default, + * a {@link VerticalLayout} is used. + * + * @param mediaBox the media box to use. + * @param orientation the orientation to use. + * @param marginLeft the left margin + * @param marginRight the right margin + * @param marginTop the top margin + * @param marginBottom the bottom margin + */ + public PageFormat(PDRectangle mediaBox, Orientation orientation, + float marginLeft, float marginRight, float marginTop, + float marginBottom) { + this(mediaBox, orientation, 0, marginLeft, marginRight, marginTop, marginBottom); + } + + /** + * Creates a Document based on the given media box and margins. By default, + * a {@link VerticalLayout} is used. + * + * @param mediaBox the media box to use. + * @param orientation the orientation to use. + * @param rotation the rotation to apply to the page after rendering. + * @param marginLeft the left margin + * @param marginRight the right margin + * @param marginTop the top margin + * @param marginBottom the bottom margin + */ + public PageFormat(PDRectangle mediaBox, Orientation orientation, + int rotation, float marginLeft, float marginRight, + float marginTop, float marginBottom) { + this.mediaBox = mediaBox; + this.orientation = orientation; + this.rotation = rotation; + this.marginLeft = marginLeft; + this.marginRight = marginRight; + this.marginTop = marginTop; + this.marginBottom = marginBottom; + } + + /** + * @return the orientation to use. + */ + public Orientation getOrientation() { + if (orientation != null) { + return orientation; + } + if (getMediaBox().getWidth() > getMediaBox().getHeight()) { + return Orientation.Landscape; + } + return Orientation.Portrait; + } + + /** + * @return the rotation to apply to the page after rendering. + */ + public int getRotation() { + return rotation; + } + + /** + * @return the left document margin. + */ + public float getMarginLeft() { + return marginLeft; + } + + /** + * @return the right document margin. + */ + public float getMarginRight() { + return marginRight; + } + + /** + * @return the top document margin. + */ + public float getMarginTop() { + return marginTop; + } + + /** + * @return the bottom document margin. + */ + public float getMarginBottom() { + return marginBottom; + } + + /** + * @return the media box to use. + */ + public PDRectangle getMediaBox() { + return mediaBox; + } + + /** + * @return a page format builder. The default of the builder is A4 portrait + * without margins. + */ + public static PageFormatBuilder with() { + return new PageFormatBuilder(); + } + + public static class PageFormatBuilder { + private float marginLeft; + private float marginRight; + private float marginTop; + private float marginBottom; + private PDRectangle mediaBox = Constants.A4; + private Orientation orientation; + private int rotation; + + protected PageFormatBuilder() { + } + + /** + * Actually builds the PageFormat. + * + * @return the resulting PageFormat. + */ + public PageFormat build() { + return new PageFormat(mediaBox, orientation, rotation, marginLeft, + marginRight, marginTop, marginBottom); + } + + /** + * Sets the left margin. + * + * @param marginLeft the left margin to use. + * @return the builder. + */ + public PageFormatBuilder marginLeft(float marginLeft) { + this.marginLeft = marginLeft; + return this; + } + + /** + * Sets the right margin. + * + * @param marginRight the right margin to use. + * @return the builder. + */ + public PageFormatBuilder marginRight(float marginRight) { + this.marginRight = marginRight; + return this; + } + + /** + * Sets the top margin. + * + * @param marginTop the top margin to use. + * @return the builder. + */ + public PageFormatBuilder marginTop(float marginTop) { + this.marginTop = marginTop; + return this; + } + + /** + * Sets the bottom margin. + * + * @param marginBottom the bottom margin to use. + * @return the builder. + */ + public PageFormatBuilder marginBottom(float marginBottom) { + this.marginBottom = marginBottom; + return this; + } + + /** + * Sets the margins. + * + * @param marginLeft the left margin to use. + * @param marginRight the right margin to use. + * @param marginTop the top margin to use. + * @param marginBottom the bottom margin to use. + * @return the builder. + */ + public PageFormatBuilder margins(float marginLeft, float marginRight, + float marginTop, float marginBottom) { + this.marginLeft = marginLeft; + this.marginRight = marginRight; + this.marginTop = marginTop; + this.marginBottom = marginBottom; + return this; + } + + /** + * Sets the media box to the given size. + * + * @param mediaBox the media box to use. + * @return the builder. + */ + public PageFormatBuilder mediaBox(PDRectangle mediaBox) { + this.mediaBox = mediaBox; + return this; + } + + /** + * Sets the media box to size {@link Constants#A0}. + * + * @return the builder. + */ + public PageFormatBuilder A0() { + this.mediaBox = Constants.A0; + return this; + } + + /** + * Sets the media box to size {@link Constants#A1}. + * + * @return the builder. + */ + public PageFormatBuilder A1() { + this.mediaBox = Constants.A1; + return this; + } + + /** + * Sets the media box to size {@link Constants#A2}. + * + * @return the builder. + */ + public PageFormatBuilder A2() { + this.mediaBox = Constants.A2; + return this; + } + + /** + * Sets the media box to size {@link Constants#A3}. + * + * @return the builder. + */ + public PageFormatBuilder A3() { + this.mediaBox = Constants.A3; + return this; + } + + /** + * Sets the media box to size {@link Constants#A4}. + * + * @return the builder. + */ + public PageFormatBuilder A4() { + this.mediaBox = Constants.A4; + return this; + } + + /** + * Sets the media box to size {@link Constants#A5}. + * + * @return the builder. + */ + public PageFormatBuilder A5() { + this.mediaBox = Constants.A5; + return this; + } + + /** + * Sets the media box to size {@link Constants#A6}. + * + * @return the builder. + */ + public PageFormatBuilder A6() { + this.mediaBox = Constants.A6; + return this; + } + + /** + * Sets the media box to size {@link Constants#Letter}. + * + * @return the builder. + */ + public PageFormatBuilder letter() { + this.mediaBox = Constants.Letter; + return this; + } + + /** + * Sets the orientation to the given one. + * + * @param orientation the orientation to use. + * @return the builder. + */ + public PageFormatBuilder orientation(Orientation orientation) { + this.orientation = orientation; + return this; + } + + /** + * Sets the orientation to {@link Orientation#Portrait}. + * + * @return the builder. + */ + public PageFormatBuilder portrait() { + this.orientation = Orientation.Portrait; + return this; + } + + /** + * Sets the orientation to {@link Orientation#Landscape}. + * + * @return the builder. + */ + public PageFormatBuilder landscape() { + this.orientation = Orientation.Landscape; + return this; + } + + /** + * Sets the rotation to apply to the page after rendering. + * + * @param angle the angle to rotate. + * @return the builder. + */ + public PageFormatBuilder rotation(int angle) { + this.rotation = angle; + return this; + } + } + +} diff --git a/layout-pdfbox/src/main/java/org/xbib/graphics/layout/pdfbox/elements/Paragraph.java b/layout-pdfbox/src/main/java/org/xbib/graphics/layout/pdfbox/elements/Paragraph.java new file mode 100644 index 0000000..6e9fdc6 --- /dev/null +++ b/layout-pdfbox/src/main/java/org/xbib/graphics/layout/pdfbox/elements/Paragraph.java @@ -0,0 +1,85 @@ +package org.xbib.graphics.layout.pdfbox.elements; + +import org.apache.pdfbox.pdmodel.PDDocument; +import org.apache.pdfbox.pdmodel.PDPageContentStream; +import org.xbib.graphics.layout.pdfbox.text.Alignment; +import org.xbib.graphics.layout.pdfbox.text.DrawListener; +import org.xbib.graphics.layout.pdfbox.text.Position; +import org.xbib.graphics.layout.pdfbox.text.TextFlow; +import org.xbib.graphics.layout.pdfbox.text.TextSequenceUtil; +import org.xbib.graphics.layout.pdfbox.text.WidthRespecting; +import java.io.IOException; + +/** + * A paragraph is used as a container for {@link TextFlow text} that is drawn as + * one element. A paragraph has a {@link #setAlignment(Alignment) (text-) + * alignment}, and {@link WidthRespecting respects a given width} by applying + * word-wrap. + */ +public class Paragraph extends TextFlow implements Drawable, Element, + WidthRespecting, Dividable { + + private Position absolutePosition; + private Alignment alignment = Alignment.Left; + + @Override + public Position getAbsolutePosition() { + return absolutePosition; + } + + /** + * Sets the absolute position to render at. + * + * @param absolutePosition the absolute position. + */ + public void setAbsolutePosition(Position absolutePosition) { + this.absolutePosition = absolutePosition; + } + + /** + * @return the text alignment to apply. Default is left. + */ + public Alignment getAlignment() { + return alignment; + } + + /** + * Sets the alignment to apply. + * + * @param alignment the text alignment. + */ + public void setAlignment(Alignment alignment) { + this.alignment = alignment; + } + + @Override + public void draw(PDDocument pdDocument, PDPageContentStream contentStream, + Position upperLeft, DrawListener drawListener) throws IOException { + drawText(contentStream, upperLeft, getAlignment(), drawListener); + } + + @Override + public Divided divide(float remainingHeight, final float pageHeight) + throws IOException { + return TextSequenceUtil.divide(this, getMaxWidth(), remainingHeight); + } + + @Override + public Paragraph removeLeadingEmptyVerticalSpace() throws IOException { + return removeLeadingEmptyLines(); + } + + @Override + public Paragraph removeLeadingEmptyLines() throws IOException { + Paragraph result = (Paragraph) super.removeLeadingEmptyLines(); + result.setAbsolutePosition(this.getAbsolutePosition()); + result.setAlignment(this.getAlignment()); + return result; + } + + @Override + protected Paragraph createInstance() { + return new Paragraph(); + } + +} diff --git a/layout-pdfbox/src/main/java/org/xbib/graphics/layout/pdfbox/elements/PositionControl.java b/layout-pdfbox/src/main/java/org/xbib/graphics/layout/pdfbox/elements/PositionControl.java new file mode 100644 index 0000000..97a9b03 --- /dev/null +++ b/layout-pdfbox/src/main/java/org/xbib/graphics/layout/pdfbox/elements/PositionControl.java @@ -0,0 +1,103 @@ +package org.xbib.graphics.layout.pdfbox.elements; + +/** + * Utility class to create elements that allow the manipulation of the current + * layout position. Use carefully. + */ +public class PositionControl extends ControlElement { + + /** + * Use this value in {@link #createSetPosition(Float, Float)} to reset + * either one or both coordinates to the marked position. + */ + public static final Float MARKED_POSITION = Float.NEGATIVE_INFINITY; + + /** + * Add this element to a document to mark the current position. + * + * @return the created element + */ + public static MarkPosition createMarkPosition() { + return new MarkPosition(); + } + + /** + * Add this element to a document to manipulate the current layout position. + * If null, the position won't be changed (useful if you want + * to change only X or Y). If the value is {@link #MARKED_POSITION}, it wil + * be (re-)set to the marked position. + * + * @param newX the new X position. + * @param newY new new Y position. + * @return the created element + */ + public static SetPosition createSetPosition(final Float newX, + final Float newY) { + return new SetPosition(newX, newY); + } + + /** + * Add this element to a document to manipulate the current layout position + * by a relative amount. If null, the position won't be changed + * (useful if you want to change only X or Y). + * + * @param relativeX the value to change position in X direction. + * @param relativeY the value to change position in Y direction. + * @return the created element + */ + public static MovePosition createMovePosition(final float relativeX, + final float relativeY) { + return new MovePosition(relativeX, relativeY); + } + + public static class MarkPosition extends PositionControl { + private MarkPosition() { + super("MARK_POSITION"); + } + } + + public static class SetPosition extends PositionControl { + private final Float newX; + private final Float newY; + + private SetPosition(final Float newX, final Float newY) { + super(String.format("SET_POSITION x:%f, y%f", newX, newY)); + this.newX = newX; + this.newY = newY; + } + + public Float getX() { + return newX; + } + + public Float getY() { + return newY; + } + + } + + public static class MovePosition extends PositionControl { + private final float relativeX; + private final float relativeY; + + private MovePosition(final float relativeX, final float relativeY) { + super(String.format("SET_POSITION x:%f, y%f", relativeX, relativeY)); + this.relativeX = relativeX; + this.relativeY = relativeY; + } + + public float getX() { + return relativeX; + } + + public float getY() { + return relativeY; + } + + } + + private PositionControl(String name) { + super(name); + } + +} diff --git a/layout-pdfbox/src/main/java/org/xbib/graphics/layout/pdfbox/elements/Rectangle.java b/layout-pdfbox/src/main/java/org/xbib/graphics/layout/pdfbox/elements/Rectangle.java new file mode 100644 index 0000000..825da6a --- /dev/null +++ b/layout-pdfbox/src/main/java/org/xbib/graphics/layout/pdfbox/elements/Rectangle.java @@ -0,0 +1,58 @@ +package org.xbib.graphics.layout.pdfbox.elements; + +/** + * In order to avoid dependencies to AWT, we use our own Rectangle class here. + */ +public class Rectangle extends Dimension { + + private final float x; + private final float y; + + public Rectangle(float x, float y, float width, float height) { + super(width, height); + this.x = x; + this.y = y; + } + + public float getX() { + return x; + } + + public float getY() { + return y; + } + + @Override + public String toString() { + return "Rectangle [x=" + x + ", y=" + y + ", width=" + getWidth() + + ", height=" + getHeight() + "]"; + } + + @Override + public int hashCode() { + final int prime = 31; + int result = super.hashCode(); + result = prime * result + Float.floatToIntBits(x); + result = prime * result + Float.floatToIntBits(y); + return result; + } + + @Override + public boolean equals(Object obj) { + if (this == obj) { + return true; + } + if (!super.equals(obj)) { + return false; + } + if (getClass() != obj.getClass()) { + return false; + } + Rectangle other = (Rectangle) obj; + if (Float.floatToIntBits(x) != Float.floatToIntBits(other.x)) { + return false; + } + return Float.floatToIntBits(y) == Float.floatToIntBits(other.y); + } + +} diff --git a/layout-pdfbox/src/main/java/org/xbib/graphics/layout/pdfbox/elements/VerticalSpacer.java b/layout-pdfbox/src/main/java/org/xbib/graphics/layout/pdfbox/elements/VerticalSpacer.java new file mode 100644 index 0000000..fb24a92 --- /dev/null +++ b/layout-pdfbox/src/main/java/org/xbib/graphics/layout/pdfbox/elements/VerticalSpacer.java @@ -0,0 +1,61 @@ +package org.xbib.graphics.layout.pdfbox.elements; + +import org.apache.pdfbox.pdmodel.PDDocument; +import org.apache.pdfbox.pdmodel.PDPageContentStream; +import org.xbib.graphics.layout.pdfbox.text.DrawListener; +import org.xbib.graphics.layout.pdfbox.text.Position; +import java.io.IOException; + +/** + * A drawable element that occupies some vertical space without any graphical + * representation. + */ +public class VerticalSpacer implements Drawable, Element, Dividable { + + private final float height; + + /** + * Creates a vertical space with the given height. + * + * @param height the height of the space. + */ + public VerticalSpacer(float height) { + this.height = height; + } + + @Override + public float getWidth() throws IOException { + return 0; + } + + @Override + public float getHeight() throws IOException { + return height; + } + + @Override + public Position getAbsolutePosition() { + return null; + } + + @Override + public void draw(PDDocument pdDocument, PDPageContentStream contentStream, + Position upperLeft, DrawListener drawListener) throws IOException { + if (drawListener != null) { + drawListener.drawn(this, upperLeft, getWidth(), getHeight()); + } + } + + @Override + public Divided divide(float remainingHeight, final float pageHeight) + throws IOException { + return new Divided(new VerticalSpacer(remainingHeight), + new VerticalSpacer(getHeight() - remainingHeight)); + } + + @Override + public Drawable removeLeadingEmptyVerticalSpace() { + return this; + } + +} diff --git a/layout-pdfbox/src/main/java/org/xbib/graphics/layout/pdfbox/elements/render/ColumnLayout.java b/layout-pdfbox/src/main/java/org/xbib/graphics/layout/pdfbox/elements/render/ColumnLayout.java new file mode 100644 index 0000000..7f2a721 --- /dev/null +++ b/layout-pdfbox/src/main/java/org/xbib/graphics/layout/pdfbox/elements/render/ColumnLayout.java @@ -0,0 +1,93 @@ +package org.xbib.graphics.layout.pdfbox.elements.render; + +import org.xbib.graphics.layout.pdfbox.elements.ControlElement; +import org.xbib.graphics.layout.pdfbox.elements.Drawable; +import org.xbib.graphics.layout.pdfbox.elements.Element; +import java.io.IOException; + +/** + * The column layout divides the page vertically into columns. You can specify + * the number of columns and the inter-column spacing. The layouting inside a + * column is similar to the {@link VerticalLayout}. See there for more details + * on the possiblities. + */ +public class ColumnLayout extends VerticalLayout { + + /** + * Triggers flip to the next column. + */ + public final static ControlElement NEWCOLUMN = new ControlElement("NEWCOLUMN"); + + + private final int columnCount; + private final float columnSpacing; + private int columnIndex = 0; + private Float offsetY = null; + + + public ColumnLayout(int columnCount) { + this(columnCount, 0); + } + + public ColumnLayout(int columnCount, float columnSpacing) { + this.columnCount = columnCount; + this.columnSpacing = columnSpacing; + } + + @Override + protected float getTargetWidth(final RenderContext renderContext) { + return (renderContext.getWidth() - ((columnCount - 1) * columnSpacing)) + / columnCount; + } + + /** + * Flips to the next column + */ + @Override + protected void turnPage(final RenderContext renderContext) + throws IOException { + if (++columnIndex >= columnCount) { + renderContext.newPage(); + columnIndex = 0; + offsetY = 0f; + } else { + float nextColumnX = (getTargetWidth(renderContext) + columnSpacing) + * columnIndex; + renderContext.resetPositionToUpperLeft(); + renderContext.movePositionBy(nextColumnX, -offsetY); + } + } + + @Override + public boolean render(RenderContext renderContext, Element element, + LayoutHint layoutHint) throws IOException { + if (element == ControlElement.NEWPAGE) { + renderContext.newPage(); + return true; + } + if (element == NEWCOLUMN) { + turnPage(renderContext); + return true; + } + return super.render(renderContext, element, layoutHint); + } + + @Override + public void render(RenderContext renderContext, Drawable drawable, + LayoutHint layoutHint) throws IOException { + if (offsetY == null) { + offsetY = renderContext.getUpperLeft().getY() - renderContext.getCurrentPosition().getY(); + } + super.render(renderContext, drawable, layoutHint); + } + + @Override + protected boolean isPositionTopOfPage(final RenderContext renderContext) { + float topPosition = renderContext.getUpperLeft().getY(); + if (offsetY != null) { + topPosition -= offsetY; + } + return renderContext.getCurrentPosition().getY() == topPosition; + } + +} \ No newline at end of file diff --git a/layout-pdfbox/src/main/java/org/xbib/graphics/layout/pdfbox/elements/render/ColumnLayoutHint.java b/layout-pdfbox/src/main/java/org/xbib/graphics/layout/pdfbox/elements/render/ColumnLayoutHint.java new file mode 100644 index 0000000..0febecd --- /dev/null +++ b/layout-pdfbox/src/main/java/org/xbib/graphics/layout/pdfbox/elements/render/ColumnLayoutHint.java @@ -0,0 +1,86 @@ +package org.xbib.graphics.layout.pdfbox.elements.render; + +import org.xbib.graphics.layout.pdfbox.text.Alignment; + +/** + * The column layout hint provides currently the same possibilities as the + * {@link VerticalLayoutHint}. See there for more details. + */ +public class ColumnLayoutHint extends VerticalLayoutHint { + + public final static ColumnLayoutHint LEFT = new ColumnLayoutHint( + Alignment.Left); + public final static ColumnLayoutHint CENTER = new ColumnLayoutHint( + Alignment.Center); + public final static ColumnLayoutHint RIGHT = new ColumnLayoutHint( + Alignment.Right); + + /** + * Creates a layout hint with {@link Alignment#Left left alignment}. + */ + public ColumnLayoutHint() { + super(); + } + + /** + * Creates a layout hint with the given alignment. + * + * @param alignment the element alignment. + */ + public ColumnLayoutHint(Alignment alignment) { + super(alignment); + } + + /** + * Creates a layout hint with the given alignment and margins. + * + * @param alignment the element alignment. + * @param marginLeft the left alignment. + * @param marginRight the right alignment. + * @param marginTop the top alignment. + * @param marginBottom the bottom alignment. + */ + public ColumnLayoutHint(Alignment alignment, float marginLeft, + float marginRight, float marginTop, float marginBottom) { + super(alignment, marginLeft, marginRight, marginTop, marginBottom); + } + + /** + * Creates a layout hint with the given alignment and margins. + * + * @param alignment the element alignment. + * @param marginLeft the left alignment. + * @param marginRight the right alignment. + * @param marginTop the top alignment. + * @param marginBottom the bottom alignment. + * @param resetY if true, the y coordinate will be reset to the + * point before layouting the element. + */ + public ColumnLayoutHint(Alignment alignment, float marginLeft, + float marginRight, float marginTop, float marginBottom, + boolean resetY) { + super(alignment, marginLeft, marginRight, marginTop, marginBottom, + resetY); + } + + + /** + * @return a {@link VerticalLayoutHintBuilder} for creating a + * {@link VerticalLayoutHint} using a fluent API. + */ + public static ColumnLayoutHintBuilder builder() { + return new ColumnLayoutHintBuilder(); + } + + /** + * A builder for creating a {@link VerticalLayoutHint} using a fluent API. + */ + public static class ColumnLayoutHintBuilder extends VerticalLayoutHintBuilder { + + public ColumnLayoutHint build() { + return new ColumnLayoutHint(alignment, marginLeft, marginRight, + marginTop, marginBottom, resetY); + } + + } +} diff --git a/layout-pdfbox/src/main/java/org/xbib/graphics/layout/pdfbox/elements/render/Layout.java b/layout-pdfbox/src/main/java/org/xbib/graphics/layout/pdfbox/elements/render/Layout.java new file mode 100644 index 0000000..686cbf1 --- /dev/null +++ b/layout-pdfbox/src/main/java/org/xbib/graphics/layout/pdfbox/elements/render/Layout.java @@ -0,0 +1,11 @@ +package org.xbib.graphics.layout.pdfbox.elements.render; + +import org.xbib.graphics.layout.pdfbox.elements.Element; + +/** + * A layout is used to size and position the elements of a document according to + * a specific strategy. + */ +public interface Layout extends Element, Renderer { + +} diff --git a/layout-pdfbox/src/main/java/org/xbib/graphics/layout/pdfbox/elements/render/LayoutHint.java b/layout-pdfbox/src/main/java/org/xbib/graphics/layout/pdfbox/elements/render/LayoutHint.java new file mode 100644 index 0000000..d15b80f --- /dev/null +++ b/layout-pdfbox/src/main/java/org/xbib/graphics/layout/pdfbox/elements/render/LayoutHint.java @@ -0,0 +1,14 @@ +package org.xbib.graphics.layout.pdfbox.elements.render; + +import org.xbib.graphics.layout.pdfbox.elements.Document; +import org.xbib.graphics.layout.pdfbox.elements.Element; + +/** + * Each element in a document is + * {@link Document#add(Element, LayoutHint) + * accompanied} by a hint, which gives some notes to the current layout on how + * to layout the element. + */ +public interface LayoutHint { + +} diff --git a/layout-pdfbox/src/main/java/org/xbib/graphics/layout/pdfbox/elements/render/RenderContext.java b/layout-pdfbox/src/main/java/org/xbib/graphics/layout/pdfbox/elements/render/RenderContext.java new file mode 100644 index 0000000..a30cd6c --- /dev/null +++ b/layout-pdfbox/src/main/java/org/xbib/graphics/layout/pdfbox/elements/render/RenderContext.java @@ -0,0 +1,462 @@ +package org.xbib.graphics.layout.pdfbox.elements.render; + +import org.apache.pdfbox.pdmodel.PDDocument; +import org.apache.pdfbox.pdmodel.PDPage; +import org.apache.pdfbox.pdmodel.PDPageContentStream; +import org.apache.pdfbox.pdmodel.common.PDRectangle; +import org.xbib.graphics.layout.pdfbox.elements.ControlElement; +import org.xbib.graphics.layout.pdfbox.elements.Document; +import org.xbib.graphics.layout.pdfbox.elements.Element; +import org.xbib.graphics.layout.pdfbox.elements.Orientation; +import org.xbib.graphics.layout.pdfbox.elements.PageFormat; +import org.xbib.graphics.layout.pdfbox.elements.PositionControl; +import org.xbib.graphics.layout.pdfbox.elements.PositionControl.MarkPosition; +import org.xbib.graphics.layout.pdfbox.elements.PositionControl.MovePosition; +import org.xbib.graphics.layout.pdfbox.elements.PositionControl.SetPosition; +import org.xbib.graphics.layout.pdfbox.text.DrawContext; +import org.xbib.graphics.layout.pdfbox.text.DrawListener; +import org.xbib.graphics.layout.pdfbox.text.Position; +import org.xbib.graphics.layout.pdfbox.text.annotations.AnnotationDrawListener; +import org.xbib.graphics.layout.pdfbox.util.CompatibilityHelper; +import java.io.Closeable; +import java.io.IOException; + +/** + * The render context is a container providing all state of the current + * rendering process. + */ +public class RenderContext implements Renderer, Closeable, DrawContext, DrawListener { + + private final Document document; + private final PDDocument pdDocument; + private PDPage page; + private int pageIndex = 0; + private PDPageContentStream contentStream; + private Position currentPosition; + private Position markedPosition; + private Position maxPositionOnPage; + private Layout layout = new VerticalLayout(); + + private PageFormat nextPageFormat; + private PageFormat pageFormat; + + private final AnnotationDrawListener annotationDrawListener; + + /** + * Creates a render context. + * + * @param document the document to render. + * @param pdDocument the underlying pdfbox document. + * @throws IOException by pdfbox. + */ + public RenderContext(Document document, PDDocument pdDocument) + throws IOException { + this.document = document; + this.pdDocument = pdDocument; + this.pageFormat = document.getPageFormat(); + this.annotationDrawListener = new AnnotationDrawListener(this); + newPage(); + } + + /** + * @return the current {@link Layout} used for rendering. + */ + public Layout getLayout() { + return layout; + } + + /** + * Sets the current {@link Layout} used for rendering. + * + * @param layout the new layout. + */ + public void setLayout(Layout layout) { + this.layout = layout; + resetPositionToLeftEndOfPage(); + } + + /** + * @return the orientation to use for the page. If no special + * {@link #setPageFormat(PageFormat) page format} is set, the + * {@link Document#getOrientation() document orientation} is used. + * @deprecated use {@link #getPageFormat()} instead. + */ + @Deprecated + public Orientation getOrientation() { + return getPageFormat().getOrientation(); + } + + /** + * @return the media box to use for the page. If no special + * {@link #setPageFormat(PageFormat) page format} is set, the + * {@link Document#getMediaBox() document media box} is used. + * @deprecated use {@link #getPageFormat()} instead. + */ + @Deprecated + public PDRectangle getMediaBox() { + return getPageFormat().getMediaBox(); + } + + public void setPageFormat(final PageFormat pageFormat) { + if (pageFormat == null) { + this.pageFormat = document.getPageFormat(); + } else { + this.pageFormat = pageFormat; + } + } + + public PageFormat getPageFormat() { + return pageFormat; + } + + /** + * @return the upper left position in the document respecting the + * {@link Document document} margins. + */ + public Position getUpperLeft() { + return new Position(getPageFormat().getMarginLeft(), getPageHeight() + - getPageFormat().getMarginTop()); + } + + /** + * @return the lower right position in the document respecting the + * {@link Document document} margins. + */ + public Position getLowerRight() { + return new Position(getPageWidth() - getPageFormat().getMarginRight(), + getPageFormat().getMarginBottom()); + } + + /** + * @return the current rendering position in pdf coord space (origin in + * lower left corner). + */ + public Position getCurrentPosition() { + return currentPosition; + } + + /** + * @return the {@link PositionControl#MARKED_POSITION}. + */ + public Position getMarkedPosition() { + return markedPosition; + } + + protected void setMarkedPosition(Position markedPosition) { + this.markedPosition = markedPosition; + } + + /** + * Moves the {@link #getCurrentPosition() current position} relatively by + * the given offset. + * + * @param x to move horizontally. + * @param y to move vertically. + */ + public void movePositionBy(final float x, final float y) { + currentPosition = currentPosition.add(x, y); + } + + /** + * Resets the position to {@link #getUpperLeft()}. + */ + public void resetPositionToUpperLeft() { + currentPosition = getUpperLeft(); + } + + /** + * Resets the position to the x of {@link #getUpperLeft()} while keeping the + * current y. + */ + public void resetPositionToLeft() { + currentPosition = new Position(getUpperLeft().getX(), + currentPosition.getY()); + } + + /** + * Resets the position to the x of {@link #getUpperLeft()} and the + * y of {@link #getMaxPositionOnPage()}. + */ + protected void resetPositionToLeftEndOfPage() { + currentPosition = new Position(getUpperLeft().getX(), + getMaxPositionOnPage().getY()); + } + + /** + * @return the orientation of the current page + */ + protected Orientation getPageOrientation() { + if (getPageWidth() > getPageHeight()) { + return Orientation.Landscape; + } + return Orientation.Portrait; + } + + /** + * @return true if the page is rotated by 90/270 degrees. + */ + public boolean isPageTilted() { + return CompatibilityHelper.getPageRotation(page) == 90 + || CompatibilityHelper.getPageRotation(page) == 270; + } + + /** + * @return the page' width, or - if {@link #isPageTilted() rotated} - the + * height. + */ + public float getPageWidth() { + if (isPageTilted()) { + return page.getMediaBox().getHeight(); + } + return page.getMediaBox().getWidth(); + } + + /** + * @return the page' height, or - if {@link #isPageTilted() rotated} - the + * width. + */ + public float getPageHeight() { + if (isPageTilted()) { + return page.getMediaBox().getWidth(); + } + return page.getMediaBox().getHeight(); + } + + /** + * @return the {@link #getPageWidth() width of the page} respecting the + * margins. + */ + public float getWidth() { + return getPageWidth() - getPageFormat().getMarginLeft() + - getPageFormat().getMarginRight(); + } + + /** + * @return the {@link #getPageHeight() height of the page} respecting the + * margins. + */ + public float getHeight() { + return getPageHeight() - getPageFormat().getMarginTop() + - getPageFormat().getMarginBottom(); + } + + /** + * @return the remaining height on the page. + */ + public float getRemainingHeight() { + return getCurrentPosition().getY() - getPageFormat().getMarginBottom(); + } + + /** + * @return the document. + */ + public Document getDocument() { + return document; + } + + /** + * @return the PDDocument. + */ + @Override + public PDDocument getPdDocument() { + return pdDocument; + } + + @Override + public PDPage getCurrentPage() { + return page; + } + + @Override + public PDPageContentStream getCurrentPageContentStream() { + return getContentStream(); + } + + /** + * @return the current PDPage. + */ + @Deprecated + public PDPage getPage() { + return getCurrentPage(); + } + + /** + * @return the current PDPageContentStream. + */ + public PDPageContentStream getContentStream() { + return contentStream; + } + + /** + * @return the current page index (starting from 0). + */ + public int getPageIndex() { + return pageIndex; + } + + @Override + public boolean render(RenderContext renderContext, Element element, + LayoutHint layoutHint) throws IOException { + boolean success = getLayout() + .render(renderContext, element, layoutHint); + if (success) { + return true; + } + if (element == ControlElement.NEWPAGE) { + newPage(); + return true; + } + if (element instanceof PositionControl) { + return render((PositionControl) element); + } + if (element instanceof PageFormat) { + nextPageFormat = (PageFormat) element; + return true; + } + if (element instanceof Layout) { + setLayout((Layout) element); + return true; + } + return false; + } + + protected boolean render(final PositionControl positionControl) { + if (positionControl instanceof MarkPosition) { + setMarkedPosition(getCurrentPosition()); + return true; + } + if (positionControl instanceof SetPosition) { + SetPosition setPosition = (SetPosition) positionControl; + Float x = setPosition.getX(); + if (x == PositionControl.MARKED_POSITION) { + x = getMarkedPosition().getX(); + } + if (x == null) { + x = getCurrentPosition().getX(); + } + Float y = setPosition.getY(); + if (y == PositionControl.MARKED_POSITION) { + y = getMarkedPosition().getY(); + } + if (y == null) { + y = getCurrentPosition().getY(); + } + Position newPosition = new Position(x, y); + currentPosition = newPosition; + return true; + } + if (positionControl instanceof MovePosition) { + MovePosition movePosition = (MovePosition) positionControl; + movePositionBy(movePosition.getX(), movePosition.getY()); + return true; + } + return false; + } + + /** + * Triggers a new page. + * + * @throws IOException by pdfbox + */ + public void newPage() throws IOException { + if (closePage()) { + ++pageIndex; + } + if (nextPageFormat != null) { + setPageFormat(nextPageFormat); + } + + this.page = new PDPage(getPageFormat().getMediaBox()); + this.pdDocument.addPage(page); + this.contentStream = CompatibilityHelper + .createAppendablePDPageContentStream(pdDocument, page); + + // fix orientation + if (getPageOrientation() != getPageFormat().getOrientation()) { + if (isPageTilted()) { + page.setRotation(0); + } else { + page.setRotation(90); + } + } + + if (isPageTilted()) { + CompatibilityHelper.transform(contentStream, 0, 1, -1, 0, + getPageHeight(), 0); + } + + resetPositionToUpperLeft(); + resetMaxPositionOnPage(); + document.beforePage(this); + annotationDrawListener.beforePage(this); + } + + /** + * Closes the current page. + * + * @return true if the current page has not been closed before. + * @throws IOException by pdfbox + */ + public boolean closePage() throws IOException { + if (contentStream != null) { + + annotationDrawListener.afterPage(this); + document.afterPage(this); + + if (getPageFormat().getRotation() != 0) { + int currentRotation = CompatibilityHelper + .getPageRotation(getCurrentPage()); + getCurrentPage().setRotation( + currentRotation + getPageFormat().getRotation()); + } + + contentStream.close(); + contentStream = null; + return true; + } + return false; + } + + @Override + public void close() throws IOException { + closePage(); + annotationDrawListener.afterRender(); + } + + @Override + public void drawn(Object drawnObject, Position upperLeft, float width, + float height) { + updateMaxPositionOnPage(upperLeft, width, height); + annotationDrawListener.drawn(drawnObject, upperLeft, width, height); + } + + /** + * Updates the maximum right resp. bottom position on the page. + * + * @param upperLeft + * @param width + * @param height + */ + protected void updateMaxPositionOnPage(Position upperLeft, float width, + float height) { + maxPositionOnPage = new Position(Math.max(maxPositionOnPage.getX(), + upperLeft.getX() + width), Math.min(maxPositionOnPage.getY(), + upperLeft.getY() - height)); + } + + /** + * Resets the maximumn position to upper left. + */ + protected void resetMaxPositionOnPage() { + maxPositionOnPage = getUpperLeft(); + } + + /** + * @return the maximum right and bottom position of all + * objects rendered on this page so far. + */ + protected Position getMaxPositionOnPage() { + return maxPositionOnPage; + } + +} diff --git a/layout-pdfbox/src/main/java/org/xbib/graphics/layout/pdfbox/elements/render/RenderListener.java b/layout-pdfbox/src/main/java/org/xbib/graphics/layout/pdfbox/elements/render/RenderListener.java new file mode 100644 index 0000000..80c8248 --- /dev/null +++ b/layout-pdfbox/src/main/java/org/xbib/graphics/layout/pdfbox/elements/render/RenderListener.java @@ -0,0 +1,26 @@ +package org.xbib.graphics.layout.pdfbox.elements.render; + +import java.io.IOException; + +/** + * A render listener is called before and after a page has been rendered. It may + * be used, to perform some custom operations (drawings) to the page. + */ +public interface RenderListener { + + /** + * Called before any rendering is performed to the page. + * + * @param renderContext the context providing all rendering state. + * @throws IOException by pdfbox. + */ + void beforePage(final RenderContext renderContext) throws IOException; + + /** + * Called after any rendering is performed to the page. + * + * @param renderContext the context providing all rendering state. + * @throws IOException by pdfbox. + */ + void afterPage(final RenderContext renderContext) throws IOException; +} diff --git a/layout-pdfbox/src/main/java/org/xbib/graphics/layout/pdfbox/elements/render/Renderer.java b/layout-pdfbox/src/main/java/org/xbib/graphics/layout/pdfbox/elements/render/Renderer.java new file mode 100644 index 0000000..57b4ab8 --- /dev/null +++ b/layout-pdfbox/src/main/java/org/xbib/graphics/layout/pdfbox/elements/render/Renderer.java @@ -0,0 +1,25 @@ +package org.xbib.graphics.layout.pdfbox.elements.render; + +import org.xbib.graphics.layout.pdfbox.elements.Element; +import java.io.IOException; + +/** + * A renderer is responsible for rendering certain, but not necessarily all + * elements. The boolean return value indicates whether the element could be + * processed by this renderer. + */ +public interface Renderer { + + /** + * Renders an element. + * + * @param renderContext the render context. + * @param element the element to draw. + * @param layoutHint the associated layout hint + * @return true if the layout is able to render the element. + * @throws IOException by pdfbox + */ + boolean render(final RenderContext renderContext, final Element element, + final LayoutHint layoutHint) throws IOException; + +} diff --git a/layout-pdfbox/src/main/java/org/xbib/graphics/layout/pdfbox/elements/render/VerticalLayout.java b/layout-pdfbox/src/main/java/org/xbib/graphics/layout/pdfbox/elements/render/VerticalLayout.java new file mode 100644 index 0000000..2e27f50 --- /dev/null +++ b/layout-pdfbox/src/main/java/org/xbib/graphics/layout/pdfbox/elements/render/VerticalLayout.java @@ -0,0 +1,294 @@ +package org.xbib.graphics.layout.pdfbox.elements.render; + +import org.apache.pdfbox.pdmodel.PDPageContentStream; +import org.xbib.graphics.layout.pdfbox.elements.ControlElement; +import org.xbib.graphics.layout.pdfbox.elements.Cutter; +import org.xbib.graphics.layout.pdfbox.elements.Dividable; +import org.xbib.graphics.layout.pdfbox.elements.Drawable; +import org.xbib.graphics.layout.pdfbox.elements.Element; +import org.xbib.graphics.layout.pdfbox.elements.PageFormat; +import org.xbib.graphics.layout.pdfbox.elements.VerticalSpacer; +import org.xbib.graphics.layout.pdfbox.text.Alignment; +import org.xbib.graphics.layout.pdfbox.text.Position; +import org.xbib.graphics.layout.pdfbox.text.WidthRespecting; +import org.xbib.graphics.layout.pdfbox.util.CompatibilityHelper; +import java.io.IOException; + +/** + * Layout implementation that stacks drawables vertically onto the page. If the + * remaining height on the page is not sufficient for the drawable, it will be + * {@link Dividable divided}. Any given {@link VerticalLayoutHint} will be taken + * into account to calculate the position, width, alignment etc. + */ +public class VerticalLayout implements Layout { + + protected boolean removeLeadingEmptyVerticalSpace = true; + + /** + * See {@link Drawable#removeLeadingEmptyVerticalSpace()} + * + * @return true if empty space (e.g. empty lines) should be + * removed at the begin of a page. + */ + public boolean isRemoveLeadingEmptyVerticalSpace() { + return removeLeadingEmptyVerticalSpace; + } + + /** + * Indicates if empty space (e.g. empty lines) should be removed at the + * begin of a page. See {@link Drawable#removeLeadingEmptyVerticalSpace()} + * + * @param removeLeadingEmptyLines true if space should be removed. + */ + public void setRemoveLeadingEmptyVerticalSpace( + boolean removeLeadingEmptyLines) { + this.removeLeadingEmptyVerticalSpace = removeLeadingEmptyLines; + } + + /** + * Turns to the next area, usually a page. + * + * @param renderContext the render context. + * @throws IOException by pdfbox. + */ + protected void turnPage(final RenderContext renderContext) + throws IOException { + renderContext.newPage(); + } + + /** + * @param renderContext the render context. + * @return the target width to draw to. + */ + protected float getTargetWidth(final RenderContext renderContext) { + float targetWidth = renderContext.getWidth(); + return targetWidth; + } + + @Override + public boolean render(RenderContext renderContext, Element element, + LayoutHint layoutHint) throws IOException { + if (element instanceof Drawable) { + render(renderContext, (Drawable) element, layoutHint); + return true; + } + if (element == ControlElement.NEWPAGE) { + turnPage(renderContext); + return true; + } + + return false; + } + + public void render(final RenderContext renderContext, Drawable drawable, + final LayoutHint layoutHint) throws IOException { + if (drawable.getAbsolutePosition() != null) { + renderAbsolute(renderContext, drawable, layoutHint, + drawable.getAbsolutePosition()); + } else { + renderReleative(renderContext, drawable, layoutHint); + } + } + + /** + * Draws at the given position, ignoring all layouting rules. + * + * @param renderContext the context providing all rendering state. + * @param drawable the drawable to draw. + * @param layoutHint the layout hint used to layout. + * @param position the left upper position to start drawing at. + * @throws IOException by pdfbox + */ + protected void renderAbsolute(final RenderContext renderContext, + Drawable drawable, final LayoutHint layoutHint, + final Position position) throws IOException { + drawable.draw(renderContext.getPdDocument(), + renderContext.getContentStream(), position, renderContext); + } + + /** + * Renders the drawable at the {@link RenderContext#getCurrentPosition() + * current position}. This method is responsible taking any top or bottom + * margin described by the (Vertical-)LayoutHint into account. The actual + * rendering of the drawable is performed by + * {@link #layoutAndDrawReleative(RenderContext, Drawable, LayoutHint)}. + * + * @param renderContext the context providing all rendering state. + * @param drawable the drawable to draw. + * @param layoutHint the layout hint used to layout. + * @throws IOException by pdfbox + */ + protected void renderReleative(final RenderContext renderContext, + Drawable drawable, final LayoutHint layoutHint) throws IOException { + VerticalLayoutHint verticalLayoutHint = null; + if (layoutHint instanceof VerticalLayoutHint) { + verticalLayoutHint = (VerticalLayoutHint) layoutHint; + if (verticalLayoutHint.getMarginTop() > 0) { + layoutAndDrawReleative(renderContext, new VerticalSpacer( + verticalLayoutHint.getMarginTop()), verticalLayoutHint); + } + } + + layoutAndDrawReleative(renderContext, drawable, verticalLayoutHint); + + if (verticalLayoutHint != null) { + if (verticalLayoutHint.getMarginBottom() > 0) { + layoutAndDrawReleative(renderContext, new VerticalSpacer( + verticalLayoutHint.getMarginBottom()), + verticalLayoutHint); + } + } + } + + /** + * Adjusts the width of the drawable (if it is {@link WidthRespecting}), and + * divides it onto multiple pages if necessary. Actual drawing is delegated + * to + * {@link #drawReletivePartAndMovePosition(RenderContext, Drawable, LayoutHint, boolean)} + * . + * + * @param renderContext the context providing all rendering state. + * @param drawable the drawable to draw. + * @param layoutHint the layout hint used to layout. + * @throws IOException by pdfbox + */ + protected void layoutAndDrawReleative(final RenderContext renderContext, + Drawable drawable, final LayoutHint layoutHint) throws IOException { + + float targetWidth = getTargetWidth(renderContext); + boolean movePosition = true; + VerticalLayoutHint verticalLayoutHint = null; + if (layoutHint instanceof VerticalLayoutHint) { + verticalLayoutHint = (VerticalLayoutHint) layoutHint; + targetWidth -= verticalLayoutHint.getMarginLeft(); + targetWidth -= verticalLayoutHint.getMarginRight(); + movePosition = !verticalLayoutHint.isResetY(); + } + + float oldMaxWidth = -1; + if (drawable instanceof WidthRespecting) { + WidthRespecting flowing = (WidthRespecting) drawable; + oldMaxWidth = flowing.getMaxWidth(); + if (oldMaxWidth < 0) { + flowing.setMaxWidth(targetWidth); + } + } + + Drawable drawablePart = removeLeadingEmptyVerticalSpace(drawable, + renderContext); + while (renderContext.getRemainingHeight() < drawablePart.getHeight()) { + Dividable dividable = null; + if (drawablePart instanceof Dividable) { + dividable = (Dividable) drawablePart; + } else { + dividable = new Cutter(drawablePart); + } + Dividable.Divided divided = dividable.divide( + renderContext.getRemainingHeight(), + renderContext.getHeight()); + drawReletivePartAndMovePosition(renderContext, divided.getFirst(), + layoutHint, true); + + // new page + turnPage(renderContext); + + drawablePart = divided.getTail(); + drawablePart = removeLeadingEmptyVerticalSpace(drawablePart, + renderContext); + } + + drawReletivePartAndMovePosition(renderContext, drawablePart, + layoutHint, movePosition); + + if (drawable instanceof WidthRespecting) { + if (oldMaxWidth < 0) { + ((WidthRespecting) drawable).setMaxWidth(oldMaxWidth); + } + } + } + + /** + * Actually draws the (drawble) part at the + * {@link RenderContext#getCurrentPosition()} and - depending on flag + * movePosition - moves to the new Y position. Any left or + * right margin is taken into account to calculate the position and + * alignment. + * + * @param renderContext the context providing all rendering state. + * @param drawable the drawable to draw. + * @param layoutHint the layout hint used to layout. + * @param movePosition indicates if the position should be moved (vertically) after + * drawing. + * @throws IOException by pdfbox + */ + protected void drawReletivePartAndMovePosition( + final RenderContext renderContext, Drawable drawable, + final LayoutHint layoutHint, final boolean movePosition) + throws IOException { + PDPageContentStream contentStream = renderContext.getContentStream(); + PageFormat pageFormat = renderContext.getPageFormat(); + float offsetX = 0; + if (layoutHint instanceof VerticalLayoutHint) { + VerticalLayoutHint verticalLayoutHint = (VerticalLayoutHint) layoutHint; + Alignment alignment = verticalLayoutHint.getAlignment(); + float horizontalExtraSpace = getTargetWidth(renderContext) + - drawable.getWidth(); + switch (alignment) { + case Right: + offsetX = horizontalExtraSpace + - verticalLayoutHint.getMarginRight(); + break; + case Center: + offsetX = horizontalExtraSpace / 2f; + break; + default: + offsetX = verticalLayoutHint.getMarginLeft(); + break; + } + } + + contentStream.saveGraphicsState(); + contentStream.addRect(0, pageFormat.getMarginBottom(), renderContext.getPageWidth(), + renderContext.getHeight()); + CompatibilityHelper.clip(contentStream); + + drawable.draw(renderContext.getPdDocument(), contentStream, + renderContext.getCurrentPosition().add(offsetX, 0), renderContext); + + contentStream.restoreGraphicsState(); + + if (movePosition) { + renderContext.movePositionBy(0, -drawable.getHeight()); + } + } + + /** + * Indicates if the current position is the top of page. + * + * @param renderContext the render context. + * @return true if the current position is top of page. + */ + protected boolean isPositionTopOfPage(final RenderContext renderContext) { + return renderContext.getCurrentPosition().getY() == renderContext + .getUpperLeft().getY(); + } + + /** + * Removes empty space (e.g. empty lines) at the begin of a page. See + * {@link Drawable#removeLeadingEmptyVerticalSpace()} + * + * @param drawable the drawable to process. + * @param renderContext the render context. + * @return the processed drawable + * @throws IOException by pdfbox + */ + protected Drawable removeLeadingEmptyVerticalSpace(final Drawable drawable, + final RenderContext renderContext) throws IOException { + if (isRemoveLeadingEmptyVerticalSpace() + && isPositionTopOfPage(renderContext)) { + return drawable.removeLeadingEmptyVerticalSpace(); + } + return drawable; + } + +} diff --git a/layout-pdfbox/src/main/java/org/xbib/graphics/layout/pdfbox/elements/render/VerticalLayoutHint.java b/layout-pdfbox/src/main/java/org/xbib/graphics/layout/pdfbox/elements/render/VerticalLayoutHint.java new file mode 100644 index 0000000..64af7fd --- /dev/null +++ b/layout-pdfbox/src/main/java/org/xbib/graphics/layout/pdfbox/elements/render/VerticalLayoutHint.java @@ -0,0 +1,180 @@ +package org.xbib.graphics.layout.pdfbox.elements.render; + +import org.xbib.graphics.layout.pdfbox.text.Alignment; + +/** + * Layout hint for the {@link VerticalLayout}. You may specify margins to define + * some extra space around the drawable. If there is still some extra space + * available vertically, the alignment decides where to position the drawable. + * The {@link #isResetY() reset Y} indicates if the Y postion should be reset to + * the value before drawing. Be aware that this only applies to the current page + * where the remainder of the element has been drawn to. Means, if the elemenent + * spawns multiple pages, the position is reset to the begin of the last page. + */ +public class VerticalLayoutHint implements LayoutHint { + + public final static VerticalLayoutHint LEFT = new VerticalLayoutHint( + Alignment.Left); + public final static VerticalLayoutHint CENTER = new VerticalLayoutHint( + Alignment.Center); + public final static VerticalLayoutHint RIGHT = new VerticalLayoutHint( + Alignment.Right); + + private final Alignment alignment; + private final float marginLeft; + private final float marginRight; + private final float marginTop; + private final float marginBottom; + private final boolean resetY; + + /** + * Creates a layout hint with {@link Alignment#Left left alignment}. + */ + public VerticalLayoutHint() { + this(Alignment.Left); + } + + /** + * Creates a layout hint with the given alignment. + * + * @param alignment the element alignment. + */ + public VerticalLayoutHint(Alignment alignment) { + this(alignment, 0, 0, 0, 0); + } + + /** + * Creates a layout hint with the given alignment and margins. + * + * @param alignment the element alignment. + * @param marginLeft the left alignment. + * @param marginRight the right alignment. + * @param marginTop the top alignment. + * @param marginBottom the bottom alignment. + */ + public VerticalLayoutHint(Alignment alignment, float marginLeft, + float marginRight, float marginTop, float marginBottom) { + this(alignment, marginLeft, marginRight, marginTop, marginBottom, false); + } + + /** + * Creates a layout hint with the given alignment and margins. + * + * @param alignment the element alignment. + * @param marginLeft the left alignment. + * @param marginRight the right alignment. + * @param marginTop the top alignment. + * @param marginBottom the bottom alignment. + * @param resetY if true, the y coordinate will be reset to the + * point before layouting the element. + */ + public VerticalLayoutHint(Alignment alignment, float marginLeft, + float marginRight, float marginTop, float marginBottom, + boolean resetY) { + this.alignment = alignment; + this.marginLeft = marginLeft; + this.marginRight = marginRight; + this.marginTop = marginTop; + this.marginBottom = marginBottom; + this.resetY = resetY; + } + + public Alignment getAlignment() { + return alignment; + } + + public float getMarginLeft() { + return marginLeft; + } + + public float getMarginRight() { + return marginRight; + } + + public float getMarginTop() { + return marginTop; + } + + public float getMarginBottom() { + return marginBottom; + } + + public boolean isResetY() { + return resetY; + } + + @Override + public String toString() { + return "VerticalLayoutHint [alignment=" + alignment + ", marginLeft=" + + marginLeft + ", marginRight=" + marginRight + ", marginTop=" + + marginTop + ", marginBottom=" + marginBottom + ", resetY=" + + resetY + "]"; + } + + /** + * @return a {@link VerticalLayoutHintBuilder} for creating a + * {@link VerticalLayoutHint} using a fluent API. + */ + public static VerticalLayoutHintBuilder builder() { + return new VerticalLayoutHintBuilder(); + } + + /** + * A builder for creating a + * {@link VerticalLayoutHint} using a fluent API. + */ + public static class VerticalLayoutHintBuilder { + protected Alignment alignment = Alignment.Left; + protected float marginLeft = 0; + protected float marginRight = 0; + protected float marginTop = 0; + protected float marginBottom = 0; + protected boolean resetY = false; + + public VerticalLayoutHintBuilder alignment(final Alignment alignment) { + this.alignment = alignment; + return this; + } + + public VerticalLayoutHintBuilder marginLeft(final float marginLeft) { + this.marginLeft = marginLeft; + return this; + } + + public VerticalLayoutHintBuilder marginRight(final float marginRight) { + this.marginRight = marginRight; + return this; + } + + public VerticalLayoutHintBuilder marginTop(final float marginTop) { + this.marginTop = marginTop; + return this; + } + + public VerticalLayoutHintBuilder marginBottom(final float marginBottom) { + this.marginBottom = marginBottom; + return this; + } + + public VerticalLayoutHintBuilder margins(float marginLeft, + float marginRight, float marginTop, float marginBottom) { + this.marginLeft = marginLeft; + this.marginRight = marginRight; + this.marginTop = marginTop; + this.marginBottom = marginBottom; + return this; + } + + public VerticalLayoutHintBuilder resetY(final boolean resetY) { + this.resetY = resetY; + return this; + } + + public VerticalLayoutHint build() { + return new VerticalLayoutHint(alignment, marginLeft, marginRight, + marginTop, marginBottom, resetY); + } + + } + +} diff --git a/layout-pdfbox/src/main/java/org/xbib/graphics/layout/pdfbox/shape/AbstractShape.java b/layout-pdfbox/src/main/java/org/xbib/graphics/layout/pdfbox/shape/AbstractShape.java new file mode 100644 index 0000000..684ca97 --- /dev/null +++ b/layout-pdfbox/src/main/java/org/xbib/graphics/layout/pdfbox/shape/AbstractShape.java @@ -0,0 +1,59 @@ +package org.xbib.graphics.layout.pdfbox.shape; + +import org.apache.pdfbox.pdmodel.PDDocument; +import org.apache.pdfbox.pdmodel.PDPageContentStream; +import org.xbib.graphics.layout.pdfbox.text.DrawListener; +import org.xbib.graphics.layout.pdfbox.text.Position; +import org.xbib.graphics.layout.pdfbox.util.CompatibilityHelper; +import java.awt.Color; +import java.io.IOException; + +/** + * Abstract base class for shapes which performs the + * {@link #fill(PDDocument, PDPageContentStream, Position, float, float, Color, DrawListener)} + * and (@link + * {@link #draw(PDDocument, PDPageContentStream, Position, float, float, Color, Stroke, DrawListener)} + * . + */ +public abstract class AbstractShape implements Shape { + + @Override + public void draw(PDDocument pdDocument, PDPageContentStream contentStream, + Position upperLeft, float width, float height, Color color, + Stroke stroke, DrawListener drawListener) throws IOException { + + add(pdDocument, contentStream, upperLeft, width, height); + + if (stroke != null) { + stroke.applyTo(contentStream); + } + if (color != null) { + contentStream.setStrokingColor(color); + } + contentStream.stroke(); + + if (drawListener != null) { + drawListener.drawn(this, upperLeft, width, height); + } + + } + + @Override + public void fill(PDDocument pdDocument, PDPageContentStream contentStream, + Position upperLeft, float width, float height, Color color, + DrawListener drawListener) throws IOException { + + add(pdDocument, contentStream, upperLeft, width, height); + + if (color != null) { + contentStream.setNonStrokingColor(color); + } + CompatibilityHelper.fillNonZero(contentStream); + + if (drawListener != null) { + drawListener.drawn(this, upperLeft, width, height); + } + + } + +} diff --git a/layout-pdfbox/src/main/java/org/xbib/graphics/layout/pdfbox/shape/Ellipse.java b/layout-pdfbox/src/main/java/org/xbib/graphics/layout/pdfbox/shape/Ellipse.java new file mode 100644 index 0000000..92ed3be --- /dev/null +++ b/layout-pdfbox/src/main/java/org/xbib/graphics/layout/pdfbox/shape/Ellipse.java @@ -0,0 +1,26 @@ +package org.xbib.graphics.layout.pdfbox.shape; + +import org.apache.pdfbox.pdmodel.PDPageContentStream; +import org.xbib.graphics.layout.pdfbox.text.Position; +import java.io.IOException; + +/** + * Shapes an ellipse, or a circle if width==height. + */ +public class Ellipse extends RoundRect { + + /** + * Default constructor. + */ + public Ellipse() { + super(0); + } + + @Override + protected void addRoundRect(PDPageContentStream contentStream, + Position upperLeft, float width, float height, float cornerRadiusX, + float cornerRadiusY) throws IOException { + super.addRoundRect(contentStream, upperLeft, width, height, width / 2f, + height / 2); + } +} diff --git a/layout-pdfbox/src/main/java/org/xbib/graphics/layout/pdfbox/shape/Rect.java b/layout-pdfbox/src/main/java/org/xbib/graphics/layout/pdfbox/shape/Rect.java new file mode 100644 index 0000000..8d69db1 --- /dev/null +++ b/layout-pdfbox/src/main/java/org/xbib/graphics/layout/pdfbox/shape/Rect.java @@ -0,0 +1,20 @@ +package org.xbib.graphics.layout.pdfbox.shape; + +import org.apache.pdfbox.pdmodel.PDDocument; +import org.apache.pdfbox.pdmodel.PDPageContentStream; +import org.xbib.graphics.layout.pdfbox.text.Position; +import java.io.IOException; + +/** + * A simple rectangular shape. + */ +public class Rect extends AbstractShape { + + @Override + public void add(PDDocument pdDocument, PDPageContentStream contentStream, + Position upperLeft, float width, float height) throws IOException { + contentStream.addRect(upperLeft.getX(), upperLeft.getY() - height, + width, height); + } + +} diff --git a/layout-pdfbox/src/main/java/org/xbib/graphics/layout/pdfbox/shape/RoundRect.java b/layout-pdfbox/src/main/java/org/xbib/graphics/layout/pdfbox/shape/RoundRect.java new file mode 100644 index 0000000..b961ba8 --- /dev/null +++ b/layout-pdfbox/src/main/java/org/xbib/graphics/layout/pdfbox/shape/RoundRect.java @@ -0,0 +1,126 @@ +package org.xbib.graphics.layout.pdfbox.shape; + +import org.apache.pdfbox.pdmodel.PDDocument; +import org.apache.pdfbox.pdmodel.PDPageContentStream; +import org.xbib.graphics.layout.pdfbox.text.Position; +import org.xbib.graphics.layout.pdfbox.util.CompatibilityHelper; +import java.io.IOException; + +/** + * A rectangular shape with rounded corners. + */ +public class RoundRect extends AbstractShape { + + private final static float BEZ = 0.551915024494f; + + private final float cornerRadiusX; + private final float cornerRadiusY; + + /** + * Creates a rounded rect with equal radiuss for both x-axis and y-axis (quarter of a circle). + * + * @param cornerRadius the radius of the corner circle. + */ + public RoundRect(float cornerRadius) { + this(cornerRadius, cornerRadius); + } + + /** + * Creates a rounded rect with potentially different radiuss for both x-axis and y-axis (quarter of an ellipse). + * + * @param cornerRadiusX the radius in x-direction of the corner ellipse. + * @param cornerRadiusY the radius in y-direction of the corner ellipse. + */ + public RoundRect(float cornerRadiusX, float cornerRadiusY) { + this.cornerRadiusX = cornerRadiusX; + this.cornerRadiusY = cornerRadiusY; + } + + @Override + public void add(PDDocument pdDocument, PDPageContentStream contentStream, + Position upperLeft, float width, float height) throws IOException { + + addRoundRect(contentStream, upperLeft, width, height, cornerRadiusX, cornerRadiusY); + } + + /** + * create points clockwise starting in upper left corner + * + *
      +     *     a          b
      +     *      ----------
      +     *     /          \
      +     *  h |            | c
      +     *    |            |
      +     *    |            |
      +     *   g \          / d
      +     *      ----------
      +     *     f          e
      +     * 
      + * + * @param contentStream the content stream. + * @param upperLeft the upper left point + * @param width the width + * @param height the height + * @param cornerRadiusX the corner radius in x direction + * @param cornerRadiusY the corner radius in y direction + * @throws IOException by pdfbox + */ + protected void addRoundRect(PDPageContentStream contentStream, + Position upperLeft, float width, float height, float cornerRadiusX, + float cornerRadiusY) throws IOException { + float nettoWidth = width - 2 * cornerRadiusX; + float nettoHeight = height - 2 * cornerRadiusY; + + // top line + Position a = new Position(upperLeft.getX() + cornerRadiusX, + upperLeft.getY()); + Position b = new Position(a.getX() + nettoWidth, a.getY()); + // right line + Position c = new Position(upperLeft.getX() + width, upperLeft.getY() + - cornerRadiusY); + Position d = new Position(c.getX(), c.getY() - nettoHeight); + // bottom line + Position e = new Position( + upperLeft.getX() + width - cornerRadiusX, upperLeft.getY() + - height); + Position f = new Position(e.getX() - nettoWidth, e.getY()); + // left line + Position g = new Position(upperLeft.getX(), upperLeft.getY() - height + + cornerRadiusY); + Position h = new Position(g.getX(), upperLeft.getY() + - cornerRadiusY); + + float bezX = cornerRadiusX * BEZ; + float bezY = cornerRadiusY * BEZ; + + contentStream.moveTo(a.getX(), a.getY()); + addLine(contentStream, a.getX(), a.getY(), b.getX(), b.getY()); + CompatibilityHelper.curveTo(contentStream, b.getX() + bezX, b.getY(), c.getX(), + c.getY() + bezY, c.getX(), c.getY()); + // contentStream.addLine(c.getX(), c.getY(), d.getX(), d.getY()); + addLine(contentStream, c.getX(), c.getY(), d.getX(), d.getY()); + CompatibilityHelper.curveTo(contentStream, d.getX(), d.getY() - bezY, e.getX() + bezX, + e.getY(), e.getX(), e.getY()); + // contentStream.addLine(e.getX(), e.getY(), f.getX(), f.getY()); + addLine(contentStream, e.getX(), e.getY(), f.getX(), f.getY()); + CompatibilityHelper.curveTo(contentStream, f.getX() - bezX, f.getY(), g.getX(), + g.getY() - bezY, g.getX(), g.getY()); + addLine(contentStream, g.getX(), g.getY(), h.getX(), h.getY()); + CompatibilityHelper.curveTo(contentStream, h.getX(), h.getY() + bezY, a.getX() - bezX, + a.getY(), a.getX(), a.getY()); + } + + /** + * Using lines won't give us a continuing path, which looks silly on fill. + * So we are approximating lines with bezier curves... is there no better + * way? + */ + private void addLine(final PDPageContentStream contentStream, float x1, + float y1, float x2, float y2) throws IOException { + float xMid = (x1 + x2) / 2f; + float yMid = (y1 + y2) / 2f; + CompatibilityHelper.curveTo1(contentStream, xMid, yMid, x2, y2); + } + +} diff --git a/layout-pdfbox/src/main/java/org/xbib/graphics/layout/pdfbox/shape/Shape.java b/layout-pdfbox/src/main/java/org/xbib/graphics/layout/pdfbox/shape/Shape.java new file mode 100644 index 0000000..08b41bd --- /dev/null +++ b/layout-pdfbox/src/main/java/org/xbib/graphics/layout/pdfbox/shape/Shape.java @@ -0,0 +1,71 @@ +package org.xbib.graphics.layout.pdfbox.shape; + +import org.apache.pdfbox.pdmodel.PDDocument; +import org.apache.pdfbox.pdmodel.PDPageContentStream; +import org.xbib.graphics.layout.pdfbox.text.DrawListener; +import org.xbib.graphics.layout.pdfbox.text.Position; +import java.awt.Color; +import java.io.IOException; + +/** + * Shapes can be used to either + * {@link #draw(PDDocument, PDPageContentStream, Position, float, float, Color, Stroke, DrawListener) + * stroke} or + * {@link #fill(PDDocument, PDPageContentStream, Position, float, float, Color, DrawListener) + * fill} the path of the shape, or simply + * {@link #add(PDDocument, PDPageContentStream, Position, float, float) add the + * path} of the shape to the drawing context. + */ +public interface Shape { + + /** + * Draws (strokes) the shape. + * + * @param pdDocument the underlying pdfbox document. + * @param contentStream the stream to draw to. + * @param upperLeft the upper left position to start drawing. + * @param width the width of the bounding box. + * @param height the height of the bounding box. + * @param color the color to use. + * @param stroke the stroke to use. + * @param drawListener the listener to + * {@link DrawListener#drawn(Object, Position, float, float) + * notify} on drawn objects. + * @throws IOException by pdfbox + */ + void draw(PDDocument pdDocument, PDPageContentStream contentStream, + Position upperLeft, float width, float height, Color color, + Stroke stroke, DrawListener drawListener) throws IOException; + + /** + * Fills the shape. + * + * @param pdDocument the underlying pdfbox document. + * @param contentStream the stream to draw to. + * @param upperLeft the upper left position to start drawing. + * @param width the width of the bounding box. + * @param height the height of the bounding box. + * @param color the color to use. + * @param drawListener the listener to + * {@link DrawListener#drawn(Object, Position, float, float) + * notify} on drawn objects. + * @throws IOException by pdfbox + */ + void fill(PDDocument pdDocument, PDPageContentStream contentStream, + Position upperLeft, float width, float height, Color color, + DrawListener drawListener) throws IOException; + + /** + * Adds (the path of) the shape without drawing anything. + * + * @param pdDocument the underlying pdfbox document. + * @param contentStream the stream to draw to. + * @param upperLeft the upper left position to start drawing. + * @param width the width of the bounding box. + * @param height the height of the bounding box. + * @throws IOException by pdfbox + */ + void add(PDDocument pdDocument, PDPageContentStream contentStream, + Position upperLeft, float width, float height) throws IOException; + +} diff --git a/layout-pdfbox/src/main/java/org/xbib/graphics/layout/pdfbox/shape/Stroke.java b/layout-pdfbox/src/main/java/org/xbib/graphics/layout/pdfbox/shape/Stroke.java new file mode 100644 index 0000000..6970475 --- /dev/null +++ b/layout-pdfbox/src/main/java/org/xbib/graphics/layout/pdfbox/shape/Stroke.java @@ -0,0 +1,228 @@ +package org.xbib.graphics.layout.pdfbox.shape; + +import org.apache.pdfbox.pdmodel.PDPageContentStream; +import java.io.IOException; + +/** + * This is a container for all information needed to perform a stroke. + */ +public class Stroke { + + /** + * Enum for the PDF cap styles. + */ + public enum CapStyle { + + Cap(0), RoundCap(1), Square(2); + + private final int value; + + CapStyle(final int value) { + this.value = value; + } + + public int value() { + return value; + } + } + + /** + * Enum for the PDF join styles. + */ + public enum JoinStyle { + + Miter(0), Round(1), Bevel(2); + + private final int value; + + JoinStyle(final int value) { + this.value = value; + } + + public int value() { + return value; + } + } + + /** + * Describes a PDF dash pattern. See the PDF documentation for more + * information on that. + */ + public static class DashPattern { + + private final float[] pattern; + private final float phase; + + /** + * Creates a pattern with equal on and off length, starting with phase + * 0. + * + * @param onOff the length of the on/off part. + */ + public DashPattern(float onOff) { + this(onOff, onOff, 0f); + } + + /** + * Creates a pattern with different on and off length, starting with + * phase 0. + * + * @param on the length of the off part. + * @param off the length of the off part. + */ + public DashPattern(float on, float off) { + this(on, off, 0f); + } + + /** + * Creates a pattern with different on and off length, starting with the + * given phase . + * + * @param on the length of the off part. + * @param off the length of the off part. + * @param phase the phase to start the pattern with. + */ + public DashPattern(float on, float off, float phase) { + this.pattern = new float[]{on, off}; + this.phase = phase; + } + + public float getOn() { + return pattern[0]; + } + + public float getOff() { + return pattern[1]; + } + + public float[] getPattern() { + return pattern; + } + + public float getPhase() { + return phase; + } + + } + + private final CapStyle capStyle; + private final JoinStyle joinStyle; + private final DashPattern dashPattern; + private final float lineWidth; + + /** + * Creates a Stroke with line width 1, cap style + * {@link CapStyle#Cap}, join style {@link JoinStyle#Miter}, and no dash + * pattern. + */ + public Stroke() { + this(1f); + } + + /** + * Creates a Stroke with the given line width, cap style + * {@link CapStyle#Cap}, join style {@link JoinStyle#Miter}, and no dash + * pattern. + * + * @param lineWidth the line width. + */ + public Stroke(float lineWidth) { + this(CapStyle.Cap, JoinStyle.Miter, null, lineWidth); + } + + /** + * Creates a stroke with the given attributes. + * + * @param capStyle the cap style. + * @param joinStyle the join style. + * @param dashPattern the dash pattern. + * @param lineWidth the line width. + */ + public Stroke(CapStyle capStyle, JoinStyle joinStyle, + DashPattern dashPattern, float lineWidth) { + this.capStyle = capStyle; + this.joinStyle = joinStyle; + this.dashPattern = dashPattern; + this.lineWidth = lineWidth; + } + + public CapStyle getCapStyle() { + return capStyle; + } + + public JoinStyle getJoinStyle() { + return joinStyle; + } + + public DashPattern getDashPattern() { + return dashPattern; + } + + public float getLineWidth() { + return lineWidth; + } + + /** + * Applies this stroke to the given content stream. + * + * @param contentStream the content stream to apply this stroke to. + * @throws IOException by PDFBox. + */ + public void applyTo(final PDPageContentStream contentStream) + throws IOException { + if (getCapStyle() != null) { + contentStream.setLineCapStyle(getCapStyle().value()); + } + if (getJoinStyle() != null) { + contentStream.setLineJoinStyle(getJoinStyle().value()); + } + if (getDashPattern() != null) { + contentStream.setLineDashPattern(getDashPattern().getPattern(), + getDashPattern().getPhase()); + } + contentStream.setLineWidth(getLineWidth()); + } + + /** + * Creates a stroke builder providing a fluent interface for creating a stroke. + * + * @return a stroke builder. + */ + public static StrokeBuilder builder() { + return new StrokeBuilder(); + } + + /** + * A builder providing a fluent interface for creating a stroke. + */ + public static class StrokeBuilder { + private CapStyle capStyle = CapStyle.Cap; + private JoinStyle joinStyle = JoinStyle.Miter; + private DashPattern dashPattern; + float lineWidth = 1f; + + public StrokeBuilder capStyle(CapStyle capStyle) { + this.capStyle = capStyle; + return this; + } + + public StrokeBuilder joinStyle(JoinStyle joinStyle) { + this.joinStyle = joinStyle; + return this; + } + + public StrokeBuilder dashPattern(DashPattern dashPattern) { + this.dashPattern = dashPattern; + return this; + } + + public StrokeBuilder lineWidth(float lineWidth) { + this.lineWidth = lineWidth; + return this; + } + + public Stroke build() { + return new Stroke(capStyle, joinStyle, dashPattern, lineWidth); + } + } +} diff --git a/layout-pdfbox/src/main/java/org/xbib/graphics/layout/pdfbox/text/Alignment.java b/layout-pdfbox/src/main/java/org/xbib/graphics/layout/pdfbox/text/Alignment.java new file mode 100644 index 0000000..f8cbdcf --- /dev/null +++ b/layout-pdfbox/src/main/java/org/xbib/graphics/layout/pdfbox/text/Alignment.java @@ -0,0 +1,9 @@ +package org.xbib.graphics.layout.pdfbox.text; + +/** + * Enumeration for (vertical) alignment. + */ +public enum Alignment { + + Left, Center, Right, Justify +} diff --git a/layout-pdfbox/src/main/java/org/xbib/graphics/layout/pdfbox/text/Area.java b/layout-pdfbox/src/main/java/org/xbib/graphics/layout/pdfbox/text/Area.java new file mode 100644 index 0000000..9544f38 --- /dev/null +++ b/layout-pdfbox/src/main/java/org/xbib/graphics/layout/pdfbox/text/Area.java @@ -0,0 +1,21 @@ +package org.xbib.graphics.layout.pdfbox.text; + +import java.io.IOException; + +/** + * Defines an area with a width and height. + */ +public interface Area { + + /** + * @return the width of the area. + * @throws IOException by pdfbox + */ + float getWidth() throws IOException; + + /** + * @return the height of the area. + * @throws IOException by pdfbox + */ + float getHeight() throws IOException; +} diff --git a/layout-pdfbox/src/main/java/org/xbib/graphics/layout/pdfbox/text/BaseFont.java b/layout-pdfbox/src/main/java/org/xbib/graphics/layout/pdfbox/text/BaseFont.java new file mode 100644 index 0000000..23a944b --- /dev/null +++ b/layout-pdfbox/src/main/java/org/xbib/graphics/layout/pdfbox/text/BaseFont.java @@ -0,0 +1,52 @@ +package org.xbib.graphics.layout.pdfbox.text; + +import org.apache.pdfbox.pdmodel.font.PDFont; +import org.apache.pdfbox.pdmodel.font.PDType1Font; + +/** + * In order to easy handling with fonts, this enum bundles the + * plain/italic/bold/bold-italic variants of the three standard font types + * {@link PDType1Font#TIMES_ROMAN Times},{@link PDType1Font#COURIER Courier} and + * {@link PDType1Font#HELVETICA Helveticy}. + * + * @author Ralf + */ +public enum BaseFont { + + Times(PDType1Font.TIMES_ROMAN, PDType1Font.TIMES_BOLD, + PDType1Font.TIMES_ITALIC, PDType1Font.TIMES_BOLD_ITALIC), // + Courier(PDType1Font.COURIER, PDType1Font.COURIER_BOLD, + PDType1Font.COURIER_OBLIQUE, PDType1Font.COURIER_BOLD_OBLIQUE), // + Helvetica(PDType1Font.HELVETICA, PDType1Font.HELVETICA_BOLD, + PDType1Font.HELVETICA_OBLIQUE, PDType1Font.HELVETICA_BOLD_OBLIQUE); + + private final PDFont plainFont; + private final PDFont boldFont; + private final PDFont italicFont; + private final PDFont boldItalicFont; + + BaseFont(PDFont plainFont, PDFont boldFont, PDFont italicFont, + PDFont boldItalicFont) { + this.plainFont = plainFont; + this.boldFont = boldFont; + this.italicFont = italicFont; + this.boldItalicFont = boldItalicFont; + } + + public PDFont getPlainFont() { + return plainFont; + } + + public PDFont getBoldFont() { + return boldFont; + } + + public PDFont getItalicFont() { + return italicFont; + } + + public PDFont getBoldItalicFont() { + return boldItalicFont; + } + +} diff --git a/layout-pdfbox/src/main/java/org/xbib/graphics/layout/pdfbox/text/Constants.java b/layout-pdfbox/src/main/java/org/xbib/graphics/layout/pdfbox/text/Constants.java new file mode 100644 index 0000000..fc481cb --- /dev/null +++ b/layout-pdfbox/src/main/java/org/xbib/graphics/layout/pdfbox/text/Constants.java @@ -0,0 +1,28 @@ +package org.xbib.graphics.layout.pdfbox.text; + +import org.apache.pdfbox.pdmodel.common.PDRectangle; + +public class Constants { + + private static final int DEFAULT_USER_SPACE_UNIT_DPI = 72; + private static final float MM_TO_UNITS = 1 / (10 * 2.54f) + * DEFAULT_USER_SPACE_UNIT_DPI; + + public static final PDRectangle A0 = new PDRectangle(841 * MM_TO_UNITS, + 1189 * MM_TO_UNITS); + public static final PDRectangle A1 = new PDRectangle(594 * MM_TO_UNITS, + 841 * MM_TO_UNITS); + public static final PDRectangle A2 = new PDRectangle(420 * MM_TO_UNITS, + 594 * MM_TO_UNITS); + public static final PDRectangle A3 = new PDRectangle(297 * MM_TO_UNITS, + 420 * MM_TO_UNITS); + public static final PDRectangle A4 = new PDRectangle(210 * MM_TO_UNITS, + 297 * MM_TO_UNITS); + public static final PDRectangle A5 = new PDRectangle(148 * MM_TO_UNITS, + 210 * MM_TO_UNITS); + public static final PDRectangle A6 = new PDRectangle(105 * MM_TO_UNITS, + 148 * MM_TO_UNITS); + + public static final PDRectangle Letter = new PDRectangle(215.9f * MM_TO_UNITS, + 279.4f * MM_TO_UNITS); +} diff --git a/layout-pdfbox/src/main/java/org/xbib/graphics/layout/pdfbox/text/ControlCharacter.java b/layout-pdfbox/src/main/java/org/xbib/graphics/layout/pdfbox/text/ControlCharacter.java new file mode 100644 index 0000000..4179be7 --- /dev/null +++ b/layout-pdfbox/src/main/java/org/xbib/graphics/layout/pdfbox/text/ControlCharacter.java @@ -0,0 +1,83 @@ +package org.xbib.graphics.layout.pdfbox.text; + +import java.util.regex.Pattern; + +/** + * A control character represents the pattern to match and escape sequence for + * character sequences with a special meaning. Currently there is newline for + * all kinds of text, and bold and italic for markup. + */ +public class ControlCharacter implements CharSequence { + + private final String description; + private final String charaterToEscape; + + protected ControlCharacter(final String description, + final String charaterToEscape) { + this.description = description; + this.charaterToEscape = charaterToEscape; + } + + /** + * @return the character to escape, e.g. '*' for bold. + */ + public String getCharacterToEscape() { + return charaterToEscape; + } + + /** + * @return true if this control character must be escaped in + * text. + */ + public boolean mustEscape() { + return getCharacterToEscape() != null; + } + + /** + * Escapes the control character in the given text if necessary. + * + * @param text the text to escape. + * @return the escaped text. + */ + public String escape(final String text) { + if (!mustEscape()) { + return text; + } + return text.replaceAll(Pattern.quote(getCharacterToEscape()), "\\" + + getCharacterToEscape()); + } + + /** + * Un-escapes the control character in the given text if necessary. + * + * @param text the text to un-escape. + * @return the un-escaped text. + */ + public String unescape(final String text) { + if (!mustEscape()) { + return text; + } + return text.replaceAll("\\\\" + Pattern.quote(getCharacterToEscape()), + getCharacterToEscape()); + } + + @Override + public int length() { + return 0; + } + + @Override + public char charAt(int index) { + throw new ArrayIndexOutOfBoundsException(index); + } + + @Override + public CharSequence subSequence(int start, int end) { + return null; + } + + @Override + public String toString() { + return description; + } +} diff --git a/layout-pdfbox/src/main/java/org/xbib/graphics/layout/pdfbox/text/ControlCharacters.java b/layout-pdfbox/src/main/java/org/xbib/graphics/layout/pdfbox/text/ControlCharacters.java new file mode 100644 index 0000000..bc68dbe --- /dev/null +++ b/layout-pdfbox/src/main/java/org/xbib/graphics/layout/pdfbox/text/ControlCharacters.java @@ -0,0 +1,284 @@ +package org.xbib.graphics.layout.pdfbox.text; + +import java.awt.Color; +import java.util.List; +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +/** + * Container class for all control character factories. + */ +public class ControlCharacters { + + /** + * Unescapes the escape character backslash. + * + * @param text the text to escape. + * @return the unescaped text. + */ + public static String unescapeBackslash(final String text) { + return text.replaceAll(Pattern.quote("\\\\"), "\\\\"); + } + + /** + * A control character factory is used to create control characters on the + * fly from the control pattern. This allows to parameterize the characters + * as needed for e.g. colors. + */ + public interface ControlCharacterFactory { + + /** + * Creates the control character from the given matched pattern. + * + * @param text the parsed text. + * @param matcher the matcher. + * @param charactersSoFar the characters created so far. + * @return the created character. + */ + ControlCharacter createControlCharacter(final String text, + final Matcher matcher, final List charactersSoFar); + + /** + * @return the pattern used to match the control character. + */ + Pattern getPattern(); + + /** + * Indicates if the pattern should be applied to the begin of line only. + * + * @return true if the pattern is to be applied at the + * begin of a line. + */ + boolean patternMatchesBeginOfLine(); + + /** + * Unescapes the pattern. + * + * @param text the text to unescape. + * @return the unescaped text. + */ + String unescape(final String text); + + } + + /** + * The factory for bold control characters. + */ + public static ControlCharacterFactory BOLD_FACTORY = new StaticControlCharacterFactory( + new BoldControlCharacter(), BoldControlCharacter.PATTERN); + /** + * The factory for italic control characters. + */ + public static ControlCharacterFactory ITALIC_FACTORY = new StaticControlCharacterFactory( + new ItalicControlCharacter(), ItalicControlCharacter.PATTERN); + /** + * The factory for new line control characters. + */ + public static ControlCharacterFactory NEWLINE_FACTORY = new StaticControlCharacterFactory( + new NewLineControlCharacter(), NewLineControlCharacter.PATTERN); + /** + * The factory for color control characters. + */ + public static ControlCharacterFactory COLOR_FACTORY = new ColorControlCharacterFactory(); + + /** + * The factory for metrics control characters. + */ + public static MetricsControlCharacterFactory METRICS_FACTORY = new MetricsControlCharacterFactory(); + + /** + * An asterisk ('*') indicates switching of bold font mode in markup. It can + * be escaped with a backslash ('\'). + */ + public static class BoldControlCharacter extends ControlCharacter { + public static Pattern PATTERN = Pattern + .compile("(?{color:#ee22aa} indicates switching the color in markup, + * where the color is given as hex RGB code (ee22aa in this case). It can be + * escaped with a backslash ('\'). + */ + public static class ColorControlCharacter extends ControlCharacter { + private final Color color; + + protected ColorControlCharacter(final String hex) { + super("COLOR", ColorControlCharacterFactory.TO_ESCAPE); + int r = Integer.parseUnsignedInt(hex.substring(0, 2), 16); + int g = Integer.parseUnsignedInt(hex.substring(2, 4), 16); + int b = Integer.parseUnsignedInt(hex.substring(4, 6), 16); + this.color = new Color(r, g, b); + } + + public Color getColor() { + return color; + } + } + + private static class StaticControlCharacterFactory implements + ControlCharacterFactory { + + private final ControlCharacter controlCharacter; + private final Pattern pattern; + + public StaticControlCharacterFactory( + final ControlCharacter controlCharacter, final Pattern pattern) { + this.controlCharacter = controlCharacter; + this.pattern = pattern; + } + + @Override + public ControlCharacter createControlCharacter(String text, + Matcher matcher, final List charactersSoFar) { + return controlCharacter; + } + + @Override + public Pattern getPattern() { + return pattern; + } + + @Override + public String unescape(String text) { + return controlCharacter.unescape(text); + } + + @Override + public boolean patternMatchesBeginOfLine() { + return false; + } + + } + + private static class ColorControlCharacterFactory implements + ControlCharacterFactory { + + private final static Pattern PATTERN = Pattern + .compile("(? charactersSoFar) { + return new ColorControlCharacter(matcher.group(2)); + } + + @Override + public Pattern getPattern() { + return PATTERN; + } + + @Override + public String unescape(String text) { + return text + .replaceAll("\\\\" + Pattern.quote(TO_ESCAPE), TO_ESCAPE); + } + + @Override + public boolean patternMatchesBeginOfLine() { + return false; + } + + } + + public static class MetricsControlCharacter extends ControlCharacter { + private final float fontScale; + private final float baselineOffsetScale; + + protected MetricsControlCharacter(String name, final String fontScale, + final String baselineOffset) { + super(name, MetricsControlCharacterFactory.TO_ESCAPE); + this.fontScale = parse(fontScale, 1); + this.baselineOffsetScale = parse(baselineOffset, 0); + } + + private static float parse(final String text, final float defaultValue) { + if (text == null || text.trim().isEmpty()) { + return defaultValue; + } + return Float.parseFloat(text); + } + + public float getFontScale() { + return fontScale; + } + + public float getBaselineOffsetScale() { + return baselineOffsetScale; + } + + } + + private static class MetricsControlCharacterFactory implements + ControlCharacterFactory { + + private final static Pattern PATTERN = Pattern + .compile("(? charactersSoFar) { + boolean isSuperscript = "^".equals(matcher.group(2)); + String name = isSuperscript ? "SUPERSCRIPT" : "SUBSCRIPT"; + String baselineOffsetScale = isSuperscript ? "-0.4" : "0.15"; + if (matcher.groupCount() > 6 && matcher.group(6) != null) { + baselineOffsetScale = matcher.group(6); + } + String fontScale = "0.61"; + if (matcher.groupCount() > 4 && matcher.group(4) != null) { + fontScale = matcher.group(4); + } + return new MetricsControlCharacter(name, fontScale, baselineOffsetScale); + } + + @Override + public Pattern getPattern() { + return PATTERN; + } + + @Override + public String unescape(String text) { + return text + .replaceAll("\\\\" + Pattern.quote(TO_ESCAPE), TO_ESCAPE); + } + + @Override + public boolean patternMatchesBeginOfLine() { + return false; + } + + } + +} diff --git a/layout-pdfbox/src/main/java/org/xbib/graphics/layout/pdfbox/text/ControlFragment.java b/layout-pdfbox/src/main/java/org/xbib/graphics/layout/pdfbox/text/ControlFragment.java new file mode 100644 index 0000000..7d35651 --- /dev/null +++ b/layout-pdfbox/src/main/java/org/xbib/graphics/layout/pdfbox/text/ControlFragment.java @@ -0,0 +1,71 @@ +package org.xbib.graphics.layout.pdfbox.text; + +import org.apache.pdfbox.pdmodel.font.PDType1Font; +import java.awt.Color; +import java.io.IOException; + +/** + * A control fragment has no drawable representation but is meant to control the + * text rendering. + */ +public class ControlFragment implements TextFragment { + + protected final static FontDescriptor DEFAULT_FONT_DESCRIPTOR = new FontDescriptor( + PDType1Font.HELVETICA, 11); + + private String name; + private final String text; + private final FontDescriptor fontDescriptor; + private final Color color; + + protected ControlFragment(final String text, + final FontDescriptor fontDescriptor) { + this(null, text, fontDescriptor, Color.black); + } + + protected ControlFragment(final String name, final String text, + final FontDescriptor fontDescriptor, final Color color) { + this.name = name; + if (this.name == null) { + this.name = getClass().getSimpleName(); + } + this.text = text; + this.fontDescriptor = fontDescriptor; + this.color = color; + } + + @Override + public float getWidth() throws IOException { + return 0; + } + + @Override + public float getHeight() throws IOException { + return getFontDescriptor() == null ? 0 : getFontDescriptor().getSize(); + } + + @Override + public FontDescriptor getFontDescriptor() { + return fontDescriptor; + } + + protected String getName() { + return name; + } + + @Override + public String getText() { + return text; + } + + @Override + public Color getColor() { + return color; + } + + @Override + public String toString() { + return "ControlFragment [" + name + "]"; + } + +} diff --git a/layout-pdfbox/src/main/java/org/xbib/graphics/layout/pdfbox/text/DrawContext.java b/layout-pdfbox/src/main/java/org/xbib/graphics/layout/pdfbox/text/DrawContext.java new file mode 100644 index 0000000..632f2a7 --- /dev/null +++ b/layout-pdfbox/src/main/java/org/xbib/graphics/layout/pdfbox/text/DrawContext.java @@ -0,0 +1,26 @@ +package org.xbib.graphics.layout.pdfbox.text; + +import org.apache.pdfbox.pdmodel.PDDocument; +import org.apache.pdfbox.pdmodel.PDPage; +import org.apache.pdfbox.pdmodel.PDPageContentStream; + +/** + * Provides the current page and document to draw to. + */ +public interface DrawContext { + + /** + * @return the document to draw to. + */ + PDDocument getPdDocument(); + + /** + * @return the current page to draw to. + */ + PDPage getCurrentPage(); + + /** + * @return the current page content stream. + */ + PDPageContentStream getCurrentPageContentStream(); +} diff --git a/layout-pdfbox/src/main/java/org/xbib/graphics/layout/pdfbox/text/DrawListener.java b/layout-pdfbox/src/main/java/org/xbib/graphics/layout/pdfbox/text/DrawListener.java new file mode 100644 index 0000000..8781c96 --- /dev/null +++ b/layout-pdfbox/src/main/java/org/xbib/graphics/layout/pdfbox/text/DrawListener.java @@ -0,0 +1,18 @@ +package org.xbib.graphics.layout.pdfbox.text; + + +/** + * Called if an object has been drawn. + */ +public interface DrawListener { + + /** + * Indicates that an object has been drawn. + * + * @param drawnObject the drawn object. + * @param upperLeft the upper left position. + * @param width the width of the drawn object. + * @param height the height of the drawn object. + */ + void drawn(Object drawnObject, Position upperLeft, float width, float height); +} diff --git a/layout-pdfbox/src/main/java/org/xbib/graphics/layout/pdfbox/text/DrawableText.java b/layout-pdfbox/src/main/java/org/xbib/graphics/layout/pdfbox/text/DrawableText.java new file mode 100644 index 0000000..bee09b1 --- /dev/null +++ b/layout-pdfbox/src/main/java/org/xbib/graphics/layout/pdfbox/text/DrawableText.java @@ -0,0 +1,24 @@ +package org.xbib.graphics.layout.pdfbox.text; + +import org.apache.pdfbox.pdmodel.PDPageContentStream; +import java.io.IOException; + +/** + * Represents a drawable text. + */ +public interface DrawableText extends Area { + + /** + * Draws the text of the (PdfBox-) cursor position. + * + * @param contentStream the content stream used to render. + * @param upperLeft the upper left position to draw to. + * @param alignment the text alignment. + * @param drawListener the listener to + * {@link DrawListener#drawn(Object, Position, float, float) + * notify} on drawn objects. + * @throws IOException by pdfbox. + */ + void drawText(PDPageContentStream contentStream, Position upperLeft, + Alignment alignment, DrawListener drawListener) throws IOException; +} diff --git a/layout-pdfbox/src/main/java/org/xbib/graphics/layout/pdfbox/text/FontDescriptor.java b/layout-pdfbox/src/main/java/org/xbib/graphics/layout/pdfbox/text/FontDescriptor.java new file mode 100644 index 0000000..83802ff --- /dev/null +++ b/layout-pdfbox/src/main/java/org/xbib/graphics/layout/pdfbox/text/FontDescriptor.java @@ -0,0 +1,81 @@ +package org.xbib.graphics.layout.pdfbox.text; + +import org.apache.pdfbox.pdmodel.font.PDFont; + +/** + * Container for a Font and size. + */ +public class FontDescriptor { + + /** + * the associated font. + */ + private final PDFont font; + + /** + * the font size. + */ + private final float size; + + /** + * Creates the descriptor the the given font and size. + * + * @param font the font. + * @param size the size. + */ + public FontDescriptor(final PDFont font, final float size) { + this.font = font; + this.size = size; + } + + /** + * @return the font. + */ + public PDFont getFont() { + return font; + } + + /** + * @return the size. + */ + public float getSize() { + return size; + } + + @Override + public String toString() { + return "FontDescriptor [font=" + font + ", size=" + size + "]"; + } + + @Override + public int hashCode() { + final int prime = 31; + int result = 1; + result = prime * result + ((font == null) ? 0 : font.hashCode()); + result = prime * result + Float.floatToIntBits(size); + return result; + } + + @Override + public boolean equals(final Object obj) { + if (this == obj) { + return true; + } + if (obj == null) { + return false; + } + if (getClass() != obj.getClass()) { + return false; + } + final FontDescriptor other = (FontDescriptor) obj; + if (font == null) { + if (other.font != null) { + return false; + } + } else if (!font.equals(other.font)) { + return false; + } + return Float.floatToIntBits(size) == Float.floatToIntBits(other.size); + } + +} diff --git a/layout-pdfbox/src/main/java/org/xbib/graphics/layout/pdfbox/text/Indent.java b/layout-pdfbox/src/main/java/org/xbib/graphics/layout/pdfbox/text/Indent.java new file mode 100644 index 0000000..0cd97b8 --- /dev/null +++ b/layout-pdfbox/src/main/java/org/xbib/graphics/layout/pdfbox/text/Indent.java @@ -0,0 +1,176 @@ +package org.xbib.graphics.layout.pdfbox.text; + +import org.apache.pdfbox.pdmodel.font.PDFont; +import java.awt.Color; +import java.io.IOException; + +/** + * Control fragment that represents a indent in text. + */ +public class Indent extends ControlFragment { + + /** + * Constant for the indentation of 0. + */ + public final static Indent UNINDENT = new Indent(0); + + protected float indentWidth = 4; + protected SpaceUnit indentUnit = SpaceUnit.em; + protected Alignment alignment = Alignment.Left; + protected StyledText styledText; + + /** + * Creates a new line with the given font descriptor. + * + * @param indentWidth the indentation. + * @param indentUnit the indentation unit. + * @throws IOException by pdfbox + */ + public Indent(final float indentWidth, final SpaceUnit indentUnit) + throws IOException { + this("", indentWidth, indentUnit, DEFAULT_FONT_DESCRIPTOR, + Alignment.Left, Color.black); + } + + /** + * Creates a new line with the + * {@link ControlFragment#DEFAULT_FONT_DESCRIPTOR}'s font and the given + * height. + * + * @param label the label of the indentation. + * @param indentWidth the indentation. + * @param indentUnit the indentation unit. + * @param fontSize the font size, resp. the height of the new line. + * @param font the font to use. + * @throws IOException by pdfbox + */ + public Indent(final String label, final float indentWidth, + final SpaceUnit indentUnit, final float fontSize, final PDFont font) + throws IOException { + + this(label, indentWidth, indentUnit, fontSize, font, Alignment.Left, + Color.black); + } + + /** + * Creates a new line with the + * {@link ControlFragment#DEFAULT_FONT_DESCRIPTOR}'s font and the given + * height. + * + * @param label the label of the indentation. + * @param indentWidth the indentation. + * @param indentUnit the indentation unit. + * @param fontSize the font size, resp. the height of the new line. + * @param font the font to use. + * @param alignment the alignment of the label. + * @throws IOException by pdfbox + */ + public Indent(final String label, final float indentWidth, + final SpaceUnit indentUnit, final float fontSize, + final PDFont font, final Alignment alignment) throws IOException { + this(label, indentWidth, indentUnit, fontSize, font, alignment, + Color.black); + } + + /** + * Creates a new line with the + * {@link ControlFragment#DEFAULT_FONT_DESCRIPTOR}'s font and the given + * height. + * + * @param label the label of the indentation. + * @param indentWidth the indentation. + * @param indentUnit the indentation unit. + * @param fontSize the font size, resp. the height of the new line. + * @param font the font to use. + * @param alignment the alignment of the label. + * @param color the color to use. + * @throws IOException by pdfbox + */ + public Indent(final String label, final float indentWidth, + final SpaceUnit indentUnit, final float fontSize, + final PDFont font, final Alignment alignment, final Color color) + throws IOException { + this(label, indentWidth, indentUnit, + new FontDescriptor(font, fontSize), alignment, color); + } + + /** + * Creates a new line with the given font descriptor. + * + * @param label the label of the indentation. + * @param indentWidth the indentation width. + * @param indentUnit the indentation unit. + * @param fontDescriptor the font and size associated with this new line. + * @param alignment the alignment of the label. + * @param color the color to use. + * @throws IOException by pdfbox + */ + public Indent(final String label, final float indentWidth, + final SpaceUnit indentUnit, final FontDescriptor fontDescriptor, + final Alignment alignment, final Color color) throws IOException { + super("INDENT", label, fontDescriptor, color); + + float indent = calculateIndent(indentWidth, indentUnit, fontDescriptor); + float textWidth = 0; + if (label != null && !label.isEmpty()) { + textWidth = fontDescriptor.getSize() + * fontDescriptor.getFont().getStringWidth(label) / 1000f; + } + float marginLeft = 0; + float marginRight = 0; + if (textWidth < indent) { + switch (alignment) { + case Left: + marginRight = indent - textWidth; + break; + case Right: + marginLeft = indent - textWidth; + break; + default: + marginLeft = (indent - textWidth) / 2f; + marginRight = marginLeft; + break; + } + } + styledText = new StyledText(label, getFontDescriptor(), getColor(), 0, + marginLeft, marginRight); + } + + /** + * Directly creates an indent of the given width in pt. + * + * @param indentPt the indentation in pt. + */ + public Indent(final float indentPt) { + super("", DEFAULT_FONT_DESCRIPTOR); + styledText = new StyledText("", getFontDescriptor(), getColor(), 0, + indentPt, 0); + } + + private float calculateIndent(final float indentWidth, + final SpaceUnit indentUnit, final FontDescriptor fontDescriptor) + throws IOException { + if (indentWidth < 0) { + return 0; + } + return indentUnit.toPt(indentWidth, fontDescriptor); + } + + @Override + public float getWidth() throws IOException { + return styledText.getWidth(); + } + + /** + * @return a styled text representation of the indent. + */ + public StyledText toStyledText() { + return styledText; + } + + @Override + public String toString() { + return "ControlFragment [" + getName() + ", " + styledText + "]"; + } + +} diff --git a/layout-pdfbox/src/main/java/org/xbib/graphics/layout/pdfbox/text/IndentCharacters.java b/layout-pdfbox/src/main/java/org/xbib/graphics/layout/pdfbox/text/IndentCharacters.java new file mode 100644 index 0000000..c9f1a5a --- /dev/null +++ b/layout-pdfbox/src/main/java/org/xbib/graphics/layout/pdfbox/text/IndentCharacters.java @@ -0,0 +1,341 @@ +package org.xbib.graphics.layout.pdfbox.text; + +import org.apache.pdfbox.pdmodel.font.PDFont; +import org.xbib.graphics.layout.pdfbox.util.CompatibilityHelper; +import org.xbib.graphics.layout.pdfbox.util.Enumerator; +import org.xbib.graphics.layout.pdfbox.util.EnumeratorFactory; +import java.awt.Color; +import java.io.IOException; +import java.util.List; +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +/** + * Container class for current supported indentation control characters. + */ +public class IndentCharacters { + + /** + * The factory for indent control characters. + */ + public static ControlCharacters.ControlCharacterFactory INDENT_FACTORY = new IndentCharacterFactory(); + + /** + * Represent un-indentation, means effectively indent of 0. + */ + public static IndentCharacter UNINDENT_CHARACTER = new IndentCharacter("0", + "0", "pt"); + + /** + * An --{7em} indicates an indentation of 7 characters in markup, + * where the number, the unit, and the brackets are optional. Default + * indentation is 4 characters, default unit is 7em It can be + * escaped with a backslash ('\'). + */ + public static class IndentCharacter extends ControlCharacter { + + protected int level = 1; + protected float indentWidth = 4; + protected SpaceUnit indentUnit = SpaceUnit.em; + + public IndentCharacter(final String level, final String indentWidth, + final String indentUnit) { + super("INDENT", IndentCharacterFactory.TO_ESCAPE); + try { + this.level = level == null ? 0 : level.length() + 1; + } catch (NumberFormatException e) { + } + try { + this.indentUnit = indentUnit == null ? SpaceUnit.em : SpaceUnit + .valueOf(indentUnit); + } catch (NumberFormatException e) { + } + float defaultIndent = this.indentUnit == SpaceUnit.em ? 4 : 10; + try { + this.indentWidth = indentWidth == null ? defaultIndent + : Integer.parseInt(indentWidth); + } catch (NumberFormatException e) { + } + + } + + /** + * @return the level of indentation, where 0 means no indent. + */ + public int getLevel() { + return level; + } + + /** + * @return the next label to use on a subsequent indent. Makes only + * sense for enumerating indents. + */ + protected String nextLabel() { + return ""; + } + + /** + * Creates the actual {@link Indent} fragment from this control + * character. + * + * @param fontSize the current font size. + * @param font the current font. + * @param color the color to use. + * @return the new Indent. + * @throws IOException by pdfbox + */ + public Indent createNewIndent(final float fontSize, final PDFont font, + final Color color) throws IOException { + return new Indent(nextLabel(), level * indentWidth, indentUnit, + fontSize, font, Alignment.Right, color); + } + + @Override + public int hashCode() { + final int prime = 31; + int result = 1; + result = prime * result + + ((indentUnit == null) ? 0 : indentUnit.hashCode()); + result = prime * result + Float.floatToIntBits(indentWidth); + result = prime * result + level; + return result; + } + + @Override + public boolean equals(Object obj) { + if (this == obj) { + return true; + } + if (obj == null) { + return false; + } + if (getClass() != obj.getClass()) { + return false; + } + IndentCharacter other = (IndentCharacter) obj; + if (indentUnit != other.indentUnit) { + return false; + } + if (Float.floatToIntBits(indentWidth) != Float + .floatToIntBits(other.indentWidth)) { + return false; + } + return level == other.level; + } + + } + + /** + * An -+{--:7em} indicates a list indentation of 7 characters in + * markup, using -- as the bullet. The number, the unit, bullet + * character and the brackets are optional. Default indentation is 4 + * characters, default unit is em and the default bullet + * depends on {@link CompatibilityHelper#getBulletCharacter(int)}. It can be + * escaped with a backslash ('\'). + */ + public static class ListCharacter extends IndentCharacter { + + protected String label; + + protected ListCharacter(String level, String indentWidth, + String indentUnit, String bulletCharacter) { + super(level, indentWidth, indentUnit); + if (bulletCharacter != null) { + label = bulletCharacter; + if (!label.endsWith(" ")) { + label += " "; + } + } else { + label = CompatibilityHelper.getBulletCharacter(getLevel()) + " "; + } + } + + @Override + protected String nextLabel() { + return label; + } + + @Override + public int hashCode() { + final int prime = 31; + int result = super.hashCode(); + result = prime * result + ((label == null) ? 0 : label.hashCode()); + return result; + } + + @Override + public boolean equals(Object obj) { + if (this == obj) { + return true; + } + if (!super.equals(obj)) { + return false; + } + if (getClass() != obj.getClass()) { + return false; + } + ListCharacter other = (ListCharacter) obj; + if (label == null) { + return other.label == null; + } else return label.equals(other.label); + } + + } + + /** + * An -#{a):7em} indicates an enumeration indentation of 7 + * characters in markup, using a)...b)...etc as the + * enumeration. The number, the unit, enumeration type/separator, and the + * brackets are optional. Default indentation is 4 characters, default unit is + * em. Default enumeration are arabic numbers, the separator + * depends on the enumerator by default ('.' for arabic). For available + * enumerators see {@link EnumeratorFactory}.It can be escaped with a + * backslash ('\'). + */ + public static class EnumerationCharacter extends IndentCharacter { + + protected Enumerator enumerator; + protected String separator; + + protected EnumerationCharacter(String level, String indentWidth, + String indentUnit, String enumerationType, String separator) { + super(level, indentWidth, indentUnit); + + if (enumerationType == null) { + enumerationType = "1"; + } + enumerator = EnumeratorFactory.createEnumerator(enumerationType); + this.separator = separator != null ? separator : enumerator + .getDefaultSeperator(); + } + + @Override + protected String nextLabel() { + String next = enumerator.next(); + StringBuilder bob = new StringBuilder(next.length() + + separator.length() + 1); + bob.append(next); + bob.append(separator); + if (!separator.endsWith(" ")) { + bob.append(" "); + } + return bob.toString(); + } + + @Override + public int hashCode() { + final int prime = 31; + int result = super.hashCode(); + result = prime * result + + ((enumerator == null) ? 0 : enumerator.hashCode()); + result = prime * result + + ((separator == null) ? 0 : separator.hashCode()); + return result; + } + + @Override + public boolean equals(Object obj) { + if (this == obj) { + return true; + } + if (!super.equals(obj)) { + return false; + } + if (getClass() != obj.getClass()) { + return false; + } + EnumerationCharacter other = (EnumerationCharacter) obj; + if (enumerator == null) { + if (other.enumerator != null) { + return false; + } + } else if (other.enumerator == null) { + return false; + } else if (!enumerator.getClass().equals( + other.enumerator.getClass())) { + return false; + } + if (separator == null) { + return other.separator == null; + } else return separator.equals(other.separator); + } + + } + + private static class IndentCharacterFactory implements + ControlCharacters.ControlCharacterFactory { + + private final static Pattern PATTERN = Pattern + .compile("^-(!)|^([ ]*)-(-)(\\{(\\d*)(em|pt)?\\})?|^([ ]*)-(\\+)(\\{(.+)?:(\\d*)(em|pt)?\\})?|^([ ]*)-(#)(\\{((?!:).)?(.+)?:((\\d*))((em|pt))?\\})?"); + private final static Pattern UNESCAPE_PATTERN = Pattern + .compile("^\\\\([ ]*-[-|+|#])"); + + private final static String TO_ESCAPE = "--"; + + @Override + public ControlCharacter createControlCharacter(String text, + Matcher matcher, final List charactersSoFar) { + if ("!".equals(matcher.group(1))) { + return UNINDENT_CHARACTER; + } + + if ("-".equals(matcher.group(3))) { + return new IndentCharacter(matcher.group(2), matcher.group(5), + matcher.group(6)); + } + + if ("+".equals(matcher.group(8))) { + return new ListCharacter(matcher.group(7), matcher.group(11), + matcher.group(12), matcher.group(10)); + } + + if ("#".equals(matcher.group(14))) { + return new EnumerationCharacter(matcher.group(13), + matcher.group(18), matcher.group(20), + matcher.group(16), matcher.group(17)); + } + + throw new IllegalArgumentException("unkown indentation " + text); + } + + @Override + public Pattern getPattern() { + return PATTERN; + } + + @Override + public String unescape(String text) { + Matcher matcher = UNESCAPE_PATTERN.matcher(text); + if (!matcher.find()) { + return text; + } + return matcher.group(1) + text.substring(matcher.end()); + } + + @Override + public boolean patternMatchesBeginOfLine() { + return true; + } + + } + + public static void main(String[] args) { + Pattern PATTERN = Pattern// + .compile("^-(!)|^([ ]*)-(-)(\\{(\\d*)(em|pt)?\\})?|^([ ]*)-(\\+)(\\{(.)?:(\\d*)(em|pt)?\\})?|^([ ]*)-(#)(\\{((?!:).)?(.+)?:((\\d*))((em|pt))?\\})?"); + Matcher matcher = PATTERN.matcher(" -#{d:3em}"); + System.out.println("matches: " + matcher.find()); + if (!matcher.matches()) { + System.err.println("exit"); + return; + } + System.out.println("start: " + matcher.start()); + System.out.println("end: " + matcher.end()); + System.out.println("groups: " + matcher.groupCount()); + for (int i = 0; i < matcher.groupCount(); i++) { + System.out.println("group " + i + ": '" + matcher.group(i) + "'"); + } + // 2 - -> 1: blanks, 4: size, 5: unit + // 7 + -> 6: blanks, 9: sign, 10: size, 11: unit + // 11 # -> 12: blanks, 15: number-sign, 16: size, 18: unit + } +} diff --git a/layout-pdfbox/src/main/java/org/xbib/graphics/layout/pdfbox/text/NewLine.java b/layout-pdfbox/src/main/java/org/xbib/graphics/layout/pdfbox/text/NewLine.java new file mode 100644 index 0000000..e13c553 --- /dev/null +++ b/layout-pdfbox/src/main/java/org/xbib/graphics/layout/pdfbox/text/NewLine.java @@ -0,0 +1,37 @@ +package org.xbib.graphics.layout.pdfbox.text; + +/** + * Control fragment that represents a new line in text. It has a (font and) + * height in order to specify the height of an empty line. + */ +public class NewLine extends ControlFragment { + + /** + * Creates a new line with the + * {@link ControlFragment#DEFAULT_FONT_DESCRIPTOR}. + */ + public NewLine() { + this(DEFAULT_FONT_DESCRIPTOR); + } + + /** + * Creates a new line with the + * {@link ControlFragment#DEFAULT_FONT_DESCRIPTOR}'s font and the given + * height. + * + * @param fontSize the font size, resp. the height of the new line. + */ + public NewLine(final float fontSize) { + this(new FontDescriptor(DEFAULT_FONT_DESCRIPTOR.getFont(), fontSize)); + } + + /** + * Creates a new line with the given font descriptor. + * + * @param fontDescriptor the font and size associated with this new line. + */ + public NewLine(final FontDescriptor fontDescriptor) { + super("\n", fontDescriptor); + } + +} diff --git a/layout-pdfbox/src/main/java/org/xbib/graphics/layout/pdfbox/text/Position.java b/layout-pdfbox/src/main/java/org/xbib/graphics/layout/pdfbox/text/Position.java new file mode 100644 index 0000000..e7a3efe --- /dev/null +++ b/layout-pdfbox/src/main/java/org/xbib/graphics/layout/pdfbox/text/Position.java @@ -0,0 +1,80 @@ +package org.xbib.graphics.layout.pdfbox.text; + +/** + * In order to avoid dependencies to AWT classes (e.g. Point), we have our own + * silly implemenation of a position. + */ +public class Position { + + private final float x; + private final float y; + + /** + * Creates a position at the given coordinates. + * + * @param x the x coordinate. + * @param y the y coordinate. + */ + public Position(float x, float y) { + this.x = x; + this.y = y; + } + + /** + * @return the x coordinate of the position. + */ + public float getX() { + return x; + } + + /** + * @return the y coordinate of the position. + */ + public float getY() { + return y; + } + + /** + * Adds an offset to the current position and returns it as a new position. + * + * @param x the x offset to add. + * @param y the y offset to add. + * @return the new position. + */ + public Position add(final float x, final float y) { + return new Position(this.x + x, this.y + y); + } + + @Override + public String toString() { + return "Position [x=" + x + ", y=" + y + "]"; + } + + @Override + public int hashCode() { + final int prime = 31; + int result = 1; + result = prime * result + Float.floatToIntBits(x); + result = prime * result + Float.floatToIntBits(y); + return result; + } + + @Override + public boolean equals(Object obj) { + if (this == obj) { + return true; + } + if (obj == null) { + return false; + } + if (getClass() != obj.getClass()) { + return false; + } + Position other = (Position) obj; + if (Float.floatToIntBits(x) != Float.floatToIntBits(other.x)) { + return false; + } + return Float.floatToIntBits(y) == Float.floatToIntBits(other.y); + } + +} diff --git a/layout-pdfbox/src/main/java/org/xbib/graphics/layout/pdfbox/text/ReplacedWhitespace.java b/layout-pdfbox/src/main/java/org/xbib/graphics/layout/pdfbox/text/ReplacedWhitespace.java new file mode 100644 index 0000000..28f1333 --- /dev/null +++ b/layout-pdfbox/src/main/java/org/xbib/graphics/layout/pdfbox/text/ReplacedWhitespace.java @@ -0,0 +1,29 @@ +package org.xbib.graphics.layout.pdfbox.text; + +/** + * Acts as a replacement for whitespace that has been removed by word wrapping. + */ +public class ReplacedWhitespace extends ControlFragment { + + private final String replacedSpace; + + public ReplacedWhitespace(String replacedSpace, FontDescriptor fontDescriptor) { + super("", fontDescriptor); + + this.replacedSpace = replacedSpace; + } + + /** + * @return the replaced space. + */ + public String getReplacedSpace() { + return replacedSpace; + } + + /** + * @return the replaced fragment. + */ + public TextFragment toReplacedFragment() { + return new StyledText(getReplacedSpace(), getFontDescriptor()); + } +} diff --git a/layout-pdfbox/src/main/java/org/xbib/graphics/layout/pdfbox/text/SpaceUnit.java b/layout-pdfbox/src/main/java/org/xbib/graphics/layout/pdfbox/text/SpaceUnit.java new file mode 100644 index 0000000..d01fce4 --- /dev/null +++ b/layout-pdfbox/src/main/java/org/xbib/graphics/layout/pdfbox/text/SpaceUnit.java @@ -0,0 +1,34 @@ +package org.xbib.graphics.layout.pdfbox.text; + +import java.io.IOException; + +/** + * Unit to specify space, currently only em and pt. + */ +public enum SpaceUnit { + + /** + * The average character width of the associated font. + */ + em, + /** + * Measuring in points. + */ + pt; + + /** + * Converts the given unit to pt. + * + * @param size the size with respect to the unit. + * @param fontDescriptor the font/size to use. + * @return the size in pt. + * @throws IOException by pdfbox + */ + public float toPt(final float size, final FontDescriptor fontDescriptor) throws IOException { + if (this == em) { + return fontDescriptor.getSize() + * fontDescriptor.getFont().getAverageFontWidth() / 1000 * size; + } + return size; + } +} diff --git a/layout-pdfbox/src/main/java/org/xbib/graphics/layout/pdfbox/text/StyledText.java b/layout-pdfbox/src/main/java/org/xbib/graphics/layout/pdfbox/text/StyledText.java new file mode 100644 index 0000000..5e61e7a --- /dev/null +++ b/layout-pdfbox/src/main/java/org/xbib/graphics/layout/pdfbox/text/StyledText.java @@ -0,0 +1,217 @@ +package org.xbib.graphics.layout.pdfbox.text; + +import org.apache.pdfbox.pdmodel.font.PDFont; +import java.awt.Color; +import java.io.IOException; + +/** + * Base class representing drawable text styled with font, size, color etc. + */ +public class StyledText implements TextFragment { + + private final String text; + private final FontDescriptor fontDescriptor; + private final Color color; + private final float leftMargin; + private final float rightMargin; + private final float baselineOffset; + + /** + * The cached (calculated) width of the text. + */ + private Float width = null; + + /** + * Creates a styled text. + * + * @param text the text to draw. Must not contain line feeds ('\n'). + * @param size the size of the font. + * @param font the font to use. + */ + public StyledText(final String text, final float size, final PDFont font) { + this(text, size, font, Color.black); + } + + /** + * Creates a styled text. + * + * @param text the text to draw. Must not contain line feeds ('\n'). + * @param size the size of the font. + * @param font the font to use. + * @param color the color to use. + */ + public StyledText(final String text, final float size, final PDFont font, + final Color color) { + this(text, new FontDescriptor(font, size), color); + } + + /** + * Creates a styled text. + * + * @param text the text to draw. Must not contain line feeds ('\n'). + * @param size the size of the font. + * @param font the font to use. + * @param color the color to use. + * @param baselineOffset the offset of the baseline. + */ + public StyledText(final String text, final float size, final PDFont font, + final Color color, final float baselineOffset) { + this(text, new FontDescriptor(font, size), color, baselineOffset, 0, 0); + } + + /** + * Creates a styled text. + * + * @param text the text to draw. Must not contain line feeds ('\n'). + * @param fontDescriptor the font to use. + */ + public StyledText(final String text, final FontDescriptor fontDescriptor) { + this(text, fontDescriptor, Color.black); + } + + /** + * Creates a styled text. + * + * @param text the text to draw. Must not contain line feeds ('\n'). + * @param fontDescriptor the font to use. + * @param color the color to use. + */ + public StyledText(final String text, final FontDescriptor fontDescriptor, + final Color color) { + this(text, fontDescriptor, color, 0, 0, 0); + } + + /** + * Creates a styled text. + * + * @param text the text to draw. Must not contain line feeds ('\n'). + * @param fontDescriptor the font to use. + * @param color the color to use. + * @param baselineOffset the offset of the baseline. + * @param leftMargin the margin left to the text. + * @param rightMargin the margin right to the text. + */ + public StyledText(final String text, final FontDescriptor fontDescriptor, + final Color color, final float baselineOffset, + final float leftMargin, final float rightMargin) { + if (text.contains("\n")) { + throw new IllegalArgumentException( + "StyledText must not contain line breaks, use TextFragment.LINEBREAK for that"); + } + if (leftMargin < 0) { + throw new IllegalArgumentException("leftMargin must be >= 0"); + } + if (rightMargin < 0) { + throw new IllegalArgumentException("rightMargin must be >= 0"); + } + this.text = text; + this.fontDescriptor = fontDescriptor; + this.color = color; + this.leftMargin = leftMargin; + this.rightMargin = rightMargin; + this.baselineOffset = baselineOffset; + } + + /** + * @return the text to draw. + */ + public String getText() { + return text; + } + + /** + * @return the font to use to draw the text. + */ + public FontDescriptor getFontDescriptor() { + return fontDescriptor; + } + + @Override + public float getWidth() throws IOException { + if (width == null) { + width = getFontDescriptor().getSize() + * getFontDescriptor().getFont().getStringWidth(getText()) + / 1000; + width += leftMargin; + width += rightMargin; + } + return width; + } + + public float getWidthWithoutMargin() throws IOException { + return getWidth() - leftMargin - rightMargin; + } + + @Override + public float getHeight() throws IOException { + return getFontDescriptor().getSize(); + } + + /** + * @return the ascent of the associated font. + * @throws IOException by pdfbox. + */ + public float getAsent() throws IOException { + return getFontDescriptor().getSize() + * getFontDescriptor().getFont().getFontDescriptor().getAscent() + / 1000; + } + + public float getBaselineOffset() { + return baselineOffset; + } + + @Override + public Color getColor() { + return color; + } + + /** + * @return the margin left to the text represented by this object. + */ + public float getLeftMargin() { + return leftMargin; + } + + /** + * @return the margin right to the text represented by this object. + */ + public float getRightMargin() { + return rightMargin; + } + + /** + * @return indicates if this text has margin. + */ + public boolean hasMargin() { + return getLeftMargin() != 0 || getRightMargin() != 0; + } + + /** + * @return converts this text to a sequence. + */ + public TextSequence asSequence() { + TextLine line = new TextLine(); + line.add(this); + return line; + } + + public StyledText inheritAttributes(String text) { + return inheritAttributes(text, getLeftMargin(), getRightMargin()); + } + + public StyledText inheritAttributes(String text, float leftMargin, + float rightMargin) { + return new StyledText(text, getFontDescriptor(), getColor(), + getBaselineOffset(), leftMargin, rightMargin); + } + + @Override + public String toString() { + return "StyledText [text=" + text + ", fontDescriptor=" + + fontDescriptor + ", width=" + width + ", color=" + color + + ", leftMargin=" + leftMargin + ", rightMargin=" + rightMargin + + ", baselineOffset=" + baselineOffset + "]"; + } + +} diff --git a/layout-pdfbox/src/main/java/org/xbib/graphics/layout/pdfbox/text/TextFlow.java b/layout-pdfbox/src/main/java/org/xbib/graphics/layout/pdfbox/text/TextFlow.java new file mode 100644 index 0000000..7a2b84d --- /dev/null +++ b/layout-pdfbox/src/main/java/org/xbib/graphics/layout/pdfbox/text/TextFlow.java @@ -0,0 +1,287 @@ +package org.xbib.graphics.layout.pdfbox.text; + +import org.apache.pdfbox.pdmodel.PDPageContentStream; +import org.apache.pdfbox.pdmodel.font.PDFont; +import java.io.IOException; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.Iterator; +import java.util.List; +import java.util.Map; + +/** + * A text flow is a text sequence that {@link WidthRespecting respects a given + * width} by word wrapping the text. The text may contain line breaks ('\n').
      + * In order to ease creation of styled text, this class supports a kind of + * {@link #addMarkup(String, float, BaseFont) markup}. The following raw text + * + *
      + * Markup supports *bold*, _italic_, and *even _mixed* markup_.
      + * 
      + *

      + * is rendered like this: + * + *

      + * Markup supports bold, italic, and even mixed markup.
      + * 
      + *

      + * Use backslash to escape special characters '*', '_' and '\' itself: + * + *

      + * Escape \* with \\\* and \_ with \\\_ in markup.
      + * 
      + *

      + * is rendered like this: + * + *

      + * Escape * with \* and _ with \_ in markup.
      + * 
      + */ +public class TextFlow implements TextSequence, WidthRespecting { + + public static final float DEFAULT_LINE_SPACING = 1.2f; + private static final String HEIGHT = "height"; + private static final String WIDTH = "width"; + + private final Map cache = new HashMap(); + + private final List text = new ArrayList(); + private float lineSpacing = DEFAULT_LINE_SPACING; + private float maxWidth = -1; + private boolean applyLineSpacingToFirstLine = true; + + private void clearCache() { + cache.clear(); + } + + private void setCachedValue(final String key, Object value) { + cache.put(key, value); + } + + @SuppressWarnings("unchecked") + private T getCachedValue(final String key, Class type) { + return (T) cache.get(key); + } + + /** + * Adds some text associated with the font to draw. The text may contain + * line breaks ('\n'). + * + * @param text the text to add. + * @param fontSize the size of the font. + * @param font the font to use to draw the text. + * @throws IOException by PDFBox + */ + public void addText(final String text, final float fontSize, + final PDFont font) throws IOException { + add(TextFlowUtil.createTextFlow(text, fontSize, font)); + } + + /** + * Adds some markup to the text flow. + * + * @param markup the markup to add. + * @param fontSize the font size to use. + * @param baseFont the base font describing the bundle of + * plain/blold/italic/bold-italic fonts. + * @throws IOException by PDFBox + */ + public void addMarkup(final String markup, final float fontSize, + final BaseFont baseFont) throws IOException { + add(TextFlowUtil.createTextFlowFromMarkup(markup, fontSize, baseFont)); + } + + /** + * Adds some markup to the text flow. + * + * @param markup the markup to add. + * @param fontSize the font size to use. + * @param plainFont the plain font to use. + * @param boldFont the bold font to use. + * @param italicFont the italic font to use. + * @param boldItalicFont the bold-italic font to use. + * @throws IOException by PDFBox + */ + public void addMarkup(final String markup, final float fontSize, + final PDFont plainFont, final PDFont boldFont, + final PDFont italicFont, final PDFont boldItalicFont) throws IOException { + add(TextFlowUtil.createTextFlowFromMarkup(markup, fontSize, plainFont, + boldFont, italicFont, boldItalicFont)); + } + + /** + * Adds a text sequence to this flow. + * + * @param sequence the sequence to add. + */ + public void add(final TextSequence sequence) { + for (TextFragment fragment : sequence) { + add(fragment); + } + } + + /** + * Adds a text fragment to this flow. + * + * @param fragment the fragment to add. + */ + public void add(final TextFragment fragment) { + text.add(fragment); + clearCache(); + } + + /** + * Removes the last added fragment. + * + * @return the removed fragment (if any). + */ + public TextFragment removeLast() { + if (text.size() > 0) { + clearCache(); + return text.remove(text.size() - 1); + } + return null; + } + + /** + * @return the last added fragment (if any). + */ + public TextFragment getLast() { + if (text.size() > 0) { + clearCache(); + return text.get(text.size() - 1); + } + return null; + } + + /** + * @return true if this flow does not contain any fragments. + */ + public boolean isEmpty() { + return text.isEmpty(); + } + + @Override + public Iterator iterator() { + return text.iterator(); + } + + @Override + public float getMaxWidth() { + return maxWidth; + } + + @Override + public void setMaxWidth(float maxWidth) { + this.maxWidth = maxWidth; + clearCache(); + } + + /** + * @return the factor multiplied with the height to calculate the line + * spacing. + */ + public float getLineSpacing() { + return lineSpacing; + } + + /** + * Sets the factor multiplied with the height to calculate the line spacing. + * + * @param lineSpacing the line spacing factor. + */ + public void setLineSpacing(float lineSpacing) { + this.lineSpacing = lineSpacing; + clearCache(); + } + + /** + * Indicates if the line spacing should be applied to the first line. Makes + * sense if there is text above to achieve an equal spacing. In case you + * want to position the text precisely on top, you may set this value to + * false. Default is true. + * + * @return true if the line spacing should be applied to the + * first line. + */ + public boolean isApplyLineSpacingToFirstLine() { + return applyLineSpacingToFirstLine; + } + + /** + * Sets the indicator whether to apply line spacing to the first line. + * + * @param applyLineSpacingToFirstLine true if the line spacing should be applied to the + * first line. + * @see TextFlow#isApplyLineSpacingToFirstLine() + */ + public void setApplyLineSpacingToFirstLine( + boolean applyLineSpacingToFirstLine) { + this.applyLineSpacingToFirstLine = applyLineSpacingToFirstLine; + } + + @Override + public float getWidth() throws IOException { + Float width = getCachedValue(WIDTH, Float.class); + if (width == null) { + width = TextSequenceUtil.getWidth(this, getMaxWidth()); + setCachedValue(WIDTH, width); + } + return width; + } + + @Override + public float getHeight() throws IOException { + Float height = getCachedValue(HEIGHT, Float.class); + if (height == null) { + height = TextSequenceUtil.getHeight(this, getMaxWidth(), + getLineSpacing(), isApplyLineSpacingToFirstLine()); + setCachedValue(HEIGHT, height); + } + return height; + } + + @Override + public void drawText(PDPageContentStream contentStream, Position upperLeft, + Alignment alignment, DrawListener drawListener) throws IOException { + TextSequenceUtil.drawText(this, contentStream, upperLeft, drawListener, alignment, + getMaxWidth(), getLineSpacing(), + isApplyLineSpacingToFirstLine()); + } + + public void drawTextRightAligned(PDPageContentStream contentStream, + Position endOfFirstLine, DrawListener drawListener) throws IOException { + drawText(contentStream, endOfFirstLine.add(-getWidth(), 0), + Alignment.Right, drawListener); + } + + /** + * @return a copy of this text flow where all leading {@link NewLine}s are removed. + * @throws IOException by pdfbox. + */ + public TextFlow removeLeadingEmptyLines() throws IOException { + if (text.size() == 0 || !(text.get(0) instanceof NewLine)) { + return this; + } + TextFlow result = createInstance(); + result.setApplyLineSpacingToFirstLine(this.isApplyLineSpacingToFirstLine()); + result.setLineSpacing(this.getLineSpacing()); + result.setMaxWidth(this.getMaxWidth()); + for (TextFragment fragment : this) { + if (!result.isEmpty() || !(fragment instanceof NewLine)) { + result.add(fragment); + } + } + return result; + } + + protected TextFlow createInstance() { + return new TextFlow(); + } + + @Override + public String toString() { + return "TextFlow [text=" + text + "]"; + } + +} diff --git a/layout-pdfbox/src/main/java/org/xbib/graphics/layout/pdfbox/text/TextFlowUtil.java b/layout-pdfbox/src/main/java/org/xbib/graphics/layout/pdfbox/text/TextFlowUtil.java new file mode 100644 index 0000000..bd7e12b --- /dev/null +++ b/layout-pdfbox/src/main/java/org/xbib/graphics/layout/pdfbox/text/TextFlowUtil.java @@ -0,0 +1,354 @@ +package org.xbib.graphics.layout.pdfbox.text; + +import org.apache.pdfbox.pdmodel.font.PDFont; +import org.xbib.graphics.layout.pdfbox.text.annotations.AnnotatedStyledText; +import org.xbib.graphics.layout.pdfbox.text.annotations.Annotation; +import org.xbib.graphics.layout.pdfbox.text.annotations.AnnotationCharacters; +import java.awt.Color; +import java.io.IOException; +import java.util.ArrayList; +import java.util.Collections; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.Stack; +import java.util.regex.Matcher; + +public class TextFlowUtil { + + /** + * Creates a text flow from the given text. The text may contain line + * breaks. + * + * @param text the text + * @param fontSize the font size to use. + * @param font the font to use. + * @return the created text flow. + * @throws IOException by pdfbox + */ + public static TextFlow createTextFlow(final String text, + final float fontSize, final PDFont font) throws IOException { + final Iterable parts = fromPlainText(text); + return createTextFlow(parts, fontSize, font, font, font, font); + } + + /** + * Convenience alternative to + * {@link #createTextFlowFromMarkup(String, float, PDFont, PDFont, PDFont, PDFont)} + * which allows to specifies the fonts to use by using the {@link BaseFont} + * enum. + * + * @param markup the markup text. + * @param fontSize the font size to use. + * @param baseFont the base font describing the bundle of + * plain/blold/italic/bold-italic fonts. + * @return the created text flow. + * @throws IOException by pdfbox + */ + public static TextFlow createTextFlowFromMarkup(final String markup, + final float fontSize, final BaseFont baseFont) throws IOException { + return createTextFlowFromMarkup(markup, fontSize, + baseFont.getPlainFont(), baseFont.getBoldFont(), + baseFont.getItalicFont(), baseFont.getBoldItalicFont()); + } + + /** + * Creates a text flow from the given text. The text may contain line + * breaks, and also supports some markup for creating bold and italic fonts. + * The following raw text + * + *
      +     * Markup supports *bold*, _italic_, and *even _mixed* markup_.
      +     * 
      + *

      + * is rendered like this: + * + *

      +     * Markup supports bold, italic, and even mixed markup.
      +     * 
      + *

      + * Use backslash to escape special characters '*', '_' and '\' itself: + * + *

      +     * Escape \* with \\\* and \_ with \\\_ in markup.
      +     * 
      + *

      + * is rendered like this: + * + *

      +     * Escape * with \* and _ with \_ in markup.
      +     * 
      + * + * @param markup the markup text. + * @param fontSize the font size to use. + * @param plainFont the plain font. + * @param boldFont the bold font. + * @param italicFont the italic font. + * @param boldItalicFont the bold-italic font. + * @return the created text flow. + * @throws IOException by pdfbox + */ + public static TextFlow createTextFlowFromMarkup(final String markup, + final float fontSize, final PDFont plainFont, + final PDFont boldFont, final PDFont italicFont, + final PDFont boldItalicFont) throws IOException { + final Iterable parts = fromMarkup(markup); + return createTextFlow(parts, fontSize, plainFont, boldFont, italicFont, + boldItalicFont); + } + + /** + * Actually creates the text flow from the given (markup) text. + * + * @param parts the parts to create the text flow from. + * @param fontSize the font size to use. + * @param plainFont the plain font. + * @param boldFont the bold font. + * @param italicFont the italic font. + * @param boldItalicFont the bold-italic font. + * @return the created text flow. + * @throws IOException by pdfbox + */ + protected static TextFlow createTextFlow( + final Iterable parts, final float fontSize, + final PDFont plainFont, final PDFont boldFont, + final PDFont italicFont, final PDFont boldItalicFont) + throws IOException { + final TextFlow result = new TextFlow(); + boolean bold = false; + boolean italic = false; + Color color = Color.black; + ControlCharacters.MetricsControlCharacter metricsControl = null; + Map, Annotation> annotationMap = new HashMap, Annotation>(); + Stack indentStack = new Stack(); + for (final CharSequence fragment : parts) { + + if (fragment instanceof ControlCharacter) { + if (fragment instanceof ControlCharacters.NewLineControlCharacter) { + result.add(new NewLine(fontSize)); + } + if (fragment instanceof ControlCharacters.BoldControlCharacter) { + bold = !bold; + } + if (fragment instanceof ControlCharacters.ItalicControlCharacter) { + italic = !italic; + } + if (fragment instanceof ControlCharacters.ColorControlCharacter) { + color = ((ControlCharacters.ColorControlCharacter) fragment).getColor(); + } + if (fragment instanceof AnnotationCharacters.AnnotationControlCharacter) { + AnnotationCharacters.AnnotationControlCharacter annotationControlCharacter = (AnnotationCharacters.AnnotationControlCharacter) fragment; + if (annotationMap.containsKey(annotationControlCharacter.getAnnotationType())) { + annotationMap.remove(annotationControlCharacter + .getAnnotationType()); + } else { + annotationMap.put( + annotationControlCharacter.getAnnotationType(), + annotationControlCharacter.getAnnotation()); + } + } + if (fragment instanceof ControlCharacters.MetricsControlCharacter) { + if (metricsControl != null && metricsControl.toString().equals(fragment.toString())) { + // end marker + metricsControl = null; + } else { + metricsControl = (ControlCharacters.MetricsControlCharacter) fragment; + } + } + if (fragment instanceof IndentCharacters.IndentCharacter) { + IndentCharacters.IndentCharacter currentIndent = (IndentCharacters.IndentCharacter) fragment; + if (currentIndent.getLevel() == 0) { + // indentation of 0 resets indent + indentStack.clear(); + result.add(Indent.UNINDENT); + continue; + } else { + IndentCharacters.IndentCharacter last = null; + while (!indentStack.isEmpty() + && indentStack.peek() != null + && currentIndent.getLevel() <= indentStack + .peek().getLevel()) { + last = indentStack.pop(); + } + if (last != null && last.equals(currentIndent)) { + currentIndent = last; + } + indentStack.push(currentIndent); + result.add(currentIndent.createNewIndent(fontSize, + plainFont, color)); + } + } + } else { + PDFont font = getFont(bold, italic, plainFont, boldFont, + italicFont, boldItalicFont); + float baselineOffset = 0; + float currentFontSize = fontSize; + if (metricsControl != null) { + baselineOffset = metricsControl.getBaselineOffsetScale() * fontSize; + currentFontSize *= metricsControl.getFontScale(); + } + if (annotationMap.isEmpty()) { + StyledText styledText = new StyledText(fragment.toString(), + currentFontSize, font, color, baselineOffset); + result.add(styledText); + } else { + AnnotatedStyledText styledText = new AnnotatedStyledText( + fragment.toString(), currentFontSize, font, color, baselineOffset, + annotationMap.values()); + result.add(styledText); + } + } + } + return result; + } + + protected static PDFont getFont(boolean bold, boolean italic, + final PDFont plainFont, final PDFont boldFont, + final PDFont italicFont, final PDFont boldItalicFont) { + PDFont font = plainFont; + if (bold && !italic) { + font = boldFont; + } else if (!bold && italic) { + font = italicFont; + } else if (bold && italic) { + font = boldItalicFont; + } + return font; + } + + /** + * Creates a char sequence where new-line is replaced by the corresponding + * {@link ControlCharacter}. + * + * @param text the original text. + * @return the create char sequence. + */ + public static Iterable fromPlainText(final CharSequence text) { + return fromPlainText(Collections.singleton(text)); + } + + /** + * Creates a char sequence where new-line is replaced by the corresponding + * {@link ControlCharacter}. + * + * @param text the original text. + * @return the create char sequence. + */ + public static Iterable fromPlainText( + final Iterable text) { + Iterable result = splitByControlCharacter( + ControlCharacters.NEWLINE_FACTORY, text); + result = unescapeBackslash(result); + return result; + } + + /** + * Creates a char sequence where new-line, asterisk and underscore are + * replaced by their corresponding {@link ControlCharacter}. + * + * @param markup the markup. + * @return the create char sequence. + */ + public static Iterable fromMarkup(final CharSequence markup) { + return fromMarkup(Collections.singleton(markup)); + } + + /** + * Creates a char sequence where new-line, asterisk and underscore are + * replaced by their corresponding {@link ControlCharacter}. + * + * @param markup the markup. + * @return the create char sequence. + */ + public static Iterable fromMarkup( + final Iterable markup) { + Iterable text = markup; + text = splitByControlCharacter(ControlCharacters.NEWLINE_FACTORY, text); + text = splitByControlCharacter(ControlCharacters.METRICS_FACTORY, text); + text = splitByControlCharacter(ControlCharacters.BOLD_FACTORY, text); + text = splitByControlCharacter(ControlCharacters.ITALIC_FACTORY, text); + text = splitByControlCharacter(ControlCharacters.COLOR_FACTORY, text); + + for (AnnotationCharacters.AnnotationControlCharacterFactory annotationControlCharacterFactory : AnnotationCharacters + .getFactories()) { + text = splitByControlCharacter(annotationControlCharacterFactory, + text); + } + + text = splitByControlCharacter(IndentCharacters.INDENT_FACTORY, text); + + text = unescapeBackslash(text); + + return text; + } + + /** + * Splits the sequence by the given control character and replaces its + * markup representation by the {@link ControlCharacter}. + * + * @param controlCharacterFactory the control character to split by. + * @param markup the markup to split. + * @return the splitted and replaced sequence. + */ + protected static Iterable splitByControlCharacter( + ControlCharacters.ControlCharacterFactory controlCharacterFactory, + final Iterable markup) { + List result = new ArrayList(); + boolean beginOfLine = true; + for (CharSequence current : markup) { + if (current instanceof String) { + String string = (String) current; + int begin = 0; + + if (!controlCharacterFactory.patternMatchesBeginOfLine() + || beginOfLine) { + Matcher matcher = controlCharacterFactory.getPattern() + .matcher(string); + while (matcher.find()) { + String part = string.substring(begin, matcher.start()); + begin = matcher.end(); + + if (!part.isEmpty()) { + String unescaped = controlCharacterFactory + .unescape(part); + result.add(unescaped); + } + + result.add(controlCharacterFactory + .createControlCharacter(string, matcher, result)); + } + } + + if (begin < string.length()) { + String part = string.substring(begin); + String unescaped = controlCharacterFactory.unescape(part); + result.add(unescaped); + } + + beginOfLine = false; + } else { + if (current instanceof ControlCharacters.NewLineControlCharacter) { + beginOfLine = true; + } + result.add(current); + } + + } + return result; + } + + private static Iterable unescapeBackslash( + final Iterable chars) { + List result = new ArrayList(); + for (CharSequence current : chars) { + if (current instanceof String) { + result.add(ControlCharacters + .unescapeBackslash((String) current)); + } else { + result.add(current); + } + } + return result; + } + +} diff --git a/layout-pdfbox/src/main/java/org/xbib/graphics/layout/pdfbox/text/TextFragment.java b/layout-pdfbox/src/main/java/org/xbib/graphics/layout/pdfbox/text/TextFragment.java new file mode 100644 index 0000000..7970a35 --- /dev/null +++ b/layout-pdfbox/src/main/java/org/xbib/graphics/layout/pdfbox/text/TextFragment.java @@ -0,0 +1,25 @@ +package org.xbib.graphics.layout.pdfbox.text; + +import java.awt.Color; + +/** + * A text fragment describes a drawable piece of text associated with a font, + * size and color. + */ +public interface TextFragment extends Area { + + /** + * @return the text. + */ + String getText(); + + /** + * @return the font and size to use to draw the text. + */ + FontDescriptor getFontDescriptor(); + + /** + * @return the color to use to draw the text. + */ + Color getColor(); +} diff --git a/layout-pdfbox/src/main/java/org/xbib/graphics/layout/pdfbox/text/TextLine.java b/layout-pdfbox/src/main/java/org/xbib/graphics/layout/pdfbox/text/TextLine.java new file mode 100644 index 0000000..98ba269 --- /dev/null +++ b/layout-pdfbox/src/main/java/org/xbib/graphics/layout/pdfbox/text/TextLine.java @@ -0,0 +1,274 @@ +package org.xbib.graphics.layout.pdfbox.text; + +import org.apache.pdfbox.pdmodel.PDPageContentStream; +import org.xbib.graphics.layout.pdfbox.util.CompatibilityHelper; +import java.awt.Color; +import java.io.IOException; +import java.util.ArrayList; +import java.util.Collections; +import java.util.HashMap; +import java.util.Iterator; +import java.util.List; +import java.util.Map; + +/** + * A text of line containing only {@link StyledText}s. It may be terminated by a + * {@link #getNewLine() new line}. + */ +public class TextLine implements TextSequence { + + /** + * The font ascent. + */ + private static final String ASCENT = "ascent"; + /** + * The font height. + */ + private static final String HEIGHT = "height"; + /** + * The text width. + */ + private static final String WIDTH = "width"; + + private final List styledTextList = new ArrayList(); + private NewLine newLine; + private final Map cache = new HashMap(); + + private void clearCache() { + cache.clear(); + } + + private void setCachedValue(final String key, Object value) { + cache.put(key, value); + } + + @SuppressWarnings("unchecked") + private T getCachedValue(final String key, Class type) { + return (T) cache.get(key); + } + + /** + * Adds a styled text. + * + * @param fragment the fagment to add. + */ + public void add(final StyledText fragment) { + styledTextList.add(fragment); + clearCache(); + } + + /** + * Adds all styled texts of the given text line. + * + * @param textLine the text line to add. + */ + public void add(final TextLine textLine) { + for (StyledText fragment : textLine.getStyledTexts()) { + add(fragment); + } + } + + /** + * @return the terminating new line, may be null. + */ + public NewLine getNewLine() { + return newLine; + } + + /** + * Sets the new line. + * + * @param newLine the new line. + */ + public void setNewLine(NewLine newLine) { + this.newLine = newLine; + clearCache(); + } + + /** + * @return the styled texts building up this line. + */ + public List getStyledTexts() { + return Collections.unmodifiableList(styledTextList); + } + + @Override + public Iterator iterator() { + return new TextLineIterator(styledTextList.iterator(), newLine); + } + + /** + * @return true if the line contains neither styled text nor a + * new line. + */ + public boolean isEmpty() { + return styledTextList.isEmpty() && newLine == null; + } + + @Override + public float getWidth() throws IOException { + Float width = getCachedValue(WIDTH, Float.class); + if (width == null) { + width = 0f; + for (TextFragment fragment : this) { + width += fragment.getWidth(); + } + setCachedValue(WIDTH, width); + } + return width; + } + + @Override + public float getHeight() throws IOException { + Float height = getCachedValue(HEIGHT, Float.class); + if (height == null) { + height = 0f; + for (TextFragment fragment : this) { + height = Math.max(height, fragment.getHeight()); + } + setCachedValue(HEIGHT, height); + } + return height; + } + + /** + * @return the (max) ascent of this line. + * @throws IOException by pdfbox. + */ + protected float getAscent() throws IOException { + Float ascent = getCachedValue(ASCENT, Float.class); + if (ascent == null) { + ascent = 0f; + for (TextFragment fragment : this) { + float currentAscent = fragment.getFontDescriptor().getSize() + * fragment.getFontDescriptor().getFont() + .getFontDescriptor().getAscent() / 1000; + ascent = Math.max(ascent, currentAscent); + } + setCachedValue(ASCENT, ascent); + } + return ascent; + } + + @Override + public void drawText(PDPageContentStream contentStream, Position upperLeft, + Alignment alignment, DrawListener drawListener) throws IOException { + drawAligned(contentStream, upperLeft, alignment, getWidth(), drawListener); + } + + public void drawAligned(PDPageContentStream contentStream, Position upperLeft, + Alignment alignment, float availableLineWidth, + DrawListener drawListener) throws IOException { + contentStream.saveGraphicsState(); + contentStream.beginText(); + + float x = upperLeft.getX(); + float y = upperLeft.getY() - getAscent(); // the baseline + float offset = TextSequenceUtil.getOffset(this, availableLineWidth, alignment); + x += offset; + CompatibilityHelper.setTextTranslation(contentStream, x, y); + float extraWordSpacing = 0; + if (alignment == Alignment.Justify && (getNewLine() instanceof WrappingNewLine)) { + extraWordSpacing = (availableLineWidth - getWidth()) / (styledTextList.size() - 1); + } + + FontDescriptor lastFontDesc = null; + float lastBaselineOffset = 0; + Color lastColor = null; + float gap = 0; + for (StyledText styledText : styledTextList) { + if (!styledText.getFontDescriptor().equals(lastFontDesc)) { + lastFontDesc = styledText.getFontDescriptor(); + contentStream.setFont(lastFontDesc.getFont(), + lastFontDesc.getSize()); + } + if (!styledText.getColor().equals(lastColor)) { + lastColor = styledText.getColor(); + contentStream.setNonStrokingColor(lastColor); + } + if (styledText.getLeftMargin() > 0) { + gap += styledText.getLeftMargin(); + } + + boolean moveBaseline = styledText.getBaselineOffset() != lastBaselineOffset; + if (moveBaseline || gap > 0) { + float baselineDelta = lastBaselineOffset - styledText.getBaselineOffset(); + lastBaselineOffset = styledText.getBaselineOffset(); + CompatibilityHelper.moveTextPosition(contentStream, gap, baselineDelta); + x += gap; + } + if (styledText.getText().length() > 0) { + CompatibilityHelper.showText(contentStream, + styledText.getText()); + } + + if (drawListener != null) { + float currentUpperLeft = y + styledText.getAsent(); + drawListener.drawn(styledText, + new Position(x, currentUpperLeft), + styledText.getWidthWithoutMargin(), + styledText.getHeight()); + } + x += styledText.getWidthWithoutMargin(); + + gap = extraWordSpacing; + if (styledText.getRightMargin() > 0) { + gap += styledText.getRightMargin(); + } + } + contentStream.endText(); + contentStream.restoreGraphicsState(); + } + + @Override + public String toString() { + return "TextLine [styledText=" + styledTextList + ", newLine=" + + newLine + "]"; + } + + /** + * An iterator for the text line. See {@link TextLine#iterator()}. + */ + private static class TextLineIterator implements Iterator { + + private final Iterator styledText; + private NewLine newLine; + + /** + * Creates an iterator of the given styled texts with an optional + * trailing new line. + * + * @param styledText the text fragments to iterate. + * @param newLine the optional trailing new line. + */ + public TextLineIterator(Iterator styledText, NewLine newLine) { + super(); + this.styledText = styledText; + this.newLine = newLine; + } + + @Override + public boolean hasNext() { + return styledText.hasNext() || newLine != null; + } + + @Override + public TextFragment next() { + TextFragment next = null; + if (styledText.hasNext()) { + next = styledText.next(); + } else if (newLine != null) { + next = newLine; + newLine = null; + } + return next; + } + + @Override + public void remove() { + throw new UnsupportedOperationException(); + } + + } + +} diff --git a/layout-pdfbox/src/main/java/org/xbib/graphics/layout/pdfbox/text/TextSequence.java b/layout-pdfbox/src/main/java/org/xbib/graphics/layout/pdfbox/text/TextSequence.java new file mode 100644 index 0000000..18071b2 --- /dev/null +++ b/layout-pdfbox/src/main/java/org/xbib/graphics/layout/pdfbox/text/TextSequence.java @@ -0,0 +1,9 @@ +package org.xbib.graphics.layout.pdfbox.text; + +/** + * A tagging interface describing some drawable text consisting of text + * fragments. + */ +public interface TextSequence extends DrawableText, Iterable { + +} diff --git a/layout-pdfbox/src/main/java/org/xbib/graphics/layout/pdfbox/text/TextSequenceUtil.java b/layout-pdfbox/src/main/java/org/xbib/graphics/layout/pdfbox/text/TextSequenceUtil.java new file mode 100644 index 0000000..c9b967d --- /dev/null +++ b/layout-pdfbox/src/main/java/org/xbib/graphics/layout/pdfbox/text/TextSequenceUtil.java @@ -0,0 +1,582 @@ +package org.xbib.graphics.layout.pdfbox.text; + +import org.apache.pdfbox.pdmodel.PDPageContentStream; +import org.xbib.graphics.layout.pdfbox.elements.Dividable.Divided; +import org.xbib.graphics.layout.pdfbox.elements.Paragraph; +import org.xbib.graphics.layout.pdfbox.util.Pair; +import org.xbib.graphics.layout.pdfbox.util.WordBreakerFactory; +import java.io.IOException; +import java.util.ArrayList; +import java.util.List; + +/** + * Utility methods for dealing with text sequences. + */ +public class TextSequenceUtil { + + /** + * Dissects the given sequence into {@link TextLine}s. + * + * @param text the text to extract the lines from. + * @return the list of text lines. + * @throws IOException by pdfbox + */ + public static List getLines(final TextSequence text) + throws IOException { + final List result = new ArrayList(); + + TextLine line = new TextLine(); + for (TextFragment fragment : text) { + if (fragment instanceof NewLine) { + line.setNewLine((NewLine) fragment); + result.add(line); + line = new TextLine(); + } else if (fragment instanceof ReplacedWhitespace) { + // ignore replaced whitespace + } else { + line.add((StyledText) fragment); + } + } + if (!line.isEmpty()) { + result.add(line); + } + return result; + } + + /** + * Word-wraps and divides the given text sequence. + * + * @param text the text to divide. + * @param maxWidth the max width used for word-wrapping. + * @param maxHeight the max height for divide. + * @return the Divided element containing the parts. + * @throws IOException by pdfbox + */ + public static Divided divide(final TextSequence text, final float maxWidth, + final float maxHeight) throws IOException { + TextFlow wrapped = wordWrap(text, maxWidth); + List lines = getLines(wrapped); + + Paragraph first = new Paragraph(); + Paragraph tail = new Paragraph(); + if (text instanceof TextFlow) { + TextFlow flow = (TextFlow) text; + first.setMaxWidth(flow.getMaxWidth()); + first.setLineSpacing(flow.getLineSpacing()); + tail.setMaxWidth(flow.getMaxWidth()); + tail.setLineSpacing(flow.getLineSpacing()); + } + if (text instanceof Paragraph) { + Paragraph paragraph = (Paragraph) text; + first.setAlignment(paragraph.getAlignment()); + first.setApplyLineSpacingToFirstLine(paragraph.isApplyLineSpacingToFirstLine()); + tail.setAlignment(paragraph.getAlignment()); + tail.setApplyLineSpacingToFirstLine(paragraph.isApplyLineSpacingToFirstLine()); + } + + int index = 0; + do { + TextLine line = lines.get(index); + first.add(line); + ++index; + } while (index < lines.size() && first.getHeight() < maxHeight); + + if (first.getHeight() > maxHeight) { + // remove last line + --index; + TextLine line = lines.get(index); + for (@SuppressWarnings("unused") + TextFragment textFragment : line) { + first.removeLast(); + } + } + + for (int i = index; i < lines.size(); ++i) { + tail.add(lines.get(i)); + } + return new Divided(first, tail); + } + + /** + * Word-wraps the given text sequence in order to fit the max width. + * + * @param text the text to word-wrap. + * @param maxWidth the max width to fit. + * @return the word-wrapped text. + * @throws IOException by pdfbox + */ + public static TextFlow wordWrap(final TextSequence text, + final float maxWidth) throws IOException { + + float indentation = 0; + TextFlow result = new TextFlow(); + float lineLength = indentation; + boolean isWrappedLine = false; + for (TextFragment fragment : text) { + if (fragment instanceof NewLine) { + isWrappedLine = fragment instanceof WrappingNewLine; + result.add(fragment); + lineLength = indentation; + if (indentation > 0) { + result.add(new Indent(indentation).toStyledText()); + } + } else if (fragment instanceof Indent) { + if (indentation > 0) { + // reset indentation + result.removeLast(); + indentation = 0; + } + indentation = fragment.getWidth(); + lineLength = fragment.getWidth(); + result.add(((Indent) fragment).toStyledText()); + } else { + TextFlow words = splitWords(fragment); + for (TextFragment word : words) { + WordWrapContext context = new WordWrapContext(word, + lineLength, indentation, isWrappedLine); + do { + context = wordWrap(context, maxWidth, result); + } while (context.isMoreToWrap()); + + indentation = context.getIndentation(); + lineLength = context.getLineLength(); + isWrappedLine = context.isWrappedLine(); + } + } + } + return result; + } + + private static WordWrapContext wordWrap(final WordWrapContext context, + final float maxWidth, final TextFlow result) throws IOException { + TextFragment word = context.getWord(); + TextFragment moreToWrap = null; + float indentation = context.getIndentation(); + float lineLength = context.getLineLength(); + boolean isWrappedLine = context.isWrappedLine(); + + if (isWrappedLine && lineLength == indentation) { + // start of line, replace leading blanks if + TextFragment[] replaceLeadingBlanks = replaceLeadingBlanks(word); + word = replaceLeadingBlanks[0]; + if (replaceLeadingBlanks.length > 1) { + result.add(replaceLeadingBlanks[1]); + } + } + + FontDescriptor fontDescriptor = word.getFontDescriptor(); + float length = word.getWidth(); + + if (maxWidth > 0 && lineLength + length > maxWidth) { + // word exceeds max width, so create new line + + // break hard, if the text does not fit in a full (next) line + boolean breakHard = indentation + length > maxWidth; + + Pair brokenWord = breakWord(word, length, maxWidth + - lineLength, maxWidth - indentation, breakHard); + if (brokenWord != null) { + // word is broken + word = brokenWord.getFirst(); + length = word.getWidth(); + moreToWrap = brokenWord.getSecond(); + + result.add(word); + if (length > 0) { + lineLength += length; + } + + } else { + if (lineLength == indentation) { + // Begin of line and word could now be broke... + // Well, so we have to use it as it is, + // it won't get any better in the next line + result.add(word); + if (length > 0) { + lineLength += length; + } + + } else { + // give it another try in a new line, there + // will be more space. + moreToWrap = word; + if (result.getLast() != null) { + // since the current word is not used, take + // font descriptor of last line. Otherwise + // the line break might be to high + fontDescriptor = result.getLast().getFontDescriptor(); + } + } + } + + // wrap line only if not empty + if (lineLength > indentation) { + // and terminate it with a new line + result.add(new WrappingNewLine(fontDescriptor)); + isWrappedLine = true; + if (indentation > 0) { + result.add(new Indent(indentation).toStyledText()); + } + lineLength = indentation; + } + + } else { + // word fits, so just add it + result.add(word); + if (length > 0) { + lineLength += length; + } + } + + return new WordWrapContext(moreToWrap, lineLength, indentation, + isWrappedLine); + } + + /** + * Replaces leading whitespace by {@link ReplacedWhitespace}. + * + * @param word the fragment to replace + * @return + */ + private static TextFragment[] replaceLeadingBlanks(final TextFragment word) { + String text = word.getText(); + int splitIndex = 0; + while (splitIndex < text.length() + && Character.isWhitespace(text.charAt(splitIndex))) { + ++splitIndex; + } + + if (splitIndex == 0) { + return new TextFragment[]{word}; + } else { + ReplacedWhitespace whitespace = new ReplacedWhitespace( + text.substring(0, splitIndex), word.getFontDescriptor()); + StyledText newWord = null; + if (word instanceof StyledText) { + newWord = ((StyledText) word).inheritAttributes(text + .substring(splitIndex)); + } else { + newWord = new StyledText(text.substring(splitIndex), + word.getFontDescriptor(), word.getColor()); + } + return new TextFragment[]{newWord, whitespace}; + } + } + + /** + * De-wraps the given text, means any new lines introduced by wrapping will + * be removed. Also all whitespace removed by wrapping are re-introduced. + * + * @param text the text to de-wrap. + * @return the de-wrapped text. + * @throws IOException by PDFBox + */ + public static TextFlow deWrap(final TextSequence text) throws IOException { + TextFlow result = new TextFlow(); + for (TextFragment fragment : text) { + if (fragment instanceof WrappingNewLine) { + // skip + } else if (fragment instanceof ReplacedWhitespace) { + result.add(((ReplacedWhitespace) fragment).toReplacedFragment()); + } else { + result.add(fragment); + } + } + + if (text instanceof TextFlow) { + result.setLineSpacing(((TextFlow) text).getLineSpacing()); + } + return result; + } + + /** + * Convencience function that {@link #wordWrap(TextSequence, float) + * word-wraps} into {@link #getLines(TextSequence)}. + * + * @param text the text to word-wrap. + * @param maxWidth the max width to fit. + * @return the word-wrapped text lines. + * @throws IOException by pdfbox + */ + public static List wordWrapToLines(final TextSequence text, + final float maxWidth) throws IOException { + TextFlow wrapped = wordWrap(text, maxWidth); + List lines = getLines(wrapped); + return lines; + } + + /** + * Splits the fragment into words. + * + * @param text the text to split. + * @return the words as a text flow. + */ + public static TextFlow splitWords(final TextFragment text) { + TextFlow result = new TextFlow(); + if (text instanceof NewLine) { + result.add(text); + } else { + float leftMargin = 0; + float rightMargin = 0; + if (text instanceof StyledText && ((StyledText) text).hasMargin()) { + leftMargin = ((StyledText) text).getLeftMargin(); + rightMargin = ((StyledText) text).getRightMargin(); + } + + String[] words = text.getText().split(" ", -1); + for (int index = 0; index < words.length; ++index) { + String newWord = index == 0 ? words[index] : " " + words[index]; + + float currentLeftMargin = 0; + float currentRightMargin = 0; + if (index == 0) { + currentLeftMargin = leftMargin; + } + if (index == words.length - 1) { + currentRightMargin = rightMargin; + } + TextFragment derived = deriveFromExisting(text, newWord, + currentLeftMargin, currentRightMargin); + result.add(derived); + } + } + return result; + } + + /** + * Derive a new TextFragment from an existing one, means use attributes like + * font, color etc. + * + * @param toDeriveFrom the fragment to derive from. + * @param text the new text. + * @param leftMargin the new left margin. + * @param rightMargin the new right margin. + * @return the derived text fragment. + */ + protected static TextFragment deriveFromExisting( + final TextFragment toDeriveFrom, final String text, + final float leftMargin, final float rightMargin) { + if (toDeriveFrom instanceof StyledText) { + return ((StyledText) toDeriveFrom).inheritAttributes(text, + leftMargin, rightMargin); + } + return new StyledText(text, toDeriveFrom.getFontDescriptor(), + toDeriveFrom.getColor(), 0, leftMargin, rightMargin); + } + + private static Pair breakWord(TextFragment word, + float wordWidth, final float remainingLineWidth, float maxWidth, + boolean breakHard) throws IOException { + + float leftMargin = 0; + float rightMargin = 0; + if (word instanceof StyledText) { + StyledText styledText = (StyledText) word; + leftMargin = styledText.getLeftMargin(); + rightMargin = styledText.getRightMargin(); + } + + Pair brokenWord = WordBreakerFactory.getWorkBreaker() + .breakWord(word.getText(), word.getFontDescriptor(), + remainingLineWidth - leftMargin, breakHard); + if (brokenWord == null) { + return null; + } + + // break at calculated index + TextFragment head = deriveFromExisting(word, + brokenWord.getFirst(), leftMargin, 0); + TextFragment tail = deriveFromExisting(word, + brokenWord.getSecond(), 0, rightMargin); + + return new Pair(head, tail); + } + + /** + * Returns the width of the character M in the given font. + * + * @param fontDescriptor font and size. + * @return the width of M. + * @throws IOException by pdfbox + */ + public static float getEmWidth(final FontDescriptor fontDescriptor) + throws IOException { + return getStringWidth("M", fontDescriptor); + } + + /** + * Returns the width of the given text in the given font. + * + * @param text the text to measure. + * @param fontDescriptor font and size. + * @return the width of given text. + * @throws IOException by pdfbox + */ + public static float getStringWidth(final String text, + final FontDescriptor fontDescriptor) throws IOException { + return fontDescriptor.getSize() + * fontDescriptor.getFont().getStringWidth(text) / 1000; + } + + /** + * Draws the given text sequence to the PDPageContentStream at the given + * position. + * + * @param text the text to draw. + * @param contentStream the stream to draw to + * @param upperLeft the position of the start of the first line. + * @param drawListener the listener to + * {@link DrawListener#drawn(Object, Position, float, float) + * notify} on drawn objects. + * @param alignment how to align the text lines. + * @param maxWidth if > 0, the text may be word-wrapped to match the width. + * @param lineSpacing the line spacing factor. + * @param applyLineSpacingToFirstLine indicates if the line spacing should be applied to the first + * line also. Makes sense in most cases to do so. + * @throws IOException by pdfbox + */ + public static void drawText(TextSequence text, + PDPageContentStream contentStream, Position upperLeft, + DrawListener drawListener, Alignment alignment, float maxWidth, + final float lineSpacing, final boolean applyLineSpacingToFirstLine) + throws IOException { + List lines = wordWrapToLines(text, maxWidth); + float maxLineWidth = Math.max(maxWidth, getMaxWidth(lines)); + Position position = upperLeft; + float lastLineHeight = 0; + for (int i = 0; i < lines.size(); i++) { + boolean applyLineSpacing = i > 0 || applyLineSpacingToFirstLine; + TextLine textLine = lines.get(i); + float currentLineHeight = textLine.getHeight(); + float lead = lastLineHeight; + if (applyLineSpacing) { + lead += (currentLineHeight * (lineSpacing - 1)); + } + lastLineHeight = currentLineHeight; + position = position.add(0, -lead); + textLine.drawAligned(contentStream, position, alignment, maxLineWidth, drawListener); + } + + } + + /** + * Gets the (left) offset of the line with respect to the target width and + * alignment. + * + * @param textLine the text + * @param targetWidth the target width + * @param alignment the alignment of the line. + * @return the left offset. + * @throws IOException by pdfbox + */ + public static float getOffset(final TextSequence textLine, + final float targetWidth, final Alignment alignment) + throws IOException { + switch (alignment) { + case Right: + return targetWidth - textLine.getWidth(); + case Center: + return (targetWidth - textLine.getWidth()) / 2f; + default: + return 0; + } + } + + /** + * Calculates the max width of all text lines. + * + * @param lines the lines for which to calculate the max width. + * @return the max width of the lines. + * @throws IOException by pdfbox. + */ + public static float getMaxWidth(final Iterable lines) + throws IOException { + float max = 0; + for (TextLine line : lines) { + max = Math.max(max, line.getWidth()); + } + return max; + } + + /** + * Calculates the width of the text + * + * @param textSequence the text. + * @param maxWidth if > 0, the text may be word-wrapped to match the width. + * @return the width of the text. + * @throws IOException by pdfbox. + */ + public static float getWidth(final TextSequence textSequence, + final float maxWidth) throws IOException { + List lines = wordWrapToLines(textSequence, maxWidth); + float max = 0; + for (TextLine line : lines) { + max = Math.max(max, line.getWidth()); + } + return max; + } + + /** + * Calculates the height of the text + * + * @param textSequence the text. + * @param maxWidth if > 0, the text may be word-wrapped to match the width. + * @param lineSpacing the line spacing factor. + * @param applyLineSpacingToFirstLine indicates if the line spacing should be applied to the first + * line also. Makes sense in most cases to do so. + * @return the height of the text. + * @throws IOException by pdfbox + */ + public static float getHeight(final TextSequence textSequence, + final float maxWidth, final float lineSpacing, + final boolean applyLineSpacingToFirstLine) throws IOException { + List lines = wordWrapToLines(textSequence, maxWidth); + float sum = 0; + for (int i = 0; i < lines.size(); i++) { + boolean applyLineSpacing = i > 0 || applyLineSpacingToFirstLine; + TextLine line = lines.get(i); + float lineHeight = line.getHeight(); + if (applyLineSpacing) { + lineHeight *= lineSpacing; + } + sum += lineHeight; + } + return sum; + } + + private static class WordWrapContext { + private final TextFragment word; + private final float lineLength; + private final float indentation; + boolean isWrappedLine; + + public WordWrapContext(TextFragment word, float lineLength, + float indentation, boolean isWrappedLine) { + this.word = word; + this.lineLength = lineLength; + this.indentation = indentation; + this.isWrappedLine = isWrappedLine; + } + + public TextFragment getWord() { + return word; + } + + public float getLineLength() { + return lineLength; + } + + public float getIndentation() { + return indentation; + } + + public boolean isWrappedLine() { + return isWrappedLine; + } + + public boolean isMoreToWrap() { + return getWord() != null; + } + + } +} diff --git a/layout-pdfbox/src/main/java/org/xbib/graphics/layout/pdfbox/text/WidthRespecting.java b/layout-pdfbox/src/main/java/org/xbib/graphics/layout/pdfbox/text/WidthRespecting.java new file mode 100644 index 0000000..e8db1ed --- /dev/null +++ b/layout-pdfbox/src/main/java/org/xbib/graphics/layout/pdfbox/text/WidthRespecting.java @@ -0,0 +1,21 @@ +package org.xbib.graphics.layout.pdfbox.text; + +/** + * If a drawable is width-respecting, a max width may be set by layouts in order + * fit layout constraints. It is the drawable job to do its best to match the + * max width (e.g. word-wrap text). + */ +public interface WidthRespecting { + + /** + * @return the max width to respect. + */ + float getMaxWidth(); + + /** + * Sets the max width to respect. + * + * @param maxWidth the maximum width. + */ + void setMaxWidth(float maxWidth); +} diff --git a/layout-pdfbox/src/main/java/org/xbib/graphics/layout/pdfbox/text/WrappingNewLine.java b/layout-pdfbox/src/main/java/org/xbib/graphics/layout/pdfbox/text/WrappingNewLine.java new file mode 100644 index 0000000..832f253 --- /dev/null +++ b/layout-pdfbox/src/main/java/org/xbib/graphics/layout/pdfbox/text/WrappingNewLine.java @@ -0,0 +1,36 @@ +package org.xbib.graphics.layout.pdfbox.text; + + +/** + * A NewLine introduced by wrapping. This interface is useful for detecting + * new-lines not contained in the original text. + */ +public class WrappingNewLine extends NewLine { + + /** + * See {@link NewLine#NewLine()}. + */ + public WrappingNewLine() { + super(); + } + + /** + * See {@link NewLine#NewLine(FontDescriptor)}. + * + * @param fontDescriptor the font and size associated with this new line. + */ + public WrappingNewLine(FontDescriptor fontDescriptor) { + super(fontDescriptor); + } + + /** + * See {@link NewLine#NewLine(float)}. + * + * @param fontSize the font size, resp. the height of the new line. + */ + public WrappingNewLine(float fontSize) { + super(fontSize); + } + + +} diff --git a/layout-pdfbox/src/main/java/org/xbib/graphics/layout/pdfbox/text/annotations/Annotated.java b/layout-pdfbox/src/main/java/org/xbib/graphics/layout/pdfbox/text/annotations/Annotated.java new file mode 100644 index 0000000..7dce078 --- /dev/null +++ b/layout-pdfbox/src/main/java/org/xbib/graphics/layout/pdfbox/text/annotations/Annotated.java @@ -0,0 +1,18 @@ +package org.xbib.graphics.layout.pdfbox.text.annotations; + + +/** + * Marker interface for annotated objects. + */ +public interface Annotated extends Iterable { + + /** + * Gets the annotations of a specific type. + * + * @param type the type of interest. + * @param the annotation type. + * @return the annotations of that type, or an empty collection. + */ + Iterable getAnnotationsOfType(Class type); + +} diff --git a/layout-pdfbox/src/main/java/org/xbib/graphics/layout/pdfbox/text/annotations/AnnotatedStyledText.java b/layout-pdfbox/src/main/java/org/xbib/graphics/layout/pdfbox/text/annotations/AnnotatedStyledText.java new file mode 100644 index 0000000..0b0fb79 --- /dev/null +++ b/layout-pdfbox/src/main/java/org/xbib/graphics/layout/pdfbox/text/annotations/AnnotatedStyledText.java @@ -0,0 +1,108 @@ +package org.xbib.graphics.layout.pdfbox.text.annotations; + +import org.apache.pdfbox.pdmodel.font.PDFont; +import org.xbib.graphics.layout.pdfbox.text.FontDescriptor; +import org.xbib.graphics.layout.pdfbox.text.StyledText; +import java.awt.Color; +import java.util.ArrayList; +import java.util.Collection; +import java.util.Collections; +import java.util.Iterator; +import java.util.List; + +/** + * Extension of styled text that supports annotations. + */ +public class AnnotatedStyledText extends StyledText implements Annotated { + + private final List annotations = new ArrayList(); + + /** + * Creates a styled text. + * + * @param text the text to draw. Must not contain line feeds ('\n'). + * @param fontDescriptor the font to use. + * @param color the color to use. + * @param baselineOffset the offset of the baseline. + * @param leftMargin the margin left to the text. + * @param rightMargin the margin right to the text. + * @param annotations the annotations associated with the text. + */ + public AnnotatedStyledText(final String text, + final FontDescriptor fontDescriptor, final Color color, + final float leftMargin, final float rightMargin, + final float baselineOffset, + Collection annotations) { + super(text, fontDescriptor, color, baselineOffset, leftMargin, + rightMargin); + if (annotations != null) { + this.annotations.addAll(annotations); + } + } + + /** + * Creates a styled text. + * + * @param text the text to draw. Must not contain line feeds ('\n'). + * @param size the size of the font. + * @param font the font to use.. + * @param color the color to use. + * @param baselineOffset the offset of the baseline. + * @param annotations the annotations associated with the text. + */ + public AnnotatedStyledText(String text, float size, PDFont font, + Color color, final float baselineOffset, + Collection annotations) { + this(text, new FontDescriptor(font, size), color, baselineOffset, 0, 0, + annotations); + } + + @Override + public Iterator iterator() { + return annotations.iterator(); + } + + @SuppressWarnings("unchecked") + @Override + public Iterable getAnnotationsOfType(Class type) { + List result = null; + for (Annotation annotation : annotations) { + if (type.isAssignableFrom(annotation.getClass())) { + if (result == null) { + result = new ArrayList(); + } + result.add((T) annotation); + } + } + + if (result == null) { + return Collections.emptyList(); + } + return result; + } + + /** + * Adds an annotation. + * + * @param annotation the annotation to add. + */ + public void addAnnotation(final Annotation annotation) { + annotations.add(annotation); + } + + /** + * Adds all annotations. + * + * @param annos the annotations to add. + */ + public void addAllAnnotation(final Collection annos) { + annotations.addAll(annos); + } + + @Override + public AnnotatedStyledText inheritAttributes(String text, float leftMargin, + float rightMargin) { + return new AnnotatedStyledText(text, getFontDescriptor(), getColor(), + getBaselineOffset(), leftMargin, rightMargin, annotations); + } +} diff --git a/layout-pdfbox/src/main/java/org/xbib/graphics/layout/pdfbox/text/annotations/Annotation.java b/layout-pdfbox/src/main/java/org/xbib/graphics/layout/pdfbox/text/annotations/Annotation.java new file mode 100644 index 0000000..58415bd --- /dev/null +++ b/layout-pdfbox/src/main/java/org/xbib/graphics/layout/pdfbox/text/annotations/Annotation.java @@ -0,0 +1,8 @@ +package org.xbib.graphics.layout.pdfbox.text.annotations; + +/** + * Marker interface for annotations. + */ +public interface Annotation { + +} diff --git a/layout-pdfbox/src/main/java/org/xbib/graphics/layout/pdfbox/text/annotations/AnnotationCharacters.java b/layout-pdfbox/src/main/java/org/xbib/graphics/layout/pdfbox/text/annotations/AnnotationCharacters.java new file mode 100644 index 0000000..a7e8fc4 --- /dev/null +++ b/layout-pdfbox/src/main/java/org/xbib/graphics/layout/pdfbox/text/annotations/AnnotationCharacters.java @@ -0,0 +1,324 @@ +package org.xbib.graphics.layout.pdfbox.text.annotations; + +import org.xbib.graphics.layout.pdfbox.text.ControlCharacter; +import org.xbib.graphics.layout.pdfbox.text.ControlCharacters.ControlCharacterFactory; +import org.xbib.graphics.layout.pdfbox.text.annotations.Annotations.AnchorAnnotation; +import org.xbib.graphics.layout.pdfbox.text.annotations.Annotations.HyperlinkAnnotation; +import org.xbib.graphics.layout.pdfbox.text.annotations.Annotations.HyperlinkAnnotation.LinkStyle; +import org.xbib.graphics.layout.pdfbox.text.annotations.Annotations.UnderlineAnnotation; +import java.util.List; +import java.util.concurrent.CopyOnWriteArrayList; +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +/** + * Container for annotation control characters. + */ +public class AnnotationCharacters { + + private final static List> FACTORIES = new CopyOnWriteArrayList>(); + + static { + register(new HyperlinkControlCharacterFactory()); + register(new AnchorControlCharacterFactory()); + register(new UnderlineControlCharacterFactory()); + } + + /** + * Use this method to register your (custom) annotation control character + * factory. + * + * @param factory the factory to register. + */ + public static void register( + final AnnotationControlCharacterFactory factory) { + FACTORIES.add(factory); + } + + /** + * @return all the default and custom annotation control character + * factories. + */ + public static Iterable> getFactories() { + return FACTORIES; + } + + private static class HyperlinkControlCharacterFactory implements + AnnotationControlCharacterFactory { + + private final static Pattern PATTERN = Pattern + .compile("(? charactersSoFar) { + return new HyperlinkControlCharacter(matcher.group(5), + matcher.group(3)); + } + + @Override + public Pattern getPattern() { + return PATTERN; + } + + @Override + public String unescape(String text) { + return text + .replaceAll("\\\\" + Pattern.quote(TO_ESCAPE), TO_ESCAPE); + } + + @Override + public boolean patternMatchesBeginOfLine() { + return false; + } + + } + + private static class AnchorControlCharacterFactory implements + AnnotationControlCharacterFactory { + + private final static Pattern PATTERN = Pattern + .compile("(? charactersSoFar) { + return new AnchorControlCharacter(matcher.group(3)); + } + + @Override + public Pattern getPattern() { + return PATTERN; + } + + @Override + public String unescape(String text) { + return text + .replaceAll("\\\\" + Pattern.quote(TO_ESCAPE), TO_ESCAPE); + } + + @Override + public boolean patternMatchesBeginOfLine() { + return false; + } + + } + + private static class UnderlineControlCharacterFactory implements + AnnotationControlCharacterFactory { + + private static final Pattern PATTERN = Pattern + .compile("(? charactersSoFar) { + return new UnderlineControlCharacter(matcher.group(4), + matcher.group(6)); + } + + @Override + public Pattern getPattern() { + return PATTERN; + } + + @Override + public String unescape(String text) { + return text + .replaceAll("\\\\" + Pattern.quote(TO_ESCAPE), TO_ESCAPE); + } + + @Override + public boolean patternMatchesBeginOfLine() { + return false; + } + + } + + /** + * A {link:#title1} indicates an internal link to the + * {@link AnchorControlCharacter anchor} title1. Any other link + * (not starting with # will be treated as an external link. It + * can be escaped with a backslash ('\'). + */ + public static class HyperlinkControlCharacter extends + AnnotationControlCharacter { + private HyperlinkAnnotation hyperlink; + + protected HyperlinkControlCharacter(final String hyperlink, + final String linkStyle) { + super("HYPERLINK", HyperlinkControlCharacterFactory.TO_ESCAPE); + if (hyperlink != null) { + LinkStyle style = LinkStyle.ul; + if (linkStyle != null) { + style = LinkStyle.valueOf(linkStyle); + } + this.hyperlink = new HyperlinkAnnotation(hyperlink, style); + } + } + + @Override + public HyperlinkAnnotation getAnnotation() { + return hyperlink; + } + + @Override + public Class getAnnotationType() { + return HyperlinkAnnotation.class; + } + } + + /** + * An {color:#ee22aa} indicates switching the color in markup, + * where the color is given as hex RGB code (ee22aa in this case). It can be + * escaped with a backslash ('\'). + */ + public static class AnchorControlCharacter extends + AnnotationControlCharacter { + private AnchorAnnotation anchor; + + protected AnchorControlCharacter(final String anchor) { + super("ANCHOR", AnchorControlCharacterFactory.TO_ESCAPE); + if (anchor != null) { + this.anchor = new AnchorAnnotation(anchor); + } + } + + @Override + public AnchorAnnotation getAnnotation() { + return anchor; + } + + @Override + public Class getAnnotationType() { + return AnchorAnnotation.class; + } + + } + + /** + * Control character for underline. It can be escaped with a backslash + * ('\'). + */ + public static class UnderlineControlCharacter extends + AnnotationControlCharacter { + + /** + * constant for the system property + * pdfbox.layout.underline.baseline.offset.scale.default. + */ + public final static String UNDERLINE_DEFAULT_BASELINE_OFFSET_SCALE_PROPERTY = "pdfbox.layout.underline.baseline.offset.scale.default"; + + private static Float defaultBaselineOffsetScale; + private final UnderlineAnnotation line; + + protected UnderlineControlCharacter() { + this(null, null); + } + + protected UnderlineControlCharacter(String baselineOffsetScaleValue, + String lineWeightValue) { + super("UNDERLINE", UnderlineControlCharacterFactory.TO_ESCAPE); + + float baselineOffsetScale = parseFloat(baselineOffsetScaleValue, + getdefaultBaselineOffsetScale()); + float lineWeight = parseFloat(lineWeightValue, 1f); + line = new UnderlineAnnotation(baselineOffsetScale, lineWeight); + } + + @Override + public UnderlineAnnotation getAnnotation() { + return line; + } + + @Override + public Class getAnnotationType() { + return UnderlineAnnotation.class; + } + + private static float parseFloat(String text, float defaultValue) { + if (text == null) { + return defaultValue; + } + try { + return Float.parseFloat(text); + } catch (NumberFormatException e) { + return defaultValue; + } + } + + private static float getdefaultBaselineOffsetScale() { + if (defaultBaselineOffsetScale == null) { + defaultBaselineOffsetScale = Float + .parseFloat(System + .getProperty( + UNDERLINE_DEFAULT_BASELINE_OFFSET_SCALE_PROPERTY, + "-0.1")); + } + return defaultBaselineOffsetScale; + } + + } + + /** + * Specialized interface for control character factories for annotations. + * + * @param the type of the annotation control character. + */ + public interface AnnotationControlCharacterFactory> + extends ControlCharacterFactory { + T createControlCharacter(String text, Matcher matcher, + final List charactersSoFar); + + } + + /** + * Common base class for annotation control characters. + */ + public static abstract class AnnotationControlCharacter + extends ControlCharacter { + + protected AnnotationControlCharacter(final String description, + final String charaterToEscape) { + super(description, charaterToEscape); + } + + /** + * @return the associated annotation. + */ + public abstract T getAnnotation(); + + /** + * @return the type of the annotation. + */ + public abstract Class getAnnotationType(); + + } + + public static void main(String[] args) { + Pattern PATTERN = Pattern + .compile("(? 1: blanks, 4: size, 5: unit + // 7 + -> 6: blanks, 9: sign, 10: size, 11: unit + // 11 # -> 12: blanks, 15: number-sign, 16: size, 18: unit + } + +} diff --git a/layout-pdfbox/src/main/java/org/xbib/graphics/layout/pdfbox/text/annotations/AnnotationDrawListener.java b/layout-pdfbox/src/main/java/org/xbib/graphics/layout/pdfbox/text/annotations/AnnotationDrawListener.java new file mode 100644 index 0000000..5b70036 --- /dev/null +++ b/layout-pdfbox/src/main/java/org/xbib/graphics/layout/pdfbox/text/annotations/AnnotationDrawListener.java @@ -0,0 +1,103 @@ +package org.xbib.graphics.layout.pdfbox.text.annotations; + +import org.apache.pdfbox.pdmodel.PDDocument; +import org.apache.pdfbox.pdmodel.PDPage; +import org.xbib.graphics.layout.pdfbox.elements.render.RenderContext; +import org.xbib.graphics.layout.pdfbox.elements.render.RenderListener; +import org.xbib.graphics.layout.pdfbox.text.Alignment; +import org.xbib.graphics.layout.pdfbox.text.DrawContext; +import org.xbib.graphics.layout.pdfbox.text.DrawListener; +import org.xbib.graphics.layout.pdfbox.text.DrawableText; +import org.xbib.graphics.layout.pdfbox.text.Position; +import java.io.IOException; + +/** + * This listener has to be passed to all + * {@link DrawableText#drawText(org.apache.pdfbox.pdmodel.PDPageContentStream, Position, Alignment, DrawListener) + * draw()} methods, in order collect all annotation metadata. After all drawing + * is done, you have to call {@link #finalizeAnnotations()} which creates all + * necessary annotations and sets them to the corresponding pages. This listener + * is used by the the rendering API, but you may also use it with the low-level + * text API. + */ +public class AnnotationDrawListener implements DrawListener, RenderListener { + + private final DrawContext drawContext; + private final Iterable annotationProcessors; + + /** + * Creates an AnnotationDrawListener with the given {@link DrawContext}. + * + * @param drawContext the context which provides the {@link PDDocument} and the + * {@link PDPage} currently drawn to. + */ + public AnnotationDrawListener(final DrawContext drawContext) { + this.drawContext = drawContext; + annotationProcessors = AnnotationProcessorFactory + .createAnnotationProcessors(); + } + + @Override + public void drawn(Object drawnObject, Position upperLeft, float width, + float height) { + if (!(drawnObject instanceof Annotated)) { + return; + } + for (AnnotationProcessor annotationProcessor : annotationProcessors) { + try { + annotationProcessor.annotatedObjectDrawn( + (Annotated) drawnObject, drawContext, upperLeft, width, + height); + } catch (IOException e) { + throw new RuntimeException( + "exception on annotation processing", e); + } + } + } + + /** + * @throws IOException by pdfbox. + * @deprecated user {@link #afterRender()} instead. + */ + @Deprecated + public void finalizeAnnotations() throws IOException { + afterRender(); + } + + @Override + public void beforePage(RenderContext renderContext) throws IOException { + for (AnnotationProcessor annotationProcessor : annotationProcessors) { + try { + annotationProcessor.beforePage(drawContext); + } catch (IOException e) { + throw new RuntimeException( + "exception on annotation processing", e); + } + } + } + + @Override + public void afterPage(RenderContext renderContext) throws IOException { + for (AnnotationProcessor annotationProcessor : annotationProcessors) { + try { + annotationProcessor.afterPage(drawContext); + } catch (IOException e) { + throw new RuntimeException( + "exception on annotation processing", e); + } + } + } + + + public void afterRender() throws IOException { + for (AnnotationProcessor annotationProcessor : annotationProcessors) { + try { + annotationProcessor.afterRender(drawContext.getPdDocument()); + } catch (IOException e) { + throw new RuntimeException( + "exception on annotation processing", e); + } + } + } + +} diff --git a/layout-pdfbox/src/main/java/org/xbib/graphics/layout/pdfbox/text/annotations/AnnotationProcessor.java b/layout-pdfbox/src/main/java/org/xbib/graphics/layout/pdfbox/text/annotations/AnnotationProcessor.java new file mode 100644 index 0000000..27089dd --- /dev/null +++ b/layout-pdfbox/src/main/java/org/xbib/graphics/layout/pdfbox/text/annotations/AnnotationProcessor.java @@ -0,0 +1,51 @@ +package org.xbib.graphics.layout.pdfbox.text.annotations; + +import org.apache.pdfbox.pdmodel.PDDocument; +import org.xbib.graphics.layout.pdfbox.text.DrawContext; +import org.xbib.graphics.layout.pdfbox.text.Position; +import java.io.IOException; + +/** + * Processes an annotation. + */ +public interface AnnotationProcessor { + + /** + * Called if an annotated object has been drawn. + * + * @param drawnObject the drawn object. + * @param drawContext the drawing context. + * @param upperLeft the upper left position the object has been drawn to. + * @param width the width of the drawn object. + * @param height the height of the drawn object. + * @throws IOException by pdfbox. + */ + void annotatedObjectDrawn(final Annotated drawnObject, + final DrawContext drawContext, Position upperLeft, float width, + float height) throws IOException; + + /** + * Called before a page is drawn. + * + * @param drawContext the drawing context. + * @throws IOException by pdfbox. + */ + void beforePage(final DrawContext drawContext) throws IOException; + + /** + * Called after a page is drawn. + * + * @param drawContext the drawing context. + * @throws IOException by pdfbox. + */ + void afterPage(final DrawContext drawContext) throws IOException; + + /** + * Called after all rendering has been performed. + * + * @param document the document. + * @throws IOException by pdfbox. + */ + void afterRender(final PDDocument document) throws IOException; + +} diff --git a/layout-pdfbox/src/main/java/org/xbib/graphics/layout/pdfbox/text/annotations/AnnotationProcessorFactory.java b/layout-pdfbox/src/main/java/org/xbib/graphics/layout/pdfbox/text/annotations/AnnotationProcessorFactory.java new file mode 100644 index 0000000..b59492f --- /dev/null +++ b/layout-pdfbox/src/main/java/org/xbib/graphics/layout/pdfbox/text/annotations/AnnotationProcessorFactory.java @@ -0,0 +1,45 @@ +package org.xbib.graphics.layout.pdfbox.text.annotations; + +import java.util.ArrayList; +import java.util.List; +import java.util.concurrent.CopyOnWriteArrayList; + +/** + * Factory used to create all available {@link AnnotationProcessor}s. You may + * {@link #register(Class) register} your own annotation processor in order to + * process custom annotations. + */ +public class AnnotationProcessorFactory { + + private final static List> ANNOTATION_PROCESSORS = new CopyOnWriteArrayList>(); + + static { + register(HyperlinkAnnotationProcessor.class); + register(UnderlineAnnotationProcessor.class); + } + + /** + * Use this method to register your (custom) annotation processors. + * + * @param annotationProcessor the processor to register. + */ + public static void register(final Class annotationProcessor) { + ANNOTATION_PROCESSORS.add(annotationProcessor); + } + + /** + * @return a (new) instance of all available annotation processors, both + * built-in and custom. + */ + public static Iterable createAnnotationProcessors() { + List annotationProcessors = new ArrayList(); + for (Class annotationProcessorClass : ANNOTATION_PROCESSORS) { + try { + annotationProcessors.add(annotationProcessorClass.getDeclaredConstructor().newInstance()); + } catch (Exception e) { + throw new RuntimeException("failed to create AnnotationProcessor", e); + } + } + return annotationProcessors; + } +} diff --git a/layout-pdfbox/src/main/java/org/xbib/graphics/layout/pdfbox/text/annotations/Annotations.java b/layout-pdfbox/src/main/java/org/xbib/graphics/layout/pdfbox/text/annotations/Annotations.java new file mode 100644 index 0000000..6294f2f --- /dev/null +++ b/layout-pdfbox/src/main/java/org/xbib/graphics/layout/pdfbox/text/annotations/Annotations.java @@ -0,0 +1,114 @@ +package org.xbib.graphics.layout.pdfbox.text.annotations; + +/** + * Container for all annotations. + */ +public class Annotations { + + /** + * Represents a underline annotation + */ + public static class UnderlineAnnotation implements Annotation { + + private float baselineOffsetScale = 0f; + private float lineWeight = 1f; + + public UnderlineAnnotation(float baselineOffsetScale, float lineWeight) { + this.baselineOffsetScale = baselineOffsetScale; + this.lineWeight = lineWeight; + } + + public float getBaselineOffsetScale() { + return baselineOffsetScale; + } + + public float getLineWeight() { + return lineWeight; + } + + @Override + public String toString() { + return "UnderlineAnnotation [baselineOffsetScale=" + + baselineOffsetScale + ", lineWeight=" + lineWeight + "]"; + } + + } + + /** + * Represents a hyperlink annotation + */ + public static class HyperlinkAnnotation implements Annotation { + + public enum LinkStyle { + /** + * Underline. + */ + ul, + /** + * None. + */ + none + } + + private final String hyperlinkUri; + private final LinkStyle linkStyle; + + /** + * Creates a hyperlink annotation. + * + * @param hyperlinkUri the hyperlinkUri. + * @param linkStyle the link style. + */ + public HyperlinkAnnotation(String hyperlinkUri, LinkStyle linkStyle) { + this.hyperlinkUri = hyperlinkUri; + this.linkStyle = linkStyle; + } + + /** + * @return the hyperlink URI. + */ + public String getHyperlinkURI() { + return hyperlinkUri; + } + + public LinkStyle getLinkStyle() { + return linkStyle; + } + + @Override + public String toString() { + return "HyperlinkAnnotation [hyperlinkUri=" + hyperlinkUri + + ", linkStyle=" + linkStyle + "]"; + } + + } + + /** + * Represents a anchor annotation + */ + public static class AnchorAnnotation implements Annotation { + private final String anchor; + + /** + * Creates a anchor annotation. + * + * @param anchor the anchor name. + */ + public AnchorAnnotation(String anchor) { + this.anchor = anchor; + } + + /** + * @return the anchor name. + */ + public String getAnchor() { + return anchor; + } + + @Override + public String toString() { + return "AnchorAnnotation [anchor=" + anchor + "]"; + } + + } +} diff --git a/layout-pdfbox/src/main/java/org/xbib/graphics/layout/pdfbox/text/annotations/HyperlinkAnnotationProcessor.java b/layout-pdfbox/src/main/java/org/xbib/graphics/layout/pdfbox/text/annotations/HyperlinkAnnotationProcessor.java new file mode 100644 index 0000000..a3096af --- /dev/null +++ b/layout-pdfbox/src/main/java/org/xbib/graphics/layout/pdfbox/text/annotations/HyperlinkAnnotationProcessor.java @@ -0,0 +1,196 @@ +package org.xbib.graphics.layout.pdfbox.text.annotations; + +import org.apache.pdfbox.pdmodel.PDDocument; +import org.apache.pdfbox.pdmodel.PDPage; +import org.apache.pdfbox.pdmodel.common.PDRectangle; +import org.apache.pdfbox.pdmodel.interactive.annotation.PDAnnotationLink; +import org.apache.pdfbox.pdmodel.interactive.documentnavigation.destination.PDPageXYZDestination; +import org.xbib.graphics.layout.pdfbox.text.DrawContext; +import org.xbib.graphics.layout.pdfbox.text.Position; +import org.xbib.graphics.layout.pdfbox.text.annotations.Annotations.AnchorAnnotation; +import org.xbib.graphics.layout.pdfbox.text.annotations.Annotations.HyperlinkAnnotation; +import org.xbib.graphics.layout.pdfbox.text.annotations.Annotations.HyperlinkAnnotation.LinkStyle; +import org.xbib.graphics.layout.pdfbox.util.CompatibilityHelper; +import java.awt.Color; +import java.io.IOException; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.Map.Entry; + +/** + * This annotation processor handles both {@link HyperlinkAnnotation}s and + * {@link AnchorAnnotation}s, and adds the needed hyperlink metadata to the PDF + * document. + */ +public class HyperlinkAnnotationProcessor implements AnnotationProcessor { + + private final Map anchorMap = new HashMap(); + private final Map> linkMap = new HashMap>(); + + @Override + public void annotatedObjectDrawn(Annotated drawnObject, + DrawContext drawContext, Position upperLeft, float width, + float height) throws IOException { + + if (!(drawnObject instanceof AnnotatedStyledText)) { + return; + } + AnnotatedStyledText annotatedText = (AnnotatedStyledText) drawnObject; + handleHyperlinkAnnotations(annotatedText, drawContext, upperLeft, + width, height); + handleAnchorAnnotations(annotatedText, drawContext, upperLeft); + } + + protected void handleAnchorAnnotations(AnnotatedStyledText annotatedText, + DrawContext drawContext, Position upperLeft) { + Iterable anchorAnnotations = annotatedText + .getAnnotationsOfType(AnchorAnnotation.class); + for (AnchorAnnotation anchorAnnotation : anchorAnnotations) { + anchorMap.put( + anchorAnnotation.getAnchor(), + new PageAnchor(drawContext.getCurrentPage(), upperLeft + .getX(), upperLeft.getY())); + } + } + + protected void handleHyperlinkAnnotations( + AnnotatedStyledText annotatedText, DrawContext drawContext, + Position upperLeft, float width, float height) { + Iterable hyperlinkAnnotations = annotatedText + .getAnnotationsOfType(HyperlinkAnnotation.class); + for (HyperlinkAnnotation hyperlinkAnnotation : hyperlinkAnnotations) { + List links = linkMap.get(drawContext.getCurrentPage()); + if (links == null) { + links = new ArrayList(); + linkMap.put(drawContext.getCurrentPage(), links); + } + PDRectangle bounds = new PDRectangle(); + bounds.setLowerLeftX(upperLeft.getX()); + bounds.setLowerLeftY(upperLeft.getY() - height); + bounds.setUpperRightX(upperLeft.getX() + width); + bounds.setUpperRightY(upperLeft.getY()); + + links.add(new Hyperlink(bounds, annotatedText.getColor(), + hyperlinkAnnotation.getLinkStyle(), hyperlinkAnnotation + .getHyperlinkURI())); + } + } + + @Override + public void beforePage(DrawContext drawContext) { + // nothing to do here + } + + @Override + public void afterPage(DrawContext drawContext) { + // nothing to do here + } + + @Override + public void afterRender(PDDocument document) throws IOException { + for (Entry> entry : linkMap.entrySet()) { + PDPage page = entry.getKey(); + List links = entry.getValue(); + for (Hyperlink hyperlink : links) { + PDAnnotationLink pdLink = null; + if (hyperlink.getHyperlinkURI().startsWith("#")) { + pdLink = createGotoLink(hyperlink); + } else { + pdLink = CompatibilityHelper.createLink(page, + hyperlink.getRect(), hyperlink.getColor(), + hyperlink.getLinkStyle(), + hyperlink.getHyperlinkURI()); + } + page.getAnnotations().add(pdLink); + } + + } + } + + private PDAnnotationLink createGotoLink(Hyperlink hyperlink) { + String anchor = hyperlink.getHyperlinkURI().substring(1); + PageAnchor pageAnchor = anchorMap.get(anchor); + if (pageAnchor == null) { + throw new IllegalArgumentException(String.format( + "anchor named '%s' not found", anchor)); + } + PDPageXYZDestination xyzDestination = new PDPageXYZDestination(); + xyzDestination.setPage(pageAnchor.getPage()); + xyzDestination.setLeft((int) pageAnchor.getX()); + xyzDestination.setTop((int) pageAnchor.getY()); + return CompatibilityHelper.createLink(pageAnchor.getPage(), hyperlink.getRect(), + hyperlink.getColor(), hyperlink.getLinkStyle(), xyzDestination); + } + + private static class PageAnchor { + private final PDPage page; + private final float x; + private final float y; + + public PageAnchor(PDPage page, float x, float y) { + this.page = page; + this.x = x; + this.y = y; + } + + public PDPage getPage() { + return page; + } + + public float getX() { + return x; + } + + public float getY() { + return y; + } + + @Override + public String toString() { + return "PageAnchor [page=" + page + ", x=" + x + ", y=" + y + "]"; + } + + } + + private static class Hyperlink { + private final PDRectangle rect; + private final Color color; + private final String hyperlinkUri; + private final LinkStyle linkStyle; + + public Hyperlink(PDRectangle rect, Color color, LinkStyle linkStyle, + String hyperlinkUri) { + this.rect = rect; + this.color = color; + this.hyperlinkUri = hyperlinkUri; + this.linkStyle = linkStyle; + } + + public PDRectangle getRect() { + return rect; + } + + public Color getColor() { + return color; + } + + public String getHyperlinkURI() { + return hyperlinkUri; + } + + public LinkStyle getLinkStyle() { + return linkStyle; + } + + @Override + public String toString() { + return "Hyperlink [rect=" + rect + ", color=" + color + + ", hyperlinkUri=" + hyperlinkUri + ", linkStyle=" + + linkStyle + "]"; + } + + } + +} diff --git a/layout-pdfbox/src/main/java/org/xbib/graphics/layout/pdfbox/text/annotations/UnderlineAnnotationProcessor.java b/layout-pdfbox/src/main/java/org/xbib/graphics/layout/pdfbox/text/annotations/UnderlineAnnotationProcessor.java new file mode 100644 index 0000000..c7b8184 --- /dev/null +++ b/layout-pdfbox/src/main/java/org/xbib/graphics/layout/pdfbox/text/annotations/UnderlineAnnotationProcessor.java @@ -0,0 +1,97 @@ +package org.xbib.graphics.layout.pdfbox.text.annotations; + +import org.apache.pdfbox.pdmodel.PDDocument; +import org.apache.pdfbox.pdmodel.PDPageContentStream; +import org.xbib.graphics.layout.pdfbox.shape.Stroke; +import org.xbib.graphics.layout.pdfbox.text.DrawContext; +import org.xbib.graphics.layout.pdfbox.text.Position; +import org.xbib.graphics.layout.pdfbox.text.StyledText; +import org.xbib.graphics.layout.pdfbox.text.annotations.Annotations.UnderlineAnnotation; +import java.awt.Color; +import java.io.IOException; +import java.util.ArrayList; +import java.util.List; + +/** + * This annotation processor handles the {@link UnderlineAnnotation}s, and adds + * the needed hyperlink metadata to the PDF document. + */ +public class UnderlineAnnotationProcessor implements AnnotationProcessor { + + private final List linesOnPage = new ArrayList(); + + @Override + public void annotatedObjectDrawn(Annotated drawnObject, + DrawContext drawContext, Position upperLeft, float width, + float height) throws IOException { + + if (!(drawnObject instanceof StyledText)) { + return; + } + + StyledText drawnText = (StyledText) drawnObject; + for (UnderlineAnnotation underlineAnnotation : drawnObject + .getAnnotationsOfType(UnderlineAnnotation.class)) { + float fontSize = drawnText.getFontDescriptor().getSize(); + float ascent = fontSize + * drawnText.getFontDescriptor().getFont() + .getFontDescriptor().getAscent() / 1000; + + float baselineOffset = fontSize * underlineAnnotation.getBaselineOffsetScale(); + float thickness = (0.01f + fontSize * 0.05f) + * underlineAnnotation.getLineWeight(); + + Position start = new Position(upperLeft.getX(), upperLeft.getY() + - ascent + baselineOffset); + Position end = new Position(start.getX() + width, start.getY()); + Stroke stroke = Stroke.builder().lineWidth(thickness).build(); + Line line = new Line(start, end, stroke, drawnText.getColor()); + linesOnPage.add(line); + } + } + + @Override + public void beforePage(DrawContext drawContext) throws IOException { + linesOnPage.clear(); + } + + @Override + public void afterPage(DrawContext drawContext) throws IOException { + for (Line line : linesOnPage) { + line.draw(drawContext.getCurrentPageContentStream()); + } + linesOnPage.clear(); + } + + @Override + public void afterRender(PDDocument document) throws IOException { + linesOnPage.clear(); + } + + private static class Line { + + private final Position start; + private final Position end; + private final Stroke stroke; + private final Color color; + + public Line(Position start, Position end, Stroke stroke, Color color) { + super(); + this.start = start; + this.end = end; + this.stroke = stroke; + this.color = color; + } + + public void draw(PDPageContentStream contentStream) throws IOException { + if (color != null) { + contentStream.setStrokingColor(color); + } + if (stroke != null) { + stroke.applyTo(contentStream); + } + contentStream.moveTo(start.getX(), start.getY()); + contentStream.lineTo(end.getX(), end.getY()); + } + } +} diff --git a/layout-pdfbox/src/main/java/org/xbib/graphics/layout/pdfbox/util/CompatibilityHelper.java b/layout-pdfbox/src/main/java/org/xbib/graphics/layout/pdfbox/util/CompatibilityHelper.java new file mode 100644 index 0000000..51ba57d --- /dev/null +++ b/layout-pdfbox/src/main/java/org/xbib/graphics/layout/pdfbox/util/CompatibilityHelper.java @@ -0,0 +1,326 @@ +package org.xbib.graphics.layout.pdfbox.util; + +import org.apache.pdfbox.pdmodel.PDDocument; +import org.apache.pdfbox.pdmodel.PDPage; +import org.apache.pdfbox.pdmodel.PDPageContentStream; +import org.apache.pdfbox.pdmodel.common.PDRectangle; +import org.apache.pdfbox.pdmodel.graphics.color.PDColor; +import org.apache.pdfbox.pdmodel.graphics.color.PDDeviceRGB; +import org.apache.pdfbox.pdmodel.graphics.image.LosslessFactory; +import org.apache.pdfbox.pdmodel.graphics.image.PDImageXObject; +import org.apache.pdfbox.pdmodel.interactive.action.PDActionGoTo; +import org.apache.pdfbox.pdmodel.interactive.action.PDActionURI; +import org.apache.pdfbox.pdmodel.interactive.annotation.PDAnnotation; +import org.apache.pdfbox.pdmodel.interactive.annotation.PDAnnotationLink; +import org.apache.pdfbox.pdmodel.interactive.annotation.PDBorderStyleDictionary; +import org.apache.pdfbox.pdmodel.interactive.documentnavigation.destination.PDDestination; +import org.apache.pdfbox.rendering.ImageType; +import org.apache.pdfbox.rendering.PDFRenderer; +import org.apache.pdfbox.util.Matrix; +import org.xbib.graphics.layout.pdfbox.text.Position; +import org.xbib.graphics.layout.pdfbox.text.annotations.Annotations.HyperlinkAnnotation.LinkStyle; +import java.awt.Color; +import java.awt.geom.AffineTransform; +import java.awt.image.BufferedImage; +import java.io.IOException; +import java.util.HashMap; +import java.util.Map; +import java.util.WeakHashMap; + +/** + * Provide compatible methods for API changes from pdfbox 1x to 2x. + */ +public class CompatibilityHelper { + + private static final String BULLET = "\u2022"; + + private static final String DOUBLE_ANGLE = "\u00bb"; + + private static final String IMAGE_CACHE = "IMAGE_CACHE"; + + private static final Map>> documentCaches = new WeakHashMap<>(); + + private static PDBorderStyleDictionary noBorder; + + /** + * Returns the bullet character for the given level. Actually only two + * bullets are used for odd and even levels. For odd levels the + * {@link #BULLET bullet} character is used, for even it is the + * {@link #DOUBLE_ANGLE double angle}. You may customize this by setting the + * system properties pdfbox.layout.bullet.odd and/or + * pdfbox.layout.bullet.even. + * + * @param level the level to return the bullet for. + * @return the bullet character for the leve. + */ + public static String getBulletCharacter(final int level) { + if (level % 2 == 1) { + return System.getProperty("pdfbox.layout.bullet.odd", BULLET); + } + return System.getProperty("pdfbox.layout.bullet.even", DOUBLE_ANGLE); + } + + public static void clip(final PDPageContentStream contentStream) + throws IOException { + contentStream.clip(); + } + + public static void transform(final PDPageContentStream contentStream, + float a, float b, float c, float d, float e, float f) + throws IOException { + contentStream.transform(new Matrix(a, b, c, d, e, f)); + } + + public static void curveTo(final PDPageContentStream contentStream, float x1, float y1, float x2, float y2, float x3, float y3) throws IOException { + contentStream.curveTo(x1, y1, x2, y2, x3, y3); + } + + public static void curveTo1(final PDPageContentStream contentStream, float x1, float y1, float x3, float y3) throws IOException { + contentStream.curveTo1(x1, y1, x3, y3); + } + + public static void fillNonZero(final PDPageContentStream contentStream) throws IOException { + contentStream.fill(); + } + + public static void showText(final PDPageContentStream contentStream, + final String text) throws IOException { + contentStream.showText(text); + } + + public static void setTextTranslation( + final PDPageContentStream contentStream, final float x, + final float y) throws IOException { + contentStream.setTextMatrix(Matrix.getTranslateInstance(x, y)); + } + + public static void moveTextPosition( + final PDPageContentStream contentStream, final float x, + final float y) throws IOException { + contentStream.transform(new Matrix(1, 0, 0, 1, x, y)); + } + + public static PDPageContentStream createAppendablePDPageContentStream( + final PDDocument pdDocument, final PDPage page) throws IOException { + return new PDPageContentStream(pdDocument, page, PDPageContentStream.AppendMode.APPEND, true); + } + + public static void drawImage(final BufferedImage image, + final PDDocument document, final PDPageContentStream contentStream, + Position upperLeft, final float width, final float height) + throws IOException { + PDImageXObject cachedImage = getCachedImage(document, image); + float x = upperLeft.getX(); + float y = upperLeft.getY() - height; + contentStream.drawImage(cachedImage, x, y, width, height); + } + + public static int getPageRotation(final PDPage page) { + return page.getRotation(); + } + + /** + * Renders the given page as an RGB image. + * + * @param document the document containing the page. + * @param pageIndex the index of the page to render. + * @param resolution the image resolution. + * @return the rendered image + * @throws IOException by pdfbox + */ + public static BufferedImage createImageFromPage(final PDDocument document, int pageIndex, final int resolution) throws IOException { + PDFRenderer pdfRenderer = new PDFRenderer(document); + return pdfRenderer.renderImageWithDPI(pageIndex, resolution, ImageType.RGB); + } + + public static PDAnnotationLink createLink(PDPage page, PDRectangle rect, Color color, + LinkStyle linkStyle, final String uri) { + PDAnnotationLink pdLink = createLink(page, rect, color, linkStyle); + + PDActionURI actionUri = new PDActionURI(); + actionUri.setURI(uri); + pdLink.setAction(actionUri); + return pdLink; + } + + public static PDAnnotationLink createLink(PDPage page, PDRectangle rect, Color color, + LinkStyle linkStyle, final PDDestination destination) { + PDAnnotationLink pdLink = createLink(page, rect, color, linkStyle); + + PDActionGoTo gotoAction = new PDActionGoTo(); + gotoAction.setDestination(destination); + pdLink.setAction(gotoAction); + return pdLink; + } + + /** + * Sets the color in the annotation. + * + * @param annotation the annotation. + * @param color the color to set. + */ + public static void setAnnotationColor(final PDAnnotation annotation, Color color) { + annotation.setColor(toPDColor(color)); + } + + + private static PDAnnotationLink createLink(PDPage page, PDRectangle rect, Color color, + LinkStyle linkStyle) { + PDAnnotationLink pdLink = new PDAnnotationLink(); + pdLink.setBorderStyle(toBorderStyle(linkStyle)); + PDRectangle rotatedRect = transformToPageRotation(rect, page); + pdLink.setRectangle(rotatedRect); + setAnnotationColor(pdLink, color); + return pdLink; + } + + private static PDBorderStyleDictionary toBorderStyle( + final LinkStyle linkStyle) { + if (linkStyle == LinkStyle.none) { + return getNoBorder(); + } + PDBorderStyleDictionary borderStyle = new PDBorderStyleDictionary(); + borderStyle.setStyle(PDBorderStyleDictionary.STYLE_UNDERLINE); + return borderStyle; + } + + private static PDColor toPDColor(final Color color) { + float[] components = new float[]{ + color.getRed() / 255f, color.getGreen() / 255f, color.getBlue() / 255f}; + return new PDColor(components, PDDeviceRGB.INSTANCE); + } + + /** + * Return the quad points representation of the given rect. + * + * @param rect the rectangle. + * @return the quad points. + */ + public static float[] toQuadPoints(final PDRectangle rect) { + return toQuadPoints(rect, 0, 0); + } + + /** + * Return the quad points representation of the given rect. + * + * @param rect the rectangle. + * @param xOffset the offset in x-direction to add. + * @param yOffset the offset in y-direction to add. + * @return the quad points. + */ + public static float[] toQuadPoints(final PDRectangle rect, float xOffset, + float yOffset) { + float[] quads = new float[8]; + quads[0] = rect.getLowerLeftX() + xOffset; // x1 + quads[1] = rect.getUpperRightY() + yOffset; // y1 + quads[2] = rect.getUpperRightX() + xOffset; // x2 + quads[3] = quads[1]; // y2 + quads[4] = quads[0]; // x3 + quads[5] = rect.getLowerLeftY() + yOffset; // y3 + quads[6] = quads[2]; // x4 + quads[7] = quads[5]; // y5 + return quads; + } + + /** + * Transform the quad points in order to match the page rotation + * + * @param quadPoints the quad points. + * @param page the page. + * @return the transformed quad points. + */ + public static float[] transformToPageRotation( + final float[] quadPoints, final PDPage page) { + AffineTransform transform = transformToPageRotation(page); + if (transform == null) { + return quadPoints; + } + float[] rotatedPoints = new float[quadPoints.length]; + transform.transform(quadPoints, 0, rotatedPoints, 0, 4); + return rotatedPoints; + } + + /** + * Transform the rectangle in order to match the page rotation + * + * @param rect the rectangle. + * @param page the page. + * @return the transformed rectangle. + */ + public static PDRectangle transformToPageRotation( + final PDRectangle rect, final PDPage page) { + AffineTransform transform = transformToPageRotation(page); + if (transform == null) { + return rect; + } + float[] points = new float[]{rect.getLowerLeftX(), rect.getLowerLeftY(), rect.getUpperRightX(), rect.getUpperRightY()}; + float[] rotatedPoints = new float[4]; + transform.transform(points, 0, rotatedPoints, 0, 2); + PDRectangle rotated = new PDRectangle(); + rotated.setLowerLeftX(rotatedPoints[0]); + rotated.setLowerLeftY(rotatedPoints[1]); + rotated.setUpperRightX(rotatedPoints[2]); + rotated.setUpperRightY(rotatedPoints[3]); + return rotated; + } + + private static AffineTransform transformToPageRotation(final PDPage page) { + int pageRotation = getPageRotation(page); + if (pageRotation == 0) { + return null; + } + float pageWidth = page.getMediaBox().getHeight(); + float pageHeight = page.getMediaBox().getWidth(); + AffineTransform transform = new AffineTransform(); + transform.rotate(pageRotation * Math.PI / 180, pageHeight / 2, + pageWidth / 2); + double offset = Math.abs(pageHeight - pageWidth) / 2; + transform.translate(-offset, offset); + return transform; + } + + private static PDBorderStyleDictionary getNoBorder() { + if (noBorder == null) { + noBorder = new PDBorderStyleDictionary(); + noBorder.setWidth(0); + } + return noBorder; + } + + + private static synchronized Map> getDocumentCache( + final PDDocument document) { + Map> cache = documentCaches.get(document); + if (cache == null) { + cache = new HashMap>(); + documentCaches.put(document, cache); + } + return cache; + } + + private static synchronized Map getImageCache( + final PDDocument document) { + Map> documentCache = getDocumentCache(document); + @SuppressWarnings("unchecked") + Map imageCache = (Map) documentCache + .get(IMAGE_CACHE); + if (imageCache == null) { + imageCache = new HashMap(); + documentCache.put(IMAGE_CACHE, imageCache); + } + return imageCache; + } + + private static synchronized PDImageXObject getCachedImage( + final PDDocument document, final BufferedImage image) + throws IOException { + Map imageCache = getImageCache(document); + PDImageXObject pdxObjectImage = imageCache.get(image); + if (pdxObjectImage == null) { + pdxObjectImage = LosslessFactory.createFromImage(document, image); + imageCache.put(image, pdxObjectImage); + } + return pdxObjectImage; + } + +} diff --git a/layout-pdfbox/src/main/java/org/xbib/graphics/layout/pdfbox/util/Enumerator.java b/layout-pdfbox/src/main/java/org/xbib/graphics/layout/pdfbox/util/Enumerator.java new file mode 100644 index 0000000..50b3fcc --- /dev/null +++ b/layout-pdfbox/src/main/java/org/xbib/graphics/layout/pdfbox/util/Enumerator.java @@ -0,0 +1,17 @@ +package org.xbib.graphics.layout.pdfbox.util; + +/** + * Defines an enumerator. + */ +public interface Enumerator { + + /** + * @return the next enumeration. + */ + String next(); + + /** + * @return the default separator. + */ + String getDefaultSeperator(); +} diff --git a/layout-pdfbox/src/main/java/org/xbib/graphics/layout/pdfbox/util/EnumeratorFactory.java b/layout-pdfbox/src/main/java/org/xbib/graphics/layout/pdfbox/util/EnumeratorFactory.java new file mode 100644 index 0000000..5884a11 --- /dev/null +++ b/layout-pdfbox/src/main/java/org/xbib/graphics/layout/pdfbox/util/EnumeratorFactory.java @@ -0,0 +1,65 @@ +package org.xbib.graphics.layout.pdfbox.util; + +import org.xbib.graphics.layout.pdfbox.util.Enumerators.AlphabeticEnumerator; +import org.xbib.graphics.layout.pdfbox.util.Enumerators.ArabicEnumerator; +import org.xbib.graphics.layout.pdfbox.util.Enumerators.LowerCaseAlphabeticEnumerator; +import org.xbib.graphics.layout.pdfbox.util.Enumerators.LowerCaseRomanEnumerator; +import org.xbib.graphics.layout.pdfbox.util.Enumerators.RomanEnumerator; +import java.util.Map; +import java.util.concurrent.ConcurrentHashMap; + +/** + * Enumerators are created using this factory. It allows you to register and use + * your own enumerations, if the built ins does not satisfy your needs.
      + * Currently supported are: + * + * + * + * + * + * + * + *
      NameKeySeperator
      Arabic1.
      RomanI.
      Roman Lower Casei.
      AlphabeticA)
      Alphabetic Lower Casea)
      + */ +public class EnumeratorFactory { + + private final static Map> ENUMERATORS = new ConcurrentHashMap>(); + + static { + register("1", ArabicEnumerator.class); + register("I", RomanEnumerator.class); + register("i", LowerCaseRomanEnumerator.class); + register("A", AlphabeticEnumerator.class); + register("a", LowerCaseAlphabeticEnumerator.class); + } + + /** + * Registers an Enumerator class for a given key. + * + * @param key the key (character) used in markup. + * @param enumeratorClass the enumerator class. + */ + public static void register(final String key, + final Class enumeratorClass) { + ENUMERATORS.put(key, enumeratorClass); + } + + /** + * Creates an Enumerator for the given key. + * + * @param key the key of the enumerator. + * @return the created enumerator. + */ + public static Enumerator createEnumerator(final String key) { + Class enumeratorClass = ENUMERATORS.get(key); + if (enumeratorClass == null) { + throw new IllegalArgumentException("no enumerator found for '" + + key + "'"); + } + try { + return enumeratorClass.getDeclaredConstructor().newInstance(); + } catch (Exception e) { + throw new RuntimeException("failed to create enumerator", e); + } + } +} diff --git a/layout-pdfbox/src/main/java/org/xbib/graphics/layout/pdfbox/util/Enumerators.java b/layout-pdfbox/src/main/java/org/xbib/graphics/layout/pdfbox/util/Enumerators.java new file mode 100644 index 0000000..6446afb --- /dev/null +++ b/layout-pdfbox/src/main/java/org/xbib/graphics/layout/pdfbox/util/Enumerators.java @@ -0,0 +1,241 @@ +package org.xbib.graphics.layout.pdfbox.util; + + +/** + * Container class for the default enumerators. + */ +public class Enumerators { + + /** + * Uses arabic numbers for the enumeration, and dot as the default + * separator.
      + * + *
      +     * 1. At vero eos et accusam.
      +     * 2. Et justo duo dolores ea rebum.
      +     * 3. Stet clita ...
      +     * 
      + */ + public static class ArabicEnumerator implements Enumerator { + + private int count; + + public ArabicEnumerator() { + this(1); + } + + public ArabicEnumerator(final int startCount) { + this.count = startCount; + } + + @Override + public String next() { + return String.valueOf(count++); + } + + @Override + public String getDefaultSeperator() { + return "."; + } + } + + /** + * Uses lower case letters for the enumeration, and braces as the default + * separator.
      + * + *
      +     * a) At vero eos et accusam.
      +     * b) Et justo duo dolores ea rebum.
      +     * c) Stet clita ...
      +     * 
      + */ + public static class LowerCaseAlphabeticEnumerator extends + AlphabeticEnumerator { + + public LowerCaseAlphabeticEnumerator() { + super(); + } + + public LowerCaseAlphabeticEnumerator(final int startCount) { + super(startCount); + } + + @Override + public String next() { + return super.next().toLowerCase(); + } + } + + /** + * Uses upper case letters for the enumeration, and braces as the default + * separator.
      + * + *
      +     * A) At vero eos et accusam.
      +     * B) Et justo duo dolores ea rebum.
      +     * C) Stet clita ...
      +     * 
      + */ + public static class AlphabeticEnumerator implements Enumerator { + + final static char[] digits = {'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', + 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T', + 'U', 'V', 'W', 'X', 'Y', 'Z'}; + + private int count; + + public AlphabeticEnumerator() { + this(1); + } + + public AlphabeticEnumerator(final int startCount) { + this.count = startCount; + } + + @Override + public String next() { + return toString(count++ - 1); + } + + @Override + public String getDefaultSeperator() { + return ")"; + } + + private static String toString(int i) { + char[] buf = new char[33]; + int charPos = 32; + + i = -i; + + while (i <= -digits.length) { + buf[charPos--] = digits[-(i % digits.length)]; + i = i / digits.length; + } + buf[charPos] = digits[-i]; + + return new String(buf, charPos, (33 - charPos)); + } + + } + + /** + * Uses lower case roman numbers for the enumeration, and dot as the default + * separator.
      + * + *
      +     *   i. At vero eos et accusam.
      +     *  ii. Et justo duo dolores ea rebum.
      +     * iii. Stet clita ...
      +     * 
      + */ + public static class LowerCaseRomanEnumerator extends RomanEnumerator { + + public LowerCaseRomanEnumerator() { + super(); + } + + public LowerCaseRomanEnumerator(final int startCount) { + super(startCount); + } + + @Override + public String next() { + return super.next().toLowerCase(); + } + } + + /** + * Uses upper case roman numbers for the enumeration, and dot as the default + * separator.
      + * + *
      +     *   I. At vero eos et accusam.
      +     *  II. Et justo duo dolores ea rebum.
      +     * III. Stet clita ...
      +     * 
      + */ + public static class RomanEnumerator implements Enumerator { + + private int count; + + public RomanEnumerator() { + this(1); + } + + public RomanEnumerator(final int startCount) { + this.count = startCount; + } + + @Override + public String next() { + return toRoman(count++); + } + + @Override + public String getDefaultSeperator() { + return "."; + } + + private String toRoman(int input) { + if (input < 1 || input > 3999) { + return "Invalid Roman Number Value"; + } + String s = ""; + while (input >= 1000) { + s += "M"; + input -= 1000; + } + while (input >= 900) { + s += "CM"; + input -= 900; + } + while (input >= 500) { + s += "D"; + input -= 500; + } + while (input >= 400) { + s += "CD"; + input -= 400; + } + while (input >= 100) { + s += "C"; + input -= 100; + } + while (input >= 90) { + s += "XC"; + input -= 90; + } + while (input >= 50) { + s += "L"; + input -= 50; + } + while (input >= 40) { + s += "XL"; + input -= 40; + } + while (input >= 10) { + s += "X"; + input -= 10; + } + while (input >= 9) { + s += "IX"; + input -= 9; + } + while (input >= 5) { + s += "V"; + input -= 5; + } + while (input >= 4) { + s += "IV"; + input -= 4; + } + while (input >= 1) { + s += "I"; + input -= 1; + } + return s; + } + } + +} diff --git a/layout-pdfbox/src/main/java/org/xbib/graphics/layout/pdfbox/util/Pair.java b/layout-pdfbox/src/main/java/org/xbib/graphics/layout/pdfbox/util/Pair.java new file mode 100644 index 0000000..822bba9 --- /dev/null +++ b/layout-pdfbox/src/main/java/org/xbib/graphics/layout/pdfbox/util/Pair.java @@ -0,0 +1,66 @@ +package org.xbib.graphics.layout.pdfbox.util; + +/** + * Generic container for a pair of objects. + * + * @param the generic parameter. + */ +public class Pair { + + private final T first; + private final T second; + + public Pair(T first, T second) { + super(); + this.first = first; + this.second = second; + } + + public T getFirst() { + return first; + } + + public T getSecond() { + return second; + } + + @Override + public String toString() { + return "Tuple [first=" + first + ", second=" + second + "]"; + } + + @Override + public int hashCode() { + final int prime = 31; + int result = 1; + result = prime * result + ((first == null) ? 0 : first.hashCode()); + result = prime * result + ((second == null) ? 0 : second.hashCode()); + return result; + } + + @Override + public boolean equals(Object obj) { + if (this == obj) { + return true; + } + if (obj == null) { + return false; + } + if (getClass() != obj.getClass()) { + return false; + } + @SuppressWarnings("rawtypes") + Pair other = (Pair) obj; + if (first == null) { + if (other.first != null) { + return false; + } + } else if (!first.equals(other.first)) { + return false; + } + if (second == null) { + return other.second == null; + } else return second.equals(other.second); + } + +} diff --git a/layout-pdfbox/src/main/java/org/xbib/graphics/layout/pdfbox/util/WordBreaker.java b/layout-pdfbox/src/main/java/org/xbib/graphics/layout/pdfbox/util/WordBreaker.java new file mode 100644 index 0000000..7c3f502 --- /dev/null +++ b/layout-pdfbox/src/main/java/org/xbib/graphics/layout/pdfbox/util/WordBreaker.java @@ -0,0 +1,28 @@ +package org.xbib.graphics.layout.pdfbox.util; + +import org.xbib.graphics.layout.pdfbox.text.FontDescriptor; +import java.io.IOException; + +/** + * This interface may be used to implement different strategies on how to break + * a word, if it does not fit into a line. + */ +public interface WordBreaker { + + /** + * Breaks the word in order to fit the given maximum width. + * + * @param word the word to break. + * @param fontDescriptor describing the font's type and size. + * @param maxWidth the maximum width to obey. + * @param breakHardIfNecessary indicates if the word should be broken hard to fit the width, + * in case there is no suitable position for breaking it + * adequately. + * @return the broken word, or null if it cannot be broken. + * @throws IOException by pdfbox + */ + Pair breakWord(final String word, + final FontDescriptor fontDescriptor, final float maxWidth, + final boolean breakHardIfNecessary) throws IOException; + +} diff --git a/layout-pdfbox/src/main/java/org/xbib/graphics/layout/pdfbox/util/WordBreakerFactory.java b/layout-pdfbox/src/main/java/org/xbib/graphics/layout/pdfbox/util/WordBreakerFactory.java new file mode 100644 index 0000000..d747e82 --- /dev/null +++ b/layout-pdfbox/src/main/java/org/xbib/graphics/layout/pdfbox/util/WordBreakerFactory.java @@ -0,0 +1,66 @@ +package org.xbib.graphics.layout.pdfbox.util; + +import org.xbib.graphics.layout.pdfbox.util.WordBreakers.DefaultWordBreaker; +import org.xbib.graphics.layout.pdfbox.util.WordBreakers.NonBreakingWordBreaker; +import java.util.Map; +import java.util.concurrent.ConcurrentHashMap; + +/** + * Factory for creating a {@link WordBreaker}. This may be used to define a + * custom strategy for breaking words. By default the {@link DefaultWordBreaker} + * is used. Another predefined word breaker is the + * {@link NonBreakingWordBreaker} which may be used to get the legacy behavior. + * To switch to a different word breaker, just set the system property + * {@link #WORD_BREAKER_CLASS_PROPERTY pdfbox.layout.word.breaker} to the class + * name of the breaker to use. + */ +public class WordBreakerFactory { + + /** + * constant for the system property pdfbox.layout.word.breaker. + */ + public final static String WORD_BREAKER_CLASS_PROPERTY = "pdfbox.layout.word.breaker"; + + /** + * class name of the default word breaker. + */ + public final static String DEFAULT_WORD_BREAKER_CLASS_NAME = DefaultWordBreaker.class + .getName(); + + /** + * class name of the (legacy) non-breaking word breaker. + */ + public final static String LEGACY_WORD_BREAKER_CLASS_NAME = NonBreakingWordBreaker.class + .getName(); + + private final static WordBreaker DEFAULT_WORD_BREAKER = new DefaultWordBreaker(); + private final static Map WORD_BREAKERS = new ConcurrentHashMap(); + + /** + * @return the word breaker instance to use. + */ + public static WordBreaker getWorkBreaker() { + return getWorkBreaker(System.getProperty(WORD_BREAKER_CLASS_PROPERTY)); + } + + private static WordBreaker getWorkBreaker(String className) { + if (className == null) { + return DEFAULT_WORD_BREAKER; + } + WordBreaker wordBreaker = WORD_BREAKERS.get(className); + if (wordBreaker == null) { + wordBreaker = createWordBreakerInstance(className); + WORD_BREAKERS.put(className, wordBreaker); + } + return wordBreaker; + } + + private static WordBreaker createWordBreakerInstance(final String className) { + try { + return (WordBreaker) Class.forName(className).getDeclaredConstructor().newInstance(); + } catch (Exception e) { + throw new RuntimeException(String.format( + "failed to create word breaker '%s'", className), e); + } + } +} diff --git a/layout-pdfbox/src/main/java/org/xbib/graphics/layout/pdfbox/util/WordBreakers.java b/layout-pdfbox/src/main/java/org/xbib/graphics/layout/pdfbox/util/WordBreakers.java new file mode 100644 index 0000000..0cca73a --- /dev/null +++ b/layout-pdfbox/src/main/java/org/xbib/graphics/layout/pdfbox/util/WordBreakers.java @@ -0,0 +1,148 @@ +package org.xbib.graphics.layout.pdfbox.util; + +import org.xbib.graphics.layout.pdfbox.text.FontDescriptor; +import org.xbib.graphics.layout.pdfbox.text.TextSequenceUtil; +import java.io.IOException; +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +/** + * Container class for the default word breakers. + */ +public class WordBreakers { + + /** + * May by used for legacy compatibility, does not break at all. + */ + public static class NonBreakingWordBreaker implements WordBreaker { + + @Override + public Pair breakWord(String word, + FontDescriptor fontDescriptor, float maxWidth, + boolean breakHardIfNecessary) throws IOException { + return null; + } + + } + + /** + * Abstract base class for implementing (custom) word breakers. Tries to + * break the word {@link #breakWordSoft(String, FontDescriptor, float) + * softly}, or - if this is not possible - + * {@link #breakWordHard(String, FontDescriptor, float) hard}. + */ + public static abstract class AbstractWordBreaker implements WordBreaker { + + @Override + public Pair breakWord(final String word, + final FontDescriptor fontDescriptor, final float maxWidth, + final boolean breakHardIfNecessary) throws IOException { + + Pair brokenWord = breakWordSoft(word, fontDescriptor, + maxWidth); + if (brokenWord == null && breakHardIfNecessary) { + brokenWord = breakWordHard(word, fontDescriptor, maxWidth); + } + return brokenWord; + } + + /** + * To be implemented by subclasses. Give your best to break the word + * softly using your strategy, otherwise return null. + * + * @param word the word to break. + * @param fontDescriptor describing the font's type and size. + * @param maxWidth the maximum width to obey. + * @return the broken word, or null if it cannot be broken. + * @throws IOException by pdfbox + */ + abstract protected Pair breakWordSoft(final String word, + final FontDescriptor fontDescriptor, final float maxWidth) + throws IOException; + + /** + * Breaks the word hard at the outermost position that fits the given + * max width. + * + * @param word the word to break. + * @param fontDescriptor describing the font's type and size. + * @param maxWidth the maximum width to obey. + * @return the broken word, or null if it cannot be broken. + * @throws IOException by pdfbox + */ + protected Pair breakWordHard(final String word, + final FontDescriptor fontDescriptor, final float maxWidth) + throws IOException { + int cutIndex = (int) (maxWidth / TextSequenceUtil.getEmWidth(fontDescriptor)); + float currentWidth = TextSequenceUtil.getStringWidth(word.substring(0, cutIndex), + fontDescriptor); + if (currentWidth > maxWidth) { + while (currentWidth > maxWidth) { + --cutIndex; + currentWidth = TextSequenceUtil.getStringWidth(word.substring(0, cutIndex), + fontDescriptor); + } + ++cutIndex; + } else if (currentWidth < maxWidth) { + while (currentWidth < maxWidth) { + ++cutIndex; + currentWidth = TextSequenceUtil.getStringWidth(word.substring(0, cutIndex), + fontDescriptor); + } + --cutIndex; + } + + return new Pair(word.substring(0, cutIndex), + word.substring(cutIndex)); + } + + } + + /** + * Breaks a word if one of the following characters is found after a + * non-digit letter: + *
        + *
      • .
      • + *
      • ,
      • + *
      • -
      • + *
      • /
      • + *
      + */ + public static class DefaultWordBreaker extends AbstractWordBreaker { + + /** + * A letter followed by either -, ., + * , or /. + */ + private final Pattern breakPattern = Pattern + .compile("[A-Za-z\u00C0-\u00D6\u00D8-\u00F6\u00F8-\u00FF]([\\-\\.\\,/])"); + + @Override + protected Pair breakWordSoft(final String word, + final FontDescriptor fontDescriptor, final float maxWidth) + throws IOException { + Matcher matcher = breakPattern.matcher(word); + int breakIndex = -1; + boolean maxWidthExceeded = false; + while (!maxWidthExceeded && matcher.find()) { + int currentIndex = matcher.end(); + if (currentIndex < word.length() - 1) { + if (TextSequenceUtil.getStringWidth(word.substring(0, currentIndex), + fontDescriptor) < maxWidth) { + breakIndex = currentIndex; + } else { + maxWidthExceeded = true; + } + } + } + + if (breakIndex < 0) { + return null; + } + return new Pair(word.substring(0, breakIndex), + word.substring(breakIndex)); + } + + } + +} diff --git a/layout-pdfbox/src/test/java/org/xbib/graphics/layout/pdfbox/Aligned.java b/layout-pdfbox/src/test/java/org/xbib/graphics/layout/pdfbox/Aligned.java new file mode 100644 index 0000000..5998340 --- /dev/null +++ b/layout-pdfbox/src/test/java/org/xbib/graphics/layout/pdfbox/Aligned.java @@ -0,0 +1,47 @@ +package org.xbib.graphics.layout.pdfbox; + +import org.apache.pdfbox.pdmodel.font.PDType1Font; +import org.junit.jupiter.api.Test; +import org.xbib.graphics.layout.pdfbox.elements.Document; +import org.xbib.graphics.layout.pdfbox.elements.Paragraph; +import org.xbib.graphics.layout.pdfbox.elements.render.VerticalLayoutHint; +import org.xbib.graphics.layout.pdfbox.text.Alignment; +import org.xbib.graphics.layout.pdfbox.util.WordBreakerFactory; +import java.io.FileOutputStream; +import java.io.OutputStream; + +public class Aligned { + + @Test + public void test() throws Exception { + System.setProperty(WordBreakerFactory.WORD_BREAKER_CLASS_PROPERTY, + WordBreakerFactory.LEGACY_WORD_BREAKER_CLASS_NAME); + Document document = new Document(40, 60, 40, 60); + Paragraph paragraph = new Paragraph(); + paragraph.addText("This is some left aligned text", 11, + PDType1Font.HELVETICA); + paragraph.setAlignment(Alignment.Left); + paragraph.setMaxWidth(40); + document.add(paragraph, VerticalLayoutHint.LEFT); + paragraph = new Paragraph(); + paragraph.addText("This is some centered text", 11, + PDType1Font.HELVETICA); + paragraph.setAlignment(Alignment.Center); + paragraph.setMaxWidth(40); + document.add(paragraph, VerticalLayoutHint.CENTER); + paragraph = new Paragraph(); + paragraph.addText("This is some right aligned text", 11, + PDType1Font.HELVETICA); + paragraph.setAlignment(Alignment.Right); + paragraph.setMaxWidth(40); + document.add(paragraph, VerticalLayoutHint.RIGHT); + paragraph = new Paragraph(); + paragraph.addText("Text is right aligned, and paragraph centered", 11, + PDType1Font.HELVETICA); + paragraph.setAlignment(Alignment.Right); + paragraph.setMaxWidth(40); + document.add(paragraph, VerticalLayoutHint.CENTER); + OutputStream outputStream = new FileOutputStream("build/aligned.pdf"); + document.save(outputStream); + } +} diff --git a/layout-pdfbox/src/test/java/org/xbib/graphics/layout/pdfbox/Columns.java b/layout-pdfbox/src/test/java/org/xbib/graphics/layout/pdfbox/Columns.java new file mode 100644 index 0000000..6e77d35 --- /dev/null +++ b/layout-pdfbox/src/test/java/org/xbib/graphics/layout/pdfbox/Columns.java @@ -0,0 +1,87 @@ +package org.xbib.graphics.layout.pdfbox; + +import org.junit.jupiter.api.Test; +import org.xbib.graphics.layout.pdfbox.elements.Document; +import org.xbib.graphics.layout.pdfbox.elements.Paragraph; +import org.xbib.graphics.layout.pdfbox.elements.VerticalSpacer; +import org.xbib.graphics.layout.pdfbox.elements.render.ColumnLayout; +import org.xbib.graphics.layout.pdfbox.elements.render.VerticalLayoutHint; +import org.xbib.graphics.layout.pdfbox.text.BaseFont; +import java.io.FileOutputStream; +import java.io.OutputStream; + +public class Columns { + + @Test + public void test() throws Exception { + String text1 = "Lorem ipsum dolor sit amet, consetetur sadipscing elitr, " + + "sed diam nonumy eirmod tempor invidunt ut labore et dolore magna " + + "aliquyam erat, _sed diam_ voluptua. At vero eos et *accusam et justo* " + + "duo dolores et ea rebum.\n\nStet clita kasd gubergren, no sea takimata " + + "sanctus est *Lorem ipsum _dolor* sit_ amet. Lorem ipsum dolor sit amet, " + + "consetetur sadipscing elitr, sed diam nonumy eirmod tempor invidunt " + + "ut labore et dolore magna aliquyam erat, *sed diam voluptua.\n\n" + + "At vero eos et accusam* et justo duo dolores et ea rebum. Stet clita kasd " + + "gubergren, no sea takimata sanctus est Lorem ipsum dolor sit amet.\n\n"; + + String text2 = "At *vero eos et accusam* et justo duo dolores et ea rebum. " + + "Stet clita kasd gubergren, no sea takimata\n\n" + + "sanctus est Lorem ipsum dolor sit amet. Lorem ipsum dolor sit amet, " + + "_consetetur sadipscing elitr_, sed diam nonumy eirmod tempor invidunt " + + "ut labore et dolore magna aliquyam erat, sed diam voluptua. At vero " + + "eos et _accusam et *justo* duo dolores_ et ea rebum. Stet clita kasd " + + "gubergren, no sea takimata sanctus est Lorem ipsum dolor sit amet.\n"; + + Document document = new Document(40, 50, 40, 60); + + Paragraph title = new Paragraph(); + title.addMarkup("*This Text is organized in Colums*", 20, BaseFont.Times); + document.add(title, VerticalLayoutHint.CENTER); + document.add(new VerticalSpacer(5)); + + // use column layout from now on + document.add(new ColumnLayout(2, 10)); + + Paragraph paragraph1 = new Paragraph(); + paragraph1.addMarkup(text1, 11, BaseFont.Times); + document.add(paragraph1); + + Paragraph paragraph2 = new Paragraph(); + paragraph2.addMarkup(text2, 12, BaseFont.Helvetica); + document.add(paragraph2); + + Paragraph paragraph3 = new Paragraph(); + paragraph3.addMarkup(text1, 8, BaseFont.Courier); + document.add(paragraph3); + + document.add(paragraph1); + document.add(paragraph3); + document.add(paragraph1); + document.add(paragraph2); + document.add(paragraph1); + document.add(paragraph3); + document.add(paragraph2); + document.add(paragraph1); + document.add(paragraph1); + document.add(paragraph3); + document.add(paragraph2); + document.add(paragraph2); + document.add(paragraph3); + document.add(paragraph1); + document.add(paragraph1); + document.add(paragraph2); + document.add(paragraph1); + document.add(paragraph3); + document.add(paragraph2); + document.add(paragraph3); + document.add(paragraph1); + document.add(paragraph1); + document.add(paragraph3); + document.add(paragraph2); + document.add(paragraph2); + + final OutputStream outputStream = new FileOutputStream("build/columns.pdf"); + document.save(outputStream); + + } +} diff --git a/layout-pdfbox/src/test/java/org/xbib/graphics/layout/pdfbox/CustomAnnotation.java b/layout-pdfbox/src/test/java/org/xbib/graphics/layout/pdfbox/CustomAnnotation.java new file mode 100644 index 0000000..c282f41 --- /dev/null +++ b/layout-pdfbox/src/test/java/org/xbib/graphics/layout/pdfbox/CustomAnnotation.java @@ -0,0 +1,226 @@ +package org.xbib.graphics.layout.pdfbox; + +import org.apache.pdfbox.pdmodel.PDDocument; +import org.apache.pdfbox.pdmodel.common.PDRectangle; +import org.apache.pdfbox.pdmodel.font.PDType1Font; +import org.apache.pdfbox.pdmodel.interactive.annotation.PDAnnotationTextMarkup; +import org.junit.jupiter.api.Test; +import org.xbib.graphics.layout.pdfbox.elements.Document; +import org.xbib.graphics.layout.pdfbox.elements.PageFormat; +import org.xbib.graphics.layout.pdfbox.elements.Paragraph; +import org.xbib.graphics.layout.pdfbox.text.BaseFont; +import org.xbib.graphics.layout.pdfbox.text.DrawContext; +import org.xbib.graphics.layout.pdfbox.text.Position; +import org.xbib.graphics.layout.pdfbox.text.annotations.Annotated; +import org.xbib.graphics.layout.pdfbox.text.annotations.AnnotatedStyledText; +import org.xbib.graphics.layout.pdfbox.text.annotations.Annotation; +import org.xbib.graphics.layout.pdfbox.text.annotations.AnnotationCharacters; +import org.xbib.graphics.layout.pdfbox.text.annotations.AnnotationCharacters.AnnotationControlCharacter; +import org.xbib.graphics.layout.pdfbox.text.annotations.AnnotationCharacters.AnnotationControlCharacterFactory; +import org.xbib.graphics.layout.pdfbox.text.annotations.AnnotationProcessor; +import org.xbib.graphics.layout.pdfbox.text.annotations.AnnotationProcessorFactory; +import org.xbib.graphics.layout.pdfbox.util.CompatibilityHelper; +import java.awt.Color; +import java.io.FileOutputStream; +import java.io.IOException; +import java.io.OutputStream; +import java.util.Collections; +import java.util.List; +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +public class CustomAnnotation { + + /** + * Represents a highlight annotation that might be added to a + * {@link AnnotatedStyledText}. + */ + public static class HighlightAnnotation implements Annotation { + + private final Color color; + + public HighlightAnnotation(Color color) { + this.color = color; + } + + public Color getColor() { + return color; + } + } + + /** + * Processes {@link HighlightAnnotation}s by adding a colored highlight to + * the pdf. + */ + public static class HighlightAnnotationProcessor implements + AnnotationProcessor { + + @Override + public void annotatedObjectDrawn(Annotated drawnObject, + DrawContext drawContext, Position upperLeft, float width, + float height) throws IOException { + + Iterable HighlightAnnotations = drawnObject + .getAnnotationsOfType(HighlightAnnotation.class); + + for (HighlightAnnotation highlightAnnotation : HighlightAnnotations) { + + // use PDF text markup to implement the highlight + PDAnnotationTextMarkup markup = new PDAnnotationTextMarkup( + PDAnnotationTextMarkup.SUB_TYPE_HIGHLIGHT); + + // use the bounding box of the drawn object to position the + // highlight + PDRectangle bounds = new PDRectangle(); + bounds.setLowerLeftX(upperLeft.getX()); + bounds.setLowerLeftY(upperLeft.getY() - height); + bounds.setUpperRightX(upperLeft.getX() + width); + bounds.setUpperRightY(upperLeft.getY() + 1); + markup.setRectangle(bounds); + float[] quadPoints = CompatibilityHelper.toQuadPoints(bounds); + quadPoints = CompatibilityHelper.transformToPageRotation( + quadPoints, drawContext.getCurrentPage()); + markup.setQuadPoints(quadPoints); + + // set the highlight color if given + if (highlightAnnotation.getColor() != null) { + CompatibilityHelper.setAnnotationColor(markup, highlightAnnotation.getColor()); + } + + // finally add the markup to the PDF + drawContext.getCurrentPage().getAnnotations().add(markup); + } + } + + @Override + public void beforePage(DrawContext drawContext) throws IOException { + // nothing to do here for us + } + + @Override + public void afterPage(DrawContext drawContext) throws IOException { + // nothing to do here for us + } + + @Override + public void afterRender(PDDocument document) throws IOException { + // nothing to do here for us + } + + } + + /** + * The control character is a representation of the parsed markup. It + * contains any information passed by the markup necessary for rendering, in + * our case here it is just the color for the highlight. + */ + public static class HighlightControlCharacter extends + AnnotationControlCharacter { + + private final HighlightAnnotation annotation; + + protected HighlightControlCharacter(final Color color) { + super("HIGHLIGHT", HighlightControlCharacterFactory.TO_ESCAPE); + annotation = new HighlightAnnotation(color); + } + + @Override + public HighlightAnnotation getAnnotation() { + return annotation; + } + + @Override + public Class getAnnotationType() { + return HighlightAnnotation.class; + } + } + + /** + * Provides a regex pattern to match the highlight markup, and creates an + * appropriate control character. In our case here the markup syntax is + * either {hl} or with optional color information + * {hl:#ee22aa}, where the color is given as hex RGB code + * (ee22aa in this case). It can be escaped with a backslash ('\'). + */ + private static class HighlightControlCharacterFactory implements + AnnotationControlCharacterFactory { + + private final static Pattern PATTERN = Pattern + .compile("(? charactersSoFar) { + Color color = null; + String hex = matcher.group(3); + if (hex != null) { + int r = Integer.parseUnsignedInt(hex.substring(0, 2), 16); + int g = Integer.parseUnsignedInt(hex.substring(2, 4), 16); + int b = Integer.parseUnsignedInt(hex.substring(4, 6), 16); + color = new Color(r, g, b); + } + return new HighlightControlCharacter(color); + } + + @Override + public Pattern getPattern() { + return PATTERN; + } + + @Override + public String unescape(String text) { + return text + .replaceAll("\\\\" + Pattern.quote(TO_ESCAPE), TO_ESCAPE); + } + + @Override + public boolean patternMatchesBeginOfLine() { + return false; + } + + } + + @Test + public void test() throws Exception { + + // register our custom highlight annotation processor + AnnotationProcessorFactory.register(HighlightAnnotationProcessor.class); + + Document document = new Document(PageFormat.with().A4() + .margins(40, 60, 40, 60).portrait().build()); + + Paragraph paragraph = new Paragraph(); + paragraph.addText("Hello there, here is ", 10, PDType1Font.HELVETICA); + + // now add some annotated text using our custom highlight annotation + HighlightAnnotation annotation = new HighlightAnnotation(Color.green); + AnnotatedStyledText highlightedText = new AnnotatedStyledText( + "highlighted text", 10, PDType1Font.HELVETICA, Color.black, 0f, + Collections.singleton(annotation)); + paragraph.add(highlightedText); + + paragraph + .addText( + ". Do whatever you want here...strike, squiggle, whatsoever\n\n", + 10, PDType1Font.HELVETICA); + paragraph.setMaxWidth(150); + document.add(paragraph); + + // register markup processing for the highlight annotation + AnnotationCharacters.register(new HighlightControlCharacterFactory()); + + paragraph = new Paragraph(); + paragraph + .addMarkup( + "Hello there, here is {hl:#ffff00}highlighted text{hl}. " + + "Do whatever you want here...strike, squiggle, whatsoever\n\n", + 10, BaseFont.Helvetica); + paragraph.setMaxWidth(150); + document.add(paragraph); + + final OutputStream outputStream = new FileOutputStream("build/customannotation.pdf"); + document.save(outputStream); + } +} diff --git a/layout-pdfbox/src/test/java/org/xbib/graphics/layout/pdfbox/CustomRenderer.java b/layout-pdfbox/src/test/java/org/xbib/graphics/layout/pdfbox/CustomRenderer.java new file mode 100644 index 0000000..53a0e00 --- /dev/null +++ b/layout-pdfbox/src/test/java/org/xbib/graphics/layout/pdfbox/CustomRenderer.java @@ -0,0 +1,136 @@ +package org.xbib.graphics.layout.pdfbox; + +import org.apache.pdfbox.pdmodel.font.PDType1Font; +import org.junit.jupiter.api.Test; +import org.xbib.graphics.layout.pdfbox.elements.Document; +import org.xbib.graphics.layout.pdfbox.elements.Element; +import org.xbib.graphics.layout.pdfbox.elements.HorizontalRuler; +import org.xbib.graphics.layout.pdfbox.elements.Paragraph; +import org.xbib.graphics.layout.pdfbox.elements.render.LayoutHint; +import org.xbib.graphics.layout.pdfbox.elements.render.RenderContext; +import org.xbib.graphics.layout.pdfbox.elements.render.RenderListener; +import org.xbib.graphics.layout.pdfbox.elements.render.Renderer; +import org.xbib.graphics.layout.pdfbox.elements.render.VerticalLayoutHint; +import org.xbib.graphics.layout.pdfbox.shape.Stroke; +import org.xbib.graphics.layout.pdfbox.shape.Stroke.CapStyle; +import org.xbib.graphics.layout.pdfbox.text.Alignment; +import org.xbib.graphics.layout.pdfbox.text.BaseFont; +import org.xbib.graphics.layout.pdfbox.text.Position; +import org.xbib.graphics.layout.pdfbox.text.TextFlow; +import org.xbib.graphics.layout.pdfbox.text.TextFlowUtil; +import org.xbib.graphics.layout.pdfbox.text.TextSequenceUtil; +import java.awt.Color; +import java.io.FileOutputStream; +import java.io.IOException; +import java.io.OutputStream; + +public class CustomRenderer { + + @Test + public void test() throws Exception { + String text1 = "Lorem ipsum dolor sit amet, consetetur sadipscing elitr, " + + "sed diam nonumy eirmod tempor invidunt ut labore et dolore magna " + + "aliquyam erat, _sed diam_ voluptua. At vero eos et *accusam et justo* " + + "duo dolores et ea rebum.\n\nStet clita kasd gubergren, no sea takimata " + + "sanctus est *Lorem ipsum _dolor* sit_ amet. Lorem ipsum dolor sit amet, " + + "consetetur sadipscing elitr, sed diam nonumy eirmod tempor invidunt " + + "ut labore et dolore magna aliquyam erat, *sed diam voluptua.\n\n" + + "At vero eos et accusam* et justo duo dolores et ea rebum. Stet clita kasd " + + "gubergren, no sea takimata sanctus est Lorem ipsum dolor sit amet.\n\n"; + + String text2 = "At *vero eos et accusam* et justo duo dolores et ea rebum." + + "Stet clita kasd gubergren, no sea takimata\n\n" + + "sanctus est Lorem ipsum dolor sit amet. Lorem ipsum dolor sit amet, " + + "_consetetur sadipscing elitr_, sed diam nonumy eirmod tempor invidunt " + + "ut labore et dolore magna aliquyam erat, sed diam voluptua. At vero " + + "eos et _accusam et *justo* duo dolores_ et ea rebum. Stet clita kasd " + + "gubergren, no sea takimata sanctus est Lorem ipsum dolor sit amet.\n"; + + Document document = new Document(40, 60, 40, 60); + SectionRenderer sectionRenderer = new SectionRenderer(); + document.addRenderer(sectionRenderer); + document.addRenderListener(sectionRenderer); + + Paragraph paragraph = new Paragraph(); + paragraph.addMarkup(text1, 11, BaseFont.Times); + paragraph.addMarkup(text2, 12, BaseFont.Helvetica); + paragraph.addMarkup(text1, 8, BaseFont.Courier); + + document.add(new Section(1)); + document.add(paragraph); + document.add(paragraph); + document.add(paragraph); + document.add(new Section(2)); + document.add(paragraph); + document.add(paragraph); + document.add(paragraph); + document.add(new Section(3)); + document.add(paragraph); + document.add(paragraph); + + final OutputStream outputStream = new FileOutputStream("build/customrenderer.pdf"); + document.save(outputStream); + + } + + public static class SectionRenderer implements Renderer, RenderListener { + + private int sectionNumber; + + @Override + public boolean render(RenderContext renderContext, Element element, + LayoutHint layoutHint) throws IOException { + if (element instanceof Section) { + + if (renderContext.getPageIndex() > 0) { + // no new page on first page ;-) + renderContext.newPage(); + } + sectionNumber = ((Section) element).getNumber(); + + renderContext.render(renderContext, element, layoutHint); + + Element ruler = new HorizontalRuler(Stroke.builder().lineWidth(2) + .capStyle(CapStyle.RoundCap).build(), Color.black); + renderContext.render(renderContext, ruler, VerticalLayoutHint.builder().marginBottom(10).build()); + + return true; + } + return false; + } + + @Override + public void beforePage(RenderContext renderContext) { + + } + + @Override + public void afterPage(RenderContext renderContext) throws IOException { + String content = String.format("Section %s, Page %s", + sectionNumber, renderContext.getPageIndex() + 1); + TextFlow text = TextFlowUtil.createTextFlow(content, 11, + PDType1Font.TIMES_ROMAN); + float offset = renderContext.getPageFormat().getMarginLeft() + + TextSequenceUtil.getOffset(text, + renderContext.getWidth(), Alignment.Right); + text.drawText(renderContext.getContentStream(), new Position( + offset, 30), Alignment.Right, null); + } + + } + + public static class Section extends Paragraph { + private final int number; + + public Section(int number) throws IOException { + super(); + this.number = number; + addMarkup(String.format("*Section %d", number), 16, BaseFont.Times); + } + + public int getNumber() { + return number; + } + + } +} \ No newline at end of file diff --git a/layout-pdfbox/src/test/java/org/xbib/graphics/layout/pdfbox/Frames.java b/layout-pdfbox/src/test/java/org/xbib/graphics/layout/pdfbox/Frames.java new file mode 100644 index 0000000..6f00f71 --- /dev/null +++ b/layout-pdfbox/src/test/java/org/xbib/graphics/layout/pdfbox/Frames.java @@ -0,0 +1,96 @@ +package org.xbib.graphics.layout.pdfbox; + +import org.junit.jupiter.api.Test; +import org.xbib.graphics.layout.pdfbox.elements.Document; +import org.xbib.graphics.layout.pdfbox.elements.Frame; +import org.xbib.graphics.layout.pdfbox.elements.PageFormat; +import org.xbib.graphics.layout.pdfbox.elements.Paragraph; +import org.xbib.graphics.layout.pdfbox.elements.render.VerticalLayoutHint; +import org.xbib.graphics.layout.pdfbox.shape.Ellipse; +import org.xbib.graphics.layout.pdfbox.shape.Rect; +import org.xbib.graphics.layout.pdfbox.shape.RoundRect; +import org.xbib.graphics.layout.pdfbox.shape.Stroke; +import org.xbib.graphics.layout.pdfbox.text.Alignment; +import org.xbib.graphics.layout.pdfbox.text.BaseFont; +import org.xbib.graphics.layout.pdfbox.text.Constants; +import java.awt.Color; +import java.io.FileOutputStream; +import java.io.OutputStream; + +public class Frames { + + @Test + public void test() throws Exception { + String text1 = "{color:#ffffff}Lorem ipsum dolor sit amet, consetetur sadipscing elitr, " + + "sed diam nonumy eirmod tempor invidunt ut labore et dolore magna " + + "aliquyam erat, _sed diam_ voluptua. At vero eos et *accusam et justo* " + + "duo dolores et ea rebum.\n\nStet clita kasd gubergren, no sea takimata " + + "sanctus est *Lorem ipsum _dolor* sit_ amet. Lorem ipsum dolor sit amet, " + + "consetetur sadipscing elitr, sed diam nonumy eirmod tempor invidunt " + + "ut labore et dolore magna aliquyam erat, *sed diam voluptua.\n\n" + + "At vero eos et accusam* et justo duo dolores et ea rebum. Stet clita kasd " + + "gubergren, no sea takimata sanctus est Lorem ipsum dolor sit amet."; + + String text2 = "At *vero eos et accusam* et justo duo dolores et ea rebum. " + + "Stet clita kasd gubergren, no sea takimata.\n\n" + + "Sanctus est Lorem ipsum dolor sit amet. Lorem ipsum dolor sit amet, " + + "_consetetur sadipscing elitr_, sed diam nonumy eirmod tempor invidunt " + + "ut labore et dolore magna aliquyam erat, sed diam voluptua. At vero " + + "eos et _accusam et *justo* duo dolores_ et ea rebum. Stet clita kasd " + + "gubergren, no sea takimata sanctus est Lorem ipsum dolor sit amet.\n"; + + Document document = new Document(new PageFormat(Constants.A5)); + + Paragraph paragraph = new Paragraph(); + paragraph.addMarkup("Am I living in a box?", 11, BaseFont.Times); + Frame frame = new Frame(paragraph); + frame.setShape(new Rect()); + frame.setBorder(Color.black, new Stroke()); + frame.setPadding(10, 10, 5, 5); + frame.setMargin(40, 40, 20, 10); + document.add(frame, VerticalLayoutHint.CENTER); + + paragraph = new Paragraph(); + paragraph.addMarkup(text1, 11, BaseFont.Times); + frame = new Frame(paragraph, 200f, null); + frame.setShape(new Rect()); + frame.setBackgroundColor(Color.black); + frame.setPadding(10, 10, 5, 5); + frame.setMargin(40, 40, 20, 10); + document.add(frame); + + paragraph = new Paragraph(); + paragraph.addMarkup("{color:#aa00aa}*Ain't no rectangle*", 22, BaseFont.Helvetica); + paragraph.setAlignment(Alignment.Center); + frame = new Frame(paragraph, 300f, 100f); + frame.setShape(new Ellipse()); + frame.setBorder(Color.green, new Stroke(2)); + frame.setBackgroundColor(Color.pink); + frame.setPadding(50, 0, 35, 0); +// frame.setMargin(30, 30, 20, 10); + document.add(frame); + + paragraph = new Paragraph(); + paragraph.addMarkup("Frames also paginate, see here:\n\n", 13, BaseFont.Times); + paragraph.addMarkup(text2, 11, BaseFont.Times); + paragraph.addMarkup(text2, 11, BaseFont.Times); + frame = new Frame(paragraph, null, null); + frame.setShape(new RoundRect(10)); + frame.setBorder(Color.magenta, new Stroke(3)); + frame.setBackgroundColor(new Color(255, 240, 180)); + frame.setPadding(20, 15, 10, 15); + frame.setMargin(50, 50, 20, 10); + + paragraph = new Paragraph(); + paragraph.addMarkup(text2, 11, BaseFont.Times); + paragraph.addMarkup(text2, 11, BaseFont.Times); + frame.add(paragraph); + + document.add(frame); + + final OutputStream outputStream = new FileOutputStream("build/frames.pdf"); + document.save(outputStream); + + } + +} diff --git a/layout-pdfbox/src/test/java/org/xbib/graphics/layout/pdfbox/HelloDoc.java b/layout-pdfbox/src/test/java/org/xbib/graphics/layout/pdfbox/HelloDoc.java new file mode 100644 index 0000000..6535995 --- /dev/null +++ b/layout-pdfbox/src/test/java/org/xbib/graphics/layout/pdfbox/HelloDoc.java @@ -0,0 +1,25 @@ +package org.xbib.graphics.layout.pdfbox; + +import org.apache.pdfbox.pdmodel.font.PDType1Font; +import org.junit.jupiter.api.Test; +import org.xbib.graphics.layout.pdfbox.elements.Document; +import org.xbib.graphics.layout.pdfbox.elements.Paragraph; +import java.io.FileOutputStream; +import java.io.OutputStream; + +public class HelloDoc { + + @Test + public void test() throws Exception { + Document document = new Document(40, 60, 40, 60); + + Paragraph paragraph = new Paragraph(); + paragraph.addText("Hello Document", 20, + PDType1Font.HELVETICA); + document.add(paragraph); + + final OutputStream outputStream = new FileOutputStream("build/hellodoc.pdf"); + document.save(outputStream); + + } +} diff --git a/layout-pdfbox/src/test/java/org/xbib/graphics/layout/pdfbox/Indentation.java b/layout-pdfbox/src/test/java/org/xbib/graphics/layout/pdfbox/Indentation.java new file mode 100644 index 0000000..7f1d235 --- /dev/null +++ b/layout-pdfbox/src/test/java/org/xbib/graphics/layout/pdfbox/Indentation.java @@ -0,0 +1,158 @@ +package org.xbib.graphics.layout.pdfbox; + +import org.apache.pdfbox.pdmodel.font.PDType1Font; +import org.junit.jupiter.api.Test; +import org.xbib.graphics.layout.pdfbox.elements.Document; +import org.xbib.graphics.layout.pdfbox.elements.Paragraph; +import org.xbib.graphics.layout.pdfbox.text.Alignment; +import org.xbib.graphics.layout.pdfbox.text.BaseFont; +import org.xbib.graphics.layout.pdfbox.text.Indent; +import org.xbib.graphics.layout.pdfbox.text.SpaceUnit; +import org.xbib.graphics.layout.pdfbox.util.CompatibilityHelper; +import org.xbib.graphics.layout.pdfbox.util.Enumerators.AlphabeticEnumerator; +import org.xbib.graphics.layout.pdfbox.util.Enumerators.ArabicEnumerator; +import org.xbib.graphics.layout.pdfbox.util.Enumerators.LowerCaseAlphabeticEnumerator; +import org.xbib.graphics.layout.pdfbox.util.Enumerators.LowerCaseRomanEnumerator; +import org.xbib.graphics.layout.pdfbox.util.Enumerators.RomanEnumerator; +import java.io.FileOutputStream; +import java.io.OutputStream; + +public class Indentation { + + @Test + public void test() throws Exception { + String bulletOdd = CompatibilityHelper.getBulletCharacter(1) + " "; + String bulletEven = CompatibilityHelper.getBulletCharacter(2) + " "; + String text1 = "Lorem ipsum dolor sit amet, consetetur sadipscing elitr, " + + "sed diam nonumy eirmod tempor invidunt ut labore et dolore magna " + + "aliquyam erat\n"; + + Document document = new Document(40, 60, 40, 60); + Paragraph paragraph = new Paragraph(); + paragraph + .addMarkup( + "This is an example for the new indent feature. Let's do some empty space indentation:\n", + 11, BaseFont.Times); + paragraph.add(new Indent(50, SpaceUnit.pt)); + paragraph.addMarkup("Here we go indented.\n", 11, BaseFont.Times); + paragraph.addMarkup( + "The Indentation holds for the rest of the paragraph, or... \n", + 11, BaseFont.Times); + paragraph.add(new Indent(70, SpaceUnit.pt)); + paragraph.addMarkup("any new indent comes.\n", 11, BaseFont.Times); + document.add(paragraph); + + paragraph = new Paragraph(); + paragraph + .addMarkup( + "New paragraph, now indentation is gone. But we can indent with a label also:\n", + 11, BaseFont.Times); + paragraph.add(new Indent("This is some label", 100, SpaceUnit.pt, 11, + PDType1Font.TIMES_BOLD)); + paragraph.addMarkup("Here we go indented.\n", 11, BaseFont.Times); + paragraph + .addMarkup( + "And again, the Indentation holds for the rest of the paragraph, or any new indent comes.\nLabels can be aligned:\n", + 11, BaseFont.Times); + paragraph.add(new Indent("Left", 100, SpaceUnit.pt, 11, + PDType1Font.TIMES_BOLD, Alignment.Left)); + paragraph.addMarkup("Indent with label aligned to the left.\n", 11, + BaseFont.Times); + paragraph.add(new Indent("Center", 100, SpaceUnit.pt, 11, + PDType1Font.TIMES_BOLD, Alignment.Center)); + paragraph.addMarkup("Indent with label aligned to the center.\n", 11, + BaseFont.Times); + paragraph.add(new Indent("Right", 100, SpaceUnit.pt, 11, + PDType1Font.TIMES_BOLD, Alignment.Right)); + paragraph.addMarkup("Indent with label aligned to the right.\n", 11, + BaseFont.Times); + document.add(paragraph); + + paragraph = new Paragraph(); + paragraph.addMarkup( + "So, what can you do with that? How about lists:\n", 11, + BaseFont.Times); + paragraph.add(new Indent(bulletOdd, 4, SpaceUnit.em, 11, + PDType1Font.TIMES_BOLD, Alignment.Right)); + paragraph.addMarkup("This is a list item\n", 11, BaseFont.Times); + paragraph.add(new Indent(bulletOdd, 4, SpaceUnit.em, 11, + PDType1Font.TIMES_BOLD, Alignment.Right)); + paragraph.addMarkup("Another list item\n", 11, BaseFont.Times); + paragraph.add(new Indent(bulletEven, 8, SpaceUnit.em, 11, + PDType1Font.TIMES_BOLD, Alignment.Right)); + paragraph.addMarkup("Sub list item\n", 11, BaseFont.Times); + paragraph.add(new Indent(bulletOdd, 4, SpaceUnit.em, 11, + PDType1Font.TIMES_BOLD, Alignment.Right)); + paragraph.addMarkup("And yet another one\n", 11, BaseFont.Times); + document.add(paragraph); + + paragraph = new Paragraph(); + paragraph.addMarkup("Also available with indents: Enumerators:\n", 11, + BaseFont.Times); + RomanEnumerator e1 = new RomanEnumerator(); + LowerCaseAlphabeticEnumerator e2 = new LowerCaseAlphabeticEnumerator(); + paragraph.add(new Indent(e1.next() + ". ", 4, SpaceUnit.em, 11, + PDType1Font.TIMES_BOLD, Alignment.Right)); + paragraph.addMarkup("First item\n", 11, BaseFont.Times); + paragraph.add(new Indent(e1.next() + ". ", 4, SpaceUnit.em, 11, + PDType1Font.TIMES_BOLD, Alignment.Right)); + paragraph.addMarkup("Second item\n", 11, BaseFont.Times); + paragraph.add(new Indent(e2.next() + ") ", 8, SpaceUnit.em, 11, + PDType1Font.TIMES_BOLD, Alignment.Right)); + paragraph.addMarkup("A sub item\n", 11, BaseFont.Times); + paragraph.add(new Indent(e2.next() + ") ", 8, SpaceUnit.em, 11, + PDType1Font.TIMES_BOLD, Alignment.Right)); + paragraph.addMarkup("Another sub item\n", 11, BaseFont.Times); + paragraph.add(new Indent(e1.next() + ". ", 4, SpaceUnit.em, 11, + PDType1Font.TIMES_BOLD, Alignment.Right)); + paragraph.addMarkup("Third item\n", 11, BaseFont.Times); + document.add(paragraph); + + paragraph = new Paragraph(); + paragraph.addMarkup("The following types are built in:\n", 11, + BaseFont.Times); + paragraph.add(new Indent(new ArabicEnumerator().next() + " ", 4, + SpaceUnit.em, 11, PDType1Font.TIMES_BOLD, Alignment.Right)); + paragraph.addMarkup("ArabicEnumerator\n", 11, BaseFont.Times); + paragraph.add(new Indent(new RomanEnumerator().next() + " ", 4, + SpaceUnit.em, 11, PDType1Font.TIMES_BOLD, Alignment.Right)); + paragraph.addMarkup("RomanEnumerator\n", 11, BaseFont.Times); + paragraph.add(new Indent(new LowerCaseRomanEnumerator().next() + " ", + 4, SpaceUnit.em, 11, PDType1Font.TIMES_BOLD, Alignment.Right)); + paragraph.addMarkup("LowerCaseRomanEnumerator\n", 11, BaseFont.Times); + paragraph.add(new Indent(new AlphabeticEnumerator().next() + " ", 4, + SpaceUnit.em, 11, PDType1Font.TIMES_BOLD, Alignment.Right)); + paragraph.addMarkup("AlphabeticEnumerator\n", 11, BaseFont.Times); + paragraph.add(new Indent(new LowerCaseAlphabeticEnumerator().next() + + " ", 4, SpaceUnit.em, 11, PDType1Font.TIMES_BOLD, + Alignment.Right)); + paragraph.addMarkup("LowerCaseAlphabeticEnumerator\n", 11, + BaseFont.Times); + document.add(paragraph); + + paragraph = new Paragraph(); + text1 = "For your convenience, you can do all that much easier with markup, e.g. simple indentation\n" + + "--At vero eos et accusam\n\n" + + "-!And end the indentation. Now a list:\n" + + "-+This is a list item\n" + + "-+Another list item\n" + + " -+A sub list item\n" + + "-+And yet another one\n\n" + + "-!Even enumeration is supported:\n" + + "-#This is a list item\n" + + "-#Another list item\n" + + " -#{a:}A sub list item\n" + + "-#And yet another one\n\n" + + "-!And you can customize it:\n" + + "-#{I ->:5}This is a list item\n" + + "-#{I ->:5}Another list item\n" + + " -#{a ~:30pt}A sub list item\n" + + "-#{I ->:5}And yet another one\n\n"; + paragraph.addMarkup(text1, 11, BaseFont.Times); + document.add(paragraph); + + final OutputStream outputStream = new FileOutputStream("build/indentation.pdf"); + document.save(outputStream); + } + +} diff --git a/layout-pdfbox/src/test/java/org/xbib/graphics/layout/pdfbox/Landscape.java b/layout-pdfbox/src/test/java/org/xbib/graphics/layout/pdfbox/Landscape.java new file mode 100644 index 0000000..d375f11 --- /dev/null +++ b/layout-pdfbox/src/test/java/org/xbib/graphics/layout/pdfbox/Landscape.java @@ -0,0 +1,111 @@ +package org.xbib.graphics.layout.pdfbox; + +import org.junit.jupiter.api.Test; +import org.xbib.graphics.layout.pdfbox.elements.ControlElement; +import org.xbib.graphics.layout.pdfbox.elements.Document; +import org.xbib.graphics.layout.pdfbox.elements.PageFormat; +import org.xbib.graphics.layout.pdfbox.elements.Paragraph; +import org.xbib.graphics.layout.pdfbox.elements.VerticalSpacer; +import org.xbib.graphics.layout.pdfbox.elements.render.ColumnLayout; +import org.xbib.graphics.layout.pdfbox.elements.render.VerticalLayout; +import org.xbib.graphics.layout.pdfbox.elements.render.VerticalLayoutHint; +import org.xbib.graphics.layout.pdfbox.text.BaseFont; +import java.io.FileOutputStream; +import java.io.OutputStream; + +public class Landscape { + + @Test + public void main() throws Exception { + String text1 = "Lorem ipsum dolor sit amet, consetetur sadipscing elitr, " + + "sed diam nonumy eirmod tempor invidunt ut labore et dolore magna " + + "aliquyam erat, _sed diam_ voluptua. At vero eos et *accusam et justo* " + + "duo dolores et ea rebum.\n\nStet clita kasd gubergren, no sea takimata " + + "sanctus est *Lorem ipsum _dolor* sit_ amet. Lorem ipsum dolor sit amet, " + + "consetetur sadipscing elitr, sed diam nonumy eirmod tempor invidunt " + + "ut labore et dolore magna aliquyam erat, *sed diam voluptua.\n\n" + + "At vero eos et accusam* et justo duo dolores et ea rebum. Stet clita kasd " + + "gubergren, no sea takimata sanctus est Lorem ipsum dolor sit amet.\n\n"; + + String text2 = "At *vero eos et accusam* et justo duo dolores et ea rebum. " + + "Stet clita kasd gubergren, no sea takimata\n\n" + + "sanctus est Lorem ipsum dolor sit amet. Lorem ipsum dolor sit amet, " + + "_consetetur sadipscing elitr_, sed diam nonumy eirmod tempor invidunt " + + "ut labore et dolore magna aliquyam erat, sed diam voluptua. At vero " + + "eos et _accusam et *justo* duo dolores_ et ea rebum. Stet clita kasd " + + "gubergren, no sea takimata sanctus est Lorem ipsum dolor sit amet.\n"; + + Paragraph paragraph1 = new Paragraph(); + paragraph1.addMarkup(text1, 11, BaseFont.Times); + Paragraph paragraph2 = new Paragraph(); + paragraph2.addMarkup(text2, 12, BaseFont.Helvetica); + Paragraph paragraph3 = new Paragraph(); + paragraph3.addMarkup(text1, 8, BaseFont.Courier); + + Paragraph titleA4 = new Paragraph(); + titleA4.addMarkup("*Format A4 in Portrait*", 20, BaseFont.Times); + Paragraph titleA5 = new Paragraph(); + titleA5.addMarkup("*Format A5 in Landscape*", 20, BaseFont.Times); + + PageFormat a5_landscape = PageFormat.with().A5().landscape().margins(10, 50, 0, 30).build(); + PageFormat a4_portrait = PageFormat.with().margins(40, 50, 40, 60).build(); + Document document = new Document(a4_portrait); + + document.add(titleA4, VerticalLayoutHint.CENTER); + document.add(new VerticalSpacer(5)); + document.add(new ColumnLayout(2, 10)); + + document.add(paragraph2); + document.add(paragraph1); + document.add(paragraph1); + document.add(paragraph3); + document.add(paragraph2); + document.add(paragraph2); + document.add(paragraph3); + document.add(paragraph2); + + document.add(a5_landscape); + document.add(ControlElement.NEWPAGE); + document.add(new VerticalLayout()); + document.add(titleA5, VerticalLayoutHint.CENTER); + document.add(new VerticalSpacer(5)); + document.add(new ColumnLayout(2, 10)); + + document.add(paragraph1); + document.add(paragraph3); + document.add(paragraph2); + document.add(paragraph3); + + document.add(a4_portrait); + document.add(ControlElement.NEWPAGE); + document.add(new VerticalLayout()); + document.add(titleA4, VerticalLayoutHint.CENTER); + document.add(new VerticalSpacer(5)); + document.add(new ColumnLayout(2, 10)); + + document.add(paragraph2); + document.add(paragraph1); + document.add(paragraph1); + document.add(paragraph3); + document.add(paragraph2); + document.add(paragraph2); + document.add(paragraph3); + document.add(paragraph2); + + document.add(a5_landscape); + document.add(ControlElement.NEWPAGE); + document.add(new VerticalLayout()); + document.add(titleA5, VerticalLayoutHint.CENTER); + document.add(new VerticalSpacer(5)); + document.add(new ColumnLayout(2, 10)); + + document.add(paragraph1); + document.add(paragraph3); + document.add(paragraph2); + document.add(paragraph3); + + final OutputStream outputStream = new FileOutputStream("build/landscape.pdf"); + document.save(outputStream); + + } +} diff --git a/layout-pdfbox/src/test/java/org/xbib/graphics/layout/pdfbox/Letter.java b/layout-pdfbox/src/test/java/org/xbib/graphics/layout/pdfbox/Letter.java new file mode 100644 index 0000000..fdc822b --- /dev/null +++ b/layout-pdfbox/src/test/java/org/xbib/graphics/layout/pdfbox/Letter.java @@ -0,0 +1,87 @@ +package org.xbib.graphics.layout.pdfbox; + +import org.apache.pdfbox.pdmodel.font.PDType1Font; +import org.junit.jupiter.api.Test; +import org.xbib.graphics.layout.pdfbox.elements.Document; +import org.xbib.graphics.layout.pdfbox.elements.ImageElement; +import org.xbib.graphics.layout.pdfbox.elements.Paragraph; +import org.xbib.graphics.layout.pdfbox.elements.VerticalSpacer; +import org.xbib.graphics.layout.pdfbox.elements.render.VerticalLayoutHint; +import org.xbib.graphics.layout.pdfbox.text.Alignment; +import org.xbib.graphics.layout.pdfbox.text.BaseFont; +import org.xbib.graphics.layout.pdfbox.text.Position; +import java.io.File; +import java.io.FileOutputStream; +import java.io.OutputStream; + +public class Letter { + + @Test + public void test() throws Exception { + float hMargin = 40; + float vMargin = 50; + Document document = new Document(hMargin, hMargin, vMargin, vMargin); + + ImageElement image; + if (new File("arrow.png").exists()) { + image = new ImageElement("arrow.png"); + } else { + image = new ImageElement(Letter.class.getResourceAsStream("arrow.png")); + } + image.setWidth(image.getWidth() / 7); + image.setHeight(image.getHeight() / 7); + document.add(image, new VerticalLayoutHint(Alignment.Right, 0, 0, + 0, 0, true)); + + document.add(new VerticalSpacer(100)); + + Paragraph paragraph = new Paragraph(); + paragraph.addText("Blubberhausen, 01.04.2016", 11, + PDType1Font.HELVETICA); + document.add(paragraph, new VerticalLayoutHint(Alignment.Right, 0, 0, + 0, 0, true)); + + paragraph = new Paragraph(); + String address = "Ralf Stuckert\nAm Hollergraben 24\n67346 Blubberhausen"; + paragraph.addText(address, 11, PDType1Font.HELVETICA); + document.add(paragraph); + + paragraph = new Paragraph(); + paragraph.addMarkup("*Labore et dolore magna aliquyam erat*", 11, + BaseFont.Helvetica); + document.add(paragraph, new VerticalLayoutHint(Alignment.Left, 0, 0, + 40, 20)); + + String text = "Lorem ipsum dolor sit amet, consetetur sadipscing elitr, " + + "sed diam nonumy eirmod tempor invidunt ut labore et dolore magna " + + "aliquyam erat, _sed diam_ voluptua. At vero eos et *accusam et justo* " + + "duo dolores et ea rebum.\n\nStet clita kasd gubergren, no sea takimata " + + "sanctus est *Lorem ipsum _dolor* sit_ amet. Lorem ipsum dolor sit amet, " + + "consetetur sadipscing elitr, sed diam nonumy eirmod tempor invidunt " + + "ut labore et dolore magna aliquyam erat, *sed diam voluptua.\n\n" + + "At vero eos et accusam* et justo duo dolores et ea rebum. Stet clita kasd " + + "gubergren, no sea takimata sanctus est Lorem ipsum dolor sit amet.\n\n"; + paragraph = new Paragraph(); + paragraph.addMarkup(text, 11, BaseFont.Helvetica); + document.add(paragraph); + + document.add(paragraph); + + paragraph = new Paragraph(); + paragraph.addMarkup("Dolore magna aliquyam erat\nRalf Stuckert", 11, + BaseFont.Helvetica); + document.add(paragraph, new VerticalLayoutHint(Alignment.Left, 60, 0, + 40, 0)); + + paragraph = new Paragraph(); + paragraph.addMarkup("*Sanctus est:* Lorem ipsum dolor consetetur " + + "sadipscing sed diam nonumy eirmod tempor invidunt", 6, + BaseFont.Times); + paragraph.setAbsolutePosition(new Position(hMargin, vMargin)); + document.add(paragraph); + + final OutputStream outputStream = new FileOutputStream("build/letter.pdf"); + document.save(outputStream); + + } +} diff --git a/layout-pdfbox/src/test/java/org/xbib/graphics/layout/pdfbox/LineSpacing.java b/layout-pdfbox/src/test/java/org/xbib/graphics/layout/pdfbox/LineSpacing.java new file mode 100644 index 0000000..e4cd354 --- /dev/null +++ b/layout-pdfbox/src/test/java/org/xbib/graphics/layout/pdfbox/LineSpacing.java @@ -0,0 +1,54 @@ +package org.xbib.graphics.layout.pdfbox; + +import org.junit.jupiter.api.Test; +import org.xbib.graphics.layout.pdfbox.elements.Document; +import org.xbib.graphics.layout.pdfbox.elements.Paragraph; +import org.xbib.graphics.layout.pdfbox.elements.render.ColumnLayout; +import org.xbib.graphics.layout.pdfbox.text.BaseFont; +import java.io.FileOutputStream; +import java.io.OutputStream; + +public class LineSpacing { + + @Test + public void test() throws Exception { + String text = "*Lorem ipsum* dolor sit amet, consetetur sadipscing elitr, " + + "sed diam nonumy eirmod tempor invidunt ut labore et dolore magna " + + "aliquyam erat, _sed diam_ voluptua. At vero eos et _accusam et justo_ " + + "duo dolores et ea rebum. Stet clita kasd gubergren, no sea takimata " + + "sanctus est _Lorem ipsum dolor sit_ amet. Lorem ipsum dolor sit amet, " + + "consetetur sadipscing elitr, sed diam nonumy eirmod tempor invidunt " + + "ut labore et dolore magna aliquyam erat, sed diam."; + + // create document without margins + Document document = new Document(); + document.add(new ColumnLayout(2, 5)); + + Paragraph left = new Paragraph(); + // no line spacing for the first line + left.setApplyLineSpacingToFirstLine(false); + // use a bigger line spacing to visualize the effects of line spacing more drastically + left.setLineSpacing(1.5f); + left.setMaxWidth(document.getPageWidth() / 2); + left.addMarkup(text, 11, BaseFont.Times); + document.add(left); + + document.add(left); + document.add(left); + + document.add(ColumnLayout.NEWCOLUMN); + + Paragraph right = new Paragraph(); + right.setLineSpacing(1.5f); + right.setMaxWidth(document.getPageWidth() / 2); + right.addMarkup(text, 11, BaseFont.Times); + document.add(right); + + document.add(right); + document.add(right); + + final OutputStream outputStream = new FileOutputStream("build/linespacing.pdf"); + document.save(outputStream); + + } +} diff --git a/layout-pdfbox/src/test/java/org/xbib/graphics/layout/pdfbox/Links.java b/layout-pdfbox/src/test/java/org/xbib/graphics/layout/pdfbox/Links.java new file mode 100644 index 0000000..071030c --- /dev/null +++ b/layout-pdfbox/src/test/java/org/xbib/graphics/layout/pdfbox/Links.java @@ -0,0 +1,73 @@ +package org.xbib.graphics.layout.pdfbox; + +import org.junit.jupiter.api.Test; +import org.xbib.graphics.layout.pdfbox.elements.Document; +import org.xbib.graphics.layout.pdfbox.elements.Paragraph; +import org.xbib.graphics.layout.pdfbox.text.BaseFont; +import java.io.FileOutputStream; +import java.io.OutputStream; + +public class Links { + + @Test + public void main() throws Exception { + String text1 = "Lorem ipsum dolor sit amet, consetetur sadipscing elitr, " + + "sed diam nonumy eirmod tempor invidunt ut labore et dolore magna " + + "aliquyam erat, _sed diam_ voluptua. At vero eos et *accusam et justo* " + + "duo dolores et ea rebum.\n\nStet clita kasd gubergren, no sea takimata " + + "sanctus est *Lorem ipsum _dolor* sit_ amet. Lorem ipsum dolor sit amet, " + + "consetetur sadipscing elitr, sed diam nonumy eirmod tempor invidunt " + + "ut labore et dolore magna aliquyam erat, *sed diam voluptua.\n\n" + + "At vero eos et accusam* et justo duo dolores et ea rebum. Stet clita kasd " + + "gubergren, no sea takimata sanctus est Lorem ipsum dolor sit amet.\n\n"; + + String text2 = "At *vero {link[#hello]}eos{link} et accusam* et justo duo dolores et ea rebum." + + "Stet clita kasd gubergren, no sea takimata\n\n" + + "sanctus est Lorem ipsum dolor sit amet. Lorem ipsum dolor sit amet, " + + "_consetetur sadipscing elitr_, sed diam nonumy eirmod tempor invidunt " + + "ut labore et dolore magna aliquyam erat, sed diam voluptua. At vero " + + "eos et _accusam et *justo* duo dolores_ et ea rebum. Stet clita kasd " + + "gubergren, no sea takimata sanctus est Lorem ipsum dolor sit amet.\n"; + + Document document = new Document(40, 60, 40, 60); + + + Paragraph paragraph0 = new Paragraph(); + paragraph0.addMarkup("This is a link to {link[https://github.com/ralfstuckert/pdfbox-layout]}PDFBox-Layout{link}.\n\n", 11, BaseFont.Times); + paragraph0.addMarkup("Now the same link with color instead of underline {color:#ff5000}{link:none[https://github.com/ralfstuckert/pdfbox-layout]}PDFBox-Layout{link}{color:#000000}.\n\n", 11, BaseFont.Times); + paragraph0.addMarkup("And here comes a link to an internal anchor name {color:#ff5000}{link[#hello]}hello{link}{color:#000000}.\n\n", 11, BaseFont.Times); + document.add(paragraph0); + + Paragraph paragraph1 = new Paragraph(); + paragraph1.addMarkup(text1, 11, BaseFont.Times); + document.add(paragraph1); + + Paragraph paragraph2 = new Paragraph(); + paragraph2.addMarkup(text2, 12, BaseFont.Helvetica); + document.add(paragraph2); + + Paragraph paragraph3 = new Paragraph(); + paragraph3.addMarkup(text1, 8, BaseFont.Courier); + document.add(paragraph3); + + Paragraph paragraph4 = new Paragraph(); + paragraph4.addMarkup("\n\n{anchor:hello}Here{anchor} comes the internal anchor named *hello*\n\n", 15, BaseFont.Courier); + + document.add(paragraph1); + document.add(paragraph3); + document.add(paragraph1); + document.add(paragraph2); + document.add(paragraph1); + document.add(paragraph3); + + document.add(paragraph4); + + document.add(paragraph2); + document.add(paragraph1); + document.add(paragraph1); + + final OutputStream outputStream = new FileOutputStream("build/links.pdf"); + document.save(outputStream); + + } +} diff --git a/layout-pdfbox/src/test/java/org/xbib/graphics/layout/pdfbox/Listener.java b/layout-pdfbox/src/test/java/org/xbib/graphics/layout/pdfbox/Listener.java new file mode 100644 index 0000000..718f859 --- /dev/null +++ b/layout-pdfbox/src/test/java/org/xbib/graphics/layout/pdfbox/Listener.java @@ -0,0 +1,83 @@ +package org.xbib.graphics.layout.pdfbox; + +import org.apache.pdfbox.pdmodel.font.PDType1Font; +import org.junit.jupiter.api.Test; +import org.xbib.graphics.layout.pdfbox.elements.Document; +import org.xbib.graphics.layout.pdfbox.elements.Paragraph; +import org.xbib.graphics.layout.pdfbox.elements.render.RenderContext; +import org.xbib.graphics.layout.pdfbox.elements.render.RenderListener; +import org.xbib.graphics.layout.pdfbox.text.Alignment; +import org.xbib.graphics.layout.pdfbox.text.BaseFont; +import org.xbib.graphics.layout.pdfbox.text.Position; +import org.xbib.graphics.layout.pdfbox.text.TextFlow; +import org.xbib.graphics.layout.pdfbox.text.TextFlowUtil; +import org.xbib.graphics.layout.pdfbox.text.TextSequenceUtil; +import java.io.FileOutputStream; +import java.io.IOException; +import java.io.OutputStream; + +public class Listener { + + @Test + public void test() throws Exception { + String text1 = "Lorem ipsum dolor sit amet, consetetur sadipscing elitr, " + + "sed diam nonumy eirmod tempor invidunt ut labore et dolore magna " + + "aliquyam erat, _sed diam_ voluptua. At vero eos et *accusam et justo* " + + "duo dolores et ea rebum.\n\nStet clita kasd gubergren, no sea takimata " + + "sanctus est *Lorem ipsum _dolor* sit_ amet. Lorem ipsum dolor sit amet, " + + "consetetur sadipscing elitr, sed diam nonumy eirmod tempor invidunt " + + "ut labore et dolore magna aliquyam erat, *sed diam voluptua.\n\n" + + "At vero eos et accusam* et justo duo dolores et ea rebum. Stet clita kasd " + + "gubergren, no sea takimata sanctus est Lorem ipsum dolor sit amet.\n\n"; + + String text2 = "At *vero eos et accusam* et justo duo dolores et ea rebum." + + "Stet clita kasd gubergren, no sea takimata\n\n" + + "sanctus est Lorem ipsum dolor sit amet. Lorem ipsum dolor sit amet, " + + "_consetetur sadipscing elitr_, sed diam nonumy eirmod tempor invidunt " + + "ut labore et dolore magna aliquyam erat, sed diam voluptua. At vero " + + "eos et _accusam et *justo* duo dolores_ et ea rebum. Stet clita kasd " + + "gubergren, no sea takimata sanctus est Lorem ipsum dolor sit amet.\n"; + + + Document document = new Document(40, 60, 40, 60); + document.addRenderListener(new RenderListener() { + + @Override + public void beforePage(RenderContext renderContext) { + + } + + @Override + public void afterPage(RenderContext renderContext) + throws IOException { + String content = String.format("Page %s", + renderContext.getPageIndex() + 1); + TextFlow text = TextFlowUtil.createTextFlow(content, 11, + PDType1Font.TIMES_ROMAN); + float offset = renderContext.getPageFormat().getMarginLeft() + + TextSequenceUtil.getOffset(text, + renderContext.getWidth(), Alignment.Right); + text.drawText(renderContext.getContentStream(), new Position( + offset, 30), Alignment.Right, null); + } + }); + + Paragraph paragraph = new Paragraph(); + paragraph.addMarkup(text1, 11, BaseFont.Times); + paragraph.addMarkup(text2, 12, BaseFont.Helvetica); + paragraph.addMarkup(text1, 8, BaseFont.Courier); + + document.add(paragraph); + document.add(paragraph); + document.add(paragraph); + document.add(paragraph); + document.add(paragraph); + document.add(paragraph); + document.add(paragraph); + document.add(paragraph); + + final OutputStream outputStream = new FileOutputStream("build/listener.pdf"); + document.save(outputStream); + + } +} diff --git a/layout-pdfbox/src/test/java/org/xbib/graphics/layout/pdfbox/LowLevelText.java b/layout-pdfbox/src/test/java/org/xbib/graphics/layout/pdfbox/LowLevelText.java new file mode 100644 index 0000000..57b2a5a --- /dev/null +++ b/layout-pdfbox/src/test/java/org/xbib/graphics/layout/pdfbox/LowLevelText.java @@ -0,0 +1,120 @@ +package org.xbib.graphics.layout.pdfbox; + +import org.apache.pdfbox.pdmodel.PDDocument; +import org.apache.pdfbox.pdmodel.PDPage; +import org.apache.pdfbox.pdmodel.PDPageContentStream; +import org.apache.pdfbox.pdmodel.font.PDType1Font; +import org.junit.jupiter.api.Test; +import org.xbib.graphics.layout.pdfbox.shape.RoundRect; +import org.xbib.graphics.layout.pdfbox.shape.Shape; +import org.xbib.graphics.layout.pdfbox.shape.Stroke; +import org.xbib.graphics.layout.pdfbox.text.Alignment; +import org.xbib.graphics.layout.pdfbox.text.BaseFont; +import org.xbib.graphics.layout.pdfbox.text.Constants; +import org.xbib.graphics.layout.pdfbox.text.DrawContext; +import org.xbib.graphics.layout.pdfbox.text.Position; +import org.xbib.graphics.layout.pdfbox.text.TextFlow; +import org.xbib.graphics.layout.pdfbox.text.TextFlowUtil; +import org.xbib.graphics.layout.pdfbox.text.TextSequenceUtil; +import org.xbib.graphics.layout.pdfbox.text.annotations.AnnotationDrawListener; +import java.awt.Color; +import java.io.FileOutputStream; +import java.io.OutputStream; + +public class LowLevelText { + + @Test + public void test() throws Exception { + + final PDDocument test = new PDDocument(); + final PDPage page = new PDPage(Constants.A4); + float pageWidth = page.getMediaBox().getWidth(); + float pageHeight = page.getMediaBox().getHeight(); + + test.addPage(page); + final PDPageContentStream contentStream = + new PDPageContentStream(test, page, PDPageContentStream.AppendMode.APPEND, true); + + // AnnotationDrawListener is only needed if you use annoation based stuff, e.g. hyperlinks + AnnotationDrawListener annotationDrawListener = new AnnotationDrawListener(new DrawContext() { + + @Override + public PDDocument getPdDocument() { + return test; + } + + @Override + public PDPage getCurrentPage() { + return page; + } + + @Override + public PDPageContentStream getCurrentPageContentStream() { + return contentStream; + } + + }); + annotationDrawListener.beforePage(null); + + TextFlow text = TextFlowUtil + .createTextFlowFromMarkup( + "Hello *bold _italic bold-end* italic-end_. Eirmod\ntempor invidunt ut \\*labore", + 11, BaseFont.Times); + + text.addText("Spongebob", 11, PDType1Font.COURIER); + text.addText(" is ", 20, PDType1Font.HELVETICA_BOLD_OBLIQUE); + text.addText("cool", 7, PDType1Font.HELVETICA); + + text.setMaxWidth(100); + float xOffset = TextSequenceUtil.getOffset(text, pageWidth, + Alignment.Right); + text.drawText(contentStream, new Position(xOffset, pageHeight - 50), + Alignment.Right, annotationDrawListener); + + String textBlock = "Lorem ipsum dolor sit amet, consetetur sadipscing elitr, " + + "{link[https://github.com/ralfstuckert/pdfbox-layout/]}pdfbox layout{link} " + + "sed diam nonumy eirmod invidunt ut labore et dolore magna " + + "aliquyam erat, _sed diam_ voluptua. At vero eos et *accusam et justo* " + + "duo dolores et ea rebum.\n\nStet clita kasd gubergren, no sea takimata " + + "sanctus est *Lorem ipsum _dolor* sit_ amet. Lorem ipsum dolor sit amet, " + + "consetetur sadipscing elitr, sed diam nonumy eirmod tempor invidunt " + + "ut labore et dolore magna aliquyam erat, *sed diam voluptua.\n\n" + + "At vero eos et accusam* et justo duo dolores et ea rebum. Stet clita kasd " + + "gubergren, no sea takimata sanctus est Lorem ipsum dolor sit amet.\n"; + + text = new TextFlow(); + text.addMarkup(textBlock, 8, BaseFont.Courier); + text.setMaxWidth(200); + xOffset = TextSequenceUtil.getOffset(text, pageWidth, Alignment.Center); + text.drawText(contentStream, new Position(xOffset, pageHeight - 100), + Alignment.Justify, annotationDrawListener); + + // draw a round rect box with text + text.setMaxWidth(350); + float x = 50; + float y = pageHeight - 300; + float paddingX = 20; + float paddingY = 15; + float boxWidth = text.getWidth() + 2 * paddingX; + float boxHeight = text.getHeight() + 2 * paddingY; + + Shape shape = new RoundRect(20); + shape.fill(test, contentStream, new Position(x, y), boxWidth, + boxHeight, Color.pink, null); + shape.draw(test, contentStream, new Position(x, y), boxWidth, + boxHeight, Color.blue, new Stroke(3), null); + // now the text + text.drawText(contentStream, new Position(x + paddingX, y - paddingY), + Alignment.Center, annotationDrawListener); + + annotationDrawListener.afterPage(null); + contentStream.close(); + + annotationDrawListener.afterRender(); + + final OutputStream outputStream = new FileOutputStream("build/lowleveltext.pdf"); + test.save(outputStream); + test.close(); + + } +} diff --git a/layout-pdfbox/src/test/java/org/xbib/graphics/layout/pdfbox/Margin.java b/layout-pdfbox/src/test/java/org/xbib/graphics/layout/pdfbox/Margin.java new file mode 100644 index 0000000..6705234 --- /dev/null +++ b/layout-pdfbox/src/test/java/org/xbib/graphics/layout/pdfbox/Margin.java @@ -0,0 +1,58 @@ +package org.xbib.graphics.layout.pdfbox; + +import org.apache.pdfbox.pdmodel.font.PDType1Font; +import org.junit.jupiter.api.Test; +import org.xbib.graphics.layout.pdfbox.elements.Document; +import org.xbib.graphics.layout.pdfbox.elements.Paragraph; +import org.xbib.graphics.layout.pdfbox.elements.render.VerticalLayoutHint; +import org.xbib.graphics.layout.pdfbox.text.Alignment; +import java.io.FileOutputStream; +import java.io.OutputStream; + +public class Margin { + + @Test + public void test() throws Exception { + String text1 = "Lorem ipsum dolor sit amet, consetetur sadipscing elitr, " + + "sed diam nonumy eirmod tempor invidunt ut labore et dolore magna " + + "aliquyam erat, sed diam voluptua. At vero eos et accusam et justo " + + "duo dolores et ea rebum. Stet clita kasd gubergren, no sea takimata " + + "sanctus est Lorem ipsum dolor sit amet. Lorem ipsum dolor sit amet, " + + "consetetur sadipscing elitr, sed diam nonumy eirmod tempor invidunt " + + "ut labore et dolore magna aliquyam erat, sed diam voluptua. At vero " + + "eos et accusam et justo duo dolores et ea rebum. Stet clita kasd " + + "gubergren, no sea takimata sanctus est Lorem ipsum dolor sit amet."; + + String text2 = "short text, right aligned with some margin"; + + String text3 = "Lorem ipsum dolor sit amet, consetetur sadipscing elitr, " + + "sed diam nonumy eirmod tempor invidunt ut labore et dolore magna " + + "aliquyam erat, sed diam voluptua. At vero eos et accusam et justo " + + "duo dolores et ea rebum. Stet clita kasd gubergren, no sea takimata " + + "sanctus est Lorem ipsum dolor sit amet. Lorem ipsum dolor sit amet, " + + "consetetur sadipscing elitr, sed diam nonumy eirmod tempor invidunt " + + "ut labore et dolore magna aliquyam erat, sed diam voluptua. At vero " + + "eos et accusam et justo duo dolores et ea rebum. Stet clita kasd " + + "gubergren, no sea takimata sanctus est Lorem ipsum dolor sit amet."; + + Document document = new Document(40, 60, 40, 60); + Paragraph paragraph = new Paragraph(); + paragraph.addText(text1, 11, PDType1Font.HELVETICA); + document.add(paragraph, new VerticalLayoutHint(Alignment.Left, 0, 100, + 100, 100)); + + paragraph = new Paragraph(); + paragraph.addText(text2, 11, PDType1Font.HELVETICA); + document.add(paragraph, new VerticalLayoutHint(Alignment.Right, 0, 50, + 0, 0)); + + paragraph = new Paragraph(); + paragraph.addText(text3, 11, PDType1Font.HELVETICA); + document.add(paragraph, new VerticalLayoutHint(Alignment.Right, 150, + 150, 20, 0)); + + final OutputStream outputStream = new FileOutputStream("build/margin.pdf"); + document.save(outputStream); + + } +} diff --git a/layout-pdfbox/src/test/java/org/xbib/graphics/layout/pdfbox/Markup.java b/layout-pdfbox/src/test/java/org/xbib/graphics/layout/pdfbox/Markup.java new file mode 100644 index 0000000..c55ddd2 --- /dev/null +++ b/layout-pdfbox/src/test/java/org/xbib/graphics/layout/pdfbox/Markup.java @@ -0,0 +1,81 @@ +package org.xbib.graphics.layout.pdfbox; + +import org.junit.jupiter.api.Test; +import org.xbib.graphics.layout.pdfbox.elements.Document; +import org.xbib.graphics.layout.pdfbox.elements.Paragraph; +import org.xbib.graphics.layout.pdfbox.elements.render.VerticalLayoutHint; +import org.xbib.graphics.layout.pdfbox.text.Alignment; +import org.xbib.graphics.layout.pdfbox.text.BaseFont; +import java.io.FileOutputStream; +import java.io.OutputStream; + +public class Markup { + + @Test + public void test() throws Exception { + String text1 = "Lorem ipsum dolor sit amet, consetetur sadipscing elitr, " + + "sed diam nonumy eirmod tempor invidunt ut labore et dolore magna " + + "aliquyam erat, _sed diam_ voluptua. At vero eos et *accusam et justo* " + + "duo dolores et ea rebum.\nStet clita kasd gubergren, no sea takimata " + + "sanctus est *Lorem ipsum _dolor* sit_ amet. Lorem ipsum dolor sit amet, " + + "consetetur sadipscing elitr, sed diam nonumy eirmod tempor invidunt " + + "ut labore et dolore magna aliquyam erat, *sed diam voluptua.\n" + + "At vero eos et accusam* et justo duo dolores et ea rebum. Stet clita kasd " + + "gubergren, no sea takimata sanctus est Lorem ipsum dolor sit amet.\n\n"; + + Document document = new Document(40, 60, 40, 60); + Paragraph paragraph = new Paragraph(); + paragraph.addMarkup(text1, 11, BaseFont.Times); + document.add(paragraph); + + paragraph = new Paragraph(); + paragraph + .addMarkup( + "Markup supports *bold*, _italic_, and *even _mixed* markup_.\n\n", + 11, BaseFont.Times); + paragraph.addMarkup( + "Escape \\* with \\\\\\* and \\_ with \\\\\\_ in markup.\n\n", + 11, BaseFont.Times); + + paragraph + .addMarkup( + "And now also {color:#ff0000}c{color:#00ff00}o{color:#0000ff}l{color:#00cccc}o{color:#cc00cc}r{color:#000000}", + 11, BaseFont.Times); + paragraph.addMarkup(" , {_}subscript{_} and {^}superscript{^}.\n\n", + 11, BaseFont.Times); + + paragraph + .addMarkup( + "You can alternate the position and thickness of an __underline__, " + + "so you may also use this to __{0.25:}strike through__ or blacken __{0.25:20}things__ out\n\n", + 11, BaseFont.Times); + + document.add(paragraph, new VerticalLayoutHint(Alignment.Left, 0, 0, + 30, 0)); + + paragraph = new Paragraph(); + text1 = "\nAlso, you can do all that indentation stuff much easier with markup, e.g. simple indentation\n" + + "--At vero eos et accusam\n\n" + + "-!And end the indentation. Now a list:\n" + + "-+This is a list item\n" + + "-+Another list item\n" + + " -+A sub list item\n" + + "-+And yet another one\n\n" + + "-!Even enumeration is supported:\n" + + "-#This is a list item\n" + + "-#Another list item\n" + + " -#{a:}A sub list item\n" + + "-#And yet another one\n\n" + + "-!And you can customize it:\n" + + "-#{I ->:5}This is a list item\n" + + "-#{I ->:5}Another list item\n" + + " -#{a ~:30pt}A sub list item\n" + + "-#{I ->:5}And yet another one\n\n"; + paragraph.addMarkup(text1, 11, BaseFont.Times); + document.add(paragraph); + + final OutputStream outputStream = new FileOutputStream("build/markup.pdf"); + document.save(outputStream); + + } +} diff --git a/layout-pdfbox/src/test/java/org/xbib/graphics/layout/pdfbox/MultiplePages.java b/layout-pdfbox/src/test/java/org/xbib/graphics/layout/pdfbox/MultiplePages.java new file mode 100644 index 0000000..fe2c26a --- /dev/null +++ b/layout-pdfbox/src/test/java/org/xbib/graphics/layout/pdfbox/MultiplePages.java @@ -0,0 +1,76 @@ +package org.xbib.graphics.layout.pdfbox; + +import org.junit.jupiter.api.Test; +import org.xbib.graphics.layout.pdfbox.elements.Document; +import org.xbib.graphics.layout.pdfbox.elements.Paragraph; +import org.xbib.graphics.layout.pdfbox.text.BaseFont; +import java.io.FileOutputStream; +import java.io.OutputStream; + +public class MultiplePages { + + @Test + public void test() throws Exception { + String text1 = "Lorem ipsum dolor sit amet, consetetur sadipscing elitr, " + + "sed diam nonumy eirmod tempor invidunt ut labore et dolore magna " + + "aliquyam erat, _sed diam_ voluptua. At vero eos et *accusam et justo* " + + "duo dolores et ea rebum.\n\nStet clita kasd gubergren, no sea takimata " + + "sanctus est *Lorem ipsum _dolor* sit_ amet. Lorem ipsum dolor sit amet, " + + "consetetur sadipscing elitr, sed diam nonumy eirmod tempor invidunt " + + "ut labore et dolore magna aliquyam erat, *sed diam voluptua.\n\n" + + "At vero eos et accusam* et justo duo dolores et ea rebum. Stet clita kasd " + + "gubergren, no sea takimata sanctus est Lorem ipsum dolor sit amet.\n\n"; + + String text2 = "At *vero eos et accusam* et justo duo dolores et ea rebum." + + "Stet clita kasd gubergren, no sea takimata\n\n" + + "sanctus est Lorem ipsum dolor sit amet. Lorem ipsum dolor sit amet, " + + "_consetetur sadipscing elitr_, sed diam nonumy eirmod tempor invidunt " + + "ut labore et dolore magna aliquyam erat, sed diam voluptua. At vero " + + "eos et _accusam et *justo* duo dolores_ et ea rebum. Stet clita kasd " + + "gubergren, no sea takimata sanctus est Lorem ipsum dolor sit amet.\n"; + + Document document = new Document(40, 60, 40, 60); + + Paragraph paragraph1 = new Paragraph(); + paragraph1.addMarkup(text1, 11, BaseFont.Times); + document.add(paragraph1); + + Paragraph paragraph2 = new Paragraph(); + paragraph2.addMarkup(text2, 12, BaseFont.Helvetica); + document.add(paragraph2); + + Paragraph paragraph3 = new Paragraph(); + paragraph3.addMarkup(text1, 8, BaseFont.Courier); + document.add(paragraph3); + + document.add(paragraph1); + document.add(paragraph3); + document.add(paragraph1); + document.add(paragraph2); + document.add(paragraph1); + document.add(paragraph3); + document.add(paragraph2); + document.add(paragraph1); + document.add(paragraph1); + document.add(paragraph3); + document.add(paragraph2); + document.add(paragraph2); + document.add(paragraph3); + document.add(paragraph1); + document.add(paragraph1); + document.add(paragraph2); + document.add(paragraph1); + document.add(paragraph3); + document.add(paragraph2); + document.add(paragraph3); + document.add(paragraph1); + document.add(paragraph1); + document.add(paragraph3); + document.add(paragraph2); + document.add(paragraph2); + + final OutputStream outputStream = new FileOutputStream("build/multiplepages.pdf"); + document.save(outputStream); + + } +} diff --git a/layout-pdfbox/src/test/java/org/xbib/graphics/layout/pdfbox/Rotation.java b/layout-pdfbox/src/test/java/org/xbib/graphics/layout/pdfbox/Rotation.java new file mode 100644 index 0000000..3ca6f7d --- /dev/null +++ b/layout-pdfbox/src/test/java/org/xbib/graphics/layout/pdfbox/Rotation.java @@ -0,0 +1,116 @@ +package org.xbib.graphics.layout.pdfbox; + +import org.junit.jupiter.api.Test; +import org.xbib.graphics.layout.pdfbox.elements.ControlElement; +import org.xbib.graphics.layout.pdfbox.elements.Document; +import org.xbib.graphics.layout.pdfbox.elements.PageFormat; +import org.xbib.graphics.layout.pdfbox.elements.Paragraph; +import org.xbib.graphics.layout.pdfbox.elements.VerticalSpacer; +import org.xbib.graphics.layout.pdfbox.elements.render.ColumnLayout; +import org.xbib.graphics.layout.pdfbox.elements.render.VerticalLayout; +import org.xbib.graphics.layout.pdfbox.elements.render.VerticalLayoutHint; +import org.xbib.graphics.layout.pdfbox.text.BaseFont; +import java.io.FileOutputStream; +import java.io.OutputStream; + +public class Rotation { + + @Test + public void test() throws Exception { + String text1 = "Lorem ipsum dolor sit amet, consetetur sadipscing elitr, " + + "sed diam nonumy eirmod tempor invidunt ut labore et dolore magna " + + "aliquyam erat, _sed diam_ voluptua. At vero eos et *accusam et justo* " + + "duo dolores et ea rebum.\n\nStet clita kasd gubergren, no sea takimata " + + "sanctus est *Lorem ipsum _dolor* sit_ amet. Lorem ipsum dolor sit amet, " + + "consetetur sadipscing elitr, sed diam nonumy eirmod tempor invidunt " + + "ut labore et dolore magna aliquyam erat, *sed diam voluptua.\n\n" + + "At vero eos et accusam* et justo duo dolores et ea rebum. Stet clita kasd " + + "gubergren, no sea takimata sanctus est Lorem ipsum dolor sit amet.\n\n"; + + String text2 = "At *vero eos et accusam* et justo duo dolores et ea rebum. " + + "Stet clita kasd gubergren, no sea takimata\n\n" + + "sanctus est Lorem ipsum dolor sit amet. Lorem ipsum dolor sit amet, " + + "_consetetur sadipscing elitr_, sed diam nonumy eirmod tempor invidunt " + + "ut labore et dolore magna aliquyam erat, sed diam voluptua. At vero " + + "eos et _accusam et *justo* duo dolores_ et ea rebum. Stet clita kasd " + + "gubergren, no sea takimata sanctus est Lorem ipsum dolor sit amet.\n"; + + Paragraph paragraph1 = new Paragraph(); + paragraph1.addMarkup(text1, 11, BaseFont.Times); + Paragraph paragraph2 = new Paragraph(); + paragraph2.addMarkup(text2, 12, BaseFont.Helvetica); + Paragraph paragraph3 = new Paragraph(); + paragraph3.addMarkup(text1, 8, BaseFont.Courier); + + Paragraph titleA4 = new Paragraph(); + titleA4.addMarkup("*Format A4 Landscape*", 20, BaseFont.Times); + Paragraph titleA5 = new Paragraph(); + titleA5.addMarkup("*Format A4 Landscape rotated by -90 degrees*", 20, BaseFont.Times); + + PageFormat a4_landscape = PageFormat.with().margins(40, 50, 40, 60).landscape().build(); + PageFormat a4_landscape_rotated = PageFormat.with().margins(40, 50, 40, 60).landscape().rotation(-90).build(); + + Document document = new Document(a4_landscape); + + document.add(titleA4, VerticalLayoutHint.CENTER); + document.add(new VerticalSpacer(5)); + document.add(new ColumnLayout(2, 10)); + + document.add(paragraph2); + document.add(paragraph1); + document.add(paragraph1); + document.add(paragraph3); + document.add(paragraph2); + document.add(paragraph2); + document.add(paragraph3); + + document.add(a4_landscape_rotated); + document.add(ControlElement.NEWPAGE); + document.add(new VerticalLayout()); + document.add(titleA5, VerticalLayoutHint.CENTER); + document.add(new VerticalSpacer(5)); + document.add(new ColumnLayout(2, 10)); + + document.add(paragraph2); + document.add(paragraph1); + document.add(paragraph1); + document.add(paragraph3); + document.add(paragraph2); + document.add(paragraph2); + document.add(paragraph3); + + document.add(a4_landscape); + document.add(ControlElement.NEWPAGE); + document.add(new VerticalLayout()); + document.add(titleA4, VerticalLayoutHint.CENTER); + document.add(new VerticalSpacer(5)); + document.add(new ColumnLayout(2, 10)); + + document.add(paragraph2); + document.add(paragraph1); + document.add(paragraph1); + document.add(paragraph3); + document.add(paragraph2); + document.add(paragraph2); + document.add(paragraph3); + + document.add(a4_landscape_rotated); + document.add(ControlElement.NEWPAGE); + document.add(new VerticalLayout()); + document.add(titleA5, VerticalLayoutHint.CENTER); + document.add(new VerticalSpacer(5)); + document.add(new ColumnLayout(2, 10)); + + document.add(paragraph2); + document.add(paragraph1); + document.add(paragraph1); + document.add(paragraph3); + document.add(paragraph2); + document.add(paragraph2); + document.add(paragraph3); + + final OutputStream outputStream = new FileOutputStream("build/rotation.pdf"); + document.save(outputStream); + + } +} diff --git a/layout-pdfbox/src/test/resources/org/xbib/graphics/layout/pdfbox/aligned.pdf b/layout-pdfbox/src/test/resources/org/xbib/graphics/layout/pdfbox/aligned.pdf new file mode 100644 index 0000000000000000000000000000000000000000..0c10685259f1786d0c54de98bc65872680249106 GIT binary patch literal 1251 zcmY!laBZ^4=fsl4ocwey{jk)c;>`R! z1$~fe{eZ;u)M5oApzurM*yv#_wV02&yOSdcMJsD-v1J{QWLoH^FqywsEl`v&gZ>fzsd95 z*15U;-=A~KICWTdF5*1O!Lv<{_x<9T$5|p3y(c@Vow^!g5`Fa1y)zC6Z1jtN&Ek7$ zSUXQBt%JR|HHX(*XEk4F^t^YMm%skeQn}0b!=Kxme8n&4{V$ohT>hof{yjA_FP)8^ zQ8}?;qlNkVgY)X{o!91>dg!1+*fPDxdHZbF8MJV*F>jnyA~}2a-){!>@dr0dzZMX! z{6+7X#eXa9r!GI;rBfCzvNC0{d8_bK!8pbA5d+)h6QW}$KCl?wlnyhWa z?(FWeZ{EIZ&W7se4_vBncoOyDyzk4a8^a!U=yLtveRBGX^}Af#dqw2hOCqqCEnp^2-rv#FDrn}MT?rK_2R zxuuzziIJ0ug@K)djf;t)i-og`rG=Y;fs>JeiL0fBfq|u^g{!lhg^7{5s~s?p24_~K b0)q#buZv3(i%P)ZWoc~0rK;-c@5TiHv$3f6 literal 0 HcmV?d00001 diff --git a/layout-pdfbox/src/test/resources/org/xbib/graphics/layout/pdfbox/arrow.png b/layout-pdfbox/src/test/resources/org/xbib/graphics/layout/pdfbox/arrow.png new file mode 100644 index 0000000000000000000000000000000000000000..ef60fc384bdb118d9f97a0b471229fc71cb789f3 GIT binary patch literal 13133 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 VefGWsy#fQ6Ua&l0cGmsw{{m&aZRr32 literal 0 HcmV?d00001 diff --git a/layout-pdfbox/src/test/resources/org/xbib/graphics/layout/pdfbox/columns.pdf b/layout-pdfbox/src/test/resources/org/xbib/graphics/layout/pdfbox/columns.pdf new file mode 100644 index 0000000000000000000000000000000000000000..b74725e488c8eb1654dfe968f939f8f759ea8b77 GIT binary patch literal 11018 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 literal 0 HcmV?d00001 diff --git a/layout-pdfbox/src/test/resources/org/xbib/graphics/layout/pdfbox/customannotation.pdf b/layout-pdfbox/src/test/resources/org/xbib/graphics/layout/pdfbox/customannotation.pdf new file mode 100644 index 0000000..4f554c6 --- /dev/null +++ b/layout-pdfbox/src/test/resources/org/xbib/graphics/layout/pdfbox/customannotation.pdf @@ -0,0 +1,116 @@ +%PDF-1.4 +%öäüß +1 0 obj +<< +/Type /Catalog +/Version /1.4 +/Pages 2 0 R +>> +endobj +2 0 obj +<< +/Type /Pages +/Kids [3 0 R] +/Count 1 +>> +endobj +3 0 obj +<< +/Type /Page +/MediaBox [0.0 0.0 595.27563 841.8898] +/Parent 2 0 R +/Contents 4 0 R +/Resources 5 0 R +/Annots [6 0 R 7 0 R 8 0 R 9 0 R] +>> +endobj +4 0 obj +<< +/Filter [/FlateDecode] +/Length 10 0 R +>> +stream +xœÅRÑJÃ0}¿_q7xÛ&¹é«LÙ«ðy`l»Õ•¦™Î¿·í²¢8Ü@EBȹ¹‡œÎmPª\‰”•Î$k¥‘e"ŒÉ™Iô` -ÜXHú• $ä<L=‹M–¢}†ë»¾ChŸ€F–/`¶tuÝÌÑ®a†¡tÞ]ÅbÀV]‡£¬Š²îwpãÅ­…û¯â†Î‹·‡7ETX½¼–«à^œå[³;6VÛðÁ¢øÆ‚6ç-Ñ_m¦owí®*Šzª]399-¤ôg¡‘5ð~ž”ÿ^vÁäüqxéóó+á‘9Þ;eÑò +endstream +endobj +5 0 obj +<< +/Font 11 0 R +>> +endobj +6 0 obj +<< +/Type /Annot +/Subtype /Highlight +/QuadPoints [126.7 800.8898 175.06 800.8898 126.7 789.8898 175.06 789.8898] +/Rect [126.7 789.8898 175.06 800.8898] +/C [0.0 1.0 0.0] +>> +endobj +7 0 obj +<< +/Type /Annot +/Subtype /Highlight +/QuadPoints [40.0 788.8898 56.120003 788.8898 40.0 777.8898 56.120003 777.8898] +/Rect [40.0 777.8898 56.120003 788.8898] +/C [0.0 1.0 0.0] +>> +endobj +8 0 obj +<< +/Type /Annot +/Subtype /Highlight +/QuadPoints [126.7 752.8898 175.06 752.8898 126.7 741.8898 175.06 741.8898] +/Rect [126.7 741.8898 175.06 752.8898] +/C [1.0 1.0 0.0] +>> +endobj +9 0 obj +<< +/Type /Annot +/Subtype /Highlight +/QuadPoints [40.0 740.8898 56.120003 740.8898 40.0 729.8898 56.120003 729.8898] +/Rect [40.0 729.8898 56.120003 740.8898] +/C [1.0 1.0 0.0] +>> +endobj +10 0 obj +239 +endobj +11 0 obj +<< +/F0 12 0 R +>> +endobj +12 0 obj +<< +/Type /Font +/Subtype /Type1 +/BaseFont /Helvetica +/Encoding /WinAnsiEncoding +>> +endobj +xref +0 13 +0000000000 65535 f +0000000015 00000 n +0000000078 00000 n +0000000135 00000 n +0000000288 00000 n +0000000606 00000 n +0000000640 00000 n +0000000826 00000 n +0000001018 00000 n +0000001204 00000 n +0000001396 00000 n +0000001416 00000 n +0000001449 00000 n +trailer +<< +/Root 1 0 R +/ID [ ] +/Size 13 +>> +startxref +1547 +%%EOF diff --git a/layout-pdfbox/src/test/resources/org/xbib/graphics/layout/pdfbox/customrenderer.pdf b/layout-pdfbox/src/test/resources/org/xbib/graphics/layout/pdfbox/customrenderer.pdf new file mode 100644 index 0000000000000000000000000000000000000000..3931e1abcb3f40090a492387fa498fe4754b78a7 GIT binary patch literal 10178 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 AU;qFB literal 0 HcmV?d00001 diff --git a/layout-pdfbox/src/test/resources/org/xbib/graphics/layout/pdfbox/frames.pdf b/layout-pdfbox/src/test/resources/org/xbib/graphics/layout/pdfbox/frames.pdf new file mode 100644 index 0000000000000000000000000000000000000000..887b1f8d130dbcf5aa12c2cb6fb4b4cc0658b8a9 GIT binary patch literal 4013 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 B94G() literal 0 HcmV?d00001 diff --git a/layout-pdfbox/src/test/resources/org/xbib/graphics/layout/pdfbox/hellodoc.pdf b/layout-pdfbox/src/test/resources/org/xbib/graphics/layout/pdfbox/hellodoc.pdf new file mode 100644 index 0000000..ed9115e --- /dev/null +++ b/layout-pdfbox/src/test/resources/org/xbib/graphics/layout/pdfbox/hellodoc.pdf @@ -0,0 +1,76 @@ +%PDF-1.4 +%öäüß +1 0 obj +<< +/Type /Catalog +/Version /1.4 +/Pages 2 0 R +>> +endobj +2 0 obj +<< +/Type /Pages +/Kids [3 0 R] +/Count 1 +>> +endobj +3 0 obj +<< +/Type /Page +/MediaBox [0.0 0.0 595.27563 841.8898] +/Parent 2 0 R +/Contents 4 0 R +/Resources 5 0 R +>> +endobj +4 0 obj +<< +/Filter [/FlateDecode] +/Length 6 0 R +>> +stream +xœÊM +Â0Eáù]ÅÖI|Ióò’©¨8.@¢ ý¡E÷o)ßèÀY LB-ê‚iê£%M´è]ÎÅLT,smx`‚S…§l<£Ðrï4Ëê5ÖÇ«0ë ²oëÝ­ Ã|`ý ãy~þÆ6}÷¼TÜ7훿 +endstream +endobj +5 0 obj +<< +/Font 7 0 R +>> +endobj +6 0 obj +118 +endobj +7 0 obj +<< +/F0 8 0 R +>> +endobj +8 0 obj +<< +/Type /Font +/Subtype /Type1 +/BaseFont /Helvetica +/Encoding /WinAnsiEncoding +>> +endobj +xref +0 9 +0000000000 65535 f +0000000015 00000 n +0000000078 00000 n +0000000135 00000 n +0000000254 00000 n +0000000450 00000 n +0000000483 00000 n +0000000502 00000 n +0000000533 00000 n +trailer +<< +/Root 1 0 R +/ID [ ] +/Size 9 +>> +startxref +630 +%%EOF diff --git a/layout-pdfbox/src/test/resources/org/xbib/graphics/layout/pdfbox/indentation.pdf b/layout-pdfbox/src/test/resources/org/xbib/graphics/layout/pdfbox/indentation.pdf new file mode 100644 index 0000000000000000000000000000000000000000..f39f9b24dcfbd7b744be854f69bb3573689f3838 GIT binary patch literal 2668 zcmb7Gdo+~W8%`vqQIV25>XS<~nEB>vrbNcfgmP&pNy-dgnqnq1v^@TDKtGa5(m>7 z|1jMSLjxedVIGAU7{E}rzQ-W|Wy0Vy+&Hc<)Lw|oV{zC3O2S52Gh87afDzN_FhfHa z#CDO$B=aBTb37PoC(DHgIAA4oM;OY4EvQH@7_$9pZ63xz6JK;^%{ z=MjOSLcO!pjze%Lp5+3MsjyC~#C$4$tF!0WHFZ^lwrS7xcH8K7oIgCvA8{K>@Pk?h zs%$>#J)Dl>j$ew`$*k$|RI3?mF1e4qqaL|@&5_eySEM?AOZUz7^J$={r{$q(@(iS= z263E;oy$s!XZU zilOmE_m)A+%y6^=;Q6dn8eVfUC+u~l?XA=zzN_*78kh4nd6(NF681C5w#$xt8#$Y< zsEF)zf-5|~zjDu)BuCE^og0exH|!1@e+H#9>|5%a9x0ndEJWwpp4JDAHokn*$8ePO zKbT`6qjpEOIUyGIxg(4JDDe8uh+}G-tF(qZ$55ATK6fFI(sc$z!qU3Y4UDfVDk9{@HRc9Fuo({60g~*s7O99;GHlz(&T1}r6aJ>($VT| zS@?xf8H*tpY5v>uCa1g zXxdkwFYjG3nklxh-k5tMm31ELoU$hKyE0dwF(s#0?dR-j9 zud2EJ?bL(Lt)fY!y}vPBp<6H*#w?+NJj~ye&OSbG<--<2j8fa%OtK< zK*nS^EizxD-8!q_C07(qv$a(}Rinm~msxb#Q3bho^D_3BDeS}5j~6uY)N`H4rGuXStkMcKC* zow^YrZ4KJtc%ItOJ9t>?k=Ex+gS_LNH|>H7%w|SHjXr!h-53`UY?oe9o$q(EY_%c{ zGdqBu8WC!lS+17fUwP2r4Im$Pqn2C{XS1X?9k)?bcg9+SW)Y9o)EcX8THd+8mpC=C z5LYRZ4>fIKwlC=JJ0oWkEw?D7(CX=K?P}%&){OdM=PI8S*^2txV-Y$5fqgeG+mybk zXt|z*HxkZBXPg=

      ?v|H_Mp!NQno1_Z+cP0s8aS@~4SPtVTjW|;CV$S_B_JAxA~Sf9WCcLkvfLq_9-ZUP zV8c*Uw%7q#Y*zqf$6_0^d8~QhZ?-MT{@+rVLT+9VpXJQ>cM0YW_1`jlI|P~I=JM}S z_;8`aFc3haVc_=x5b$^`9yqM<9Yc$+O)>!XUzonQ!areXvFc|GgBQ0MKl0EdP<+cj zV_0!Hf5!0tz%U?M+@<}jeCf+d(>$%cRd1&5{(O(-NH4Ft&;5Jx2uL6AfuQcY+? l97dmNC~lQ(SbmVCj^h4`$7gW)bCtzlK>|!mi)uxK{U4K-GEV>i literal 0 HcmV?d00001 diff --git a/layout-pdfbox/src/test/resources/org/xbib/graphics/layout/pdfbox/landscape.pdf b/layout-pdfbox/src/test/resources/org/xbib/graphics/layout/pdfbox/landscape.pdf new file mode 100644 index 0000000000000000000000000000000000000000..287128e5ce8b56118c35eb41876e2b7748f05754 GIT binary patch literal 10456 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 literal 0 HcmV?d00001 diff --git a/layout-pdfbox/src/test/resources/org/xbib/graphics/layout/pdfbox/linespacing.pdf b/layout-pdfbox/src/test/resources/org/xbib/graphics/layout/pdfbox/linespacing.pdf new file mode 100644 index 0000000000000000000000000000000000000000..bc3292fbbb310852fd1450cffd04f1b586b6f6f1 GIT binary patch literal 1739 zcmbVNe^3-v95+Rr*1>dW7)i}H)OkwD+jqOi?Fu3scbg&xA zAp_`{fXIMF8JHf4YW zGJpiqqhyr<3Y9XD0t}4;7=~qVz-XPB#}WI=!Qn+K-dX_dDi6l+7E_uz8P}_H8J^ZF z0o-Xh!C>XhAOUg)ot4+{$)*$@hmPb$z4cQN=w@21W?pCXA~MRZZ=Pc-B}Zr9=_Mx4 z-H~L>pTX>l={`IA+|21gSGZfA1q*^&AN6dUf8zEVel_PJ8m?_<^7HQ5lCH3ov9i&j zp~S}2{OB?&V)7L>f`wzVJQSm*mGnl$nj`8(Z;ya~iqqHMeVw{EA}n@vm2qQj&m(V- zeZPFu{YCjDdFdUayfkV-&jv$i=2<77nQqH&^4fke{F2w0p97CX(5)-pJkM9|rCZxj zWbDIF_+8E;!(cVF+U|QE*j;NsHKo?RuI-&A^JNL9&ysk}B*UPOIRlXdSv#TZ-KTPN-}qvm1$_AmX{ zO(^mG+bF8X>#~s^X7CK$*vwvn{v%?N$i?+g+t>3|x_{hXu?3CBZoa?jGCIZlnOJc6Xjak;-@@UAU60M}1t%U1^`skjg06%^xvO_hEoob+ zrbliUg8y}%IW9ESCHcPFU9H!K3g?F`&0c&o-2i>(oo+}xka=u;XGhni{i_{KyB#al zY{I#^j;Pm;WCYx-cb0s0c-pTasuejtw{%pl_Vek6Lsbi_*0#v~hAwZ*@S9h)b{)0p zRp-IYqbB=!I%kwmD(vu*#;&g)-2*5!cz;9VF#sxe-#(d8sgET;IF z90Kf&(f5pZr=lGP;}k`WP5t+Viw2f$dt$XlkWO@jVRDcKtVEOeQSueZ4bF0}_IfMnls`$&m^!j15z=jFt>!R0xq2Npd6# z3jx6z8flnt4a;yOsaBA*mSsqi#mI1up%p4^2v*{`!e_j6j#$1eR-M`EI$1=iRD?{X IjpB&^0nqDiMgRZ+ literal 0 HcmV?d00001 diff --git a/layout-pdfbox/src/test/resources/org/xbib/graphics/layout/pdfbox/links.pdf b/layout-pdfbox/src/test/resources/org/xbib/graphics/layout/pdfbox/links.pdf new file mode 100644 index 0000000000000000000000000000000000000000..94a413f6f12bea10fb7ebb350db7902ac1408c53 GIT binary patch literal 8186 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 bmx1I8epgHP;_|Zx8ApLX^{>&}LPPxrya+0l literal 0 HcmV?d00001 diff --git a/layout-pdfbox/src/test/resources/org/xbib/graphics/layout/pdfbox/listener.pdf b/layout-pdfbox/src/test/resources/org/xbib/graphics/layout/pdfbox/listener.pdf new file mode 100644 index 0000000000000000000000000000000000000000..359dd4ca25f84f35ab6a7b3c6066935e88f26ec3 GIT binary patch literal 8985 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 M1Mu?7si6V?13dZYp#T5? literal 0 HcmV?d00001 diff --git a/layout-pdfbox/src/test/resources/org/xbib/graphics/layout/pdfbox/lowleveltext.pdf b/layout-pdfbox/src/test/resources/org/xbib/graphics/layout/pdfbox/lowleveltext.pdf new file mode 100644 index 0000000000000000000000000000000000000000..36db260a74ed7c1d34ad903424581740ac43db32 GIT binary patch literal 3702 zcmbVP3piAH8(*2PGT0(Y*gDb3{mhx`8O>T6PBDqFRJx6EFk)uRj8LpXlUlXXWn8mV zmP+Z%h^A7yDixJ4whJPu82VaPF7=&Jy6`%P(ctL^g%^po`4U4Dr?Y#6NHKZ5@zm+ zx3|Zm`~a1k>itXiVLLp?<^_lWKeEbv6&_>>BKQ&j`pi-FWvn9}bVCDpoCSg?z>jE4 z1XMSbLA52(sWdVGQy^OyX295h9vl&hjrcwsYLAFhCpe6iY-XCrle3mDRcY!OjPWR_uM> z_He&YGjRp8)ZtEEJ3Dhot*FTQ%!#5MHizmfz=f%Vn{K9a9rvdKnew(IW0ACV&QN*Z zP%c1_Rvc_aOsdmc(};zxhb&B7EpsWkc`>lQpiw<}&y?Ak4=Y5`mddwBf4kZy`)_l3 znbp9%$LHpmwkuCCl#d7-{On5VUKJb7uu;0b8&GV0lJn%G!eTSrk*vv`Yq9ssI_`$u3-{Z>FLyzpT0x!gm#6D)+^bBne2 zirH&< z7(x9dF7JlX6?W%~KBH>8o=U!nfi7@4SZhiAB*pFsl@v|hI3@$VB{x+m_I3R8vG|X zCt=z?&j>v-68PNv$Lu3J>@Vtucc)L^?v>F!GvMOwM@J$H48k}1IC*f)rrGX|tm76y z>IIHB@pmdhA^?gOaL6BAL-%zj z9Pl9!tWM2KOy=J?mpLIo{vhIhQ(1-UGjU{C=Djz4-BibcV^x+$l`V?C!;cMq@?_5~ ztMO<^cda|b9;(l-K{mUZsB1|*ZIVhRygvS2?dCHVR~p@>-A6YuTb^j%ccTBW6A?`CcUh7*2b)~ z{__QZtUdk0Wd=vfusLz*w$q-r7vH84YH3c)^f(AVb$NHsfh%-=xTPTdsJ(#=6+&@X^mtm)wiP>6S*WjK{nRdnF zRfmD2RD02-nogPa2H(rg&%DpbyOzle_$f&`&8bt}T5DwJ8Xal<xO_*F4O%gdH_8{YUOaNBTy+Tp6CoS>@#%1w7yT)C7il)uqVft`#y`|OJSaq-E=Q@{n7Aa$LqF*+lT~+JUD2wten? z&w=U)HRh|V-jJpHGgs9;*4RS#4>~?C-hP**ZE>w~Qd#`$xYY-8?gw9_Rz6+R-BrH! zY24+MuD3%6`kDeAsFZb0^TW1G2!Ch?HD(McuQs0DY1(d;rQ|4erngq7t8J|^UOY3O zq<6$~>O*%0Bw>-z>iEw2S@u1#+b>qmlQwm?5{!D`uGEHp@=Zlj`CTc?OKxmu=0Z&H zQlD4_-?KK4HTk)8rH`F9VMdJ5Y?J2Ql^LB{{nHDs)$jwulxbnFd}76SUzGhSN+KuZ z64ez6`_kIsh3j|bsO#WTk5fvvRJNL}WQWKBj|+=K@?i3=SMA%`u7NLfV3Phqe^g!$SBtEDGB7g%N6 z-Hyr=X7taV+NQIwP~X2dF|D*bNi*c4K0Y3o;8|#~Sl=$&a!_EZyA)Tq#I5mB=r)rp z{pPx^*B)fSbwO>qE+xLn`6)VW>DwCs1xY^!kyzupnN+M*#&yP|M*|Gqul zZrPXKU7-k@v{kD6UXDieSEX+s^$b%%fIcEy9|T$@V=4oPMx%{5scb$v5dnDE1wCz!9UWF97Jx3q!>=o`NtA9}hb7F{$S9g8rn}h%dt@{io1U(FFXPrC)?+J-uV8ok8I;)_`LGQ$M7LvHs3ar zkplkIpnolIBRt>mD}TNteA{Hcd1<0VXds>lKro*8@d9X6DwzrdYJbF38;R-y_@6L3 zwrz~UAk1qtMxtVi|D_$oV2tr45y@CxeQ8G`QpaKtjO~S^>_~KMe;I|5=~xy=VH6lE zt5Fyg#!6unM*BbG(c!TiFg%un!59-WnaKEzS0du@La{nk{Xw1r0d@|1zoEN00e(z` zL~^FmASWt~yR*;%0yV literal 0 HcmV?d00001 diff --git a/layout-pdfbox/src/test/resources/org/xbib/graphics/layout/pdfbox/margin.pdf b/layout-pdfbox/src/test/resources/org/xbib/graphics/layout/pdfbox/margin.pdf new file mode 100644 index 0000000..465da57 --- /dev/null +++ b/layout-pdfbox/src/test/resources/org/xbib/graphics/layout/pdfbox/margin.pdf @@ -0,0 +1,79 @@ +%PDF-1.4 +%öäüß +1 0 obj +<< +/Type /Catalog +/Version /1.4 +/Pages 2 0 R +>> +endobj +2 0 obj +<< +/Type /Pages +/Kids [3 0 R] +/Count 1 +>> +endobj +3 0 obj +<< +/Type /Page +/MediaBox [0.0 0.0 595.27563 841.8898] +/Parent 2 0 R +/Contents 4 0 R +/Resources 5 0 R +>> +endobj +4 0 obj +<< +/Filter [/FlateDecode] +/Length 6 0 R +>> +stream +xœÍ–;oÛ0…wý +Ž)¨|?Æh§.A t¦mFfbI6EºÍ¿/%˜rëX~ ±x¡d’:Ÿtxî]gp˜b9Œ*8ã@P”K©„€ + œÉ~eUö­/˜½Î¾N2`ü!@ã:…r¡ R®À¤Ì>!0yÌ`7ÍÙÝÚ™ò˜> +endobj +6 0 obj +686 +endobj +7 0 obj +<< +/F0 8 0 R +>> +endobj +8 0 obj +<< +/Type /Font +/Subtype /Type1 +/BaseFont /Helvetica +/Encoding /WinAnsiEncoding +>> +endobj +xref +0 9 +0000000000 65535 f +0000000015 00000 n +0000000078 00000 n +0000000135 00000 n +0000000254 00000 n +0000001018 00000 n +0000001051 00000 n +0000001070 00000 n +0000001101 00000 n +trailer +<< +/Root 1 0 R +/ID [<8E57577CC59848BA1707730A4C5BAD9B> ] +/Size 9 +>> +startxref +1198 +%%EOF diff --git a/layout-pdfbox/src/test/resources/org/xbib/graphics/layout/pdfbox/markup.pdf b/layout-pdfbox/src/test/resources/org/xbib/graphics/layout/pdfbox/markup.pdf new file mode 100644 index 0000000000000000000000000000000000000000..972f878e1351e3ec5f5a2855c71aec614772e0ec GIT binary patch literal 2920 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 X=elT$bJ_7u6a)$d@bM|AqXGX9^6rsK literal 0 HcmV?d00001 diff --git a/layout-pdfbox/src/test/resources/org/xbib/graphics/layout/pdfbox/rotation.pdf b/layout-pdfbox/src/test/resources/org/xbib/graphics/layout/pdfbox/rotation.pdf new file mode 100644 index 0000000000000000000000000000000000000000..ea26c8e603579179e4e2c4b6eee976bfc8df4f9c GIT binary patch literal 9812 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 text) { + // compute the compression level similarly to what the Clib code does + int level = Math.round(9 * (1f - quality)); + // get the optimal scanline provider for this image + ScanlineProvider scanlines = ScanlineProviderFactory.getProvider(image); + if (scanlines == null) { + throw new IllegalArgumentException("Could not find a scanline extractor for " + image); + } + ColorModel colorModel = image.getColorModel(); + boolean indexed = colorModel instanceof IndexColorModel; + ImageInfo ii = getImageInfo(image, scanlines, colorModel, indexed); + try (PngWriter pw = new PngWriter(outStream, ii)) { + pw.setShouldCloseStream(false); + pw.setCompLevel(level); + pw.setFilterType(filterType); + ChunksListForWrite chunkList = pw.getChunksList(); + PngMetadata metadata = pw.getMetadata(); + if (indexed) { + IndexColorModel icm = (IndexColorModel) colorModel; + PngChunkPLTE palette = metadata.createPLTEChunk(); + int ncolors = icm.getMapSize(); + palette.setNentries(ncolors); + for (int i = 0; i < ncolors; i++) { + final int red = icm.getRed(i); + final int green = icm.getGreen(i); + final int blue = icm.getBlue(i); + palette.setEntry(i, red, green, blue); + } + if (icm.hasAlpha()) { + PngChunkTRNS transparent = new PngChunkTRNS(ii); + int[] alpha = new int[ncolors]; + for (int i = 0; i < ncolors; i++) { + final int a = icm.getAlpha(i); + alpha[i] = a; + } + transparent.setPalAlpha(alpha); + chunkList.queue(transparent); + } + } + if (text != null && !text.isEmpty()) { + for (Entry entrySet : text.entrySet()) { + metadata.setText(entrySet.getKey(), entrySet.getValue(), true, false); + } + } + // write out the actual image lines + for (int row = 0; row < image.getHeight(); row++) { + pw.writeRow(scanlines); + } + pw.end(); + } + } + + /** + * Quick method used for checking if the image can be optimized with the + * selected scanline extractors or if the image must be rescaled to byte + * before writing the image. + */ + public boolean isScanlineSupported(RenderedImage image) { + ScanlineProvider scanlines = ScanlineProviderFactory.getProvider(image); + return scanlines != null; + } + + private ImageInfo getImageInfo(RenderedImage image, ScanlineProvider scanlines, + ColorModel colorModel, boolean indexed) { + int numColorComponents = colorModel.getNumColorComponents(); + boolean grayscale = !indexed && numColorComponents < 3; + byte bitDepth = scanlines.getBitDepth(); + boolean hasAlpha = !indexed && colorModel.hasAlpha(); + return new ImageInfo(image.getWidth(), image.getHeight(), bitDepth, hasAlpha, grayscale, indexed); + } +} diff --git a/png/src/main/java/org/xbib/graphics/imageio/plugins/png/RasterByteABGRProvider.java b/png/src/main/java/org/xbib/graphics/imageio/plugins/png/RasterByteABGRProvider.java new file mode 100644 index 0000000..b29a597 --- /dev/null +++ b/png/src/main/java/org/xbib/graphics/imageio/plugins/png/RasterByteABGRProvider.java @@ -0,0 +1,56 @@ +package org.xbib.graphics.imageio.plugins.png; + +import java.awt.image.ComponentSampleModel; +import java.awt.image.DataBufferByte; +import java.awt.image.Raster; + +/** + * A scanline provider optimized for Raster objects containing + * a 8bit BGR or ABGR image. + */ +public final class RasterByteABGRProvider extends AbstractScanlineProvider { + + final static int[] PIXEL_STRIDES = new int[]{3,4}; + final byte[] bytes; + final boolean bgrOrder; + final boolean hasAlpha; + final int pixelStride; + final int[] bandOffsets; + final int numBands; + + public RasterByteABGRProvider(Raster raster, boolean hasAlpha) { + super(raster, 8, raster.getWidth() * (computePixelStride(raster, PIXEL_STRIDES, hasAlpha))); + this.hasAlpha = hasAlpha; + this.bytes = ((DataBufferByte) raster.getDataBuffer()).getData(); + ComponentSampleModel sm = (ComponentSampleModel) raster.getSampleModel(); + this.bgrOrder = sm.getBandOffsets()[0] != 0; + this.pixelStride = sm.getPixelStride(); + this.bandOffsets = sm.getBandOffsets(); + this.numBands = sm.getNumBands(); + } + + @Override + public void next(final byte[] row, final int offset, final int length) { + int bytesIdx = cursor.next(); + int i = offset; + final int max = offset + length; + if (!bgrOrder && (numBands == pixelStride)) { + System.arraycopy(bytes, bytesIdx, row, offset, length); + } else { + while (i < max) { + for (int j = 0; j < numBands; j++) { + // We assign data pixels on the expected order + // So if bgrOrder (bandOffset is 2,1,0): + // row[i+2] = B + // row[i+1] = G + // row[i+0] = R + row[i + j] = bytes[bytesIdx + bandOffsets[j]]; + } + // Pixel stride may be longer than numBands due to bandSelect + // sharing same dataBuffer of the original image + bytesIdx += pixelStride; + i += numBands; + } + } + } +} diff --git a/png/src/main/java/org/xbib/graphics/imageio/plugins/png/RasterByteGrayAlphaProvider.java b/png/src/main/java/org/xbib/graphics/imageio/plugins/png/RasterByteGrayAlphaProvider.java new file mode 100644 index 0000000..5e88b28 --- /dev/null +++ b/png/src/main/java/org/xbib/graphics/imageio/plugins/png/RasterByteGrayAlphaProvider.java @@ -0,0 +1,51 @@ +package org.xbib.graphics.imageio.plugins.png; + +import java.awt.image.ComponentSampleModel; +import java.awt.image.DataBufferByte; +import java.awt.image.Raster; + +/** + * A scanline provider optimized for Raster objects containing a 8bit gray and alpha bands + */ +public final class RasterByteGrayAlphaProvider extends AbstractScanlineProvider { + + final static int[] PIXEL_STRIDES = new int[]{2}; + + final byte[] bytes; + + boolean alphaFirst; + + int[] bandOffsets; + + int pixelStride; + + int numBands; + + public RasterByteGrayAlphaProvider(Raster raster) { + super(raster, 8, raster.getWidth() * computePixelStride(raster, PIXEL_STRIDES)); + this.bytes = ((DataBufferByte) raster.getDataBuffer()).getData(); + ComponentSampleModel sm = (ComponentSampleModel) raster.getSampleModel(); + this.bandOffsets = sm.getBandOffsets(); + this.numBands = sm.getNumBands(); + this.pixelStride = sm.getPixelStride(); + this.alphaFirst = bandOffsets[0] != 0; + } + + @Override + public void next(final byte[] row, final int offset, final int length) { + int bytesIdx = cursor.next(); + if (!alphaFirst && (numBands == pixelStride)) { + System.arraycopy(bytes, bytesIdx, row, offset, length); + } else { + int i = offset; + final int max = offset + length; + while (i < max) { + for (int j = 0; j < numBands; j++) { + row[i + j] = bytes[bytesIdx + bandOffsets[j]]; + } + bytesIdx += pixelStride; + i += numBands; + } + } + } +} diff --git a/png/src/main/java/org/xbib/graphics/imageio/plugins/png/RasterByteRepackSingleBandProvider.java b/png/src/main/java/org/xbib/graphics/imageio/plugins/png/RasterByteRepackSingleBandProvider.java new file mode 100644 index 0000000..8b6a8fa --- /dev/null +++ b/png/src/main/java/org/xbib/graphics/imageio/plugins/png/RasterByteRepackSingleBandProvider.java @@ -0,0 +1,65 @@ +package org.xbib.graphics.imageio.plugins.png; + +import java.awt.image.DataBufferByte; +import java.awt.image.IndexColorModel; +import java.awt.image.Raster; + +/** + * A scanline provider that packs more than one pixel per output byte. + */ +public final class RasterByteRepackSingleBandProvider extends AbstractScanlineProvider { + + final byte[] bytes; + + public RasterByteRepackSingleBandProvider(Raster raster, int bitDepth, int scanlineLength) { + super(raster, bitDepth, scanlineLength); + this.bytes = ((DataBufferByte) raster.getDataBuffer()).getData(); + } + + public RasterByteRepackSingleBandProvider(Raster raster, int bitDepth, int scanlineLength, + IndexColorModel palette) { + super(raster, bitDepth, scanlineLength, palette); + this.bytes = ((DataBufferByte) raster.getDataBuffer()).getData(); + } + + @Override + public void next(final byte[] row, final int offset, final int length) { + if (this.currentRow == height) { + throw new IllegalStateException("All scanlines have been read already"); + } + + int pxIdx = cursor.next(); + final int pxLimit = pxIdx + width; + int i = offset; + final int max = offset + length; + if (bitDepth == 4) { + while (i < max) { + final int low = bytes[pxIdx++]; + final int high = pxIdx < pxLimit ? bytes[pxIdx++] : 0; + row[i++] = (byte) ((low << 4) | high); + } + } else if (bitDepth == 2) { + while (i < max) { + final int b1 = bytes[pxIdx++]; + final int b2 = pxIdx < pxLimit ? bytes[pxIdx++] : 0; + final int b3 = pxIdx < pxLimit ? bytes[pxIdx++] : 0; + final int b4 = pxIdx < pxLimit ? bytes[pxIdx++] : 0; + row[i++] = (byte) (b4 | (b3 << 2) | (b2 << 4) | (b1 << 6)); + } + } else { + while (i < max) { + final int b1 = pxIdx < pxLimit ? bytes[pxIdx++] : 0; + final int b2 = pxIdx < pxLimit ? bytes[pxIdx++] : 0; + final int b3 = pxIdx < pxLimit ? bytes[pxIdx++] : 0; + final int b4 = pxIdx < pxLimit ? bytes[pxIdx++] : 0; + final int b5 = pxIdx < pxLimit ? bytes[pxIdx++] : 0; + final int b6 = pxIdx < pxLimit ? bytes[pxIdx++] : 0; + final int b7 = pxIdx < pxLimit ? bytes[pxIdx++] : 0; + final int b8 = pxIdx < pxLimit ? bytes[pxIdx++] : 0; + row[i++] = (byte) (b8 | (b7 << 1) | (b6 << 2) | (b5 << 3) | (b4 << 4) | (b3 << 5) + | (b2 << 6) | (b1 << 7)); + } + } + currentRow++; + } +} diff --git a/png/src/main/java/org/xbib/graphics/imageio/plugins/png/RasterByteSingleBandProvider.java b/png/src/main/java/org/xbib/graphics/imageio/plugins/png/RasterByteSingleBandProvider.java new file mode 100644 index 0000000..52dc79f --- /dev/null +++ b/png/src/main/java/org/xbib/graphics/imageio/plugins/png/RasterByteSingleBandProvider.java @@ -0,0 +1,35 @@ +package org.xbib.graphics.imageio.plugins.png; + +import java.awt.image.DataBufferByte; +import java.awt.image.IndexColorModel; +import java.awt.image.Raster; + +/** + * A scanline provider that can copy 1-1 data from the buffered image into the scanline without + * performing any kind of transformation. + */ +public final class RasterByteSingleBandProvider extends AbstractScanlineProvider { + + final byte[] bytes; + + public RasterByteSingleBandProvider(Raster raster, int bitDepth, int scanlineLength) { + super(raster, bitDepth, scanlineLength); + this.bytes = ((DataBufferByte) raster.getDataBuffer()).getData(); + } + + public RasterByteSingleBandProvider(Raster raster, int bitDepth, int scanlineLength, + IndexColorModel palette) { + super(raster, bitDepth, scanlineLength, palette); + this.bytes = ((DataBufferByte) raster.getDataBuffer()).getData(); + } + + @Override + public void next(final byte[] scanline, final int offset, final int length) { + if (this.currentRow == height) { + throw new IllegalStateException("All scanlines have been read already"); + } + final int next = cursor.next(); + System.arraycopy(bytes, next, scanline, offset, length); + currentRow++; + } +} diff --git a/png/src/main/java/org/xbib/graphics/imageio/plugins/png/RasterByteSingleBandSkippingBytesProvider.java b/png/src/main/java/org/xbib/graphics/imageio/plugins/png/RasterByteSingleBandSkippingBytesProvider.java new file mode 100644 index 0000000..d86afe0 --- /dev/null +++ b/png/src/main/java/org/xbib/graphics/imageio/plugins/png/RasterByteSingleBandSkippingBytesProvider.java @@ -0,0 +1,54 @@ +package org.xbib.graphics.imageio.plugins.png; + +import java.awt.image.DataBufferByte; +import java.awt.image.PixelInterleavedSampleModel; +import java.awt.image.Raster; + +/** + * A scanline provider that copy data from the buffered image into the scanline + * by skipping some bytes due to pixelStride not equal to number of bands + * (There might be some bandSelect happening). + */ +public final class RasterByteSingleBandSkippingBytesProvider extends AbstractScanlineProvider { + + final static int[] PIXEL_STRIDES = new int[]{1}; + + int pixelStride; + + final byte[] bytes; + + int[] bandOffsets; + + int numBands; + + public RasterByteSingleBandSkippingBytesProvider(Raster raster) { + super(raster, 8, raster.getWidth() * computePixelStride(raster, PIXEL_STRIDES)); + PixelInterleavedSampleModel sm = (PixelInterleavedSampleModel) raster.getSampleModel(); + this.bytes = ((DataBufferByte) raster.getDataBuffer()).getData(); + this.pixelStride = sm.getPixelStride(); + this.numBands = sm.getNumBands(); + this.bandOffsets = sm.getBandOffsets(); + } + + @Override + public void next(final byte[] scanline, final int offset, final int length) { + if (this.currentRow == height) { + throw new IllegalStateException("All scanlines have been read already"); + } + int bytesIdx = cursor.next(); + if (numBands == pixelStride) { + System.arraycopy(bytes, bytesIdx, scanline, offset, length); + } else { + int i = offset; + final int max = offset + length; + while (i < max) { + for (int j = 0; j < numBands; j++) { + scanline[i + j] = bytes[bytesIdx + bandOffsets[j]]; + } + bytesIdx += pixelStride; + i += numBands; + } + } + currentRow++; + } +} diff --git a/png/src/main/java/org/xbib/graphics/imageio/plugins/png/RasterIntABGRProvider.java b/png/src/main/java/org/xbib/graphics/imageio/plugins/png/RasterIntABGRProvider.java new file mode 100644 index 0000000..648802a --- /dev/null +++ b/png/src/main/java/org/xbib/graphics/imageio/plugins/png/RasterIntABGRProvider.java @@ -0,0 +1,60 @@ +package org.xbib.graphics.imageio.plugins.png; + +import java.awt.image.DataBufferInt; +import java.awt.image.Raster; +import java.awt.image.SinglePixelPackedSampleModel; + +/** + * A scanline provider optimized for rasters with int packed RGB or RGBA pixels. + */ +public final class RasterIntABGRProvider extends AbstractScanlineProvider { + + final int[] pixels; + + final boolean bgrOrder; + + final boolean hasAlpha; + + public RasterIntABGRProvider(Raster raster, boolean hasAlpha) { + super(raster, 8, raster.getWidth() * (hasAlpha ? 4 : 3)); + this.pixels = ((DataBufferInt) raster.getDataBuffer()).getData(); + this.hasAlpha = hasAlpha; + if (hasAlpha) { + bgrOrder = false; + } else { + int[] offsets = ((SinglePixelPackedSampleModel) raster.getSampleModel()).getBitOffsets(); + bgrOrder = offsets[0] != 0; + } + } + + @Override + public void next(final byte[] row, final int offset, final int length) { + int pxIdx = cursor.next(); + int i = offset; + final int max = offset + length; + if (hasAlpha) { + while (i < max) { + final int color = pixels[pxIdx++]; + row[i++] = (byte) ((color >> 16) & 0xff); + row[i++] = (byte) ((color >> 8) & 0xff); + row[i++] = (byte) ((color) & 0xff); + row[i++] = (byte) ((color >> 24) & 0xff); + } + } else if (bgrOrder) { + while (i < max) { + final int color = pixels[pxIdx++]; + row[i++] = (byte) ((color >> 16) & 0xff); + row[i++] = (byte) ((color >> 8) & 0xff); + row[i++] = (byte) ((color) & 0xff); + } + } else { + while (i < max) { + final int color = pixels[pxIdx++]; + row[i++] = (byte) ((color) & 0xff); + row[i++] = (byte) ((color >> 8) & 0xff); + row[i++] = (byte) ((color >> 16) & 0xff); + } + } + } + +} diff --git a/png/src/main/java/org/xbib/graphics/imageio/plugins/png/RasterShortABGRProvider.java b/png/src/main/java/org/xbib/graphics/imageio/plugins/png/RasterShortABGRProvider.java new file mode 100644 index 0000000..91b094c --- /dev/null +++ b/png/src/main/java/org/xbib/graphics/imageio/plugins/png/RasterShortABGRProvider.java @@ -0,0 +1,90 @@ +package org.xbib.graphics.imageio.plugins.png; + +import java.awt.image.ComponentSampleModel; +import java.awt.image.DataBufferUShort; +import java.awt.image.Raster; + +/** + * A scanline provider optimized for Raster objects containing a 16bit BGR or ABGR image. + */ +public final class RasterShortABGRProvider extends AbstractScanlineProvider { + + final short[] shorts; + + final boolean bgrOrder; + + final boolean hasAlpha; + + public RasterShortABGRProvider(Raster raster, boolean hasAlpha) { + super(raster, 16, (hasAlpha ? 8 : 6) * raster.getWidth()); + this.hasAlpha = hasAlpha; + shorts = ((DataBufferUShort) raster.getDataBuffer()).getData(); + bgrOrder = ((ComponentSampleModel) raster.getSampleModel()).getBandOffsets()[0] != 0; + } + + @Override + public void next(final byte[] scanline, final int offset, final int length) { + int shortsIdx = cursor.next(); + int i = offset; + final int max = offset + length; + if (hasAlpha) { + if (bgrOrder) { + while (i < max) { + final short a = shorts[shortsIdx++]; + final short b = shorts[shortsIdx++]; + final short g = shorts[shortsIdx++]; + final short r = shorts[shortsIdx++]; + scanline[i++] = (byte) ((r >> 8) & 0xFF); + scanline[i++] = (byte) (r & 0xFF); + scanline[i++] = (byte) ((g >> 8) & 0xFF); + scanline[i++] = (byte) (g & 0xFF); + scanline[i++] = (byte) ((b >> 8) & 0xFF); + scanline[i++] = (byte) (b & 0xFF); + scanline[i++] = (byte) ((a >> 8) & 0xFF); + scanline[i++] = (byte) (a & 0xFF); + } + } else { + while (i < max) { + final short r = shorts[shortsIdx++]; + final short g = shorts[shortsIdx++]; + final short b = shorts[shortsIdx++]; + final short a = shorts[shortsIdx++]; + scanline[i++] = (byte) ((r >> 8) & 0xFF); + scanline[i++] = (byte) (r & 0xFF); + scanline[i++] = (byte) ((g >> 8) & 0xFF); + scanline[i++] = (byte) (g & 0xFF); + scanline[i++] = (byte) ((b >> 8) & 0xFF); + scanline[i++] = (byte) (b & 0xFF); + scanline[i++] = (byte) ((a >> 8) & 0xFF); + scanline[i++] = (byte) (a & 0xFF); + } + } + } else { + if(bgrOrder) { + while (i < max) { + final short b = shorts[shortsIdx++]; + final short g = shorts[shortsIdx++]; + final short r = shorts[shortsIdx++]; + scanline[i++] = (byte) ((r >> 8) & 0xFF); + scanline[i++] = (byte) (r & 0xFF); + scanline[i++] = (byte) ((g >> 8) & 0xFF); + scanline[i++] = (byte) (g & 0xFF); + scanline[i++] = (byte) ((b >> 8) & 0xFF); + scanline[i++] = (byte) (b & 0xFF); + } + } else { + while (i < max) { + final short r = shorts[shortsIdx++]; + final short g = shorts[shortsIdx++]; + final short b = shorts[shortsIdx++]; + scanline[i++] = (byte) ((r >> 8) & 0xFF); + scanline[i++] = (byte) (r & 0xFF); + scanline[i++] = (byte) ((g >> 8) & 0xFF); + scanline[i++] = (byte) (g & 0xFF); + scanline[i++] = (byte) ((b >> 8) & 0xFF); + scanline[i++] = (byte) (b & 0xFF); + } + } + } + } +} diff --git a/png/src/main/java/org/xbib/graphics/imageio/plugins/png/RasterShortGrayAlphaProvider.java b/png/src/main/java/org/xbib/graphics/imageio/plugins/png/RasterShortGrayAlphaProvider.java new file mode 100644 index 0000000..bdce95c --- /dev/null +++ b/png/src/main/java/org/xbib/graphics/imageio/plugins/png/RasterShortGrayAlphaProvider.java @@ -0,0 +1,49 @@ +package org.xbib.graphics.imageio.plugins.png; + +import java.awt.image.DataBufferUShort; +import java.awt.image.PixelInterleavedSampleModel; +import java.awt.image.Raster; + +/** + * A scanline provider optimized for a Raster with 16 bit gray + 16 bits alpha. + */ +public final class RasterShortGrayAlphaProvider extends AbstractScanlineProvider { + + final short[] shorts; + + final boolean alphaFirst; + + public RasterShortGrayAlphaProvider(Raster raster) { + super(raster, 16, raster.getWidth() * 4); + this.shorts = ((DataBufferUShort) raster.getDataBuffer()).getData(); + int[] bandOffsets = ((PixelInterleavedSampleModel) raster.getSampleModel()).getBandOffsets(); + this.alphaFirst = bandOffsets[0] != 0; + } + + + public void next(final byte[] scanline, final int offset, final int length) { + int shortsIdx = cursor.next(); + int i = offset; + final int max = offset + length; + if(alphaFirst) { + while (i < max) { + final short alpha = shorts[shortsIdx++]; + final short gray = shorts[shortsIdx++]; + scanline[i++] = (byte) ((gray >> 8) & 0xFF); + scanline[i++] = (byte) (gray & 0xFF); + scanline[i++] = (byte) ((alpha >> 8) & 0xFF); + scanline[i++] = (byte) (alpha & 0xFF); + } + } else { + while (i < max) { + final short gray = shorts[shortsIdx++]; + final short alpha = shorts[shortsIdx++]; + scanline[i++] = (byte) ((gray >> 8) & 0xFF); + scanline[i++] = (byte) (gray & 0xFF); + scanline[i++] = (byte) ((alpha >> 8) & 0xFF); + scanline[i++] = (byte) (alpha & 0xFF); + } + } + } + +} diff --git a/png/src/main/java/org/xbib/graphics/imageio/plugins/png/RasterShortSingleBandProvider.java b/png/src/main/java/org/xbib/graphics/imageio/plugins/png/RasterShortSingleBandProvider.java new file mode 100644 index 0000000..6516f0d --- /dev/null +++ b/png/src/main/java/org/xbib/graphics/imageio/plugins/png/RasterShortSingleBandProvider.java @@ -0,0 +1,40 @@ +package org.xbib.graphics.imageio.plugins.png; + +import java.awt.image.DataBufferUShort; +import java.awt.image.IndexColorModel; +import java.awt.image.Raster; + +/** + * A scanline provider optimized for a Raster with 16 bit gray pixels + * + * @author Andrea Aime - GeoSolutions + */ +public final class RasterShortSingleBandProvider extends AbstractScanlineProvider { + + final short[] shorts; + + public RasterShortSingleBandProvider(Raster raster) { + super(raster, 16, raster.getWidth() * 2); + this.shorts = ((DataBufferUShort) raster.getDataBuffer()).getData(); + } + + public RasterShortSingleBandProvider(Raster raster, int bidDepth, int scanlineLength, IndexColorModel palette) { + super(raster, bidDepth, scanlineLength, palette); + this.shorts = ((DataBufferUShort) raster.getDataBuffer()).getData(); + } + + + public void next(final byte[] scanline, final int offset, final int length) { + int shortsIdx = cursor.next(); + int i = offset; + int max = offset + length; + while (i < max) { + short gray = shorts[shortsIdx++]; + scanline[i++] = (byte) ((gray >> 8) & 0xFF); + if(i < max) { + scanline[i++] = (byte) (gray & 0xFF); + } + } + } + +} diff --git a/png/src/main/java/org/xbib/graphics/imageio/plugins/png/ScanlineCursor.java b/png/src/main/java/org/xbib/graphics/imageio/plugins/png/ScanlineCursor.java new file mode 100644 index 0000000..c138bb6 --- /dev/null +++ b/png/src/main/java/org/xbib/graphics/imageio/plugins/png/ScanlineCursor.java @@ -0,0 +1,52 @@ +package org.xbib.graphics.imageio.plugins.png; + +import java.awt.image.ComponentSampleModel; +import java.awt.image.Raster; + +/** + * A helper class that supports the scanline provider in navigating the structure of a Java image. + */ +final class ScanlineCursor { + + final int scanlineStride; + + final int maxPosition; + + int position; + + public ScanlineCursor(Raster raster) { + // the data buffer can have lines that are longer than width * bytes per pixel, can have + // extra at the end + this.scanlineStride = getScanlineStride(raster); + // the data buffer itself could be longer + this.position = raster.getDataBuffer().getOffset(); + this.maxPosition = raster.getDataBuffer().getSize(); + } + + /** + * Returns the initial position of the current line, and moves to the next. + */ + public int next() { + final int result = position; + if (result >= maxPosition) { + throw new IllegalStateException( + "We got past the end of the buffer, current position is " + position + + " and max position value is " + maxPosition); + } + position += scanlineStride; + return result; + } + + /** + * Gets the scanline stride for the given raster. + */ + int getScanlineStride(Raster raster) { + if (raster.getSampleModel() instanceof ComponentSampleModel) { + ComponentSampleModel csm = ((ComponentSampleModel) raster.getSampleModel()); + return csm.getScanlineStride(); + } else { + return raster.getDataBuffer().getSize() / raster.getHeight(); + } + } + +} diff --git a/png/src/main/java/org/xbib/graphics/imageio/plugins/png/ScanlineProvider.java b/png/src/main/java/org/xbib/graphics/imageio/plugins/png/ScanlineProvider.java new file mode 100644 index 0000000..c5408f0 --- /dev/null +++ b/png/src/main/java/org/xbib/graphics/imageio/plugins/png/ScanlineProvider.java @@ -0,0 +1,41 @@ +package org.xbib.graphics.imageio.plugins.png; + +import java.awt.image.IndexColorModel; + +import org.xbib.graphics.imageio.plugins.png.pngj.IImageLine; + +/** + * The bridge between images and PNG scanlines + */ +public interface ScanlineProvider extends IImageLine { + + /** + * Image width + */ + int getWidth(); + + /** + * Image height + */ + int getHeight(); + + /** + * The bit depth of this image, 1, 2, 4, 8 or 16 + */ + byte getBitDepth(); + + /** + * The number of byte[] elements in the scaline + */ + int getScanlineLength(); + + /** + * The next scanline, or throws an exception if we got past the end of the image + */ + void next(byte[] scaline, int offset, int length); + + /** + * Returns the palette for this image, or null if the image does not have one + */ + IndexColorModel getPalette(); +} diff --git a/png/src/main/java/org/xbib/graphics/imageio/plugins/png/ScanlineProviderFactory.java b/png/src/main/java/org/xbib/graphics/imageio/plugins/png/ScanlineProviderFactory.java new file mode 100644 index 0000000..feee785 --- /dev/null +++ b/png/src/main/java/org/xbib/graphics/imageio/plugins/png/ScanlineProviderFactory.java @@ -0,0 +1,151 @@ +package org.xbib.graphics.imageio.plugins.png; + +import java.awt.Rectangle; +import java.awt.image.BufferedImage; +import java.awt.image.ColorModel; +import java.awt.image.ComponentColorModel; +import java.awt.image.DataBuffer; +import java.awt.image.DirectColorModel; +import java.awt.image.IndexColorModel; +import java.awt.image.MultiPixelPackedSampleModel; +import java.awt.image.PixelInterleavedSampleModel; +import java.awt.image.Raster; +import java.awt.image.RenderedImage; +import java.awt.image.SampleModel; + +/** + * Wraps a {@link RenderedImage} into a scanline provider + * optimized to turn its pixels into PNG scanlines at the best performance. + */ +public class ScanlineProviderFactory { + + public static ScanlineProvider getProvider(RenderedImage image) { + ColorModel cm = image.getColorModel(); + SampleModel sm = image.getSampleModel(); + Raster raster; + if (image instanceof BufferedImage) { + raster = ((BufferedImage) image).getRaster(); + // in case the raster has a parent, this is likely a subimage, we have to force + // a copy of the raster to get a data buffer we can scroll over without issues + if (raster.getParent() != null) { + raster = image.getData(new Rectangle(0, 0, raster.getWidth(), raster.getHeight())); + } + } else { + // TODO: we could build a tile oriented reader that fetches tiles in parallel here + raster = image.getData(); + } + // grab the right scanline extractor based on image features + if (cm instanceof ComponentColorModel && sm.getDataType() == DataBuffer.TYPE_BYTE) { + if (sm.getNumBands() == 3 || sm.getNumBands() == 4) { + return new RasterByteABGRProvider(raster, cm.hasAlpha()); + } else if (sm.getNumBands() == 2 && cm.hasAlpha()) { + return new RasterByteGrayAlphaProvider(raster); + } else if (sm.getNumBands() == 1) { + if (sm instanceof MultiPixelPackedSampleModel) { + if (cm.getPixelSize() == 8) { + return new RasterByteSingleBandProvider(raster, 8, raster.getWidth()); + } else if (cm.getPixelSize() == 4) { + int scanlineLength = (raster.getWidth() + 1) / 2; + return new RasterByteSingleBandProvider(raster, 4, scanlineLength); + } else if (cm.getPixelSize() == 2) { + int scanlineLength = (raster.getWidth() + 2) / 4; + return new RasterByteSingleBandProvider(raster, 2, scanlineLength); + } else if (cm.getPixelSize() == 1) { + int scanlineLength = (raster.getWidth() + 4) / 8; + return new RasterByteSingleBandProvider(raster, 1, scanlineLength); + } + } else { + if (cm.getPixelSize() == 8) { + if (sm instanceof PixelInterleavedSampleModel && + (((PixelInterleavedSampleModel)sm).getPixelStride() != 1)) { + return new RasterByteSingleBandSkippingBytesProvider(raster); + } + return new RasterByteSingleBandProvider(raster, 8, raster.getWidth()); + } else if (cm.getPixelSize() == 4) { + int scanlineLength = (raster.getWidth() + 1) / 2; + return new RasterByteRepackSingleBandProvider(raster, 4, scanlineLength); + } else if (cm.getPixelSize() == 2) { + int scanlineLength = (raster.getWidth() + 2) / 4; + return new RasterByteRepackSingleBandProvider(raster, 2, scanlineLength); + } else if (cm.getPixelSize() == 1) { + int scanlineLength = (raster.getWidth() + 4) / 8; + return new RasterByteRepackSingleBandProvider(raster, 1, scanlineLength); + } + } + } + } else if (cm instanceof ComponentColorModel && sm.getDataType() == DataBuffer.TYPE_USHORT) { + if (sm.getNumBands() == 3 || sm.getNumBands() == 4) { + return new RasterShortABGRProvider(raster, cm.hasAlpha()); + } else if (sm.getNumBands() == 2 && cm.hasAlpha()) { + return new RasterShortGrayAlphaProvider(raster); + } else if (sm.getNumBands() == 1) { + return new RasterShortSingleBandProvider(raster); + } + } else if (cm instanceof DirectColorModel && sm.getDataType() == DataBuffer.TYPE_INT) { + if (sm.getNumBands() == 3 || sm.getNumBands() == 4) { + return new RasterIntABGRProvider(raster, cm.hasAlpha()); + } + } else if (cm instanceof IndexColorModel) { + IndexColorModel icm = (IndexColorModel) cm; + int pixelSize = icm.getPixelSize(); + // the RGBA quantizer can generate pixel sizes which are not powers of two, + // re-align to powers of two + if((pixelSize & (pixelSize - 1)) != 0) { + int nextPower = (int) (Math.floor(Math.log(pixelSize) / Math.log(2)) + 1); + pixelSize = (int) Math.pow(2, nextPower); + } + if (sm.getDataType() == DataBuffer.TYPE_BYTE) { + if (sm instanceof MultiPixelPackedSampleModel) { + if (pixelSize == 8) { + return new RasterByteSingleBandProvider(raster, 8, raster.getWidth(), icm); + } else if (pixelSize == 4) { + int scanlineLength = (raster.getWidth() + 1) / 2; + return new RasterByteSingleBandProvider(raster, 4, scanlineLength, icm); + } else if (pixelSize == 2) { + int scanlineLength = (raster.getWidth() + 2) / 4; + return new RasterByteSingleBandProvider(raster, 2, scanlineLength, icm); + } else if (pixelSize == 1) { + int scanlineLength = (raster.getWidth() + 4) / 8; + return new RasterByteSingleBandProvider(raster, 1, scanlineLength, icm); + } + } else { + if (pixelSize == 8) { + return new RasterByteSingleBandProvider(raster, 8, raster.getWidth(), icm); + } else if (pixelSize == 4) { + int scanlineLength = (raster.getWidth() + 1) / 2; + return new RasterByteRepackSingleBandProvider(raster, 4, scanlineLength, + icm); + } else if (pixelSize == 2) { + int scanlineLength = (raster.getWidth() + 2) / 4; + return new RasterByteRepackSingleBandProvider(raster, 2, scanlineLength, + icm); + } else if (pixelSize == 1) { + int scanlineLength = (raster.getWidth() + 4) / 8; + return new RasterByteRepackSingleBandProvider(raster, 1, scanlineLength, + icm); + } + } + } else if (sm.getDataType() == DataBuffer.TYPE_USHORT) { + if (sm instanceof MultiPixelPackedSampleModel) { + if (pixelSize == 16) { + int scanlineLength = raster.getWidth() * 2; + return new RasterShortSingleBandProvider(raster, 16, scanlineLength, icm); + } else if (pixelSize == 8) { + int scanlineLength = raster.getWidth() + ((raster.getWidth() % 2 == 0) ? 0 : 1); + return new RasterShortSingleBandProvider(raster, 8, scanlineLength, icm); + } else if (pixelSize == 4) { + int scanlineLength = (raster.getWidth() + 1) / 2; + return new RasterShortSingleBandProvider(raster, 4, scanlineLength, icm); + } else if (pixelSize == 2) { + int scanlineLength = (raster.getWidth() + 2) / 4; + return new RasterShortSingleBandProvider(raster, 2, scanlineLength, icm); + } else if (pixelSize == 1) { + int scanlineLength = (raster.getWidth() + 4) / 8; + return new RasterShortSingleBandProvider(raster, 1, scanlineLength, icm); + } + } + } + } + return null; + } +} diff --git a/png/src/main/java/org/xbib/graphics/imageio/plugins/png/pngj/BufferedStreamFeeder.java b/png/src/main/java/org/xbib/graphics/imageio/plugins/png/pngj/BufferedStreamFeeder.java new file mode 100644 index 0000000..8e3313f --- /dev/null +++ b/png/src/main/java/org/xbib/graphics/imageio/plugins/png/pngj/BufferedStreamFeeder.java @@ -0,0 +1,221 @@ +package org.xbib.graphics.imageio.plugins.png.pngj; + +import java.io.Closeable; +import java.io.IOException; +import java.io.InputStream; + +public class BufferedStreamFeeder implements Closeable { + + private InputStream stream; + private byte[] buf; + private int pendinglen; // bytes read+stored in buf, not yet still sent to IBytesConsumer + private int offset; + private boolean eof = false; // EOF on inputStream + private boolean closeStream = true; + private long bytesRead = 0; + + private static final int DEFAULTSIZE = 16384; + + /** + * By default, the stream will be closed on close() + */ + public BufferedStreamFeeder(InputStream is) { + this(is, DEFAULTSIZE); + } + + public BufferedStreamFeeder(InputStream is, int bufsize) { + this.stream = is; + buf = new byte[bufsize < 1 ? DEFAULTSIZE : bufsize]; + } + + /** + * Returns inputstream + * + * @return Input Stream from which bytes are read + */ + public InputStream getStream() { + return stream; + } + + /** + * @see BufferedStreamFeeder#feed(IBytesConsumer, int) + */ + public int feed(IBytesConsumer consumer) { + return feed(consumer, Integer.MAX_VALUE); + } + + /** + * Tries to feed the consumer with bytes read from the stream, at most + * maxbytes + *

      + * It can return less than maxbytes (that doesn't mean that the consumer or + * the input stream is done) + *

      + * Returns 0 if premature ending (no more to read, consumer not done)
      + * Returns -1 if nothing fed, but consumer is done + */ + public int feed(IBytesConsumer consumer, int maxbytes) { + refillBufferIfAppropiate(); + int consumed = 0; + final int tofeed = maxbytes > 0 && maxbytes < pendinglen ? maxbytes : pendinglen; + if (tofeed > 0) { + consumed = consumer.consume(buf, offset, tofeed); // never returns 0 + if (consumed > 0) { + offset += consumed; + pendinglen -= consumed; + assert pendinglen >= 0; + } + } else { + // nothing to fed ? premature ending ? + if (!eof) { + throw new PngjInputException("This should not happen"); + } + return consumer.isDone() ? -1 : 0 /* premature ending */; + } + if (consumed > 0) { + return consumed; + } else { // read bytes, but consumer refused to eat them ? (rare) + if (!consumer.isDone()) { + throw new PngjInputException("This should not happen!"); + } + return -1; + } + } + + /** + * Feeds as much bytes as it can to the consumer, in a loop.
      + * Returns bytes actually consumed
      + * This will stop when either the input stream is eof, or when the consumer + * refuses to eat more bytes. The caller can distinguish both cases by + * calling {@link #hasPendingBytes()} + */ + public long feedAll(IBytesConsumer consumer) { + long n = 0; + while (hasPendingBytes()) { + int n1 = feed(consumer); + if (n1 <= 0) { + break; + } + n += n1; + } + return n; + } + + /** + * Feeds exactly nbytes, retrying if necessary + * + * @param consumer Consumer + * @param nbytes Number of bytes + * @return nbytes if success, 0 if premature input ending, -1 if consumer + * done + */ + public int feedFixed(IBytesConsumer consumer, final int nbytes) { + int remain = nbytes; + while (remain > 0) { + int n = feed(consumer, remain); + if (n <= 0) { + return n; + } + remain -= n; + } + assert remain == 0; + return nbytes; + } + + /** + * If there are not pending bytes to be consumed, tries to fill the buffer + * reading bytes from the stream. + *

      + * If EOF is reached, sets eof=TRUE and calls close() + *

      + * Find in pendinglen the amounts of bytes read. + *

      + * If IOException, throws a PngjInputException + */ + protected void refillBufferIfAppropiate() { + if (pendinglen > 0 || eof) { + return; // only if not pending data + } + try { + // try to read + offset = 0; + pendinglen = stream.read(buf); + if (pendinglen == 0) // should never happen + { + throw new PngjInputException("This should not happen: stream.read(buf) returned 0"); + } else if (pendinglen < 0) { + close(); // this sets EOF and pendinglen=0 + } else { + bytesRead += pendinglen; + } + } catch (IOException e) { + throw new PngjInputException(e); + } + // on return, either pendinglen > 0 or eof == true + } + + /** + * Returuns true if we have more data to fed the consumer. This might try to + * grab more bytes from the stream if necessary + */ + public boolean hasPendingBytes() { + refillBufferIfAppropiate(); + return pendinglen > 0; + } + + /** + * @param closeStream If true, the underlying stream will be closed on when close() + * is called + */ + public void setCloseStream(boolean closeStream) { + this.closeStream = closeStream; + } + + /** + * Closes this object. + *

      + * Sets EOF=true, and closes the stream if closeStream is true + *

      + * This can be called internally, or from outside. + *

      + * Idempotent, secure, never throws exception. + **/ + public void close() { + eof = true; + buf = null; + pendinglen = 0; + offset = 0; + if (stream != null && closeStream) { + try { + stream.close(); + } catch (Exception e) { + // PngHelperInternal.LOGGER.log(Level.WARNING, "Exception closing stream", e); + } + } + stream = null; + } + + /** + * Sets a new underlying inputstream. This allows to reuse this object. The + * old underlying is not closed and the state is not reset (you should call + * close() previously if you want that) + * + * @param is + */ + public void setInputStream(InputStream is) { // to reuse this object + this.stream = is; + eof = false; + } + + /** + * @return EOF on stream, or close() was called + */ + public boolean isEof() { + return eof; + } + + public long getBytesRead() { + return bytesRead; + } + +} diff --git a/png/src/main/java/org/xbib/graphics/imageio/plugins/png/pngj/ChunkReader.java b/png/src/main/java/org/xbib/graphics/imageio/plugins/png/pngj/ChunkReader.java new file mode 100644 index 0000000..29c790c --- /dev/null +++ b/png/src/main/java/org/xbib/graphics/imageio/plugins/png/pngj/ChunkReader.java @@ -0,0 +1,258 @@ +package org.xbib.graphics.imageio.plugins.png.pngj; + +import java.util.logging.Logger; +import org.xbib.graphics.imageio.plugins.png.pngj.chunks.ChunkRaw; + +/** + * Parses a PNG chunk, consuming bytes in one of three modes: + * {@link ChunkReaderMode#BUFFER}, {@link ChunkReaderMode#PROCESS}, + * {@link ChunkReaderMode#SKIP}. + *

      + * It calls {@link #chunkDone()} when done. Also calls + * {@link #processData(byte[], int, int)} if PROCESS mode. Apart + * from thas, it's totally agnostic (it doesn't know about IDAT chunks, or PNG + * general structure) + *

      + * The object wraps a ChunkRaw instance (content allocated and filled + * only if BUFFER mode). It should be short lived (one instance created for each + * chunk, and discarded after reading), but the wrapped chunkRaw can be + * (usually is) long lived. + */ +public abstract class ChunkReader implements IBytesConsumer { + private static final Logger LOGGER = Logger.getLogger(ChunkReader.class.getName()); + + /** + * see {@link ChunkReaderMode} + */ + public final ChunkReaderMode mode; + private final ChunkRaw chunkRaw; + + /** + * How many bytes have been read for this chunk, data only + */ + protected int read = 0; + private int crcn = 0; // how many bytes have been read from crc + + private boolean crcCheck; // by default, this is false for SKIP, true elsewhere + protected ErrorBehaviour errorBehav = ErrorBehaviour.STRICT; + + /** + * Modes of ChunkReader chunk processing. + */ + public enum ChunkReaderMode { + /** + * Stores full chunk data in buffer + */ + BUFFER, + /** + * Does not store content, processes on the fly, calling processData() + * for each partial read + */ + PROCESS, + /** + * Does not store nor process - implies crcCheck=false (by default). + */ + SKIP + } + + /** + * The constructor creates also a chunkRaw, preallocated if mode = + * ChunkReaderMode.BUFFER + * + * @param clen + * @param id + * @param offsetInPng Informational, is stored in chunkRaw + * @param mode + */ + public ChunkReader(int clen, String id, long offsetInPng, ChunkReaderMode mode) { + if (mode == null || id.length() != 4 || clen < 0) { + throw new PngjInputException("Bad chunk paramenters: " + mode); + } + this.mode = mode; + chunkRaw = new ChunkRaw(clen, id, mode == ChunkReaderMode.BUFFER); + chunkRaw.setOffset(offsetInPng); + this.crcCheck = mode != ChunkReaderMode.SKIP; // can be changed with setter + // PngHelperInternal.debug("ChunkReader " + this.getClass() + " id="+id + " mode:"+mode); + } + + /** + * Returns raw chunk (data can be empty or not, depending on + * ChunkReaderMode) + * + * @return Raw chunk - never null + */ + public ChunkRaw getChunkRaw() { + return chunkRaw; + } + + /** + * Consumes data for the chunk (data and CRC). This only consumes bytes + * owned by this chunk (data + crc , not id+len prefix) + *

      + * In ChunkReaderMode.PROCESS can call processData() (not more than once) + *

      + * If this ends the chunk (included CRC) it checks CRC (if checking) and + * calls chunkDone() + * + * @param buf + * @param off + * @param len + * @return How many bytes have been consumed + */ + public final int consume(byte[] buf, int off, int len) { + if (len == 0) { + return 0; + } + if (len < 0) { + throw new PngjException("negative length??"); + } + if (read == 0 && crcn == 0 && crcCheck) { + chunkRaw.updateCrc(chunkRaw.idbytes, 0, 4); // initializes crc calculation with the Chunk ID + } + int bytesForData = chunkRaw.len - read; // bytesForData : bytes to be actually read from chunk data + if (bytesForData > len) { + bytesForData = len; + } + // we want to call processData even for empty chunks (IEND:bytesForData=0) at + // least once + if (bytesForData > 0 || crcn == 0) { + // in buffer mode we compute the CRC at the end + if (crcCheck && mode != ChunkReaderMode.BUFFER && bytesForData > 0) { + chunkRaw.updateCrc(buf, off, bytesForData); + } + + if (mode == ChunkReaderMode.BUFFER) { + // just copy the contents to the internal buffer + if (chunkRaw.data != buf && bytesForData > 0) { + // If the buffer passed if the same as this one, we don't copy. + // The caller should know what he's doing + System.arraycopy(buf, off, chunkRaw.data, read, bytesForData); + } + } else if (mode == ChunkReaderMode.PROCESS) { + processData(read, buf, off, bytesForData); + } else { + // mode == ChunkReaderMode.SKIP; nothing to do + } + read += bytesForData; + off += bytesForData; + len -= bytesForData; + } + int crcRead = 0; + if (read == chunkRaw.len) { // data done - read crc? + crcRead = 4 - crcn; + if (crcRead > len) { + crcRead = len; + } + if (crcRead > 0) { + if (buf != chunkRaw.crcval) { + System.arraycopy(buf, off, chunkRaw.crcval, crcn, crcRead); + } + crcn += crcRead; + if (crcn == 4) { + if (crcCheck) { + if (mode == ChunkReaderMode.BUFFER) { // in buffer mode we compute the CRC on one single call + chunkRaw.updateCrc(chunkRaw.data, 0, chunkRaw.len); + } + chunkRaw.checkCrc(errorBehav == ErrorBehaviour.STRICT); + } + LOGGER.fine("Chunk done"); + chunkDone(); + } + } + } + return bytesForData > 0 || crcRead > 0 ? bytesForData + crcRead : -1 /* should not happen */; + } + + /** + * Chunks has been read + * + * @return true if we have read all chunk, including trailing CRC + */ + public final boolean isDone() { + return crcn == 4; // has read all 4 bytes from the crc + } + + /** + * Determines if CRC should be checked. This should be called before + * starting reading. + * + * @param crcCheck + * @see also #setErrorBehav(ErrorBehaviour) + */ + public void setCrcCheck(boolean crcCheck) { + if (read != 0 && crcCheck && !this.crcCheck) { + throw new PngjException("too late!"); + } + this.crcCheck = crcCheck; + } + + /** + * This method will only be called in PROCESS mode, probably several times, + * each time with a new fragment of data read from inside the chunk. For + * chunks with zero-length data, this will still be called once. + *

      + * It's guaranteed that the data corresponds exclusively to this chunk data + * (no crc, no data from no other chunks, ) + * + * @param offsetInchunk data bytes that had already been read/processed for this chunk + * @param buf + * @param off + * @param len + */ + protected abstract void processData(int offsetInchunk, byte[] buf, int off, int len); + + /** + * This method will be called (in all modes) when the full chunk -including + * crc- has been read + */ + protected abstract void chunkDone(); + + public boolean isFromDeflatedSet() { + return false; + } + + public ErrorBehaviour getErrorBehav() { + return errorBehav; + } + + /** + * see also {@link #setCrcCheck(boolean)} + **/ + public void setErrorBehav(ErrorBehaviour errorBehav) { + this.errorBehav = errorBehav; + } + + @Override + public int hashCode() { + final int prime = 31; + int result = 1; + result = prime * result + ((chunkRaw == null) ? 0 : chunkRaw.hashCode()); + return result; + } + + /** + * Equality (and hash) is basically delegated to the ChunkRaw + */ + @Override + public boolean equals(Object obj) { // delegates to chunkraw + if (this == obj) { + return true; + } + if (obj == null) { + return false; + } + if (getClass() != obj.getClass()) { + return false; + } + ChunkReader other = (ChunkReader) obj; + if (chunkRaw == null) { + return other.chunkRaw == null; + } else return chunkRaw.equals(other.chunkRaw); + } + + @Override + public String toString() { + return chunkRaw.toString(); + } + +} diff --git a/png/src/main/java/org/xbib/graphics/imageio/plugins/png/pngj/ChunkSeqBuffering.java b/png/src/main/java/org/xbib/graphics/imageio/plugins/png/pngj/ChunkSeqBuffering.java new file mode 100644 index 0000000..3b6d35d --- /dev/null +++ b/png/src/main/java/org/xbib/graphics/imageio/plugins/png/pngj/ChunkSeqBuffering.java @@ -0,0 +1,35 @@ +package org.xbib.graphics.imageio.plugins.png.pngj; + +/** + * This loads the png as a plain sequence of chunks, buffering all + *

      + * Useful to do things like insert or delete a ancilllary chunk. This does not + * distinguish IDAT from others + **/ +public class ChunkSeqBuffering extends ChunkSeqReader { + protected boolean checkCrc = true; + + public ChunkSeqBuffering() { + super(); + } + + @Override + protected boolean isIdatKind(String id) { + return false; + } + + @Override + protected boolean shouldCheckCrc(int len, String id) { + return checkCrc; + } + + public void setCheckCrc(boolean checkCrc) { + this.checkCrc = checkCrc; + } + + @Override + protected DeflatedChunksSet createIdatSet(String id) { + throw new RuntimeException("Should not get here"); + } + +} diff --git a/png/src/main/java/org/xbib/graphics/imageio/plugins/png/pngj/ChunkSeqReader.java b/png/src/main/java/org/xbib/graphics/imageio/plugins/png/pngj/ChunkSeqReader.java new file mode 100644 index 0000000..939ca89 --- /dev/null +++ b/png/src/main/java/org/xbib/graphics/imageio/plugins/png/pngj/ChunkSeqReader.java @@ -0,0 +1,473 @@ +package org.xbib.graphics.imageio.plugins.png.pngj; + +import java.io.Closeable; +import java.io.File; +import java.io.FileInputStream; +import java.io.FileNotFoundException; +import java.io.InputStream; +import java.util.Arrays; +import java.util.logging.Level; +import java.util.logging.Logger; +import org.xbib.graphics.imageio.plugins.png.pngj.ChunkReader.ChunkReaderMode; +import org.xbib.graphics.imageio.plugins.png.pngj.chunks.ChunkHelper; + +/** + * Consumes a stream of bytes that consist of a series of PNG-like chunks. + *

      + * This has little intelligence, it's quite low-level and general (it could even + * be used for a MNG stream, for example). It supports signature recognition and + * idat deflate + */ +public abstract class ChunkSeqReader implements IBytesConsumer, Closeable { + private static final Logger LOGGER = Logger.getLogger(ChunkSeqReader.class.getName()); + + private final byte[] expectedSignature; + private final int signatureLength; + + private final byte[] buf0 = new byte[8]; // for signature or chunk starts + private int buf0len = 0; + + protected boolean signatureDone = false; + protected boolean endChunkDone = false; + protected boolean closed = false; // ended, normally or not + + private int chunkCount = 0; + + private long bytesCount = 0; + + private DeflatedChunksSet curDeflatedSet; // one instance for each "idat-like set". Normally one. + + private ChunkReader curChunkReader; + + private long idatBytes; // this is only for the IDAT (not mrerely "idat-like") + + private ErrorBehaviour errorBehaviour = ErrorBehaviour.STRICT; + + /** + * Creates a ChunkSeqReader (with signature) + */ + public ChunkSeqReader() { + this(PngHelperInternal.getPngIdSignature()); + } + + /** + * Pass _expectedSignature = null (or empty) for no signature + */ + public ChunkSeqReader(byte[] _expectedSignature) { + this.expectedSignature = _expectedSignature; + signatureLength = expectedSignature == null ? 0 : expectedSignature.length; + signatureDone = signatureLength <= 0; + } + + /** + * Consumes (in general, partially) a number of bytes. A single call never + * involves more than one chunk. + *

      + * When the signature is read, it calls checkSignature() + *

      + * When the start of a chunk is detected, it calls + * {@link #startNewChunk(int, String, long)} + *

      + * When data from a chunk is being read, it delegates to + * {@link ChunkReader#feedBytes(byte[], int, int)} + *

      + * The caller might want to call this method more than once in succesion + *

      + * This should rarely be overriden + * + * @param buffer + * @param offset Offset in buffer + * @param len Valid bytes that can be consumed + * @return processed bytes, in the 1-len range. -1 if done. Only returns 0 + * if len=0. + **/ + @Override + public int consume(byte[] buffer, int offset, int len) { + if (closed) { + return -1; + } + if (len == 0) { + return 0; // nothing to do (should not happen) + } + if (len < 0) { + throw new PngjInputException("This should not happen. Bad length: " + len); + } + int processed = 0; + if (signatureDone) { + if (curChunkReader == null || curChunkReader.isDone()) { // new chunk: read first 8 bytes + int read0 = 8 - buf0len; + if (read0 > len) { + read0 = len; + } + System.arraycopy(buffer, offset, buf0, buf0len, read0); + buf0len += read0; + processed += read0; + bytesCount += read0; + // len -= read0; + // offset += read0; + if (buf0len == 8) { // end reading chunk length and id + chunkCount++; + int clen = PngHelperInternal.readInt4fromBytes(buf0, 0); + String cid = ChunkHelper.idFromBytes(buf0, 4); + startNewChunk(clen, cid, bytesCount - 8); + buf0len = 0; + } + } else { // reading chunk, delegates to curChunkReader + int read1 = curChunkReader.consume(buffer, offset, len); + if (read1 < 0) { + return -1; // should not happen + } + processed += read1; + bytesCount += read1; + } + } else { // reading signature + int read = signatureLength - buf0len; + if (read > len) { + read = len; + } + System.arraycopy(buffer, offset, buf0, buf0len, read); + buf0len += read; + if (buf0len == signatureLength) { + checkSignature(buf0); + buf0len = 0; + signatureDone = true; + } + processed += read; + bytesCount += read; + } + return processed; + } + + /** + * Trys to feeds exactly len bytes, calling + * {@link #consume(byte[], int, int)} retrying if necessary. + *

      + * This should only be used in callback mode + * + * @return Excess bytes (not feed). Normally should return 0 + */ + public int feedAll(byte[] buf, int off, int len) { + while (len > 0) { + int n = consume(buf, off, len); + if (n < 1) { + return len; + } + len -= n; + off += n; + } + assert len == 0; + return 0; + } + + /** + * Called for all chunks when a chunk start has been read (id and length), + * before the chunk data itself is read. It creates a new ChunkReader (field + * accesible via {@link #getCurChunkReader()}) in the corresponding mode, + * and eventually a curReaderDeflatedSet.(field accesible via + * {@link #getCurDeflatedSet()}) + *

      + * To decide the mode and options, it calls + * {@link #shouldCheckCrc(int, String)}, + * {@link #shouldSkipContent(int, String)}, {@link #isIdatKind(String)}. + * Those methods should be overriden in preference to this; if overriden, + * this should be called first. + *

      + * The respective {@link ChunkReader#chunkDone()} method is directed to this + * {@link #postProcessChunk(ChunkReader)}. + *

      + * Instead of overriding this, see also + * {@link #createChunkReaderForNewChunk(String, int, long, boolean)} + */ + protected void startNewChunk(int len, String id, long offset) { + if (LOGGER.isLoggable(Level.FINE)) { + LOGGER.fine("New chunk: " + id + " " + len + " off:" + offset); + } + // check id an length + if (id.length() != 4 || !ChunkHelper.CHUNK_ID_PAT.matcher(id).matches()) { + throw new PngjInputException("Bad chunk id: " + id); + } + if (len < 0) { + throw new PngjInputException("Bad chunk len: " + len); + } + if (id.equals(ChunkHelper.IDAT)) { + idatBytes += len; + } + boolean checkCrc = shouldCheckCrc(len, id); + boolean skip = shouldSkipContent(len, id); + boolean isIdatType = isIdatKind(id); + // PngHelperInternal.debug("start new chunk id=" + id + " off=" + offset + " + // skip=" + skip + " idat=" + + // isIdatType); + // first see if we should terminate an active curReaderDeflatedSet + boolean forCurrentIdatSet = false; + if (curDeflatedSet != null && !curDeflatedSet.isClosed()) { + forCurrentIdatSet = curDeflatedSet.ackNextChunkId(id); + } + if (isIdatType && !skip) { // IDAT non skipped: create a DeflatedChunkReader owned by a idatSet + if (!forCurrentIdatSet) { + if (curDeflatedSet != null && !curDeflatedSet.isDone()) { + throw new PngjInputException("new IDAT-like chunk when previous was not done"); + } + curDeflatedSet = createIdatSet(id); + } + curChunkReader = new DeflatedChunkReader(len, id, checkCrc, offset, curDeflatedSet) { + @Override + protected void chunkDone() { + super.chunkDone(); + postProcessChunk(this); + } + }; + + } else { // for non-idat chunks (or skipped idat like) + curChunkReader = createChunkReaderForNewChunk(id, len, offset, skip); + } + if (curChunkReader != null && !checkCrc) { + curChunkReader.setCrcCheck(false); + } + } + + /** + * This will be called for all chunks (even skipped), except for IDAT-like + * non-skiped chunks + *

      + * The default behaviour is to create a ChunkReader in BUFFER mode (or SKIP + * if skip==true) that calls {@link #postProcessChunk(ChunkReader)} (always) + * when done. + * + * @param id Chunk id + * @param len Chunk length + * @param offset offset inside PNG stream , merely informative + * @param skip flag: is true, the content will not be buffered (nor + * processed) + * @return a newly created ChunkReader that will create the ChunkRaw and + * then discarded + */ + protected ChunkReader createChunkReaderForNewChunk(String id, int len, long offset, boolean skip) { + return new ChunkReader(len, id, offset, skip ? ChunkReaderMode.SKIP : ChunkReaderMode.BUFFER) { + @Override + protected void chunkDone() { + postProcessChunk(this); + } + + @Override + protected void processData(int offsetinChhunk, byte[] buf, int off, int len) { + throw new PngjExceptionInternal("should never happen"); + } + }; + } + + /** + * This is called after a chunk is read, in all modes + *

      + * This implementation only chenks the id of the first chunk, and process + * the IEND chunk (sets done=true) + * * + * Further processing should be overriden (call this first!) + **/ + protected void postProcessChunk(ChunkReader chunkR) { // called after chunk is read + if (chunkCount == 1) { + // first chunk ? + String cid = firstChunkId(); + if (cid != null && !cid.equals(chunkR.getChunkRaw().id)) { + String msg = "Bad first chunk: " + chunkR.getChunkRaw().id + " expected: " + firstChunkId(); + if (errorBehaviour.c < ErrorBehaviour.SUPER_LENIENT.c) { + throw new PngjInputException(msg); + } else { + LOGGER.warning(msg); + } + } + } + if (endChunkId() != null && chunkR.getChunkRaw().id.equals(endChunkId())) { + // last chunk ? + endChunkDone = true; + close(); + } + } + + /** + * DeflatedChunksSet factory. This implementation is quite dummy, it usually + * should be overriden. + */ + protected abstract DeflatedChunksSet createIdatSet(String id); + + /** + * Decides if this Chunk is of "IDAT" kind (in concrete: if it is, and if + * it's not to be skiped, a DeflatedChunksSet will be created to deflate it + * and process+ the deflated data) + *

      + * This implementation always returns always false + * + * @param id + */ + protected boolean isIdatKind(String id) { + return false; + } + + /** + * Chunks can be skipped depending on id and/or length. Skipped chunks are + * still processed, but their data will be null, and CRC will never checked + * + * @param len + * @param id + */ + protected boolean shouldSkipContent(int len, String id) { + return false; + } + + protected boolean shouldCheckCrc(int len, String id) { + return true; + } + + /** + * Throws PngjInputException if bad signature + * + * @param buf Signature. Should be of length 8 + */ + protected void checkSignature(byte[] buf) { + if (!Arrays.equals(buf, PngHelperInternal.getPngIdSignature())) { + throw new PngjBadSignature("Bad signature:" + Arrays.toString(buf)); + } + } + + /** + * If false, we are still reading the signature + * + * @return true if signature has been read (or if we don't have signature) + */ + public boolean isSignatureDone() { + return signatureDone; + } + + /** + * If true, we have processe the IEND chunk + */ + @Override + public boolean isDone() { + return endChunkDone; + } + + /** + * Terminated, normally or not - If true, this will not accept more bytes + */ + public boolean isClosed() { + return closed; + } + + /** + * total of bytes read (buffered or not) + */ + public long getBytesCount() { + return bytesCount; + } + + /** + * @return Chunks already read, including partial reading (currently + * reading) + */ + public int getChunkCount() { + return chunkCount; + } + + /** + * Currently reading chunk, or just ended reading + * + * @return null only if still reading signature + */ + public ChunkReader getCurChunkReader() { + return curChunkReader; + } + + /** + * The latest deflated set (typically IDAT chunks) reader. Notice that there + * could be several idat sets (eg for APNG) + */ + public DeflatedChunksSet getCurDeflatedSet() { + return curDeflatedSet; + } + + /** + * Closes this object and release resources. For normal termination or + * abort. Secure and idempotent. + */ + public void close() { // forced closing + if (curDeflatedSet != null) { + curDeflatedSet.close(); + } + closed = true; + } + + /** + * Returns true if we are not in middle of a chunk: we have just ended + * reading past chunk , or we are at the start, or end of signature, or we + * are done + */ + public boolean isAtChunkBoundary() { + return bytesCount == 0 || bytesCount == 8 || closed || curChunkReader == null || curChunkReader.isDone(); + } + + /** + * Which should be the id of the first chunk. This will be checked + * + * @return null if you don't want to check it + */ + protected String firstChunkId() { + return "IHDR"; + } + + /** + * Helper method, reports amount of bytes inside IDAT chunks. + * + * @return Bytes in IDAT chunks + */ + public long getIdatBytes() { + return idatBytes; + } + + /** + * Which should be the id of the last chunk + */ + protected String endChunkId() { + return "IEND"; + } + + /** + * Reads all content from a file. Helper method, only for callback mode + */ + public void feedFromFile(File f) { + try { + feedFromInputStream(new FileInputStream(f), true); + } catch (FileNotFoundException e) { + throw new PngjInputException("Error reading from file '" + f + "' :" + e.getMessage()); + } + } + + /** + * Reads all content from an input stream. Helper method, only for callback + * mode + *

      + * Caller should call isDone() to assert all expected chunks have been read + *

      + * Warning: this does not close this object, unless ended + * + * @param is + * @param closeStream Closes the input stream when done (or if error) + */ + public void feedFromInputStream(InputStream is, boolean closeStream) { + BufferedStreamFeeder sf = new BufferedStreamFeeder(is); + sf.setCloseStream(closeStream); + try { + sf.feedAll(this); + } finally { + sf.close(); + } + } + + public void feedFromInputStream(InputStream is) { + feedFromInputStream(is, true); + } + + public void setErrorBehaviour(ErrorBehaviour errorBehaviour) { + this.errorBehaviour = errorBehaviour; + } + +} diff --git a/png/src/main/java/org/xbib/graphics/imageio/plugins/png/pngj/ChunkSeqReaderPng.java b/png/src/main/java/org/xbib/graphics/imageio/plugins/png/pngj/ChunkSeqReaderPng.java new file mode 100644 index 0000000..e1289db --- /dev/null +++ b/png/src/main/java/org/xbib/graphics/imageio/plugins/png/pngj/ChunkSeqReaderPng.java @@ -0,0 +1,341 @@ +package org.xbib.graphics.imageio.plugins.png.pngj; + +import java.util.HashSet; +import java.util.List; +import java.util.Set; +import org.xbib.graphics.imageio.plugins.png.pngj.ChunkReader.ChunkReaderMode; +import org.xbib.graphics.imageio.plugins.png.pngj.chunks.ChunkFactory; +import org.xbib.graphics.imageio.plugins.png.pngj.chunks.ChunkHelper; +import org.xbib.graphics.imageio.plugins.png.pngj.chunks.ChunkLoadBehaviour; +import org.xbib.graphics.imageio.plugins.png.pngj.chunks.ChunksList; +import org.xbib.graphics.imageio.plugins.png.pngj.chunks.PngChunk; +import org.xbib.graphics.imageio.plugins.png.pngj.chunks.PngChunkIDAT; +import org.xbib.graphics.imageio.plugins.png.pngj.chunks.PngChunkIEND; +import org.xbib.graphics.imageio.plugins.png.pngj.chunks.PngChunkIHDR; +import org.xbib.graphics.imageio.plugins.png.pngj.chunks.PngChunkPLTE; + +/** + * Adds to ChunkSeqReader the storing of PngChunk, with a PngFactory, and + * imageInfo + deinterlacer. + *

      + * Most usual PNG reading should use this class, or a {@link PngReader}, which + * is a thin wrapper over this. + */ +public class ChunkSeqReaderPng extends ChunkSeqReader { + + protected ImageInfo imageInfo; // initialized at parsing the IHDR + protected ImageInfo curImageInfo; // can vary, for apng + protected Deinterlacer deinterlacer; + + /** + * From 0 to 6, see ChunksList CHUNK_GROUP_* + */ + protected int currentChunkGroup = -1; + + /** + * All chunks, but some of them can have the buffer empty (IDAT and skipped) + */ + protected ChunksList chunksList = null; + protected final boolean callbackMode; + private long bytesAncChunksLoaded = 0; // bytes loaded from buffered chunks non-critical chunks (data only) + + private boolean checkCrc = true; + + // --- parameters to be set prior to reading --- + private boolean includeNonBufferedChunks = false; + + private final Set chunksToSkip = new HashSet(); + private long maxTotalBytesRead = 0; + private long skipChunkMaxSize = 0; + private long maxBytesMetadata = 0; + private IChunkFactory chunkFactory; + private ChunkLoadBehaviour chunkLoadBehaviour = ChunkLoadBehaviour.LOAD_CHUNK_ALWAYS; + + public ChunkSeqReaderPng(boolean callbackMode) { + super(); + this.callbackMode = callbackMode; + chunkFactory = new ChunkFactory(); // default factory + } + + // call just after beginging new chunk parse + private void updateAndCheckChunkGroup(String id) { + if (id.equals(PngChunkIHDR.ID)) { // IDHR + if (currentChunkGroup < 0) { + currentChunkGroup = ChunksList.CHUNK_GROUP_0_IDHR; + } else { + throw new PngjInputException("unexpected chunk " + id); + } + } else if (id.equals(PngChunkPLTE.ID)) { // PLTE + if ((currentChunkGroup == ChunksList.CHUNK_GROUP_0_IDHR + || currentChunkGroup == ChunksList.CHUNK_GROUP_1_AFTERIDHR)) { + currentChunkGroup = ChunksList.CHUNK_GROUP_2_PLTE; + } else { + throw new PngjInputException("unexpected chunk here " + id); + } + } else if (id.equals(PngChunkIDAT.ID)) { // IDAT (no necessarily the first) + if ((currentChunkGroup >= ChunksList.CHUNK_GROUP_0_IDHR + && currentChunkGroup <= ChunksList.CHUNK_GROUP_4_IDAT)) { + currentChunkGroup = ChunksList.CHUNK_GROUP_4_IDAT; + } else { + throw new PngjInputException("unexpected chunk " + id); + } + } else if (id.equals(PngChunkIEND.ID)) { // END + if ((currentChunkGroup >= ChunksList.CHUNK_GROUP_4_IDAT)) { + currentChunkGroup = ChunksList.CHUNK_GROUP_6_END; + } else { + throw new PngjInputException("unexpected chunk " + id); + } + } else { // ancillary + if (currentChunkGroup <= ChunksList.CHUNK_GROUP_1_AFTERIDHR) { + currentChunkGroup = ChunksList.CHUNK_GROUP_1_AFTERIDHR; + } else if (currentChunkGroup <= ChunksList.CHUNK_GROUP_3_AFTERPLTE) { + currentChunkGroup = ChunksList.CHUNK_GROUP_3_AFTERPLTE; + } else { + currentChunkGroup = ChunksList.CHUNK_GROUP_5_AFTERIDAT; + } + } + } + + @Override + public boolean shouldSkipContent(int len, String id) { + if (super.shouldSkipContent(len, id)) { + return true; + } + if (maxTotalBytesRead > 0 && len + getBytesCount() > maxTotalBytesRead) { + throw new PngjInputException("Maximum total bytes to read exceeeded: " + maxTotalBytesRead + " offset:" + + getBytesCount() + " len=" + len); + } + if (chunksToSkip.contains(id)) { + return true; // specific skip + } + if (ChunkHelper.isCritical(id)) { + return false;// critical chunks are never skipped + } + if (skipChunkMaxSize > 0 && len > skipChunkMaxSize) { + return true; // too big chunk + } + if (maxBytesMetadata > 0 && len > maxBytesMetadata - bytesAncChunksLoaded) { + return true; // too much ancillary chunks loaded + } + switch (chunkLoadBehaviour) { + case LOAD_CHUNK_IF_SAFE: + if (!ChunkHelper.isSafeToCopy(id)) { + return true; + } + break; + case LOAD_CHUNK_NEVER: + return true; + default: + break; + } + return false; + } + + public long getBytesChunksLoaded() { + return bytesAncChunksLoaded; + } + + public int getCurrentChunkGroup() { + return currentChunkGroup; + } + + public void setChunksToSkip(String... chunksToSkip) { + this.chunksToSkip.clear(); + for (String c : chunksToSkip) { + this.chunksToSkip.add(c); + } + } + + public void addChunkToSkip(String chunkToSkip) { + this.chunksToSkip.add(chunkToSkip); + } + + public void dontSkipChunk(String chunkToSkip) { + this.chunksToSkip.remove(chunkToSkip); + } + + public boolean firstChunksNotYetRead() { + return getCurrentChunkGroup() < ChunksList.CHUNK_GROUP_4_IDAT; + } + + @Override + protected void postProcessChunk(ChunkReader chunkR) { + // PngHelperInternal.debug("postProcessChunk " + chunkR.getChunkRaw().id); + super.postProcessChunk(chunkR); + if (chunkR.getChunkRaw().id.equals(PngChunkIHDR.ID)) { + PngChunkIHDR ch = new PngChunkIHDR(null); + ch.parseFromRaw(chunkR.getChunkRaw()); + imageInfo = ch.createImageInfo(); + curImageInfo = imageInfo; + if (ch.isInterlaced()) { + deinterlacer = new Deinterlacer(curImageInfo); + } + chunksList = new ChunksList(imageInfo); + } + if (chunkR.mode == ChunkReaderMode.BUFFER && countChunkTypeAsAncillary(chunkR.getChunkRaw().id)) { + bytesAncChunksLoaded += chunkR.getChunkRaw().len; + } + if (chunkR.mode == ChunkReaderMode.BUFFER || includeNonBufferedChunks) { + try { + PngChunk chunk = chunkFactory.createChunk(chunkR.getChunkRaw(), getImageInfo()); + chunksList.appendReadChunk(chunk, currentChunkGroup); + } catch (PngjException e) { + throw e; + } + } + if (isDone()) { + processEndPng(); + } + } + + protected boolean countChunkTypeAsAncillary(String id) { + return !ChunkHelper.isCritical(id); + } + + @Override + protected DeflatedChunksSet createIdatSet(String id) { + IdatSet ids = new IdatSet(id, callbackMode, getCurImgInfo(), deinterlacer); + return ids; + } + + public IdatSet getIdatSet() { + DeflatedChunksSet c = getCurDeflatedSet(); + return c instanceof IdatSet ? (IdatSet) c : null; + } + + @Override + protected boolean isIdatKind(String id) { + return id.equals(PngChunkIDAT.ID); + } + + @Override + public int consume(byte[] buf, int off, int len) { + return super.consume(buf, off, len); + } + + /** + * sets a custom chunk factory. This is typically called with a custom class + * extends ChunkFactory, to adds custom chunks to the default well-know ones + * + * @param chunkFactory + */ + public void setChunkFactory(IChunkFactory chunkFactory) { + this.chunkFactory = chunkFactory; + } + + /** + * Things to be done after closed. + */ + protected void processEndPng() { + // nothing to do + } + + public ImageInfo getImageInfo() { + return imageInfo; + } + + public boolean isInterlaced() { + return deinterlacer != null; + } + + public Deinterlacer getDeinterlacer() { + return deinterlacer; + } + + @Override + protected void startNewChunk(int len, String id, long offset) { + updateAndCheckChunkGroup(id); + super.startNewChunk(len, id, offset); + } + + @Override + public void close() { + if (currentChunkGroup != ChunksList.CHUNK_GROUP_6_END)// this could only happen if forced close + { + currentChunkGroup = ChunksList.CHUNK_GROUP_6_END; + } + super.close(); + } + + public List getChunks() { + return chunksList.getChunks(); + } + + public void setMaxTotalBytesRead(long maxTotalBytesRead) { + this.maxTotalBytesRead = maxTotalBytesRead; + } + + public long getSkipChunkMaxSize() { + return skipChunkMaxSize; + } + + public void setSkipChunkMaxSize(long skipChunkMaxSize) { + this.skipChunkMaxSize = skipChunkMaxSize; + } + + public long getMaxBytesMetadata() { + return maxBytesMetadata; + } + + public void setMaxBytesMetadata(long maxBytesMetadata) { + this.maxBytesMetadata = maxBytesMetadata; + } + + public long getMaxTotalBytesRead() { + return maxTotalBytesRead; + } + + @Override + protected boolean shouldCheckCrc(int len, String id) { + return checkCrc; + } + + public boolean isCheckCrc() { + return checkCrc; + } + + public void setCheckCrc(boolean checkCrc) { + this.checkCrc = checkCrc; + } + + public boolean isCallbackMode() { + return callbackMode; + } + + public Set getChunksToSkip() { + return chunksToSkip; + } + + public void setChunkLoadBehaviour(ChunkLoadBehaviour chunkLoadBehaviour) { + this.chunkLoadBehaviour = chunkLoadBehaviour; + } + + public ImageInfo getCurImgInfo() { + return curImageInfo; + } + + public void updateCurImgInfo(ImageInfo iminfo) { + if (!iminfo.equals(curImageInfo)) { + curImageInfo = iminfo; + } + if (deinterlacer != null) { + deinterlacer = new Deinterlacer(curImageInfo); // we could reset it, but... + } + } + + /** + * If true, the chunks with no data (because skipped or because processed + * like IDAT-type) are still stored in the PngChunks list, which might be + * more informative. + *

      + * Setting this to false saves a few bytes + *

      + * Default: false + * + * @param includeNonBufferedChunks + */ + public void setIncludeNonBufferedChunks(boolean includeNonBufferedChunks) { + this.includeNonBufferedChunks = includeNonBufferedChunks; + } + +} diff --git a/png/src/main/java/org/xbib/graphics/imageio/plugins/png/pngj/ChunkSeqSkipping.java b/png/src/main/java/org/xbib/graphics/imageio/plugins/png/pngj/ChunkSeqSkipping.java new file mode 100644 index 0000000..af8ff0d --- /dev/null +++ b/png/src/main/java/org/xbib/graphics/imageio/plugins/png/pngj/ChunkSeqSkipping.java @@ -0,0 +1,76 @@ +package org.xbib.graphics.imageio.plugins.png.pngj; + +import java.util.ArrayList; +import java.util.List; +import org.xbib.graphics.imageio.plugins.png.pngj.ChunkReader.ChunkReaderMode; +import org.xbib.graphics.imageio.plugins.png.pngj.chunks.ChunkRaw; + +/** + * This simple reader skips all chunks contents and stores the chunkRaw in a + * list. Useful to read chunks structure. + *

      + * Optionally the contents might be processed. This doesn't distinguish IDAT + * chunks + */ +public class ChunkSeqSkipping extends ChunkSeqReader { + + private final List chunks = new ArrayList(); + private boolean skip = true; + + /** + * @param skipAll if true, contents will be truly skipped, and CRC will not be + * computed + */ + public ChunkSeqSkipping(boolean skipAll) { + super(); + skip = skipAll; + } + + public ChunkSeqSkipping() { + this(true); + } + + protected ChunkReader createChunkReaderForNewChunk(String id, int len, long offset, boolean skip) { + return new ChunkReader(len, id, offset, skip ? ChunkReaderMode.SKIP : ChunkReaderMode.PROCESS) { + @Override + protected void chunkDone() { + postProcessChunk(this); + } + + @Override + protected void processData(int offsetinChhunk, byte[] buf, int off, int len) { + processChunkContent(getChunkRaw(), offsetinChhunk, buf, off, len); + } + }; + } + + protected void processChunkContent(ChunkRaw chunkRaw, int offsetinChhunk, byte[] buf, int off, int len) { + // does nothing + } + + @Override + protected void postProcessChunk(ChunkReader chunkR) { + super.postProcessChunk(chunkR); + chunks.add(chunkR.getChunkRaw()); + } + + @Override + protected boolean shouldSkipContent(int len, String id) { + return skip; + } + + @Override + protected boolean isIdatKind(String id) { + return false; + } + + public List getChunks() { + return chunks; + } + + @Override + protected DeflatedChunksSet createIdatSet(String id) { + throw new RuntimeException("Should not get here"); + } + +} diff --git a/png/src/main/java/org/xbib/graphics/imageio/plugins/png/pngj/DeflatedChunkReader.java b/png/src/main/java/org/xbib/graphics/imageio/plugins/png/pngj/DeflatedChunkReader.java new file mode 100644 index 0000000..3e1254c --- /dev/null +++ b/png/src/main/java/org/xbib/graphics/imageio/plugins/png/pngj/DeflatedChunkReader.java @@ -0,0 +1,88 @@ +package org.xbib.graphics.imageio.plugins.png.pngj; + +import org.xbib.graphics.imageio.plugins.png.pngj.chunks.PngChunkFDAT; + +/** + * Specialization of ChunkReader, for IDAT-like chunks. These chunks are part of + * a set of similar chunks (contiguos normally, not necessariyl) which conforms + * a zlib stream + */ +public abstract class DeflatedChunkReader extends ChunkReader { + + protected final DeflatedChunksSet deflatedChunksSet; + protected boolean alsoBuffer = false; + + protected boolean skipBytes = false; // fDAT (APNG) skips 4 bytes) + protected byte[] skippedBytes; // only for fDAT + protected int seqNumExpected = -1; // only for fDAT + + public DeflatedChunkReader(int clen, String chunkid, boolean checkCrc, long offsetInPng, + DeflatedChunksSet iDatSet) { + super(clen, chunkid, offsetInPng, ChunkReaderMode.PROCESS); + this.deflatedChunksSet = iDatSet; + if (chunkid.equals(PngChunkFDAT.ID)) { + skipBytes = true; + skippedBytes = new byte[4]; + } + iDatSet.appendNewChunk(this); + } + + /** + * Delegates to ChunkReaderDeflatedSet.processData() + */ + @Override + protected void processData(int offsetInchunk, byte[] buf, int off, int len) { + if (skipBytes && offsetInchunk < 4) {// only for APNG (sigh) + for (int oc = offsetInchunk; oc < 4 && len > 0; oc++, off++, len--) { + skippedBytes[oc] = buf[off]; + } + } + if (len > 0) { // delegate to idatSet + + deflatedChunksSet.processBytes(buf, off, len); + if (alsoBuffer) { // very rare! + System.arraycopy(buf, off, getChunkRaw().data, read, len); + } + } + } + + /** + * only a stupid check for fDAT (I wonder how many APGN readers do this) + */ + @Override + protected void chunkDone() { + if (skipBytes && getChunkRaw().id.equals(PngChunkFDAT.ID)) { + if (seqNumExpected >= 0) { + int seqNum = PngHelperInternal.readInt4fromBytes(skippedBytes, 0); + if (seqNum != seqNumExpected) { + throw new PngjInputException( + "bad chunk sequence for fDAT chunk " + seqNum + " expected " + seqNumExpected); + } + } + } + } + + @Override + public boolean isFromDeflatedSet() { + return true; + } + + /** + * In some rare cases you might want to also buffer the data? + */ + public void setAlsoBuffer() { + if (read > 0) { + throw new RuntimeException("too late"); + } + alsoBuffer = true; + getChunkRaw().allocData(); + } + + /** + * only relevant for fDAT + */ + public void setSeqNumExpected(int seqNumExpected) { + this.seqNumExpected = seqNumExpected; + } + +} diff --git a/png/src/main/java/org/xbib/graphics/imageio/plugins/png/pngj/DeflatedChunksSet.java b/png/src/main/java/org/xbib/graphics/imageio/plugins/png/pngj/DeflatedChunksSet.java new file mode 100644 index 0000000..5b631e7 --- /dev/null +++ b/png/src/main/java/org/xbib/graphics/imageio/plugins/png/pngj/DeflatedChunksSet.java @@ -0,0 +1,452 @@ +package org.xbib.graphics.imageio.plugins.png.pngj; + +import java.util.zip.DataFormatException; +import java.util.zip.Inflater; + +/** + * A set of IDAT-like chunks which, concatenated, form a zlib stream. + *

      + * The inflated stream is intented to be read as a sequence of "rows", of which + * the caller knows the lengths (not necessary equal!) and number. + *

      + * Eg: For IDAT non-interlaced images, a row has bytesPerRow + 1 filter byte
      + * For interlaced images, the lengths are variable. + *

      + * This class can work in sync (polled) mode or async (callback) mode. But for + * callback mode the method processRowCallback() must be overriden + *

      + * See {@link IdatSet}, which is mostly used and has a slightly simpler use.
      + * See DeflatedChunkSetTest for example of use. + */ +public class DeflatedChunksSet { + + protected byte[] row; // a "row" here means a raw (uncopressed filtered) part of the IDAT stream, + // normally a image row (or subimage row for interlaced) plus a filter byte + private int rowfilled; // effective/valid length of row + private int rowlen; // what amount of bytes is to be interpreted as a complete "row". can change + // (for interlaced) + private int rown; // only coincide with image row if non-interlaced - incremented by + // setNextRowSize() + + /* + * States WAITING_FOR_INPUT ROW_READY WORK_DONE TERMINATED + * + * processBytes() is externally called, prohibited in READY (in DONE it's + * ignored) + * + * WARNING: inflater.finished() != DONE (not enough, not neccesary) DONE means + * that we have already uncompressed all the data of interest. + * + * In non-callback mode, prepareForNextRow() is also externally called, in + * + * Flow: - processBytes() calls inflateData() + * + * inflateData() : if buffer is filled goes to READY + * + * else if ! inf.finished goes to WAITING + * + * else if any data goes to READY (incomplete data to be read) + * + * else goes to DONE + * + * in Callback mode, after going to READY, n=processCallback() is called and + * then prepareForNextRow(n) is called. + * + * in Polled mode, prepareForNextRow(n) must be called from outside (after + * checking state=READY) + * + * prepareForNextRow(n) goes to DONE if n==0 calls inflateData() again - end() + * goes to DONE + */ + private enum State { + WAITING_FOR_INPUT, // waiting for more bytes to be fed + ROW_READY, // ready for consumption (might be less than fully filled), ephemeral state for + // CALLBACK mode + DONE, // all data of interest has been read, but we might accept still more trailing + // chunks (we'll ignore them) + CLOSED; // we are done, and also won't accept more IDAT chunks + + public boolean isDone() { + return this == DONE || this == CLOSED; + } // the caller has already uncompressed all the data of interest or EOF + + public boolean isClosed() { + return this == CLOSED; + } // we dont accept more chunks + } + + State state = State.WAITING_FOR_INPUT; // never null + + private Inflater inf; + private final boolean infOwn; // true if we own the inflater (we created it) + + private DeflatedChunkReader curChunk; + + protected final boolean callbackMode; // true: CALLBACK (non-blocking) false: POLL (blocking) + + private long nBytesIn = 0; // count the total compressed bytes that have been fed + private long nBytesOut = 0; // count the total uncompressed bytes + int chunkNum = -1; // incremented at each new chunk start + int firstChunqSeqNum = -1; // expected seq num for first chunk. used only for fDAT (APNG) + + /** + * All IDAT-like chunks that form a same DeflatedChunksSet should have the + * same id + */ + public final String chunkid; + + /** + * @param initialRowLen Length in bytes of first "row" (see description) + * @param maxRowLen Max length in bytes of "rows" + * @param inflater Can be null. If not null, must be already reset (and it must + * be closed/released by caller!) + */ + public DeflatedChunksSet(String chunkid, boolean callbackMode, int initialRowLen, int maxRowLen, Inflater inflater, + byte[] buffer) { + this.chunkid = chunkid; + this.callbackMode = callbackMode; + this.rowlen = initialRowLen; + if (initialRowLen < 1 || maxRowLen < initialRowLen) { + throw new PngjException("bad inital row len " + initialRowLen); + } + if (inflater != null) { + this.inf = inflater; + infOwn = false; + } else { + this.inf = new Inflater(); + infOwn = true; // inflater is own, we will release on close() + } + this.row = buffer != null && buffer.length >= initialRowLen ? buffer : new byte[maxRowLen]; + rown = -1; + this.state = State.WAITING_FOR_INPUT; + try { + prepareForNextRow(initialRowLen); + } catch (RuntimeException e) { + close(); + throw e; + } + } + + public DeflatedChunksSet(String chunkid, boolean callbackMode, int initialRowLen, int maxRowLen) { + this(chunkid, callbackMode, initialRowLen, maxRowLen, null, null); + } + + protected void appendNewChunk(DeflatedChunkReader cr) { + // all chunks must have same id + if (!this.chunkid.equals(cr.getChunkRaw().id)) { + throw new PngjInputException( + "Bad chunk inside IdatSet, id:" + cr.getChunkRaw().id + ", expected:" + this.chunkid); + } + this.curChunk = cr; + chunkNum++; + if (firstChunqSeqNum >= 0) { + cr.setSeqNumExpected(chunkNum + firstChunqSeqNum); + } + } + + /** + * Feeds the inflater with the compressed bytes + *

      + * In poll mode, the caller should not call repeatedly this, without + * consuming first, checking isDataReadyForConsumer() + * + * @param buf + * @param off + * @param len + */ + protected void processBytes(byte[] buf, int off, int len) { + nBytesIn += len; + // PngHelperInternal.LOGGER.info("processing compressed bytes in chunkreader : " + // + len); + if (len < 1 || state.isDone()) { + return; + } + if (state == State.ROW_READY) { + throw new PngjInputException("this should only be called if waitingForMoreInput"); + } + if (inf.needsDictionary() || !inf.needsInput()) { + throw new RuntimeException("should not happen"); + } + inf.setInput(buf, off, len); + // PngHelperInternal.debug("entering processs bytes, state=" + state + + // " callback="+callbackMode); + if (callbackMode) { + while (inflateData()) { + int nextRowLen = processRowCallback(); + prepareForNextRow(nextRowLen); + if (isDone()) { + processDoneCallback(); + } + } + } else { + inflateData(); + } + } + + /* + * This never inflates more than one row This returns true if this has resulted + * in a row being ready and preprocessed with preProcessRow (in callback mode, + * we should call immediately processRowCallback() and + * prepareForNextRow(nextRowLen) + */ + private boolean inflateData() { + try { + // PngHelperInternal.debug("entering inflateData bytes, state=" + state + + // " callback="+callbackMode); + if (state == State.ROW_READY) { + throw new PngjException("invalid state");// assert + } + if (state.isDone()) { + return false; + } + int ninflated = 0; + if (row == null || row.length < rowlen) { + row = new byte[rowlen]; // should not happen + } + if (rowfilled < rowlen && !inf.finished()) { + try { + ninflated = inf.inflate(row, rowfilled, rowlen - rowfilled); + } catch (DataFormatException e) { + throw new PngjInputException("error decompressing zlib stream ", e); + } + rowfilled += ninflated; + nBytesOut += ninflated; + } + State nextstate = null; + if (rowfilled == rowlen) { + nextstate = State.ROW_READY; // complete row, process it + } else if (!inf.finished()) { + nextstate = State.WAITING_FOR_INPUT; + } else if (rowfilled > 0) { + nextstate = State.ROW_READY; // complete row, process it + } else { + nextstate = State.DONE; // eof, no more data + } + state = nextstate; + if (state == State.ROW_READY) { + preProcessRow(); + return true; + } + } catch (RuntimeException e) { + close(); + throw e; + } + return false; + } + + /** + * Called automatically in all modes when a full row has been fully + * inflated. + */ + protected void preProcessRow() { + + } + + /** + * Callback, must be implemented in callbackMode + *

      + * This should use {@link #getRowFilled()} and {@link #getInflatedRow()} to + * access the row. + *

      + * Must return byes of next row, for next callback. + */ + protected int processRowCallback() { + throw new PngjInputException("not implemented"); + } + + /** + * Callback, to be implemented in callbackMode + *

      + * This will be called once to notify state done + */ + protected void processDoneCallback() { + } + + /** + * Inflated buffer. + *

      + * The effective length is given by {@link #getRowFilled()} + */ + public byte[] getInflatedRow() { + return row; + } + + /** + * Should be called after the previous row was processed + *

      + * Pass 0 or negative to signal that we are done (not expecting more bytes) + *

      + * This resets {@link #rowfilled} + *

      + * The + */ + public void prepareForNextRow(int len) { + rowfilled = 0; + rown++; + if (len < 1) { + rowlen = 0; + markAsDone(); + } else if (inf.finished()) { + rowlen = 0; + markAsDone(); + } else { + state = State.WAITING_FOR_INPUT; + rowlen = len; + if (!callbackMode) { + inflateData(); + } + } + } + + /** + * In this state, the object is waiting for more input to deflate. + *

      + * Only in this state it's legal to feed this + */ + public boolean isWaitingForMoreInput() { + return state == State.WAITING_FOR_INPUT; + } + + /** + * In this state, the object is waiting the caller to retrieve inflated data + *

      + * Effective length: see {@link #getRowFilled()} + */ + public boolean isRowReady() { + return state == State.ROW_READY; + } + + /** + * In this state, all relevant data has been uncompressed and retrieved + * (exceptionally, the reading has ended prematurely). + *

      + * We can still feed this object, but the bytes will be swallowed/ignored. + */ + public boolean isDone() { + return state.isDone(); + } + + public boolean isClosed() { + return state.isClosed(); + } + + /** + * This will be called by the owner to report us the next chunk to come. We + * can make our own internal changes and checks. This returns true if we + * acknowledge the next chunk as part of this set + */ + public boolean ackNextChunkId(String id) { + if (state.isClosed()) { + return false; + } else if (id.equals(chunkid)) { + return true; + } else { + if (!allowOtherChunksInBetween(id)) { + if (state.isDone()) { + if (!state.isClosed()) { + close(); + } + return false; + } else { + throw new PngjInputException("Unexpected chunk " + id + " while " + chunkid + " set is not done"); + } + } else { + return true; + } + } + } + + /** + * This should be called when discarding this object, or for aborting. + * Secure, idempotent Don't use this just to notify this object that it has + * no more work to do, see {@link #markAsDone()} + */ + public void close() { + try { + if (!state.isClosed()) { + state = State.CLOSED; + } + if (infOwn && inf != null) { + inf.end();// we end the Inflater only if we created it + inf = null; + } + } catch (Exception e) { + } + } + + /** + * Forces the DONE state (except if it was CLOSED), this object won't uncompress more data. It's still + * not terminated, it will accept more IDAT chunks, but will ignore them. + */ + public void markAsDone() { + if (!isDone()) { + state = State.DONE; + } + } + + /** + * Target size of the current row, including filter byte.
      + * should coincide (or be less than) with row.length + */ + public int getRowLen() { + return rowlen; + } + + /** + * This the amount of valid bytes in the buffer + */ + public int getRowFilled() { + return rowfilled; + } + + /** + * Get current (last) row number. + *

      + * This corresponds to the raw numeration of rows as seen by the deflater. + * Not the same as the real image row, if interlaced. + */ + public int getRown() { + return rown; + } + + /** + * Some IDAT-like set can allow other chunks in between (APGN?). + *

      + * Normally false. + * + * @param id Id of the other chunk that appeared in middel of this set. + * @return true if allowed + */ + public boolean allowOtherChunksInBetween(String id) { + return false; + } + + /** + * Callback mode = async processing + */ + public boolean isCallbackMode() { + return callbackMode; + } + + /** + * total number of bytes that have been fed to this object + */ + public long getBytesIn() { + return nBytesIn; + } + + /** + * total number of bytes that have been uncompressed + */ + public long getBytesOut() { + return nBytesOut; + } + + @Override + public String toString() { + StringBuilder sb = new StringBuilder("idatSet : " + curChunk.getChunkRaw().id + " state=" + state + " rows=" + + rown + " bytes=" + nBytesIn + "/" + nBytesOut); + return sb.toString(); + } + +} diff --git a/png/src/main/java/org/xbib/graphics/imageio/plugins/png/pngj/Deinterlacer.java b/png/src/main/java/org/xbib/graphics/imageio/plugins/png/pngj/Deinterlacer.java new file mode 100644 index 0000000..051a7e5 --- /dev/null +++ b/png/src/main/java/org/xbib/graphics/imageio/plugins/png/pngj/Deinterlacer.java @@ -0,0 +1,208 @@ +package org.xbib.graphics.imageio.plugins.png.pngj; + +public class Deinterlacer { + final ImageInfo imi; + private int pass; // 1-7 + private int rows, cols; + int dY, dX, oY, oX; // current step and offset (in pixels) + int oXsamples, dXsamples; // step in samples + + // current row in the virtual subsampled image; this increments (by 1) from 0 to rows/dy 7 times + private int currRowSubimg; + // in the real image, this will cycle from 0 to im.rows in different steps, 7 times + private int currRowReal; + private int currRowSeq; // not counting empty rows + + int totalRows; + private boolean ended = false; + + public Deinterlacer(ImageInfo iminfo) { + this.imi = iminfo; + pass = 0; + currRowSubimg = -1; + currRowReal = -1; + currRowSeq = 0; + ended = false; + totalRows = 0; // lazy compute + setPass(1); + setRow(0); + } + + /** + * this refers to the row currRowSubimg + */ + private void setRow(int n) { // This should be called only intercally, in sequential order + currRowSubimg = n; + currRowReal = n * dY + oY; + if (currRowReal < 0 || currRowReal >= imi.rows) { + throw new PngjExceptionInternal("bad row - this should not happen"); + } + } + + /** + * Skips passes with no rows. Return false is no more rows + */ + boolean nextRow() { + currRowSeq++; + if (rows == 0 || currRowSubimg >= rows - 1) { // next pass + if (pass == 7) { + ended = true; + return false; + } + setPass(pass + 1); + if (rows == 0) { + currRowSeq--; + return nextRow(); + } + setRow(0); + } else { + setRow(currRowSubimg + 1); + } + return true; + } + + boolean isEnded() { + return ended; + } + + void setPass(int p) { + if (this.pass == p) { + return; + } + pass = p; + byte[] pp = paramsForPass(p);// dx,dy,ox,oy + dX = pp[0]; + dY = pp[1]; + oX = pp[2]; + oY = pp[3]; + rows = imi.rows > oY ? (imi.rows + dY - 1 - oY) / dY : 0; + cols = imi.cols > oX ? (imi.cols + dX - 1 - oX) / dX : 0; + if (cols == 0) { + rows = 0; // well, really... + } + dXsamples = dX * imi.channels; + oXsamples = oX * imi.channels; + } + + static byte[] paramsForPass(final int p) {// dx,dy,ox,oy + switch (p) { + case 1: + return new byte[]{8, 8, 0, 0}; + case 2: + return new byte[]{8, 8, 4, 0}; + case 3: + return new byte[]{4, 8, 0, 4}; + case 4: + return new byte[]{4, 4, 2, 0}; + case 5: + return new byte[]{2, 4, 0, 2}; + case 6: + return new byte[]{2, 2, 1, 0}; + case 7: + return new byte[]{1, 2, 0, 1}; + default: + throw new PngjExceptionInternal("bad interlace pass" + p); + } + } + + /** + * current row number inside the "sub image" + */ + int getCurrRowSubimg() { + return currRowSubimg; + } + + /** + * current row number inside the "real image" + */ + int getCurrRowReal() { + return currRowReal; + } + + /** + * current pass number (1-7) + */ + int getPass() { + return pass; + } + + /** + * How many rows has the current pass? + **/ + int getRows() { + return rows; + } + + /** + * How many columns (pixels) are there in the current row + */ + int getCols() { + return cols; + } + + public int getPixelsToRead() { + return getCols(); + } + + public int getBytesToRead() { // not including filter byte + return (imi.bitspPixel * getPixelsToRead() + 7) / 8; + } + + public int getdY() { + return dY; + } + + /* + * in pixels + */ + public int getdX() { + return dX; + } + + public int getoY() { + return oY; + } + + /* + * in pixels + */ + public int getoX() { + return oX; + } + + public int getTotalRows() { + if (totalRows == 0) { // lazy compute + for (int p = 1; p <= 7; p++) { + byte[] pp = paramsForPass(p); // dx dy ox oy + int rows = imi.rows > pp[3] ? (imi.rows + pp[1] - 1 - pp[3]) / pp[1] : 0; + int cols = imi.cols > pp[2] ? (imi.cols + pp[0] - 1 - pp[2]) / pp[0] : 0; + if (rows > 0 && cols > 0) { + totalRows += rows; + } + } + } + return totalRows; + } + + /** + * total unfiltered bytes in the image, including the filter byte + */ + public long getTotalRawBytes() { // including the filter byte + long bytes = 0; + for (int p = 1; p <= 7; p++) { + byte[] pp = paramsForPass(p); // dx dy ox oy + int rows = imi.rows > pp[3] ? (imi.rows + pp[1] - 1 - pp[3]) / pp[1] : 0; + int cols = imi.cols > pp[2] ? (imi.cols + pp[0] - 1 - pp[2]) / pp[0] : 0; + int bytesr = (imi.bitspPixel * cols + 7) / 8; // without filter byte + if (rows > 0 && cols > 0) { + bytes += rows * (1 + (long) bytesr); + } + } + return bytes; + } + + public int getCurrRowSeq() { + return currRowSeq; + } + +} diff --git a/png/src/main/java/org/xbib/graphics/imageio/plugins/png/pngj/ErrorBehaviour.java b/png/src/main/java/org/xbib/graphics/imageio/plugins/png/pngj/ErrorBehaviour.java new file mode 100644 index 0000000..5bb6325 --- /dev/null +++ b/png/src/main/java/org/xbib/graphics/imageio/plugins/png/pngj/ErrorBehaviour.java @@ -0,0 +1,14 @@ +package org.xbib.graphics.imageio.plugins.png.pngj; + +public enum ErrorBehaviour { + STRICT(0), // default mode: any error aborts reading with exception + LENIENT1_CRC(1), // CRC errors only trigger warning (or nothing if not checking) + LENIENT2_ANCILLARY(3), // also: content errors in ancillary chunks are ignored + SUPER_LENIENT(5); // we try hard to read, even garbage, without throwing exceptions + final int c; + + ErrorBehaviour(int c) { + this.c = c; + } + +} \ No newline at end of file diff --git a/png/src/main/java/org/xbib/graphics/imageio/plugins/png/pngj/FilterType.java b/png/src/main/java/org/xbib/graphics/imageio/plugins/png/pngj/FilterType.java new file mode 100644 index 0000000..e64e312 --- /dev/null +++ b/png/src/main/java/org/xbib/graphics/imageio/plugins/png/pngj/FilterType.java @@ -0,0 +1,119 @@ +package org.xbib.graphics.imageio.plugins.png.pngj; + +import java.util.HashMap; + +/** + * Internal PNG predictor filter type + *

      + * Negative values are pseudo types, actually global strategies for writing, + * that (can) result on different real filters for different rows + */ +public enum FilterType { + /** + * No filter. + */ + FILTER_NONE(0), + /** + * SUB filter (uses same row) + */ + FILTER_SUB(1), + /** + * UP filter (uses previous row) + */ + FILTER_UP(2), + /** + * AVERAGE filter + */ + FILTER_AVERAGE(3), + /** + * PAETH predictor + */ + FILTER_PAETH(4), + /** + * Default strategy: select one of the standard filters depending on global + * image parameters + */ + FILTER_DEFAULT(-1), + /** + * Adaptative strategy, sampling each row, or almost + */ + FILTER_ADAPTIVE_FULL(-4), + /** + * Adaptive strategy, skippping some rows + */ + FILTER_ADAPTIVE_MEDIUM(-3), // samples about 1/4 row + /** + * Adaptative strategy, skipping many rows - more speed + */ + FILTER_ADAPTIVE_FAST(-2), // samples each 8 or 16 rows + /** + * Experimental + */ + FILTER_SUPER_ADAPTIVE(-10), // + /** + * Preserves the filter passed in original row. + */ + FILTER_PRESERVE(-40), + /** + * Uses all fiters, one for lines, cyciclally. Only for tests. + */ + FILTER_CYCLIC(-50), + /** + * Not specified, placeholder for unknown or NA filters. + */ + FILTER_UNKNOWN(-100); + + public final int val; + + FilterType(int val) { + this.val = val; + } + + private static final HashMap byVal; + + static { + byVal = new HashMap(); + for (FilterType ft : values()) { + byVal.put(ft.val, ft); + } + } + + public static FilterType getByVal(int i) { + return byVal.get(i); + } + + /** + * only considers standard + */ + public static boolean isValidStandard(int i) { + return i >= 0 && i <= 4; + } + + public static boolean isValidStandard(FilterType fy) { + return fy != null && isValidStandard(fy.val); + } + + public static boolean isAdaptive(FilterType fy) { + return fy.val <= -2 && fy.val >= -4; + } + + /** + * Returns all "standard" filters + */ + public static FilterType[] getAllStandard() { + return new FilterType[]{FILTER_NONE, FILTER_SUB, FILTER_UP, FILTER_AVERAGE, FILTER_PAETH}; + } + + public static FilterType[] getAllStandardNoneLast() { + return new FilterType[]{FILTER_SUB, FILTER_UP, FILTER_AVERAGE, FILTER_PAETH, FILTER_NONE}; + } + + public static FilterType[] getAllStandardExceptNone() { + return new FilterType[]{FILTER_SUB, FILTER_UP, FILTER_AVERAGE, FILTER_PAETH}; + } + + static FilterType[] getAllStandardForFirstRow() { + return new FilterType[]{FILTER_SUB, FILTER_NONE}; + } + +} diff --git a/png/src/main/java/org/xbib/graphics/imageio/plugins/png/pngj/IBytesConsumer.java b/png/src/main/java/org/xbib/graphics/imageio/plugins/png/pngj/IBytesConsumer.java new file mode 100644 index 0000000..e31a43d --- /dev/null +++ b/png/src/main/java/org/xbib/graphics/imageio/plugins/png/pngj/IBytesConsumer.java @@ -0,0 +1,31 @@ +package org.xbib.graphics.imageio.plugins.png.pngj; + +/** + * Bytes consumer. + *

      + * An object implementing can be fed with bytes. + *

      + * It can consume in steps, so each time it's fed with n bytes it can eat + * between 1 and n bytes. + */ +public interface IBytesConsumer { + /** + * Eats some bytes, at most len (perhaps less). + *

      + * Returns bytes actually consumed. + *

      + * It returns -1 if the object didn't consume bytes because it was done or + * closed + *

      + * It should only returns 0 if len is 0 + */ + int consume(byte[] buf, int offset, int len); + + /** + * The consumer is DONE when it does not need more bytes, + * either because it ended normally, or abnormally + * Typically this implies it will return -1 if consume() is called afterwards, + * but it might happen that it will consume more (unneeded) bytes anwyway + */ + boolean isDone(); +} diff --git a/png/src/main/java/org/xbib/graphics/imageio/plugins/png/pngj/IChunkFactory.java b/png/src/main/java/org/xbib/graphics/imageio/plugins/png/pngj/IChunkFactory.java new file mode 100644 index 0000000..77b9b73 --- /dev/null +++ b/png/src/main/java/org/xbib/graphics/imageio/plugins/png/pngj/IChunkFactory.java @@ -0,0 +1,21 @@ +package org.xbib.graphics.imageio.plugins.png.pngj; + +import org.xbib.graphics.imageio.plugins.png.pngj.chunks.ChunkRaw; +import org.xbib.graphics.imageio.plugins.png.pngj.chunks.PngChunk; + +/** + * Factory to create a {@link PngChunk} from a {@link ChunkRaw}. + *

      + * Used by {@link PngReader} + */ +public interface IChunkFactory { + + /** + * @param chunkRaw Chunk in raw form. Data can be null if it was skipped or + * processed directly (eg IDAT) + * @param imgInfo Not normally necessary, but some chunks want this info + * @return should never return null. + */ + PngChunk createChunk(ChunkRaw chunkRaw, ImageInfo imgInfo); + +} diff --git a/png/src/main/java/org/xbib/graphics/imageio/plugins/png/pngj/IImageLine.java b/png/src/main/java/org/xbib/graphics/imageio/plugins/png/pngj/IImageLine.java new file mode 100644 index 0000000..72bcfd6 --- /dev/null +++ b/png/src/main/java/org/xbib/graphics/imageio/plugins/png/pngj/IImageLine.java @@ -0,0 +1,45 @@ +package org.xbib.graphics.imageio.plugins.png.pngj; + +/** + * General format-translated image line. + *

      + * The methods from this interface provides translation from/to PNG raw + * unfiltered pixel data, for each image line. This doesn't make any assumptions + * of underlying storage. + *

      + * The user of this library will not normally use this methods, but instead will + * cast to a more concrete implementation, as {@link ImageLineInt} or + * {@link ImageLineByte} with its methods for accessing the pixel values. + */ +public interface IImageLine { + + /** + * Extract pixels from a raw unlfiltered PNG row. Len is the total amount of + * bytes in the array, including the first byte (filter type) + *

      + * Arguments offset and step (0 and 1 for non interlaced) are in PIXELS. + * It's guaranteed that when step==1 then offset=0 + *

      + * Notice that when step!=1 the data is partial, this method will be called + * several times + *

      + * Warning: the data in array 'raw' starts at position 0 and has 'len' + * consecutive bytes. 'offset' and 'step' refer to the pixels in destination + */ + void readFromPngRaw(byte[] raw, int len, int offset, int step); + + /** + * This is called when the read for the line has been completed (eg for + * interlaced). It's called exactly once for each line. This is provided in + * case the class needs to to some postprocessing. + */ + void endReadFromPngRaw(); + + /** + * Writes the line to a PNG raw byte array, in the unfiltered PNG format + * Notice that the first byte is the filter type, you should write it only + * if you know it. + */ + void writeToPngRaw(byte[] raw); + +} diff --git a/png/src/main/java/org/xbib/graphics/imageio/plugins/png/pngj/IImageLineArray.java b/png/src/main/java/org/xbib/graphics/imageio/plugins/png/pngj/IImageLineArray.java new file mode 100644 index 0000000..bb4b968 --- /dev/null +++ b/png/src/main/java/org/xbib/graphics/imageio/plugins/png/pngj/IImageLineArray.java @@ -0,0 +1,24 @@ +package org.xbib.graphics.imageio.plugins.png.pngj; + +/** + * This interface is just for the sake of unifying some methods of + * {@link ImageLineHelper} that can use both {@link ImageLineInt} or + * {@link ImageLineByte}. It's not very useful outside that, and the user should + * not rely much on this. + */ +public interface IImageLineArray { + ImageInfo getImageInfo(); + + FilterType getFilterType(); + + /** + * length of array (should correspond to samples) + */ + int getSize(); + + /** + * Get i-th element of array (for 0 to size-1). The meaning of this is type + * dependent. For ImageLineInt and ImageLineByte is the sample value. + */ + int getElem(int i); +} diff --git a/png/src/main/java/org/xbib/graphics/imageio/plugins/png/pngj/IImageLineFactory.java b/png/src/main/java/org/xbib/graphics/imageio/plugins/png/pngj/IImageLineFactory.java new file mode 100644 index 0000000..f83dac3 --- /dev/null +++ b/png/src/main/java/org/xbib/graphics/imageio/plugins/png/pngj/IImageLineFactory.java @@ -0,0 +1,8 @@ +package org.xbib.graphics.imageio.plugins.png.pngj; + +/** + * Image Line factory. + */ +public interface IImageLineFactory { + T createImageLine(ImageInfo iminfo); +} diff --git a/png/src/main/java/org/xbib/graphics/imageio/plugins/png/pngj/IImageLineSet.java b/png/src/main/java/org/xbib/graphics/imageio/plugins/png/pngj/IImageLineSet.java new file mode 100644 index 0000000..385b9e8 --- /dev/null +++ b/png/src/main/java/org/xbib/graphics/imageio/plugins/png/pngj/IImageLineSet.java @@ -0,0 +1,59 @@ +package org.xbib.graphics.imageio.plugins.png.pngj; + +/** + * Set of {@link IImageLine} elements. + *

      + * This is actually a "virtual" set, it can be implemented in several ways; for + * example + *

        + *
      • Cursor-like: stores only one line, which is implicitly moved when + * requested
      • + *
      • All lines: all lines stored as an array of IImageLine
      • + *
      • Subset of lines: eg, only first 3 lines, or odd numbered lines. Or a band + * of neighbours lines that is moved like a cursor.
      • The ImageLine that + * PngReader returns is hosted by a IImageLineSet (this abstraction allows the + * implementation to deal with interlaced images cleanly) but the library user + * does not normally needs to know that (or rely on that), except for the + * {@link PngReader#readRows()} method. + *
      + */ +public interface IImageLineSet { + + /** + * Asks for imageline corresponding to row n in the original image + * (zero based). This can trigger side effects in this object (eg, advance a + * cursor, set current row number...) In some scenarios, this should be + * consider as alias to (pseudocode) + * positionAtLine(n); getCurrentLine(); + *

      + * Throws exception if not available. The caller is supposed to know what + * he/she is doing + **/ + IImageLine getImageLine(int n); + + /** + * Like {@link #getImageLine(int)} but uses the raw numbering inside the + * LineSet This makes little sense for a cursor + * + * @param n Should normally go from 0 to {@link #size()} + * @return + */ + IImageLine getImageLineRawNum(int n); + + /** + * Returns true if the set contain row n (in the original + * image,zero based) currently allocated. + *

      + * If it's a single-cursor, this should return true only if it's positioned + * there. (notice that hasImageLine(n) can return false, but getImageLine(n) + * can be ok) + **/ + boolean hasImageLine(int n); + + /** + * Internal size of allocated rows This is informational, it should rarely + * be important for the caller. + **/ + int size(); + +} diff --git a/png/src/main/java/org/xbib/graphics/imageio/plugins/png/pngj/IImageLineSetFactory.java b/png/src/main/java/org/xbib/graphics/imageio/plugins/png/pngj/IImageLineSetFactory.java new file mode 100644 index 0000000..626d208 --- /dev/null +++ b/png/src/main/java/org/xbib/graphics/imageio/plugins/png/pngj/IImageLineSetFactory.java @@ -0,0 +1,25 @@ +package org.xbib.graphics.imageio.plugins.png.pngj; + +/** + * Factory of {@link IImageLineSet}, used by {@link PngReader}. + *

      + * + * @param Generic type of IImageLine + */ +public interface IImageLineSetFactory { + /** + * Creates a new {@link IImageLineSet} + *

      + * If singleCursor=true, the caller will read and write one row fully at a + * time, in order (it'll never try to read out of order lines), so the + * implementation can opt for allocate only one line. + * + * @param imgInfo Image info + * @param singleCursor : will read/write one row at a time + * @param nlines : how many lines we plan to read + * @param noffset : how many lines we want to skip from the original image + * (normally 0) + * @param step : row step (normally 1) + */ + IImageLineSet create(ImageInfo imgInfo, boolean singleCursor, int nlines, int noffset, int step); +} diff --git a/png/src/main/java/org/xbib/graphics/imageio/plugins/png/pngj/IPngWriterFactory.java b/png/src/main/java/org/xbib/graphics/imageio/plugins/png/pngj/IPngWriterFactory.java new file mode 100644 index 0000000..5a3c162 --- /dev/null +++ b/png/src/main/java/org/xbib/graphics/imageio/plugins/png/pngj/IPngWriterFactory.java @@ -0,0 +1,7 @@ +package org.xbib.graphics.imageio.plugins.png.pngj; + +import java.io.OutputStream; + +public interface IPngWriterFactory { + PngWriter createPngWriter(OutputStream outputStream, ImageInfo imgInfo); +} diff --git a/png/src/main/java/org/xbib/graphics/imageio/plugins/png/pngj/IdatChunkWriter.java b/png/src/main/java/org/xbib/graphics/imageio/plugins/png/pngj/IdatChunkWriter.java new file mode 100644 index 0000000..ea3114a --- /dev/null +++ b/png/src/main/java/org/xbib/graphics/imageio/plugins/png/pngj/IdatChunkWriter.java @@ -0,0 +1,136 @@ +package org.xbib.graphics.imageio.plugins.png.pngj; + +import java.io.OutputStream; +import org.xbib.graphics.imageio.plugins.png.pngj.chunks.ChunkHelper; +import org.xbib.graphics.imageio.plugins.png.pngj.chunks.ChunkRaw; + +/** + * Outputs a sequence of IDAT-like chunk, that is filled progressively until the + * max chunk length is reached (or until flush()) + */ +public class IdatChunkWriter { + + private static final int MAX_LEN_DEFAULT = 32768; // 32K rather arbitrary - data only + + private final OutputStream outputStream; + private final int maxChunkLen; + private byte[] buf; + + private int offset = 0; + private int availLen; + private long totalBytesWriten = 0; // including header+crc + private int chunksWriten = 0; + + public IdatChunkWriter(OutputStream outputStream) { + this(outputStream, 0); + } + + public IdatChunkWriter(OutputStream outputStream, int maxChunkLength) { + this.outputStream = outputStream; + this.maxChunkLen = maxChunkLength > 0 ? maxChunkLength : MAX_LEN_DEFAULT; + buf = new byte[maxChunkLen]; + availLen = maxChunkLen - offset; + postReset(); + } + + public IdatChunkWriter(OutputStream outputStream, byte[] b) { + this.outputStream = outputStream; + this.buf = b != null ? b : new byte[MAX_LEN_DEFAULT]; + this.maxChunkLen = b.length; + availLen = maxChunkLen - offset; + postReset(); + } + + protected byte[] getChunkId() { + return ChunkHelper.b_IDAT; + } + + /** + * Writes a chhunk if there is more than minLenToWrite. + *

      + * This is normally called internally, but can be called explicitly to force + * flush. + */ + public final void flush() { + if (offset > 0 && offset >= minLenToWrite()) { + ChunkRaw c = new ChunkRaw(offset, getChunkId(), false); + c.data = buf; + c.writeChunk(outputStream); + totalBytesWriten += c.len + 12; + chunksWriten++; + offset = 0; + availLen = maxChunkLen; + postReset(); + } + } + + public int getOffset() { + return offset; + } + + public int getAvailLen() { + return availLen; + } + + /** + * triggers an flush+reset if appropiate + */ + public void incrementOffset(int n) { + offset += n; + availLen -= n; + if (availLen < 0) { + throw new PngjOutputException("Anomalous situation"); + } + if (availLen == 0) { + flush(); + } + } + + /** + * this should rarely be used, the normal way (to avoid double copying) is + * to get the buffer and write directly to it + */ + public void write(byte[] b, int o, int len) { + while (len > 0) { + int n = len <= availLen ? len : availLen; + System.arraycopy(b, o, buf, offset, n); + incrementOffset(n); + len -= n; + o += n; + } + } + + /** + * this will be called after reset + */ + protected void postReset() { + // fdat could override this (and minLenToWrite) to add a prefix + } + + protected int minLenToWrite() { + return 1; + } + + public void close() { + flush(); + offset = 0; + buf = null; + } + + /** + * You can write directly to this buffer, using {@link #getOffset()} and + * {@link #getAvailLen()}. You should call {@link #incrementOffset(int)} + * inmediately after. + */ + public byte[] getBuf() { + return buf; + } + + public long getTotalBytesWriten() { + return totalBytesWriten; + } + + public int getChunksWriten() { + return chunksWriten; + } +} diff --git a/png/src/main/java/org/xbib/graphics/imageio/plugins/png/pngj/IdatSet.java b/png/src/main/java/org/xbib/graphics/imageio/plugins/png/pngj/IdatSet.java new file mode 100644 index 0000000..92d7973 --- /dev/null +++ b/png/src/main/java/org/xbib/graphics/imageio/plugins/png/pngj/IdatSet.java @@ -0,0 +1,256 @@ +package org.xbib.graphics.imageio.plugins.png.pngj; + +import java.util.Arrays; +import java.util.logging.Logger; +import java.util.zip.Checksum; +import java.util.zip.Inflater; + +/** + * This object process the concatenation of IDAT chunks. + *

      + * It extends {@link DeflatedChunksSet}, adding the intelligence to unfilter + * rows, and to understand row lenghts in terms of ImageInfo and (eventually) + * Deinterlacer + */ +public class IdatSet extends DeflatedChunksSet { + private static final Logger LOGGER = Logger.getLogger(IdatSet.class.getName()); + + protected byte[] rowUnfiltered; + protected byte[] rowUnfilteredPrev; + protected final ImageInfo imgInfo; // in the case of APNG this is the frame image + protected final Deinterlacer deinterlacer; + + final RowInfo rowinfo; // info for the last processed row, for debug + + protected int[] filterUseStat = new int[5]; // for stats + + /** + * @param id Chunk id (first chunk), should be shared by all concatenated + * chunks + * @param iminfo Image info + * @param deinterlacer Not null if interlaced + */ + public IdatSet(String id, boolean callbackMode, ImageInfo iminfo, Deinterlacer deinterlacer) { + this(id, callbackMode, iminfo, deinterlacer, null, null); + } + + /** + * Special constructor with preallocated buffer. + *

      + *

      + * Same as {@link #IdatSet(String, ImageInfo, Deinterlacer)}, but you can + * pass a Inflater (will be reset internally), and a buffer (will be used + * only if size is enough) + */ + public IdatSet(String id, boolean callbackMode, ImageInfo iminfo, Deinterlacer deinterlacer, Inflater inf, + byte[] buffer) { + super(id, callbackMode, deinterlacer != null ? deinterlacer.getBytesToRead() + 1 : iminfo.bytesPerRow + 1, + iminfo.bytesPerRow + 1, inf, buffer); + this.imgInfo = iminfo; + this.deinterlacer = deinterlacer; + this.rowinfo = new RowInfo(iminfo, deinterlacer); + LOGGER.fine("Creating IDAT set "); + } + + /** + * Applies PNG un-filter to inflated raw line. Result in + * {@link #getUnfilteredRow()} {@link #getRowLen()} + */ + public void unfilterRow() { + unfilterRow(rowinfo.bytesRow); + } + + // nbytes: NOT including the filter byte. leaves result in rowUnfiltered + protected void unfilterRow(int nbytes) { + if (rowUnfiltered == null || rowUnfiltered.length < row.length) { + rowUnfiltered = new byte[row.length]; + rowUnfilteredPrev = new byte[row.length]; + } + if (rowinfo.rowNsubImg == 0) { + Arrays.fill(rowUnfiltered, (byte) 0); // see swap that follows + } + // swap + byte[] tmp = rowUnfiltered; + rowUnfiltered = rowUnfilteredPrev; + rowUnfilteredPrev = tmp; + + int ftn = row[0]; + if (!FilterType.isValidStandard(ftn)) { + throw new PngjInputException("Filter type " + ftn + " invalid"); + } + FilterType ft = FilterType.getByVal(ftn); + filterUseStat[ftn]++; + rowUnfiltered[0] = row[0]; // we copy the filter type, can be useful + switch (ft) { + case FILTER_NONE: + unfilterRowNone(nbytes); + break; + case FILTER_SUB: + unfilterRowSub(nbytes); + break; + case FILTER_UP: + unfilterRowUp(nbytes); + break; + case FILTER_AVERAGE: + unfilterRowAverage(nbytes); + break; + case FILTER_PAETH: + unfilterRowPaeth(nbytes); + break; + default: + throw new PngjInputException("Filter type " + ftn + " not implemented"); + } + } + + private void unfilterRowAverage(final int nbytes) { + int i, j, x; + for (j = 1 - imgInfo.bytesPixel, i = 1; i <= nbytes; i++, j++) { + x = j > 0 ? (rowUnfiltered[j] & 0xff) : 0; + rowUnfiltered[i] = (byte) (row[i] + (x + (rowUnfilteredPrev[i] & 0xFF)) / 2); + } + } + + private void unfilterRowNone(final int nbytes) { + for (int i = 1; i <= nbytes; i++) { + rowUnfiltered[i] = row[i]; + } + } + + private void unfilterRowPaeth(final int nbytes) { + int i, j, x, y; + for (j = 1 - imgInfo.bytesPixel, i = 1; i <= nbytes; i++, j++) { + x = j > 0 ? (rowUnfiltered[j] & 0xFF) : 0; + y = j > 0 ? (rowUnfilteredPrev[j] & 0xFF) : 0; + rowUnfiltered[i] = (byte) (row[i] + + PngHelperInternal.filterPaethPredictor(x, rowUnfilteredPrev[i] & 0xFF, y)); + } + } + + private void unfilterRowSub(final int nbytes) { + int i, j; + for (i = 1; i <= imgInfo.bytesPixel; i++) { + rowUnfiltered[i] = row[i]; + } + for (j = 1, i = imgInfo.bytesPixel + 1; i <= nbytes; i++, j++) { + rowUnfiltered[i] = (byte) (row[i] + rowUnfiltered[j]); + } + } + + private void unfilterRowUp(final int nbytes) { + for (int i = 1; i <= nbytes; i++) { + rowUnfiltered[i] = (byte) (row[i] + rowUnfilteredPrev[i]); + } + } + + /** + * does the unfiltering of the inflated row, and updates row info + */ + @Override + protected void preProcessRow() { + super.preProcessRow(); + rowinfo.update(getRown()); + unfilterRow(); + rowinfo.updateBuf(rowUnfiltered, rowinfo.bytesRow + 1); + } + + /** + * Method for async/callback mode . + *

      + * In callback mode will be called as soon as each row is retrieved + * (inflated and unfiltered), after {@link #preProcessRow()} + *

      + * This is a dummy implementation (this normally should be overriden) that + * does nothing more than compute the length of next row. + *

      + * The return value is essential + *

      + * + * @return Length of next row, in bytes (including filter byte), + * non-positive if done + */ + @Override + protected int processRowCallback() { + int bytesNextRow = advanceToNextRow(); + return bytesNextRow; + } + + @Override + protected void processDoneCallback() { + super.processDoneCallback(); + } + + /** + * Signals that we are done with the previous row, begin reading the next + * one. + *

      + * In polled mode, calls setNextRowLen() + *

      + * Warning: after calling this, the unfilterRow is invalid! + * + * @return Returns nextRowLen + */ + public int advanceToNextRow() { + // PngHelperInternal.LOGGER.info("advanceToNextRow"); + int bytesNextRow; + if (deinterlacer == null) { + bytesNextRow = getRown() >= imgInfo.rows - 1 ? 0 : imgInfo.bytesPerRow + 1; + } else { + boolean more = deinterlacer.nextRow(); + bytesNextRow = more ? deinterlacer.getBytesToRead() + 1 : 0; + } + if (!callbackMode) { // in callback mode, setNextRowLen() is called internally + prepareForNextRow(bytesNextRow); + } + return bytesNextRow; + } + + public boolean isRowReady() { + return !isWaitingForMoreInput(); + + } + + /** + * Unfiltered row. + *

      + * This should be called only if {@link #isRowReady()} returns true. + *

      + * To get real length, use {@link #getRowLen()} + *

      + * + * @return Unfiltered row, includes filter byte + */ + public byte[] getUnfilteredRow() { + return rowUnfiltered; + } + + public Deinterlacer getDeinterlacer() { + return deinterlacer; + } + + void updateCrcs(Checksum... idatCrcs) { + for (Checksum idatCrca : idatCrcs) { + if (idatCrca != null)// just for testing + { + idatCrca.update(getUnfilteredRow(), 1, getRowFilled() - 1); + } + } + } + + @Override + public void close() { + super.close(); + rowUnfiltered = null;// not really necessary... + rowUnfilteredPrev = null; + } + + /** + * Only for debug/stats + * + * @return Array of 5 integers (sum equal numbers of rows) counting each + * filter use + */ + public int[] getFilterUseStat() { + return filterUseStat; + } + +} diff --git a/png/src/main/java/org/xbib/graphics/imageio/plugins/png/pngj/ImageInfo.java b/png/src/main/java/org/xbib/graphics/imageio/plugins/png/pngj/ImageInfo.java new file mode 100644 index 0000000..14ead0a --- /dev/null +++ b/png/src/main/java/org/xbib/graphics/imageio/plugins/png/pngj/ImageInfo.java @@ -0,0 +1,273 @@ +package org.xbib.graphics.imageio.plugins.png.pngj; + +import java.util.zip.Checksum; + +/** + * Simple immutable wrapper for basic image info. + * Some parameters are redundant, but the constructor receives an 'orthogonal' + * subset. + * ref: http://www.w3.org/TR/PNG/#11IHDR + */ +public class ImageInfo { + + /** + * Absolute allowed maximum value for rows and cols (2^24 ~16 million). + * (bytesPerRow must fit in a 32bit integer, though total amount of pixels + * not necessarily). + */ + public static final int MAX_COLS_ROW = 16777216; + + /** + * Cols= Image width, in pixels. + */ + public final int cols; + + /** + * Rows= Image height, in pixels + */ + public final int rows; + + /** + * Bits per sample (per channel) in the buffer (1-2-4-8-16). This is 8-16 + * for RGB/ARGB images, 1-2-4-8 for grayscale. For indexed images, number of + * bits per palette index (1-2-4-8) + */ + public final int bitDepth; + + /** + * Number of channels, as used internally: 3 for RGB, 4 for RGBA, 2 for GA + * (gray with alpha), 1 for grayscale or indexed. + */ + public final int channels; + + /** + * Flag: true if has alpha channel (RGBA/GA) + */ + public final boolean alpha; + + /** + * Flag: true if is grayscale (G/GA) + */ + public final boolean greyscale; + + /** + * Flag: true if image is indexed, i.e., it has a palette + */ + public final boolean indexed; + + /** + * Flag: true if image internally uses less than one byte per sample (bit + * depth 1-2-4) + */ + public final boolean packed; + + /** + * Bits used for each pixel in the buffer: channel * bitDepth + */ + public final int bitspPixel; + + /** + * rounded up value: this is only used internally for filter + */ + public final int bytesPixel; + + /** + * ceil(bitspp*cols/8) - does not include filter + */ + public final int bytesPerRow; + + /** + * Equals cols * channels + */ + public final int samplesPerRow; + + /** + * Amount of "packed samples" : when several samples are stored in a single + * byte (bitdepth 1,2 4) they are counted as one "packed sample". This is + * less that samplesPerRow only when bitdepth is 1-2-4 (flag packed = true) + *

      + * This equals the number of elements in the scanline array if working with + * packedMode=true + *

      + * For internal use, client code should rarely access this. + */ + public final int samplesPerRowPacked; + + private long totalPixels = -1; // lazy getter + + private long totalRawBytes = -1; // lazy getter + + /** + * Short constructor: assumes truecolor (RGB/RGBA) + */ + public ImageInfo(int cols, int rows, int bitdepth, boolean alpha) { + this(cols, rows, bitdepth, alpha, false, false); + } + + /** + * Full constructor + * + * @param cols Width in pixels + * @param rows Height in pixels + * @param bitdepth Bits per sample, in the buffer : 8-16 for RGB true color and + * greyscale + * @param alpha Flag: has an alpha channel (RGBA or GA) + * @param grayscale Flag: is gray scale (any bitdepth, with or without alpha) + * @param indexed Flag: has palette + */ + public ImageInfo(int cols, int rows, int bitdepth, boolean alpha, boolean grayscale, boolean indexed) { + this.cols = cols; + this.rows = rows; + this.alpha = alpha; + this.indexed = indexed; + this.greyscale = grayscale; + if (greyscale && indexed) { + throw new PngjException("palette and greyscale are mutually exclusive"); + } + this.channels = (grayscale || indexed) ? (alpha ? 2 : 1) : (alpha ? 4 : 3); + // http://www.w3.org/TR/PNG/#11IHDR + this.bitDepth = bitdepth; + this.packed = bitdepth < 8; + this.bitspPixel = (channels * this.bitDepth); + this.bytesPixel = (bitspPixel + 7) / 8; + this.bytesPerRow = (bitspPixel * cols + 7) / 8; + this.samplesPerRow = channels * this.cols; + this.samplesPerRowPacked = packed ? bytesPerRow : samplesPerRow; + // several checks + switch (this.bitDepth) { + case 1: + case 2: + case 4: + if (!(this.indexed || this.greyscale)) { + throw new PngjException("only indexed or grayscale can have bitdepth=" + this.bitDepth); + } + break; + case 8: + break; + case 16: + if (this.indexed) { + throw new PngjException("indexed can't have bitdepth=" + this.bitDepth); + } + break; + default: + throw new PngjException("invalid bitdepth=" + this.bitDepth); + } + if (cols < 1 || cols > MAX_COLS_ROW) { + throw new PngjException("invalid cols=" + cols + " ???"); + } + if (rows < 1 || rows > MAX_COLS_ROW) { + throw new PngjException("invalid rows=" + rows + " ???"); + } + if (samplesPerRow < 1) { + throw new PngjException("invalid image parameters (overflow?)"); + } + } + + /** + * returns a copy with different size + * + * @param cols if non-positive, the original is used + * @param rows if non-positive, the original is used + * @return a new copy with the specified size and same properties + */ + public ImageInfo withSize(int cols, int rows) { + return new ImageInfo(cols > 0 ? cols : this.cols, rows > 0 ? rows : this.rows, this.bitDepth, this.alpha, + this.greyscale, this.indexed); + } + + public long getTotalPixels() { + if (totalPixels < 0) { + totalPixels = cols * (long) rows; + } + return totalPixels; + } + + /** + * Total uncompressed bytes in IDAT, including filter byte. This is not + * valid for interlaced. + */ + public long getTotalRawBytes() { + if (totalRawBytes < 0) { + totalRawBytes = (bytesPerRow + 1) * (long) rows; + } + return totalRawBytes; + } + + @Override + public String toString() { + return "ImageInfo [cols=" + cols + ", rows=" + rows + ", bitDepth=" + bitDepth + ", channels=" + channels + + ", alpha=" + alpha + ", greyscale=" + greyscale + ", indexed=" + indexed + "]"; + } + + /** + * Brief info: COLSxROWS[dBITDEPTH][a][p][g] ( the default dBITDEPTH='d8' is + * ommited) + **/ + public String toStringBrief() { + return cols + "x" + rows + (bitDepth != 8 ? ("d" + bitDepth) : "") + (alpha ? "a" : "") + + (indexed ? "p" : "") + (greyscale ? "g" : ""); + } + + public String toStringDetail() { + return "ImageInfo [cols=" + cols + ", rows=" + rows + ", bitDepth=" + bitDepth + ", channels=" + channels + + ", bitspPixel=" + bitspPixel + ", bytesPixel=" + bytesPixel + ", bytesPerRow=" + bytesPerRow + + ", samplesPerRow=" + samplesPerRow + ", samplesPerRowP=" + samplesPerRowPacked + ", alpha=" + alpha + + ", greyscale=" + greyscale + ", indexed=" + indexed + ", packed=" + packed + "]"; + } + + void updateCrc(Checksum crc) { + crc.update((byte) rows); + crc.update((byte) (rows >> 8)); + crc.update((byte) (rows >> 16)); + crc.update((byte) cols); + crc.update((byte) (cols >> 8)); + crc.update((byte) (cols >> 16)); + crc.update((byte) (bitDepth)); + crc.update((byte) (indexed ? 1 : 2)); + crc.update((byte) (greyscale ? 3 : 4)); + crc.update((byte) (alpha ? 3 : 4)); + } + + @Override + public int hashCode() { + final int prime = 31; + int result = 1; + result = prime * result + (alpha ? 1231 : 1237); + result = prime * result + bitDepth; + result = prime * result + cols; + result = prime * result + (greyscale ? 1231 : 1237); + result = prime * result + (indexed ? 1231 : 1237); + result = prime * result + rows; + return result; + } + + @Override + public boolean equals(Object obj) { + if (this == obj) { + return true; + } + if (obj == null) { + return false; + } + if (getClass() != obj.getClass()) { + return false; + } + ImageInfo other = (ImageInfo) obj; + if (alpha != other.alpha) { + return false; + } + if (bitDepth != other.bitDepth) { + return false; + } + if (cols != other.cols) { + return false; + } + if (greyscale != other.greyscale) { + return false; + } + if (indexed != other.indexed) { + return false; + } + return rows == other.rows; + } +} diff --git a/png/src/main/java/org/xbib/graphics/imageio/plugins/png/pngj/ImageLineByte.java b/png/src/main/java/org/xbib/graphics/imageio/plugins/png/pngj/ImageLineByte.java new file mode 100644 index 0000000..b5e04c9 --- /dev/null +++ b/png/src/main/java/org/xbib/graphics/imageio/plugins/png/pngj/ImageLineByte.java @@ -0,0 +1,190 @@ +package org.xbib.graphics.imageio.plugins.png.pngj; + +/** + * Lightweight wrapper for an image scanline, used for read and write. + *

      + * This object can be (usually it is) reused while iterating over the image + * lines. + *

      + * See scanline field, to understand the format. + *

      + * Format: byte (one bytes per sample) (for 16bpp the extra byte is placed in an + * extra array) + */ +public class ImageLineByte implements IImageLine, IImageLineArray { + public final ImageInfo imgInfo; + + final byte[] scanline; + final byte[] scanline2; // only used for 16 bpp (less significant byte) Normally you'd prefer + // ImageLineInt in this case + + protected FilterType filterType; // informational ; only filled by the reader. not significant for + // interlaced + final int size; // = imgInfo.samplePerRowPacked, if packed:imgInfo.samplePerRow elswhere + + public ImageLineByte(ImageInfo imgInfo) { + this(imgInfo, null); + } + + public ImageLineByte(ImageInfo imgInfo, byte[] sci) { + this.imgInfo = imgInfo; + filterType = FilterType.FILTER_UNKNOWN; + size = imgInfo.samplesPerRow; + scanline = sci != null && sci.length >= size ? sci : new byte[size]; + scanline2 = imgInfo.bitDepth == 16 ? new byte[size] : null; + } + + /** + * Returns a factory for this object + */ + public static IImageLineFactory getFactory() { + return new IImageLineFactory() { + public ImageLineByte createImageLine(ImageInfo iminfo) { + return new ImageLineByte(iminfo); + } + }; + } + + public FilterType getFilterUsed() { + return filterType; + } + + /** + * One byte per sample. This can be used also for 16bpp images, but in this + * case this loses the less significant 8-bits ; see also getScanlineByte2 + * and getElem. + */ + public byte[] getScanlineByte() { + return scanline; + } + + /** + * only for 16bpp (less significant byte) + * + * @return null for less than 16bpp + */ + public byte[] getScanlineByte2() { + return scanline2; + } + + /** + * Basic info + */ + public String toString() { + return " cols=" + imgInfo.cols + " bpc=" + imgInfo.bitDepth + " size=" + scanline.length; + } + + public void readFromPngRaw(byte[] raw, final int len, final int offset, final int step) { + filterType = FilterType.getByVal(raw[0]); // only for non interlaced line the filter is significative + int len1 = len - 1; + int step1 = (step - 1) * imgInfo.channels; + if (imgInfo.bitDepth == 8) { + if (step == 1) {// 8bispp non-interlaced: most important case, should be optimized + System.arraycopy(raw, 1, scanline, 0, len1); + } else {// 8bispp interlaced + for (int s = 1, c = 0, i = offset * imgInfo.channels; s <= len1; s++, i++) { + scanline[i] = raw[s]; + c++; + if (c == imgInfo.channels) { + c = 0; + i += step1; + } + } + } + } else if (imgInfo.bitDepth == 16) { + if (step == 1) {// 16bispp non-interlaced + for (int i = 0, s = 1; i < imgInfo.samplesPerRow; i++) { + scanline[i] = raw[s++]; // get the first byte + scanline2[i] = raw[s++]; // get the first byte + } + } else { + for (int s = 1, c = 0, i = offset != 0 ? offset * imgInfo.channels : 0; s <= len1; i++) { + scanline[i] = raw[s++]; + scanline2[i] = raw[s++]; + c++; + if (c == imgInfo.channels) { + c = 0; + i += step1; + } + } + } + } else { // packed formats + int mask0, mask, shi, bd; + bd = imgInfo.bitDepth; + mask0 = ImageLineHelper.getMaskForPackedFormats(bd); + for (int i = offset * imgInfo.channels, r = 1, c = 0; r < len; r++) { + mask = mask0; + shi = 8 - bd; + do { + scanline[i] = (byte) ((raw[r] & mask) >> shi); + mask >>= bd; + shi -= bd; + i++; + c++; + if (c == imgInfo.channels) { + c = 0; + i += step1; + } + } while (mask != 0 && i < size); + } + } + } + + public void writeToPngRaw(byte[] raw) { + raw[0] = (byte) filterType.val; + if (imgInfo.bitDepth == 8) { + System.arraycopy(scanline, 0, raw, 1, size); + } else if (imgInfo.bitDepth == 16) { + for (int i = 0, s = 1; i < size; i++) { + raw[s++] = scanline[i]; + raw[s++] = scanline2[i]; + } + } else { // packed formats + int shi, bd, v; + bd = imgInfo.bitDepth; + shi = 8 - bd; + v = 0; + for (int i = 0, r = 1; i < size; i++) { + v |= (scanline[i] << shi); + shi -= bd; + if (shi < 0 || i == size - 1) { + raw[r++] = (byte) v; + shi = 8 - bd; + v = 0; + } + } + } + } + + public void endReadFromPngRaw() { + } + + public int getSize() { + return size; + } + + public int getElem(int i) { + return scanline2 == null ? scanline[i] & 0xFF : ((scanline[i] & 0xFF) << 8) | (scanline2[i] & 0xFF); + } + + public byte[] getScanline() { + return scanline; + } + + public ImageInfo getImageInfo() { + return imgInfo; + } + + public FilterType getFilterType() { + return filterType; + } + + /** + * This should rarely be used by client code. Only relevant if + * FilterPreserve==true + */ + public void setFilterType(FilterType ft) { + filterType = ft; + } + +} diff --git a/png/src/main/java/org/xbib/graphics/imageio/plugins/png/pngj/ImageLineHelper.java b/png/src/main/java/org/xbib/graphics/imageio/plugins/png/pngj/ImageLineHelper.java new file mode 100644 index 0000000..76f3455 --- /dev/null +++ b/png/src/main/java/org/xbib/graphics/imageio/plugins/png/pngj/ImageLineHelper.java @@ -0,0 +1,515 @@ +package org.xbib.graphics.imageio.plugins.png.pngj; + +import java.util.Arrays; +import org.xbib.graphics.imageio.plugins.png.pngj.chunks.PngChunkPLTE; +import org.xbib.graphics.imageio.plugins.png.pngj.chunks.PngChunkTRNS; + +/** + * Bunch of utility static methods to proces an image line at the pixel level. + *

      + * WARNING: this has little testing/optimizing, and this API is not stable. some + * methods will probably be changed or removed if future releases. + *

      + * WARNING: most methods for getting/setting values work currently only for + * ImageLine or ImageLineByte + */ +public class ImageLineHelper { + + static int[] DEPTH_UNPACK_1; + static int[] DEPTH_UNPACK_2; + static int[] DEPTH_UNPACK_4; + static int[][] DEPTH_UNPACK; + + static { + initDepthScale(); + } + + private static void initDepthScale() { + DEPTH_UNPACK_1 = new int[2]; + for (int i = 0; i < 2; i++) { + DEPTH_UNPACK_1[i] = i * 255; + } + DEPTH_UNPACK_2 = new int[4]; + for (int i = 0; i < 4; i++) { + DEPTH_UNPACK_2[i] = (i * 255) / 3; + } + DEPTH_UNPACK_4 = new int[16]; + for (int i = 0; i < 16; i++) { + DEPTH_UNPACK_4[i] = (i * 255) / 15; + } + DEPTH_UNPACK = new int[][]{null, DEPTH_UNPACK_1, DEPTH_UNPACK_2, null, DEPTH_UNPACK_4}; + } + + /** + * When the bitdepth is less than 8, the imageLine is usually + * returned/expected unscaled. This method upscales it in place. Eg, if + * bitdepth=1, values 0-1 will be converted to 0-255 + */ + public static void scaleUp(IImageLineArray line) { + if (line.getImageInfo().indexed || line.getImageInfo().bitDepth >= 8) { + return; + } + final int[] scaleArray = DEPTH_UNPACK[line.getImageInfo().bitDepth]; + if (line instanceof ImageLineInt) { + ImageLineInt iline = (ImageLineInt) line; + for (int i = 0; i < iline.getSize(); i++) { + iline.scanline[i] = scaleArray[iline.scanline[i]]; + } + } else if (line instanceof ImageLineByte) { + ImageLineByte iline = (ImageLineByte) line; + for (int i = 0; i < iline.getSize(); i++) { + iline.scanline[i] = (byte) scaleArray[iline.scanline[i]]; + } + } else { + throw new PngjException("not implemented"); + } + } + + /** + * Reverse of {@link #scaleUp(IImageLineArray)} + */ + public static void scaleDown(IImageLineArray line) { + if (line.getImageInfo().indexed || line.getImageInfo().bitDepth >= 8) { + return; + } + if (line instanceof ImageLineInt) { + final int scalefactor = 8 - line.getImageInfo().bitDepth; + if (line instanceof ImageLineInt) { + ImageLineInt iline = (ImageLineInt) line; + for (int i = 0; i < line.getSize(); i++) { + iline.scanline[i] = iline.scanline[i] >> scalefactor; + } + } else if (line instanceof ImageLineByte) { + ImageLineByte iline = (ImageLineByte) line; + for (int i = 0; i < line.getSize(); i++) { + iline.scanline[i] = (byte) ((iline.scanline[i] & 0xFF) >> scalefactor); + } + } + } else { + throw new PngjException("not implemented"); + } + } + + public static byte scaleUp(int bitdepth, byte v) { + return bitdepth < 8 ? (byte) DEPTH_UNPACK[bitdepth][v] : v; + } + + public static byte scaleDown(int bitdepth, byte v) { + return bitdepth < 8 ? (byte) (v >> (8 - bitdepth)) : v; + } + + /** + * Given an indexed line with a palette, unpacks as a RGB array, or RGBA if + * a non nul PngChunkTRNS chunk is passed + * + * @param line ImageLine as returned from PngReader + * @param pal Palette chunk + * @param trns Transparency chunk, can be null (absent) + * @param buf Preallocated array, optional + * @return R G B (A), one sample 0-255 per array element. Ready for + * pngw.writeRowInt() + */ + public static int[] palette2rgb(ImageLineInt line, PngChunkPLTE pal, PngChunkTRNS trns, int[] buf) { + return palette2rgb(line, pal, trns, buf, false); + } + + /** + * Warning: the line should be upscaled, see + * {@link #scaleUp(IImageLineArray)} + */ + static int[] lineToARGB32(ImageLineByte line, PngChunkPLTE pal, PngChunkTRNS trns, int[] buf) { + boolean alphachannel = line.imgInfo.alpha; + int cols = line.getImageInfo().cols; + if (buf == null || buf.length < cols) { + buf = new int[cols]; + } + int index, rgb, alpha, ga, g; + if (line.getImageInfo().indexed) {// palette + int nindexesWithAlpha = trns != null ? trns.getPalletteAlpha().length : 0; + for (int c = 0; c < cols; c++) { + index = line.scanline[c] & 0xFF; + rgb = pal.getEntry(index); + alpha = index < nindexesWithAlpha ? trns.getPalletteAlpha()[index] : 255; + buf[c] = (alpha << 24) | rgb; + } + } else if (line.imgInfo.greyscale) { // gray + ga = trns != null ? trns.getGray() : -1; + for (int c = 0, c2 = 0; c < cols; c++) { + g = (line.scanline[c2++] & 0xFF); + alpha = alphachannel ? line.scanline[c2++] & 0xFF : (g != ga ? 255 : 0); + buf[c] = (alpha << 24) | g | (g << 8) | (g << 16); + } + } else { // true color + ga = trns != null ? trns.getRGB888() : -1; + for (int c = 0, c2 = 0; c < cols; c++) { + rgb = ((line.scanline[c2++] & 0xFF) << 16) | ((line.scanline[c2++] & 0xFF) << 8) + | (line.scanline[c2++] & 0xFF); + alpha = alphachannel ? line.scanline[c2++] & 0xFF : (rgb != ga ? 255 : 0); + buf[c] = (alpha << 24) | rgb; + } + } + return buf; + } + + /** + * Warning: the line should be upscaled, see + * {@link #scaleUp(IImageLineArray)} + */ + static byte[] lineToRGBA8888(ImageLineByte line, PngChunkPLTE pal, PngChunkTRNS trns, byte[] buf) { + boolean alphachannel = line.imgInfo.alpha; + int cols = line.imgInfo.cols; + int bytes = cols * 4; + if (buf == null || buf.length < bytes) { + buf = new byte[bytes]; + } + int index, rgb, ga; + byte val; + if (line.imgInfo.indexed) {// palette + int nindexesWithAlpha = trns != null ? trns.getPalletteAlpha().length : 0; + for (int c = 0, b = 0; c < cols; c++) { + index = line.scanline[c] & 0xFF; + rgb = pal.getEntry(index); + buf[b++] = (byte) ((rgb >> 16) & 0xFF); + buf[b++] = (byte) ((rgb >> 8) & 0xFF); + buf[b++] = (byte) (rgb & 0xFF); + buf[b++] = (byte) (index < nindexesWithAlpha ? trns.getPalletteAlpha()[index] : 255); + } + } else if (line.imgInfo.greyscale) { // + ga = trns != null ? trns.getGray() : -1; + for (int c = 0, b = 0; b < bytes; ) { + val = line.scanline[c++]; + buf[b++] = val; + buf[b++] = val; + buf[b++] = val; + buf[b++] = alphachannel ? line.scanline[c++] : ((val & 0xFF) == ga) ? (byte) 0 : (byte) 255; + } + } else { // true color + if (alphachannel) // same format! + { + System.arraycopy(line.scanline, 0, buf, 0, bytes); + } else { + for (int c = 0, b = 0; b < bytes; ) { + buf[b++] = line.scanline[c++]; + buf[b++] = line.scanline[c++]; + buf[b++] = line.scanline[c++]; + buf[b++] = (byte) (255); // tentative (probable) + if (trns != null && buf[b - 3] == (byte) trns.getRGB()[0] && buf[b - 2] == (byte) trns.getRGB()[1] + && buf[b - 1] == (byte) trns.getRGB()[2]) // not + // very + // efficient, + // but + // not + // frecuent + { + buf[b - 1] = 0; + } + } + } + } + return buf; + } + + static byte[] lineToRGB888(ImageLineByte line, PngChunkPLTE pal, byte[] buf) { + boolean alphachannel = line.imgInfo.alpha; + int cols = line.imgInfo.cols; + int bytes = cols * 3; + if (buf == null || buf.length < bytes) { + buf = new byte[bytes]; + } + byte val; + int[] rgb = new int[3]; + if (line.imgInfo.indexed) {// palette + for (int c = 0, b = 0; c < cols; c++) { + pal.getEntryRgb(line.scanline[c] & 0xFF, rgb); + buf[b++] = (byte) rgb[0]; + buf[b++] = (byte) rgb[1]; + buf[b++] = (byte) rgb[2]; + } + } else if (line.imgInfo.greyscale) { // + for (int c = 0, b = 0; b < bytes; ) { + val = line.scanline[c++]; + buf[b++] = val; + buf[b++] = val; + buf[b++] = val; + if (alphachannel) { + c++; // skip alpha + } + } + } else { // true color + if (!alphachannel) // same format! + { + System.arraycopy(line.scanline, 0, buf, 0, bytes); + } else { + for (int c = 0, b = 0; b < bytes; ) { + buf[b++] = line.scanline[c++]; + buf[b++] = line.scanline[c++]; + buf[b++] = line.scanline[c++]; + c++;// skip alpha + } + } + } + return buf; + } + + /** + * Same as palette2rgbx , but returns rgba always, even if trns is null + * + * @param line ImageLine as returned from PngReader + * @param pal Palette chunk + * @param trns Transparency chunk, can be null (absent) + * @param buf Preallocated array, optional + * @return R G B (A), one sample 0-255 per array element. Ready for + * pngw.writeRowInt() + */ + public static int[] palette2rgba(ImageLineInt line, PngChunkPLTE pal, PngChunkTRNS trns, int[] buf) { + return palette2rgb(line, pal, trns, buf, true); + } + + public static int[] palette2rgb(ImageLineInt line, PngChunkPLTE pal, int[] buf) { + return palette2rgb(line, pal, null, buf, false); + } + + /** + * this is not very efficient, only for tests and troubleshooting + */ + public static int[] convert2rgba(IImageLineArray line, PngChunkPLTE pal, PngChunkTRNS trns, int[] buf) { + ImageInfo imi = line.getImageInfo(); + int nsamples = imi.cols * 4; + if (buf == null || buf.length < nsamples) { + buf = new int[nsamples]; + } + int maxval = imi.bitDepth == 16 ? (1 << 16) - 1 : 255; + Arrays.fill(buf, maxval); + + if (imi.indexed) { + int tlen = trns != null ? trns.getPalletteAlpha().length : 0; + for (int s = 0; s < imi.cols; s++) { + int index = line.getElem(s); + pal.getEntryRgb(index, buf, s * 4); + if (index < tlen) { + buf[s * 4 + 3] = trns.getPalletteAlpha()[index]; + } + } + } else if (imi.greyscale) { + int[] unpack = null; + if (imi.bitDepth < 8) { + unpack = ImageLineHelper.DEPTH_UNPACK[imi.bitDepth]; + } + for (int s = 0, i = 0, p = 0; p < imi.cols; p++) { + buf[s++] = unpack != null ? unpack[line.getElem(i++)] : line.getElem(i++); + buf[s] = buf[s - 1]; + s++; + buf[s] = buf[s - 1]; + s++; + if (imi.channels == 2) { + buf[s++] = unpack != null ? unpack[line.getElem(i++)] : line.getElem(i++); + } else { + buf[s++] = maxval; + } + } + } else { + for (int s = 0, i = 0, p = 0; p < imi.cols; p++) { + buf[s++] = line.getElem(i++); + buf[s++] = line.getElem(i++); + buf[s++] = line.getElem(i++); + buf[s++] = imi.alpha ? line.getElem(i++) : maxval; + } + } + return buf; + } + + private static int[] palette2rgb(IImageLine line, PngChunkPLTE pal, PngChunkTRNS trns, int[] buf, + boolean alphaForced) { + boolean isalpha = trns != null; + int channels = isalpha ? 4 : 3; + ImageLineInt linei = (ImageLineInt) (line instanceof ImageLineInt ? line : null); + ImageLineByte lineb = (ImageLineByte) (line instanceof ImageLineByte ? line : null); + boolean isbyte = lineb != null; + int cols = linei != null ? linei.imgInfo.cols : lineb.imgInfo.cols; + int nsamples = cols * channels; + if (buf == null || buf.length < nsamples) { + buf = new int[nsamples]; + } + int nindexesWithAlpha = trns != null ? trns.getPalletteAlpha().length : 0; + for (int c = 0; c < cols; c++) { + int index = isbyte ? (lineb.scanline[c] & 0xFF) : linei.scanline[c]; + pal.getEntryRgb(index, buf, c * channels); + if (isalpha) { + int alpha = index < nindexesWithAlpha ? trns.getPalletteAlpha()[index] : 255; + buf[c * channels + 3] = alpha; + } + } + return buf; + } + + /** + * what follows is pretty uninteresting/untested/obsolete, subject to change + */ + /** + * Just for basic info or debugging. Shows values for first and last pixel. + * Does not include alpha + */ + public static String infoFirstLastPixels(ImageLineInt line) { + return line.imgInfo.channels == 1 + ? String.format("first=(%d) last=(%d)", line.scanline[0], line.scanline[line.scanline.length - 1]) + : String.format("first=(%d %d %d) last=(%d %d %d)", line.scanline[0], line.scanline[1], + line.scanline[2], line.scanline[line.scanline.length - line.imgInfo.channels], + line.scanline[line.scanline.length - line.imgInfo.channels + 1], + line.scanline[line.scanline.length - line.imgInfo.channels + 2]); + } + + /** + * Returns pixel as integer packed [A R G B] + *

      + * This only makes sense for ARGB images, with bitdepth=8 (this is not + * checked!) + **/ + public static int getPixelARGB8(IImageLine line, int column) { + if (line instanceof ImageLineInt) { + if (((ImageLineInt) line).imgInfo.channels != 4) { + return getPixelRGB8(line, column); + } + int offset = column * ((ImageLineInt) line).imgInfo.channels; + int[] scanline = ((ImageLineInt) line).getScanline(); + return (scanline[offset + 3] << 24) | (scanline[offset] << 16) | (scanline[offset + 1] << 8) + | (scanline[offset + 2]); + } else if (line instanceof ImageLineByte) { + if (((ImageLineByte) line).imgInfo.channels != 4) { + return getPixelRGB8(line, column); + } + int offset = column * ((ImageLineByte) line).imgInfo.channels; + byte[] scanline = ((ImageLineByte) line).getScanline(); + return (((scanline[offset + 3] & 0xff) << 24) | ((scanline[offset] & 0xff) << 16) + | ((scanline[offset + 1] & 0xff) << 8) | ((scanline[offset + 2] & 0xff))); + } else { + throw new PngjUnsupportedException("Not supported " + line.getClass()); + } + } + + /** + * Returns pixel as integer packed [(A) R G B] with A=0xff + *

      + * This only makes sense for RGB images, with bitdepth=8 (this is not + * checked!) + **/ + public static int getPixelRGB8(IImageLine line, int column) { + if (line instanceof ImageLineInt) { + int offset = column * ((ImageLineInt) line).imgInfo.channels; + int[] scanline = ((ImageLineInt) line).getScanline(); + return (0xff << 24) | (scanline[offset] << 16) | (scanline[offset + 1] << 8) | (scanline[offset + 2]); + } else if (line instanceof ImageLineByte) { + int offset = column * ((ImageLineByte) line).imgInfo.channels; + byte[] scanline = ((ImageLineByte) line).getScanline(); + return (0xff << 24) | ((scanline[offset] & 0xff) << 16) | ((scanline[offset + 1] & 0xff) << 8) + | ((scanline[offset + 2] & 0xff)); + } else { + throw new PngjUnsupportedException("Not supported " + line.getClass()); + } + } + + public static void setPixelsRGB8(ImageLineInt line, int[] rgb) { + for (int i = 0, j = 0; i < line.imgInfo.cols; i++) { + line.scanline[j++] = ((rgb[i] >> 16) & 0xFF); + line.scanline[j++] = ((rgb[i] >> 8) & 0xFF); + line.scanline[j++] = ((rgb[i] & 0xFF)); + } + } + + public static void setPixelRGB8(ImageLineInt line, int col, int r, int g, int b) { + col *= line.imgInfo.channels; + line.scanline[col++] = r; + line.scanline[col++] = g; + line.scanline[col] = b; + } + + public static void setPixelRGB8(ImageLineInt line, int col, int rgb) { + setPixelRGB8(line, col, (rgb >> 16) & 0xFF, (rgb >> 8) & 0xFF, rgb & 0xFF); + } + + public static void setPixelsRGBA8(ImageLineInt line, int[] rgb) { + for (int i = 0, j = 0; i < line.imgInfo.cols; i++) { + line.scanline[j++] = ((rgb[i] >> 16) & 0xFF); + line.scanline[j++] = ((rgb[i] >> 8) & 0xFF); + line.scanline[j++] = ((rgb[i] & 0xFF)); + line.scanline[j++] = ((rgb[i] >> 24) & 0xFF); + } + } + + public static void setPixelRGBA8(ImageLineInt line, int col, int r, int g, int b, int a) { + col *= line.imgInfo.channels; + line.scanline[col++] = r; + line.scanline[col++] = g; + line.scanline[col++] = b; + line.scanline[col] = a; + } + + public static void setPixelRGBA8(ImageLineInt line, int col, int rgb) { + setPixelRGBA8(line, col, (rgb >> 16) & 0xFF, (rgb >> 8) & 0xFF, rgb & 0xFF, (rgb >> 24) & 0xFF); + } + + public static void setValD(ImageLineInt line, int i, double d) { + line.scanline[i] = double2int(line, d); + } + + public static int interpol(int a, int b, int c, int d, double dx, double dy) { + // a b -> x (0-1) + // c d + double e = a * (1.0 - dx) + b * dx; + double f = c * (1.0 - dx) + d * dx; + return (int) (e * (1 - dy) + f * dy + 0.5); + } + + public static double int2double(ImageLineInt line, int p) { + return line.imgInfo.bitDepth == 16 ? p / 65535.0 : p / 255.0; + // TODO: replace my multiplication? check for other bitdepths + } + + public static double int2doubleClamped(ImageLineInt line, int p) { + // TODO: replace my multiplication? + double d = line.imgInfo.bitDepth == 16 ? p / 65535.0 : p / 255.0; + return d <= 0.0 ? 0 : (d >= 1.0 ? 1.0 : d); + } + + public static int double2int(ImageLineInt line, double d) { + d = d <= 0.0 ? 0 : (d >= 1.0 ? 1.0 : d); + return line.imgInfo.bitDepth == 16 ? (int) (d * 65535.0 + 0.5) : (int) (d * 255.0 + 0.5); // + } + + public static int double2intClamped(ImageLineInt line, double d) { + d = d <= 0.0 ? 0 : (d >= 1.0 ? 1.0 : d); + return line.imgInfo.bitDepth == 16 ? (int) (d * 65535.0 + 0.5) : (int) (d * 255.0 + 0.5); // + } + + public static int clampTo_0_255(int i) { + return i > 255 ? 255 : (i < 0 ? 0 : i); + } + + public static int clampTo_0_65535(int i) { + return i > 65535 ? 65535 : (i < 0 ? 0 : i); + } + + public static int clampTo_128_127(int x) { + return x > 127 ? 127 : (x < -128 ? -128 : x); + } + + public static int getMaskForPackedFormats(int bitDepth) { // Utility function for pack/unpack + if (bitDepth == 4) { + return 0xf0; + } else if (bitDepth == 2) { + return 0xc0; + } else { + return 0x80; // bitDepth == 1 + } + } + + public static int getMaskForPackedFormatsLs(int bitDepth) { // Utility function for pack/unpack + if (bitDepth == 4) { + return 0x0f; + } else if (bitDepth == 2) { + return 0x03; + } else { + return 0x01; // bitDepth == 1 + } + } + +} diff --git a/png/src/main/java/org/xbib/graphics/imageio/plugins/png/pngj/ImageLineInt.java b/png/src/main/java/org/xbib/graphics/imageio/plugins/png/pngj/ImageLineInt.java new file mode 100644 index 0000000..87da68f --- /dev/null +++ b/png/src/main/java/org/xbib/graphics/imageio/plugins/png/pngj/ImageLineInt.java @@ -0,0 +1,199 @@ +package org.xbib.graphics.imageio.plugins.png.pngj; + +/** + * Represents an image line, integer format (one integer by sample). See + * {@link #scanline} to understand the format. + */ +public class ImageLineInt implements IImageLine, IImageLineArray { + public final ImageInfo imgInfo; + + /** + * The 'scanline' is an array of integers, corresponds to an image line + * (row). + *

      + * Each int is a "sample" (one for channel), (0-255 or 0-65535) + * in the corresponding PNG sequence: R G B R G B... or + * R G B A R G B A... or g g g ... or + * i i i (palette index) + *

      + * For bitdepth=1/2/4 the value is not scaled (hence, eg, if bitdepth=2 the + * range will be 0-4) + *

      + * To convert a indexed line to RGB values, see + * {@link ImageLineHelper#palette2rgb(ImageLineInt, org.xbib.graphics.imageio.plugins.png.pngj.chunks.PngChunkPLTE, int[])} + * (you can't do the reverse) + */ + protected final int[] scanline; + + /** + * number of elements in the scanline + */ + protected final int size; + + /** + * informational ; only filled by the reader. not meaningful for interlaced + */ + protected FilterType filterType = FilterType.FILTER_UNKNOWN; + + /** + * @param imgInfo Inmutable ImageInfo, basic parameters of the image we are + * reading or writing + */ + public ImageLineInt(ImageInfo imgInfo) { + this(imgInfo, null); + } + + /** + * @param imgInfo Inmutable ImageInfo, basic parameters of the image we are + * reading or writing + * @param sci prealocated buffer (can be null) + */ + public ImageLineInt(ImageInfo imgInfo, int[] sci) { + this.imgInfo = imgInfo; + filterType = FilterType.FILTER_UNKNOWN; + size = imgInfo.samplesPerRow; + scanline = sci != null && sci.length >= size ? sci : new int[size]; + } + + /** + * Helper method, returns a default factory for this object + */ + public static IImageLineFactory getFactory() { + return new IImageLineFactory() { + public ImageLineInt createImageLine(ImageInfo iminfo) { + return new ImageLineInt(iminfo); + } + }; + } + + public FilterType getFilterType() { + return filterType; + } + + /** + * This should rarely be used by client code. Only relevant if + * FilterPreserve==true + */ + public void setFilterType(FilterType ft) { + filterType = ft; + } + + /** + * Basic info + */ + public String toString() { + return " cols=" + imgInfo.cols + " bpc=" + imgInfo.bitDepth + " size=" + scanline.length; + } + + public void readFromPngRaw(byte[] raw, final int len, final int offset, final int step) { + setFilterType(FilterType.getByVal(raw[0])); + int len1 = len - 1; + int step1 = (step - 1) * imgInfo.channels; + if (imgInfo.bitDepth == 8) { + if (step == 1) {// 8bispp non-interlaced: most important case, should be optimized + for (int i = 0; i < size; i++) { + scanline[i] = (raw[i + 1] & 0xff); + } + } else {// 8bispp interlaced + for (int s = 1, c = 0, i = offset * imgInfo.channels; s <= len1; s++, i++) { + scanline[i] = (raw[s] & 0xff); + c++; + if (c == imgInfo.channels) { + c = 0; + i += step1; + } + } + } + } else if (imgInfo.bitDepth == 16) { + if (step == 1) {// 16bispp non-interlaced + for (int i = 0, s = 1; i < size; i++) { + scanline[i] = ((raw[s++] & 0xFF) << 8) | (raw[s++] & 0xFF); // 16 bitspc + } + } else { + for (int s = 1, c = 0, i = offset != 0 ? offset * imgInfo.channels : 0; s <= len1; s++, i++) { + scanline[i] = ((raw[s++] & 0xFF) << 8) | (raw[s] & 0xFF); // 16 bitspc + c++; + if (c == imgInfo.channels) { + c = 0; + i += step1; + } + } + } + } else { // packed formats + int mask0, mask, shi, bd; + bd = imgInfo.bitDepth; + mask0 = ImageLineHelper.getMaskForPackedFormats(bd); + for (int i = offset * imgInfo.channels, r = 1, c = 0; r < len; r++) { + mask = mask0; + shi = 8 - bd; + do { + scanline[i++] = (raw[r] & mask) >> shi; + mask >>= bd; + shi -= bd; + c++; + if (c == imgInfo.channels) { + c = 0; + i += step1; + } + } while (mask != 0 && i < size); + } + } + } + + public void writeToPngRaw(byte[] raw) { + raw[0] = (byte) filterType.val; + if (imgInfo.bitDepth == 8) { + for (int i = 0; i < size; i++) { + raw[i + 1] = (byte) scanline[i]; + } + } else if (imgInfo.bitDepth == 16) { + for (int i = 0, s = 1; i < size; i++) { + raw[s++] = (byte) (scanline[i] >> 8); + raw[s++] = (byte) (scanline[i] & 0xff); + } + } else { // packed formats + int shi, bd, v; + bd = imgInfo.bitDepth; + shi = 8 - bd; + v = 0; + for (int i = 0, r = 1; i < size; i++) { + v |= (scanline[i] << shi); + shi -= bd; + if (shi < 0 || i == size - 1) { + raw[r++] = (byte) v; + shi = 8 - bd; + v = 0; + } + } + } + } + + /** + * Does nothing in this implementation + */ + public void endReadFromPngRaw() { + + } + + /** + * @see #size + */ + public int getSize() { + return size; + } + + public int getElem(int i) { + return scanline[i]; + } + + /** + * @return see {@link #scanline} + */ + public int[] getScanline() { + return scanline; + } + + public ImageInfo getImageInfo() { + return imgInfo; + } +} diff --git a/png/src/main/java/org/xbib/graphics/imageio/plugins/png/pngj/ImageLineSetDefault.java b/png/src/main/java/org/xbib/graphics/imageio/plugins/png/pngj/ImageLineSetDefault.java new file mode 100644 index 0000000..ba2e109 --- /dev/null +++ b/png/src/main/java/org/xbib/graphics/imageio/plugins/png/pngj/ImageLineSetDefault.java @@ -0,0 +1,167 @@ +package org.xbib.graphics.imageio.plugins.png.pngj; + +import java.util.ArrayList; +import java.util.List; + +/** + * Default implementation of {@link IImageLineSet}. + *

      + * Supports all modes: single cursor, full rows, or partial. This should not be + * used for + */ +public abstract class ImageLineSetDefault implements IImageLineSet { + + protected final ImageInfo imgInfo; + private final boolean singleCursor; + private final int nlines, offset, step; + protected List imageLines; // null if single cursor + protected T imageLine; // null unless single cursor + protected int currentRow = -1; // only relevant (and not much) for cursor + + public ImageLineSetDefault(ImageInfo imgInfo, final boolean singleCursor, final int nlinesx, final int noffsetx, + final int stepx) { + this.imgInfo = imgInfo; + this.singleCursor = singleCursor; + if (singleCursor) { + this.nlines = 1; // we store only one line, no matter how many will be read + offset = 0; + this.step = 1;// don't matter + } else { + this.nlines = nlinesx; // note that it can also be 1 + offset = noffsetx; + this.step = stepx;// don't matter + } + createImageLines(); + } + + private void createImageLines() { + if (singleCursor) { + imageLine = createImageLine(); + } else { + imageLines = new ArrayList(); + for (int i = 0; i < nlines; i++) { + imageLines.add(createImageLine()); + } + } + } + + protected abstract T createImageLine(); + + /** + * Retrieves the image line + *

      + * Warning: the argument is the row number in the original image + *

      + * If this is a cursor, no check is done, always the same row is returned + */ + public T getImageLine(int n) { + currentRow = n; + if (singleCursor) { + return imageLine; + } else { + int r = imageRowToMatrixRowStrict(n); + if (r < 0) { + throw new PngjException("Invalid row number"); + } + return imageLines.get(r); + } + } + + /** + * does not check for valid range + */ + public T getImageLineRawNum(int r) { + if (singleCursor) { + return imageLine; + } else { + return imageLines.get(r); + } + } + + /** + * True if the set contains this image line + *

      + * Warning: the argument is the row number in the original image + *

      + * If this works as cursor, this returns true only if that is the number of + * its "current" line + */ + public boolean hasImageLine(int n) { + return singleCursor ? currentRow == n : imageRowToMatrixRowStrict(n) >= 0; + } + + /** + * How many lines does this object contain? + */ + public int size() { + return nlines; + } + + /** + * Same as {@link #imageRowToMatrixRow(int)}, but returns negative if + * invalid + */ + public int imageRowToMatrixRowStrict(int imrow) { + imrow -= offset; + int mrow = imrow >= 0 && (step == 1 || imrow % step == 0) ? imrow / step : -1; + return mrow < nlines ? mrow : -1; + } + + /** + * Converts from matrix row number (0 : nRows-1) to image row number + * + * @param mrow Matrix row number + * @return Image row number. Returns trash if mrow is invalid + */ + public int matrixRowToImageRow(int mrow) { + return mrow * step + offset; + } + + /** + * Converts from real image row to this object row number. + *

      + * Warning: this always returns a valid matrix row (clamping on 0 : nrows-1, + * and rounding down) + *

      + * Eg: rowOffset=4,rowStep=2 imageRowToMatrixRow(17) returns 6 , + * imageRowToMatrixRow(1) returns 0 + */ + public int imageRowToMatrixRow(int imrow) { + int r = (imrow - offset) / step; + return r < 0 ? 0 : (r < nlines ? r : nlines - 1); + } + + /** + * utility function, given a factory for one line, returns a factory for a + * set + */ + public static IImageLineSetFactory createImageLineSetFactoryFromImageLineFactory( + final IImageLineFactory ifactory) { // ugly method must have ugly name. don't let this intimidate you + return new IImageLineSetFactory() { + public IImageLineSet create(final ImageInfo iminfo, boolean singleCursor, int nlines, int noffset, + int step) { + return new ImageLineSetDefault(iminfo, singleCursor, nlines, noffset, step) { + @Override + protected T createImageLine() { + return ifactory.createImageLine(iminfo); + } + }; + } + + }; + } + + /** + * utility function, returns default factory for {@link ImageLineInt} + */ + public static IImageLineSetFactory getFactoryInt() { + return createImageLineSetFactoryFromImageLineFactory(ImageLineInt.getFactory()); + } + + /** + * utility function, returns default factory for {@link ImageLineByte} + */ + public static IImageLineSetFactory getFactoryByte() { + return createImageLineSetFactoryFromImageLineFactory(ImageLineByte.getFactory()); + } +} diff --git a/png/src/main/java/org/xbib/graphics/imageio/plugins/png/pngj/PngHelperInternal.java b/png/src/main/java/org/xbib/graphics/imageio/plugins/png/pngj/PngHelperInternal.java new file mode 100644 index 0000000..939bd09 --- /dev/null +++ b/png/src/main/java/org/xbib/graphics/imageio/plugins/png/pngj/PngHelperInternal.java @@ -0,0 +1,248 @@ +package org.xbib.graphics.imageio.plugins.png.pngj; + +import java.io.File; +import java.io.FileInputStream; +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; +import java.nio.charset.Charset; +import java.nio.charset.StandardCharsets; + +/** + * Some utility static methods for internal use. + * Client code should not normally use this class + */ +public final class PngHelperInternal { + + /** + * Default charset, used internally by PNG for several things + */ + public static Charset charsetLatin1 = StandardCharsets.ISO_8859_1; + /** + * UTF-8 is only used for some chunks + */ + public static Charset charsetUTF8 = StandardCharsets.UTF_8; + + /** + * PNG magic bytes + */ + public static byte[] getPngIdSignature() { + return new byte[]{-119, 80, 78, 71, 13, 10, 26, 10}; + } + + public static int doubleToInt100000(double d) { + return (int) (d * 100000.0 + 0.5); + } + + public static double intToDouble100000(int i) { + return i / 100000.0; + } + + public static int readByte(InputStream is) { + try { + return is.read(); + } catch (IOException e) { + throw new PngjInputException("error reading byte", e); + } + } + + /** + * -1 if eof + * PNG uses "network byte order" + */ + public static int readInt2(InputStream is) { + try { + int b1 = is.read(); + int b2 = is.read(); + if (b1 == -1 || b2 == -1) { + return -1; + } + return (b1 << 8) | b2; + } catch (IOException e) { + throw new PngjInputException("error reading Int2", e); + } + } + + /** + * -1 if eof + * PNG uses "network byte order" + */ + public static int readInt4(InputStream is) { + try { + int b1 = is.read(); + int b2 = is.read(); + int b3 = is.read(); + int b4 = is.read(); + if (b1 == -1 || b2 == -1 || b3 == -1 || b4 == -1) { + return -1; + } + return (b1 << 24) | (b2 << 16) | (b3 << 8) + b4; + } catch (IOException e) { + throw new PngjInputException("error reading Int4", e); + } + } + + public static int readInt1fromByte(byte[] b, int offset) { + return (b[offset] & 0xff); + } + + public static int readInt2fromBytes(byte[] b, int offset) { + return ((b[offset] & 0xff) << 8) | ((b[offset + 1] & 0xff)); + } + + public static int readInt4fromBytes(byte[] b, int offset) { + return ((b[offset] & 0xff) << 24) | ((b[offset + 1] & 0xff) << 16) | ((b[offset + 2] & 0xff) << 8) + | (b[offset + 3] & 0xff); + } + + public static void writeByte(OutputStream os, byte b) { + try { + os.write(b); + } catch (IOException e) { + throw new PngjOutputException(e); + } + } + + public static void writeByte(OutputStream os, byte[] bs) { + try { + os.write(bs); + } catch (IOException e) { + throw new PngjOutputException(e); + } + } + + public static void writeInt2(OutputStream os, int n) { + byte[] temp = {(byte) ((n >> 8) & 0xff), (byte) (n & 0xff)}; + writeBytes(os, temp); + } + + public static void writeInt4(OutputStream os, int n) { + byte[] temp = new byte[4]; + writeInt4tobytes(n, temp, 0); + writeBytes(os, temp); + } + + public static void writeInt2tobytes(int n, byte[] b, int offset) { + b[offset] = (byte) ((n >> 8) & 0xff); + b[offset + 1] = (byte) (n & 0xff); + } + + public static void writeInt4tobytes(int n, byte[] b, int offset) { + b[offset] = (byte) ((n >> 24) & 0xff); + b[offset + 1] = (byte) ((n >> 16) & 0xff); + b[offset + 2] = (byte) ((n >> 8) & 0xff); + b[offset + 3] = (byte) (n & 0xff); + } + + /** + * guaranteed to read exactly len bytes. throws error if it can't + */ + public static void readBytes(InputStream is, byte[] b, int offset, int len) { + if (len == 0) { + return; + } + try { + int read = 0; + while (read < len) { + int n = is.read(b, offset + read, len - read); + if (n < 1) { + throw new PngjInputException("error reading bytes, " + n + " !=" + len); + } + read += n; + } + } catch (IOException e) { + throw new PngjInputException("error reading", e); + } + } + + public static void skipBytes(InputStream is, long len) { + try { + while (len > 0) { + long n1 = is.skip(len); + if (n1 > 0) { + len -= n1; + } else { // should we retry? lets read one byte + if (is.read() == -1) // EOF + { + break; + } else { + len--; + } + } + } + } catch (IOException e) { + throw new PngjInputException(e); + } + } + + public static void writeBytes(OutputStream os, byte[] b) { + try { + os.write(b); + } catch (IOException e) { + throw new PngjOutputException(e); + } + } + + public static void writeBytes(OutputStream os, byte[] b, int offset, int n) { + try { + os.write(b, offset, n); + } catch (IOException e) { + throw new PngjOutputException(e); + } + } + + public static int filterRowPaeth(int r, int left, int up, int upleft) { // a = left, b = above, c + // = upper left + return (r - filterPaethPredictor(left, up, upleft)) & 0xFF; + } + + static int filterPaethPredictor(final int a, final int b, final int c) { // a = left, b = + // above, c = upper + // left + // from + // http://www.libpng.org/pub/png/spec/1.2/PNG-Filters.html + + final int p = a + b - c;// ; initial estimate + final int pa = p >= a ? p - a : a - p; + final int pb = p >= b ? p - b : b - p; + final int pc = p >= c ? p - c : c - p; + // ; return nearest of a,b,c, + // ; breaking ties in order a,b,c. + if (pa <= pb && pa <= pc) { + return a; + } else if (pb <= pc) { + return b; + } else { + return c; + } + } + + public static InputStream istreamFromFile(File f) { + FileInputStream is; + try { + is = new FileInputStream(f); + } catch (Exception e) { + throw new PngjInputException("Could not open " + f, e); + } + return is; + } + + static OutputStream ostreamFromFile(File f, boolean allowoverwrite) { + // In old versions of GAE (Google App Engine) this could trigger + // issues because java.io.FileOutputStream was not whitelisted. + java.io.FileOutputStream os = null; + if (f.exists() && !allowoverwrite) { + throw new PngjOutputException("File already exists: " + f); + } + try { + os = new java.io.FileOutputStream(f); + } catch (Exception e) { + throw new PngjInputException("Could not open for write" + f, e); + } + return os; + } + + public static long getDigest(PngReader pngr) { + return pngr.getSimpleDigest(); + } +} diff --git a/png/src/main/java/org/xbib/graphics/imageio/plugins/png/pngj/PngHelperInternal2.java b/png/src/main/java/org/xbib/graphics/imageio/plugins/png/pngj/PngHelperInternal2.java new file mode 100644 index 0000000..fddb9e5 --- /dev/null +++ b/png/src/main/java/org/xbib/graphics/imageio/plugins/png/pngj/PngHelperInternal2.java @@ -0,0 +1,36 @@ +package org.xbib.graphics.imageio.plugins.png.pngj; + +import java.io.File; +import java.io.OutputStream; + +/** + * For organization purposes, this class is the onlt that uses classes not in + * GAE (Google App Engine) white list + *

      + * You should not use this class in GAE + */ +final class PngHelperInternal2 { + + /** + * WARNING: this uses FileOutputStream which is not allowed in + * GoogleAppEngine + *

      + * In GAE, dont use this + * + * @param f + * @param allowoverwrite + * @return + */ + static OutputStream ostreamFromFile(File f, boolean allowoverwrite) { + java.io.FileOutputStream os = null; // this will fail in GAE! + if (f.exists() && !allowoverwrite) { + throw new PngjOutputException("File already exists: " + f); + } + try { + os = new java.io.FileOutputStream(f); + } catch (Exception e) { + throw new PngjInputException("Could not open for write" + f, e); + } + return os; + } +} diff --git a/png/src/main/java/org/xbib/graphics/imageio/plugins/png/pngj/PngReader.java b/png/src/main/java/org/xbib/graphics/imageio/plugins/png/pngj/PngReader.java new file mode 100644 index 0000000..0944277 --- /dev/null +++ b/png/src/main/java/org/xbib/graphics/imageio/plugins/png/pngj/PngReader.java @@ -0,0 +1,674 @@ +package org.xbib.graphics.imageio.plugins.png.pngj; + +import java.io.Closeable; +import java.io.File; +import java.io.InputStream; +import java.util.logging.Logger; +import java.util.zip.Adler32; +import java.util.zip.CRC32; +import org.xbib.graphics.imageio.plugins.png.pngj.chunks.ChunkLoadBehaviour; +import org.xbib.graphics.imageio.plugins.png.pngj.chunks.ChunksList; +import org.xbib.graphics.imageio.plugins.png.pngj.chunks.PngChunkFCTL; +import org.xbib.graphics.imageio.plugins.png.pngj.chunks.PngChunkFDAT; +import org.xbib.graphics.imageio.plugins.png.pngj.chunks.PngChunkIDAT; +import org.xbib.graphics.imageio.plugins.png.pngj.chunks.PngMetadata; + +/** + * Reads a PNG image (pixels and/or metadata) from a file or stream. + *

      + * Each row is read as an {@link ImageLineInt} object (one int per sample), but + * this can be changed by setting a different ImageLineFactory + *

      + * Internally, this wraps a {@link ChunkSeqReaderPng} with a + * {@link BufferedStreamFeeder} + *

      + * The reading sequence is as follows:
      + * 1. At construction time, the header and IHDR chunk are read (basic image + * info)
      + * 2. Afterwards you can set some additional global options. Eg. + * {@link #setCrcCheckDisabled()}.
      + * 3. Optional: If you call getMetadata() or getChunksLisk() before start + * reading the rows, all the chunks before IDAT are then loaded and available + *
      + * 4a. The rows are read in order by calling {@link #readRow()}. You can also + * call {@link #readRow(int)} to skip rows -but you can't go backwards, at least + * not with this implementation. This method returns a {@link IImageLine} object + * which can be casted to the concrete class. This class returns by default a + * {@link ImageLineInt}, but this can be changed.
      + * 4b. Alternatively, you can read all rows, or a subset, in a single call: + * {@link #readRows()}, {@link #readRows(int, int, int)} ,etc. In general this + * consumes more memory, but for interlaced images this is equally efficient, + * and more so if reading a small subset of rows.
      + * 5. Reading of the last row automatically loads the trailing chunks, and ends + * the reader.
      + * 6. end() also loads the trailing chunks, if not done, and finishes cleanly + * the reading and closes the stream. + *

      + * See also {@link PngReaderInt} (esentially the same as this, and slightly + * preferred) and {@link PngReaderByte} (uses byte instead of int to store the + * samples). + */ +public class PngReader implements Closeable { + private static final Logger LOGGER = Logger.getLogger(PngReader.class.getName()); + + // some performance/defensive limits + /** + * Defensive limit: refuse to read more than 900MB, can be changed with + * {@link #setMaxTotalBytesRead(long)} + */ + public static final long MAX_TOTAL_BYTES_READ_DEFAULT = 901001001L; // ~ 900MB + + /** + * Defensive limit: refuse to load more than 5MB of ancillary metadata, see + * {@link #setMaxBytesMetadata(long)} and also + * {@link #addChunkToSkip(String)} + */ + public static final long MAX_BYTES_METADATA_DEFAULT = 5024024; // for ancillary chunks + + /** + * Skip ancillary chunks greater than 2MB, see + * {@link #setSkipChunkMaxSize(long)} + */ + public static final long MAX_CHUNK_SIZE_SKIP = 2024024; // chunks exceeding this size will be skipped (nor even CRC + // checked) + + /** + * Basic image info - final and inmutable. + */ + public final ImageInfo imgInfo; // People always told me: be careful what you do, and don't go around declaring + // public + // fields... + /** + * flag: image was in interlaced format + */ + public final boolean interlaced; + + /** + * This object has most of the intelligence to parse the chunks and + * decompress the IDAT stream + */ + protected final ChunkSeqReaderPng chunkseq; + + /** + * Takes bytes from the InputStream and passes it to the ChunkSeqReaderPng. + * Never null. + */ + protected final BufferedStreamFeeder streamFeeder; + + /** + * @see #getMetadata() + */ + protected final PngMetadata metadata; // this a wrapper over chunks + + /** + * Current row number (reading or read), numbered from 0 + */ + protected int rowNum = -1; + + /** + * Represents the set of lines (rows) being read. Normally this works as a + * cursor, storing only one (the current) row. This stores several (perhaps + * all) rows only if calling {@link #readRows()} or for interlaced images + * (this later is transparent to the user) + */ + protected IImageLineSet imlinesSet; + + /** + * This factory decides the concrete type of the ImageLine that will be + * used. See {@link ImageLineSetDefault} for examples + */ + private IImageLineSetFactory imageLineSetFactory; + + CRC32 idatCrca;// for internal testing + Adler32 idatCrcb;// for internal testing + + protected ErrorBehaviour errorBehaviour = ErrorBehaviour.STRICT; + + /** + * Constructs a PngReader object from a stream, with default options. This + * reads the signature and the first IHDR chunk only. + *

      + * Warning: In case of exception the stream is NOT closed. + *

      + * Warning: By default the stream will be closed when this object is + * {@link #close()}d. See {@link #PngReader(InputStream, boolean)} or + * {@link #setShouldCloseStream(boolean)} + *

      + * + * @param inputStream PNG stream + */ + public PngReader(InputStream inputStream) { + this(inputStream, true); + } + + /** + * Same as {@link #PngReader(InputStream)} but allows to specify early if + * the stream must be closed + * + * @param inputStream + * @param shouldCloseStream The stream will be closed in case of exception (constructor + * included) or normal termination. + */ + public PngReader(InputStream inputStream, boolean shouldCloseStream) { + streamFeeder = new BufferedStreamFeeder(inputStream); + streamFeeder.setCloseStream(shouldCloseStream); + chunkseq = createChunkSeqReader(); + try { + if (streamFeeder.feedFixed(chunkseq, 36) != 36) // 8+13+12=36 PNG signature+IHDR chunk + { + throw new PngjInputException("Could not read first 36 bytes (PNG signature+IHDR chunk)"); + } + imgInfo = chunkseq.getImageInfo(); + interlaced = chunkseq.getDeinterlacer() != null; + setMaxBytesMetadata(MAX_BYTES_METADATA_DEFAULT); + setMaxTotalBytesRead(MAX_TOTAL_BYTES_READ_DEFAULT); + setSkipChunkMaxSize(MAX_CHUNK_SIZE_SKIP); + chunkseq.addChunkToSkip(PngChunkFDAT.ID);// default: skip fdAT chunks! + chunkseq.addChunkToSkip(PngChunkFCTL.ID);// default: skip fctl chunks! + this.metadata = new PngMetadata(chunkseq.chunksList); + // sets a default factory (with ImageLineInt), + // this can be overwriten by a extended constructor, or by a setter + setLineSetFactory(ImageLineSetDefault.getFactoryInt()); + rowNum = -1; + } catch (RuntimeException e) { + streamFeeder.close(); + chunkseq.close(); + throw e; + } + } + + /** + * Constructs a PngReader opening a file. Sets + * shouldCloseStream=true, so that the stream will be closed with + * this object. + * + * @param file PNG image file + */ + public PngReader(File file) { + this(PngHelperInternal.istreamFromFile(file), true); + } + + /** + * Reads chunks before first IDAT. Normally this is called automatically + *

      + * Position before: after IDHR (crc included) Position after: just after the + * first IDAT chunk id + *

      + * This can be called several times (tentatively), it does nothing if + * already run + *

      + * (Note: when should this be called? in the constructor? hardly, because we + * loose the opportunity to call setChunkLoadBehaviour() and perhaps other + * settings before reading the first row? but sometimes we want to access + * some metadata (plte, phys) before. Because of this, this method can be + * called explicitly but is also called implicititly in some methods + * (getMetatada(), getChunksList()) + */ + protected void readFirstChunks() { + while (chunkseq.currentChunkGroup < ChunksList.CHUNK_GROUP_4_IDAT) { + if (streamFeeder.feed(chunkseq) <= 0) { + throw new PngjInputException("Premature ending reading first chunks"); + } + } + } + + /** + * Determines which ancillary chunks (metadata) are to be loaded and which + * skipped. + *

      + * Additional restrictions may apply. See also + * {@link #setChunksToSkip(String...)}, {@link #addChunkToSkip(String)}, + * {@link #setMaxBytesMetadata(long)}, {@link #setSkipChunkMaxSize(long)} + * + * @param chunkLoadBehaviour {@link ChunkLoadBehaviour} + */ + public void setChunkLoadBehaviour(ChunkLoadBehaviour chunkLoadBehaviour) { + this.chunkseq.setChunkLoadBehaviour(chunkLoadBehaviour); + } + + /** + * All loaded chunks (metada). If we have not yet end reading the image, + * this will include only the chunks before the pixels data (IDAT) + *

      + * Critical chunks are included, except that all IDAT chunks appearance are + * replaced by a single dummy-marker IDAT chunk. These might be copied to + * the PngWriter + *

      + * + * @see #getMetadata() + */ + public ChunksList getChunksList() { + return getChunksList(true); + } + + /** + * @param forceLoadingOfFirstChunks If true, chunks before IDAT will be load if needed + * @return list of currently read chunks + */ + public ChunksList getChunksList(boolean forceLoadingOfFirstChunks) { + if (forceLoadingOfFirstChunks && chunkseq.firstChunksNotYetRead()) { + readFirstChunks(); + } + return chunkseq.chunksList; + } + + /** + * From 0 to 6, see ChunksList CHUNK_GROUP_* + */ + int getCurrentChunkGroup() { + return chunkseq.currentChunkGroup; + } + + /** + * High level wrapper over chunksList + * + * @see #getChunksList() + */ + public PngMetadata getMetadata() { + if (chunkseq.firstChunksNotYetRead()) { + readFirstChunks(); + } + return metadata; + } + + /** + * Reads next row. + *

      + * The caller must know that there are more rows to read. + * + * @return Never null. Throws PngInputException if no more + */ + public IImageLine readRow() { + return readRow(rowNum + 1); + } + + /** + * True if last row has not yet been read + */ + public boolean hasMoreRows() { + return rowNum < getCurImgInfo().rows - 1; + } + + /** + * The row number is mostly meant as a check, the rows must be called in + * ascending order (not necessarily consecutive) + */ + public IImageLine readRow(int nrow) { + if (chunkseq.firstChunksNotYetRead()) { + readFirstChunks(); + } + if (!interlaced) { + if (imlinesSet == null) { + imlinesSet = createLineSet(true, -1, 0, 1); + } + IImageLine line = imlinesSet.getImageLine(nrow); + if (nrow == rowNum) { + return line; // already read?? + } else if (nrow < rowNum) { + throw new PngjInputException("rows must be read in increasing order: " + nrow); + } + while (rowNum < nrow) { + while (!chunkseq.getIdatSet().isRowReady()) { + if (streamFeeder.feed(chunkseq) < 1) { + throw new PngjInputException("premature ending"); + } + } + rowNum++; + chunkseq.getIdatSet().updateCrcs(idatCrca, idatCrcb); + if (rowNum == nrow) { + line.readFromPngRaw(chunkseq.getIdatSet().getUnfilteredRow(), getCurImgInfo().bytesPerRow + 1, 0, + 1); + line.endReadFromPngRaw(); + } + chunkseq.getIdatSet().advanceToNextRow(); + } + return line; + } else { // and now, for something completely different (interlaced!) + if (imlinesSet == null) { + imlinesSet = createLineSet(false, getCurImgInfo().rows, 0, 1); + loadAllInterlaced(getCurImgInfo().rows, 0, 1); + } + rowNum = nrow; + return imlinesSet.getImageLine(nrow); + } + + } + + /** + * Reads all rows in a ImageLineSet This is handy, but less memory-efficient + * (except for interlaced) + */ + public IImageLineSet readRows() { + return readRows(getCurImgInfo().rows, 0, 1); + } + + /** + * Reads a subset of rows. + *

      + * This method should called once, and not be mixed with {@link #readRow()} + * + * @param nRows how many rows to read (default: imageInfo.rows; negative: + * autocompute) + * @param rowOffset rows to skip (default:0) + * @param rowStep step between rows to load( default:1) + */ + public IImageLineSet readRows(int nRows, int rowOffset, int rowStep) { + if (chunkseq.firstChunksNotYetRead()) { + readFirstChunks(); + } + if (nRows < 0) { + nRows = (getCurImgInfo().rows - rowOffset) / rowStep; + } + if (rowStep < 1 || rowOffset < 0 || nRows == 0 || nRows * rowStep + rowOffset > getCurImgInfo().rows) { + throw new PngjInputException("bad args"); + } + if (rowNum >= rowOffset) { + throw new PngjInputException("readRows cannot be mixed with readRow"); + } + imlinesSet = createLineSet(false, nRows, rowOffset, rowStep); + if (!interlaced) { + int m = -1; // last row already read in + while (m < nRows - 1) { + while (!chunkseq.getIdatSet().isRowReady()) { + if (streamFeeder.feed(chunkseq) < 1) { + throw new PngjInputException("Premature ending"); + } + } + rowNum++; + chunkseq.getIdatSet().updateCrcs(idatCrca, idatCrcb); + m = (rowNum - rowOffset) / rowStep; + if (rowNum >= rowOffset && rowStep * m + rowOffset == rowNum) { + IImageLine line = imlinesSet.getImageLine(rowNum); + line.readFromPngRaw(chunkseq.getIdatSet().getUnfilteredRow(), getCurImgInfo().bytesPerRow + 1, 0, + 1); + line.endReadFromPngRaw(); + } + chunkseq.getIdatSet().advanceToNextRow(); + } + } else { // and now, for something completely different (interlaced) + loadAllInterlaced(nRows, rowOffset, rowStep); + } + chunkseq.getIdatSet().markAsDone(); + return imlinesSet; + } + + /** + * Sets the factory that creates the ImageLine. By default, this + * implementation uses ImageLineInt but this can be changed (at construction + * time or later) by calling this method. + *

      + * See also {@link #createLineSet(boolean, int, int, int)} + * + * @param factory + */ + public void setLineSetFactory(IImageLineSetFactory factory) { + imageLineSetFactory = factory; + } + + /** + * By default this uses the factory (which, by default creates + * ImageLineInt). You should rarely override this. + *

      + * See doc in + * {@link IImageLineSetFactory#create(ImageInfo, boolean, int, int, int)} + */ + protected IImageLineSet createLineSet(boolean singleCursor, int nlines, int noffset, + int step) { + return imageLineSetFactory.create(getCurImgInfo(), singleCursor, nlines, noffset, step); + } + + protected void loadAllInterlaced(int nRows, int rowOffset, int rowStep) { + IdatSet idat = chunkseq.getIdatSet(); + int nread = 0; + do { + while (!chunkseq.getIdatSet().isRowReady()) { + if (streamFeeder.feed(chunkseq) <= 0) { + break; + } + } + if (!chunkseq.getIdatSet().isRowReady()) { + throw new PngjInputException("Premature ending?"); + } + chunkseq.getIdatSet().updateCrcs(idatCrca, idatCrcb); + int rowNumreal = idat.rowinfo.rowNreal; + boolean inset = imlinesSet.hasImageLine(rowNumreal); + if (inset) { + imlinesSet.getImageLine(rowNumreal).readFromPngRaw(idat.getUnfilteredRow(), idat.rowinfo.buflen, + idat.rowinfo.oX, idat.rowinfo.dX); + nread++; + } + idat.advanceToNextRow(); + } while (nread < nRows || !idat.isDone()); + idat.markAsDone(); + for (int i = 0, j = rowOffset; i < nRows; i++, j += rowStep) { + imlinesSet.getImageLine(j).endReadFromPngRaw(); + } + } + + /** + * Reads all the (remaining) file, skipping the pixels data. This is much + * more efficient that calling {@link #readRow()}, specially for big files + * (about 10 times faster!), because it doesn't even decompress the IDAT + * stream and disables CRC check Use this if you are not interested in + * reading pixels,only metadata. + */ + public void readSkippingAllRows() { + setCrcCheckDisabled(); + chunkseq.addChunkToSkip(PngChunkIDAT.ID); + chunkseq.addChunkToSkip(PngChunkFDAT.ID); + if (chunkseq.firstChunksNotYetRead()) { + readFirstChunks(); + } + end(); + } + + /** + * Set total maximum bytes to read (0: unlimited; default: 200MB).
      + * These are the bytes read (not loaded) in the input stream. If exceeded, + * an exception will be thrown. + */ + public void setMaxTotalBytesRead(long maxTotalBytesToRead) { + chunkseq.setMaxTotalBytesRead(maxTotalBytesToRead); + } + + /** + * Set total maximum bytes to load from ancillary chunks (0: unlimited; + * default: 5Mb).
      + * If exceeded, some chunks will be skipped + */ + public void setMaxBytesMetadata(long maxBytesMetadata) { + chunkseq.setMaxBytesMetadata(maxBytesMetadata); + } + + /** + * Set maximum size in bytes for individual ancillary chunks (0: unlimited; + * default: 2MB).
      + * Chunks exceeding this length will be skipped (the CRC will not be + * checked) and the chunk will be saved as a PngChunkSkipped object. See + * also setSkipChunkIds + */ + public void setSkipChunkMaxSize(long skipChunkMaxSize) { + chunkseq.setSkipChunkMaxSize(skipChunkMaxSize); + } + + /** + * Chunks ids to be skipped.
      + * These chunks will be skipped (the CRC will not be checked) and the chunk + * will be saved as a PngChunkSkipped object. See also setSkipChunkMaxSize + */ + public void setChunksToSkip(String... chunksToSkip) { + chunkseq.setChunksToSkip(chunksToSkip); + } + + public void addChunkToSkip(String chunkToSkip) { + chunkseq.addChunkToSkip(chunkToSkip); + } + + public void dontSkipChunk(String chunkToSkip) { + chunkseq.dontSkipChunk(chunkToSkip); + } + + /** + * if true, input stream will be closed after ending read + *

      + * default=true + */ + public void setShouldCloseStream(boolean shouldCloseStream) { + streamFeeder.setCloseStream(shouldCloseStream); + } + + /** + * Reads till end of PNG stream and calls close() + *

      + * This should normally be called after reading the pixel data, to read the + * trailing chunks and close the stream. But it can be called at anytime. + * This will also read the first chunks if not still read, and skip pixels + * (IDAT) if still pending. + *

      + * If you want to read all metadata skipping pixels, + * readSkippingAllRows() is a little more efficient. + *

      + * If you want to abort immediately, call instead close() + */ + public void end() { + try { + if (chunkseq.firstChunksNotYetRead()) { + readFirstChunks(); + } + if (chunkseq.getIdatSet() != null && !chunkseq.getIdatSet().isDone()) { + chunkseq.getIdatSet().markAsDone(); // it will ignore data + } + while (!chunkseq.isDone()) { + if (streamFeeder.feed(chunkseq) <= 0) { + break; + } + } + } finally { + close(); + } + } + + /** + * Releases resources, and closes stream if corresponds. Idempotent, secure, + * no exceptions. + *

      + * This can be also called for abort. It is recommended to call this in case + * of exceptions + */ + public void close() { + try { + if (chunkseq != null) { + chunkseq.close(); + } + } catch (Exception e) { + LOGGER.warning("error closing chunk sequence:" + e.getMessage()); + } + if (streamFeeder != null) { + streamFeeder.close(); + } + } + + /** + * Interlaced PNG is accepted -though not welcomed- now... + */ + public boolean isInterlaced() { + return interlaced; + } + + /** + * Disables the CRC integrity check in IDAT chunks and ancillary chunks, + * this gives a slight increase in reading speed for big files + */ + public void setCrcCheckDisabled() { + chunkseq.setCheckCrc(false); + } + + /** + * Gets wrapped {@link ChunkSeqReaderPng} object + */ + public ChunkSeqReaderPng getChunkseq() { + return chunkseq; + } + + /** + * called on construction time. Override if you want an alternative class + */ + protected ChunkSeqReaderPng createChunkSeqReader() { + return new ChunkSeqReaderPng(false); + } + + /** + * Enables and prepare the simple digest computation. Must be called before + * reading the pixels. See {@link #getSimpleDigestHex()} + */ + public void prepareSimpleDigestComputation() { + if (idatCrca == null) { + idatCrca = new CRC32(); + } else { + idatCrca.reset(); + } + if (idatCrcb == null) { + idatCrcb = new Adler32(); + } else { + idatCrcb.reset(); + } + imgInfo.updateCrc(idatCrca); + idatCrcb.update((byte) imgInfo.rows); // not important + } + + long getSimpleDigest() { + if (idatCrca == null) { + return 0; + } else { + return (idatCrca.getValue() ^ (idatCrcb.getValue() << 31)); + } + } + + /** + * Pseudo 64-bits digest computed over the basic image properties and the + * raw pixels data: it should coincide for equivalent images encoded with + * different filters and compressors; but will not coincide for + * interlaced/non-interlaced; also, this does not take into account the + * palette info. This will be valid only if + * {@link #prepareSimpleDigestComputation()} has been called, and all rows + * have been read. Not fool-proof, not cryptografically secure, only for + * informal testing and duplicates detection. + * + * @return A 64-digest in hexadecimal + */ + public String getSimpleDigestHex() { + return String.format("%016X", getSimpleDigest()); + } + + /** + * Basic info, for debugging. + */ + public String toString() { // basic info + return imgInfo.toString() + " interlaced=" + interlaced; + } + + /** + * Basic info, in a compact format, apt for scripting + * COLSxROWS[dBITDEPTH][a][p][g][i] ( the default dBITDEPTH='d8' is ommited) + */ + public String toStringCompact() { + return imgInfo.toStringBrief() + (interlaced ? "i" : ""); + } + + public ImageInfo getImgInfo() { + return imgInfo; + } + + public ImageInfo getCurImgInfo() { + return chunkseq.getCurImgInfo(); + } + + public void setErrorBehaviour(ErrorBehaviour er) { + this.errorBehaviour = er; + chunkseq.setErrorBehaviour(er); + } + + public boolean isDone() { + return chunkseq.isDone(); + } +} diff --git a/png/src/main/java/org/xbib/graphics/imageio/plugins/png/pngj/PngReaderApng.java b/png/src/main/java/org/xbib/graphics/imageio/plugins/png/pngj/PngReaderApng.java new file mode 100644 index 0000000..46d536e --- /dev/null +++ b/png/src/main/java/org/xbib/graphics/imageio/plugins/png/pngj/PngReaderApng.java @@ -0,0 +1,219 @@ +package org.xbib.graphics.imageio.plugins.png.pngj; + +import java.io.File; +import java.io.InputStream; +import java.util.List; +import org.xbib.graphics.imageio.plugins.png.pngj.chunks.PngChunk; +import org.xbib.graphics.imageio.plugins.png.pngj.chunks.PngChunkACTL; +import org.xbib.graphics.imageio.plugins.png.pngj.chunks.PngChunkFCTL; +import org.xbib.graphics.imageio.plugins.png.pngj.chunks.PngChunkFDAT; +import org.xbib.graphics.imageio.plugins.png.pngj.chunks.PngChunkIDAT; + +/** + * + */ +public class PngReaderApng extends PngReaderByte { + + public PngReaderApng(File file) { + super(file); + dontSkipChunk(PngChunkFCTL.ID); + } + + public PngReaderApng(InputStream inputStream) { + super(inputStream); + dontSkipChunk(PngChunkFCTL.ID); + } + + private Boolean apngKind = null; + private boolean firsIdatApngFrame = false; + protected PngChunkACTL actlChunk; // null if not APNG + private PngChunkFCTL fctlChunk; // current (null for the pseudo still frame) + + /** + * Current frame number (reading or read). First animated frame is 0. Frame + * -1 represents the IDAT (default image) when it's not part of the + * animation + */ + protected int frameNum = -1; // incremented after each fctl finding + + public boolean isApng() { + if (apngKind == null) { + // this triggers the loading of first chunks; + actlChunk = (PngChunkACTL) getChunksList().getById1(PngChunkACTL.ID); // null if not apng + apngKind = actlChunk != null; + firsIdatApngFrame = fctlChunk != null; + + } + return apngKind.booleanValue(); + } + + public void advanceToFrame(int frame) { + if (frame < frameNum) { + throw new PngjInputException("Cannot go backwards"); + } + if (frame >= getApngNumFrames()) { + throw new PngjInputException("Frame out of range " + frame); + } + if (frame > frameNum) { + addChunkToSkip(PngChunkIDAT.ID); + addChunkToSkip(PngChunkFDAT.ID); + if (chunkseq.getIdatSet() != null && !chunkseq.getIdatSet().isDone()) { + chunkseq.getIdatSet().markAsDone(); // seems to be necessary sometimes (we should check this) + } + while (frameNum < frame & !chunkseq.isDone()) { + if (streamFeeder.feed(chunkseq) <= 0) { + break; + } + } + } + if (frame == frameNum) { // prepare to read rows. at this point we have a new + dontSkipChunk(PngChunkIDAT.ID); + dontSkipChunk(PngChunkFDAT.ID); + rowNum = -1; + imlinesSet = null;// force recreation (this is slightly dirty) + // seek the next IDAT/fDAT - TODO: set the expected sequence number + while (!chunkseq.isDone() && !chunkseq.getCurChunkReader().isFromDeflatedSet()) { + if (streamFeeder.feed(chunkseq) <= 0) { + break; + } + } + } else { + throw new PngjInputException("unexpected error seeking from frame " + frame); + } + } + + /** + * True if it has a default image (IDAT) that is not part of the animation. + * In that case, we consider it as a pseudo-frame (number -1) + */ + public boolean hasExtraStillImage() { + return isApng() && !firsIdatApngFrame; + } + + /** + * Only counts true animation frames. + */ + public int getApngNumFrames() { + if (isApng()) { + return actlChunk.getNumFrames(); + } else { + return 0; + } + } + + /** + * 0 if it's to been played infinitely. -1 if not APNG + */ + public int getApngNumPlays() { + if (isApng()) { + return actlChunk.getNumPlays(); + } else { + return -1; + } + } + + @Override + public IImageLine readRow() { + // TODO Auto-generated method stub + return super.readRow(); + } + + @Override + public boolean hasMoreRows() { + // TODO Auto-generated method stub + return super.hasMoreRows(); + } + + @Override + public IImageLine readRow(int nrow) { + // TODO Auto-generated method stub + return super.readRow(nrow); + } + + @Override + public IImageLineSet readRows() { + // TODO Auto-generated method stub + return super.readRows(); + } + + @Override + public IImageLineSet readRows(int nRows, int rowOffset, int rowStep) { + // TODO Auto-generated method stub + return super.readRows(nRows, rowOffset, rowStep); + } + + @Override + public void readSkippingAllRows() { + // TODO Auto-generated method stub + super.readSkippingAllRows(); + } + + @Override + protected ChunkSeqReaderPng createChunkSeqReader() { + ChunkSeqReaderPng cr = new ChunkSeqReaderPng(false) { + + @Override + public boolean shouldSkipContent(int len, String id) { + return super.shouldSkipContent(len, id); + } + + @Override + protected boolean isIdatKind(String id) { + return id.equals(PngChunkIDAT.ID) || id.equals(PngChunkFDAT.ID); + } + + @Override + protected DeflatedChunksSet createIdatSet(String id) { + IdatSet ids = new IdatSet(id, callbackMode, getCurImgInfo(), deinterlacer); + return ids; + } + + @Override + protected void startNewChunk(int len, String id, long offset) { + super.startNewChunk(len, id, offset); + } + + @Override + protected void postProcessChunk(ChunkReader chunkR) { + super.postProcessChunk(chunkR); + if (chunkR.getChunkRaw().id.equals(PngChunkFCTL.ID)) { + frameNum++; + List chunkslist = chunkseq.getChunks(); + fctlChunk = (PngChunkFCTL) chunkslist.get(chunkslist.size() - 1); + // as this is slightly dirty, we check + if (chunkR.getChunkRaw().getOffset() != fctlChunk.getRaw().getOffset()) { + throw new PngjInputException("something went wrong"); + } + ImageInfo frameInfo = fctlChunk.getEquivImageInfo(); + getChunkseq().updateCurImgInfo(frameInfo); + } + } + + @Override + protected boolean countChunkTypeAsAncillary(String id) { + // we don't count fdat as ancillary data + return super.countChunkTypeAsAncillary(id) && !id.equals(PngChunkFDAT.ID); + } + + }; + return cr; + } + + /** + * @see #frameNum + */ + public int getFrameNum() { + return frameNum; + } + + @Override + public void end() { + // TODO Auto-generated method stub + super.end(); + } + + public PngChunkFCTL getFctl() { + return fctlChunk; + } + +} diff --git a/png/src/main/java/org/xbib/graphics/imageio/plugins/png/pngj/PngReaderByte.java b/png/src/main/java/org/xbib/graphics/imageio/plugins/png/pngj/PngReaderByte.java new file mode 100644 index 0000000..f6614a3 --- /dev/null +++ b/png/src/main/java/org/xbib/graphics/imageio/plugins/png/pngj/PngReaderByte.java @@ -0,0 +1,32 @@ +package org.xbib.graphics.imageio.plugins.png.pngj; + +import java.io.File; +import java.io.InputStream; + +/** + * Trivial extension of {@link PngReader} that uses {@link ImageLineByte} + *

      + * The factory is set at construction time. Remember that this could still be + * changed at runtime. + */ +public class PngReaderByte extends PngReader { + + public PngReaderByte(File file) { + super(file); + setLineSetFactory(ImageLineSetDefault.getFactoryByte()); + } + + public PngReaderByte(InputStream inputStream) { + super(inputStream); + setLineSetFactory(ImageLineSetDefault.getFactoryByte()); + } + + /** + * Utility method that casts {@link #readRow()} return to + * {@link ImageLineByte}. + */ + public ImageLineByte readRowByte() { + return (ImageLineByte) readRow(); + } + +} diff --git a/png/src/main/java/org/xbib/graphics/imageio/plugins/png/pngj/PngReaderFilter.java b/png/src/main/java/org/xbib/graphics/imageio/plugins/png/pngj/PngReaderFilter.java new file mode 100644 index 0000000..c824841 --- /dev/null +++ b/png/src/main/java/org/xbib/graphics/imageio/plugins/png/pngj/PngReaderFilter.java @@ -0,0 +1,104 @@ +package org.xbib.graphics.imageio.plugins.png.pngj; + +import java.io.FilterInputStream; +import java.io.IOException; +import java.io.InputStream; +import java.util.List; +import org.xbib.graphics.imageio.plugins.png.pngj.chunks.PngChunk; + +/** + * This class allows to use a simple PNG reader as an input filter, wrapping a + * ChunkSeqReaderPng in callback mode. + *

      + * In this sample implementation, all IDAT chunks are skipped and the rest are + * stored. An example of use, that lets us grab the Metadata and let the pixels + * go towards a BufferedImage: + * + * + *

      + * PngReaderFilter reader = new PngReaderFilter(new FileInputStream("image.png"));
      + * BufferedImage image1 = ImageIO.read(reader);
      + * reader.readUntilEndAndClose(); // in case ImageIO.read() does not read the traling chunks (it happens)
      + * 
      + */ +public class PngReaderFilter extends FilterInputStream { + + private final ChunkSeqReaderPng chunkseq; + + public PngReaderFilter(InputStream arg0) { + super(arg0); + chunkseq = createChunkSequenceReader(); + } + + protected ChunkSeqReaderPng createChunkSequenceReader() { + return new ChunkSeqReaderPng(true) { + @Override + public boolean shouldSkipContent(int len, String id) { + return super.shouldSkipContent(len, id) || id.equals("IDAT"); + } + + @Override + protected boolean shouldCheckCrc(int len, String id) { + return false; + } + + @Override + protected void postProcessChunk(ChunkReader chunkR) { + super.postProcessChunk(chunkR); + } + }; + } + + @Override + public void close() throws IOException { + super.close(); + chunkseq.close(); + } + + @Override + public int read() throws IOException { + int r = super.read(); + if (r > 0) { + chunkseq.feedAll(new byte[]{(byte) r}, 0, 1); + } + return r; + } + + @Override + public int read(byte[] b, int off, int len) throws IOException { + int res = super.read(b, off, len); + if (res > 0) { + chunkseq.feedAll(b, off, res); + } + return res; + } + + @Override + public int read(byte[] b) throws IOException { + int res = super.read(b); + if (res > 0) { + chunkseq.feedAll(b, 0, res); + } + return res; + } + + public void readUntilEndAndClose() throws IOException { + BufferedStreamFeeder br = new BufferedStreamFeeder(this.in); + while ((!chunkseq.isDone()) && br.hasPendingBytes()) { + if (br.feed(chunkseq) < 1) { + break; + } + } + br.close(); + close(); + } + + public List getChunksList() { + return chunkseq.getChunks(); + } + + public ChunkSeqReaderPng getChunkseq() { + return chunkseq; + } + +} diff --git a/png/src/main/java/org/xbib/graphics/imageio/plugins/png/pngj/PngReaderInt.java b/png/src/main/java/org/xbib/graphics/imageio/plugins/png/pngj/PngReaderInt.java new file mode 100644 index 0000000..5293427 --- /dev/null +++ b/png/src/main/java/org/xbib/graphics/imageio/plugins/png/pngj/PngReaderInt.java @@ -0,0 +1,38 @@ +package org.xbib.graphics.imageio.plugins.png.pngj; + +import java.io.File; +import java.io.InputStream; + +/** + * Trivial extension of {@link PngReader} that uses {@link ImageLineInt}. + *

      + * In the current implementation this is quite dummy/redundant, because (for + * backward compatibility) PngReader already uses a {@link ImageLineInt}. + *

      + * The factory is set at construction time. Remember that this could still be + * changed at runtime. + */ +public class PngReaderInt extends PngReader { + + public PngReaderInt(File file) { + super(file); // not necessary to set factory, PngReader already does that + } + + public PngReaderInt(InputStream inputStream) { + super(inputStream); + } + + /** + * Utility method that casts the IImageLine to a ImageLineInt + *

      + * This only make sense for this concrete class + */ + public ImageLineInt readRowInt() { + IImageLine line = readRow(); + if (line instanceof ImageLineInt) { + return (ImageLineInt) line; + } else { + throw new PngjException("This is not a ImageLineInt : " + line.getClass()); + } + } +} diff --git a/png/src/main/java/org/xbib/graphics/imageio/plugins/png/pngj/PngWriter.java b/png/src/main/java/org/xbib/graphics/imageio/plugins/png/pngj/PngWriter.java new file mode 100644 index 0000000..42ac918 --- /dev/null +++ b/png/src/main/java/org/xbib/graphics/imageio/plugins/png/pngj/PngWriter.java @@ -0,0 +1,468 @@ +package org.xbib.graphics.imageio.plugins.png.pngj; + +import java.io.Closeable; +import java.io.File; +import java.io.OutputStream; +import java.util.List; +import java.util.logging.Logger; +import org.xbib.graphics.imageio.plugins.png.pngj.chunks.ChunkCopyBehaviour; +import org.xbib.graphics.imageio.plugins.png.pngj.chunks.ChunkPredicate; +import org.xbib.graphics.imageio.plugins.png.pngj.chunks.ChunksList; +import org.xbib.graphics.imageio.plugins.png.pngj.chunks.ChunksListForWrite; +import org.xbib.graphics.imageio.plugins.png.pngj.chunks.PngChunk; +import org.xbib.graphics.imageio.plugins.png.pngj.chunks.PngChunkIEND; +import org.xbib.graphics.imageio.plugins.png.pngj.chunks.PngChunkIHDR; +import org.xbib.graphics.imageio.plugins.png.pngj.chunks.PngChunkPLTE; +import org.xbib.graphics.imageio.plugins.png.pngj.chunks.PngMetadata; +import org.xbib.graphics.imageio.plugins.png.pngj.pixels.PixelsWriter; +import org.xbib.graphics.imageio.plugins.png.pngj.pixels.PixelsWriterDefault; + +/** + * Writes a PNG image, line by line. + */ +public class PngWriter implements Closeable { + + public final ImageInfo imgInfo; + + /** + * last writen row number, starting from 0 + */ + protected int rowNum = -1; + + private final ChunksListForWrite chunksList; + + private final PngMetadata metadata; + + /** + * Current chunk grounp, (0-6) already written or currently writing (this is + * advanced when just starting to write the new group, not when finalizing + * the previous) + *

      + * see {@link ChunksList} + */ + protected int currentChunkGroup = -1; + + private final int passes = 1; // Some writes might require two passes (NOT USED STILL) + private int currentpass = 0; // numbered from 1 + + private boolean shouldCloseStream = true; + + private int idatMaxSize = 0; // 0=use default (PngIDatChunkOutputStream 64k) + // private PngIDatChunkOutputStream datStream; + + protected PixelsWriter pixelsWriter; + + private final OutputStream os; + + private ChunkPredicate copyFromPredicate = null; + private ChunksList copyFromList = null; + + protected StringBuilder debuginfo = new StringBuilder(); + + private static final Logger LOGGER = Logger.getLogger(PngWriter.class.getName()); + + /** + * Opens a file for writing. + *

      + * Sets shouldCloseStream=true. For more info see + * {@link #PngWriter(OutputStream, ImageInfo)} + * + * @param file + * @param imgInfo + * @param allowoverwrite If false and file exists, an {@link PngjOutputException} is + * thrown + */ + public PngWriter(File file, ImageInfo imgInfo, boolean allowoverwrite) { + this(PngHelperInternal.ostreamFromFile(file, allowoverwrite), imgInfo); + setShouldCloseStream(true); + } + + /** + * @see #PngWriter(File, ImageInfo, boolean) (overwrite=true) + */ + public PngWriter(File file, ImageInfo imgInfo) { + this(file, imgInfo, true); + } + + /** + * Constructs a new PngWriter from a output stream. After construction + * nothing is writen yet. You still can set some parameters (compression, + * filters) and queue chunks before start writing the pixels. + *

      + * + * @param outputStream Open stream for binary writing + * @param imgInfo Basic image parameters + */ + public PngWriter(OutputStream outputStream, ImageInfo imgInfo) { + this.os = outputStream; + this.imgInfo = imgInfo; + // prealloc + chunksList = new ChunksListForWrite(imgInfo); + metadata = new PngMetadata(chunksList); + pixelsWriter = createPixelsWriter(imgInfo); + setCompLevel(9); + } + + private void initIdat() { // this triggers the writing of first chunks + pixelsWriter.setOs(this.os); + pixelsWriter.setIdatMaxSize(idatMaxSize); + writeSignatureAndIHDR(); + writeFirstChunks(); + } + + private void writeEndChunk() { + currentChunkGroup = ChunksList.CHUNK_GROUP_6_END; + PngChunkIEND c = new PngChunkIEND(imgInfo); + c.createRawChunk().writeChunk(os); + chunksList.getChunks().add(c); + } + + private void writeFirstChunks() { + if (currentChunkGroup >= ChunksList.CHUNK_GROUP_4_IDAT) { + return; + } + int nw = 0; + currentChunkGroup = ChunksList.CHUNK_GROUP_1_AFTERIDHR; + queueChunksFromOther(); + nw = chunksList.writeChunks(os, currentChunkGroup); + currentChunkGroup = ChunksList.CHUNK_GROUP_2_PLTE; + nw = chunksList.writeChunks(os, currentChunkGroup); + if (nw > 0 && imgInfo.greyscale) { + throw new PngjOutputException("cannot write palette for this format"); + } + if (nw == 0 && imgInfo.indexed) { + throw new PngjOutputException("missing palette"); + } + currentChunkGroup = ChunksList.CHUNK_GROUP_3_AFTERPLTE; + nw = chunksList.writeChunks(os, currentChunkGroup); + } + + private void writeLastChunks() { // not including end + currentChunkGroup = ChunksList.CHUNK_GROUP_5_AFTERIDAT; + queueChunksFromOther(); + chunksList.writeChunks(os, currentChunkGroup); + // should not be unwriten chunks + List pending = chunksList.getQueuedChunks(); + if (!pending.isEmpty()) { + throw new PngjOutputException( + pending.size() + " chunks were not written! Eg: " + pending.get(0).toString()); + } + } + + /** + * Write id signature and also "IHDR" chunk + */ + private void writeSignatureAndIHDR() { + PngHelperInternal.writeBytes(os, PngHelperInternal.getPngIdSignature()); // signature + currentChunkGroup = ChunksList.CHUNK_GROUP_0_IDHR; + PngChunkIHDR ihdr = new PngChunkIHDR(imgInfo); + // http://www.libpng.org/pub/png/spec/1.2/PNG-Chunks.html + ihdr.createRawChunk().writeChunk(os); + chunksList.getChunks().add(ihdr); + } + + private void queueChunksFromOther() { + if (copyFromList == null || copyFromPredicate == null) { + return; + } + boolean idatDone = currentChunkGroup >= ChunksList.CHUNK_GROUP_4_IDAT; // we assume this method is not either + // before + // or after the IDAT writing, not in the + // middle! + for (PngChunk chunk : copyFromList.getChunks()) { + if (chunk.getRaw().data == null) { + continue; // we cannot copy skipped chunks? + } + int groupOri = chunk.getChunkGroup(); + if (groupOri <= ChunksList.CHUNK_GROUP_4_IDAT && idatDone) { + continue; + } + if (groupOri >= ChunksList.CHUNK_GROUP_4_IDAT && !idatDone) { + continue; + } + if (chunk.crit && !chunk.id.equals(PngChunkPLTE.ID)) { + continue; // critical chunks (except perhaps PLTE) are never + } + // copied + boolean copy = copyFromPredicate.match(chunk); + if (copy) { + // but if the chunk is already queued or writen, it's ommited! + if (chunksList.getEquivalent(chunk).isEmpty() && chunksList.getQueuedEquivalent(chunk).isEmpty()) { + chunksList.queue(chunk); + } + } + } + } + + /** + * Queues an ancillary chunk for writing. + *

      + * If a "equivalent" chunk is already queued (see + * {@link ChunkHelper#equivalent(PngChunk, PngChunk)), this overwrites it. + *

      The chunk will be written as late as possible, unless the priority is + * set. + * + * @param chunk + */ + public void queueChunk(PngChunk chunk) { + for (PngChunk other : chunksList.getQueuedEquivalent(chunk)) { + getChunksList().removeChunk(other); + } + chunksList.queue(chunk); + } + + /** + * Sets an origin (typically from a {@link PngReader}) of Chunks to be + * copied. This should be called only once, before starting writing the + * rows. It doesn't matter the current state of the PngReader reading, this + * is a live object and what matters is that when the writer writes the + * pixels (IDAT) the reader has already read them, and that when the writer + * ends, the reader is already ended (all this is very natural). + *

      + * Apart from the copyMask, there is some addional heuristics: + *

      + * - The chunks will be queued, but will be written as late as possible + * (unless you explicitly set priority=true) + *

      + * - The chunk will not be queued if an "equivalent" chunk was already + * queued explicitly. And it will be overwriten another is queued + * explicitly. + * + * @param chunks + * @param copyMask Some bitmask from {@link ChunkCopyBehaviour} + * @see #copyChunksFrom(ChunksList, ChunkPredicate) + */ + public void copyChunksFrom(ChunksList chunks, int copyMask) { + copyChunksFrom(chunks, ChunkCopyBehaviour.createPredicate(copyMask, imgInfo)); + } + + /** + * Copy all chunks from origin. See {@link #copyChunksFrom(ChunksList, int)} + * for more info + */ + public void copyChunksFrom(ChunksList chunks) { + copyChunksFrom(chunks, ChunkCopyBehaviour.COPY_ALL); + } + + /** + * Copy chunks from origin depending on some {@link ChunkPredicate} + * + * @param chunks + * @param predicate The chunks (ancillary or PLTE) will be copied if and only if + * predicate matches + * @see #copyChunksFrom(ChunksList, int) for more info + */ + public void copyChunksFrom(ChunksList chunks, ChunkPredicate predicate) { + if (copyFromList != null && chunks != null) { + LOGGER.warning("copyChunksFrom should only be called once"); + } + if (predicate == null) { + throw new PngjOutputException("copyChunksFrom requires a predicate"); + } + this.copyFromList = chunks; + this.copyFromPredicate = predicate; + } + + /** + * Computes compressed size/raw size, approximate. + *

      + * Actually: compressed size = total size of IDAT data , raw size = + * uncompressed pixel bytes = rows * (bytesPerRow + 1). + *

      + * This must be called after pngw.end() + */ + public double computeCompressionRatio() { + if (currentChunkGroup < ChunksList.CHUNK_GROUP_5_AFTERIDAT) { + throw new PngjOutputException("must be called after end()"); + } + return pixelsWriter.getCompression(); + } + + /** + * Finalizes all the steps and closes the stream. This must be called after + * writing the lines. Idempotent + */ + public void end() { + if (rowNum != imgInfo.rows - 1 || !pixelsWriter.isDone()) { + throw new PngjOutputException("all rows have not been written"); + } + try { + if (pixelsWriter != null) { + pixelsWriter.close(); + } + if (currentChunkGroup < ChunksList.CHUNK_GROUP_5_AFTERIDAT) { + writeLastChunks(); + } + if (currentChunkGroup < ChunksList.CHUNK_GROUP_6_END) { + writeEndChunk(); + } + } finally { + close(); + } + } + + /** + * Closes and releases resources + *

      + * This is normally called internally from {@link #end()}, you should only + * call this for aborting the writing and release resources (close the + * stream). + *

      + * Idempotent and secure - never throws exceptions + */ + public void close() { + if (pixelsWriter != null) { + pixelsWriter.close(); + } + if (shouldCloseStream && os != null) { + try { + os.close(); + } catch (Exception e) { + LOGGER.warning("Error closing writer " + e.toString()); + } + } + } + + /** + * returns the chunks list (queued and writen chunks) + */ + public ChunksListForWrite getChunksList() { + return chunksList; + } + + /** + * Retruns a high level wrapper over for metadata handling + */ + public PngMetadata getMetadata() { + return metadata; + } + + /** + * Sets internal prediction filter type, or strategy to choose it. + *

      + * This must be called just after constructor, before starting writing. + *

      + */ + public void setFilterType(FilterType filterType) { + pixelsWriter.setFilterType(filterType); + } + + /** + * This is kept for backwards compatibility, now the PixelsWriter object + * should be used for setting compression/filtering options + * + * @param compLevel between 0 (no compression, max speed) and 9 (max compression) + * @see PixelsWriter#setCompressionFactor(double) + */ + public void setCompLevel(int complevel) { + pixelsWriter.setDeflaterCompLevel(complevel); + } + + /** + * + */ + public void setFilterPreserve(boolean filterPreserve) { + if (filterPreserve) { + pixelsWriter.setFilterType(FilterType.FILTER_PRESERVE); + } else if (pixelsWriter.getFilterType() == null) { + pixelsWriter.setFilterType(FilterType.FILTER_DEFAULT); + } + } + + /** + * Sets maximum size of IDAT fragments. Incrementing this from the default + * has very little effect on compression and increments memory usage. You + * should rarely change this. + *

      + * + * @param idatMaxSize default=0 : use defaultSize (32K) + */ + public void setIdatMaxSize(int idatMaxSize) { + this.idatMaxSize = idatMaxSize; + } + + /** + * If true, output stream will be closed after ending write + *

      + * default=true + */ + public void setShouldCloseStream(boolean shouldCloseStream) { + this.shouldCloseStream = shouldCloseStream; + } + + /** + * Writes next row, does not check row number. + * + * @param imgline + */ + public void writeRow(IImageLine imgline) { + writeRow(imgline, rowNum + 1); + } + + /** + * Writes the full set of row. The ImageLineSet should contain (allow to + * acces) imgInfo.rows + */ + public void writeRows(IImageLineSet imglines) { + for (int i = 0; i < imgInfo.rows; i++) { + writeRow(imglines.getImageLineRawNum(i)); + } + } + + public void writeRow(IImageLine imgline, int rownumber) { + rowNum++; + if (rowNum == imgInfo.rows) { + rowNum = 0; + } + if (rownumber == imgInfo.rows) { + rownumber = 0; + } + if (rownumber >= 0 && rowNum != rownumber) { + throw new PngjOutputException("rows must be written in order: expected:" + rowNum + " passed:" + rownumber); + } + if (rowNum == 0) { + currentpass++; + } + if (rownumber == 0 && currentpass == passes) { + initIdat(); + currentChunkGroup = ChunksList.CHUNK_GROUP_4_IDAT; // we just begin writing IDAT + } + byte[] rowb = pixelsWriter.getRowb(); + imgline.writeToPngRaw(rowb); + pixelsWriter.processRow(rowb); + + } + + /** + * Utility method, uses internaly a ImageLineInt + */ + public void writeRowInt(int[] buf) { + writeRow(new ImageLineInt(imgInfo, buf)); + } + + /** + * Factory method for pixels writer. This will be called once at the moment + * at start writing a set of IDAT chunks (typically once in a normal PNG) + *

      + * This should be overriden if custom filtering strategies are desired. + * Remember to release this with close() + * + * @param imginfo Might be different than that of this object (eg: APNG with + * subimages) + * @param os Output stream + * @return new PixelsWriter. Don't forget to call close() when discarding it + */ + protected PixelsWriter createPixelsWriter(ImageInfo imginfo) { + PixelsWriterDefault pw = new PixelsWriterDefault(imginfo); + return pw; + } + + public final PixelsWriter getPixelsWriter() { + return pixelsWriter; + } + + public String getDebuginfo() { + return debuginfo.toString(); + } + +} diff --git a/png/src/main/java/org/xbib/graphics/imageio/plugins/png/pngj/PngWriterHc.java b/png/src/main/java/org/xbib/graphics/imageio/plugins/png/pngj/PngWriterHc.java new file mode 100644 index 0000000..402af77 --- /dev/null +++ b/png/src/main/java/org/xbib/graphics/imageio/plugins/png/pngj/PngWriterHc.java @@ -0,0 +1,36 @@ +package org.xbib.graphics.imageio.plugins.png.pngj; + +import java.io.File; +import java.io.OutputStream; +import org.xbib.graphics.imageio.plugins.png.pngj.pixels.PixelsWriter; +import org.xbib.graphics.imageio.plugins.png.pngj.pixels.PixelsWriterMultiple; + +/** + * Pngwriter with High compression EXPERIMENTAL + */ +public class PngWriterHc extends PngWriter { + + public PngWriterHc(File file, ImageInfo imgInfo, boolean allowoverwrite) { + super(file, imgInfo, allowoverwrite); + setFilterType(FilterType.FILTER_SUPER_ADAPTIVE); + } + + public PngWriterHc(File file, ImageInfo imgInfo) { + super(file, imgInfo); + } + + public PngWriterHc(OutputStream outputStream, ImageInfo imgInfo) { + super(outputStream, imgInfo); + } + + @Override + protected PixelsWriter createPixelsWriter(ImageInfo imginfo) { + PixelsWriterMultiple pw = new PixelsWriterMultiple(imginfo); + return pw; + } + + public PixelsWriterMultiple getPixelWriterMultiple() { + return (PixelsWriterMultiple) pixelsWriter; + } + +} diff --git a/png/src/main/java/org/xbib/graphics/imageio/plugins/png/pngj/PngjBadCrcException.java b/png/src/main/java/org/xbib/graphics/imageio/plugins/png/pngj/PngjBadCrcException.java new file mode 100644 index 0000000..021cd79 --- /dev/null +++ b/png/src/main/java/org/xbib/graphics/imageio/plugins/png/pngj/PngjBadCrcException.java @@ -0,0 +1,20 @@ +package org.xbib.graphics.imageio.plugins.png.pngj; + +/** + * Exception thrown by bad CRC check + */ +public class PngjBadCrcException extends PngjInputException { + private static final long serialVersionUID = 1L; + + public PngjBadCrcException(String message, Throwable cause) { + super(message, cause); + } + + public PngjBadCrcException(String message) { + super(message); + } + + public PngjBadCrcException(Throwable cause) { + super(cause); + } +} diff --git a/png/src/main/java/org/xbib/graphics/imageio/plugins/png/pngj/PngjBadSignature.java b/png/src/main/java/org/xbib/graphics/imageio/plugins/png/pngj/PngjBadSignature.java new file mode 100644 index 0000000..aa0f4ee --- /dev/null +++ b/png/src/main/java/org/xbib/graphics/imageio/plugins/png/pngj/PngjBadSignature.java @@ -0,0 +1,20 @@ +package org.xbib.graphics.imageio.plugins.png.pngj; + +/** + * Exception thrown by bad signature (not a PNG file) + */ +public class PngjBadSignature extends PngjInputException { + private static final long serialVersionUID = 1L; + + public PngjBadSignature(String message, Throwable cause) { + super(message, cause); + } + + public PngjBadSignature(String message) { + super(message); + } + + public PngjBadSignature(Throwable cause) { + super(cause); + } +} diff --git a/png/src/main/java/org/xbib/graphics/imageio/plugins/png/pngj/PngjException.java b/png/src/main/java/org/xbib/graphics/imageio/plugins/png/pngj/PngjException.java new file mode 100644 index 0000000..63513ed --- /dev/null +++ b/png/src/main/java/org/xbib/graphics/imageio/plugins/png/pngj/PngjException.java @@ -0,0 +1,20 @@ +package org.xbib.graphics.imageio.plugins.png.pngj; + +/** + * Generic exception for this library. It's a RuntimeException (unchecked) + */ +public class PngjException extends RuntimeException { + private static final long serialVersionUID = 1L; + + public PngjException(String message, Throwable cause) { + super(message, cause); + } + + public PngjException(String message) { + super(message); + } + + public PngjException(Throwable cause) { + super(cause); + } +} diff --git a/png/src/main/java/org/xbib/graphics/imageio/plugins/png/pngj/PngjExceptionInternal.java b/png/src/main/java/org/xbib/graphics/imageio/plugins/png/pngj/PngjExceptionInternal.java new file mode 100644 index 0000000..6775fee --- /dev/null +++ b/png/src/main/java/org/xbib/graphics/imageio/plugins/png/pngj/PngjExceptionInternal.java @@ -0,0 +1,23 @@ +package org.xbib.graphics.imageio.plugins.png.pngj; + +/** + * Exception for anomalous internal problems (sort of asserts) that point to + * some issue with the library + * + * @author Hernan J Gonzalez + */ +public class PngjExceptionInternal extends RuntimeException { + private static final long serialVersionUID = 1L; + + public PngjExceptionInternal(String message, Throwable cause) { + super(message, cause); + } + + public PngjExceptionInternal(String message) { + super(message); + } + + public PngjExceptionInternal(Throwable cause) { + super(cause); + } +} diff --git a/png/src/main/java/org/xbib/graphics/imageio/plugins/png/pngj/PngjInputException.java b/png/src/main/java/org/xbib/graphics/imageio/plugins/png/pngj/PngjInputException.java new file mode 100644 index 0000000..e124e2b --- /dev/null +++ b/png/src/main/java/org/xbib/graphics/imageio/plugins/png/pngj/PngjInputException.java @@ -0,0 +1,20 @@ +package org.xbib.graphics.imageio.plugins.png.pngj; + +/** + * Exception thrown when reading a PNG. + */ +public class PngjInputException extends PngjException { + private static final long serialVersionUID = 1L; + + public PngjInputException(String message, Throwable cause) { + super(message, cause); + } + + public PngjInputException(String message) { + super(message); + } + + public PngjInputException(Throwable cause) { + super(cause); + } +} diff --git a/png/src/main/java/org/xbib/graphics/imageio/plugins/png/pngj/PngjOutputException.java b/png/src/main/java/org/xbib/graphics/imageio/plugins/png/pngj/PngjOutputException.java new file mode 100644 index 0000000..8fc0a76 --- /dev/null +++ b/png/src/main/java/org/xbib/graphics/imageio/plugins/png/pngj/PngjOutputException.java @@ -0,0 +1,20 @@ +package org.xbib.graphics.imageio.plugins.png.pngj; + +/** + * Exception thrown by writing process + */ +public class PngjOutputException extends PngjException { + private static final long serialVersionUID = 1L; + + public PngjOutputException(String message, Throwable cause) { + super(message, cause); + } + + public PngjOutputException(String message) { + super(message); + } + + public PngjOutputException(Throwable cause) { + super(cause); + } +} diff --git a/png/src/main/java/org/xbib/graphics/imageio/plugins/png/pngj/PngjPrematureEnding.java b/png/src/main/java/org/xbib/graphics/imageio/plugins/png/pngj/PngjPrematureEnding.java new file mode 100644 index 0000000..a8e036c --- /dev/null +++ b/png/src/main/java/org/xbib/graphics/imageio/plugins/png/pngj/PngjPrematureEnding.java @@ -0,0 +1,10 @@ +package org.xbib.graphics.imageio.plugins.png.pngj; + +@SuppressWarnings("serial") +public class PngjPrematureEnding extends PngjInputException { + + public PngjPrematureEnding(String message) { + super(message); + } + +} diff --git a/png/src/main/java/org/xbib/graphics/imageio/plugins/png/pngj/PngjUnsupportedException.java b/png/src/main/java/org/xbib/graphics/imageio/plugins/png/pngj/PngjUnsupportedException.java new file mode 100644 index 0000000..479f375 --- /dev/null +++ b/png/src/main/java/org/xbib/graphics/imageio/plugins/png/pngj/PngjUnsupportedException.java @@ -0,0 +1,21 @@ +package org.xbib.graphics.imageio.plugins.png.pngj; + +/** + * Exception thrown because of some valid feature of PNG standard that this + * library does not support. + */ +public class PngjUnsupportedException extends PngjException { + private static final long serialVersionUID = 1L; + + public PngjUnsupportedException(String message, Throwable cause) { + super(message, cause); + } + + public PngjUnsupportedException(String message) { + super(message); + } + + public PngjUnsupportedException(Throwable cause) { + super(cause); + } +} diff --git a/png/src/main/java/org/xbib/graphics/imageio/plugins/png/pngj/RowInfo.java b/png/src/main/java/org/xbib/graphics/imageio/plugins/png/pngj/RowInfo.java new file mode 100644 index 0000000..799c716 --- /dev/null +++ b/png/src/main/java/org/xbib/graphics/imageio/plugins/png/pngj/RowInfo.java @@ -0,0 +1,54 @@ +package org.xbib.graphics.imageio.plugins.png.pngj; + +/** + * Packs information of current row. Only used internally + */ +class RowInfo { + public final ImageInfo imgInfo; + public final Deinterlacer deinterlacer; + public final boolean imode; // Interlaced + int dY, dX, oY, oX; // current step and offset (in pixels) + int rowNseq; // row number (from 0) in sequential read order + int rowNreal; // row number in the real image + int rowNsubImg; // current row in the virtual subsampled image; this increments (by 1) from 0 to rows/dy 7 times + int rowsSubImg, colsSubImg; // size of current subimage , in pixels + int bytesRow; + int pass; // 1-7 + byte[] buf; // non-deep copy + int buflen; // valid bytes in buffer (include filter byte) + + public RowInfo(ImageInfo imgInfo, Deinterlacer deinterlacer) { + this.imgInfo = imgInfo; + this.deinterlacer = deinterlacer; + this.imode = deinterlacer != null; + } + + void update(int rowseq) { + rowNseq = rowseq; + if (imode) { + pass = deinterlacer.getPass(); + dX = deinterlacer.dX; + dY = deinterlacer.dY; + oX = deinterlacer.oX; + oY = deinterlacer.oY; + rowNreal = deinterlacer.getCurrRowReal(); + rowNsubImg = deinterlacer.getCurrRowSubimg(); + rowsSubImg = deinterlacer.getRows(); + colsSubImg = deinterlacer.getCols(); + bytesRow = (imgInfo.bitspPixel * colsSubImg + 7) / 8; + } else { + pass = 1; + dX = dY = 1; + oX = oY = 0; + rowNreal = rowNsubImg = rowseq; + rowsSubImg = imgInfo.rows; + colsSubImg = imgInfo.cols; + bytesRow = imgInfo.bytesPerRow; + } + } + + void updateBuf(byte[] buf, int buflen) { + this.buf = buf; + this.buflen = buflen; + } +} diff --git a/png/src/main/java/org/xbib/graphics/imageio/plugins/png/pngj/chunks/ChunkCopyBehaviour.java b/png/src/main/java/org/xbib/graphics/imageio/plugins/png/pngj/chunks/ChunkCopyBehaviour.java new file mode 100644 index 0000000..9f86230 --- /dev/null +++ b/png/src/main/java/org/xbib/graphics/imageio/plugins/png/pngj/chunks/ChunkCopyBehaviour.java @@ -0,0 +1,110 @@ +package org.xbib.graphics.imageio.plugins.png.pngj.chunks; + +import org.xbib.graphics.imageio.plugins.png.pngj.ImageInfo; +import org.xbib.graphics.imageio.plugins.png.pngj.PngReader; +import org.xbib.graphics.imageio.plugins.png.pngj.PngWriter; + +/** + * Chunk copy policy to apply when copyng from a {@link PngReader} to a + * {@link PngWriter}. + *

      + * The constants are bit-masks, they can be OR-ed + *

      + * Reference: + * http://www.w3.org/TR/PNG/#14
      + */ +public class ChunkCopyBehaviour { + + /** + * Don't copy anything + */ + public static final int COPY_NONE = 0; + + /** + * copy the palette + */ + public static final int COPY_PALETTE = 1; + + /** + * copy all 'safe to copy' chunks + */ + public static final int COPY_ALL_SAFE = 1 << 2; + + /** + * copy all, including palette + */ + public static final int COPY_ALL = 1 << 3; // includes palette! + /** + * Copy PHYS chunk (physical resolution) + */ + public static final int COPY_PHYS = 1 << 4; // dpi + /** + * Copy al textual chunks. + */ + public static final int COPY_TEXTUAL = 1 << 5; // all textual types + /** + * Copy TRNS chunk + */ + public static final int COPY_TRANSPARENCY = 1 << 6; // + /** + * Copy unknown chunks (unknown by our factory) + */ + public static final int COPY_UNKNOWN = 1 << 7; // all unknown (by the factory!) + /** + * Copy almost all: excepts only HIST (histogram) TIME and TEXTUAL chunks + */ + public static final int COPY_ALMOSTALL = 1 << 8; + + private static boolean maskMatch(int v, int mask) { + return (v & mask) != 0; + } + + /** + * Creates a predicate equivalent to the copy mask + * Given a copy mask (see static fields) and the ImageInfo of the target + * PNG, returns a predicate that tells if a chunk should be copied. + * This is a handy helper method, you can also create and set your own + * predicate + */ + public static ChunkPredicate createPredicate(final int copyFromMask, final ImageInfo imgInfo) { + return new ChunkPredicate() { + public boolean match(PngChunk chunk) { + if (chunk.crit) { + if (chunk.id.equals(ChunkHelper.PLTE)) { + if (imgInfo.indexed && maskMatch(copyFromMask, ChunkCopyBehaviour.COPY_PALETTE)) { + return true; + } + return !imgInfo.greyscale && maskMatch(copyFromMask, ChunkCopyBehaviour.COPY_ALL); + } + } else { // ancillary + boolean text = (chunk instanceof PngChunkTextVar); + boolean safe = chunk.safe; + // notice that these if are not exclusive + if (maskMatch(copyFromMask, ChunkCopyBehaviour.COPY_ALL)) { + return true; + } + if (safe && maskMatch(copyFromMask, ChunkCopyBehaviour.COPY_ALL_SAFE)) { + return true; + } + if (chunk.id.equals(ChunkHelper.tRNS) + && maskMatch(copyFromMask, ChunkCopyBehaviour.COPY_TRANSPARENCY)) { + return true; + } + if (chunk.id.equals(ChunkHelper.pHYs) && maskMatch(copyFromMask, ChunkCopyBehaviour.COPY_PHYS)) { + return true; + } + if (text && maskMatch(copyFromMask, ChunkCopyBehaviour.COPY_TEXTUAL)) { + return true; + } + if (maskMatch(copyFromMask, ChunkCopyBehaviour.COPY_ALMOSTALL) && !(ChunkHelper.isUnknown(chunk) + || text || chunk.id.equals(ChunkHelper.hIST) || chunk.id.equals(ChunkHelper.tIME))) { + return true; + } + return maskMatch(copyFromMask, ChunkCopyBehaviour.COPY_UNKNOWN) && ChunkHelper.isUnknown(chunk); + } + return false; + } + + }; + } +} diff --git a/png/src/main/java/org/xbib/graphics/imageio/plugins/png/pngj/chunks/ChunkFactory.java b/png/src/main/java/org/xbib/graphics/imageio/plugins/png/pngj/chunks/ChunkFactory.java new file mode 100644 index 0000000..5704df3 --- /dev/null +++ b/png/src/main/java/org/xbib/graphics/imageio/plugins/png/pngj/chunks/ChunkFactory.java @@ -0,0 +1,134 @@ +package org.xbib.graphics.imageio.plugins.png.pngj.chunks; + +import org.xbib.graphics.imageio.plugins.png.pngj.IChunkFactory; +import org.xbib.graphics.imageio.plugins.png.pngj.ImageInfo; + +/** + * Default chunk factory. + *

      + * The user that wants to parse custom chunks can extend + * {@link #createEmptyChunkExtended(String, ImageInfo)} + */ +public class ChunkFactory implements IChunkFactory { + + boolean parse; + + public ChunkFactory() { + this(true); + } + + public ChunkFactory(boolean parse) { + this.parse = parse; + } + + public final PngChunk createChunk(ChunkRaw chunkRaw, ImageInfo imgInfo) { + PngChunk c = createEmptyChunkKnown(chunkRaw.id, imgInfo); + if (c == null) { + c = createEmptyChunkExtended(chunkRaw.id, imgInfo); + } + if (c == null) { + c = createEmptyChunkUnknown(chunkRaw.id, imgInfo); + } + c.setRaw(chunkRaw); + if (parse && chunkRaw.data != null) { + c.parseFromRaw(chunkRaw); + } + return c; + } + + protected final PngChunk createEmptyChunkKnown(String id, ImageInfo imgInfo) { + if (id.equals(ChunkHelper.IDAT)) { + return new PngChunkIDAT(imgInfo); + } + if (id.equals(ChunkHelper.IHDR)) { + return new PngChunkIHDR(imgInfo); + } + if (id.equals(ChunkHelper.PLTE)) { + return new PngChunkPLTE(imgInfo); + } + if (id.equals(ChunkHelper.IEND)) { + return new PngChunkIEND(imgInfo); + } + if (id.equals(ChunkHelper.tEXt)) { + return new PngChunkTEXT(imgInfo); + } + if (id.equals(ChunkHelper.iTXt)) { + return new PngChunkITXT(imgInfo); + } + if (id.equals(ChunkHelper.zTXt)) { + return new PngChunkZTXT(imgInfo); + } + if (id.equals(ChunkHelper.bKGD)) { + return new PngChunkBKGD(imgInfo); + } + if (id.equals(ChunkHelper.gAMA)) { + return new PngChunkGAMA(imgInfo); + } + if (id.equals(ChunkHelper.pHYs)) { + return new PngChunkPHYS(imgInfo); + } + if (id.equals(ChunkHelper.iCCP)) { + return new PngChunkICCP(imgInfo); + } + if (id.equals(ChunkHelper.tIME)) { + return new PngChunkTIME(imgInfo); + } + if (id.equals(ChunkHelper.tRNS)) { + return new PngChunkTRNS(imgInfo); + } + if (id.equals(ChunkHelper.cHRM)) { + return new PngChunkCHRM(imgInfo); + } + if (id.equals(ChunkHelper.sBIT)) { + return new PngChunkSBIT(imgInfo); + } + if (id.equals(ChunkHelper.sRGB)) { + return new PngChunkSRGB(imgInfo); + } + if (id.equals(ChunkHelper.hIST)) { + return new PngChunkHIST(imgInfo); + } + if (id.equals(ChunkHelper.sPLT)) { + return new PngChunkSPLT(imgInfo); + } + // apng + if (id.equals(PngChunkFDAT.ID)) { + return new PngChunkFDAT(imgInfo); + } + if (id.equals(PngChunkACTL.ID)) { + return new PngChunkACTL(imgInfo); + } + if (id.equals(PngChunkFCTL.ID)) { + return new PngChunkFCTL(imgInfo); + } + return null; + } + + /** + * This is used as last resort factory method. + *

      + * It creates a {@link PngChunkUNKNOWN} chunk. + */ + protected final PngChunk createEmptyChunkUnknown(String id, ImageInfo imgInfo) { + return new PngChunkUNKNOWN(id, imgInfo); + } + + /** + * Factory for chunks that are not in the original PNG standard. This can be + * overriden (but dont forget to call this also) + * + * @param id Chunk id , 4 letters + * @param imgInfo Usually not needed + * @return null if chunk id not recognized + */ + protected PngChunk createEmptyChunkExtended(String id, ImageInfo imgInfo) { + if (id.equals(PngChunkOFFS.ID)) { + return new PngChunkOFFS(imgInfo); + } + if (id.equals(PngChunkSTER.ID)) { + return new PngChunkSTER(imgInfo); + } + return null; // extend! + } + +} diff --git a/png/src/main/java/org/xbib/graphics/imageio/plugins/png/pngj/chunks/ChunkHelper.java b/png/src/main/java/org/xbib/graphics/imageio/plugins/png/pngj/chunks/ChunkHelper.java new file mode 100644 index 0000000..1afbacc --- /dev/null +++ b/png/src/main/java/org/xbib/graphics/imageio/plugins/png/pngj/chunks/ChunkHelper.java @@ -0,0 +1,287 @@ +package org.xbib.graphics.imageio.plugins.png.pngj.chunks; + +import java.io.ByteArrayInputStream; +import java.io.ByteArrayOutputStream; +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; +import java.util.ArrayList; +import java.util.Iterator; +import java.util.List; +import java.util.regex.Pattern; +import java.util.zip.DeflaterOutputStream; +import java.util.zip.InflaterInputStream; +import org.xbib.graphics.imageio.plugins.png.pngj.PngHelperInternal; +import org.xbib.graphics.imageio.plugins.png.pngj.PngjException; + +/** + * Helper methods and constants related to Chunk processing. + * This should only be of interest to developers doing special chunk processing + * or extending the ChunkFactory + */ +public class ChunkHelper { + ChunkHelper() { + } + + public static final String IHDR = "IHDR"; + public static final String PLTE = "PLTE"; + public static final String IDAT = "IDAT"; + public static final String IEND = "IEND"; + public static final String cHRM = "cHRM"; + public static final String gAMA = "gAMA"; + public static final String iCCP = "iCCP"; + public static final String sBIT = "sBIT"; + public static final String sRGB = "sRGB"; + public static final String bKGD = "bKGD"; + public static final String hIST = "hIST"; + public static final String tRNS = "tRNS"; + public static final String pHYs = "pHYs"; + public static final String sPLT = "sPLT"; + public static final String tIME = "tIME"; + public static final String iTXt = "iTXt"; + public static final String tEXt = "tEXt"; + public static final String zTXt = "zTXt"; + + public static final byte[] b_IHDR = toBytesLatin1(IHDR); + public static final byte[] b_PLTE = toBytesLatin1(PLTE); + public static final byte[] b_IDAT = toBytesLatin1(IDAT); + public static final byte[] b_IEND = toBytesLatin1(IEND); + + /* + * static auxiliary buffer. any method that uses this should synchronize against + * this + */ + private static final byte[] tmpbuffer = new byte[4096]; + + /** + * Converts to bytes using Latin1 (ISO-8859-1) + */ + public static byte[] toBytesLatin1(String x) { + return x.getBytes(PngHelperInternal.charsetLatin1); + } + + /** + * Converts to String using Latin1 (ISO-8859-1) + */ + public static String toStringLatin1(byte[] x) { + return new String(x, PngHelperInternal.charsetLatin1); + } + + /** + * Converts to String using Latin1 (ISO-8859-1) + */ + public static String toStringLatin1(byte[] x, int offset, int len) { + return new String(x, offset, len, PngHelperInternal.charsetLatin1); + } + + /** + * Converts to bytes using UTF-8 + */ + public static byte[] toBytesUTF8(String x) { + return x.getBytes(PngHelperInternal.charsetUTF8); + } + + /** + * Converts to string using UTF-8 + */ + public static String toStringUTF8(byte[] x) { + return new String(x, PngHelperInternal.charsetUTF8); + } + + /** + * Converts to string using UTF-8 + */ + public static String toStringUTF8(byte[] x, int offset, int len) { + return new String(x, offset, len, PngHelperInternal.charsetUTF8); + } + + /** + * critical chunk : first letter is uppercase + */ + public static boolean isCritical(String id) { + return (Character.isUpperCase(id.charAt(0))); + } + + /** + * public chunk: second letter is uppercase + */ + public static boolean isPublic(String id) { // + return (Character.isUpperCase(id.charAt(1))); + } + + /** + * Safe to copy chunk: fourth letter is lower case + */ + public static boolean isSafeToCopy(String id) { + return (!Character.isUpperCase(id.charAt(3))); + } + + /** + * "Unknown" just means that our chunk factory (even when it has been + * augmented by client code) did not recognize its id + */ + public static boolean isUnknown(PngChunk c) { + return c instanceof PngChunkUNKNOWN; + } + + /** + * Finds position of null byte in array + * + * @param b + * @return -1 if not found + */ + public static int posNullByte(byte[] b) { + for (int i = 0; i < b.length; i++) { + if (b[i] == 0) { + return i; + } + } + return -1; + } + + /** + * Decides if a chunk should be loaded, according to a ChunkLoadBehaviour + * + * @param id + * @param behav + * @return true/false + */ + public static boolean shouldLoad(String id, ChunkLoadBehaviour behav) { + if (isCritical(id)) { + return true; + } + switch (behav) { + case LOAD_CHUNK_ALWAYS: + return true; + case LOAD_CHUNK_IF_SAFE: + return isSafeToCopy(id); + case LOAD_CHUNK_NEVER: + return false; + case LOAD_CHUNK_MOST_IMPORTANT: + return id.equals(PngChunkTRNS.ID); + } + return false; // should not reach here + } + + public final static byte[] compressBytes(byte[] ori, boolean compress) { + return compressBytes(ori, 0, ori.length, compress); + } + + public static byte[] compressBytes(byte[] ori, int offset, int len, boolean compress) { + try { + ByteArrayInputStream inb = new ByteArrayInputStream(ori, offset, len); + InputStream in = compress ? inb : new InflaterInputStream(inb); + ByteArrayOutputStream outb = new ByteArrayOutputStream(); + OutputStream out = compress ? new DeflaterOutputStream(outb) : outb; + shovelInToOut(in, out); + in.close(); + out.close(); + return outb.toByteArray(); + } catch (Exception e) { + throw new PngjException(e); + } + } + + /** + * Shovels all data from an input stream to an output stream. + */ + private static void shovelInToOut(InputStream in, OutputStream out) throws IOException { + synchronized (tmpbuffer) { + int len; + while ((len = in.read(tmpbuffer)) > 0) { + out.write(tmpbuffer, 0, len); + } + } + } + + /** + * Returns only the chunks that "match" the predicate + *

      + * See also trimList() + */ + public static List filterList(List target, ChunkPredicate predicateKeep) { + List result = new ArrayList(); + for (PngChunk element : target) { + if (predicateKeep.match(element)) { + result.add(element); + } + } + return result; + } + + /** + * Remove (in place) the chunks that "match" the predicate + *

      + * See also filterList + */ + public static int trimList(List target, ChunkPredicate predicateRemove) { + Iterator it = target.iterator(); + int cont = 0; + while (it.hasNext()) { + PngChunk c = it.next(); + if (predicateRemove.match(c)) { + it.remove(); + cont++; + } + } + return cont; + } + + /** + * Adhoc criteria: two ancillary chunks are "equivalent" ("practically same + * type") if they have same id and (perhaps, if multiple are allowed) if the + * match also in some "internal key" (eg: key for string values, palette for + * sPLT, etc) + *

      + * When we use this method, we implicitly assume that we don't allow/expect + * two "equivalent" chunks in a single PNG + *

      + * Notice that the use of this is optional, and that the PNG standard + * actually allows text chunks that have same key + * + * @return true if "equivalent" + */ + public static final boolean equivalent(PngChunk c1, PngChunk c2) { + if (c1 == c2) { + return true; + } + if (c1 == null || c2 == null || !c1.id.equals(c2.id)) { + return false; + } + if (c1.crit) { + return false; + } + // same id + if (c1.getClass() != c2.getClass()) { + return false; // should not happen + } + if (!c2.allowsMultiple()) { + return true; + } + if (c1 instanceof PngChunkTextVar) { + return ((PngChunkTextVar) c1).getKey().equals(((PngChunkTextVar) c2).getKey()); + } + if (c1 instanceof PngChunkSPLT) { + return ((PngChunkSPLT) c1).getPalName().equals(((PngChunkSPLT) c2).getPalName()); + } + // unknown chunks that allow multiple? consider they don't match + return false; + } + + public static boolean isText(PngChunk c) { + return c instanceof PngChunkTextVar; + } + + /** + * Convert four bytes to String (chunk id) + */ + public static String idFromBytes(byte[] buf, int offset) { + if (buf == null || buf.length < 4 + offset) { + return "?"; + } + return toStringLatin1(buf, offset, 4); + } + + public static Pattern CHUNK_ID_PAT = Pattern.compile("[a-zA-Z][a-zA-Z][A-Z][a-zA-Z]"); + +} diff --git a/png/src/main/java/org/xbib/graphics/imageio/plugins/png/pngj/chunks/ChunkLoadBehaviour.java b/png/src/main/java/org/xbib/graphics/imageio/plugins/png/pngj/chunks/ChunkLoadBehaviour.java new file mode 100644 index 0000000..09bb9de --- /dev/null +++ b/png/src/main/java/org/xbib/graphics/imageio/plugins/png/pngj/chunks/ChunkLoadBehaviour.java @@ -0,0 +1,25 @@ +package org.xbib.graphics.imageio.plugins.png.pngj.chunks; + +/** + * What to do with ancillary (non-critical) chunks when reading. + */ +public enum ChunkLoadBehaviour { + /** + * All non-critical chunks are skipped + */ + LOAD_CHUNK_NEVER, + /** + * Load chunk if "safe to copy" + */ + LOAD_CHUNK_IF_SAFE, + /** + * Load only most important chunk: TRNS + */ + LOAD_CHUNK_MOST_IMPORTANT, + /** + * Load all chunks. + * Notice that other restrictions might apply, see + * PngReader.skipChunkMaxSize PngReader.skipChunkIds + */ + LOAD_CHUNK_ALWAYS +} diff --git a/png/src/main/java/org/xbib/graphics/imageio/plugins/png/pngj/chunks/ChunkPredicate.java b/png/src/main/java/org/xbib/graphics/imageio/plugins/png/pngj/chunks/ChunkPredicate.java new file mode 100644 index 0000000..f982b94 --- /dev/null +++ b/png/src/main/java/org/xbib/graphics/imageio/plugins/png/pngj/chunks/ChunkPredicate.java @@ -0,0 +1,14 @@ +package org.xbib.graphics.imageio.plugins.png.pngj.chunks; + +/** + * Decides if another chunk "matches", according to some criterion. + */ +public interface ChunkPredicate { + /** + * The other chunk matches with this one. + * + * @param chunk chunk + * @return true if match + */ + boolean match(PngChunk chunk); +} diff --git a/png/src/main/java/org/xbib/graphics/imageio/plugins/png/pngj/chunks/ChunkRaw.java b/png/src/main/java/org/xbib/graphics/imageio/plugins/png/pngj/chunks/ChunkRaw.java new file mode 100644 index 0000000..a94464d --- /dev/null +++ b/png/src/main/java/org/xbib/graphics/imageio/plugins/png/pngj/chunks/ChunkRaw.java @@ -0,0 +1,191 @@ +package org.xbib.graphics.imageio.plugins.png.pngj.chunks; + +import java.io.ByteArrayInputStream; +import java.io.OutputStream; +import java.util.logging.Logger; +import java.util.zip.CRC32; +import org.xbib.graphics.imageio.plugins.png.pngj.PngHelperInternal; +import org.xbib.graphics.imageio.plugins.png.pngj.PngjBadCrcException; +import org.xbib.graphics.imageio.plugins.png.pngj.PngjException; +import org.xbib.graphics.imageio.plugins.png.pngj.PngjOutputException; + +/** + * Raw (physical) chunk. + * Short lived object, to be created while serialing/deserializing Do not reuse + * it for different chunks. + * See http://www.libpng.org/pub/png/spec/1.2/PNG-Structure.html + */ +public class ChunkRaw { + private static final Logger LOGGER = Logger.getLogger(ChunkRaw.class.getName()); + /** + * The length counts only the data field, not itself, the chunk type code, + * or the CRC. Zero is a valid length. Although encoders and decoders should + * treat the length as unsigned, its value must not exceed 231-1 bytes. + */ + public final int len; + + /** + * A 4-byte chunk type code. uppercase and lowercase ASCII letters + */ + public final byte[] idbytes; + public final String id; + + /** + * The data bytes appropriate to the chunk type, if any. This field can be + * of zero length. Does not include crc. If it's null, it means that the + * data is ot available + */ + public byte[] data = null; + /** + * @see ChunkRaw#getOffset() + */ + private long offset = 0; + + /** + * A 4-byte CRC (Cyclic Redundancy Check) calculated on the preceding bytes + * in the chunk, including the chunk type code and chunk data fields, but + * not including the length field. + */ + public byte[] crcval = new byte[4]; + + private CRC32 crcengine; // lazily instantiated + + public ChunkRaw(int len, String id, boolean alloc) { + this.len = len; + this.id = id; + this.idbytes = ChunkHelper.toBytesLatin1(id); + for (int i = 0; i < 4; i++) { + if (idbytes[i] < 0x41 || idbytes[i] > 0x7a || (idbytes[i] > 0x5a && idbytes[i] < 0x61)) { + throw new PngjException("Bad id chunk: must be ascii letters " + id); + } + } + if (alloc) { + allocData(); + } + } + + public ChunkRaw(int len, byte[] idbytes, boolean alloc) { + this(len, ChunkHelper.toStringLatin1(idbytes), alloc); + } + + public void allocData() { // TODO: not public + if (data == null || data.length < len) { + data = new byte[len]; + } + } + + /** + * this is called after setting data, before writing to os + */ + private void computeCrcForWriting() { + crcengine = new CRC32(); + crcengine.update(idbytes, 0, 4); + if (len > 0) { + crcengine.update(data, 0, len); // + } + PngHelperInternal.writeInt4tobytes((int) crcengine.getValue(), crcval, 0); + } + + /** + * Computes the CRC and writes to the stream. If error, a + * PngjOutputException is thrown + *

      + * Note that this is only used for non idat chunks + */ + public void writeChunk(OutputStream os) { + writeChunkHeader(os); + if (len > 0) { + if (data == null) { + throw new PngjOutputException("cannot write chunk, raw chunk data is null [" + id + "]"); + } + PngHelperInternal.writeBytes(os, data, 0, len); + } + computeCrcForWriting(); + writeChunkCrc(os); + } + + public void writeChunkHeader(OutputStream os) { + if (idbytes.length != 4) { + throw new PngjOutputException("bad chunkid [" + id + "]"); + } + PngHelperInternal.writeInt4(os, len); + PngHelperInternal.writeBytes(os, idbytes); + } + + public void writeChunkCrc(OutputStream os) { + PngHelperInternal.writeBytes(os, crcval, 0, 4); + } + + public void checkCrc(boolean throwExcep) { + int crcComputed = (int) crcengine.getValue(); + int crcExpected = PngHelperInternal.readInt4fromBytes(crcval, 0); + if (crcComputed != crcExpected) { + String msg = String.format("Bad CRC in chunk: %s (offset:%d). Expected:%x Got:%x", id, offset, crcExpected, + crcComputed); + if (throwExcep) { + throw new PngjBadCrcException(msg); + } else { + LOGGER.warning(msg); + } + } + } + + public void updateCrc(byte[] buf, int off, int len) { + if (crcengine == null) { + crcengine = new CRC32(); + } + crcengine.update(buf, off, len); + } + + ByteArrayInputStream getAsByteStream() { // only the data + return new ByteArrayInputStream(data); + } + + /** + * offset in the full PNG stream, in bytes. only informational, for read + * chunks (0=NA) + */ + public long getOffset() { + return offset; + } + + public void setOffset(long offset) { + this.offset = offset; + } + + public String toString() { + return "chunkid=" + ChunkHelper.toStringLatin1(idbytes) + " len=" + len; + } + + @Override + public int hashCode() { + final int prime = 31; + int result = 1; + result = prime * result + ((id == null) ? 0 : id.hashCode()); + result = prime * result + (int) (offset ^ (offset >>> 32)); + return result; + } + + @Override + public boolean equals(Object obj) { + if (this == obj) { + return true; + } + if (obj == null) { + return false; + } + if (getClass() != obj.getClass()) { + return false; + } + ChunkRaw other = (ChunkRaw) obj; + if (id == null) { + if (other.id != null) { + return false; + } + } else if (!id.equals(other.id)) { + return false; + } + return offset == other.offset; + } + +} diff --git a/png/src/main/java/org/xbib/graphics/imageio/plugins/png/pngj/chunks/ChunksList.java b/png/src/main/java/org/xbib/graphics/imageio/plugins/png/pngj/chunks/ChunksList.java new file mode 100644 index 0000000..b46da74 --- /dev/null +++ b/png/src/main/java/org/xbib/graphics/imageio/plugins/png/pngj/chunks/ChunksList.java @@ -0,0 +1,169 @@ +package org.xbib.graphics.imageio.plugins.png.pngj.chunks; + +import java.util.ArrayList; +import java.util.List; +import org.xbib.graphics.imageio.plugins.png.pngj.ImageInfo; +import org.xbib.graphics.imageio.plugins.png.pngj.PngjException; + +/** + * All chunks that form an image, read or to be written. + *

      + * chunks include all chunks, but IDAT is a single pseudo chunk without data + **/ +public class ChunksList { + // ref: http://www.w3.org/TR/PNG/#table53 + public static final int CHUNK_GROUP_0_IDHR = 0; // required - single + public static final int CHUNK_GROUP_1_AFTERIDHR = 1; // optional - multiple + public static final int CHUNK_GROUP_2_PLTE = 2; // optional - single + public static final int CHUNK_GROUP_3_AFTERPLTE = 3; // optional - multple + public static final int CHUNK_GROUP_4_IDAT = 4; // required (single pseudo chunk) + public static final int CHUNK_GROUP_5_AFTERIDAT = 5; // optional - multple + public static final int CHUNK_GROUP_6_END = 6; // only 1 chunk - requried + + /** + * All chunks, read (or written) + *

      + * But IDAT is a single pseudo chunk without data + */ + List chunks = new ArrayList(); + + final ImageInfo imageInfo; // only required for writing + + boolean withPlte = false; + + public ChunksList(ImageInfo imfinfo) { + this.imageInfo = imfinfo; + } + + /** + * WARNING: this does NOT return a copy, but the list itself. The called + * should not modify this directly! Don't use this to manipulate the chunks. + */ + public List getChunks() { + return chunks; + } + + protected static List getXById(final List list, final String id, final String innerid) { + if (innerid == null) { + return ChunkHelper.filterList(list, new ChunkPredicate() { + public boolean match(PngChunk c) { + return c.id.equals(id); + } + }); + } else { + return ChunkHelper.filterList(list, new ChunkPredicate() { + public boolean match(PngChunk c) { + if (!c.id.equals(id)) { + return false; + } + if (c instanceof PngChunkTextVar && !((PngChunkTextVar) c).getKey().equals(innerid)) { + return false; + } + return !(c instanceof PngChunkSPLT) || ((PngChunkSPLT) c).getPalName().equals(innerid); + } + }); + } + } + + /** + * Adds chunk in next position. This is used onyl by the pngReader + */ + public void appendReadChunk(PngChunk chunk, int chunkGroup) { + chunk.setChunkGroup(chunkGroup); + chunks.add(chunk); + if (chunk.id.equals(PngChunkPLTE.ID)) { + withPlte = true; + } + } + + /** + * All chunks with this ID + * + * @param id + * @return List, empty if none + */ + public List getById(final String id) { + return getById(id, null); + } + + /** + * If innerid!=null and the chunk is PngChunkTextVar or PngChunkSPLT, it's + * filtered by that id + * + * @param id + * @return List, empty if none + */ + public List getById(final String id, final String innerid) { + return getXById(chunks, id, innerid); + } + + /** + * Returns only one chunk + * + * @param id + * @return First chunk found, null if not found + */ + public PngChunk getById1(final String id) { + return getById1(id, false); + } + + /** + * Returns only one chunk or null if nothing found - does not include queued + *

      + * If more than one chunk is found, then an exception is thrown + * (failifMultiple=true or chunk is single) or the last one is returned + * (failifMultiple=false) + **/ + public PngChunk getById1(final String id, final boolean failIfMultiple) { + return getById1(id, null, failIfMultiple); + } + + /** + * Returns only one chunk or null if nothing found - does not include queued + *

      + * If more than one chunk (after filtering by inner id) is found, then an + * exception is thrown (failifMultiple=true or chunk is single) or the last + * one is returned (failifMultiple=false) + **/ + public PngChunk getById1(final String id, final String innerid, final boolean failIfMultiple) { + List list = getById(id, innerid); + if (list.isEmpty()) { + return null; + } + if (list.size() > 1 && (failIfMultiple || !list.get(0).allowsMultiple())) { + throw new PngjException("unexpected multiple chunks id=" + id); + } + return list.get(list.size() - 1); + } + + /** + * Finds all chunks "equivalent" to this one + * + * @param c2 + * @return Empty if nothing found + */ + public List getEquivalent(final PngChunk c2) { + return ChunkHelper.filterList(chunks, new ChunkPredicate() { + public boolean match(PngChunk c) { + return ChunkHelper.equivalent(c, c2); + } + }); + } + + public String toString() { + return "ChunkList: read: " + chunks.size(); + } + + /** + * for debugging + */ + public String toStringFull() { + StringBuilder sb = new StringBuilder(toString()); + sb.append("\n Read:\n"); + for (PngChunk chunk : chunks) { + sb.append(chunk).append(" G=" + chunk.getChunkGroup() + "\n"); + } + return sb.toString(); + } + +} diff --git a/png/src/main/java/org/xbib/graphics/imageio/plugins/png/pngj/chunks/ChunksListForWrite.java b/png/src/main/java/org/xbib/graphics/imageio/plugins/png/pngj/chunks/ChunksListForWrite.java new file mode 100644 index 0000000..bd2cea9 --- /dev/null +++ b/png/src/main/java/org/xbib/graphics/imageio/plugins/png/pngj/chunks/ChunksListForWrite.java @@ -0,0 +1,198 @@ +package org.xbib.graphics.imageio.plugins.png.pngj.chunks; + +import java.io.OutputStream; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.Iterator; +import java.util.List; +import org.xbib.graphics.imageio.plugins.png.pngj.ImageInfo; +import org.xbib.graphics.imageio.plugins.png.pngj.PngjException; +import org.xbib.graphics.imageio.plugins.png.pngj.PngjOutputException; + +public class ChunksListForWrite extends ChunksList { + + /** + * chunks not yet writen - does not include IHDR, IDAT, END, perhaps yes + * PLTE + */ + private final List queuedChunks = new ArrayList(); + + // redundant, just for eficciency + private final HashMap alreadyWrittenKeys = new HashMap(); + + public ChunksListForWrite(ImageInfo imfinfo) { + super(imfinfo); + } + + /** + * Same as getById(), but looking in the queued chunks + */ + public List getQueuedById(final String id) { + return getQueuedById(id, null); + } + + /** + * Same as getById(), but looking in the queued chunks + */ + public List getQueuedById(final String id, final String innerid) { + return getXById(queuedChunks, id, innerid); + } + + /** + * Same as getById1(), but looking in the queued chunks + **/ + public PngChunk getQueuedById1(final String id, final String innerid, final boolean failIfMultiple) { + List list = getQueuedById(id, innerid); + if (list.isEmpty()) { + return null; + } + if (list.size() > 1 && (failIfMultiple || !list.get(0).allowsMultiple())) { + throw new PngjException("unexpected multiple chunks id=" + id); + } + return list.get(list.size() - 1); + } + + /** + * Same as getById1(), but looking in the queued chunks + **/ + public PngChunk getQueuedById1(final String id, final boolean failIfMultiple) { + return getQueuedById1(id, null, failIfMultiple); + } + + /** + * Same as getById1(), but looking in the queued chunks + **/ + public PngChunk getQueuedById1(final String id) { + return getQueuedById1(id, false); + } + + /** + * Finds all chunks "equivalent" to this one + * + * @param c2 + * @return Empty if nothing found + */ + public List getQueuedEquivalent(final PngChunk c2) { + return ChunkHelper.filterList(queuedChunks, new ChunkPredicate() { + public boolean match(PngChunk c) { + return ChunkHelper.equivalent(c, c2); + } + }); + } + + /** + * Remove Chunk: only from queued + *

      + * WARNING: this depends on c.equals() implementation, which is + * straightforward for SingleChunks. For MultipleChunks, it will normally + * check for reference equality! + */ + public boolean removeChunk(PngChunk c) { + if (c == null) { + return false; + } + return queuedChunks.remove(c); + } + + /** + * Adds chunk to queue + *

      + * If there + * + * @param c + */ + public boolean queue(PngChunk c) { + queuedChunks.add(c); + return true; + } + + /** + * this should be called only for ancillary chunks and PLTE (groups 1 - 3 - + * 5) + **/ + private static boolean shouldWrite(PngChunk c, int currentGroup) { + if (currentGroup == CHUNK_GROUP_2_PLTE) { + return c.id.equals(ChunkHelper.PLTE); + } + if (currentGroup % 2 == 0) { + throw new PngjOutputException("bad chunk group?"); + } + int minChunkGroup, maxChunkGroup; + if (c.getOrderingConstraint().mustGoBeforePLTE()) { + minChunkGroup = maxChunkGroup = ChunksList.CHUNK_GROUP_1_AFTERIDHR; + } else if (c.getOrderingConstraint().mustGoBeforeIDAT()) { + maxChunkGroup = ChunksList.CHUNK_GROUP_3_AFTERPLTE; + minChunkGroup = c.getOrderingConstraint().mustGoAfterPLTE() ? ChunksList.CHUNK_GROUP_3_AFTERPLTE + : ChunksList.CHUNK_GROUP_1_AFTERIDHR; + } else { + maxChunkGroup = ChunksList.CHUNK_GROUP_5_AFTERIDAT; + minChunkGroup = ChunksList.CHUNK_GROUP_1_AFTERIDHR; + } + + int preferred = maxChunkGroup; + if (c.hasPriority()) { + preferred = minChunkGroup; + } + if (ChunkHelper.isUnknown(c) && c.getChunkGroup() > 0) { + preferred = c.getChunkGroup(); + } + if (currentGroup == preferred) { + return true; + } + return currentGroup > preferred && currentGroup <= maxChunkGroup; + } + + public int writeChunks(OutputStream os, int currentGroup) { + int cont = 0; + Iterator it = queuedChunks.iterator(); + while (it.hasNext()) { + PngChunk c = it.next(); + if (!shouldWrite(c, currentGroup)) { + continue; + } + if (ChunkHelper.isCritical(c.id) && !c.id.equals(ChunkHelper.PLTE)) { + throw new PngjOutputException("bad chunk queued: " + c); + } + if (alreadyWrittenKeys.containsKey(c.id) && !c.allowsMultiple()) { + throw new PngjOutputException("duplicated chunk does not allow multiple: " + c); + } + c.write(os); + chunks.add(c); + alreadyWrittenKeys.put(c.id, alreadyWrittenKeys.containsKey(c.id) ? alreadyWrittenKeys.get(c.id) + 1 : 1); + c.setChunkGroup(currentGroup); + it.remove(); + cont++; + } + return cont; + } + + /** + * warning: this is NOT a copy, do not modify + */ + public List getQueuedChunks() { + return queuedChunks; + } + + public String toString() { + return "ChunkList: written: " + getChunks().size() + " queue: " + queuedChunks.size(); + } + + /** + * for debugging + */ + public String toStringFull() { + StringBuilder sb = new StringBuilder(toString()); + sb.append("\n Written:\n"); + for (PngChunk chunk : getChunks()) { + sb.append(chunk).append(" G=" + chunk.getChunkGroup() + "\n"); + } + if (!queuedChunks.isEmpty()) { + sb.append(" Queued:\n"); + for (PngChunk chunk : queuedChunks) { + sb.append(chunk).append("\n"); + } + + } + return sb.toString(); + } +} diff --git a/png/src/main/java/org/xbib/graphics/imageio/plugins/png/pngj/chunks/PngBadCharsetException.java b/png/src/main/java/org/xbib/graphics/imageio/plugins/png/pngj/chunks/PngBadCharsetException.java new file mode 100644 index 0000000..bd3369c --- /dev/null +++ b/png/src/main/java/org/xbib/graphics/imageio/plugins/png/pngj/chunks/PngBadCharsetException.java @@ -0,0 +1,20 @@ +package org.xbib.graphics.imageio.plugins.png.pngj.chunks; + +import org.xbib.graphics.imageio.plugins.png.pngj.PngjException; + +public class PngBadCharsetException extends PngjException { + private static final long serialVersionUID = 1L; + + public PngBadCharsetException(String message, Throwable cause) { + super(message, cause); + } + + public PngBadCharsetException(String message) { + super(message); + } + + public PngBadCharsetException(Throwable cause) { + super(cause); + } + +} diff --git a/png/src/main/java/org/xbib/graphics/imageio/plugins/png/pngj/chunks/PngChunk.java b/png/src/main/java/org/xbib/graphics/imageio/plugins/png/pngj/chunks/PngChunk.java new file mode 100644 index 0000000..5f18971 --- /dev/null +++ b/png/src/main/java/org/xbib/graphics/imageio/plugins/png/pngj/chunks/PngChunk.java @@ -0,0 +1,227 @@ +package org.xbib.graphics.imageio.plugins.png.pngj.chunks; + +import java.io.OutputStream; +import org.xbib.graphics.imageio.plugins.png.pngj.ImageInfo; +import org.xbib.graphics.imageio.plugins.png.pngj.PngjExceptionInternal; + +/** + * Represents a instance of a PNG chunk. + *

      + * See + * http://www + * .libpng.org/pub/png/spec/1.2/PNG-Chunks .html + *

      + * Concrete classes should extend {@link PngChunkSingle} or + * {@link PngChunkMultiple} + *

      + * Note that some methods/fields are type-specific (getOrderingConstraint(), + * allowsMultiple()),
      + * some are 'almost' type-specific (id,crit,pub,safe; the exception is + * PngUKNOWN),
      + * and the rest are instance-specific + */ +public abstract class PngChunk { + + /** + * Chunk-id: 4 letters + */ + public final String id; + /** + * Autocomputed at creation time + */ + public final boolean crit, pub, safe; + + protected final ImageInfo imgInfo; + + protected ChunkRaw raw; + + // For writing. Queued chunks with high priority will be written as soon as possible + private boolean priority = false; + + protected int chunkGroup = -1; // chunk group where it was read or writen + + /** + * Possible ordering constraint for a PngChunk type -only relevant for + * ancillary chunks. Theoretically, there could be more general constraints, + * but these cover the constraints for standard chunks. + */ + public enum ChunkOrderingConstraint { + /** + * no ordering constraint + */ + NONE, + /** + * Must go before PLTE (and hence, also before IDAT) + */ + BEFORE_PLTE_AND_IDAT, + /** + * Must go after PLTE (if exists) but before IDAT + */ + AFTER_PLTE_BEFORE_IDAT, + /** + * Must go after PLTE (and it must exist) but before IDAT + */ + AFTER_PLTE_BEFORE_IDAT_PLTE_REQUIRED, + /** + * Must before IDAT (before or after PLTE) + */ + BEFORE_IDAT, + /** + * After IDAT (this restriction does not apply to the standard PNG + * chunks) + */ + AFTER_IDAT, + /** + * Does not apply + */ + NA; + + public boolean mustGoBeforePLTE() { + return this == BEFORE_PLTE_AND_IDAT; + } + + public boolean mustGoBeforeIDAT() { + return this == BEFORE_IDAT || this == BEFORE_PLTE_AND_IDAT || this == AFTER_PLTE_BEFORE_IDAT; + } + + /** + * after pallete, if exists + */ + public boolean mustGoAfterPLTE() { + return this == AFTER_PLTE_BEFORE_IDAT || this == AFTER_PLTE_BEFORE_IDAT_PLTE_REQUIRED; + } + + public boolean mustGoAfterIDAT() { + return this == AFTER_IDAT; + } + + public boolean isOk(int currentChunkGroup, boolean hasplte) { + if (this == NONE) { + return true; + } else if (this == BEFORE_IDAT) { + return currentChunkGroup < ChunksList.CHUNK_GROUP_4_IDAT; + } else if (this == BEFORE_PLTE_AND_IDAT) { + return currentChunkGroup < ChunksList.CHUNK_GROUP_2_PLTE; + } else if (this == AFTER_PLTE_BEFORE_IDAT) { + return hasplte ? currentChunkGroup < ChunksList.CHUNK_GROUP_4_IDAT + : (currentChunkGroup < ChunksList.CHUNK_GROUP_4_IDAT + && currentChunkGroup > ChunksList.CHUNK_GROUP_2_PLTE); + } else if (this == AFTER_IDAT) { + return currentChunkGroup > ChunksList.CHUNK_GROUP_4_IDAT; + } + return false; + } + } + + public PngChunk(String id, ImageInfo imgInfo) { + this.id = id; + this.imgInfo = imgInfo; + this.crit = ChunkHelper.isCritical(id); + this.pub = ChunkHelper.isPublic(id); + this.safe = ChunkHelper.isSafeToCopy(id); + } + + protected final ChunkRaw createEmptyChunk(int len, boolean alloc) { + ChunkRaw c = new ChunkRaw(len, ChunkHelper.toBytesLatin1(id), alloc); + return c; + } + + /** + * In which "chunkGroup" (see {@link ChunksList}for definition) this chunks + * instance was read or written. + *

      + * -1 if not read or written (eg, queued) + */ + final public int getChunkGroup() { + return chunkGroup; + } + + /** + * @see #getChunkGroup() + */ + final void setChunkGroup(int chunkGroup) { + this.chunkGroup = chunkGroup; + } + + public boolean hasPriority() { + return priority; + } + + public void setPriority(boolean priority) { + this.priority = priority; + } + + final void write(OutputStream os) { + if (raw == null || raw.data == null) { + raw = createRawChunk(); + } + if (raw == null) { + throw new PngjExceptionInternal("null chunk ! creation failed for " + this); + } + raw.writeChunk(os); + } + + /** + * Creates the physical chunk. This is used when writing (serialization). + * Each particular chunk class implements its own logic. + * + * @return A newly allocated and filled raw chunk + */ + public abstract ChunkRaw createRawChunk(); + + /** + * Parses raw chunk and fill inside data. This is used when reading + * (deserialization). Each particular chunk class implements its own logic. + */ + protected abstract void parseFromRaw(ChunkRaw c); + + /** + * See {@link PngChunkMultiple} and {@link PngChunkSingle} + * + * @return true if PNG accepts multiple chunks of this class + */ + protected abstract boolean allowsMultiple(); + + public ChunkRaw getRaw() { + return raw; + } + + void setRaw(ChunkRaw raw) { + this.raw = raw; + } + + /** + * @see ChunkRaw#len + */ + public int getLen() { + return raw != null ? raw.len : -1; + } + + /** + * @see ChunkRaw#getOffset() + */ + public long getOffset() { + return raw != null ? raw.getOffset() : -1; + } + + /** + * This signals that the raw chunk (serialized data) as invalid, so that + * it's regenerated on write. This should be called for the (infrequent) + * case of chunks that were copied from a PngReader and we want to manually + * modify it. + */ + public void invalidateRawData() { + raw = null; + } + + /** + * see {@link ChunkOrderingConstraint} + */ + public abstract ChunkOrderingConstraint getOrderingConstraint(); + + @Override + public String toString() { + return "chunk id= " + id + " (len=" + getLen() + " offset=" + getOffset() + ")"; + } + +} diff --git a/png/src/main/java/org/xbib/graphics/imageio/plugins/png/pngj/chunks/PngChunkACTL.java b/png/src/main/java/org/xbib/graphics/imageio/plugins/png/pngj/chunks/PngChunkACTL.java new file mode 100644 index 0000000..e652bb5 --- /dev/null +++ b/png/src/main/java/org/xbib/graphics/imageio/plugins/png/pngj/chunks/PngChunkACTL.java @@ -0,0 +1,57 @@ +package org.xbib.graphics.imageio.plugins.png.pngj.chunks; + +import org.xbib.graphics.imageio.plugins.png.pngj.ImageInfo; +import org.xbib.graphics.imageio.plugins.png.pngj.PngHelperInternal; + +/** + * acTL chunk. For APGN, not PGN standard + *

      + * see + * https://wiki.mozilla.org/APNG_Specification#.60acTL.60:_The_Animation_Control_Chunk + *

      + */ +public class PngChunkACTL extends PngChunkSingle { + public final static String ID = "acTL"; + private int numFrames; + private int numPlays; + + public PngChunkACTL(ImageInfo info) { + super(ID, info); + } + + @Override + public ChunkOrderingConstraint getOrderingConstraint() { + return ChunkOrderingConstraint.BEFORE_IDAT; + } + + @Override + public ChunkRaw createRawChunk() { + ChunkRaw c = createEmptyChunk(8, true); + PngHelperInternal.writeInt4tobytes(numFrames, c.data, 0); + PngHelperInternal.writeInt4tobytes(numPlays, c.data, 4); + return c; + } + + @Override + public void parseFromRaw(ChunkRaw chunk) { + numFrames = PngHelperInternal.readInt4fromBytes(chunk.data, 0); + numPlays = PngHelperInternal.readInt4fromBytes(chunk.data, 4); + } + + public int getNumFrames() { + return numFrames; + } + + public void setNumFrames(int numFrames) { + this.numFrames = numFrames; + } + + public int getNumPlays() { + return numPlays; + } + + public void setNumPlays(int numPlays) { + this.numPlays = numPlays; + } + +} diff --git a/png/src/main/java/org/xbib/graphics/imageio/plugins/png/pngj/chunks/PngChunkBKGD.java b/png/src/main/java/org/xbib/graphics/imageio/plugins/png/pngj/chunks/PngChunkBKGD.java new file mode 100644 index 0000000..14908c6 --- /dev/null +++ b/png/src/main/java/org/xbib/graphics/imageio/plugins/png/pngj/chunks/PngChunkBKGD.java @@ -0,0 +1,116 @@ +package org.xbib.graphics.imageio.plugins.png.pngj.chunks; + +import org.xbib.graphics.imageio.plugins.png.pngj.ImageInfo; +import org.xbib.graphics.imageio.plugins.png.pngj.PngHelperInternal; +import org.xbib.graphics.imageio.plugins.png.pngj.PngjException; + +/** + * bKGD Chunk. + *

      + * see {@link http://www.w3.org/TR/PNG/#11bKGD} + *

      + * This chunk structure depends on the image type + */ +public class PngChunkBKGD extends PngChunkSingle { + public final static String ID = ChunkHelper.bKGD; + // only one of these is meaningful + private int gray; + private int red, green, blue; + private int paletteIndex; + + public PngChunkBKGD(ImageInfo info) { + super(ChunkHelper.bKGD, info); + } + + @Override + public ChunkOrderingConstraint getOrderingConstraint() { + return ChunkOrderingConstraint.AFTER_PLTE_BEFORE_IDAT; + } + + @Override + public ChunkRaw createRawChunk() { + ChunkRaw c = null; + if (imgInfo.greyscale) { + c = createEmptyChunk(2, true); + PngHelperInternal.writeInt2tobytes(gray, c.data, 0); + } else if (imgInfo.indexed) { + c = createEmptyChunk(1, true); + c.data[0] = (byte) paletteIndex; + } else { + c = createEmptyChunk(6, true); + PngHelperInternal.writeInt2tobytes(red, c.data, 0); + PngHelperInternal.writeInt2tobytes(green, c.data, 0); + PngHelperInternal.writeInt2tobytes(blue, c.data, 0); + } + return c; + } + + @Override + public void parseFromRaw(ChunkRaw c) { + if (imgInfo.greyscale) { + gray = PngHelperInternal.readInt2fromBytes(c.data, 0); + } else if (imgInfo.indexed) { + paletteIndex = c.data[0] & 0xff; + } else { + red = PngHelperInternal.readInt2fromBytes(c.data, 0); + green = PngHelperInternal.readInt2fromBytes(c.data, 2); + blue = PngHelperInternal.readInt2fromBytes(c.data, 4); + } + } + + /** + * Set gray value (0-255 if bitdept=8) + * + * @param gray + */ + public void setGray(int gray) { + if (!imgInfo.greyscale) { + throw new PngjException("only gray images support this"); + } + this.gray = gray; + } + + public int getGray() { + if (!imgInfo.greyscale) { + throw new PngjException("only gray images support this"); + } + return gray; + } + + /** + * Set pallette index + */ + public void setPaletteIndex(int i) { + if (!imgInfo.indexed) { + throw new PngjException("only indexed (pallete) images support this"); + } + this.paletteIndex = i; + } + + public int getPaletteIndex() { + if (!imgInfo.indexed) { + throw new PngjException("only indexed (pallete) images support this"); + } + return paletteIndex; + } + + /** + * Set rgb values + */ + public void setRGB(int r, int g, int b) { + if (imgInfo.greyscale || imgInfo.indexed) { + throw new PngjException("only rgb or rgba images support this"); + } + red = r; + green = g; + blue = b; + } + + public int[] getRGB() { + if (imgInfo.greyscale || imgInfo.indexed) { + throw new PngjException("only rgb or rgba images support this"); + } + return new int[]{red, green, blue}; + } + +} diff --git a/png/src/main/java/org/xbib/graphics/imageio/plugins/png/pngj/chunks/PngChunkCHRM.java b/png/src/main/java/org/xbib/graphics/imageio/plugins/png/pngj/chunks/PngChunkCHRM.java new file mode 100644 index 0000000..079f294 --- /dev/null +++ b/png/src/main/java/org/xbib/graphics/imageio/plugins/png/pngj/chunks/PngChunkCHRM.java @@ -0,0 +1,76 @@ +package org.xbib.graphics.imageio.plugins.png.pngj.chunks; + +import org.xbib.graphics.imageio.plugins.png.pngj.ImageInfo; +import org.xbib.graphics.imageio.plugins.png.pngj.PngHelperInternal; +import org.xbib.graphics.imageio.plugins.png.pngj.PngjException; + +/** + * cHRM chunk. + *

      + * see http://www.w3.org/TR/PNG/#11cHRM + */ +public class PngChunkCHRM extends PngChunkSingle { + public final static String ID = ChunkHelper.cHRM; + + // http://www.w3.org/TR/PNG/#11cHRM + private double whitex, whitey; + private double redx, redy; + private double greenx, greeny; + private double bluex, bluey; + + public PngChunkCHRM(ImageInfo info) { + super(ID, info); + } + + @Override + public ChunkOrderingConstraint getOrderingConstraint() { + return ChunkOrderingConstraint.AFTER_PLTE_BEFORE_IDAT; + } + + @Override + public ChunkRaw createRawChunk() { + ChunkRaw c = null; + c = createEmptyChunk(32, true); + PngHelperInternal.writeInt4tobytes(PngHelperInternal.doubleToInt100000(whitex), c.data, 0); + PngHelperInternal.writeInt4tobytes(PngHelperInternal.doubleToInt100000(whitey), c.data, 4); + PngHelperInternal.writeInt4tobytes(PngHelperInternal.doubleToInt100000(redx), c.data, 8); + PngHelperInternal.writeInt4tobytes(PngHelperInternal.doubleToInt100000(redy), c.data, 12); + PngHelperInternal.writeInt4tobytes(PngHelperInternal.doubleToInt100000(greenx), c.data, 16); + PngHelperInternal.writeInt4tobytes(PngHelperInternal.doubleToInt100000(greeny), c.data, 20); + PngHelperInternal.writeInt4tobytes(PngHelperInternal.doubleToInt100000(bluex), c.data, 24); + PngHelperInternal.writeInt4tobytes(PngHelperInternal.doubleToInt100000(bluey), c.data, 28); + return c; + } + + @Override + public void parseFromRaw(ChunkRaw c) { + if (c.len != 32) { + throw new PngjException("bad chunk " + c); + } + whitex = PngHelperInternal.intToDouble100000(PngHelperInternal.readInt4fromBytes(c.data, 0)); + whitey = PngHelperInternal.intToDouble100000(PngHelperInternal.readInt4fromBytes(c.data, 4)); + redx = PngHelperInternal.intToDouble100000(PngHelperInternal.readInt4fromBytes(c.data, 8)); + redy = PngHelperInternal.intToDouble100000(PngHelperInternal.readInt4fromBytes(c.data, 12)); + greenx = PngHelperInternal.intToDouble100000(PngHelperInternal.readInt4fromBytes(c.data, 16)); + greeny = PngHelperInternal.intToDouble100000(PngHelperInternal.readInt4fromBytes(c.data, 20)); + bluex = PngHelperInternal.intToDouble100000(PngHelperInternal.readInt4fromBytes(c.data, 24)); + bluey = PngHelperInternal.intToDouble100000(PngHelperInternal.readInt4fromBytes(c.data, 28)); + } + + public void setChromaticities(double whitex, double whitey, double redx, double redy, double greenx, double greeny, + double bluex, double bluey) { + this.whitex = whitex; + this.redx = redx; + this.greenx = greenx; + this.bluex = bluex; + this.whitey = whitey; + this.redy = redy; + this.greeny = greeny; + this.bluey = bluey; + } + + public double[] getChromaticities() { + return new double[]{whitex, whitey, redx, redy, greenx, greeny, bluex, bluey}; + } + +} diff --git a/png/src/main/java/org/xbib/graphics/imageio/plugins/png/pngj/chunks/PngChunkFCTL.java b/png/src/main/java/org/xbib/graphics/imageio/plugins/png/pngj/chunks/PngChunkFCTL.java new file mode 100644 index 0000000..5bb56c0 --- /dev/null +++ b/png/src/main/java/org/xbib/graphics/imageio/plugins/png/pngj/chunks/PngChunkFCTL.java @@ -0,0 +1,158 @@ +package org.xbib.graphics.imageio.plugins.png.pngj.chunks; + +import org.xbib.graphics.imageio.plugins.png.pngj.ImageInfo; +import org.xbib.graphics.imageio.plugins.png.pngj.PngHelperInternal; + +/** + * fcTL chunk. For APGN, not PGN standard + *

      + * see + * https://wiki.mozilla.org/APNG_Specification#.60fcTL.60:_The_Frame_Control_Chunk + *

      + */ +public class PngChunkFCTL extends PngChunkMultiple { + public final static String ID = "fcTL"; + + public final static byte APNG_DISPOSE_OP_NONE = 0; + public final static byte APNG_DISPOSE_OP_BACKGROUND = 1; + public final static byte APNG_DISPOSE_OP_PREVIOUS = 2; + public final static byte APNG_BLEND_OP_SOURCE = 0; + public final static byte APNG_BLEND_OP_OVER = 1; + + private int seqNum; + private int width, height, xOff, yOff; + private int delayNum, delayDen; + private byte disposeOp, blendOp; + + public PngChunkFCTL(ImageInfo info) { + super(ID, info); + } + + public ImageInfo getEquivImageInfo() { + return new ImageInfo(width, height, imgInfo.bitDepth, imgInfo.alpha, imgInfo.greyscale, imgInfo.indexed); + } + + @Override + public ChunkOrderingConstraint getOrderingConstraint() { + return ChunkOrderingConstraint.NONE; + } + + @Override + public ChunkRaw createRawChunk() { + ChunkRaw c = createEmptyChunk(8, true); + int off = 0; + PngHelperInternal.writeInt4tobytes(seqNum, c.data, off); + off += 4; + PngHelperInternal.writeInt4tobytes(width, c.data, off); + off += 4; + PngHelperInternal.writeInt4tobytes(height, c.data, off); + off += 4; + PngHelperInternal.writeInt4tobytes(xOff, c.data, off); + off += 4; + PngHelperInternal.writeInt4tobytes(yOff, c.data, off); + off += 4; + PngHelperInternal.writeInt2tobytes(delayNum, c.data, off); + off += 2; + PngHelperInternal.writeInt2tobytes(delayDen, c.data, off); + off += 2; + c.data[off] = disposeOp; + off += 1; + c.data[off] = blendOp; + return c; + } + + @Override + public void parseFromRaw(ChunkRaw chunk) { + int off = 0; + seqNum = PngHelperInternal.readInt4fromBytes(chunk.data, off); + off += 4; + width = PngHelperInternal.readInt4fromBytes(chunk.data, off); + off += 4; + height = PngHelperInternal.readInt4fromBytes(chunk.data, off); + off += 4; + xOff = PngHelperInternal.readInt4fromBytes(chunk.data, off); + off += 4; + yOff = PngHelperInternal.readInt4fromBytes(chunk.data, off); + off += 4; + delayNum = PngHelperInternal.readInt2fromBytes(chunk.data, off); + off += 2; + delayDen = PngHelperInternal.readInt2fromBytes(chunk.data, off); + off += 2; + disposeOp = chunk.data[off]; + off += 1; + blendOp = chunk.data[off]; + } + + public int getSeqNum() { + return seqNum; + } + + public void setSeqNum(int seqNum) { + this.seqNum = seqNum; + } + + public int getWidth() { + return width; + } + + public void setWidth(int width) { + this.width = width; + } + + public int getHeight() { + return height; + } + + public void setHeight(int height) { + this.height = height; + } + + public int getxOff() { + return xOff; + } + + public void setxOff(int xOff) { + this.xOff = xOff; + } + + public int getyOff() { + return yOff; + } + + public void setyOff(int yOff) { + this.yOff = yOff; + } + + public int getDelayNum() { + return delayNum; + } + + public void setDelayNum(int delayNum) { + this.delayNum = delayNum; + } + + public int getDelayDen() { + return delayDen; + } + + public void setDelayDen(int delayDen) { + this.delayDen = delayDen; + } + + public byte getDisposeOp() { + return disposeOp; + } + + public void setDisposeOp(byte disposeOp) { + this.disposeOp = disposeOp; + } + + public byte getBlendOp() { + return blendOp; + } + + public void setBlendOp(byte blendOp) { + this.blendOp = blendOp; + } + +} diff --git a/png/src/main/java/org/xbib/graphics/imageio/plugins/png/pngj/chunks/PngChunkFDAT.java b/png/src/main/java/org/xbib/graphics/imageio/plugins/png/pngj/chunks/PngChunkFDAT.java new file mode 100644 index 0000000..b396563 --- /dev/null +++ b/png/src/main/java/org/xbib/graphics/imageio/plugins/png/pngj/chunks/PngChunkFDAT.java @@ -0,0 +1,72 @@ +package org.xbib.graphics.imageio.plugins.png.pngj.chunks; + +import org.xbib.graphics.imageio.plugins.png.pngj.ImageInfo; +import org.xbib.graphics.imageio.plugins.png.pngj.PngHelperInternal; +import org.xbib.graphics.imageio.plugins.png.pngj.PngjException; + +/** + * fdAT chunk. For APGN, not PGN standard + *

      + * see + * https://wiki.mozilla.org/APNG_Specification#.60fdAT.60:_The_Frame_Data_Chunk + *

      + * This implementation does not support buffering, this should be not managed + * similar to a IDAT chunk + */ +public class PngChunkFDAT extends PngChunkMultiple { + public final static String ID = "fdAT"; + private int seqNum; + private byte[] buffer; // normally not allocated - if so, it's the raw data, includes the 4bytes seqNum + int datalen; // length of idat data, excluding seqNUm (= chunk.len-4) + + public PngChunkFDAT(ImageInfo info) { + super(ID, info); + } + + @Override + public ChunkOrderingConstraint getOrderingConstraint() { + return ChunkOrderingConstraint.AFTER_IDAT; + } + + @Override + public ChunkRaw createRawChunk() { + if (buffer == null) { + throw new PngjException("not buffered"); + } + ChunkRaw c = createEmptyChunk(datalen + 4, false); + c.data = buffer; // shallow copy! + return c; + } + + @Override + public void parseFromRaw(ChunkRaw chunk) { + seqNum = PngHelperInternal.readInt4fromBytes(chunk.data, 0); + datalen = chunk.len - 4; + buffer = chunk.data; + } + + public int getSeqNum() { + return seqNum; + } + + public void setSeqNum(int seqNum) { + this.seqNum = seqNum; + } + + public byte[] getBuffer() { + return buffer; + } + + public void setBuffer(byte[] buffer) { + this.buffer = buffer; + } + + public int getDatalen() { + return datalen; + } + + public void setDatalen(int datalen) { + this.datalen = datalen; + } + +} diff --git a/png/src/main/java/org/xbib/graphics/imageio/plugins/png/pngj/chunks/PngChunkGAMA.java b/png/src/main/java/org/xbib/graphics/imageio/plugins/png/pngj/chunks/PngChunkGAMA.java new file mode 100644 index 0000000..1402ac0 --- /dev/null +++ b/png/src/main/java/org/xbib/graphics/imageio/plugins/png/pngj/chunks/PngChunkGAMA.java @@ -0,0 +1,52 @@ +package org.xbib.graphics.imageio.plugins.png.pngj.chunks; + +import org.xbib.graphics.imageio.plugins.png.pngj.ImageInfo; +import org.xbib.graphics.imageio.plugins.png.pngj.PngHelperInternal; +import org.xbib.graphics.imageio.plugins.png.pngj.PngjException; + +/** + * gAMA chunk. + *

      + * see http://www.w3.org/TR/PNG/#11gAMA + */ +public class PngChunkGAMA extends PngChunkSingle { + public final static String ID = ChunkHelper.gAMA; + + // http://www.w3.org/TR/PNG/#11gAMA + private double gamma; + + public PngChunkGAMA(ImageInfo info) { + super(ID, info); + } + + @Override + public ChunkOrderingConstraint getOrderingConstraint() { + return ChunkOrderingConstraint.BEFORE_PLTE_AND_IDAT; + } + + @Override + public ChunkRaw createRawChunk() { + ChunkRaw c = createEmptyChunk(4, true); + int g = (int) (gamma * 100000 + 0.5); + PngHelperInternal.writeInt4tobytes(g, c.data, 0); + return c; + } + + @Override + public void parseFromRaw(ChunkRaw chunk) { + if (chunk.len != 4) { + throw new PngjException("bad chunk " + chunk); + } + int g = PngHelperInternal.readInt4fromBytes(chunk.data, 0); + gamma = ((double) g) / 100000.0; + } + + public double getGamma() { + return gamma; + } + + public void setGamma(double gamma) { + this.gamma = gamma; + } + +} diff --git a/png/src/main/java/org/xbib/graphics/imageio/plugins/png/pngj/chunks/PngChunkHIST.java b/png/src/main/java/org/xbib/graphics/imageio/plugins/png/pngj/chunks/PngChunkHIST.java new file mode 100644 index 0000000..44ab3a7 --- /dev/null +++ b/png/src/main/java/org/xbib/graphics/imageio/plugins/png/pngj/chunks/PngChunkHIST.java @@ -0,0 +1,60 @@ +package org.xbib.graphics.imageio.plugins.png.pngj.chunks; + +import org.xbib.graphics.imageio.plugins.png.pngj.ImageInfo; +import org.xbib.graphics.imageio.plugins.png.pngj.PngHelperInternal; +import org.xbib.graphics.imageio.plugins.png.pngj.PngjException; + +/** + * hIST chunk. + *

      + * see http://www.w3.org/TR/PNG/#11hIST
      + * only for palette images + */ +public class PngChunkHIST extends PngChunkSingle { + public final static String ID = ChunkHelper.hIST; + + private int[] hist = new int[0]; // should have same lenght as palette + + public PngChunkHIST(ImageInfo info) { + super(ID, info); + } + + @Override + public ChunkOrderingConstraint getOrderingConstraint() { + return ChunkOrderingConstraint.AFTER_PLTE_BEFORE_IDAT; + } + + @Override + public void parseFromRaw(ChunkRaw c) { + if (!imgInfo.indexed) { + throw new PngjException("only indexed images accept a HIST chunk"); + } + int nentries = c.data.length / 2; + hist = new int[nentries]; + for (int i = 0; i < hist.length; i++) { + hist[i] = PngHelperInternal.readInt2fromBytes(c.data, i * 2); + } + } + + @Override + public ChunkRaw createRawChunk() { + if (!imgInfo.indexed) { + throw new PngjException("only indexed images accept a HIST chunk"); + } + ChunkRaw c = null; + c = createEmptyChunk(hist.length * 2, true); + for (int i = 0; i < hist.length; i++) { + PngHelperInternal.writeInt2tobytes(hist[i], c.data, i * 2); + } + return c; + } + + public int[] getHist() { + return hist; + } + + public void setHist(int[] hist) { + this.hist = hist; + } + +} diff --git a/png/src/main/java/org/xbib/graphics/imageio/plugins/png/pngj/chunks/PngChunkICCP.java b/png/src/main/java/org/xbib/graphics/imageio/plugins/png/pngj/chunks/PngChunkICCP.java new file mode 100644 index 0000000..24653b0 --- /dev/null +++ b/png/src/main/java/org/xbib/graphics/imageio/plugins/png/pngj/chunks/PngChunkICCP.java @@ -0,0 +1,77 @@ +package org.xbib.graphics.imageio.plugins.png.pngj.chunks; + +import org.xbib.graphics.imageio.plugins.png.pngj.ImageInfo; +import org.xbib.graphics.imageio.plugins.png.pngj.PngjException; + +/** + * iCCP chunk. + *

      + * See {@link http://www.w3.org/TR/PNG/#11iCCP} + */ +public class PngChunkICCP extends PngChunkSingle { + public final static String ID = ChunkHelper.iCCP; + + // http://www.w3.org/TR/PNG/#11iCCP + private String profileName; + private byte[] compressedProfile; // copmression/decopmresion is done in getter/setter + + public PngChunkICCP(ImageInfo info) { + super(ID, info); + } + + @Override + public ChunkOrderingConstraint getOrderingConstraint() { + return ChunkOrderingConstraint.BEFORE_PLTE_AND_IDAT; + } + + @Override + public ChunkRaw createRawChunk() { + ChunkRaw c = createEmptyChunk(profileName.length() + compressedProfile.length + 2, true); + System.arraycopy(ChunkHelper.toBytesLatin1(profileName), 0, c.data, 0, profileName.length()); + c.data[profileName.length()] = 0; + c.data[profileName.length() + 1] = 0; + System.arraycopy(compressedProfile, 0, c.data, profileName.length() + 2, compressedProfile.length); + return c; + } + + @Override + public void parseFromRaw(ChunkRaw chunk) { + int pos0 = ChunkHelper.posNullByte(chunk.data); + profileName = ChunkHelper.toStringLatin1(chunk.data, 0, pos0); + int comp = (chunk.data[pos0 + 1] & 0xff); + if (comp != 0) { + throw new PngjException("bad compression for ChunkTypeICCP"); + } + int compdatasize = chunk.data.length - (pos0 + 2); + compressedProfile = new byte[compdatasize]; + System.arraycopy(chunk.data, pos0 + 2, compressedProfile, 0, compdatasize); + } + + /** + * The profile should be uncompressed bytes + */ + public void setProfileNameAndContent(String name, byte[] profile) { + profileName = name; + compressedProfile = ChunkHelper.compressBytes(profile, true); + } + + public void setProfileNameAndContent(String name, String profile) { + setProfileNameAndContent(name, ChunkHelper.toBytesLatin1(profile)); + } + + public String getProfileName() { + return profileName; + } + + /** + * uncompressed + **/ + public byte[] getProfile() { + return ChunkHelper.compressBytes(compressedProfile, false); + } + + public String getProfileAsString() { + return ChunkHelper.toStringLatin1(getProfile()); + } + +} diff --git a/png/src/main/java/org/xbib/graphics/imageio/plugins/png/pngj/chunks/PngChunkIDAT.java b/png/src/main/java/org/xbib/graphics/imageio/plugins/png/pngj/chunks/PngChunkIDAT.java new file mode 100644 index 0000000..b471b82 --- /dev/null +++ b/png/src/main/java/org/xbib/graphics/imageio/plugins/png/pngj/chunks/PngChunkIDAT.java @@ -0,0 +1,35 @@ +package org.xbib.graphics.imageio.plugins.png.pngj.chunks; + +import org.xbib.graphics.imageio.plugins.png.pngj.ImageInfo; + +/** + * IDAT chunk. + *

      + * see http://www.w3.org/TR/PNG/#11IDAT + *

      + * This is dummy placeholder - we write/read this chunk (actually several) by + * special code. + */ +public class PngChunkIDAT extends PngChunkMultiple { + public final static String ID = ChunkHelper.IDAT; + + // http://www.w3.org/TR/PNG/#11IDAT + public PngChunkIDAT(ImageInfo i) { + super(ID, i); + } + + @Override + public ChunkOrderingConstraint getOrderingConstraint() { + return ChunkOrderingConstraint.NA; + } + + @Override + public ChunkRaw createRawChunk() {// does nothing + return null; + } + + @Override + public void parseFromRaw(ChunkRaw c) { // does nothing + } + +} diff --git a/png/src/main/java/org/xbib/graphics/imageio/plugins/png/pngj/chunks/PngChunkIEND.java b/png/src/main/java/org/xbib/graphics/imageio/plugins/png/pngj/chunks/PngChunkIEND.java new file mode 100644 index 0000000..ed4655e --- /dev/null +++ b/png/src/main/java/org/xbib/graphics/imageio/plugins/png/pngj/chunks/PngChunkIEND.java @@ -0,0 +1,35 @@ +package org.xbib.graphics.imageio.plugins.png.pngj.chunks; + +import org.xbib.graphics.imageio.plugins.png.pngj.ImageInfo; + +/** + * IEND chunk. + *

      + * see http://www.w3.org/TR/PNG/#11IEND + */ +public class PngChunkIEND extends PngChunkSingle { + public final static String ID = ChunkHelper.IEND; + + // http://www.w3.org/TR/PNG/#11IEND + // this is a dummy placeholder + public PngChunkIEND(ImageInfo info) { + super(ID, info); + } + + @Override + public ChunkOrderingConstraint getOrderingConstraint() { + return ChunkOrderingConstraint.NA; + } + + @Override + public ChunkRaw createRawChunk() { + ChunkRaw c = new ChunkRaw(0, ChunkHelper.b_IEND, false); + return c; + } + + @Override + public void parseFromRaw(ChunkRaw c) { + // this is not used + } + +} diff --git a/png/src/main/java/org/xbib/graphics/imageio/plugins/png/pngj/chunks/PngChunkIHDR.java b/png/src/main/java/org/xbib/graphics/imageio/plugins/png/pngj/chunks/PngChunkIHDR.java new file mode 100644 index 0000000..e5cbbec --- /dev/null +++ b/png/src/main/java/org/xbib/graphics/imageio/plugins/png/pngj/chunks/PngChunkIHDR.java @@ -0,0 +1,195 @@ +package org.xbib.graphics.imageio.plugins.png.pngj.chunks; + +import java.io.ByteArrayInputStream; +import org.xbib.graphics.imageio.plugins.png.pngj.ImageInfo; +import org.xbib.graphics.imageio.plugins.png.pngj.PngHelperInternal; +import org.xbib.graphics.imageio.plugins.png.pngj.PngjException; +import org.xbib.graphics.imageio.plugins.png.pngj.PngjInputException; + +/** + * IHDR chunk. + *

      + * see http://www.w3.org/TR/PNG/#11IHDR + *

      + * This is a special critical Chunk. + */ +public class PngChunkIHDR extends PngChunkSingle { + public final static String ID = ChunkHelper.IHDR; + + private int cols; + private int rows; + private int bitspc; + private int colormodel; + private int compmeth; + private int filmeth; + private int interlaced; + + // http://www.w3.org/TR/PNG/#11IHDR + // + public PngChunkIHDR(ImageInfo info) { // argument is normally null here, if not null is used to fill the fields + super(ID, info); + if (info != null) { + fillFromInfo(info); + } + } + + @Override + public ChunkOrderingConstraint getOrderingConstraint() { + return ChunkOrderingConstraint.NA; + } + + @Override + public ChunkRaw createRawChunk() { + ChunkRaw c = new ChunkRaw(13, ChunkHelper.b_IHDR, true); + int offset = 0; + PngHelperInternal.writeInt4tobytes(cols, c.data, offset); + offset += 4; + PngHelperInternal.writeInt4tobytes(rows, c.data, offset); + offset += 4; + c.data[offset++] = (byte) bitspc; + c.data[offset++] = (byte) colormodel; + c.data[offset++] = (byte) compmeth; + c.data[offset++] = (byte) filmeth; + c.data[offset++] = (byte) interlaced; + return c; + } + + @Override + public void parseFromRaw(ChunkRaw c) { + if (c.len != 13) { + throw new PngjException("Bad IDHR len " + c.len); + } + ByteArrayInputStream st = c.getAsByteStream(); + cols = PngHelperInternal.readInt4(st); + rows = PngHelperInternal.readInt4(st); + // bit depth: number of bits per channel + bitspc = PngHelperInternal.readByte(st); + colormodel = PngHelperInternal.readByte(st); + compmeth = PngHelperInternal.readByte(st); + filmeth = PngHelperInternal.readByte(st); + interlaced = PngHelperInternal.readByte(st); + } + + public int getCols() { + return cols; + } + + public void setCols(int cols) { + this.cols = cols; + } + + public int getRows() { + return rows; + } + + public void setRows(int rows) { + this.rows = rows; + } + + public int getBitspc() { + return bitspc; + } + + public void setBitspc(int bitspc) { + this.bitspc = bitspc; + } + + public int getColormodel() { + return colormodel; + } + + public void setColormodel(int colormodel) { + this.colormodel = colormodel; + } + + public int getCompmeth() { + return compmeth; + } + + public void setCompmeth(int compmeth) { + this.compmeth = compmeth; + } + + public int getFilmeth() { + return filmeth; + } + + public void setFilmeth(int filmeth) { + this.filmeth = filmeth; + } + + public int getInterlaced() { + return interlaced; + } + + public void setInterlaced(int interlaced) { + this.interlaced = interlaced; + } + + public boolean isInterlaced() { + return getInterlaced() == 1; + } + + public void fillFromInfo(ImageInfo info) { + setCols(imgInfo.cols); + setRows(imgInfo.rows); + setBitspc(imgInfo.bitDepth); + int colormodel = 0; + if (imgInfo.alpha) { + colormodel += 0x04; + } + if (imgInfo.indexed) { + colormodel += 0x01; + } + if (!imgInfo.greyscale) { + colormodel += 0x02; + } + setColormodel(colormodel); + setCompmeth(0); // compression method 0=deflate + setFilmeth(0); // filter method (0) + setInterlaced(0); // we never interlace + } + + /** + * throws PngInputException if unexpected values + */ + public ImageInfo createImageInfo() { + check(); + boolean alpha = (getColormodel() & 0x04) != 0; + boolean palette = (getColormodel() & 0x01) != 0; + boolean grayscale = (getColormodel() == 0 || getColormodel() == 4); + // creates ImgInfo and imgLine, and allocates buffers + return new ImageInfo(getCols(), getRows(), getBitspc(), alpha, grayscale, palette); + } + + public void check() { + if (cols < 1 || rows < 1 || compmeth != 0 || filmeth != 0) { + throw new PngjInputException("bad IHDR: col/row/compmethod/filmethod invalid"); + } + if (bitspc != 1 && bitspc != 2 && bitspc != 4 && bitspc != 8 && bitspc != 16) { + throw new PngjInputException("bad IHDR: bitdepth invalid"); + } + if (interlaced < 0 || interlaced > 1) { + throw new PngjInputException("bad IHDR: interlace invalid"); + } + switch (colormodel) { + case 0: + break; + case 3: + if (bitspc == 16) { + throw new PngjInputException("bad IHDR: bitdepth invalid"); + } + break; + case 2: + case 4: + case 6: + if (bitspc != 8 && bitspc != 16) { + throw new PngjInputException("bad IHDR: bitdepth invalid"); + } + break; + default: + throw new PngjInputException("bad IHDR: invalid colormodel"); + } + } + +} diff --git a/png/src/main/java/org/xbib/graphics/imageio/plugins/png/pngj/chunks/PngChunkITXT.java b/png/src/main/java/org/xbib/graphics/imageio/plugins/png/pngj/chunks/PngChunkITXT.java new file mode 100644 index 0000000..fc48256 --- /dev/null +++ b/png/src/main/java/org/xbib/graphics/imageio/plugins/png/pngj/chunks/PngChunkITXT.java @@ -0,0 +1,115 @@ +package org.xbib.graphics.imageio.plugins.png.pngj.chunks; + +import java.io.ByteArrayOutputStream; +import java.io.IOException; +import org.xbib.graphics.imageio.plugins.png.pngj.ImageInfo; +import org.xbib.graphics.imageio.plugins.png.pngj.PngjException; + +/** + * iTXt chunk. + *

      + * see http://www.w3.org/TR/PNG/#11iTXt + */ +public class PngChunkITXT extends PngChunkTextVar { + public final static String ID = ChunkHelper.iTXt; + + private boolean compressed = false; + private String langTag = ""; + private String translatedTag = ""; + + // http://www.w3.org/TR/PNG/#11iTXt + public PngChunkITXT(ImageInfo info) { + super(ID, info); + } + + @Override + public ChunkRaw createRawChunk() { + if (key == null || key.trim().length() == 0) { + throw new PngjException("Text chunk key must be non empty"); + } + try { + ByteArrayOutputStream ba = new ByteArrayOutputStream(); + ba.write(ChunkHelper.toBytesLatin1(key)); + ba.write(0); // separator + ba.write(compressed ? 1 : 0); + ba.write(0); // compression method (always 0) + ba.write(ChunkHelper.toBytesLatin1(langTag)); + ba.write(0); // separator + ba.write(ChunkHelper.toBytesUTF8(translatedTag)); + ba.write(0); // separator + byte[] textbytes = ChunkHelper.toBytesUTF8(val); + if (compressed) { + textbytes = ChunkHelper.compressBytes(textbytes, true); + } + ba.write(textbytes); + byte[] b = ba.toByteArray(); + ChunkRaw chunk = createEmptyChunk(b.length, false); + chunk.data = b; + return chunk; + } catch (IOException e) { + throw new PngjException(e); + } + } + + @Override + public void parseFromRaw(ChunkRaw c) { + int nullsFound = 0; + int[] nullsIdx = new int[3]; + for (int i = 0; i < c.data.length; i++) { + if (c.data[i] != 0) { + continue; + } + nullsIdx[nullsFound] = i; + nullsFound++; + if (nullsFound == 1) { + i += 2; + } + if (nullsFound == 3) { + break; + } + } + if (nullsFound != 3) { + throw new PngjException("Bad formed PngChunkITXT chunk"); + } + key = ChunkHelper.toStringLatin1(c.data, 0, nullsIdx[0]); + int i = nullsIdx[0] + 1; + compressed = c.data[i] != 0; + i++; + if (compressed && c.data[i] != 0) { + throw new PngjException("Bad formed PngChunkITXT chunk - bad compression method "); + } + langTag = ChunkHelper.toStringLatin1(c.data, i, nullsIdx[1] - i); + translatedTag = ChunkHelper.toStringUTF8(c.data, nullsIdx[1] + 1, nullsIdx[2] - nullsIdx[1] - 1); + i = nullsIdx[2] + 1; + if (compressed) { + byte[] bytes = ChunkHelper.compressBytes(c.data, i, c.data.length - i, false); + val = ChunkHelper.toStringUTF8(bytes); + } else { + val = ChunkHelper.toStringUTF8(c.data, i, c.data.length - i); + } + } + + public boolean isCompressed() { + return compressed; + } + + public void setCompressed(boolean compressed) { + this.compressed = compressed; + } + + public String getLangtag() { + return langTag; + } + + public void setLangtag(String langtag) { + this.langTag = langtag; + } + + public String getTranslatedTag() { + return translatedTag; + } + + public void setTranslatedTag(String translatedTag) { + this.translatedTag = translatedTag; + } +} diff --git a/png/src/main/java/org/xbib/graphics/imageio/plugins/png/pngj/chunks/PngChunkMultiple.java b/png/src/main/java/org/xbib/graphics/imageio/plugins/png/pngj/chunks/PngChunkMultiple.java new file mode 100644 index 0000000..5a797a0 --- /dev/null +++ b/png/src/main/java/org/xbib/graphics/imageio/plugins/png/pngj/chunks/PngChunkMultiple.java @@ -0,0 +1,28 @@ +package org.xbib.graphics.imageio.plugins.png.pngj.chunks; + +import org.xbib.graphics.imageio.plugins.png.pngj.ImageInfo; + +/** + * PNG chunk type (abstract) that allows multiple instances in same image. + */ +public abstract class PngChunkMultiple extends PngChunk { + + protected PngChunkMultiple(String id, ImageInfo imgInfo) { + super(id, imgInfo); + } + + @Override + public final boolean allowsMultiple() { + return true; + } + + /** + * NOTE: this chunk uses the default Object's equals() hashCode() + * implementation. + * + * This is the right thing to do, normally. + * + * This is important, eg see ChunkList.removeFromList() + */ + +} diff --git a/png/src/main/java/org/xbib/graphics/imageio/plugins/png/pngj/chunks/PngChunkOFFS.java b/png/src/main/java/org/xbib/graphics/imageio/plugins/png/pngj/chunks/PngChunkOFFS.java new file mode 100644 index 0000000..cb40299 --- /dev/null +++ b/png/src/main/java/org/xbib/graphics/imageio/plugins/png/pngj/chunks/PngChunkOFFS.java @@ -0,0 +1,84 @@ +package org.xbib.graphics.imageio.plugins.png.pngj.chunks; + +import org.xbib.graphics.imageio.plugins.png.pngj.ImageInfo; +import org.xbib.graphics.imageio.plugins.png.pngj.PngHelperInternal; +import org.xbib.graphics.imageio.plugins.png.pngj.PngjException; + +/** + * oFFs chunk. + *

      + * see http://www.libpng.org/pub/png/spec/register/pngext-1.3.0-pdg.html#C.oFFs + */ +public class PngChunkOFFS extends PngChunkSingle { + public final static String ID = "oFFs"; + + // http://www.libpng.org/pub/png/spec/register/pngext-1.3.0-pdg.html#C.oFFs + private long posX; + private long posY; + private int units; // 0: pixel 1:micrometer + + public PngChunkOFFS(ImageInfo info) { + super(ID, info); + } + + @Override + public ChunkOrderingConstraint getOrderingConstraint() { + return ChunkOrderingConstraint.BEFORE_IDAT; + } + + @Override + public ChunkRaw createRawChunk() { + ChunkRaw c = createEmptyChunk(9, true); + PngHelperInternal.writeInt4tobytes((int) posX, c.data, 0); + PngHelperInternal.writeInt4tobytes((int) posY, c.data, 4); + c.data[8] = (byte) units; + return c; + } + + @Override + public void parseFromRaw(ChunkRaw chunk) { + if (chunk.len != 9) { + throw new PngjException("bad chunk length " + chunk); + } + posX = PngHelperInternal.readInt4fromBytes(chunk.data, 0); + if (posX < 0) { + posX += 0x100000000L; + } + posY = PngHelperInternal.readInt4fromBytes(chunk.data, 4); + if (posY < 0) { + posY += 0x100000000L; + } + units = PngHelperInternal.readInt1fromByte(chunk.data, 8); + } + + /** + * 0: pixel, 1:micrometer + */ + public int getUnits() { + return units; + } + + /** + * 0: pixel, 1:micrometer + */ + public void setUnits(int units) { + this.units = units; + } + + public long getPosX() { + return posX; + } + + public void setPosX(long posX) { + this.posX = posX; + } + + public long getPosY() { + return posY; + } + + public void setPosY(long posY) { + this.posY = posY; + } + +} diff --git a/png/src/main/java/org/xbib/graphics/imageio/plugins/png/pngj/chunks/PngChunkPHYS.java b/png/src/main/java/org/xbib/graphics/imageio/plugins/png/pngj/chunks/PngChunkPHYS.java new file mode 100644 index 0000000..edd591d --- /dev/null +++ b/png/src/main/java/org/xbib/graphics/imageio/plugins/png/pngj/chunks/PngChunkPHYS.java @@ -0,0 +1,112 @@ +package org.xbib.graphics.imageio.plugins.png.pngj.chunks; + +import org.xbib.graphics.imageio.plugins.png.pngj.ImageInfo; +import org.xbib.graphics.imageio.plugins.png.pngj.PngHelperInternal; +import org.xbib.graphics.imageio.plugins.png.pngj.PngjException; + +/** + * pHYs chunk. + *

      + * see http://www.w3.org/TR/PNG/#11pHYs + */ +public class PngChunkPHYS extends PngChunkSingle { + public final static String ID = ChunkHelper.pHYs; + + // http://www.w3.org/TR/PNG/#11pHYs + private long pixelsxUnitX; + private long pixelsxUnitY; + private int units; // 0: unknown 1:metre + + public PngChunkPHYS(ImageInfo info) { + super(ID, info); + } + + @Override + public ChunkOrderingConstraint getOrderingConstraint() { + return ChunkOrderingConstraint.BEFORE_IDAT; + } + + @Override + public ChunkRaw createRawChunk() { + ChunkRaw c = createEmptyChunk(9, true); + PngHelperInternal.writeInt4tobytes((int) pixelsxUnitX, c.data, 0); + PngHelperInternal.writeInt4tobytes((int) pixelsxUnitY, c.data, 4); + c.data[8] = (byte) units; + return c; + } + + @Override + public void parseFromRaw(ChunkRaw chunk) { + if (chunk.len != 9) { + throw new PngjException("bad chunk length " + chunk); + } + pixelsxUnitX = PngHelperInternal.readInt4fromBytes(chunk.data, 0); + if (pixelsxUnitX < 0) { + pixelsxUnitX += 0x100000000L; + } + pixelsxUnitY = PngHelperInternal.readInt4fromBytes(chunk.data, 4); + if (pixelsxUnitY < 0) { + pixelsxUnitY += 0x100000000L; + } + units = PngHelperInternal.readInt1fromByte(chunk.data, 8); + } + + public long getPixelsxUnitX() { + return pixelsxUnitX; + } + + public void setPixelsxUnitX(long pixelsxUnitX) { + this.pixelsxUnitX = pixelsxUnitX; + } + + public long getPixelsxUnitY() { + return pixelsxUnitY; + } + + public void setPixelsxUnitY(long pixelsxUnitY) { + this.pixelsxUnitY = pixelsxUnitY; + } + + public int getUnits() { + return units; + } + + public void setUnits(int units) { + this.units = units; + } + + // special getters / setters + + /** + * returns -1 if the physicial unit is unknown, or X-Y are not equal + */ + public double getAsDpi() { + if (units != 1 || pixelsxUnitX != pixelsxUnitY) { + return -1; + } + return ((double) pixelsxUnitX) * 0.0254; + } + + /** + * returns -1 if the physicial unit is unknown + */ + public double[] getAsDpi2() { + if (units != 1) { + return new double[]{-1, -1}; + } + return new double[]{((double) pixelsxUnitX) * 0.0254, ((double) pixelsxUnitY) * 0.0254}; + } + + public void setAsDpi(double dpi) { + units = 1; + pixelsxUnitX = (long) (dpi / 0.0254 + 0.5); + pixelsxUnitY = pixelsxUnitX; + } + + public void setAsDpi2(double dpix, double dpiy) { + units = 1; + pixelsxUnitX = (long) (dpix / 0.0254 + 0.5); + pixelsxUnitY = (long) (dpiy / 0.0254 + 0.5); + } + +} diff --git a/png/src/main/java/org/xbib/graphics/imageio/plugins/png/pngj/chunks/PngChunkPLTE.java b/png/src/main/java/org/xbib/graphics/imageio/plugins/png/pngj/chunks/PngChunkPLTE.java new file mode 100644 index 0000000..e188b69 --- /dev/null +++ b/png/src/main/java/org/xbib/graphics/imageio/plugins/png/pngj/chunks/PngChunkPLTE.java @@ -0,0 +1,99 @@ +package org.xbib.graphics.imageio.plugins.png.pngj.chunks; + +import org.xbib.graphics.imageio.plugins.png.pngj.ImageInfo; +import org.xbib.graphics.imageio.plugins.png.pngj.PngjException; + +/** + * PLTE chunk. + *

      + * see http://www.w3.org/TR/PNG/#11PLTE + *

      + * Critical chunk + */ +public class PngChunkPLTE extends PngChunkSingle { + public final static String ID = ChunkHelper.PLTE; + + // http://www.w3.org/TR/PNG/#11PLTE + private int nentries = 0; + /** + * RGB8 packed in one integer + */ + private int[] entries; + + public PngChunkPLTE(ImageInfo info) { + super(ID, info); + } + + @Override + public ChunkOrderingConstraint getOrderingConstraint() { + return ChunkOrderingConstraint.NA; + } + + @Override + public ChunkRaw createRawChunk() { + int len = 3 * nentries; + int[] rgb = new int[3]; + ChunkRaw c = createEmptyChunk(len, true); + for (int n = 0, i = 0; n < nentries; n++) { + getEntryRgb(n, rgb); + c.data[i++] = (byte) rgb[0]; + c.data[i++] = (byte) rgb[1]; + c.data[i++] = (byte) rgb[2]; + } + return c; + } + + @Override + public void parseFromRaw(ChunkRaw chunk) { + setNentries(chunk.len / 3); + for (int n = 0, i = 0; n < nentries; n++) { + setEntry(n, chunk.data[i++] & 0xff, chunk.data[i++] & 0xff, chunk.data[i++] & 0xff); + } + } + + public void setNentries(int n) { + nentries = n; + if (nentries < 1 || nentries > 256) { + throw new PngjException("invalid pallette - nentries=" + nentries); + } + if (entries == null || entries.length != nentries) { // alloc + entries = new int[nentries]; + } + } + + public int getNentries() { + return nentries; + } + + public void setEntry(int n, int r, int g, int b) { + entries[n] = ((r << 16) | (g << 8) | b); + } + + public int getEntry(int n) { + return entries[n]; + } + + public void getEntryRgb(int n, int[] rgb) { + getEntryRgb(n, rgb, 0); + } + + public void getEntryRgb(int n, int[] rgb, int offset) { + int v = entries[n]; + rgb[offset + 0] = ((v & 0xff0000) >> 16); + rgb[offset + 1] = ((v & 0xff00) >> 8); + rgb[offset + 2] = (v & 0xff); + } + + public int minBitDepth() { + if (nentries <= 2) { + return 1; + } else if (nentries <= 4) { + return 2; + } else if (nentries <= 16) { + return 4; + } else { + return 8; + } + } + +} diff --git a/png/src/main/java/org/xbib/graphics/imageio/plugins/png/pngj/chunks/PngChunkSBIT.java b/png/src/main/java/org/xbib/graphics/imageio/plugins/png/pngj/chunks/PngChunkSBIT.java new file mode 100644 index 0000000..1ce87da --- /dev/null +++ b/png/src/main/java/org/xbib/graphics/imageio/plugins/png/pngj/chunks/PngChunkSBIT.java @@ -0,0 +1,125 @@ +package org.xbib.graphics.imageio.plugins.png.pngj.chunks; + +import org.xbib.graphics.imageio.plugins.png.pngj.ImageInfo; +import org.xbib.graphics.imageio.plugins.png.pngj.PngHelperInternal; +import org.xbib.graphics.imageio.plugins.png.pngj.PngjException; + +/** + * sBIT chunk. + *

      + * see http://www.w3.org/TR/PNG/#11sBIT + *

      + * this chunk structure depends on the image type + */ +public class PngChunkSBIT extends PngChunkSingle { + public final static String ID = ChunkHelper.sBIT; + // http://www.w3.org/TR/PNG/#11sBIT + + // significant bits + private int graysb, alphasb; + private int redsb, greensb, bluesb; + + public PngChunkSBIT(ImageInfo info) { + super(ID, info); + } + + @Override + public ChunkOrderingConstraint getOrderingConstraint() { + return ChunkOrderingConstraint.BEFORE_PLTE_AND_IDAT; + } + + private int getCLen() { + int len = imgInfo.greyscale ? 1 : 3; + if (imgInfo.alpha) { + len += 1; + } + return len; + } + + @Override + public void parseFromRaw(ChunkRaw c) { + if (c.len != getCLen()) { + throw new PngjException("bad chunk length " + c); + } + if (imgInfo.greyscale) { + graysb = PngHelperInternal.readInt1fromByte(c.data, 0); + if (imgInfo.alpha) { + alphasb = PngHelperInternal.readInt1fromByte(c.data, 1); + } + } else { + redsb = PngHelperInternal.readInt1fromByte(c.data, 0); + greensb = PngHelperInternal.readInt1fromByte(c.data, 1); + bluesb = PngHelperInternal.readInt1fromByte(c.data, 2); + if (imgInfo.alpha) { + alphasb = PngHelperInternal.readInt1fromByte(c.data, 3); + } + } + } + + @Override + public ChunkRaw createRawChunk() { + ChunkRaw c = null; + c = createEmptyChunk(getCLen(), true); + if (imgInfo.greyscale) { + c.data[0] = (byte) graysb; + if (imgInfo.alpha) { + c.data[1] = (byte) alphasb; + } + } else { + c.data[0] = (byte) redsb; + c.data[1] = (byte) greensb; + c.data[2] = (byte) bluesb; + if (imgInfo.alpha) { + c.data[3] = (byte) alphasb; + } + } + return c; + } + + public void setGraysb(int gray) { + if (!imgInfo.greyscale) { + throw new PngjException("only greyscale images support this"); + } + graysb = gray; + } + + public int getGraysb() { + if (!imgInfo.greyscale) { + throw new PngjException("only greyscale images support this"); + } + return graysb; + } + + public void setAlphasb(int a) { + if (!imgInfo.alpha) { + throw new PngjException("only images with alpha support this"); + } + alphasb = a; + } + + public int getAlphasb() { + if (!imgInfo.alpha) { + throw new PngjException("only images with alpha support this"); + } + return alphasb; + } + + /** + * Set rgb values + */ + public void setRGB(int r, int g, int b) { + if (imgInfo.greyscale || imgInfo.indexed) { + throw new PngjException("only rgb or rgba images support this"); + } + redsb = r; + greensb = g; + bluesb = b; + } + + public int[] getRGB() { + if (imgInfo.greyscale || imgInfo.indexed) { + throw new PngjException("only rgb or rgba images support this"); + } + return new int[]{redsb, greensb, bluesb}; + } +} diff --git a/png/src/main/java/org/xbib/graphics/imageio/plugins/png/pngj/chunks/PngChunkSPLT.java b/png/src/main/java/org/xbib/graphics/imageio/plugins/png/pngj/chunks/PngChunkSPLT.java new file mode 100644 index 0000000..b6496a1 --- /dev/null +++ b/png/src/main/java/org/xbib/graphics/imageio/plugins/png/pngj/chunks/PngChunkSPLT.java @@ -0,0 +1,132 @@ +package org.xbib.graphics.imageio.plugins.png.pngj.chunks; + +import java.io.ByteArrayOutputStream; +import java.io.IOException; +import org.xbib.graphics.imageio.plugins.png.pngj.ImageInfo; +import org.xbib.graphics.imageio.plugins.png.pngj.PngHelperInternal; +import org.xbib.graphics.imageio.plugins.png.pngj.PngjException; + +/** + * sPLT chunk. + *

      + * see http://www.w3.org/TR/PNG/#11sPLT + */ +public class PngChunkSPLT extends PngChunkMultiple { + public final static String ID = ChunkHelper.sPLT; + + // http://www.w3.org/TR/PNG/#11sPLT + + private String palName; + private int sampledepth; // 8/16 + private int[] palette; // 5 elements per entry + + public PngChunkSPLT(ImageInfo info) { + super(ID, info); + } + + @Override + public ChunkOrderingConstraint getOrderingConstraint() { + return ChunkOrderingConstraint.BEFORE_IDAT; + } + + @Override + public ChunkRaw createRawChunk() { + try { + ByteArrayOutputStream ba = new ByteArrayOutputStream(); + ba.write(ChunkHelper.toBytesLatin1(palName)); + ba.write(0); // separator + ba.write((byte) sampledepth); + int nentries = getNentries(); + for (int n = 0; n < nentries; n++) { + for (int i = 0; i < 4; i++) { + if (sampledepth == 8) { + PngHelperInternal.writeByte(ba, (byte) palette[n * 5 + i]); + } else { + PngHelperInternal.writeInt2(ba, palette[n * 5 + i]); + } + } + PngHelperInternal.writeInt2(ba, palette[n * 5 + 4]); + } + byte[] b = ba.toByteArray(); + ChunkRaw chunk = createEmptyChunk(b.length, false); + chunk.data = b; + return chunk; + } catch (IOException e) { + throw new PngjException(e); + } + } + + @Override + public void parseFromRaw(ChunkRaw c) { + int t = -1; + for (int i = 0; i < c.data.length; i++) { // look for first zero + if (c.data[i] == 0) { + t = i; + break; + } + } + if (t <= 0 || t > c.data.length - 2) { + throw new PngjException("bad sPLT chunk: no separator found"); + } + palName = ChunkHelper.toStringLatin1(c.data, 0, t); + sampledepth = PngHelperInternal.readInt1fromByte(c.data, t + 1); + t += 2; + int nentries = (c.data.length - t) / (sampledepth == 8 ? 6 : 10); + palette = new int[nentries * 5]; + int r, g, b, a, f, ne; + ne = 0; + for (int i = 0; i < nentries; i++) { + if (sampledepth == 8) { + r = PngHelperInternal.readInt1fromByte(c.data, t++); + g = PngHelperInternal.readInt1fromByte(c.data, t++); + b = PngHelperInternal.readInt1fromByte(c.data, t++); + a = PngHelperInternal.readInt1fromByte(c.data, t++); + } else { + r = PngHelperInternal.readInt2fromBytes(c.data, t); + t += 2; + g = PngHelperInternal.readInt2fromBytes(c.data, t); + t += 2; + b = PngHelperInternal.readInt2fromBytes(c.data, t); + t += 2; + a = PngHelperInternal.readInt2fromBytes(c.data, t); + t += 2; + } + f = PngHelperInternal.readInt2fromBytes(c.data, t); + t += 2; + palette[ne++] = r; + palette[ne++] = g; + palette[ne++] = b; + palette[ne++] = a; + palette[ne++] = f; + } + } + + public int getNentries() { + return palette.length / 5; + } + + public String getPalName() { + return palName; + } + + public void setPalName(String palName) { + this.palName = palName; + } + + public int getSampledepth() { + return sampledepth; + } + + public void setSampledepth(int sampledepth) { + this.sampledepth = sampledepth; + } + + public int[] getPalette() { + return palette; + } + + public void setPalette(int[] palette) { + this.palette = palette; + } + +} diff --git a/png/src/main/java/org/xbib/graphics/imageio/plugins/png/pngj/chunks/PngChunkSRGB.java b/png/src/main/java/org/xbib/graphics/imageio/plugins/png/pngj/chunks/PngChunkSRGB.java new file mode 100644 index 0000000..b5dadab --- /dev/null +++ b/png/src/main/java/org/xbib/graphics/imageio/plugins/png/pngj/chunks/PngChunkSRGB.java @@ -0,0 +1,56 @@ +package org.xbib.graphics.imageio.plugins.png.pngj.chunks; + +import org.xbib.graphics.imageio.plugins.png.pngj.ImageInfo; +import org.xbib.graphics.imageio.plugins.png.pngj.PngHelperInternal; +import org.xbib.graphics.imageio.plugins.png.pngj.PngjException; + +/** + * sRGB chunk. + *

      + * see http://www.w3.org/TR/PNG/#11sRGB + */ +public class PngChunkSRGB extends PngChunkSingle { + public final static String ID = ChunkHelper.sRGB; + + // http://www.w3.org/TR/PNG/#11sRGB + + public static final int RENDER_INTENT_Perceptual = 0; + public static final int RENDER_INTENT_Relative_colorimetric = 1; + public static final int RENDER_INTENT_Saturation = 2; + public static final int RENDER_INTENT_Absolute_colorimetric = 3; + + private int intent; + + public PngChunkSRGB(ImageInfo info) { + super(ID, info); + } + + @Override + public ChunkOrderingConstraint getOrderingConstraint() { + return ChunkOrderingConstraint.BEFORE_PLTE_AND_IDAT; + } + + @Override + public void parseFromRaw(ChunkRaw c) { + if (c.len != 1) { + throw new PngjException("bad chunk length " + c); + } + intent = PngHelperInternal.readInt1fromByte(c.data, 0); + } + + @Override + public ChunkRaw createRawChunk() { + ChunkRaw c = null; + c = createEmptyChunk(1, true); + c.data[0] = (byte) intent; + return c; + } + + public int getIntent() { + return intent; + } + + public void setIntent(int intent) { + this.intent = intent; + } +} diff --git a/png/src/main/java/org/xbib/graphics/imageio/plugins/png/pngj/chunks/PngChunkSTER.java b/png/src/main/java/org/xbib/graphics/imageio/plugins/png/pngj/chunks/PngChunkSTER.java new file mode 100644 index 0000000..d4ba65f --- /dev/null +++ b/png/src/main/java/org/xbib/graphics/imageio/plugins/png/pngj/chunks/PngChunkSTER.java @@ -0,0 +1,55 @@ +package org.xbib.graphics.imageio.plugins.png.pngj.chunks; + +import org.xbib.graphics.imageio.plugins.png.pngj.ImageInfo; +import org.xbib.graphics.imageio.plugins.png.pngj.PngjException; + +/** + * sTER chunk. + *

      + * see http://www.libpng.org/pub/png/spec/register/pngext-1.3.0-pdg.html#C.sTER + */ +public class PngChunkSTER extends PngChunkSingle { + public final static String ID = "sTER"; + + // http://www.libpng.org/pub/png/spec/register/pngext-1.3.0-pdg.html#C.sTER + private byte mode; // 0: cross-fuse layout 1: diverging-fuse layout + + public PngChunkSTER(ImageInfo info) { + super(ID, info); + } + + @Override + public ChunkOrderingConstraint getOrderingConstraint() { + return ChunkOrderingConstraint.BEFORE_IDAT; + } + + @Override + public ChunkRaw createRawChunk() { + ChunkRaw c = createEmptyChunk(1, true); + c.data[0] = mode; + return c; + } + + @Override + public void parseFromRaw(ChunkRaw chunk) { + if (chunk.len != 1) { + throw new PngjException("bad chunk length " + chunk); + } + mode = chunk.data[0]; + } + + /** + * 0: cross-fuse layout 1: diverging-fuse layout + */ + public byte getMode() { + return mode; + } + + /** + * 0: cross-fuse layout 1: diverging-fuse layout + */ + public void setMode(byte mode) { + this.mode = mode; + } + +} diff --git a/png/src/main/java/org/xbib/graphics/imageio/plugins/png/pngj/chunks/PngChunkSingle.java b/png/src/main/java/org/xbib/graphics/imageio/plugins/png/pngj/chunks/PngChunkSingle.java new file mode 100644 index 0000000..9e0779e --- /dev/null +++ b/png/src/main/java/org/xbib/graphics/imageio/plugins/png/pngj/chunks/PngChunkSingle.java @@ -0,0 +1,44 @@ +package org.xbib.graphics.imageio.plugins.png.pngj.chunks; + +import org.xbib.graphics.imageio.plugins.png.pngj.ImageInfo; + +/** + * PNG chunk type (abstract) that does not allow multiple instances in same + * image. + */ +public abstract class PngChunkSingle extends PngChunk { + + protected PngChunkSingle(String id, ImageInfo imgInfo) { + super(id, imgInfo); + } + + public final boolean allowsMultiple() { + return false; + } + + @Override + public int hashCode() { + final int prime = 31; + int result = 1; + result = prime * result + ((id == null) ? 0 : id.hashCode()); + return result; + } + + @Override + public boolean equals(Object obj) { + if (this == obj) { + return true; + } + if (obj == null) { + return false; + } + if (getClass() != obj.getClass()) { + return false; + } + PngChunkSingle other = (PngChunkSingle) obj; + if (id == null) { + return other.id == null; + } else return id.equals(other.id); + } + +} diff --git a/png/src/main/java/org/xbib/graphics/imageio/plugins/png/pngj/chunks/PngChunkTEXT.java b/png/src/main/java/org/xbib/graphics/imageio/plugins/png/pngj/chunks/PngChunkTEXT.java new file mode 100644 index 0000000..f2b5cf2 --- /dev/null +++ b/png/src/main/java/org/xbib/graphics/imageio/plugins/png/pngj/chunks/PngChunkTEXT.java @@ -0,0 +1,47 @@ +package org.xbib.graphics.imageio.plugins.png.pngj.chunks; + +import org.xbib.graphics.imageio.plugins.png.pngj.ImageInfo; +import org.xbib.graphics.imageio.plugins.png.pngj.PngjException; + +/** + * tEXt chunk. + *

      + * see http://www.w3.org/TR/PNG/#11tEXt + */ +public class PngChunkTEXT extends PngChunkTextVar { + public final static String ID = ChunkHelper.tEXt; + + public PngChunkTEXT(ImageInfo info) { + super(ID, info); + } + + public PngChunkTEXT(ImageInfo info, String key, String val) { + super(ID, info); + setKeyVal(key, val); + } + + @Override + public ChunkRaw createRawChunk() { + if (key == null || key.trim().length() == 0) { + throw new PngjException("Text chunk key must be non empty"); + } + byte[] b = ChunkHelper.toBytesLatin1(key + "\0" + val); + ChunkRaw chunk = createEmptyChunk(b.length, false); + chunk.data = b; + return chunk; + } + + @Override + public void parseFromRaw(ChunkRaw c) { + int i; + for (i = 0; i < c.data.length; i++) { + if (c.data[i] == 0) { + break; + } + } + key = ChunkHelper.toStringLatin1(c.data, 0, i); + i++; + val = i < c.data.length ? ChunkHelper.toStringLatin1(c.data, i, c.data.length - i) : ""; + } + +} diff --git a/png/src/main/java/org/xbib/graphics/imageio/plugins/png/pngj/chunks/PngChunkTIME.java b/png/src/main/java/org/xbib/graphics/imageio/plugins/png/pngj/chunks/PngChunkTIME.java new file mode 100644 index 0000000..c7825de --- /dev/null +++ b/png/src/main/java/org/xbib/graphics/imageio/plugins/png/pngj/chunks/PngChunkTIME.java @@ -0,0 +1,84 @@ +package org.xbib.graphics.imageio.plugins.png.pngj.chunks; + +import java.util.Calendar; +import org.xbib.graphics.imageio.plugins.png.pngj.ImageInfo; +import org.xbib.graphics.imageio.plugins.png.pngj.PngHelperInternal; +import org.xbib.graphics.imageio.plugins.png.pngj.PngjException; + +/** + * tIME chunk. + *

      + * see http://www.w3.org/TR/PNG/#11tIME + */ +public class PngChunkTIME extends PngChunkSingle { + public final static String ID = ChunkHelper.tIME; + + // http://www.w3.org/TR/PNG/#11tIME + private int year, mon, day, hour, min, sec; + + public PngChunkTIME(ImageInfo info) { + super(ID, info); + } + + @Override + public ChunkOrderingConstraint getOrderingConstraint() { + return ChunkOrderingConstraint.NONE; + } + + @Override + public ChunkRaw createRawChunk() { + ChunkRaw c = createEmptyChunk(7, true); + PngHelperInternal.writeInt2tobytes(year, c.data, 0); + c.data[2] = (byte) mon; + c.data[3] = (byte) day; + c.data[4] = (byte) hour; + c.data[5] = (byte) min; + c.data[6] = (byte) sec; + return c; + } + + @Override + public void parseFromRaw(ChunkRaw chunk) { + if (chunk.len != 7) { + throw new PngjException("bad chunk " + chunk); + } + year = PngHelperInternal.readInt2fromBytes(chunk.data, 0); + mon = PngHelperInternal.readInt1fromByte(chunk.data, 2); + day = PngHelperInternal.readInt1fromByte(chunk.data, 3); + hour = PngHelperInternal.readInt1fromByte(chunk.data, 4); + min = PngHelperInternal.readInt1fromByte(chunk.data, 5); + sec = PngHelperInternal.readInt1fromByte(chunk.data, 6); + } + + public void setNow(int secsAgo) { + Calendar d = Calendar.getInstance(); + d.setTimeInMillis(System.currentTimeMillis() - 1000 * (long) secsAgo); + year = d.get(Calendar.YEAR); + mon = d.get(Calendar.MONTH) + 1; + day = d.get(Calendar.DAY_OF_MONTH); + hour = d.get(Calendar.HOUR_OF_DAY); + min = d.get(Calendar.MINUTE); + sec = d.get(Calendar.SECOND); + } + + public void setYMDHMS(int yearx, int monx, int dayx, int hourx, int minx, int secx) { + year = yearx; + mon = monx; + day = dayx; + hour = hourx; + min = minx; + sec = secx; + } + + public int[] getYMDHMS() { + return new int[]{year, mon, day, hour, min, sec}; + } + + /** + * format YYYY/MM/DD HH:mm:SS + */ + public String getAsString() { + return String.format("%04d/%02d/%02d %02d:%02d:%02d", year, mon, day, hour, min, sec); + } + +} diff --git a/png/src/main/java/org/xbib/graphics/imageio/plugins/png/pngj/chunks/PngChunkTRNS.java b/png/src/main/java/org/xbib/graphics/imageio/plugins/png/pngj/chunks/PngChunkTRNS.java new file mode 100644 index 0000000..68fc990 --- /dev/null +++ b/png/src/main/java/org/xbib/graphics/imageio/plugins/png/pngj/chunks/PngChunkTRNS.java @@ -0,0 +1,157 @@ +package org.xbib.graphics.imageio.plugins.png.pngj.chunks; + +import org.xbib.graphics.imageio.plugins.png.pngj.ImageInfo; +import org.xbib.graphics.imageio.plugins.png.pngj.PngHelperInternal; +import org.xbib.graphics.imageio.plugins.png.pngj.PngjException; + +/** + * tRNS chunk. + *

      + * see http://www.w3.org/TR/PNG/#11tRNS + *

      + * this chunk structure depends on the image type + */ +public class PngChunkTRNS extends PngChunkSingle { + public final static String ID = ChunkHelper.tRNS; + + // http://www.w3.org/TR/PNG/#11tRNS + + // only one of these is meaningful, depending on the image type + private int gray; + private int red, green, blue; + private int[] paletteAlpha = new int[]{}; + + public PngChunkTRNS(ImageInfo info) { + super(ID, info); + } + + @Override + public ChunkOrderingConstraint getOrderingConstraint() { + return ChunkOrderingConstraint.AFTER_PLTE_BEFORE_IDAT; + } + + @Override + public ChunkRaw createRawChunk() { + ChunkRaw c = null; + if (imgInfo.greyscale) { + c = createEmptyChunk(2, true); + PngHelperInternal.writeInt2tobytes(gray, c.data, 0); + } else if (imgInfo.indexed) { + c = createEmptyChunk(paletteAlpha.length, true); + for (int n = 0; n < c.len; n++) { + c.data[n] = (byte) paletteAlpha[n]; + } + } else { + c = createEmptyChunk(6, true); + PngHelperInternal.writeInt2tobytes(red, c.data, 0); + PngHelperInternal.writeInt2tobytes(green, c.data, 0); + PngHelperInternal.writeInt2tobytes(blue, c.data, 0); + } + return c; + } + + @Override + public void parseFromRaw(ChunkRaw c) { + if (imgInfo.greyscale) { + gray = PngHelperInternal.readInt2fromBytes(c.data, 0); + } else if (imgInfo.indexed) { + int nentries = c.data.length; + paletteAlpha = new int[nentries]; + for (int n = 0; n < nentries; n++) { + paletteAlpha[n] = c.data[n] & 0xff; + } + } else { + red = PngHelperInternal.readInt2fromBytes(c.data, 0); + green = PngHelperInternal.readInt2fromBytes(c.data, 2); + blue = PngHelperInternal.readInt2fromBytes(c.data, 4); + } + } + + /** + * Set rgb values + */ + public void setRGB(int r, int g, int b) { + if (imgInfo.greyscale || imgInfo.indexed) { + throw new PngjException("only rgb or rgba images support this"); + } + red = r; + green = g; + blue = b; + } + + public int[] getRGB() { + if (imgInfo.greyscale || imgInfo.indexed) { + throw new PngjException("only rgb or rgba images support this"); + } + return new int[]{red, green, blue}; + } + + public int getRGB888() { + if (imgInfo.greyscale || imgInfo.indexed) { + throw new PngjException("only rgb or rgba images support this"); + } + return (red << 16) | (green << 8) | blue; + } + + public void setGray(int g) { + if (!imgInfo.greyscale) { + throw new PngjException("only grayscale images support this"); + } + gray = g; + } + + public int getGray() { + if (!imgInfo.greyscale) { + throw new PngjException("only grayscale images support this"); + } + return gray; + } + + /** + * Sets the length of the palette alpha. This should be followed by + * #setNentriesPalAlpha + * + * @param idx index inside the table + * @param val alpha value (0-255) + */ + public void setEntryPalAlpha(int idx, int val) { + paletteAlpha[idx] = val; + } + + public void setNentriesPalAlpha(int len) { + paletteAlpha = new int[len]; + } + + /** + * WARNING: non deep copy. See also {@link #setNentriesPalAlpha(int)} + * {@link #setEntryPalAlpha(int, int)} + */ + public void setPalAlpha(int[] palAlpha) { + if (!imgInfo.indexed) { + throw new PngjException("only indexed images support this"); + } + paletteAlpha = palAlpha; + } + + /** + * WARNING: non deep copy + */ + public int[] getPalletteAlpha() { + return paletteAlpha; + } + + /** + * to use when only one pallete index is set as totally transparent + */ + public void setIndexEntryAsTransparent(int palAlphaIndex) { + if (!imgInfo.indexed) { + throw new PngjException("only indexed images support this"); + } + paletteAlpha = new int[]{palAlphaIndex + 1}; + for (int i = 0; i < palAlphaIndex; i++) { + paletteAlpha[i] = 255; + } + paletteAlpha[palAlphaIndex] = 0; + } + +} diff --git a/png/src/main/java/org/xbib/graphics/imageio/plugins/png/pngj/chunks/PngChunkTextVar.java b/png/src/main/java/org/xbib/graphics/imageio/plugins/png/pngj/chunks/PngChunkTextVar.java new file mode 100644 index 0000000..1dc572a --- /dev/null +++ b/png/src/main/java/org/xbib/graphics/imageio/plugins/png/pngj/chunks/PngChunkTextVar.java @@ -0,0 +1,59 @@ +package org.xbib.graphics.imageio.plugins.png.pngj.chunks; + +import org.xbib.graphics.imageio.plugins.png.pngj.ImageInfo; + +/** + * Superclass (abstract) for three textual chunks (TEXT, ITXT, ZTXT) + */ +public abstract class PngChunkTextVar extends PngChunkMultiple { + protected String key; // key/val: only for tEXt. lazy computed + protected String val; + + // http://www.w3.org/TR/PNG/#11keywords + public final static String KEY_Title = "Title"; // Short (one line) title or caption for image + public final static String KEY_Author = "Author"; // Name of image's creator + public final static String KEY_Description = "Description"; // Description of image (possibly long) + public final static String KEY_Copyright = "Copyright"; // Copyright notice + public final static String KEY_Creation_Time = "Creation Time"; // Time of original image creation + public final static String KEY_Software = "Software"; // Software used to create the image + public final static String KEY_Disclaimer = "Disclaimer"; // Legal disclaimer + public final static String KEY_Warning = "Warning"; // Warning of nature of content + public final static String KEY_Source = "Source"; // Device used to create the image + public final static String KEY_Comment = "Comment"; // Miscellaneous comment + + protected PngChunkTextVar(String id, ImageInfo info) { + super(id, info); + } + + @Override + public ChunkOrderingConstraint getOrderingConstraint() { + return ChunkOrderingConstraint.NONE; + } + + public static class PngTxtInfo { + public String title; + public String author; + public String description; + public String creation_time;// = (new Date()).toString(); + public String software; + public String disclaimer; + public String warning; + public String source; + public String comment; + + } + + public String getKey() { + return key; + } + + public String getVal() { + return val; + } + + public void setKeyVal(String key, String val) { + this.key = key; + this.val = val; + } + +} diff --git a/png/src/main/java/org/xbib/graphics/imageio/plugins/png/pngj/chunks/PngChunkUNKNOWN.java b/png/src/main/java/org/xbib/graphics/imageio/plugins/png/pngj/chunks/PngChunkUNKNOWN.java new file mode 100644 index 0000000..ab7a919 --- /dev/null +++ b/png/src/main/java/org/xbib/graphics/imageio/plugins/png/pngj/chunks/PngChunkUNKNOWN.java @@ -0,0 +1,40 @@ +package org.xbib.graphics.imageio.plugins.png.pngj.chunks; + +import org.xbib.graphics.imageio.plugins.png.pngj.ImageInfo; + +/** + * Placeholder for UNKNOWN (custom or not) chunks. + *

      + * For PngReader, a chunk is unknown if it's not registered in the chunk factory + */ +public class PngChunkUNKNOWN extends PngChunkMultiple { // unkown, custom or not + + public PngChunkUNKNOWN(String id, ImageInfo info) { + super(id, info); + } + + @Override + public ChunkOrderingConstraint getOrderingConstraint() { + return ChunkOrderingConstraint.NONE; + } + + @Override + public ChunkRaw createRawChunk() { + return raw; + } + + @Override + public void parseFromRaw(ChunkRaw c) { + + } + + /* does not do deep copy! */ + public byte[] getData() { + return raw.data; + } + + /* does not do deep copy! */ + public void setData(byte[] data) { + raw.data = data; + } +} diff --git a/png/src/main/java/org/xbib/graphics/imageio/plugins/png/pngj/chunks/PngChunkZTXT.java b/png/src/main/java/org/xbib/graphics/imageio/plugins/png/pngj/chunks/PngChunkZTXT.java new file mode 100644 index 0000000..87da89e --- /dev/null +++ b/png/src/main/java/org/xbib/graphics/imageio/plugins/png/pngj/chunks/PngChunkZTXT.java @@ -0,0 +1,64 @@ +package org.xbib.graphics.imageio.plugins.png.pngj.chunks; + +import java.io.ByteArrayOutputStream; +import java.io.IOException; +import org.xbib.graphics.imageio.plugins.png.pngj.ImageInfo; +import org.xbib.graphics.imageio.plugins.png.pngj.PngjException; + +/** + * zTXt chunk. + *

      + * see http://www.w3.org/TR/PNG/#11zTXt + */ +public class PngChunkZTXT extends PngChunkTextVar { + public final static String ID = ChunkHelper.zTXt; + + // http://www.w3.org/TR/PNG/#11zTXt + public PngChunkZTXT(ImageInfo info) { + super(ID, info); + } + + @Override + public ChunkRaw createRawChunk() { + if (key == null || key.trim().length() == 0) { + throw new PngjException("Text chunk key must be non empty"); + } + try { + ByteArrayOutputStream ba = new ByteArrayOutputStream(); + ba.write(ChunkHelper.toBytesLatin1(key)); + ba.write(0); // separator + ba.write(0); // compression method: 0 + byte[] textbytes = ChunkHelper.compressBytes(ChunkHelper.toBytesLatin1(val), true); + ba.write(textbytes); + byte[] b = ba.toByteArray(); + ChunkRaw chunk = createEmptyChunk(b.length, false); + chunk.data = b; + return chunk; + } catch (IOException e) { + throw new PngjException(e); + } + } + + @Override + public void parseFromRaw(ChunkRaw c) { + int nullsep = -1; + for (int i = 0; i < c.data.length; i++) { // look for first zero + if (c.data[i] != 0) { + continue; + } + nullsep = i; + break; + } + if (nullsep < 0 || nullsep > c.data.length - 2) { + throw new PngjException("bad zTXt chunk: no separator found"); + } + key = ChunkHelper.toStringLatin1(c.data, 0, nullsep); + int compmet = c.data[nullsep + 1]; + if (compmet != 0) { + throw new PngjException("bad zTXt chunk: unknown compression method"); + } + byte[] uncomp = ChunkHelper.compressBytes(c.data, nullsep + 2, c.data.length - nullsep - 2, false); // uncompress + val = ChunkHelper.toStringLatin1(uncomp); + } + +} diff --git a/png/src/main/java/org/xbib/graphics/imageio/plugins/png/pngj/chunks/PngMetadata.java b/png/src/main/java/org/xbib/graphics/imageio/plugins/png/pngj/chunks/PngMetadata.java new file mode 100644 index 0000000..f39a3c7 --- /dev/null +++ b/png/src/main/java/org/xbib/graphics/imageio/plugins/png/pngj/chunks/PngMetadata.java @@ -0,0 +1,237 @@ +package org.xbib.graphics.imageio.plugins.png.pngj.chunks; + +import java.util.ArrayList; +import java.util.List; +import org.xbib.graphics.imageio.plugins.png.pngj.PngjException; + +/** + * We consider "image metadata" every info inside the image except for the most + * basic image info (IHDR chunk - ImageInfo class) and the pixels values. + *

      + * This includes the palette (if present) and all the ancillary chunks + *

      + * This class provides a wrapper over the collection of chunks of a image (read + * or to write) and provides some high level methods to access them + */ +public class PngMetadata { + private final ChunksList chunkList; + private final boolean readonly; + + public PngMetadata(ChunksList chunks) { + this.chunkList = chunks; + this.readonly = !(chunks instanceof ChunksListForWrite); + } + + /** + * Queues the chunk at the writer + *

      + * lazyOverwrite: if true, checks if there is a queued "equivalent" chunk + * and if so, overwrites it. However if that not check for already written + * chunks. + */ + public void queueChunk(final PngChunk c, boolean lazyOverwrite) { + ChunksListForWrite cl = getChunkListW(); + if (readonly) { + throw new PngjException("cannot set chunk : readonly metadata"); + } + if (lazyOverwrite) { + ChunkHelper.trimList(cl.getQueuedChunks(), new ChunkPredicate() { + public boolean match(PngChunk c2) { + return ChunkHelper.equivalent(c, c2); + } + }); + } + cl.queue(c); + } + + public void queueChunk(final PngChunk c) { + queueChunk(c, true); + } + + private ChunksListForWrite getChunkListW() { + return (ChunksListForWrite) chunkList; + } + + // ///// high level utility methods follow //////////// + + // //////////// DPI + + /** + * returns -1 if not found or dimension unknown + */ + public double[] getDpi() { + PngChunk c = chunkList.getById1(ChunkHelper.pHYs, true); + if (c == null) { + return new double[]{-1, -1}; + } else { + return ((PngChunkPHYS) c).getAsDpi2(); + } + } + + public void setDpi(double x) { + setDpi(x, x); + } + + public void setDpi(double x, double y) { + PngChunkPHYS c = new PngChunkPHYS(chunkList.imageInfo); + c.setAsDpi2(x, y); + queueChunk(c); + } + + // //////////// TIME + + /** + * Creates a time chunk with current time, less secsAgo seconds + *

      + * + * @return Returns the created-queued chunk, just in case you want to + * examine or modify it + */ + public PngChunkTIME setTimeNow(int secsAgo) { + PngChunkTIME c = new PngChunkTIME(chunkList.imageInfo); + c.setNow(secsAgo); + queueChunk(c); + return c; + } + + public PngChunkTIME setTimeNow() { + return setTimeNow(0); + } + + /** + * Creates a time chunk with diven date-time + *

      + * + * @return Returns the created-queued chunk, just in case you want to + * examine or modify it + */ + public PngChunkTIME setTimeYMDHMS(int yearx, int monx, int dayx, int hourx, int minx, int secx) { + PngChunkTIME c = new PngChunkTIME(chunkList.imageInfo); + c.setYMDHMS(yearx, monx, dayx, hourx, minx, secx); + queueChunk(c, true); + return c; + } + + /** + * null if not found + */ + public PngChunkTIME getTime() { + return (PngChunkTIME) chunkList.getById1(ChunkHelper.tIME); + } + + public String getTimeAsString() { + PngChunkTIME c = getTime(); + return c == null ? "" : c.getAsString(); + } + + // //////////// TEXT + + /** + * Creates a text chunk and queue it. + *

      + * + * @param k : key (latin1) + * @param val (arbitrary, should be latin1 if useLatin1) + * @param useLatin1 + * @param compress + * @return Returns the created-queued chunks, just in case you want to + * examine, touch it + */ + public PngChunkTextVar setText(String k, String val, boolean useLatin1, boolean compress) { + if (compress && !useLatin1) { + throw new PngjException("cannot compress non latin text"); + } + PngChunkTextVar c; + if (useLatin1) { + if (compress) { + c = new PngChunkZTXT(chunkList.imageInfo); + } else { + c = new PngChunkTEXT(chunkList.imageInfo); + } + } else { + c = new PngChunkITXT(chunkList.imageInfo); + //((PngChunkITXT) c).setTranslatedTag(k); // we use the same orig tag (this is not quite right) + } + c.setKeyVal(k, val); + queueChunk(c, true); + return c; + } + + public PngChunkTextVar setText(String k, String val) { + return setText(k, val, false, false); + } + + /** + * gets all text chunks with a given key + *

      + * returns null if not found + *

      + * Warning: this does not check the "lang" key of iTxt + */ + @SuppressWarnings("unchecked") + public List getTxtsForKey(String k) { + @SuppressWarnings("rawtypes") + List c = new ArrayList(); + c.addAll(chunkList.getById(ChunkHelper.tEXt, k)); + c.addAll(chunkList.getById(ChunkHelper.zTXt, k)); + c.addAll(chunkList.getById(ChunkHelper.iTXt, k)); + return c; + } + + /** + * Returns empty if not found, concatenated (with newlines) if multiple! - + * and trimmed + *

      + * Use getTxtsForKey() if you don't want this behaviour + */ + public String getTxtForKey(String k) { + List li = getTxtsForKey(k); + if (li.isEmpty()) { + return ""; + } + StringBuilder t = new StringBuilder(); + for (PngChunkTextVar c : li) { + t.append(c.getVal()).append("\n"); + } + return t.toString().trim(); + } + + /** + * Returns the palette chunk, if present + * + * @return null if not present + */ + public PngChunkPLTE getPLTE() { + return (PngChunkPLTE) chunkList.getById1(PngChunkPLTE.ID); + } + + /** + * Creates a new empty palette chunk, queues it for write and return it to + * the caller, who should fill its entries + */ + public PngChunkPLTE createPLTEChunk() { + PngChunkPLTE plte = new PngChunkPLTE(chunkList.imageInfo); + queueChunk(plte); + return plte; + } + + /** + * Returns the TRNS chunk, if present + * + * @return null if not present + */ + public PngChunkTRNS getTRNS() { + return (PngChunkTRNS) chunkList.getById1(PngChunkTRNS.ID); + } + + /** + * Creates a new empty TRNS chunk, queues it for write and return it to the + * caller, who should fill its entries + */ + public PngChunkTRNS createTRNSChunk() { + PngChunkTRNS trns = new PngChunkTRNS(chunkList.imageInfo); + queueChunk(trns); + return trns; + } + +} diff --git a/png/src/main/java/org/xbib/graphics/imageio/plugins/png/pngj/pixels/CompressorStream.java b/png/src/main/java/org/xbib/graphics/imageio/plugins/png/pngj/pixels/CompressorStream.java new file mode 100644 index 0000000..890bf7d --- /dev/null +++ b/png/src/main/java/org/xbib/graphics/imageio/plugins/png/pngj/pixels/CompressorStream.java @@ -0,0 +1,171 @@ +package org.xbib.graphics.imageio.plugins.png.pngj.pixels; + +import java.io.OutputStream; +import org.xbib.graphics.imageio.plugins.png.pngj.IdatChunkWriter; + +/** + * This is an OutputStream that compresses (via Deflater or a deflater-like + * object), and optionally passes the compressed stream to another output + * stream. + *

      + * It allows to compute in/out/ratio stats. + *

      + * It works as a stream (similar to DeflaterOutputStream), but it's peculiar in + * that it expects that each writes has a fixed length (other lenghts are + * accepted, but it's less efficient) and that the total amount of bytes is + * known (so it can close itself, but it can also be closed on demand) In PNGJ + * use, the block is typically a row (including filter byte). + *

      + * We use this to do the real compression (with Deflate) but also to compute + * tentative estimators + *

      + * If not closed, it can be recicled via reset() + */ +public abstract class CompressorStream extends OutputStream { + + protected IdatChunkWriter idatChunkWriter; + public final int blockLen; + public final long totalbytes; + + boolean closed = false; + protected boolean done = false; + protected long bytesIn = 0; + protected long bytesOut = 0; + protected int block = -1; + + /** + * optionally stores the first byte of each block (row) + */ + private byte[] firstBytes; + protected boolean storeFirstByte = false; + + /** + * @param idatCw Can be null (if we are only interested in compute compression + * ratio) + * @param blockLen Estimated maximum block length. If unknown, use -1. + * @param totalbytes Expected total bytes to be fed. If unknown, use -1. + */ + public CompressorStream(IdatChunkWriter idatCw, int blockLen, long totalbytes) { + this.idatChunkWriter = idatCw; + if (blockLen < 0) { + blockLen = 4096; + } + if (totalbytes < 0) { + totalbytes = Long.MAX_VALUE; + } + if (blockLen < 1 || totalbytes < 1) { + throw new RuntimeException(" maxBlockLen or totalLen invalid"); + } + this.blockLen = blockLen; + this.totalbytes = totalbytes; + } + + /** + * Releases resources. Idempotent. + */ + @Override + public void close() { + done(); + if (idatChunkWriter != null) { + idatChunkWriter.close(); + } + closed = true; + } + + /** + * Will be called automatically when the number of bytes reaches the total + * expected Can be also be called from outside. This should set the flag + * done=true + */ + public abstract void done(); + + @Override + public final void write(byte[] data) { + write(data, 0, data.length); + } + + @Override + public final void write(byte[] data, int off, int len) { + block++; + if (len <= blockLen) { // normal case + mywrite(data, off, len); + if (storeFirstByte && block < firstBytes.length) { + firstBytes[block] = data[off]; // only makes sense in this case + } + } else { + while (len > 0) { + mywrite(data, off, blockLen); + off += blockLen; + len -= blockLen; + } + } + if (bytesIn >= totalbytes) { + done(); + } + + } + + /** + * same as write, but guarantedd to not exceed blockLen The implementation + * should update bytesOut and bytesInt but not check for totalBytes + */ + public abstract void mywrite(byte[] data, int off, int len); + + /** + * compressed/raw. This should be called only when done + */ + public final double getCompressionRatio() { + return bytesOut == 0 ? 1.0 : bytesOut / (double) bytesIn; + } + + /** + * raw (input) bytes. This should be called only when done + */ + public final long getBytesRaw() { + return bytesIn; + } + + /** + * compressed (out) bytes. This should be called only when done + */ + public final long getBytesCompressed() { + return bytesOut; + } + + public boolean isClosed() { + return closed; + } + + public boolean isDone() { + return done; + } + + public byte[] getFirstBytes() { + return firstBytes; + } + + public void setStoreFirstByte(boolean storeFirstByte, int nblocks) { + this.storeFirstByte = storeFirstByte; + if (this.storeFirstByte) { + if (firstBytes == null || firstBytes.length < nblocks) { + firstBytes = new byte[nblocks]; + } + } else { + firstBytes = null; + } + } + + public void reset() { + done(); + bytesIn = 0; + bytesOut = 0; + block = -1; + done = false; + } + + @Override + public void write(int i) { // should not be used + write(new byte[]{(byte) i}); + } + +} diff --git a/png/src/main/java/org/xbib/graphics/imageio/plugins/png/pngj/pixels/CompressorStreamDeflater.java b/png/src/main/java/org/xbib/graphics/imageio/plugins/png/pngj/pixels/CompressorStreamDeflater.java new file mode 100644 index 0000000..bbb643a --- /dev/null +++ b/png/src/main/java/org/xbib/graphics/imageio/plugins/png/pngj/pixels/CompressorStreamDeflater.java @@ -0,0 +1,113 @@ +package org.xbib.graphics.imageio.plugins.png.pngj.pixels; + +import java.util.zip.Deflater; +import org.xbib.graphics.imageio.plugins.png.pngj.IdatChunkWriter; +import org.xbib.graphics.imageio.plugins.png.pngj.PngjOutputException; + +/** + * CompressorStream backed by a Deflater. + *

      + * Note that the Deflater is not disposed after done, you should either recycle + * this with reset() or dispose it with close() + */ +public class CompressorStreamDeflater extends CompressorStream { + + protected Deflater deflater; + protected byte[] buf1; // temporary storage of compressed bytes: only used if idatWriter is null + protected boolean deflaterIsOwn = true; + + /** + * if a deflater is passed, it must be already reset. It will not be + * released on close + */ + public CompressorStreamDeflater(IdatChunkWriter idatCw, int maxBlockLen, long totalLen, Deflater def) { + super(idatCw, maxBlockLen, totalLen); + this.deflater = def == null ? new Deflater() : def; + this.deflaterIsOwn = def == null; + } + + public CompressorStreamDeflater(IdatChunkWriter idatCw, int maxBlockLen, long totalLen) { + this(idatCw, maxBlockLen, totalLen, null); + } + + public CompressorStreamDeflater(IdatChunkWriter idatCw, int maxBlockLen, long totalLen, int deflaterCompLevel, + int deflaterStrategy) { + this(idatCw, maxBlockLen, totalLen, new Deflater(deflaterCompLevel)); + this.deflaterIsOwn = true; + deflater.setStrategy(deflaterStrategy); + } + + @Override + public void mywrite(byte[] data, int off, final int len) { + if (deflater.finished() || done || closed) { + throw new PngjOutputException("write beyond end of stream"); + } + deflater.setInput(data, off, len); + bytesIn += len; + while (!deflater.needsInput()) { + deflate(); + } + } + + protected void deflate() { + byte[] buf; + int off, n; + if (idatChunkWriter != null) { + buf = idatChunkWriter.getBuf(); + off = idatChunkWriter.getOffset(); + n = idatChunkWriter.getAvailLen(); + } else { + if (buf1 == null) { + buf1 = new byte[4096]; + } + buf = buf1; + off = 0; + n = buf1.length; + } + int len = deflater.deflate(buf, off, n); + if (len > 0) { + if (idatChunkWriter != null) { + idatChunkWriter.incrementOffset(len); + } + bytesOut += len; + } + } + + /** + * automatically called when done + */ + @Override + public void done() { + if (done) { + return; + } + if (!deflater.finished()) { + deflater.finish(); + while (!deflater.finished()) { + deflate(); + } + } + done = true; + if (idatChunkWriter != null) { + idatChunkWriter.close(); + } + } + + public void close() { + done(); + try { + if (deflaterIsOwn) { + deflater.end(); + } + } catch (Exception e) { + } + super.close(); + } + + @Override + public void reset() { + deflater.reset(); + super.reset(); + } + +} diff --git a/png/src/main/java/org/xbib/graphics/imageio/plugins/png/pngj/pixels/CompressorStreamLz4.java b/png/src/main/java/org/xbib/graphics/imageio/plugins/png/pngj/pixels/CompressorStreamLz4.java new file mode 100644 index 0000000..f135dff --- /dev/null +++ b/png/src/main/java/org/xbib/graphics/imageio/plugins/png/pngj/pixels/CompressorStreamLz4.java @@ -0,0 +1,99 @@ +package org.xbib.graphics.imageio.plugins.png.pngj.pixels; + +import java.util.zip.Deflater; +import org.xbib.graphics.imageio.plugins.png.pngj.IdatChunkWriter; +import org.xbib.graphics.imageio.plugins.png.pngj.PngjOutputException; + +/** + * This class uses a quick compressor to get a rough estimate of deflate + * compression ratio. + *

      + * This just ignores the outputStream, and the deflater related parameters + */ +public class CompressorStreamLz4 extends CompressorStream { + + private final DeflaterEstimatorLz4 lz4; + + private byte[] buf; // lazily allocated, only if needed + private final int buffer_size; + // bufpos=bytes in buffer yet not compressed (bytesIn include this) + private int inbuf = 0; + + private static final int MAX_BUFFER_SIZE = 16000; + + public CompressorStreamLz4(IdatChunkWriter os, int maxBlockLen, long totalLen) { + super(os, maxBlockLen, totalLen); + lz4 = new DeflaterEstimatorLz4(); + buffer_size = (int) (totalLen > MAX_BUFFER_SIZE ? MAX_BUFFER_SIZE : totalLen); + } + + public CompressorStreamLz4(IdatChunkWriter os, int maxBlockLen, long totalLen, Deflater def) { + this(os, maxBlockLen, totalLen);// edlfater ignored + } + + public CompressorStreamLz4(IdatChunkWriter os, int maxBlockLen, long totalLen, int deflaterCompLevel, + int deflaterStrategy) { + this(os, maxBlockLen, totalLen); // paramters ignored + } + + @Override + public void mywrite(byte[] b, int off, int len) { + if (len == 0) { + return; + } + if (done || closed) { + throw new PngjOutputException("write beyond end of stream"); + } + bytesIn += len; + while (len > 0) { + if (inbuf == 0 && (len >= MAX_BUFFER_SIZE || bytesIn == totalbytes)) { + // direct copy (buffer might be null or empty) + bytesOut += lz4.compressEstim(b, off, len); + len = 0; + } else { + if (buf == null) { + buf = new byte[buffer_size]; + } + int len1 = inbuf + len <= buffer_size ? len : buffer_size - inbuf; // to copy + if (len1 > 0) { + System.arraycopy(b, off, buf, inbuf, len1); + } + inbuf += len1; + len -= len1; + off += len1; + if (inbuf == buffer_size) { + compressFromBuffer(); + } + } + } + } + + void compressFromBuffer() { + if (inbuf > 0) { + bytesOut += lz4.compressEstim(buf, 0, inbuf); + inbuf = 0; + } + } + + @Override + public void done() { + if (!done) { + compressFromBuffer(); + done = true; + } + } + + @Override + public void close() { + done(); + if (!closed) { + super.close(); + buf = null; + } + } + + public void reset() { + super.reset(); + } + +} diff --git a/png/src/main/java/org/xbib/graphics/imageio/plugins/png/pngj/pixels/DeflaterEstimatorHjg.java b/png/src/main/java/org/xbib/graphics/imageio/plugins/png/pngj/pixels/DeflaterEstimatorHjg.java new file mode 100644 index 0000000..c0fe3c3 --- /dev/null +++ b/png/src/main/java/org/xbib/graphics/imageio/plugins/png/pngj/pixels/DeflaterEstimatorHjg.java @@ -0,0 +1,260 @@ +package org.xbib.graphics.imageio.plugins.png.pngj.pixels; + +final class DeflaterEstimatorHjg { + + /** + * This object is stateless, it's thread safe and can be reused + */ + public DeflaterEstimatorHjg() { + } + + /** + * Estimates the length of the compressed bytes, as compressed by Lz4 + * WARNING: if larger than LZ4_64K_LIMIT it cuts it in fragments + *

      + * WARNING: if some part of the input is discarded, this should return the + * proportional (so that returnValue/srcLen=compressionRatio) + * + * @param src + * @param srcOff + * @param srcLen + * @return length of the compressed bytes + */ + public int compressEstim(byte[] src, int srcOff, final int srcLen) { + if (srcLen < 10) { + return srcLen; // too small + } + int stride = LZ4_64K_LIMIT - 1; + int segments = (srcLen + stride - 1) / stride; + stride = srcLen / segments; + if (stride >= LZ4_64K_LIMIT - 1 || stride * segments > srcLen || segments < 1 || stride < 1) { + throw new RuntimeException("?? " + srcLen); + } + int bytesIn = 0; + int bytesOut = 0; + int len = srcLen; + while (len > 0) { + if (len > stride) { + len = stride; + } + bytesOut += compress64k(src, srcOff, len); + srcOff += len; + bytesIn += len; + len = srcLen - bytesIn; + } + double ratio = bytesOut / (double) bytesIn; + return bytesIn == srcLen ? bytesOut : (int) (ratio * srcLen + 0.5); + } + + public int compressEstim(byte[] src) { + return compressEstim(src, 0, src.length); + } + + static final int MEMORY_USAGE = 14; + static final int NOT_COMPRESSIBLE_DETECTION_LEVEL = 6; // see SKIP_STRENGTH + + static final int MIN_MATCH = 4; + + static final int HASH_LOG = MEMORY_USAGE - 2; + static final int HASH_TABLE_SIZE = 1 << HASH_LOG; + + static final int SKIP_STRENGTH = Math.max(NOT_COMPRESSIBLE_DETECTION_LEVEL, 2); // 6 findMatchAttempts = 2^SKIP_STRENGTH+3 + static final int COPY_LENGTH = 8; + static final int LAST_LITERALS = 5; + static final int MF_LIMIT = COPY_LENGTH + MIN_MATCH; + static final int MIN_LENGTH = MF_LIMIT + 1; + + static final int MAX_DISTANCE = 1 << 16; + + static final int ML_BITS = 4; + static final int ML_MASK = (1 << ML_BITS) - 1; + static final int RUN_BITS = 8 - ML_BITS; + static final int RUN_MASK = (1 << RUN_BITS) - 1; + + static final int LZ4_64K_LIMIT = (1 << 16) + (MF_LIMIT - 1); + static final int HASH_LOG_64K = HASH_LOG + 1; + static final int HASH_TABLE_SIZE_64K = 1 << HASH_LOG_64K; + + static final int HASH_LOG_HC = 15; + static final int HASH_TABLE_SIZE_HC = 1 << HASH_LOG_HC; + static final int OPTIMAL_ML = ML_MASK - 1 + MIN_MATCH; + + static int compress64k(byte[] src, final int srcOff, final int srcLen) { + final int srcEnd = srcOff + srcLen; + final int srcLimit = srcEnd - LAST_LITERALS; + final int mflimit = srcEnd - MF_LIMIT; + + int sOff = srcOff, dOff = 0; + + int anchor = sOff; + + if (srcLen >= MIN_LENGTH) { + + final short[] hashTable = new short[HASH_TABLE_SIZE_64K]; + + ++sOff; + + main: + while (true) { + + // find a match + int forwardOff = sOff; + + int ref; + int findMatchAttempts1 = (1 << SKIP_STRENGTH) + 3; // 64+3=67 + do { + sOff = forwardOff; + forwardOff += findMatchAttempts1++ >>> SKIP_STRENGTH; + + if (forwardOff > mflimit) { + break main; // ends all + } + + final int h = hash64k(readInt(src, sOff)); + ref = srcOff + readShort(hashTable, h); + writeShort(hashTable, h, sOff - srcOff); + } while (!readIntEquals(src, ref, sOff)); + + // catch up + final int excess = commonBytesBackward(src, ref, sOff, srcOff, anchor); + sOff -= excess; + ref -= excess; + // sequence == refsequence + final int runLen = sOff - anchor; + dOff++; + + if (runLen >= RUN_MASK) { + if (runLen > RUN_MASK) { + dOff += (runLen - RUN_MASK) / 0xFF; + } + dOff++; + } + dOff += runLen; + while (true) { + // encode offset + dOff += 2; + // count nb matches + sOff += MIN_MATCH; + ref += MIN_MATCH; + final int matchLen = commonBytes(src, ref, sOff, srcLimit); + sOff += matchLen; + // encode match len + if (matchLen >= ML_MASK) { + if (matchLen >= ML_MASK + 0xFF) { + dOff += (matchLen - ML_MASK) / 0xFF; + } + dOff++; + } + // test end of chunk + if (sOff > mflimit) { + anchor = sOff; + break main; + } + // fill table + writeShort(hashTable, hash64k(readInt(src, sOff - 2)), sOff - 2 - srcOff); + // test next position + final int h = hash64k(readInt(src, sOff)); + ref = srcOff + readShort(hashTable, h); + writeShort(hashTable, h, sOff - srcOff); + if (!readIntEquals(src, sOff, ref)) { + break; + } + dOff++; + } + // prepare next loop + anchor = sOff++; + } + } + int runLen = srcEnd - anchor; + if (runLen >= RUN_MASK + 0xFF) { + dOff += (runLen - RUN_MASK) / 0xFF; + } + dOff++; + dOff += runLen; + return dOff; + } + + static final int maxCompressedLength(int length) { + if (length < 0) { + throw new IllegalArgumentException("length must be >= 0, got " + length); + } + return length + length / 255 + 16; + } + + static int hash(int i) { + return (i * -1640531535) >>> ((MIN_MATCH * 8) - HASH_LOG); + } + + static int hash64k(int i) { + return (i * -1640531535) >>> ((MIN_MATCH * 8) - HASH_LOG_64K); + } + + static int readShortLittleEndian(byte[] buf, int i) { + return (buf[i] & 0xFF) | ((buf[i + 1] & 0xFF) << 8); + } + + static boolean readIntEquals(byte[] buf, int i, int j) { + return buf[i] == buf[j] && buf[i + 1] == buf[j + 1] && buf[i + 2] == buf[j + 2] && buf[i + 3] == buf[j + 3]; + } + + static int commonBytes(byte[] b, int o1, int o2, int limit) { + int count = 0; + while (o2 < limit && b[o1++] == b[o2++]) { + ++count; + } + return count; + } + + static int commonBytesBackward(byte[] b, int o1, int o2, int l1, int l2) { + int count = 0; + while (o1 > l1 && o2 > l2 && b[--o1] == b[--o2]) { + ++count; + } + return count; + } + + static int readShort(short[] buf, int off) { + return buf[off] & 0xFFFF; + } + + static byte readByte(byte[] buf, int i) { + return buf[i]; + } + + static void checkRange(byte[] buf, int off) { + if (off < 0 || off >= buf.length) { + throw new ArrayIndexOutOfBoundsException(off); + } + } + + static void checkRange(byte[] buf, int off, int len) { + checkLength(len); + if (len > 0) { + checkRange(buf, off); + checkRange(buf, off + len - 1); + } + } + + static void checkLength(int len) { + if (len < 0) { + throw new IllegalArgumentException("lengths must be >= 0"); + } + } + + static int readIntBE(byte[] buf, int i) { + return ((buf[i] & 0xFF) << 24) | ((buf[i + 1] & 0xFF) << 16) | ((buf[i + 2] & 0xFF) << 8) | (buf[i + 3] & 0xFF); + } + + static int readIntLE(byte[] buf, int i) { + return (buf[i] & 0xFF) | ((buf[i + 1] & 0xFF) << 8) | ((buf[i + 2] & 0xFF) << 16) | ((buf[i + 3] & 0xFF) << 24); + } + + static int readInt(byte[] buf, int i) { + return readIntBE(buf, i); + } + + static void writeShort(short[] buf, int off, int v) { + buf[off] = (short) v; + } + +} diff --git a/png/src/main/java/org/xbib/graphics/imageio/plugins/png/pngj/pixels/DeflaterEstimatorLz4.java b/png/src/main/java/org/xbib/graphics/imageio/plugins/png/pngj/pixels/DeflaterEstimatorLz4.java new file mode 100644 index 0000000..6ce0031 --- /dev/null +++ b/png/src/main/java/org/xbib/graphics/imageio/plugins/png/pngj/pixels/DeflaterEstimatorLz4.java @@ -0,0 +1,278 @@ +package org.xbib.graphics.imageio.plugins.png.pngj.pixels; + +import java.nio.ByteOrder; + +/** + * This estimator actually uses the LZ4 compression algorithm, and hopes that + * it's well correlated with Deflater. It's about 3 to 4 times faster than + * Deflater. + *

      + * This is a modified heavily trimmed version of the + * net.jpountz.lz4.LZ4JavaSafeCompressor class plus some methods from other + * classes from LZ4 Java library: https://github.com/jpountz/lz4-java , + * originally licensed under the Apache License 2.0 + */ +final public class DeflaterEstimatorLz4 { + + /** + * This object is stateless, it's thread safe and can be reused + */ + public DeflaterEstimatorLz4() { + } + + /** + * Estimates the length of the compressed bytes, as compressed by Lz4 + * WARNING: if larger than LZ4_64K_LIMIT it cuts it in fragments + *

      + * WARNING: if some part of the input is discarded, this should return the + * proportional (so that returnValue/srcLen=compressionRatio) + * + * @param src + * @param srcOff + * @param srcLen + * @return length of the compressed bytes + */ + public int compressEstim(byte[] src, int srcOff, final int srcLen) { + if (srcLen < 10) { + return srcLen; // too small + } + int stride = LZ4_64K_LIMIT - 1; + int segments = (srcLen + stride - 1) / stride; + stride = srcLen / segments; + if (stride >= LZ4_64K_LIMIT - 1 || stride * segments > srcLen || segments < 1 || stride < 1) { + throw new RuntimeException("?? " + srcLen); + } + int bytesIn = 0; + int bytesOut = 0; + int len = srcLen; + while (len > 0) { + if (len > stride) { + len = stride; + } + bytesOut += compress64k(src, srcOff, len); + srcOff += len; + bytesIn += len; + len = srcLen - bytesIn; + } + double ratio = bytesOut / (double) bytesIn; + return bytesIn == srcLen ? bytesOut : (int) (ratio * srcLen + 0.5); + } + + public int compressEstim(byte[] src) { + return compressEstim(src, 0, src.length); + } + + static final ByteOrder NATIVE_BYTE_ORDER = ByteOrder.nativeOrder(); + + static final int MEMORY_USAGE = 14; + static final int NOT_COMPRESSIBLE_DETECTION_LEVEL = 6; + + static final int MIN_MATCH = 4; + + static final int HASH_LOG = MEMORY_USAGE - 2; + static final int HASH_TABLE_SIZE = 1 << HASH_LOG; + + static final int SKIP_STRENGTH = Math.max(NOT_COMPRESSIBLE_DETECTION_LEVEL, 2); + static final int COPY_LENGTH = 8; + static final int LAST_LITERALS = 5; + static final int MF_LIMIT = COPY_LENGTH + MIN_MATCH; + static final int MIN_LENGTH = MF_LIMIT + 1; + + static final int MAX_DISTANCE = 1 << 16; + + static final int ML_BITS = 4; + static final int ML_MASK = (1 << ML_BITS) - 1; + static final int RUN_BITS = 8 - ML_BITS; + static final int RUN_MASK = (1 << RUN_BITS) - 1; + + static final int LZ4_64K_LIMIT = (1 << 16) + (MF_LIMIT - 1); + static final int HASH_LOG_64K = HASH_LOG + 1; + static final int HASH_TABLE_SIZE_64K = 1 << HASH_LOG_64K; + + static final int HASH_LOG_HC = 15; + static final int HASH_TABLE_SIZE_HC = 1 << HASH_LOG_HC; + static final int OPTIMAL_ML = ML_MASK - 1 + MIN_MATCH; + + static int compress64k(byte[] src, int srcOff, int srcLen) { + final int srcEnd = srcOff + srcLen; + final int srcLimit = srcEnd - LAST_LITERALS; + final int mflimit = srcEnd - MF_LIMIT; + + int sOff = srcOff, dOff = 0; + + int anchor = sOff; + + if (srcLen >= MIN_LENGTH) { + + final short[] hashTable = new short[HASH_TABLE_SIZE_64K]; + + ++sOff; + + main: + while (true) { + + // find a match + int forwardOff = sOff; + + int ref; + int findMatchAttempts = (1 << SKIP_STRENGTH) + 3; + do { + sOff = forwardOff; + forwardOff += findMatchAttempts++ >>> SKIP_STRENGTH; + + if (forwardOff > mflimit) { + break main; + } + + final int h = hash64k(readInt(src, sOff)); + ref = srcOff + readShort(hashTable, h); + writeShort(hashTable, h, sOff - srcOff); + } while (!readIntEquals(src, ref, sOff)); + + // catch up + final int excess = commonBytesBackward(src, ref, sOff, srcOff, anchor); + sOff -= excess; + ref -= excess; + // sequence == refsequence + final int runLen = sOff - anchor; + dOff++; + + if (runLen >= RUN_MASK) { + if (runLen > RUN_MASK) { + dOff += (runLen - RUN_MASK) / 0xFF; + } + dOff++; + } + dOff += runLen; + while (true) { + // encode offset + dOff += 2; + // count nb matches + sOff += MIN_MATCH; + ref += MIN_MATCH; + final int matchLen = commonBytes(src, ref, sOff, srcLimit); + sOff += matchLen; + // encode match len + if (matchLen >= ML_MASK) { + if (matchLen >= ML_MASK + 0xFF) { + dOff += (matchLen - ML_MASK) / 0xFF; + } + dOff++; + } + // test end of chunk + if (sOff > mflimit) { + anchor = sOff; + break main; + } + // fill table + writeShort(hashTable, hash64k(readInt(src, sOff - 2)), sOff - 2 - srcOff); + // test next position + final int h = hash64k(readInt(src, sOff)); + ref = srcOff + readShort(hashTable, h); + writeShort(hashTable, h, sOff - srcOff); + if (!readIntEquals(src, sOff, ref)) { + break; + } + dOff++; + } + // prepare next loop + anchor = sOff++; + } + } + int runLen = srcEnd - anchor; + if (runLen >= RUN_MASK + 0xFF) { + dOff += (runLen - RUN_MASK) / 0xFF; + } + dOff++; + dOff += runLen; + return dOff; + } + + static final int maxCompressedLength(int length) { + if (length < 0) { + throw new IllegalArgumentException("length must be >= 0, got " + length); + } + return length + length / 255 + 16; + } + + static int hash(int i) { + return (i * -1640531535) >>> ((MIN_MATCH * 8) - HASH_LOG); + } + + static int hash64k(int i) { + return (i * -1640531535) >>> ((MIN_MATCH * 8) - HASH_LOG_64K); + } + + static int readShortLittleEndian(byte[] buf, int i) { + return (buf[i] & 0xFF) | ((buf[i + 1] & 0xFF) << 8); + } + + static boolean readIntEquals(byte[] buf, int i, int j) { + return buf[i] == buf[j] && buf[i + 1] == buf[j + 1] && buf[i + 2] == buf[j + 2] && buf[i + 3] == buf[j + 3]; + } + + static int commonBytes(byte[] b, int o1, int o2, int limit) { + int count = 0; + while (o2 < limit && b[o1++] == b[o2++]) { + ++count; + } + return count; + } + + static int commonBytesBackward(byte[] b, int o1, int o2, int l1, int l2) { + int count = 0; + while (o1 > l1 && o2 > l2 && b[--o1] == b[--o2]) { + ++count; + } + return count; + } + + static int readShort(short[] buf, int off) { + return buf[off] & 0xFFFF; + } + + static byte readByte(byte[] buf, int i) { + return buf[i]; + } + + static void checkRange(byte[] buf, int off) { + if (off < 0 || off >= buf.length) { + throw new ArrayIndexOutOfBoundsException(off); + } + } + + static void checkRange(byte[] buf, int off, int len) { + checkLength(len); + if (len > 0) { + checkRange(buf, off); + checkRange(buf, off + len - 1); + } + } + + static void checkLength(int len) { + if (len < 0) { + throw new IllegalArgumentException("lengths must be >= 0"); + } + } + + static int readIntBE(byte[] buf, int i) { + return ((buf[i] & 0xFF) << 24) | ((buf[i + 1] & 0xFF) << 16) | ((buf[i + 2] & 0xFF) << 8) | (buf[i + 3] & 0xFF); + } + + static int readIntLE(byte[] buf, int i) { + return (buf[i] & 0xFF) | ((buf[i + 1] & 0xFF) << 8) | ((buf[i + 2] & 0xFF) << 16) | ((buf[i + 3] & 0xFF) << 24); + } + + static int readInt(byte[] buf, int i) { + if (NATIVE_BYTE_ORDER == ByteOrder.BIG_ENDIAN) { + return readIntBE(buf, i); + } else { + return readIntLE(buf, i); + } + } + + static void writeShort(short[] buf, int off, int v) { + buf[off] = (short) v; + } + +} diff --git a/png/src/main/java/org/xbib/graphics/imageio/plugins/png/pngj/pixels/FiltersPerformance.java b/png/src/main/java/org/xbib/graphics/imageio/plugins/png/pngj/pixels/FiltersPerformance.java new file mode 100644 index 0000000..9b3ccab --- /dev/null +++ b/png/src/main/java/org/xbib/graphics/imageio/plugins/png/pngj/pixels/FiltersPerformance.java @@ -0,0 +1,224 @@ +package org.xbib.graphics.imageio.plugins.png.pngj.pixels; + +import java.util.Arrays; +import org.xbib.graphics.imageio.plugins.png.pngj.FilterType; +import org.xbib.graphics.imageio.plugins.png.pngj.ImageInfo; +import org.xbib.graphics.imageio.plugins.png.pngj.PngHelperInternal; +import org.xbib.graphics.imageio.plugins.png.pngj.PngjExceptionInternal; + +/** + * for use in adaptative strategy + */ +public class FiltersPerformance { + + private final ImageInfo iminfo; + private double memoryA = 0.7; // empirical (not very critical: 0.72) + private int lastrow = -1; + private final double[] absum = new double[5];// depending on the strategy not all values might be computed for all + private final double[] entropy = new double[5]; + private final double[] cost = new double[5]; + private final int[] histog = new int[256]; // temporary, not normalized + private int lastprefered = -1; + private boolean initdone = false; + private double preferenceForNone = 1.0; // higher gives more preference to NONE + + // this values are empirical (montecarlo), for RGB8 images with entropy estimator for NONE and memory=0.7 + //DONT MODIFY THIS + // lower is better! + public static final double[] FILTER_WEIGHTS_DEFAULT = {0.73, 1.03, 0.97, 1.11, 1.22}; + + private final double[] filter_weights = new double[]{-1, -1, -1, -1, -1}; + + private final static double LOG2NI = -1.0 / Math.log(2.0); + + public FiltersPerformance(ImageInfo imgInfo) { + this.iminfo = imgInfo; + } + + private void init() { + if (filter_weights[0] < 0) {// has not been set from outside + System.arraycopy(FILTER_WEIGHTS_DEFAULT, 0, filter_weights, 0, 5); + double wNone = filter_weights[0]; + if (iminfo.bitDepth == 16) { + wNone = 1.2; + } else if (iminfo.alpha) { + wNone = 0.8; + } else if (iminfo.indexed || iminfo.bitDepth < 8) { + wNone = 0.4; // we prefer NONE strongly + } + wNone /= preferenceForNone; + filter_weights[0] = wNone; + } + Arrays.fill(cost, 1.0); + initdone = true; + } + + public void updateFromFiltered(FilterType ftype, byte[] rowff, int rown) { + updateFromRawOrFiltered(ftype, rowff, null, null, rown); + } + + /** + * alternative: computes statistic without filtering + */ + public void updateFromRaw(FilterType ftype, byte[] rowb, byte[] rowbprev, int rown) { + updateFromRawOrFiltered(ftype, null, rowb, rowbprev, rown); + } + + private void updateFromRawOrFiltered(FilterType ftype, byte[] rowff, byte[] rowb, byte[] rowbprev, int rown) { + if (!initdone) { + init(); + } + if (rown != lastrow) { + Arrays.fill(absum, Double.NaN); + Arrays.fill(entropy, Double.NaN); + } + lastrow = rown; + if (rowff != null) { + computeHistogram(rowff); + } else { + computeHistogramForFilter(ftype, rowb, rowbprev); + } + if (ftype == FilterType.FILTER_NONE) { + entropy[ftype.val] = computeEntropyFromHistogram(); + } else { + absum[ftype.val] = computeAbsFromHistogram(); + } + } + + /* WARNING: this is not idempotent, call it just once per cycle (sigh) */ + public FilterType getPreferred() { + int fi = 0; + double vali = Double.MAX_VALUE, val = 0; // lower wins + for (int i = 0; i < 5; i++) { + if (!Double.isNaN(absum[i])) { + val = absum[i]; + } else if (!Double.isNaN(entropy[i])) { + val = (Math.pow(2.0, entropy[i]) - 1.0) * 0.5; + } else { + continue; + } + val *= filter_weights[i]; + val = cost[i] * memoryA + (1 - memoryA) * val; + cost[i] = val; + if (val < vali) { + vali = val; + fi = i; + } + } + lastprefered = fi; + return FilterType.getByVal(lastprefered); + } + + public final void computeHistogramForFilter(FilterType filterType, byte[] rowb, byte[] rowbprev) { + Arrays.fill(histog, 0); + int i, j, imax = iminfo.bytesPerRow; + switch (filterType) { + case FILTER_NONE: + for (i = 1; i <= imax; i++) { + histog[rowb[i] & 0xFF]++; + } + break; + case FILTER_PAETH: + for (i = 1; i <= imax; i++) { + histog[PngHelperInternal.filterRowPaeth(rowb[i], 0, rowbprev[i] & 0xFF, 0)]++; + } + for (j = 1, i = iminfo.bytesPixel + 1; i <= imax; i++, j++) { + histog[PngHelperInternal.filterRowPaeth(rowb[i], rowb[j] & 0xFF, rowbprev[i] & 0xFF, + rowbprev[j] & 0xFF)]++; + } + break; + case FILTER_SUB: + for (i = 1; i <= iminfo.bytesPixel; i++) { + histog[rowb[i] & 0xFF]++; + } + for (j = 1, i = iminfo.bytesPixel + 1; i <= imax; i++, j++) { + histog[(rowb[i] - rowb[j]) & 0xFF]++; + } + break; + case FILTER_UP: + for (i = 1; i <= iminfo.bytesPerRow; i++) { + histog[(rowb[i] - rowbprev[i]) & 0xFF]++; + } + break; + case FILTER_AVERAGE: + for (i = 1; i <= iminfo.bytesPixel; i++) { + histog[((rowb[i] & 0xFF) - ((rowbprev[i] & 0xFF)) / 2) & 0xFF]++; + } + for (j = 1, i = iminfo.bytesPixel + 1; i <= imax; i++, j++) { + histog[((rowb[i] & 0xFF) - ((rowbprev[i] & 0xFF) + (rowb[j] & 0xFF)) / 2) & 0xFF]++; + } + break; + default: + throw new PngjExceptionInternal("Bad filter:" + filterType); + } + } + + public void computeHistogram(byte[] rowff) { + Arrays.fill(histog, 0); + for (int i = 1; i < iminfo.bytesPerRow; i++) { + histog[rowff[i] & 0xFF]++; + } + } + + public double computeAbsFromHistogram() { + int s = 0; + for (int i = 1; i < 128; i++) { + s += histog[i] * i; + } + for (int i = 128, j = 128; j > 0; i++, j--) { + s += histog[i] * j; + } + return s / (double) iminfo.bytesPerRow; + } + + public final double computeEntropyFromHistogram() { + double s = 1.0 / iminfo.bytesPerRow; + double ls = Math.log(s); + + double h = 0; + for (int x : histog) { + if (x > 0) { + h += (Math.log(x) + ls) * x; + } + } + h *= s * LOG2NI; + if (h < 0.0) { + h = 0.0; + } + return h; + } + + /** + * If larger than 1.0, NONE will be more prefered. This must be called + * before init + * + * @param preferenceForNone around 1.0 (default: 1.0) + */ + public void setPreferenceForNone(double preferenceForNone) { + this.preferenceForNone = preferenceForNone; + } + + /** + * Values greater than 1.0 (towards infinite) increase the memory towards 1. + * Values smaller than 1.0 (towards zero) decreases the memory . + */ + public void tuneMemory(double m) { + if (m == 0) { + memoryA = 0.0; + } else { + memoryA = Math.pow(memoryA, 1.0 / m); + } + } + + /** + * To set manually the filter weights. This is not recommended, unless you + * know what you are doing. Setting this ignores preferenceForNone and omits + * some heuristics + * + * @param weights Five doubles around 1.0, one for each filter type. Lower is + * preferered + */ + public void setFilterWeights(double[] weights) { + System.arraycopy(weights, 0, filter_weights, 0, 5); + } +} diff --git a/png/src/main/java/org/xbib/graphics/imageio/plugins/png/pngj/pixels/PixelsWriter.java b/png/src/main/java/org/xbib/graphics/imageio/plugins/png/pngj/pixels/PixelsWriter.java new file mode 100644 index 0000000..3cc9a4d --- /dev/null +++ b/png/src/main/java/org/xbib/graphics/imageio/plugins/png/pngj/pixels/PixelsWriter.java @@ -0,0 +1,289 @@ +package org.xbib.graphics.imageio.plugins.png.pngj.pixels; + +import java.io.OutputStream; +import java.util.zip.Deflater; +import org.xbib.graphics.imageio.plugins.png.pngj.FilterType; +import org.xbib.graphics.imageio.plugins.png.pngj.IdatChunkWriter; +import org.xbib.graphics.imageio.plugins.png.pngj.ImageInfo; +import org.xbib.graphics.imageio.plugins.png.pngj.PngHelperInternal; +import org.xbib.graphics.imageio.plugins.png.pngj.PngjOutputException; + +/** + * Encodes a set of rows (pixels) as a continuous deflated stream (does not know + * about IDAT chunk segmentation). + *

      + * This includes the filter selection strategy, plus the filtering itself and + * the deflating. Only supports fixed length rows (no interlaced writing). + *

      + * Typically an instance of this is hold by a PngWriter - but more instances + * could be used (for APGN) + */ +public abstract class PixelsWriter { + + private static final int IDAT_MAX_SIZE_DEFAULT = 32000; + + protected final ImageInfo imgInfo; + /** + * row buffer length, including filter byte (imgInfo.bytesPerRow + 1) + */ + protected final int buflen; + + protected final int bytesPixel; + protected final int bytesRow; + + private CompressorStream compressorStream; // to compress the idat stream + + protected int deflaterCompLevel = 6; + protected int deflaterStrategy = Deflater.DEFAULT_STRATEGY; + + protected boolean initdone = false; + + /** + * This is the globally configured filter type - it can be a concrete type + * or a pseudo type (hint or strategy) + */ + protected FilterType filterType; + + // counts the filters used - just for stats + private final int[] filtersUsed = new int[5]; + + // this is the raw underlying os (shared with the PngWriter) + private OutputStream os; + + private int idatMaxSize = IDAT_MAX_SIZE_DEFAULT; + + /** + * row being processed, couting from zero + */ + protected int currentRow; + + public PixelsWriter(ImageInfo imgInfo) { + this.imgInfo = imgInfo; + bytesRow = imgInfo.bytesPerRow; + buflen = bytesRow + 1; + bytesPixel = imgInfo.bytesPixel; + currentRow = -1; + filterType = FilterType.FILTER_DEFAULT; + } + + /** + * main internal point for external call. It does the lazy initializion if + * necessary, sets current row, and call {@link #filterAndWrite(byte[])} + */ + public final void processRow(final byte[] rowb) { + if (!initdone) { + init(); + } + currentRow++; + filterAndWrite(rowb); + } + + protected void sendToCompressedStream(byte[] rowf) { + compressorStream.write(rowf, 0, rowf.length); + filtersUsed[rowf[0]]++; + } + + /** + * This does the filtering and send to stream. Typically should decide the + * filtering, call + * {@link #filterRowWithFilterType(FilterType, byte[], byte[], byte[])} and + * and {@link #sendToCompressedStream(byte[])} + * + * @param rowb + */ + protected abstract void filterAndWrite(final byte[] rowb); + + /** + * Does the real filtering. This must be called with the real (standard) + * filterType. This should rarely be overriden. + *

      + * WARNING: look out the contract + * + * @param _filterType + * @param _rowb current row (the first byte might be modified) + * @param _rowbprev previous row (should be all zero the first time) + * @param _rowf tentative buffer to store the filtered bytes. might not be + * used! + * @return normally _rowf, but eventually _rowb. This MUST NOT BE MODIFIED + * nor reused by caller + */ + final protected byte[] filterRowWithFilterType(FilterType _filterType, byte[] _rowb, byte[] _rowbprev, + byte[] _rowf) { + // warning: some filters rely on: "previous row" (rowbprev) it must be initialized to 0 the first time + if (_filterType == FilterType.FILTER_NONE) { + _rowf = _rowb; + } + _rowf[0] = (byte) _filterType.val; + int i, j; + switch (_filterType) { + case FILTER_NONE: + // we return the same original (be careful!) + break; + case FILTER_PAETH: + for (i = 1; i <= bytesPixel; i++) { + _rowf[i] = (byte) PngHelperInternal.filterRowPaeth(_rowb[i], 0, _rowbprev[i] & 0xFF, 0); + } + for (j = 1, i = bytesPixel + 1; i <= bytesRow; i++, j++) { + _rowf[i] = (byte) PngHelperInternal.filterRowPaeth(_rowb[i], _rowb[j] & 0xFF, _rowbprev[i] & 0xFF, + _rowbprev[j] & 0xFF); + } + break; + case FILTER_SUB: + for (i = 1; i <= bytesPixel; i++) { + _rowf[i] = _rowb[i]; + } + for (j = 1, i = bytesPixel + 1; i <= bytesRow; i++, j++) { + _rowf[i] = (byte) (_rowb[i] - _rowb[j]); + } + break; + case FILTER_AVERAGE: + for (i = 1; i <= bytesPixel; i++) { + _rowf[i] = (byte) (_rowb[i] - (_rowbprev[i] & 0xFF) / 2); + } + for (j = 1, i = bytesPixel + 1; i <= bytesRow; i++, j++) { + _rowf[i] = (byte) (_rowb[i] - ((_rowbprev[i] & 0xFF) + (_rowb[j] & 0xFF)) / 2); + } + break; + case FILTER_UP: + for (i = 1; i <= bytesRow; i++) { + _rowf[i] = (byte) (_rowb[i] - _rowbprev[i]); + } + break; + default: + throw new PngjOutputException("Filter type not recognized: " + _filterType); + } + return _rowf; + } + + /** + * This will be called by the PngWrite to fill the raw pixels for each row. + * This can change from call to call. Warning: this can be called before the + * object is init, implementations should call init() to be sure + */ + public abstract byte[] getRowb(); + + /** + * This will be called lazily just before writing row 0. Idempotent. + */ + protected final void init() { + if (!initdone) { + initParams(); + initdone = true; + } + } + + /** + * called by init(); override (calling this first) to do additional + * initialization + */ + protected void initParams() { + IdatChunkWriter idatWriter = new IdatChunkWriter(os, idatMaxSize); + if (compressorStream == null) { // if not set, use the deflater + compressorStream = new CompressorStreamDeflater(idatWriter, buflen, imgInfo.getTotalRawBytes(), + deflaterCompLevel, deflaterStrategy); + } + } + + /** + * cleanup. This should be called explicitly. Idempotent and secure + */ + public void close() { + if (compressorStream != null) { + compressorStream.close(); + } + } + + /** + * Deflater (ZLIB) strategy. You should rarely change this from the default + * (Deflater.DEFAULT_STRATEGY) to Deflater.FILTERED (Deflater.HUFFMAN_ONLY + * is fast but compress poorly) + */ + public void setDeflaterStrategy(Integer deflaterStrategy) { + this.deflaterStrategy = deflaterStrategy; + } + + /** + * Deflater (ZLIB) compression level, between 0 (no compression) and 9 + */ + public void setDeflaterCompLevel(Integer deflaterCompLevel) { + this.deflaterCompLevel = deflaterCompLevel; + } + + public Integer getDeflaterCompLevel() { + return deflaterCompLevel; + } + + public final void setOs(OutputStream datStream) { + this.os = datStream; + } + + public OutputStream getOs() { + return os; + } + + /** + * @see #filterType + */ + final public FilterType getFilterType() { + return filterType; + } + + /** + * @see #filterType + */ + final public void setFilterType(FilterType filterType) { + this.filterType = filterType; + } + + /* out/in This should be called only after end() to get reliable results */ + public double getCompression() { + return compressorStream.isDone() ? compressorStream.getCompressionRatio() : 1.0; + } + + public void setCompressorStream(CompressorStream compressorStream) { + this.compressorStream = compressorStream; + } + + public long getTotalBytesToWrite() { + return imgInfo.getTotalRawBytes(); + } + + public boolean isDone() { + return currentRow == imgInfo.rows - 1; + } + + /** + * computed default fixed filter type to use, if specified DEFAULT; wilde + * guess based on image properties + * + * @return One of the five concrete filter types + */ + protected FilterType getDefaultFilter() { + if (imgInfo.indexed || imgInfo.bitDepth < 8) { + return FilterType.FILTER_NONE; + } else if (imgInfo.getTotalPixels() < 1024) { + return FilterType.FILTER_NONE; + } else if (imgInfo.rows == 1) { + return FilterType.FILTER_SUB; + } else if (imgInfo.cols == 1) { + return FilterType.FILTER_UP; + } else { + return FilterType.FILTER_PAETH; + } + } + + /** + * informational stats : filter used, in percentages + */ + final public String getFiltersUsed() { + return String.format("%d,%d,%d,%d,%d", (int) (filtersUsed[0] * 100.0 / imgInfo.rows + 0.5), + (int) (filtersUsed[1] * 100.0 / imgInfo.rows + 0.5), + (int) (filtersUsed[2] * 100.0 / imgInfo.rows + 0.5), + (int) (filtersUsed[3] * 100.0 / imgInfo.rows + 0.5), + (int) (filtersUsed[4] * 100.0 / imgInfo.rows + 0.5)); + } + + public void setIdatMaxSize(int idatMaxSize) { + this.idatMaxSize = idatMaxSize; + } +} diff --git a/png/src/main/java/org/xbib/graphics/imageio/plugins/png/pngj/pixels/PixelsWriterDefault.java b/png/src/main/java/org/xbib/graphics/imageio/plugins/png/pngj/pixels/PixelsWriterDefault.java new file mode 100644 index 0000000..2a20bc1 --- /dev/null +++ b/png/src/main/java/org/xbib/graphics/imageio/plugins/png/pngj/pixels/PixelsWriterDefault.java @@ -0,0 +1,185 @@ +package org.xbib.graphics.imageio.plugins.png.pngj.pixels; + +import java.util.Arrays; +import org.xbib.graphics.imageio.plugins.png.pngj.FilterType; +import org.xbib.graphics.imageio.plugins.png.pngj.ImageInfo; +import org.xbib.graphics.imageio.plugins.png.pngj.PngjOutputException; + +/** + * Default implementation of PixelsWriter, with fixed filters and also adaptive + * strategies. + */ +public class PixelsWriterDefault extends PixelsWriter { + /** + * current raw row + */ + protected byte[] rowb; + /** + * previous raw row + */ + protected byte[] rowbprev; + /** + * buffer for filtered row + */ + protected byte[] rowbfilter; + + /** + * evaluates different filters, for adaptive strategy + */ + protected FiltersPerformance filtersPerformance; + + /** + * currently concrete selected filter type + */ + protected FilterType curfilterType; + + /** + * parameters for adaptive strategy + */ + protected int adaptMaxSkip; // set in initParams, does not change + protected int adaptSkipIncreaseSinceRow; // set in initParams, does not change + protected double adaptSkipIncreaseFactor; // set in initParams, does not change + protected int adaptNextRow = 0; + + public PixelsWriterDefault(ImageInfo imgInfo) { + super(imgInfo); + filtersPerformance = new FiltersPerformance(imgInfo); + } + + @Override + protected void initParams() { + super.initParams(); + + if (rowb == null || rowb.length < buflen) { + rowb = new byte[buflen]; + } + if (rowbfilter == null || rowbfilter.length < buflen) { + rowbfilter = new byte[buflen]; + } + if (rowbprev == null || rowbprev.length < buflen) { + rowbprev = new byte[buflen]; + } else { + Arrays.fill(rowbprev, (byte) 0); + } + + // if adaptative but too few rows or columns, use default + if (imgInfo.cols < 3 && !FilterType.isValidStandard(filterType)) { + filterType = FilterType.FILTER_DEFAULT; + } + if (imgInfo.rows < 3 && !FilterType.isValidStandard(filterType)) { + filterType = FilterType.FILTER_DEFAULT; + } + + if (imgInfo.getTotalPixels() <= 1024 && !FilterType.isValidStandard(filterType)) { + filterType = getDefaultFilter(); + } + + if (FilterType.isAdaptive(filterType)) { + // adaptCurSkip = 0; + adaptNextRow = 0; + if (filterType == FilterType.FILTER_ADAPTIVE_FAST) { + adaptMaxSkip = 200; + adaptSkipIncreaseSinceRow = 3; + adaptSkipIncreaseFactor = 1 / 4.0; // skip ~ row/3 + } else if (filterType == FilterType.FILTER_ADAPTIVE_MEDIUM) { + adaptMaxSkip = 8; + adaptSkipIncreaseSinceRow = 32; + adaptSkipIncreaseFactor = 1 / 80.0; + } else if (filterType == FilterType.FILTER_ADAPTIVE_FULL) { + adaptMaxSkip = 0; + adaptSkipIncreaseSinceRow = 128; + adaptSkipIncreaseFactor = 1 / 120.0; + } else { + throw new PngjOutputException("bad filter " + filterType); + } + } + } + + @Override + protected void filterAndWrite(final byte[] rowb) { + if (rowb != this.rowb) { + throw new RuntimeException("??"); // we rely on this + } + decideCurFilterType(); + byte[] filtered = filterRowWithFilterType(curfilterType, rowb, rowbprev, rowbfilter); + sendToCompressedStream(filtered); + // swap rowb <-> rowbprev + byte[] aux = this.rowb; + this.rowb = rowbprev; + rowbprev = aux; + } + + protected void decideCurFilterType() { + // decide the real filter and store in curfilterType + if (FilterType.isValidStandard(getFilterType())) { + curfilterType = getFilterType(); + } else if (getFilterType() == FilterType.FILTER_PRESERVE) { + curfilterType = FilterType.getByVal(rowb[0]); + } else if (getFilterType() == FilterType.FILTER_CYCLIC) { + curfilterType = FilterType.getByVal(currentRow % 5); + } else if (getFilterType() == FilterType.FILTER_DEFAULT) { + setFilterType(getDefaultFilter()); + curfilterType = getFilterType(); // this could be done once + } else if (FilterType.isAdaptive(getFilterType())) {// adaptive + if (currentRow == adaptNextRow) { + for (FilterType ftype : FilterType.getAllStandard()) { + filtersPerformance.updateFromRaw(ftype, rowb, rowbprev, currentRow); + } + curfilterType = filtersPerformance.getPreferred(); + int skip = (currentRow >= adaptSkipIncreaseSinceRow + ? (int) Math.round((currentRow - adaptSkipIncreaseSinceRow) * adaptSkipIncreaseFactor) + : 0); + if (skip > adaptMaxSkip) { + skip = adaptMaxSkip; + } + if (currentRow == 0) { + skip = 0; + } + adaptNextRow = currentRow + 1 + skip; + } + } else { + throw new PngjOutputException("not implemented filter: " + getFilterType()); + } + if (currentRow == 0 && curfilterType != FilterType.FILTER_NONE && curfilterType != FilterType.FILTER_SUB) { + curfilterType = FilterType.FILTER_SUB; // first row should always be none or sub + } + } + + @Override + public byte[] getRowb() { + if (!initdone) { + init(); + } + return rowb; + } + + @Override + public void close() { + super.close(); + } + + /** + * Only for adaptive strategies. See + * {@link FiltersPerformance#setPreferenceForNone(double)} + */ + public void setPreferenceForNone(double preferenceForNone) { + filtersPerformance.setPreferenceForNone(preferenceForNone); + } + + /** + * Only for adaptive strategies. See + * {@link FiltersPerformance#tuneMemory(double)} + */ + public void tuneMemory(double m) { + filtersPerformance.tuneMemory(m); + } + + /** + * Only for adaptive strategies. See + * {@link FiltersPerformance#setFilterWeights(double[])} + */ + public void setFilterWeights(double[] weights) { + filtersPerformance.setFilterWeights(weights); + } + +} diff --git a/png/src/main/java/org/xbib/graphics/imageio/plugins/png/pngj/pixels/PixelsWriterMultiple.java b/png/src/main/java/org/xbib/graphics/imageio/plugins/png/pngj/pixels/PixelsWriterMultiple.java new file mode 100644 index 0000000..06485b3 --- /dev/null +++ b/png/src/main/java/org/xbib/graphics/imageio/plugins/png/pngj/pixels/PixelsWriterMultiple.java @@ -0,0 +1,254 @@ +package org.xbib.graphics.imageio.plugins.png.pngj.pixels; + +import java.util.LinkedList; +import java.util.zip.Deflater; +import org.xbib.graphics.imageio.plugins.png.pngj.FilterType; +import org.xbib.graphics.imageio.plugins.png.pngj.ImageInfo; + +/** + * Special pixels writer for experimental super adaptive strategy + */ +public class PixelsWriterMultiple extends PixelsWriter { + /** + * unfiltered rowsperband elements, 0 is the current (rowb). This should + * include all rows of current band, plus one + */ + protected LinkedList rows; + + /** + * bank of compressor estimators, one for each filter and (perhaps) an + * adaptive strategy + */ + protected CompressorStream[] filterBank = new CompressorStream[6]; + /** + * stored filtered rows, one for each filter (0=none is not allocated but + * linked) + */ + protected byte[][] filteredRows = new byte[5][]; + protected byte[] filteredRowTmp; // + + protected FiltersPerformance filtersPerf; + protected int rowsPerBand = 0; // This is a 'nominal' size + protected int rowsPerBandCurrent = 0; // lastRowInThisBand-firstRowInThisBand +1 : might be smaller than rowsPerBand + protected int rowInBand = -1; + protected int bandNum = -1; + protected int firstRowInThisBand, lastRowInThisBand; + private boolean tryAdaptive = true; + + protected static final int HINT_MEMORY_DEFAULT_KB = 100; + // we will consume about (not more than) this memory (in buffers, not counting the compressors) + protected int hintMemoryKb = HINT_MEMORY_DEFAULT_KB; + + private int hintRowsPerBand = 1000; // default: very large number, can be changed + + private boolean useLz4 = true; + + public PixelsWriterMultiple(ImageInfo imgInfo) { + super(imgInfo); + filtersPerf = new FiltersPerformance(imgInfo); + rows = new LinkedList(); + for (int i = 0; i < 2; i++) { + rows.add(new byte[buflen]); // we preallocate 2 rows (rowb and rowbprev) + } + filteredRowTmp = new byte[buflen]; + } + + @Override + protected void filterAndWrite(byte[] rowb) { + if (!initdone) { + init(); + } + if (rowb != rows.get(0)) { + throw new RuntimeException("?"); + } + setBandFromNewRown(); + byte[] rowbprev = rows.get(1); + for (FilterType ftype : FilterType.getAllStandardNoneLast()) { + // this has a special behaviour for NONE: filteredRows[0] is null, and the returned value is rowb + if (currentRow == 0 && ftype != FilterType.FILTER_NONE && ftype != FilterType.FILTER_SUB) { + continue; + } + byte[] filtered = filterRowWithFilterType(ftype, rowb, rowbprev, filteredRows[ftype.val]); + filterBank[ftype.val].write(filtered); + if (currentRow == 0 && ftype == FilterType.FILTER_SUB) { // litle lie, only for first row + filterBank[FilterType.FILTER_PAETH.val].write(filtered); + filterBank[FilterType.FILTER_AVERAGE.val].write(filtered); + filterBank[FilterType.FILTER_UP.val].write(filtered); + } + // adptive: report each filterted + if (tryAdaptive) { + filtersPerf.updateFromFiltered(ftype, filtered, currentRow); + } + } + filteredRows[0] = rowb; + if (tryAdaptive) { + FilterType preferredAdaptive = filtersPerf.getPreferred(); + filterBank[5].write(filteredRows[preferredAdaptive.val]); + } + if (currentRow == lastRowInThisBand) { + int best = getBestCompressor(); + // PngHelperInternal.debug("won: " + best + " (rows: " + firstRowInThisBand +":" + lastRowInThisBand + ")"); + //if(currentRow>90&¤tRow<100) PngHelperInternal.debug(String.format("row=%d ft=%s",currentRow,FilterType.getByVal(best))); + byte[] filtersAdapt = filterBank[best].getFirstBytes(); + for (int r = firstRowInThisBand, i = 0, j = lastRowInThisBand + - firstRowInThisBand; r <= lastRowInThisBand; r++, j--, i++) { + int fti = filtersAdapt[i]; + byte[] filtered = null; + if (r != lastRowInThisBand) { + filtered = filterRowWithFilterType(FilterType.getByVal(fti), rows.get(j), rows.get(j + 1), + filteredRowTmp); + } else { // no need to do this filtering, we already have it + filtered = filteredRows[fti]; + } + sendToCompressedStream(filtered); + } + } + // rotate + if (rows.size() > rowsPerBandCurrent) { + rows.addFirst(rows.removeLast()); + } else { + rows.addFirst(new byte[buflen]); + } + } + + @Override + public byte[] getRowb() { + return rows.get(0); + } + + private void setBandFromNewRown() { + boolean newBand = currentRow == 0 || currentRow > lastRowInThisBand; + if (currentRow == 0) { + bandNum = -1; + } + if (newBand) { + bandNum++; + rowInBand = 0; + } else { + rowInBand++; + } + if (newBand) { + firstRowInThisBand = currentRow; + lastRowInThisBand = firstRowInThisBand + rowsPerBand - 1; + int lastRowInNextBand = firstRowInThisBand + 2 * rowsPerBand - 1; + if (lastRowInNextBand >= imgInfo.rows) // hack:make this band bigger, so we don't have a small last band + { + lastRowInThisBand = imgInfo.rows - 1; + } + rowsPerBandCurrent = 1 + lastRowInThisBand - firstRowInThisBand; + tryAdaptive = rowsPerBandCurrent > 3 && (rowsPerBandCurrent >= 10 || imgInfo.bytesPerRow >= 64); + // rebuild bank + rebuildFiltersBank(); + } + } + + private void rebuildFiltersBank() { + long bytesPerBandCurrent = rowsPerBandCurrent * (long) buflen; + final int DEFLATER_COMP_LEVEL = 4; + for (int i = 0; i <= 5; i++) {// one for each filter plus one adaptive + CompressorStream cp = filterBank[i]; + if (cp == null || cp.totalbytes != bytesPerBandCurrent) { + if (cp != null) { + cp.close(); + } + if (useLz4) { + cp = new CompressorStreamLz4(null, buflen, bytesPerBandCurrent); + } else { + cp = new CompressorStreamDeflater(null, buflen, bytesPerBandCurrent, DEFLATER_COMP_LEVEL, + Deflater.DEFAULT_STRATEGY); + } + filterBank[i] = cp; + } else { + cp.reset(); + } + cp.setStoreFirstByte(true, rowsPerBandCurrent); // TODO: only for adaptive? + } + } + + private int computeInitialRowsPerBand() { + // memory (only buffers) ~ (r+1+5) * bytesPerRow + int r = (int) ((hintMemoryKb * 1024.0) / (imgInfo.bytesPerRow + 1) - 5); + if (r < 1) { + r = 1; + } + if (hintRowsPerBand > 0 && r > hintRowsPerBand) { + r = hintRowsPerBand; + } + if (r > imgInfo.rows) { + r = imgInfo.rows; + } + if (r > 2 && r > imgInfo.rows / 8) { // redistribute more evenly + int k = (imgInfo.rows + (r - 1)) / r; + r = (imgInfo.rows + k / 2) / k; + } + // PngHelperInternal.debug("rows :" + r + "/" + imgInfo.rows); + return r; + } + + private int getBestCompressor() { + double bestcr = Double.MAX_VALUE; + int bestb = -1; + for (int i = tryAdaptive ? 5 : 4; i >= 0; i--) { + CompressorStream fb = filterBank[i]; + double cr = fb.getCompressionRatio(); + if (cr <= bestcr) { // dirty trick, here the equality gains for row 0, so that SUB is prefered over PAETH, UP, AVE... + bestb = i; + bestcr = cr; + } + } + return bestb; + } + + @Override + protected void initParams() { + super.initParams(); + // if adaptative but too few rows or columns, use default + if (imgInfo.cols < 3 && !FilterType.isValidStandard(filterType)) { + filterType = FilterType.FILTER_DEFAULT; + } + if (imgInfo.rows < 3 && !FilterType.isValidStandard(filterType)) { + filterType = FilterType.FILTER_DEFAULT; + } + for (int i = 1; i <= 4; i++) { // element 0 is not allocated + if (filteredRows[i] == null || filteredRows[i].length < buflen) { + filteredRows[i] = new byte[buflen]; + } + } + if (rowsPerBand == 0) { + rowsPerBand = computeInitialRowsPerBand(); + } + } + + @Override + public void close() { + super.close(); + rows.clear(); + for (CompressorStream f : filterBank) { + f.close(); + } + } + + public void setHintMemoryKb(int hintMemoryKb) { + this.hintMemoryKb = hintMemoryKb <= 0 ? HINT_MEMORY_DEFAULT_KB : (hintMemoryKb > 10000 ? 10000 : hintMemoryKb); + } + + public void setHintRowsPerBand(int hintRowsPerBand) { + this.hintRowsPerBand = hintRowsPerBand; + } + + public void setUseLz4(boolean lz4) { + this.useLz4 = lz4; + } + + /** + * for tuning memory or other parameters + */ + public FiltersPerformance getFiltersPerf() { + return filtersPerf; + } + + public void setTryAdaptive(boolean tryAdaptive) { + this.tryAdaptive = tryAdaptive; + } + +} diff --git a/png/src/main/resources/META-INF/services/javax.imageio.spi.ImageWriterSpi b/png/src/main/resources/META-INF/services/javax.imageio.spi.ImageWriterSpi new file mode 100644 index 0000000..883ad6e --- /dev/null +++ b/png/src/main/resources/META-INF/services/javax.imageio.spi.ImageWriterSpi @@ -0,0 +1 @@ +o \ No newline at end of file diff --git a/png/src/test/java/org/xbib/graphics/imageio/plugins/png/BufferedImageChildTest.java b/png/src/test/java/org/xbib/graphics/imageio/plugins/png/BufferedImageChildTest.java new file mode 100644 index 0000000..38108cb --- /dev/null +++ b/png/src/test/java/org/xbib/graphics/imageio/plugins/png/BufferedImageChildTest.java @@ -0,0 +1,62 @@ +package org.xbib.graphics.imageio.plugins.png; + +import org.junit.jupiter.api.Test; +import org.xbib.graphics.imageio.plugins.png.pngj.FilterType; +import java.awt.Color; +import java.awt.Graphics; +import java.awt.image.BufferedImage; +import java.io.ByteArrayInputStream; +import java.io.ByteArrayOutputStream; +import javax.imageio.ImageIO; + +public class BufferedImageChildTest { + + BufferedImage getSample() { + BufferedImage bi = new BufferedImage(100, 100, BufferedImage.TYPE_4BYTE_ABGR); + Graphics graphics = bi.getGraphics(); + graphics.setColor(Color.WHITE); + graphics.fillRect(0, 0, 25, 25); + graphics.setColor(Color.BLUE); + graphics.fillRect(25, 0, 25, 25); + graphics.setColor(Color.YELLOW); + graphics.fillRect(0, 25, 25, 25); + graphics.setColor(Color.RED); + graphics.fillRect(25, 25, 25, 25); + graphics.dispose(); + return bi; + } + + @Test + public void testSmallerSameOrigin() throws Exception { + testSubImage(0, 0, 25, 25); + } + + @Test + public void testSmallerTranslateX() throws Exception { + testSubImage(25, 0, 25, 25); + } + + @Test + public void testSmallerTranslateY() throws Exception { + testSubImage(0, 25, 25, 25); + } + + @Test + public void testSmallerTranslateXY() throws Exception { + testSubImage(25, 25, 25, 25); + } + + private void testSubImage(int x, int y, int w, int h) throws Exception { + BufferedImage bi = getSample(); + // ImageAssert.showImage("Original", 2000, bi); + BufferedImage subimage = bi.getSubimage(x, y, w, h); + // ImageAssert.showImage("Subimage", 2000, subimage); + ByteArrayOutputStream bos = new ByteArrayOutputStream(); + float quality = 4f / 9 - 1; + new PNGWriter().writePNG(subimage, bos, -quality, FilterType.FILTER_NONE); + ByteArrayInputStream bis = new ByteArrayInputStream(bos.toByteArray()); + BufferedImage readBack = ImageIO.read(bis); + // ImageAssert.showImage("ReadBack", 2000, readBack); + ImageAssert.assertImagesEqual(subimage, readBack); + } +} diff --git a/png/src/test/java/org/xbib/graphics/imageio/plugins/png/BufferedImageTypesTest.java b/png/src/test/java/org/xbib/graphics/imageio/plugins/png/BufferedImageTypesTest.java new file mode 100644 index 0000000..3d4c062 --- /dev/null +++ b/png/src/test/java/org/xbib/graphics/imageio/plugins/png/BufferedImageTypesTest.java @@ -0,0 +1,67 @@ +package org.xbib.graphics.imageio.plugins.png; + +import org.junit.jupiter.api.Test; +import org.xbib.graphics.imageio.plugins.png.pngj.FilterType; +import java.awt.image.BufferedImage; +import java.io.ByteArrayInputStream; +import java.io.ByteArrayOutputStream; +import java.io.File; +import java.lang.reflect.Field; +import java.util.ArrayList; +import java.util.Collection; +import java.util.List; +import javax.imageio.ImageIO; + +public class BufferedImageTypesTest { + + static final int WIDTH = 1024; + + static final int HEIGTH = 1024; + + static final int STROKE_WIDTH = 30; + + static final int LINES = 200; + + BufferedImage image; + + String name; + + public BufferedImageTypesTest(String name, int imageType) { + this.name = name; + image = new BufferedImage(WIDTH, HEIGTH, imageType); + new SampleImagePainter().paintImage(image); + } + + //@Parameters(name = "{0}") + public static Collection parameters() throws Exception { + String[] types = new String[]{"4BYTE_ABGR", "INT_ARGB", "3BYTE_BGR", "INT_BGR", + "INT_RGB", "BYTE_INDEXED", "BYTE_GRAY"}; + List parameters = new ArrayList(); + for (String type : types) { + Field field = BufferedImage.class.getDeclaredField("TYPE_" + type); + int imageType = (Integer) field.get(null); + parameters.add(new Object[]{type.toLowerCase(), imageType}); + } + return parameters; + } + + @Test + public void compareImage() throws Exception { + ByteArrayOutputStream bos = new ByteArrayOutputStream(); + float quality = 4f / 9 - 1; + new PNGWriter().writePNG(image, bos, -quality, FilterType.FILTER_NONE); + ByteArrayInputStream bis = new ByteArrayInputStream(bos.toByteArray()); + BufferedImage readBack = ImageIO.read(bis); + + boolean success = false; + try { + ImageAssert.assertImagesEqual(image, readBack); + success = true; + } finally { + if (!success) { + ImageIO.write(image, "PNG", new File("./build/" + name + "_expected.png")); + ImageIO.write(readBack, "PNG", new File("./build/" + name + "_actual.png")); + } + } + } +} diff --git a/png/src/test/java/org/xbib/graphics/imageio/plugins/png/CustomByteIndexImageTypesTest.java b/png/src/test/java/org/xbib/graphics/imageio/plugins/png/CustomByteIndexImageTypesTest.java new file mode 100644 index 0000000..3d8ffdd --- /dev/null +++ b/png/src/test/java/org/xbib/graphics/imageio/plugins/png/CustomByteIndexImageTypesTest.java @@ -0,0 +1,88 @@ +package org.xbib.graphics.imageio.plugins.png; + +import org.junit.jupiter.api.Test; +import org.xbib.graphics.imageio.plugins.png.pngj.FilterType; +import java.awt.Color; +import java.awt.Graphics2D; +import java.awt.Point; +import java.awt.image.BufferedImage; +import java.awt.image.DataBuffer; +import java.awt.image.DataBufferByte; +import java.awt.image.IndexColorModel; +import java.awt.image.MultiPixelPackedSampleModel; +import java.awt.image.Raster; +import java.awt.image.SampleModel; +import java.awt.image.WritableRaster; +import java.io.ByteArrayInputStream; +import java.io.ByteArrayOutputStream; +import java.util.ArrayList; +import java.util.Collection; +import java.util.List; +import javax.imageio.ImageIO; + +public class CustomByteIndexImageTypesTest { + + private final int ncolors; + + private final int size; + + public CustomByteIndexImageTypesTest(int ncolors, int size) { + this.ncolors = ncolors; + this.size = size; + } + + //@Parameters(name = "colors{0}/size{1}") + public static Collection parameters() { + List result = new ArrayList(); + for (int ncolors : new int[]{1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, + 19, 23, 29, 31, 37, 41, 43, 47, 53, 59, 61, 67, 71, 73, 79, 83, 89, 97, 101, 103, + 107, 109, 113, 127, 131, 137, 139, 149, 151, 157, 163, 167, 173, 179, 181, 191, 193, + 197, 199, 211, 223, 227, 229, 233, 239, 241, 255, 256}) { + for (int size = 1; size <= 8; size++) { + result.add(new Object[]{ncolors, size}); + } + } + + return result; + } + + @Test + public void testCustomIndexedImage() throws Exception { + byte[] colors = new byte[ncolors]; + for (int i = 0; i < ncolors; i++) { + colors[i] = (byte) i; + } + int nbits; + if (ncolors <= 2) { + nbits = 1; + } else { + nbits = (int) Math.ceil(Math.log(ncolors) / Math.log(2)); + if ((nbits & (nbits - 1)) != 0) { + int nextPower = (int) (Math.floor(Math.log(nbits) / Math.log(2)) + 1); + nbits = (int) Math.pow(2, nextPower); + } + } + + IndexColorModel icm = new IndexColorModel(nbits, ncolors, colors, colors, colors); + SampleModel sm = new MultiPixelPackedSampleModel(DataBuffer.TYPE_BYTE, size, size, nbits); + int pixelsPerByte = 8 / nbits; + int bytesPerRow = (int) Math.max(1, Math.ceil(1d * size / pixelsPerByte)); + int bytes = bytesPerRow * size; + DataBufferByte dataBuffer = new DataBufferByte(bytes); + WritableRaster wr = Raster.createWritableRaster(sm, dataBuffer, new Point(0, 0)); + BufferedImage bi = new BufferedImage(icm, wr, false, null); + Graphics2D graphics = bi.createGraphics(); + graphics.setColor(Color.BLACK); + graphics.fillRect(0, 0, 16, 32); + graphics.setColor(Color.WHITE); + graphics.fillRect(16, 0, 16, 32); + graphics.dispose(); + + ByteArrayOutputStream bos = new ByteArrayOutputStream(); + float quality = 5f / 9 - 1; + new PNGWriter().writePNG(bi, bos, -quality, FilterType.FILTER_NONE); + + BufferedImage read = ImageIO.read(new ByteArrayInputStream(bos.toByteArray())); + ImageAssert.assertImagesEqual(bi, read); + } +} diff --git a/png/src/test/java/org/xbib/graphics/imageio/plugins/png/CustomUShortImageTypesTest.java b/png/src/test/java/org/xbib/graphics/imageio/plugins/png/CustomUShortImageTypesTest.java new file mode 100644 index 0000000..b101298 --- /dev/null +++ b/png/src/test/java/org/xbib/graphics/imageio/plugins/png/CustomUShortImageTypesTest.java @@ -0,0 +1,57 @@ +package org.xbib.graphics.imageio.plugins.png; + +import org.junit.jupiter.api.Test; +import org.xbib.graphics.imageio.plugins.png.pngj.FilterType; +import java.awt.Color; +import java.awt.Graphics2D; +import java.awt.image.BufferedImage; +import java.awt.image.DataBuffer; +import java.io.ByteArrayInputStream; +import java.io.ByteArrayOutputStream; +import java.util.ArrayList; +import java.util.Collection; +import java.util.List; +import javax.imageio.ImageIO; +import javax.imageio.ImageTypeSpecifier; + +public class CustomUShortImageTypesTest { + + private final int nbits; + private final int size; + + public CustomUShortImageTypesTest(int nbits, int size) { + this.nbits = nbits; + this.size = size; + } + + //@Parameters(name = "bits{0}/size{1}") + public static Collection parameters() { + List result = new ArrayList(); + for (int nbits : new int[]{1, 2, 4, 8, 16}) { + for (int size = 1; size <= 32; size++) { + result.add(new Object[]{nbits, size}); + } + } + + return result; + } + + @Test + public void testCustomUShortImage() throws Exception { + BufferedImage bi = ImageTypeSpecifier.createGrayscale(nbits, DataBuffer.TYPE_USHORT, false) + .createBufferedImage(size, size); + Graphics2D graphics = bi.createGraphics(); + graphics.setColor(Color.BLACK); + graphics.fillRect(0, 0, 16, 32); + graphics.setColor(Color.WHITE); + graphics.fillRect(16, 0, 16, 32); + graphics.dispose(); + + ByteArrayOutputStream bos = new ByteArrayOutputStream(); + float quality = 5f / 9 - 1; + new PNGWriter().writePNG(bi, bos, -quality, FilterType.FILTER_NONE); + + BufferedImage read = ImageIO.read(new ByteArrayInputStream(bos.toByteArray())); + ImageAssert.assertImagesEqual(bi, read); + } +} diff --git a/png/src/test/java/org/xbib/graphics/imageio/plugins/png/ImageAssert.java b/png/src/test/java/org/xbib/graphics/imageio/plugins/png/ImageAssert.java new file mode 100644 index 0000000..fd38bb8 --- /dev/null +++ b/png/src/test/java/org/xbib/graphics/imageio/plugins/png/ImageAssert.java @@ -0,0 +1,84 @@ + +package org.xbib.graphics.imageio.plugins.png; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.fail; +import java.awt.Color; +import java.awt.Dimension; +import java.awt.Frame; +import java.awt.Graphics; +import java.awt.HeadlessException; +import java.awt.Panel; +import java.awt.event.WindowAdapter; +import java.awt.event.WindowEvent; +import java.awt.image.BufferedImage; + +public class ImageAssert { + + public static void assertImagesEqual(BufferedImage original, BufferedImage image) { + assertEquals(original.getWidth(), image.getWidth()); + assertEquals(original.getHeight(), image.getHeight()); + // these tests got disabled, as depending on the reader being used you can get a different + // structure back + // assertEquals(original.getSampleModel(), image.getSampleModel()); + // assertEquals(original.getColorModel(), image.getColorModel()); + + for (int x = 0; x < original.getWidth(); x++) { + for (int y = 0; y < original.getHeight(); y++) { + int rgbOriginal = original.getRGB(x, y); + int rgbActual = image.getRGB(x, y); + if (rgbOriginal != rgbActual) { + fail("Comparison failed at x:" + x + ", y: " + y + ", expected " + + colorToString(rgbOriginal) + ", got " + colorToString(rgbActual)); + } + } + } + } + + private static String colorToString(int rgb) { + Color c = new Color(rgb); + return "RGBA[" + c.getRed() + ", " + c.getGreen() + ", " + c.getBlue() + ", " + + c.getAlpha() + "]"; + } + + public static void showImage(String title, long timeOut, final BufferedImage image) + throws InterruptedException { + final String headless = System.getProperty("java.awt.headless", "false"); + if (!headless.equalsIgnoreCase("true")) { + try { + Frame frame = new Frame(title); + frame.addWindowListener(new WindowAdapter() { + + public void windowClosing(WindowEvent e) { + e.getWindow().dispose(); + } + }); + + Panel p = new Panel() { + + /** serialVersionUID field */ + private static final long serialVersionUID = 1L; + + { + setPreferredSize(new Dimension(image.getWidth(), image.getHeight())); + } + + public void paint(Graphics g) { + g.drawImage(image, 0, 0, this); + } + + }; + + frame.add(p); + frame.pack(); + frame.setVisible(true); + + Thread.sleep(timeOut); + frame.dispose(); + } catch (HeadlessException exception) { + // The test is running on a machine without X11 display. Ignore. + } + } + } + +} diff --git a/png/src/test/java/org/xbib/graphics/imageio/plugins/png/PNGWriterTest.java b/png/src/test/java/org/xbib/graphics/imageio/plugins/png/PNGWriterTest.java new file mode 100644 index 0000000..da02933 --- /dev/null +++ b/png/src/test/java/org/xbib/graphics/imageio/plugins/png/PNGWriterTest.java @@ -0,0 +1,76 @@ +package org.xbib.graphics.imageio.plugins.png; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertNotNull; +import org.junit.jupiter.api.Test; +import org.xbib.graphics.imageio.plugins.png.pngj.FilterType; +import org.xbib.graphics.imageio.plugins.png.pngj.PngReader; +import org.xbib.graphics.imageio.plugins.png.pngj.chunks.PngMetadata; +import java.awt.image.BufferedImage; +import java.io.File; +import java.io.FileOutputStream; +import java.io.IOException; +import java.io.OutputStream; +import java.util.HashMap; +import java.util.Map; +import javax.imageio.ImageIO; + +public class PNGWriterTest { + + @Test + public void testWriter() throws Exception { + PNGWriter writer = new PNGWriter(); + OutputStream out = null; + try { + // read test image + BufferedImage read = ImageIO.read(new File("sample.jpeg")); + File pngOut = new File("build/test.png"); + out = new FileOutputStream(pngOut); + writer.writePNG(read, out, 1, FilterType.FILTER_NONE); + BufferedImage test = ImageIO.read(pngOut); + assertNotNull(test); + } finally { + if (out != null) { + out.close(); + } + } + } + + @Test + public void testTeXt() throws Exception { + PNGWriter writer = new PNGWriter(); + OutputStream out = null; + File pngOut = null; + final String title = "Title"; + final String description = "Sample Description"; + final String software = "ImageIO-Ext"; + final String author = "Me"; + try { + BufferedImage read = ImageIO.read(new File("sample.jpeg")); + pngOut = new File("build/test.png"); + out = new FileOutputStream(pngOut); + Map textMetadata = new HashMap(); + textMetadata.put("Title", title); + textMetadata.put("Author", author); + textMetadata.put("Software", software); + textMetadata.put("Description", description); + + writer.writePNG(read, out, 1, FilterType.FILTER_NONE, textMetadata); + } finally { + if (out != null) { + out.close(); + } + } + BufferedImage test = ImageIO.read(pngOut); + assertNotNull(test); + try (PngReader reader = new PngReader(pngOut)) { + reader.readSkippingAllRows(); + PngMetadata metadata = reader.getMetadata(); + assertNotNull(metadata); + assertEquals(title, metadata.getTxtForKey("Title")); + assertEquals(description, metadata.getTxtForKey("Description")); + assertEquals(author, metadata.getTxtForKey("Author")); + assertEquals(software, metadata.getTxtForKey("Software")); + } + } +} diff --git a/png/src/test/java/org/xbib/graphics/imageio/plugins/png/PngSuiteImagesTest.java b/png/src/test/java/org/xbib/graphics/imageio/plugins/png/PngSuiteImagesTest.java new file mode 100644 index 0000000..06bcbb4 --- /dev/null +++ b/png/src/test/java/org/xbib/graphics/imageio/plugins/png/PngSuiteImagesTest.java @@ -0,0 +1,84 @@ + +package org.xbib.graphics.imageio.plugins.png; + +import org.junit.jupiter.api.Test; +import org.xbib.graphics.imageio.plugins.png.pngj.FilterType; +import java.awt.image.BufferedImage; +import java.io.ByteArrayInputStream; +import java.io.ByteArrayOutputStream; +import java.io.File; +import java.io.FileOutputStream; +import java.io.FilenameFilter; +import java.io.IOException; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collection; +import java.util.List; +import javax.imageio.ImageIO; + +public class PngSuiteImagesTest { + + private final File sourceFile; + + public PngSuiteImagesTest(File sourceFile) { + this.sourceFile = sourceFile; + } + + //@Parameters(name = "{0}") + public static Collection parameters() { + List result = new ArrayList(); + File source = new File("./src/test/resources/pngsuite"); + File[] files = source.listFiles(new FilenameFilter() { + + @Override + public boolean accept(File dir, String name) { + return name.endsWith(".png"); + } + }); + Arrays.sort(files); + for (File file : files) { + result.add(new Object[]{file}); + } + + return result; + } + + @Test + public void testRoundTripFilterNone() throws Exception { + BufferedImage input = ImageIO.read(sourceFile); + roundTripPNGJ(input); + } + + @Test + public void testRoundTripTiledImage() throws Exception { + BufferedImage input = ImageIO.read(sourceFile); + roundTripPNGJ(input); + } + + private void roundTripPNGJ(BufferedImage original) throws Exception { + ByteArrayOutputStream bos = new ByteArrayOutputStream(); + float quality = 4f / 9 - 1; + new PNGWriter().writePNG(original, bos, -quality, FilterType.FILTER_NONE); + // write the output to file for eventual visual comparison + byte[] bytes = bos.toByteArray(); + writeToFile(new File("./build/roundTripNone", sourceFile.getName()), bytes); + ByteArrayInputStream bis = new ByteArrayInputStream(bytes); + BufferedImage image = ImageIO.read(bis); + ImageAssert.assertImagesEqual(original, image); + } + + private void writeToFile(File file, byte[] bytes) throws IOException { + File parent = file.getParentFile(); + parent.mkdirs(); + FileOutputStream fos = null; + try { + fos = new FileOutputStream(file); + fos.write(bytes); + } finally { + if (fos != null) { + fos.close(); + } + } + } + +} diff --git a/png/src/test/java/org/xbib/graphics/imageio/plugins/png/SampleImagePainter.java b/png/src/test/java/org/xbib/graphics/imageio/plugins/png/SampleImagePainter.java new file mode 100644 index 0000000..fc57873 --- /dev/null +++ b/png/src/test/java/org/xbib/graphics/imageio/plugins/png/SampleImagePainter.java @@ -0,0 +1,82 @@ +package org.xbib.graphics.imageio.plugins.png; + +import java.awt.BasicStroke; +import java.awt.Color; +import java.awt.Font; +import java.awt.Graphics2D; +import java.awt.RenderingHints; +import java.awt.image.BufferedImage; +import java.util.HashMap; +import java.util.Map; +import java.util.Random; + +/** + * Helper painting a random buffered image, to be used for tests + */ +class SampleImagePainter { + + int lines = 200; + + int labels = 50; + + int strokeWidth = 30; + + public void paintImage(BufferedImage image) { + Graphics2D g = image.createGraphics(); + + // setup some basic rendering hints, mimicks the output we'd get from GeoServer + final Map hintsMap = new HashMap(); + hintsMap.put(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON); + hintsMap.put(RenderingHints.KEY_TEXT_ANTIALIASING, RenderingHints.VALUE_TEXT_ANTIALIAS_ON); + hintsMap.put(RenderingHints.KEY_FRACTIONALMETRICS, + RenderingHints.VALUE_FRACTIONALMETRICS_ON); + hintsMap.put(RenderingHints.KEY_STROKE_CONTROL, RenderingHints.VALUE_STROKE_PURE); + g.setRenderingHints(hintsMap); + + Random random = new Random(0); + for (int i = 0; i < lines; i++) { + int x1 = (int) (random.nextDouble() * image.getWidth()); + int y1 = (int) (random.nextDouble() * image.getHeight()); + int x2 = (int) (random.nextDouble() * image.getWidth()); + int y2 = (int) (random.nextDouble() * image.getHeight()); + int w = (int) (random.nextDouble() * (strokeWidth - 1) + 1); + g.setStroke(new BasicStroke(w)); + g.setColor(new Color((int) (random.nextDouble() * Integer.MAX_VALUE))); + g.drawLine(x1, y1, x2, y2); + } + + g.setColor(Color.BLACK); + g.setFont(new Font("Arial", Font.BOLD, 32)); + for (int i = 0; i < labels; i++) { + int x1 = (int) (random.nextDouble() * image.getWidth()); + int y1 = (int) (random.nextDouble() * image.getHeight()); + g.drawString("TestLabel", x1, y1); + } + + g.dispose(); + } + + public int getLines() { + return lines; + } + + public void setLines(int lines) { + this.lines = lines; + } + + public int getStrokeWidth() { + return strokeWidth; + } + + public void setStrokeWidth(int strokeWidth) { + this.strokeWidth = strokeWidth; + } + + public int getLabels() { + return labels; + } + + public void setLabels(int labels) { + this.labels = labels; + } +} diff --git a/settings.gradle b/settings.gradle new file mode 100644 index 0000000..29bb9ff --- /dev/null +++ b/settings.gradle @@ -0,0 +1,5 @@ +include 'png' +include 'io-vector' +include 'chart' +include 'barcode' +include 'layout-pdfbox' \ No newline at end of file diff --git a/src/main/java/com/sun/imageio/plugins/png/PNGImageWriterBackport.java b/src/main/java/com/sun/imageio/plugins/png/PNGImageWriterBackport.java deleted file mode 100644 index 9e242a6..0000000 --- a/src/main/java/com/sun/imageio/plugins/png/PNGImageWriterBackport.java +++ /dev/null @@ -1,1274 +0,0 @@ -/* - * Copyright (c) 2000, 2005, Oracle and/or its affiliates. All rights reserved. - * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. - * - * This code is free software; you can redistribute it and/or modify it - * under the terms of the GNU General Public License version 2 only, as - * published by the Free Software Foundation. Oracle designates this - * particular file as subject to the "Classpath" exception as provided - * by Oracle in the LICENSE file that accompanied this code. - * - * This code is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License - * version 2 for more details (a copy is included in the LICENSE file that - * accompanied this code). - * - * You should have received a copy of the GNU General Public License version - * 2 along with this work; if not, write to the Free Software Foundation, - * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. - * - * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA - * or visit www.oracle.com if you need additional information or have any - * questions. - */ -package com.sun.imageio.plugins.png; - -import javax.imageio.IIOException; -import javax.imageio.IIOImage; -import javax.imageio.ImageTypeSpecifier; -import javax.imageio.ImageWriteParam; -import javax.imageio.ImageWriter; -import javax.imageio.metadata.IIOMetadata; -import javax.imageio.spi.ImageWriterSpi; -import javax.imageio.stream.ImageOutputStream; -import javax.imageio.stream.ImageOutputStreamImpl; -import java.awt.Rectangle; -import java.awt.image.IndexColorModel; -import java.awt.image.Raster; -import java.awt.image.RenderedImage; -import java.awt.image.SampleModel; -import java.awt.image.WritableRaster; -import java.io.ByteArrayOutputStream; -import java.io.IOException; -import java.util.Iterator; -import java.util.Locale; -import java.util.zip.Deflater; -import java.util.zip.DeflaterOutputStream; - -final class CRCBackport { - - private static final int[] crcTable = new int[256]; - - static { - // Initialize CRC table - for (int n = 0; n < 256; n++) { - int c = n; - for (int k = 0; k < 8; k++) { - if ((c & 1) == 1) { - c = 0xedb88320 ^ (c >>> 1); - } else { - c >>>= 1; - } - - crcTable[n] = c; - } - } - } - - private int crc = 0xffffffff; - - CRCBackport() { - } - - void reset() { - crc = 0xffffffff; - } - - void update(byte[] data, int off, int len) { - int c = crc; - for (int n = 0; n < len; n++) { - c = crcTable[(c ^ data[off + n]) & 0xff] ^ (c >>> 8); - } - crc = c; - } - - void update(int data) { - crc = crcTable[(crc ^ data) & 0xff] ^ (crc >>> 8); - } - - int getValue() { - return ~crc; - } -} - -final class ChunkStreamBackport extends ImageOutputStreamImpl { - - private final ImageOutputStream stream; - - private final long startPos; - - private final CRCBackport crc = new CRCBackport(); - - ChunkStreamBackport(int type, ImageOutputStream stream) throws IOException { - this.stream = stream; - this.startPos = stream.getStreamPosition(); - - stream.writeInt(-1); // length, will backpatch - writeInt(type); - } - - @Override - public int read() { - throw new UnsupportedOperationException("Method not available"); - } - - @Override - public int read(byte[] b, int off, int len) { - throw new UnsupportedOperationException("Method not available"); - } - - @Override - public void write(byte[] b, int off, int len) throws IOException { - crc.update(b, off, len); - stream.write(b, off, len); - } - - @Override - public void write(int b) throws IOException { - crc.update(b); - stream.write(b); - } - - void finish() throws IOException { - // Write CRC - stream.writeInt(crc.getValue()); - - // Write length - long pos = stream.getStreamPosition(); - stream.seek(startPos); - stream.writeInt((int) (pos - startPos) - 12); - - // Return to end of chunk and flush to minimize buffering - stream.seek(pos); - stream.flushBefore(pos); - } - - @Override - protected void finalize() throws Throwable { - // Empty finalizer (for improved performance; no need to call - // super.finalize() in this case) - } -} - -/* - * Compress output and write as a series of 'IDAT' chunks of - * fixed length. - */ -final class IDATOutputStreamBackport extends ImageOutputStreamImpl { - - private static final byte[] chunkType = { - (byte) 'I', (byte) 'D', (byte) 'A', (byte) 'T' - }; - - private final ImageOutputStream stream; - private final int chunkLength; - private final CRCBackport crc = new CRCBackport(); - private final Deflater def; - private final byte[] buf = new byte[512]; - // reused 1 byte[] array: - private final byte[] wbuf1 = new byte[1]; - private long startPos; - private int bytesRemaining; - - IDATOutputStreamBackport(ImageOutputStream stream, int chunkLength, - int deflaterLevel) throws IOException { - this.stream = stream; - this.chunkLength = chunkLength; - this.def = new Deflater(deflaterLevel); - - startChunk(); - } - - private void startChunk() throws IOException { - crc.reset(); - this.startPos = stream.getStreamPosition(); - stream.writeInt(-1); // length, will backpatch - - crc.update(chunkType, 0, 4); - stream.write(chunkType, 0, 4); - - this.bytesRemaining = chunkLength; - } - - private void finishChunk() throws IOException { - // Write CRC - stream.writeInt(crc.getValue()); - - // Write length - long pos = stream.getStreamPosition(); - stream.seek(startPos); - stream.writeInt((int) (pos - startPos) - 12); - - // Return to end of chunk and flush to minimize buffering - stream.seek(pos); - try { - stream.flushBefore(pos); - } catch (IOException e) { - /* - * If flushBefore() fails we try to access startPos in finally - * block of write_IDAT(). We should update startPos to avoid - * IndexOutOfBoundException while seek() is happening. - */ - this.startPos = stream.getStreamPosition(); - throw e; - } - } - - @Override - public int read() { - throw new UnsupportedOperationException("Method not available"); - } - - @Override - public int read(byte[] b, int off, int len) { - throw new UnsupportedOperationException("Method not available"); - } - - @Override - public void write(byte[] b, int off, int len) throws IOException { - if (len == 0) { - return; - } - - if (!def.finished()) { - def.setInput(b, off, len); - while (!def.needsInput()) { - deflate(); - } - } - } - - private void deflate() throws IOException { - int len = def.deflate(buf, 0, buf.length); - int off = 0; - - while (len > 0) { - if (bytesRemaining == 0) { - finishChunk(); - startChunk(); - } - - int nbytes = Math.min(len, bytesRemaining); - crc.update(buf, off, nbytes); - stream.write(buf, off, nbytes); - - off += nbytes; - len -= nbytes; - bytesRemaining -= nbytes; - } - } - - @Override - public void write(int b) throws IOException { - wbuf1[0] = (byte) b; - write(wbuf1, 0, 1); - } - - void finish() throws IOException { - try { - if (!def.finished()) { - def.finish(); - while (!def.finished()) { - deflate(); - } - } - finishChunk(); - } finally { - def.end(); - } - } - - @Override - protected void finalize() throws Throwable { - // Empty finalizer (for improved performance; no need to call - // super.finalize() in this case) - } -} - - -final class PNGImageWriteParamBackport extends ImageWriteParam { - - /** - * Default quality level = 0.5 ie medium compression - */ - private static final float DEFAULT_QUALITY = 0.5f; - - private static final String[] compressionNames = {"Deflate"}; - private static final float[] qualityVals = {0.00F, 0.30F, 0.75F, 1.00F}; - private static final String[] qualityDescs = { - "High compression", // 0.00 -> 0.30 - "Medium compression", // 0.30 -> 0.75 - "Low compression" // 0.75 -> 1.00 - }; - - PNGImageWriteParamBackport(Locale locale) { - super(); - this.canWriteProgressive = true; - this.locale = locale; - this.canWriteCompressed = true; - this.compressionTypes = compressionNames; - this.compressionType = compressionTypes[0]; - this.compressionMode = MODE_DEFAULT; - this.compressionQuality = DEFAULT_QUALITY; - } - - /** - * Removes any previous compression quality setting. - * The default implementation resets the compression quality - * to 0.5F. - * - * @throws IllegalStateException if the compression mode is not - * MODE_EXPLICIT. - */ - @Override - public void unsetCompression() { - super.unsetCompression(); - this.compressionType = compressionTypes[0]; - this.compressionQuality = DEFAULT_QUALITY; - } - - /** - * Returns true since the PNG plug-in only supports - * lossless compression. - * - * @return true. - */ - @Override - public boolean isCompressionLossless() { - return true; - } - - @Override - public String[] getCompressionQualityDescriptions() { - super.getCompressionQualityDescriptions(); - return qualityDescs.clone(); - } - - @Override - public float[] getCompressionQualityValues() { - super.getCompressionQualityValues(); - return qualityVals.clone(); - } -} - -/** - */ -public final class PNGImageWriterBackport extends ImageWriter { - - /** - * Default compression level = 4 ie medium compression - */ - private static final int DEFAULT_COMPRESSION_LEVEL = 4; - - private ImageOutputStream stream = null; - - private PNGMetadata metadata = null; - - // Factors from the ImageWriteParam - private int sourceXOffset = 0; - private int sourceYOffset = 0; - private int sourceWidth = 0; - private int sourceHeight = 0; - private int[] sourceBands = null; - private int periodX = 1; - private int periodY = 1; - - private int numBands; - private int bpp; - - private RowFilter rowFilter = new RowFilter(); - - // Per-band scaling tables - // - // After the first call to initializeScaleTables, either scale and scale0 - // will be valid, or scaleh and scalel will be valid, but not both. - // - // The tables will be designed for use with a set of input but depths - // given by sampleSize, and an output bit depth given by scalingBitDepth. - // - private int[] sampleSize = null; // Sample size per band, in bits - private int scalingBitDepth = -1; // Output bit depth of the scaling tables - - // Tables for 1, 2, 4, or 8 bit output - private byte[][] scale = null; // 8 bit table - private byte[] scale0 = null; // equivalent to scale[0] - - // Tables for 16 bit output - private byte[][] scaleh = null; // High bytes of output - private byte[][] scalel = null; // Low bytes of output - - private int totalPixels; // Total number of pixels to be written by write_IDAT - private int pixelsDone; // Running count of pixels written by write_IDAT - - PNGImageWriterBackport(ImageWriterSpi originatingProvider) { - super(originatingProvider); - } - - private static int chunkType(String typeString) { - char c0 = typeString.charAt(0); - char c1 = typeString.charAt(1); - char c2 = typeString.charAt(2); - char c3 = typeString.charAt(3); - - return (c0 << 24) | (c1 << 16) | (c2 << 8) | c3; - } - - @Override - public void setOutput(Object output) { - super.setOutput(output); - if (output != null) { - if (!(output instanceof ImageOutputStream)) { - throw new IllegalArgumentException("output not an ImageOutputStream!"); - } - this.stream = (ImageOutputStream) output; - } else { - this.stream = null; - } - } - - @Override - public ImageWriteParam getDefaultWriteParam() { - return new PNGImageWriteParamBackport(getLocale()); - } - - @Override - public IIOMetadata getDefaultStreamMetadata(ImageWriteParam param) { - return null; - } - - @Override - public IIOMetadata getDefaultImageMetadata(ImageTypeSpecifier imageType, - ImageWriteParam param) { - PNGMetadata m = new PNGMetadata(); - m.initialize(imageType, imageType.getSampleModel().getNumBands()); - return m; - } - - @Override - public IIOMetadata convertStreamMetadata(IIOMetadata inData, - ImageWriteParam param) { - return null; - } - - @Override - public IIOMetadata convertImageMetadata(IIOMetadata inData, - ImageTypeSpecifier imageType, - ImageWriteParam param) { - if (inData instanceof PNGMetadata) { - return (PNGMetadata) ((PNGMetadata) inData).clone(); - } else { - return new PNGMetadata(inData); - } - } - - private void write_magic() throws IOException { - // Write signature - byte[] magic = {(byte) 137, 80, 78, 71, 13, 10, 26, 10}; - stream.write(magic); - } - - private void write_IHDR() throws IOException { - // Write IHDR chunk - ChunkStreamBackport cs = new ChunkStreamBackport(PNGImageReader.IHDR_TYPE, stream); - cs.writeInt(metadata.IHDR_width); - cs.writeInt(metadata.IHDR_height); - cs.writeByte(metadata.IHDR_bitDepth); - cs.writeByte(metadata.IHDR_colorType); - if (metadata.IHDR_compressionMethod != 0) { - throw new IIOException( - "Only compression method 0 is defined in PNG 1.1"); - } - cs.writeByte(metadata.IHDR_compressionMethod); - if (metadata.IHDR_filterMethod != 0) { - throw new IIOException( - "Only filter method 0 is defined in PNG 1.1"); - } - cs.writeByte(metadata.IHDR_filterMethod); - if (metadata.IHDR_interlaceMethod < 0 || - metadata.IHDR_interlaceMethod > 1) { - throw new IIOException( - "Only interlace methods 0 (node) and 1 (adam7) are defined in PNG 1.1"); - } - cs.writeByte(metadata.IHDR_interlaceMethod); - cs.finish(); - } - - private void write_cHRM() throws IOException { - if (metadata.cHRM_present) { - ChunkStreamBackport cs = new ChunkStreamBackport(PNGImageReader.cHRM_TYPE, stream); - cs.writeInt(metadata.cHRM_whitePointX); - cs.writeInt(metadata.cHRM_whitePointY); - cs.writeInt(metadata.cHRM_redX); - cs.writeInt(metadata.cHRM_redY); - cs.writeInt(metadata.cHRM_greenX); - cs.writeInt(metadata.cHRM_greenY); - cs.writeInt(metadata.cHRM_blueX); - cs.writeInt(metadata.cHRM_blueY); - cs.finish(); - } - } - - private void write_gAMA() throws IOException { - if (metadata.gAMA_present) { - ChunkStreamBackport cs = new ChunkStreamBackport(PNGImageReader.gAMA_TYPE, stream); - cs.writeInt(metadata.gAMA_gamma); - cs.finish(); - } - } - - private void write_iCCP() throws IOException { - if (metadata.iCCP_present) { - ChunkStreamBackport cs = new ChunkStreamBackport(PNGImageReader.iCCP_TYPE, stream); - cs.writeBytes(metadata.iCCP_profileName); - cs.writeByte(0); // null terminator - - cs.writeByte(metadata.iCCP_compressionMethod); - cs.write(metadata.iCCP_compressedProfile); - cs.finish(); - } - } - - private void write_sBIT() throws IOException { - if (metadata.sBIT_present) { - ChunkStreamBackport cs = new ChunkStreamBackport(PNGImageReader.sBIT_TYPE, stream); - int colorType = metadata.IHDR_colorType; - if (metadata.sBIT_colorType != colorType) { - processWarningOccurred(0, - "sBIT metadata has wrong color type.\n" + - "The chunk will not be written."); - return; - } - - if (colorType == PNGImageReader.PNG_COLOR_GRAY || - colorType == PNGImageReader.PNG_COLOR_GRAY_ALPHA) { - cs.writeByte(metadata.sBIT_grayBits); - } else if (colorType == PNGImageReader.PNG_COLOR_RGB || - colorType == PNGImageReader.PNG_COLOR_PALETTE || - colorType == PNGImageReader.PNG_COLOR_RGB_ALPHA) { - cs.writeByte(metadata.sBIT_redBits); - cs.writeByte(metadata.sBIT_greenBits); - cs.writeByte(metadata.sBIT_blueBits); - } - - if (colorType == PNGImageReader.PNG_COLOR_GRAY_ALPHA || - colorType == PNGImageReader.PNG_COLOR_RGB_ALPHA) { - cs.writeByte(metadata.sBIT_alphaBits); - } - cs.finish(); - } - } - - private void write_sRGB() throws IOException { - if (metadata.sRGB_present) { - ChunkStreamBackport cs = new ChunkStreamBackport(PNGImageReader.sRGB_TYPE, stream); - cs.writeByte(metadata.sRGB_renderingIntent); - cs.finish(); - } - } - - private void write_PLTE() throws IOException { - if (metadata.PLTE_present) { - if (metadata.IHDR_colorType == PNGImageReader.PNG_COLOR_GRAY || - metadata.IHDR_colorType == PNGImageReader.PNG_COLOR_GRAY_ALPHA) { - // PLTE cannot occur in a gray image - - processWarningOccurred(0, - "A PLTE chunk may not appear in a gray or gray alpha image.\n" + - "The chunk will not be written"); - return; - } - - ChunkStreamBackport cs = new ChunkStreamBackport(PNGImageReader.PLTE_TYPE, stream); - - int numEntries = metadata.PLTE_red.length; - byte[] palette = new byte[numEntries * 3]; - int index = 0; - for (int i = 0; i < numEntries; i++) { - palette[index++] = metadata.PLTE_red[i]; - palette[index++] = metadata.PLTE_green[i]; - palette[index++] = metadata.PLTE_blue[i]; - } - - cs.write(palette); - cs.finish(); - } - } - - private void write_hIST() throws IOException { - if (metadata.hIST_present) { - ChunkStreamBackport cs = new ChunkStreamBackport(PNGImageReader.hIST_TYPE, stream); - - if (!metadata.PLTE_present) { - throw new IIOException("hIST chunk without PLTE chunk!"); - } - - cs.writeChars(metadata.hIST_histogram, - 0, metadata.hIST_histogram.length); - cs.finish(); - } - } - - private void write_tRNS() throws IOException { - if (metadata.tRNS_present) { - ChunkStreamBackport cs = new ChunkStreamBackport(PNGImageReader.tRNS_TYPE, stream); - int colorType = metadata.IHDR_colorType; - int chunkType = metadata.tRNS_colorType; - - // Special case: image is RGB and chunk is Gray - // Promote chunk contents to RGB - int chunkRed = metadata.tRNS_red; - int chunkGreen = metadata.tRNS_green; - int chunkBlue = metadata.tRNS_blue; - if (colorType == PNGImageReader.PNG_COLOR_RGB && - chunkType == PNGImageReader.PNG_COLOR_GRAY) { - chunkType = colorType; - chunkRed = chunkGreen = chunkBlue = - metadata.tRNS_gray; - } - - if (chunkType != colorType) { - processWarningOccurred(0, - "tRNS metadata has incompatible color type.\n" + - "The chunk will not be written."); - return; - } - - if (colorType == PNGImageReader.PNG_COLOR_PALETTE) { - if (!metadata.PLTE_present) { - throw new IIOException("tRNS chunk without PLTE chunk!"); - } - cs.write(metadata.tRNS_alpha); - } else if (colorType == PNGImageReader.PNG_COLOR_GRAY) { - cs.writeShort(metadata.tRNS_gray); - } else if (colorType == PNGImageReader.PNG_COLOR_RGB) { - cs.writeShort(chunkRed); - cs.writeShort(chunkGreen); - cs.writeShort(chunkBlue); - } else { - throw new IIOException("tRNS chunk for color type 4 or 6!"); - } - cs.finish(); - } - } - - private void write_bKGD() throws IOException { - if (metadata.bKGD_present) { - ChunkStreamBackport cs = new ChunkStreamBackport(PNGImageReader.bKGD_TYPE, stream); - int colorType = metadata.IHDR_colorType & 0x3; - int chunkType = metadata.bKGD_colorType; - - // Special case: image is RGB(A) and chunk is Gray - // Promote chunk contents to RGB - int chunkRed = metadata.bKGD_red; - int chunkGreen = metadata.bKGD_red; - int chunkBlue = metadata.bKGD_red; - if (colorType == PNGImageReader.PNG_COLOR_RGB && - chunkType == PNGImageReader.PNG_COLOR_GRAY) { - // Make a gray bKGD chunk look like RGB - chunkType = colorType; - chunkRed = chunkGreen = chunkBlue = - metadata.bKGD_gray; - } - - // Ignore status of alpha in colorType - if (chunkType != colorType) { - processWarningOccurred(0, - "bKGD metadata has incompatible color type.\n" + - "The chunk will not be written."); - return; - } - - if (colorType == PNGImageReader.PNG_COLOR_PALETTE) { - cs.writeByte(metadata.bKGD_index); - } else if (colorType == PNGImageReader.PNG_COLOR_GRAY) { - cs.writeShort(metadata.bKGD_gray); - } else { // colorType == PNGImageReader.PNG_COLOR_RGB || - // colorType == PNGImageReader.PNG_COLOR_RGB_ALPHA - cs.writeShort(chunkRed); - cs.writeShort(chunkGreen); - cs.writeShort(chunkBlue); - } - cs.finish(); - } - } - - private void write_pHYs() throws IOException { - if (metadata.pHYs_present) { - ChunkStreamBackport cs = new ChunkStreamBackport(PNGImageReader.pHYs_TYPE, stream); - cs.writeInt(metadata.pHYs_pixelsPerUnitXAxis); - cs.writeInt(metadata.pHYs_pixelsPerUnitYAxis); - cs.writeByte(metadata.pHYs_unitSpecifier); - cs.finish(); - } - } - - private void write_sPLT() throws IOException { - if (metadata.sPLT_present) { - ChunkStreamBackport cs = new ChunkStreamBackport(PNGImageReader.sPLT_TYPE, stream); - - cs.writeBytes(metadata.sPLT_paletteName); - cs.writeByte(0); // null terminator - - cs.writeByte(metadata.sPLT_sampleDepth); - int numEntries = metadata.sPLT_red.length; - - if (metadata.sPLT_sampleDepth == 8) { - for (int i = 0; i < numEntries; i++) { - cs.writeByte(metadata.sPLT_red[i]); - cs.writeByte(metadata.sPLT_green[i]); - cs.writeByte(metadata.sPLT_blue[i]); - cs.writeByte(metadata.sPLT_alpha[i]); - cs.writeShort(metadata.sPLT_frequency[i]); - } - } else { // sampleDepth == 16 - for (int i = 0; i < numEntries; i++) { - cs.writeShort(metadata.sPLT_red[i]); - cs.writeShort(metadata.sPLT_green[i]); - cs.writeShort(metadata.sPLT_blue[i]); - cs.writeShort(metadata.sPLT_alpha[i]); - cs.writeShort(metadata.sPLT_frequency[i]); - } - } - cs.finish(); - } - } - - private void write_tIME() throws IOException { - if (metadata.tIME_present) { - ChunkStreamBackport cs = new ChunkStreamBackport(PNGImageReader.tIME_TYPE, stream); - cs.writeShort(metadata.tIME_year); - cs.writeByte(metadata.tIME_month); - cs.writeByte(metadata.tIME_day); - cs.writeByte(metadata.tIME_hour); - cs.writeByte(metadata.tIME_minute); - cs.writeByte(metadata.tIME_second); - cs.finish(); - } - } - - private void write_tEXt() throws IOException { - Iterator keywordIter = metadata.tEXt_keyword.iterator(); - Iterator textIter = metadata.tEXt_text.iterator(); - - while (keywordIter.hasNext()) { - ChunkStreamBackport cs = new ChunkStreamBackport(PNGImageReader.tEXt_TYPE, stream); - String keyword = keywordIter.next(); - cs.writeBytes(keyword); - cs.writeByte(0); - - String text = textIter.next(); - cs.writeBytes(text); - cs.finish(); - } - } - - private byte[] deflate(byte[] b) throws IOException { - ByteArrayOutputStream baos = new ByteArrayOutputStream(); - DeflaterOutputStream dos = new DeflaterOutputStream(baos); - dos.write(b); - dos.close(); - return baos.toByteArray(); - } - - private void write_iTXt() throws IOException { - Iterator keywordIter = metadata.iTXt_keyword.iterator(); - Iterator flagIter = metadata.iTXt_compressionFlag.iterator(); - Iterator methodIter = metadata.iTXt_compressionMethod.iterator(); - Iterator languageIter = metadata.iTXt_languageTag.iterator(); - Iterator translatedKeywordIter = - metadata.iTXt_translatedKeyword.iterator(); - Iterator textIter = metadata.iTXt_text.iterator(); - - while (keywordIter.hasNext()) { - ChunkStreamBackport cs = new ChunkStreamBackport(PNGImageReader.iTXt_TYPE, stream); - - cs.writeBytes(keywordIter.next()); - cs.writeByte(0); - - Boolean compressed = flagIter.next(); - cs.writeByte(compressed ? 1 : 0); - - cs.writeByte(methodIter.next()); - - cs.writeBytes(languageIter.next()); - cs.writeByte(0); - - - cs.write(translatedKeywordIter.next().getBytes("UTF8")); - cs.writeByte(0); - - String text = textIter.next(); - if (compressed) { - cs.write(deflate(text.getBytes("UTF8"))); - } else { - cs.write(text.getBytes("UTF8")); - } - cs.finish(); - } - } - - private void write_zTXt() throws IOException { - Iterator keywordIter = metadata.zTXt_keyword.iterator(); - Iterator methodIter = metadata.zTXt_compressionMethod.iterator(); - Iterator textIter = metadata.zTXt_text.iterator(); - - while (keywordIter.hasNext()) { - ChunkStreamBackport cs = new ChunkStreamBackport(PNGImageReader.zTXt_TYPE, stream); - String keyword = keywordIter.next(); - cs.writeBytes(keyword); - cs.writeByte(0); - - int compressionMethod = methodIter.next(); - cs.writeByte(compressionMethod); - - String text = textIter.next(); - cs.write(deflate(text.getBytes("ISO-8859-1"))); - cs.finish(); - } - } - - private void writeUnknownChunks() throws IOException { - Iterator typeIter = metadata.unknownChunkType.iterator(); - Iterator dataIter = metadata.unknownChunkData.iterator(); - - while (typeIter.hasNext() && dataIter.hasNext()) { - String type = typeIter.next(); - ChunkStreamBackport cs = new ChunkStreamBackport(chunkType(type), stream); - byte[] data = dataIter.next(); - cs.write(data); - cs.finish(); - } - } - - private void encodePass(ImageOutputStream os, - RenderedImage image, - int xOffset, int yOffset, - int xSkip, int ySkip) throws IOException { - int minX = sourceXOffset; - int minY = sourceYOffset; - int width = sourceWidth; - int height = sourceHeight; - - // Adjust offsets and skips based on source subsampling factors - xOffset *= periodX; - xSkip *= periodX; - yOffset *= periodY; - ySkip *= periodY; - - // Early exit if no data for this pass - int hpixels = (width - xOffset + xSkip - 1) / xSkip; - int vpixels = (height - yOffset + ySkip - 1) / ySkip; - if (hpixels == 0 || vpixels == 0) { - return; - } - - // Convert X offset and skip from pixels to samples - xOffset *= numBands; - xSkip *= numBands; - - // Create row buffers - int samplesPerByte = 8 / metadata.IHDR_bitDepth; - int numSamples = width * numBands; - int[] samples = new int[numSamples]; - - int bytesPerRow = hpixels * numBands; - if (metadata.IHDR_bitDepth < 8) { - bytesPerRow = (bytesPerRow + samplesPerByte - 1) / samplesPerByte; - } else if (metadata.IHDR_bitDepth == 16) { - bytesPerRow *= 2; - } - - IndexColorModel icm_gray_alpha = null; - if (metadata.IHDR_colorType == PNGImageReader.PNG_COLOR_GRAY_ALPHA && - image.getColorModel() instanceof IndexColorModel) { - // reserve space for alpha samples - bytesPerRow *= 2; - - // will be used to calculate alpha value for the pixel - icm_gray_alpha = (IndexColorModel) image.getColorModel(); - } - - byte[] currRow = new byte[bytesPerRow + bpp]; - byte[] prevRow = new byte[bytesPerRow + bpp]; - byte[][] filteredRows = new byte[5][bytesPerRow + bpp]; - - int bitDepth = metadata.IHDR_bitDepth; - for (int row = minY + yOffset; row < minY + height; row += ySkip) { - Rectangle rect = new Rectangle(minX, row, width, 1); - Raster ras = image.getData(rect); - if (sourceBands != null) { - ras = ras.createChild(minX, row, width, 1, minX, row, - sourceBands); - } - - ras.getPixels(minX, row, width, 1, samples); - - if (image.getColorModel().isAlphaPremultiplied()) { - WritableRaster wr = ras.createCompatibleWritableRaster(); - wr.setPixels(wr.getMinX(), wr.getMinY(), - wr.getWidth(), wr.getHeight(), - samples); - - image.getColorModel().coerceData(wr, false); - wr.getPixels(wr.getMinX(), wr.getMinY(), - wr.getWidth(), wr.getHeight(), - samples); - } - - // Reorder palette data if necessary - int[] paletteOrder = metadata.PLTE_order; - if (paletteOrder != null) { - for (int i = 0; i < numSamples; i++) { - samples[i] = paletteOrder[samples[i]]; - } - } - - int count = bpp; // leave first 'bpp' bytes zero - int pos = 0; - int tmp = 0; - - switch (bitDepth) { - case 1: - case 2: - case 4: - // Image can only have a single band - - int mask = samplesPerByte - 1; - for (int s = xOffset; s < numSamples; s += xSkip) { - byte val = scale0[samples[s]]; - tmp = (tmp << bitDepth) | val; - - if ((pos++ & mask) == mask) { - currRow[count++] = (byte) tmp; - tmp = 0; - pos = 0; - } - } - - // Left shift the last byte - if ((pos & mask) != 0) { - tmp <<= ((8 / bitDepth) - pos) * bitDepth; - currRow[count] = (byte) tmp; - } - break; - - case 8: - if (numBands == 1) { - for (int s = xOffset; s < numSamples; s += xSkip) { - currRow[count++] = scale0[samples[s]]; - if (icm_gray_alpha != null) { - currRow[count++] = - scale0[icm_gray_alpha.getAlpha(0xff & samples[s])]; - } - } - } else { - for (int s = xOffset; s < numSamples; s += xSkip) { - for (int b = 0; b < numBands; b++) { - currRow[count++] = scale[b][samples[s + b]]; - } - } - } - break; - - case 16: - for (int s = xOffset; s < numSamples; s += xSkip) { - for (int b = 0; b < numBands; b++) { - currRow[count++] = scaleh[b][samples[s + b]]; - currRow[count++] = scalel[b][samples[s + b]]; - } - } - break; - } - - // Perform filtering - int filterType = rowFilter.filterRow(metadata.IHDR_colorType, - currRow, prevRow, - filteredRows, - bytesPerRow, bpp); - - os.write(filterType); - os.write(filteredRows[filterType], bpp, bytesPerRow); - - // Swap current and previous rows - byte[] swap = currRow; - currRow = prevRow; - prevRow = swap; - - pixelsDone += hpixels; - processImageProgress(100.0F * pixelsDone / totalPixels); - - // If write has been aborted, just return; - // processWriteAborted will be called later - if (abortRequested()) { - return; - } - } - } - - // Use sourceXOffset, etc. - private void write_IDAT(RenderedImage image, int deflaterLevel) - throws IOException { - IDATOutputStreamBackport ios = new IDATOutputStreamBackport(stream, 32768, - deflaterLevel); - try { - if (metadata.IHDR_interlaceMethod == 1) { - for (int i = 0; i < 7; i++) { - encodePass(ios, image, - PNGImageReader.adam7XOffset[i], - PNGImageReader.adam7YOffset[i], - PNGImageReader.adam7XSubsampling[i], - PNGImageReader.adam7YSubsampling[i]); - if (abortRequested()) { - break; - } - } - } else { - encodePass(ios, image, 0, 0, 1, 1); - } - } finally { - ios.finish(); - } - } - - private void writeIEND() throws IOException { - ChunkStreamBackport cs = new ChunkStreamBackport(PNGImageReader.IEND_TYPE, stream); - cs.finish(); - } - - // Check two int arrays for value equality, always returns false - // if either array is null - private boolean equals(int[] s0, int[] s1) { - if (s0 == null || s1 == null) { - return false; - } - if (s0.length != s1.length) { - return false; - } - for (int i = 0; i < s0.length; i++) { - if (s0[i] != s1[i]) { - return false; - } - } - return true; - } - - // Initialize the scale/scale0 or scaleh/scalel arrays to - // hold the results of scaling an input value to the desired - // output bit depth - private void initializeScaleTables(int[] sampleSize) { - int bitDepth = metadata.IHDR_bitDepth; - - // If the existing tables are still valid, just return - if (bitDepth == scalingBitDepth && - equals(sampleSize, this.sampleSize)) { - return; - } - - // Compute new tables - this.sampleSize = sampleSize; - this.scalingBitDepth = bitDepth; - int maxOutSample = (1 << bitDepth) - 1; - if (bitDepth <= 8) { - scale = new byte[numBands][]; - for (int b = 0; b < numBands; b++) { - int maxInSample = (1 << sampleSize[b]) - 1; - int halfMaxInSample = maxInSample / 2; - scale[b] = new byte[maxInSample + 1]; - for (int s = 0; s <= maxInSample; s++) { - scale[b][s] = - (byte) ((s * maxOutSample + halfMaxInSample) / maxInSample); - } - } - scale0 = scale[0]; - scaleh = scalel = null; - } else { // bitDepth == 16 - // Divide scaling table into high and low bytes - scaleh = new byte[numBands][]; - scalel = new byte[numBands][]; - - for (int b = 0; b < numBands; b++) { - int maxInSample = (1 << sampleSize[b]) - 1; - int halfMaxInSample = maxInSample / 2; - scaleh[b] = new byte[maxInSample + 1]; - scalel[b] = new byte[maxInSample + 1]; - for (int s = 0; s <= maxInSample; s++) { - int val = (s * maxOutSample + halfMaxInSample) / maxInSample; - scaleh[b][s] = (byte) (val >> 8); - scalel[b][s] = (byte) (val & 0xff); - } - } - scale = null; - scale0 = null; - } - } - - @Override - public void write(IIOMetadata streamMetadata, - IIOImage image, - ImageWriteParam param) throws IIOException { - if (stream == null) { - throw new IllegalStateException("output == null!"); - } - if (image == null) { - throw new IllegalArgumentException("image == null!"); - } - if (image.hasRaster()) { - throw new UnsupportedOperationException("image has a Raster!"); - } - - RenderedImage im = image.getRenderedImage(); - SampleModel sampleModel = im.getSampleModel(); - this.numBands = sampleModel.getNumBands(); - - // Set source region and subsampling to default values - this.sourceXOffset = im.getMinX(); - this.sourceYOffset = im.getMinY(); - this.sourceWidth = im.getWidth(); - this.sourceHeight = im.getHeight(); - this.sourceBands = null; - this.periodX = 1; - this.periodY = 1; - - if (param != null) { - // Get source region and subsampling factors - Rectangle sourceRegion = param.getSourceRegion(); - if (sourceRegion != null) { - Rectangle imageBounds = new Rectangle(im.getMinX(), - im.getMinY(), - im.getWidth(), - im.getHeight()); - // Clip to actual image bounds - sourceRegion = sourceRegion.intersection(imageBounds); - sourceXOffset = sourceRegion.x; - sourceYOffset = sourceRegion.y; - sourceWidth = sourceRegion.width; - sourceHeight = sourceRegion.height; - } - - // Adjust for subsampling offsets - int gridX = param.getSubsamplingXOffset(); - int gridY = param.getSubsamplingYOffset(); - sourceXOffset += gridX; - sourceYOffset += gridY; - sourceWidth -= gridX; - sourceHeight -= gridY; - - // Get subsampling factors - periodX = param.getSourceXSubsampling(); - periodY = param.getSourceYSubsampling(); - - int[] sBands = param.getSourceBands(); - if (sBands != null) { - sourceBands = sBands; - numBands = sourceBands.length; - } - } - - // Compute output dimensions - int destWidth = (sourceWidth + periodX - 1) / periodX; - int destHeight = (sourceHeight + periodY - 1) / periodY; - if (destWidth <= 0 || destHeight <= 0) { - throw new IllegalArgumentException("Empty source region!"); - } - - // Compute total number of pixels for progress notification - this.totalPixels = destWidth * destHeight; - this.pixelsDone = 0; - - // Create metadata - IIOMetadata imd = image.getMetadata(); - if (imd != null) { - metadata = (PNGMetadata) convertImageMetadata(imd, - ImageTypeSpecifier.createFromRenderedImage(im), - null); - } else { - metadata = new PNGMetadata(); - } - - // reset compression level to default: - int deflaterLevel = DEFAULT_COMPRESSION_LEVEL; - - if (param != null) { - switch (param.getCompressionMode()) { - case ImageWriteParam.MODE_DISABLED: - deflaterLevel = Deflater.NO_COMPRESSION; - break; - case ImageWriteParam.MODE_EXPLICIT: - float quality = param.getCompressionQuality(); - if (quality >= 0f && quality <= 1f) { - deflaterLevel = 9 - Math.round(9f * quality); - } - break; - default: - } - - // Use Adam7 interlacing if set in write param - switch (param.getProgressiveMode()) { - case ImageWriteParam.MODE_DEFAULT: - metadata.IHDR_interlaceMethod = 1; - break; - case ImageWriteParam.MODE_DISABLED: - metadata.IHDR_interlaceMethod = 0; - break; - // MODE_COPY_FROM_METADATA should already be taken care of - // MODE_EXPLICIT is not allowed - default: - } - } - - // Initialize bitDepth and colorType - metadata.initialize(new ImageTypeSpecifier(im), numBands); - - // Overwrite IHDR width and height values with values from image - metadata.IHDR_width = destWidth; - metadata.IHDR_height = destHeight; - - this.bpp = numBands * ((metadata.IHDR_bitDepth == 16) ? 2 : 1); - - // Initialize scaling tables for this image - initializeScaleTables(sampleModel.getSampleSize()); - - clearAbortRequest(); - - processImageStarted(0); - - try { - write_magic(); - write_IHDR(); - - write_cHRM(); - write_gAMA(); - write_iCCP(); - write_sBIT(); - write_sRGB(); - - write_PLTE(); - - write_hIST(); - write_tRNS(); - write_bKGD(); - - write_pHYs(); - write_sPLT(); - write_tIME(); - write_tEXt(); - write_iTXt(); - write_zTXt(); - - writeUnknownChunks(); - - write_IDAT(im, deflaterLevel); - - if (abortRequested()) { - processWriteAborted(); - } else { - // Finish up and inform the listeners we are done - writeIEND(); - processImageComplete(); - } - } catch (IOException e) { - throw new IIOException("I/O error writing PNG file!", e); - } - } -} diff --git a/src/main/java/com/sun/imageio/plugins/png/PNGImageWriterSpiBackport.java b/src/main/java/com/sun/imageio/plugins/png/PNGImageWriterSpiBackport.java deleted file mode 100644 index fc32bb8..0000000 --- a/src/main/java/com/sun/imageio/plugins/png/PNGImageWriterSpiBackport.java +++ /dev/null @@ -1,145 +0,0 @@ -/* - * Copyright (c) 2000, 2010, Oracle and/or its affiliates. All rights reserved. - * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. - * - * This code is free software; you can redistribute it and/or modify it - * under the terms of the GNU General Public License version 2 only, as - * published by the Free Software Foundation. Oracle designates this - * particular file as subject to the "Classpath" exception as provided - * by Oracle in the LICENSE file that accompanied this code. - * - * This code is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License - * version 2 for more details (a copy is included in the LICENSE file that - * accompanied this code). - * - * You should have received a copy of the GNU General Public License version - * 2 along with this work; if not, write to the Free Software Foundation, - * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. - * - * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA - * or visit www.oracle.com if you need additional information or have any - * questions. - */ - -package com.sun.imageio.plugins.png; - -import javax.imageio.ImageTypeSpecifier; -import javax.imageio.ImageWriter; -import javax.imageio.spi.ImageWriterSpi; -import javax.imageio.spi.ServiceRegistry; -import javax.imageio.stream.ImageOutputStream; -import java.awt.image.ColorModel; -import java.awt.image.IndexColorModel; -import java.awt.image.SampleModel; -import java.util.Iterator; -import java.util.Locale; - -public class PNGImageWriterSpiBackport extends ImageWriterSpi { - - private static final String vendorName = "xbib"; - - private static final String version = "1.0"; - - private static final String[] names = {"png", "PNG"}; - - private static final String[] suffixes = {"png"}; - - private static final String[] MIMETypes = {"image/png", "image/x-png"}; - - private static final String writerClassName = - "com.sun.imageio.plugins.png.PNGImageWriterBackport"; - - private static final String[] readerSpiNames = { - "com.sun.imageio.plugins.png.PNGImageReaderSpi" - }; - - public PNGImageWriterSpiBackport() { - super(vendorName, - version, - names, - suffixes, - MIMETypes, - writerClassName, - new Class[]{ImageOutputStream.class}, - readerSpiNames, - false, - null, null, - null, null, - true, - PNGMetadata.nativeMetadataFormatName, - "com.sun.imageio.plugins.png.PNGMetadataFormat", - null, null - ); - } - - @Override - public boolean canEncodeImage(ImageTypeSpecifier type) { - SampleModel sampleModel = type.getSampleModel(); - ColorModel colorModel = type.getColorModel(); - - // Find the maximum bit depth across all channels - int[] sampleSize = sampleModel.getSampleSize(); - int bitDepth = sampleSize[0]; - for (int i = 1; i < sampleSize.length; i++) { - if (sampleSize[i] > bitDepth) { - bitDepth = sampleSize[i]; - } - } - - // Ensure bitDepth is between 1 and 16 - if (bitDepth < 1 || bitDepth > 16) { - return false; - } - - // Check number of bands, alpha - int numBands = sampleModel.getNumBands(); - if (numBands < 1 || numBands > 4) { - return false; - } - - boolean hasAlpha = colorModel.hasAlpha(); - // Fix 4464413: PNGTransparency reg-test was failing - // because for IndexColorModels that have alpha, - // numBands == 1 && hasAlpha == true, thus causing - // the check below to fail and return false. - if (colorModel instanceof IndexColorModel) { - return true; - } - if ((numBands == 1 || numBands == 3) && hasAlpha) { - return false; - } - if ((numBands == 2 || numBands == 4) && !hasAlpha) { - return false; - } - - return true; - } - - @Override - public String getDescription(Locale locale) { - return "JDK9 Backport PNG image writer"; - } - - @Override - public ImageWriter createWriterInstance(Object extension) { - return new PNGImageWriterBackport(this); - } - - @Override - public void onRegistration(ServiceRegistry registry, Class category) { - Iterator others = registry.getServiceProviders(ImageWriterSpi.class, false); - while (others.hasNext()) { - ImageWriterSpi other = others.next(); - if (other != this) { - for (String formatName : other.getFormatNames()) { - if ("png".equals(formatName)) { - registry.setOrdering(ImageWriterSpi.class, this, other); - break; - } - } - } - } - } -} diff --git a/src/main/resources/META-INF/services/javax.imageio.spi.ImageWriterSpi b/src/main/resources/META-INF/services/javax.imageio.spi.ImageWriterSpi deleted file mode 100644 index 50a4998..0000000 --- a/src/main/resources/META-INF/services/javax.imageio.spi.ImageWriterSpi +++ /dev/null @@ -1 +0,0 @@ -com.sun.imageio.plugins.png.PNGImageWriterSpiBackport \ No newline at end of file diff --git a/src/test/java/com/sun/imageio/plugins/png/PNGImageWriterBackportTest.java b/src/test/java/com/sun/imageio/plugins/png/PNGImageWriterBackportTest.java deleted file mode 100644 index 4427146..0000000 --- a/src/test/java/com/sun/imageio/plugins/png/PNGImageWriterBackportTest.java +++ /dev/null @@ -1,85 +0,0 @@ -package com.sun.imageio.plugins.png; - -import static org.junit.Assert.assertArrayEquals; -import static org.junit.Assert.assertTrue; - -import java.awt.image.BufferedImage; -import java.io.ByteArrayOutputStream; -import java.io.IOException; -import java.util.Iterator; - -import javax.imageio.IIOImage; -import javax.imageio.ImageIO; -import javax.imageio.ImageWriteParam; -import javax.imageio.ImageWriter; -import javax.imageio.stream.ImageOutputStream; -import javax.imageio.stream.MemoryCacheImageOutputStream; - -import org.junit.Test; - -public class PNGImageWriterBackportTest { - - @Test - public void testPrecedence() { - Iterator it = ImageIO.getImageWritersByFormatName("png"); - assertTrue(it.next() instanceof PNGImageWriterBackport); - assertTrue(it.next() instanceof PNGImageWriter); - } - - @Test - public void testCompressionLevels() throws IOException { - - BufferedImage img = ImageIO.read(getClass().getResourceAsStream("placeholder-text.gif")); - - byte[] bd = toPng(img, null); // default - byte[] b00 = toPng(img, 0.0f); // highest compression, slowest - byte[] b01 = toPng(img, 0.1f); - byte[] b02 = toPng(img, 0.2f); - byte[] b03 = toPng(img, 0.3f); - byte[] b04 = toPng(img, 0.4f); - byte[] b05 = toPng(img, 0.5f); - byte[] b06 = toPng(img, 0.6f); - byte[] b07 = toPng(img, 0.7f); - byte[] b08 = toPng(img, 0.8f); - byte[] b09 = toPng(img, 0.9f); - byte[] b10 = toPng(img, 1.0f); // lowest compression, fastest - - assertArrayEquals(bd, b05); - assertArrayEquals(bd, b06); - - assertTrue(b00.length < b01.length); - assertTrue(b01.length < b02.length); - assertTrue(b02.length < b03.length); - assertTrue(b03.length < b04.length); - assertTrue(b04.length < b05.length); - assertTrue(b05.length == b06.length); - assertTrue(b06.length < b07.length); - assertTrue(b07.length < b08.length); - assertTrue(b08.length < b09.length); - assertTrue(b09.length < b10.length); - } - - private byte[] toPng(BufferedImage img, Float compressionQuality) throws IOException { - - ByteArrayOutputStream baos = new ByteArrayOutputStream(); - Iterator imageWriters = ImageIO.getImageWritersByFormatName("png"); - while (imageWriters.hasNext()) { - ImageWriter writer = imageWriters.next(); - if (writer instanceof PNGImageWriterBackport) { - ImageWriteParam writeParam = writer.getDefaultWriteParam(); - if (compressionQuality != null) { - writeParam.setCompressionMode(ImageWriteParam.MODE_EXPLICIT); - writeParam.setCompressionQuality(compressionQuality); - } - try (ImageOutputStream stream = new MemoryCacheImageOutputStream(baos)) { - writer.setOutput(stream); - writer.write(null, new IIOImage(img, null, null), writeParam); - } finally { - writer.dispose(); - } - } - } - - return baos.toByteArray(); - } -} diff --git a/src/test/resources/com/sun/imageio/plugins/png/placeholder-text.gif b/src/test/resources/com/sun/imageio/plugins/png/placeholder-text.gif deleted file mode 100644 index d9058916f250a6e147c145df154c01706334bef3..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 7153 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 literal 0 HcmV?d00001 diff --git a/barcode/src/test/resources/org/xbib/graphics/barcode/pdf417/normal-basic-ecc-level-8.properties b/barcode/src/test/resources/org/xbib/graphics/barcode/pdf417/normal-basic-ecc-level-8.properties new file mode 100755 index 0000000..f670abe --- /dev/null +++ b/barcode/src/test/resources/org/xbib/graphics/barcode/pdf417/normal-basic-ecc-level-8.properties @@ -0,0 +1,3 @@ +mode=NORMAL +preferredEccLevel=8 +content=This is just a test. diff --git a/barcode/src/test/resources/org/xbib/graphics/barcode/pdf417/normal-basic-ecc-level-9.error b/barcode/src/test/resources/org/xbib/graphics/barcode/pdf417/normal-basic-ecc-level-9.error new file mode 100755 index 0000000..3aba9d2 --- /dev/null +++ b/barcode/src/test/resources/org/xbib/graphics/barcode/pdf417/normal-basic-ecc-level-9.error @@ -0,0 +1 @@ +ECC level must be between 0 and 8. diff --git a/barcode/src/test/resources/org/xbib/graphics/barcode/pdf417/normal-basic-ecc-level-9.properties b/barcode/src/test/resources/org/xbib/graphics/barcode/pdf417/normal-basic-ecc-level-9.properties new file mode 100755 index 0000000..c5bf86a --- /dev/null +++ b/barcode/src/test/resources/org/xbib/graphics/barcode/pdf417/normal-basic-ecc-level-9.properties @@ -0,0 +1,3 @@ +mode=NORMAL +preferredEccLevel=9 +content=This is just a test. diff --git a/barcode/src/test/resources/org/xbib/graphics/barcode/pdf417/normal-basic-row-height-10.codewords b/barcode/src/test/resources/org/xbib/graphics/barcode/pdf417/normal-basic-row-height-10.codewords new file mode 100755 index 0000000..7f552e2 --- /dev/null +++ b/barcode/src/test/resources/org/xbib/graphics/barcode/pdf417/normal-basic-row-height-10.codewords @@ -0,0 +1,7 @@ +811111135111115231112144341113311212242351111152711311121 +811111135111132311312261221151321414411141111216711311121 +811111131111124611451311223411316111322121111353711311121 +811111132111425121333221322412212332211321114251711311121 +811111132111341411611124331141313511231141113232711311121 +811111135111412212232241133114222224114141114411711311121 +811111133112315123112332133142212331241131123151711311121 diff --git a/barcode/src/test/resources/org/xbib/graphics/barcode/pdf417/normal-basic-row-height-10.png b/barcode/src/test/resources/org/xbib/graphics/barcode/pdf417/normal-basic-row-height-10.png new file mode 100644 index 0000000000000000000000000000000000000000..26c0290fe0b86e7dce5f610a21930485f1e13b59 GIT binary patch literal 43738 zcmeHQdr*^C7N@0XS8&~#-M~f%w6d*Z17(IRxRM5I5m~z(*;!i?3=fe!W+6PPCTJqC z&;hrIB2{n@0>$pGe~<_H7(NgT_^1$G0ooGs89};MiSq>*OA-bmviCEQHtf#+v9mM# zm;-<1&NpA~J?GwY&-tC-Ir-$b`{UNH+qlll%WM6e-#aQ-R08J zz#qK!?b-doiDc#A?sMKde&#)LZ~4m~UEjBRPsC}=8M?6VaeYCuV|F>e*H2fOIVQg2 zPwY$>3b!?;jy#;XIK2&T8W?68gKx`XrFGxV1hcM+mk03-{Ky&Wsh){orm@*CF-25= zPgF9|=Nhh6is*}@O_rm%eU6{uWyK$zuTUJomxbmCo;Ft6nKoNNCll7F zHO=ue%$RI}Yj{6;;Zi#H_%)Wm`AKL9(^h8JHzijnwq0}T4cpBH&C{1vrmd{|+IfAM zJ;Zx#jMz3`-O!4;hPROkCHWB{?e`Se(ii?!bXm8I&(-J+7w+Zpr!G!+5z@hf;tIvA zNzU(MH4Nx@zQ2|8#N0>P-Dha}lVL{c8*=oJv?6Y+oQ>C~i>v2QMVAvUhf0YEc z&=)0l7+p=Pp!&=qKQ@1g&kp}eo>-ySv_w-kF^Kf>FJrncr}!&ELrC!m(?*tZ@>5r5 z2HLVUkSHG4NL&$F#%P+Sw^k=6*i74b#Ra?m z{n?LVrCByk+buP}QSzG&OX>Lu$MGg*bL`NthLOBbyc!^v3oWm)wviRp6AwzQ_bi&6 z;-ub#WL41O=u^xhPE8Ps8@H7@TM7><$dHwH6xJv8FlNXj?sIzCkyL)o^* z1;Ge$3p4n$W4hEQ9tNyV2TWHtoVL~~pT@tX5q~BUuiBDC_-f= z`AJXu!d7P?%*>b#D0NdlU<*Pyi@gQK4C{ONp;p_l=Fz2Ck#Bz#&*rz({@2Qs)yWS~ zBq$P2B%DYvtzcT=j)XfBoE`95!E1%^1Huo8BO#7l#h3#?0FWI(b^xLSi0f)=)l>IUkm*)^S9}2P@?f5)=_0(x-pbu!rD5mp zq>mpe_d}pGMZzu`Q0ffHS_;RFp3xu#`QXB>?PgEnxpv}mVaBD7O^$U5Jx5xyRB>w% z6i#ulcMFZlA`K$HzsA};KZbDY##7&1oc83MbF&9;wDga!mlrG3w1--a!QKeBr5N{H zm>HU84$8rwSCl(55K;TX&YV7nC+%F>DxAFgaHd4}u2IWX@yLoo3G*0;ebI+{k0*Odrj?o%|ZfRT@=x{S6vLQU4@VKX8|{(h&2VKlR|* zUBt?-3`68|PtrN5qPIz}b);&}OF|BkMj3yylwzoV;m;RNOB|&Flogo!r~tv(t$hKA zd+xzG2Q`D5;WWc(M)g8qw83b@-3)g#D*gi(3|uhqw87IxmAnv8K|lp@GsMlPEFa(z zK*0b71F#eI*Z{Eee+APX{x~U=kaqwiH)B8>83_Opf7(aJI}27OoZk2IpUwz{8>B7Dv_k7sRko96dmoP{p6Ly7T}233aEmI;QJ$=nYMb>V~x)hm6{Hq!W27!qOR{cOovm``v2SGC4#L zL0?*j;D6NirgO4%ys_8ura74P15urv|9+9E3(earis*1nFIgbxi);*(y9j2*FBUPR zNZgI$a;x7DmN2JZ1L~UtT#~p?I$MvgaBzAu(w%HpaBRC$zXl znwx@myNUX!2ao~L*If#`?%q2e;&IZwAPm<*0%4$|3_Amk3D^uUpm3YO)e3$FI9=d@ zzKA%$9f#Ni0yT(Q4G_Wup0R>p0k{K5*Y^l1z?P!&a1<#eT8-#&il zWcH=Z-W#LbawTfCgX$QXsoYD!i+s?^ZR)3+F%(&Abw{W|2%bPLU}IUiA5EAAyzra| zGz+Q&|7oidr90*}yYpHrshaQ@O-J(<7UH;2B`XJsuqJp0(>y8zZV{ zgi5zOeNp7Hk{y91gO*`qyW5Gaq#fZbgZToB?fy*~xTN4-23G{UFK{@)$A+*1BB?|O z9swc%R0Pps0SI!_bRk}bcp2hlh?o2SGdMQF*h9(ZRRu1ajA{%}yCmvYf~h)-3im(i zf`s7x5R!Ww-{O4HhtcKBzza}gCJ-XX0F}(ZJ^&d|H6gGMKnDMGAI#l+so2Zw&B}dy zc7FgDEjSzCaDmeajyF}{4Eq3N0Q&&3RLH37Kfy2a{a zUhfR5(HbIi@(lUkMT5(Br#_~d2x5-J9FIf_*Bxm)O^ z3fZ(L{9}DGG->K-dQ@5sGs#6?wJEt`a69RKT4*Vg zhy%mUQ(B-T`7|xe)V?E3Or97aeDO^Vc4V3&b8@kE>077Jf?HLYNd-4)W(KwsF|S=u z!)AcZ0Gr`ow+zUL+!z(JzPf)j!l*55>|MNQM@_D(GuI-^qSaki#_Jy3C43{(0yni? z&J<-$Z1l8k-BFP_5oUv}HSw%OJeiI~3)qXa<3e*GvW;TR8{~o6HYu`I+@3XRp}D%I zwcEowBSXGEaS&%Ng>s&Mfiga!Jl{t_NGFkXH>1gX%~B(Ch^%#CBO!hCNTu~KDaiVG zbDF2$FX$hoz5soj@$;13B0Lb8q;3bAjQv~&GU&*}Os39~xo+ literal 0 HcmV?d00001 diff --git a/barcode/src/test/resources/org/xbib/graphics/barcode/pdf417/normal-basic-row-height-10.properties b/barcode/src/test/resources/org/xbib/graphics/barcode/pdf417/normal-basic-row-height-10.properties new file mode 100755 index 0000000..e74dd56 --- /dev/null +++ b/barcode/src/test/resources/org/xbib/graphics/barcode/pdf417/normal-basic-row-height-10.properties @@ -0,0 +1,3 @@ +mode=NORMAL +barHeight=10 +content=This is just a test. diff --git a/barcode/src/test/resources/org/xbib/graphics/barcode/pdf417/normal-basic.codewords b/barcode/src/test/resources/org/xbib/graphics/barcode/pdf417/normal-basic.codewords new file mode 100755 index 0000000..7f552e2 --- /dev/null +++ b/barcode/src/test/resources/org/xbib/graphics/barcode/pdf417/normal-basic.codewords @@ -0,0 +1,7 @@ +811111135111115231112144341113311212242351111152711311121 +811111135111132311312261221151321414411141111216711311121 +811111131111124611451311223411316111322121111353711311121 +811111132111425121333221322412212332211321114251711311121 +811111132111341411611124331141313511231141113232711311121 +811111135111412212232241133114222224114141114411711311121 +811111133112315123112332133142212331241131123151711311121 diff --git a/barcode/src/test/resources/org/xbib/graphics/barcode/pdf417/normal-basic.png b/barcode/src/test/resources/org/xbib/graphics/barcode/pdf417/normal-basic.png new file mode 100644 index 0000000000000000000000000000000000000000..3e20ce24e604491a760d5e07dda518d1b8e9e090 GIT binary patch literal 13222 zcmeHOZA@F|6=tK*4ID-41eJqgWkp>S8FvyELj>KVln}<+k#(tNJo6F4wJil3lVrHX zE+8^NwLk>UhbM>Fj8tt@0sgoi7l`9PX+uo-Fr>q9bNO1^6lb=Hwiu&A2|F*)X{LWR zDf=_MvV<&Mbnki3d(U~!^E|Kr@T=VHT`_xNA|fJo<^24}uOlL!v%>fLKY%|GHEFUp zBO-*#oFf@;y;ImZnqht}l@mEJvvL22f6n>%NJ{q8SyhfMba>-k|NMq?h~pHnWp)AW z8XMB++lqEIyz=%9#bH~WwBBUnacL2`n6q?Loy$BOrCCLTfO?V|;u&lv>B4H-+Lwof zL0aJ$rKev?-|rLGSEnT1oH0`CpT_ICnn4XYU8pne?@eIRq-&|$51F6Vl7kw3t#8qP zX>DYjr2;==Pd50kb;!A^lUDDUx9^31B*~L`xjtR7HcM^#vW#539GKsJNKMIO9oHw0 zHn-Z3EeSJK!&@<(rA$(<=#%Tivj^B?ief{Xyd%3l5XV$rbm)x7=^FdiL%zA-%$C7} zdaB0`^}6xG@s8tH9oIN+Zn&#IJ2+FiIRv>#63L%w^L56A^*FPY&v?|Y?_NJOmvm5) z=V7kPy;Fk1%1`DJg&sR6|FFDuMqWRsS88S69|MZ&40B(c_s@7O<#h!^G%eA1MNecC z%m6hOPh|F${rz6TPW)wsnY)=ZanneU2WWk?rjGCb1>H4!KeO*@YQtE(zJm60S1TGs zbJi7C<8o5ul$+VVVrc7@vL>zu!9MxnO3zE_sjK|_ViDCD(DPO&Z;5|LTRZGo?s1Fc z)<5cZ;$~=MJX4+PNfbKo#xcDUMSqQPTvRH%gX5O3Mz7oJq(*_JzIyiaE2(}Wxr7+* zUp^Zes*-t?3@xwcQD-Th;SphWja_&eBbvUVD0O`BUUiBp-Y7`&DJFzLU%cg3@lK#8 zgu!OxrDfL}k56Z*%Yvew0W&FK1w^pD((Y9hi{%ZbwxaHL9`4dMjpVtL%h#_}ooVH2 zBy_;g_8!a^*)mNNpE~<~-$0!DW%fbYqqd^amMnSf@IgtCvwrgqU$t{G!CQ&95?3Xz zO2z+wgDr6?qs;|NWz&3eZ$oFmqahd0`>!D)N6J{AKW-R6O+Hfu5T$$=dqc4iMx+{- zX>O0Y*HLU~u1EsJ^66@!(IOt#Y*1S4q9oM0PuD;n?!NLZz!7l9LGhj#@a638%r*Br%J-5%iZd(j*?pc zH=geG7|quIH&#ah%7WfnRDLIPYU{REzM<`T$8SBhSJl;8drCOA(jR?H{4mKeSsG4tc}F;Ak8Zde0>d#Hahuc-snVI%>gnJJvh zkN>_vAPnAA#gi)_IP`ZRGZ*CW3ieJF_`Kx8DTsm@K#V?3swEdy;nMQQW?0Z{YAmG< zotGj(;c@`P;+uibZ+A;2$>k{!1u}*~38skec-SyH;BCg+jH?+}Gj7`dUDIZ%PH6!m ztVk~=C?WL(G-GshNPPGE40roec`}w-YN?a%1v`@bj?ac~fRl($g5mc7=K#M4Tc;v% zE#O*s2DKpcBu0wt0mJQwz@DPbn>sV-BlbXT6eXlZmjcHhpALZ2iZ04np^6SClIR3* z&sC*6C;-j~B#JpC)du-qgK~FLv(e_fs%X_TmC9q`h!0$3O_e(Z>c(YP|D;0up{f^{ za&7xi=m7hEQHEQgJyGj#3`~-1f6?ZrOPb-VZ*4mh8fobapFiOg?`Bw4!$2H4`M;!( zF+5PqWo%?DDc@At=e~HroEt$GM``(-BFvWN5Zhrrk|4Ugu&IoJOfO`4dHd2!w6L0G z-V_WZn-^)~iL5l7Qf`O!#{3yzkC7-@bavl46BFu($%cj1e6-S|ruTiDdpoRE zJ#c17qoS1}R^x&r^z&dHOc%mxoOiqIQHJdt-(vQJ%Lm(Geb$YXf__NW<-i2k4K!hW zRXvFOfxi)EqX15HZRYr^QBU~!rEkN+TaULM1SVYdxax7k{~tB{jqiQ*!n=pbJ<(n0 P2RJ!dxkt>eojCVTjtsHi literal 0 HcmV?d00001 diff --git a/barcode/src/test/resources/org/xbib/graphics/barcode/pdf417/normal-basic.properties b/barcode/src/test/resources/org/xbib/graphics/barcode/pdf417/normal-basic.properties new file mode 100755 index 0000000..358dc74 --- /dev/null +++ b/barcode/src/test/resources/org/xbib/graphics/barcode/pdf417/normal-basic.properties @@ -0,0 +1,2 @@ +mode=NORMAL +content=This is just a test. diff --git a/barcode/src/test/resources/org/xbib/graphics/barcode/pdf417/normal-data-variety.codewords b/barcode/src/test/resources/org/xbib/graphics/barcode/pdf417/normal-data-variety.codewords new file mode 100755 index 0000000..8320923 --- /dev/null +++ b/barcode/src/test/resources/org/xbib/graphics/barcode/pdf417/normal-data-variety.codewords @@ -0,0 +1,11 @@ +811111133111123521113243213332213224122151111152711311121 +811111136111133122114322311621121161122351111224711311121 +811111131111124651214211113113254131115131111361711311121 +811111131111515211232314212232321414222121114251711311121 +811111133111342241151212116111243511231121113315711311121 +811111135111412222331231214211244122511131115114711311121 +811111131112323432122142111423231422211431123151711311121 +811111135112112431132511331321131123126121116141711311121 +811111136112221223521211133312222161221211122451711311121 +811111131113252232113151222414113332131111132423711311121 +811111133112332241131511131152222161113221123215711311121 diff --git a/barcode/src/test/resources/org/xbib/graphics/barcode/pdf417/normal-data-variety.png b/barcode/src/test/resources/org/xbib/graphics/barcode/pdf417/normal-data-variety.png new file mode 100644 index 0000000000000000000000000000000000000000..db3bb8fd8ab7115a31f70cb8418031435bc328c5 GIT binary patch literal 21195 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 Jyes|qKLNivJKX>P literal 0 HcmV?d00001 diff --git a/barcode/src/test/resources/org/xbib/graphics/barcode/pdf417/normal-data-variety.properties b/barcode/src/test/resources/org/xbib/graphics/barcode/pdf417/normal-data-variety.properties new file mode 100755 index 0000000..08f5eb4 --- /dev/null +++ b/barcode/src/test/resources/org/xbib/graphics/barcode/pdf417/normal-data-variety.properties @@ -0,0 +1,2 @@ +mode=NORMAL +content=TESTING 12345678901234567890 testing@TESTING diff --git a/barcode/src/test/resources/org/xbib/graphics/barcode/pdf417/normal-structured-append-1-of-3.codewords b/barcode/src/test/resources/org/xbib/graphics/barcode/pdf417/normal-structured-append-1-of-3.codewords new file mode 100755 index 0000000..e11d41e --- /dev/null +++ b/barcode/src/test/resources/org/xbib/graphics/barcode/pdf417/normal-structured-append-1-of-3.codewords @@ -0,0 +1,5 @@ +81111113411111441111232614212214121224232315112231111235711311121 +81111113611111331262122121124421211235126113113161111133711311121 +81111113211112542352121123521211235212112352121131111163711311121 +81111113111142431423221214232212142322121423221211115152711311121 +81111113311132241161112411611124522411113213322131113224711311121 diff --git a/barcode/src/test/resources/org/xbib/graphics/barcode/pdf417/normal-structured-append-1-of-3.png b/barcode/src/test/resources/org/xbib/graphics/barcode/pdf417/normal-structured-append-1-of-3.png new file mode 100644 index 0000000000000000000000000000000000000000..7e700dbf74a3f2b5c3195bcc005db9451f796405 GIT binary patch literal 10922 zcmeHNZA?>V6z(Lr1#myMNYgEtsR1{!CDKS^Iv5ZMLxh>Buu=-+s~00J(#Bi)s00&O zTonZbtfinX`bR!j(n~ELeoUm$577uMw}q)K;@Zjrv1=Y> zb&jZa$I@+%$Y?XORGO_od=-^;FhlQdq6MQ4Qf@-4Zl*ln79N^KABtRT5Qggfl#MJS zJDgGk4?`DrC@2c?xxSm;T;^QM(-1Dx%-%A+o0|m04AZldrs^_(1;fy)i#)^=)`gU0 zO;?dn#@ewbwwgk=-eG$bW-S<&i6XpOR7X^r-~L`tIgDP)Pbe)wH__;Y$voL05dQ%~ zwmQGg0nH#RT$@s3m6bgFy!fYVKo zW-p6J-CkPmYm`qh@24B(1DeF~T4OU1%dVM7%0MqNPxdRTscgi&ur2+s{_)LJxy-Lq zXV@~UZnqk4b4DItf`~4At0gDqgHwN($4s1Gek70K!ZWRAcCEEq^1!aulS#Q%l3Kg# zNTB(fH0K6!hcxGcP#YnJDp)!SSGIu|uAIW`tvBvbr$1bSkQYYghzL+$BLJrb#LZkm zKUWngGB)RpVn-4&MC|Q&K5){$&CAYrs=lV$ir@l?qif0#gV}q{%+6f)A$Dt@br`R(pNFHXob075lj8UTqMNGIRvBkl+gD@+bsEvjy(DwaJl zRU%8~Qg3PJ=wphKt48@bfaG?I31D&|KrBECmg`_`+D58;GZj%by0it2z5(xi=OBlz z7axnI*XP%w{D(tX zEj%=Hyt+(YF91ws65u$XN(;=foyFy9eWlv?tjMh`wbYaJXbi!KI0IS;u&4GO;4(1> zIA^$9rU!vfFvu;9r$cRLNNAN9W$9enYOHVYubst~Fsf}&-!IZ3WRN2Q4j4-tx0_jF z;1huU*8GD{Z0iAG3*{@VZf(3JPaLN~ZANy5^r@heKcA%FYzGcNhm?Q^U_J>Ng_g+n zhMs74UM!m{2zP61?enA*lkQ_$s)Q*MTM;tmfZ{@=Mvw!10yt4~-({&R8E}vRwY}A? zP37=pH+WFIWWsn*tWsV)DDa?=;+f+A4IdvKTJ-BgX3E_UNAUjras@Z$qgOC!h`5Sp zy?ZCg;tmrh>`w4ofZQ(EMHUCA>)kJOP-s{|;<}KtE2v&_afKmRd;zeKP!pTqo%tr3 z;pX7p!DGjxl7-(aJn!IH`SsEz4ifJVNOZ8899@y1P|iJ~^db(p+7g!fk;7%K`uo0P z2iLtVoa8i_iufXR%Yh6Q06fgFhVUumOYrNC;psI6#625>%Q>))`R1 ztt*p5#B9Fv=Nzmt!RXz~+sBh-5ru61#rzvu&~?8uN6v%989y1%f?*4j2PdeIf~J2( zfP2NtHw@l}hMr8UsxAQa5Nwe?rilbkF+p#HqWDVai!#{yjkG{8DDm8b7Gyv#)#z8h z0Y`;FZ9iar0;$C}5L*PMH!1=a{B3){WW?@?o2z?C92a`_E5N+Qikj7LG_G#9UI5KU zt6UD41N-Xrrq_U}hI>f+>RxIDo*W8y8SXOIjlrV~znFL~do}2Vr+*wVUFiQlAm)n? YvX9S3WmFD7#D-AgViI??M*n#HALPv%djJ3c literal 0 HcmV?d00001 diff --git a/barcode/src/test/resources/org/xbib/graphics/barcode/pdf417/normal-structured-append-1-of-3.properties b/barcode/src/test/resources/org/xbib/graphics/barcode/pdf417/normal-structured-append-1-of-3.properties new file mode 100755 index 0000000..d993bc2 --- /dev/null +++ b/barcode/src/test/resources/org/xbib/graphics/barcode/pdf417/normal-structured-append-1-of-3.properties @@ -0,0 +1,8 @@ +mode=NORMAL +rows=5 +dataColumns=4 +preferredEccLevel=0 +structuredAppendFileId=123 +structuredAppendPosition=1 +structuredAppendTotal=3 +content=this diff --git a/barcode/src/test/resources/org/xbib/graphics/barcode/pdf417/normal-structured-append-2-of-3.codewords b/barcode/src/test/resources/org/xbib/graphics/barcode/pdf417/normal-structured-append-2-of-3.codewords new file mode 100755 index 0000000..cba3df1 --- /dev/null +++ b/barcode/src/test/resources/org/xbib/graphics/barcode/pdf417/normal-structured-append-2-of-3.codewords @@ -0,0 +1,5 @@ +81111113411111441111232624211214231511223111325131111235711311121 +81111113611111331262122121124421211236116113113161111133711311121 +81111113211112542352121123521211235212112352121131111163711311121 +81111113111142431423221214232212142322121423221211115152711311121 +81111113311132241161112411611124311622113112413231113224711311121 diff --git a/barcode/src/test/resources/org/xbib/graphics/barcode/pdf417/normal-structured-append-2-of-3.png b/barcode/src/test/resources/org/xbib/graphics/barcode/pdf417/normal-structured-append-2-of-3.png new file mode 100644 index 0000000000000000000000000000000000000000..bc4bf4683f87b362a98bebd394f36be7b5011781 GIT binary patch literal 10944 zcmeHNZA?>F7$y?ghSeY2l++C;8ejo|j7A#5V2BXL5Hv&30!8`iWr3F2hKo?D!3>Pz z2Ok2frJ(NPkF-#Wz19M560wE0z!st9wlHU@ZnQEHszBU!E`^25GXE?~Typ+2&HZuT zbKmox_j%6qKBx92#;$kY?C#>?vOezP=ucf-)~1604X=YA7x(UhBo`OIlX20z_8-lV z=ytuC?nQAOGX3#GTuEKv{ zx!ZKd7NJa>dr6>=5F(Y#fk2LYi$}QX5pD2ddZTHNCC!^2R&t=#d-wR}z-rMwyWS8$ z&8rdB+jr1x)gn2bhR+f~2c|0uZQ;S$Vf~Q{O@iP7imI7uVuc5mqC?1~oytI^P>p@U zL8qk$<14#&rf;QR#l^C;;K6Nlf1JTGc+n9IQ%n*;TPD_x8~8cBXhXRpbRnmiiiWFh z=t|RPY*z*%^SMket){TvWV0}34HQBCjYv)k7M6|WT#DhODCFAq-5foqkUc^Kjp+C2 zmOWFSvF~uT^BkHFX|_`Ih-mi4BV$ZU`LN}tg|%(mPifSsvI3~{!=IO%S~r5i({EkDK8ra~`GjtmVpYnyD&p*8r~knmD)8lgmT-`6?plETj9k zvt6;Q0)(2^BHwF;1qBNV78LAIutPz}`%fTGXY|K}naI4SJ-s`blcoje?U?5HPW5$q z6L9oJS5yenR!zlgggA2`L#d;L)lIJA1fl$i5Snezt*^xWSRcEpB;IW_W6W-03ITra zR5rICP+M&q0f`9asV1G#xG%^UM@QOBtV;1O#T~r46eatELQN#zuL4}>DIhpLBX2p} zjZIxqtb$k;r#RdhO?rz=0<5-rT-#22aSja}_rn6LfY0ztu)5;hv04DorMEM@MDhcG zHK?_AsWX~pmdujOOaNkO2FHIdQBWH~19=`V23cVml2}H7`c<8CgY( zc#b|c%OE@!8`fA@k8dIp(A9}+RKXx8`P}f#wra3rfJfz6(Gh1f%RHGka`cPa^y5_C z!)XZ@&jJZhG!_H8N8}_y>TBD0EtFrOHJsC$9+x_!r5`8rAXpU3)EO4VU*s4T1uP07 z>=dsye6%YDBKiJ@3>sCfD4DpwJ8=VCbuTzENM3f?iD%$O4zQ7mn_0Zhtkr=0TK)p1 z1Dmb@5UgZOfa~r1P9#R=qMWD>ZsAJ|fziFs8X%OJ*&%oR|AuC8IWRl0c2>$c@MOWh z1G_Rw35Fo?{{abnmT?|TF?^?hK2K=tK)Fvk_8Fng_<>=^MB}31PJWm><}ga=m|91= zZug>s;RkC7o&KJE12H)ObBPOwQVDImuSk!8km z2-Eb>Weuwd9y-{YU<+RXe-P>Z1tG{poP+)gCYxa`7#w7QQ2@FNj4=)pg9%ANEKn-c z39<$tfdg?JUkAJsP76f>;nWb2rEuU5AdC#aFR}cJW+oxQHxQz_KVGAI;?7NK^#+>0 zD|^au!gG0|pF|i3$fN)T?gwDh08!2@zXI_9GlkQ+#FU^9&Ea*PFuk3x0Yi#NT-9~> z|a0mNybZlPFPdpViKdJQHM|b E2}_IfZU6uP literal 0 HcmV?d00001 diff --git a/barcode/src/test/resources/org/xbib/graphics/barcode/pdf417/normal-structured-append-2-of-3.properties b/barcode/src/test/resources/org/xbib/graphics/barcode/pdf417/normal-structured-append-2-of-3.properties new file mode 100755 index 0000000..c6cc5d0 --- /dev/null +++ b/barcode/src/test/resources/org/xbib/graphics/barcode/pdf417/normal-structured-append-2-of-3.properties @@ -0,0 +1,8 @@ +mode=NORMAL +rows=5 +dataColumns=4 +preferredEccLevel=0 +structuredAppendFileId=123 +structuredAppendPosition=2 +structuredAppendTotal=3 +content=is a diff --git a/barcode/src/test/resources/org/xbib/graphics/barcode/pdf417/normal-structured-append-3-of-3.codewords b/barcode/src/test/resources/org/xbib/graphics/barcode/pdf417/normal-structured-append-3-of-3.codewords new file mode 100755 index 0000000..631eccc --- /dev/null +++ b/barcode/src/test/resources/org/xbib/graphics/barcode/pdf417/normal-structured-append-3-of-3.codewords @@ -0,0 +1,5 @@ +81111113411111441111232614212214111611152411142231111235711311121 +81111113611111331262122121124421111241166113113161111133711311121 +81111113211112542451111223521211235212112352121131111163711311121 +81111113111142431423221214232212142322121423221211115152711311121 +81111113311132241161112411611124411115136131212131113224711311121 diff --git a/barcode/src/test/resources/org/xbib/graphics/barcode/pdf417/normal-structured-append-3-of-3.png b/barcode/src/test/resources/org/xbib/graphics/barcode/pdf417/normal-structured-append-3-of-3.png new file mode 100644 index 0000000000000000000000000000000000000000..d1c4dcc8184a27956cd86596424d00b42e2bbf94 GIT binary patch literal 10645 zcmeHNZA?>F7;Yr!0@YtrQXOVJ@Qi(X5)Piqy49bs z&hiO9;DL2+C{jN8%WtVQ&S%08^5JpP0b(;Htb#=QkN{JY|?gmOC z-6O{Ge7@^kKEu3)Oy=gKRR`N7wuwAdT)-{utnAFAP=0lvaUf=}bRZ64Rb(2XBx%)^ z&6zWV0$NKFXNRlxL|I{_W;-_rX;vRW-tOq_ugXjvdAv#)uH1>V7~+o=dn2+>-M79^ zQpoUfvFvkKW6r3ovT)?_HCBjV^`W|{T2Ymkm~nXQS`m0t!D>z2(H&WQGB-pxdpn-9 zpF>wnkp`PGcIiW`(&xSB?GXX?KhZh?(>RMBX_~$L1W6Q^jo9wkn7MO7A(s?S(!rZ{ zgeataS!p(dKSzzaAgUGB+1+?~*r>r-&O@m&xfFf8XQwAm4ATPB0;>gnM6kEOmJ5++ z5uiI*t;wW&12dE{^_d@&)6w*pL6>tn%yI5#!RoQ*%nSCXx@PrmCVMYaqH0#h^y4XY zQ!8+l@6yGR(B!TkEcyA#DfWJ+-pgdO2nD-XVw7}m-H3}&aKl+H>Lejg>JJ)ZSqH`0*@#D#?=CY#*vI@N7+`zr{cKY;+~-GHZHMB<7r1W*dwM3|!) zIW8{Spxim`PSYH4MPllNcsw}&VJ*-G7M2>NmRQr{^{)_>HV^=%A=+%q)b`V?RGBF* z0B~U)&_4XqvXrS@RiWZOF>KUf0)_WmNIqTE&*)n zQbt#9 zE5K$FfVd2QwdRe1EJcT!;zaI=4ayQfSwfmA#^w?LQDZG-Zwh2tbRLC4qERMt{0A5? zO)Cx&rs?0y8dekh=$3U(*qh#fC9s7wyE?yvEV%yAJ9L*TByf9am_DHPzhnbbLtopcz@@-J&$10!QoNk1de-AA0Z52H%i#hQVPXF&Fj@iDg)#-p7t^?iL{+z@J1caCSa}zKx#=^K~KBkL2GBA%YhSRzH;S{$eI+D$5%=yM#kI70a zv>D*9SlGWOs@H|C*Tyo$m4zrH8LY4uBk%j#?`3}b5i%GBAq(rgPelJ;Gvv#R7{3bS~IgmI=PFv;5CtXb$IzeU|Xf(e`ba-*R6feNW5o H!)N~ibEY{9 literal 0 HcmV?d00001 diff --git a/barcode/src/test/resources/org/xbib/graphics/barcode/pdf417/normal-structured-append-3-of-3.properties b/barcode/src/test/resources/org/xbib/graphics/barcode/pdf417/normal-structured-append-3-of-3.properties new file mode 100755 index 0000000..09c7239 --- /dev/null +++ b/barcode/src/test/resources/org/xbib/graphics/barcode/pdf417/normal-structured-append-3-of-3.properties @@ -0,0 +1,8 @@ +mode=NORMAL +rows=5 +dataColumns=4 +preferredEccLevel=0 +structuredAppendFileId=123 +structuredAppendPosition=3 +structuredAppendTotal=3 +content=test diff --git a/barcode/src/test/resources/org/xbib/graphics/barcode/pdf417/normal-too-few-columns-and-rows.error b/barcode/src/test/resources/org/xbib/graphics/barcode/pdf417/normal-too-few-columns-and-rows.error new file mode 100755 index 0000000..eff26c5 --- /dev/null +++ b/barcode/src/test/resources/org/xbib/graphics/barcode/pdf417/normal-too-few-columns-and-rows.error @@ -0,0 +1 @@ +Too few rows (3) and columns (3) to hold codewords (20) diff --git a/barcode/src/test/resources/org/xbib/graphics/barcode/pdf417/normal-too-few-columns-and-rows.properties b/barcode/src/test/resources/org/xbib/graphics/barcode/pdf417/normal-too-few-columns-and-rows.properties new file mode 100755 index 0000000..550513b --- /dev/null +++ b/barcode/src/test/resources/org/xbib/graphics/barcode/pdf417/normal-too-few-columns-and-rows.properties @@ -0,0 +1,4 @@ +mode=NORMAL +dataColumns=3 +rows=3 +content=This is just a test. diff --git a/barcode/src/test/resources/org/xbib/graphics/barcode/pdf417/normal-tracking-sample-2.codewords b/barcode/src/test/resources/org/xbib/graphics/barcode/pdf417/normal-tracking-sample-2.codewords new file mode 100755 index 0000000..0453447 --- /dev/null +++ b/barcode/src/test/resources/org/xbib/graphics/barcode/pdf417/normal-tracking-sample-2.codewords @@ -0,0 +1,28 @@ +81111113111115162122212513313213421111433113224115141311211141521112161421122135111141441423221211111516711311121 +81111113411116125331211131122512222441112111523243232111211152322116311221143411216111322111432351111422711311121 +81111113211114522252121223512121144113121153231131121162111213441111336114431112211112542212216111112155711311121 +81111113311212342131322341311124232312132213314123332112211221352112223431142141221141511114252131121234711311121 +81111113311141331161122341112422121442121151224121611132211143232421411231132313311135213112221531113521711311121 +81111113311152132112115421116312211211541111336123521211116133112121114551113312122121442261221131115411711311121 +81111113111242421111414421131324111141441423221233311312312212242331241111143331211421332324212111124242711311121 +81111113411213144312222123143112116213215111314111611124412232211212613141112125511131412161113241121215711311121 +81111113611231212112115411113361132121431321113521321134111213445111341111121146111213441111336111123261711311121 +81111113111341421423221215122321211133421141512211223224311111363212214242421112111141441423221211134142711311121 +81111113111241164513111121115232114114414131311311431241311325112313412111621321511131411161112431123421711311121 +81111113111322521161331131131161143122311443111261113221111232614312411111522411154211121112114611133161711311121 +81111113311512311512151115113231212114153112234111132126311413311331422114232113223232211423221231151231711311121 +81111113511321314422211141113133432321114111222451111125216111324111313331114331211145211111512541131511711311121 +81111113121111461111336123521211134321211112154215221132234211222342112223421122234211222342112212111245711311121 +81111113221122341511121515111215151112151511121515111215151112151511121515111215151112151511121522112234711311121 +81111113411411143511221235113121216111325111314111115224511214211161122361111133123211524113313111135222711311121 +81111113321151132261221111113361235212112162211213111343215411121122234211232143111112462261221132115311711311121 +81111113312115222112152312132125311132512123111633222131311322412211213531422311133141221113141531211522711311121 +81111113311521134111313311611124231133134111141431233212221143222116321111621321411131331161112451151121711311121 +81111113221213511132162123411222111612412251212221131252123131421122214415331121143311221442212111212442711311121 +81111113112134231511121515141311211215231121322512323213332213212411142221113342124141135111115211213423711311121 +81111113321121166211113223113412132152121261132111114315512211145123212134223111541111221161112452111421711311121 +81111113312162111434112151214112143121321112154221121451111422511122224322612211511133121111336111221136711311121 +81111113221312242111415241111243142322121423221214232212142322121423221214232212111232343323122122131224711311121 +81111113121143143311241241323211411311153112211621315311126121314513111141151113113311524115212112114215711311121 +81111113112232511242232111231531231311512242122261223111116132123133114114221331221361113163111142134111711311121 +81111113121333311132412313151213222414111114121611232512321112343121324112233222123242213241211312133331711311121 diff --git a/barcode/src/test/resources/org/xbib/graphics/barcode/pdf417/normal-tracking-sample-2.png b/barcode/src/test/resources/org/xbib/graphics/barcode/pdf417/normal-tracking-sample-2.png new file mode 100644 index 0000000000000000000000000000000000000000..5ce7edd4217af1498ad9724cc38375703d4bd508 GIT binary patch literal 107524 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/115.0LBN77 REDDY DRIVEMELIANABC0260610ZGI00611ZRTC TEST12Z901263303523ZN22ZN20Z0.0015631Z 9K50224199ZGIBI02840156CADPaintingNO EEI 30.37 (f) 010S630151821EXN26Zb1d7 diff --git a/barcode/src/test/resources/org/xbib/graphics/barcode/pdf417/normal-tracking-sample.codewords b/barcode/src/test/resources/org/xbib/graphics/barcode/pdf417/normal-tracking-sample.codewords new file mode 100755 index 0000000..9355b96 --- /dev/null +++ b/barcode/src/test/resources/org/xbib/graphics/barcode/pdf417/normal-tracking-sample.codewords @@ -0,0 +1,27 @@ +8111111321111425412113321331321342111143311322411514131121114152111216142112213531111334711311121 +8111111351112133511131411161122341111612122162212211422321611132211143232152114141111414711311121 +8111111331111361121322516112312112232142211214511111336114431112511134111222113561112213711311121 +8111111321121226111414143121122514214131232113141514131111114144111243412112415151121151711311121 +8111111311114216411212155111314111611223411112161111623251211412122154112161113221113513711311121 +8111111331115114111211462252121215311321111312442153131121121154211163122112115431116122711311121 +8111111321124151111141441423221233311114213221152111334212151115151413111111414411124143711311121 +8111111341121413311221165111314111611124433131113111261223315111112361214214131161121132711311121 +8111111311122451124411133113521121231242521131131262311111113361235212111132162141124113711311121 +8111111311133332241115213321112411133134151413111111414421131126111233331111414421133241711311121 +8111111331124132116111241312421321133115122311613214221235112311311232233216211121123413711311121 +8111111321132161114116122131153122231241226122115111341111121146111213441111336141134112711311121 +8111111321151223142322121512232121113342123241221114232331111136321221424242111211151215711311121 +8111111341132222511131411161112445131111211152321231611225113212312242212123411341131412711311121 +8111111311161142125121141322224131231151114216111262311111113361235212111161331132111261711311121 +8111111312112226211322332142331112414113221122341413141214131115132131241514131142112151711311121 +8111111341141213511131411161112445131111211433124232311111611223611111331311411521135131711311121 +8111111342114212511233111231324122612211111211461252211312442121311162211243121332116121711311121 +8111111321211514111216141112161411114144142322121512313111122424214233111241411341211431711311121 +8111111331152212411133311111512552122131122352115421111231213511341231214122322141151113711311121 +8111111311212244325112211232122421541112511241212261221111113361235212111343212111212541711311121 +8111111312122423421112422233231151111152424211121111414414232212151231315121114211213324711311121 +8111111352112132432321116111113321611132511131412111321661111232116111241161112442111413711311121 +8111111331216112115223123211531123126111113212254221511131621112115224112323114131221152711311121 +8111111321222125222212151513141124213131311611312132231314232113112125141313111642131141711311121 +8111111312114413412124126221122161121231312232133112413251322211421231312112523132114132711311121 +8111111311223152114511132531113121126113143213211422133131135112214211245113321132135111711311121 diff --git a/barcode/src/test/resources/org/xbib/graphics/barcode/pdf417/normal-tracking-sample.png b/barcode/src/test/resources/org/xbib/graphics/barcode/pdf417/normal-tracking-sample.png new file mode 100644 index 0000000000000000000000000000000000000000..ea785c963d5b5616387b3adcb764b1d0f7018afc GIT binary patch literal 86530 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 V8Nc}MH2k-A^@ewf-j3S;zW_szfCT^m literal 0 HcmV?d00001 diff --git a/barcode/src/test/resources/org/xbib/graphics/barcode/pdf417/normal-tracking-sample.properties b/barcode/src/test/resources/org/xbib/graphics/barcode/pdf417/normal-tracking-sample.properties new file mode 100755 index 0000000..6d6c2f1 --- /dev/null +++ b/barcode/src/test/resources/org/xbib/graphics/barcode/pdf417/normal-tracking-sample.properties @@ -0,0 +1,2 @@ +mode=NORMAL +content=[)>010212550840497954912206750221FDE5100871431121/15.00LBN30 JOHNNY DRIVENOUBURDEGAReturns Department0610ZED00611ZJamony Reits12Z1646888994415Z11867621520Z10031Z100231290331000125400079549122047534Z0235Z01 diff --git a/barcode/src/test/resources/org/xbib/graphics/barcode/pdf417/normal-utf-8.codewords b/barcode/src/test/resources/org/xbib/graphics/barcode/pdf417/normal-utf-8.codewords new file mode 100755 index 0000000..01d5f69 --- /dev/null +++ b/barcode/src/test/resources/org/xbib/graphics/barcode/pdf417/normal-utf-8.codewords @@ -0,0 +1,15 @@ +81111113411112432112142423332211311132511241411331111235711311121 +81111113511115211112451221611132121244124111232361111232711311121 +81111113211112544121511211232341243111326221311161112114711311121 +81111113511161112121222512233321424121212122323211115152711311121 +81111113211141254211222341131412111444111161112431113323711311121 +81111113411142131343212112512213114125212121154131115312711311121 +81111113211232421241411322113242112332234141141111123234711311121 +81111113611212314111161211611124251132121221541111116232711311121 +81111113111222531121153311223251311111632113115351123212711311121 +81111113111331341111232621133142333114111123211611132522711311121 +81111113211236115111223241123231411113154422211131123223711311121 +81111113111314422142142113532111312161121123153151133112711311121 +81111113111511161313222333241121323111153322132111143331711311121 +81111113411321232322411221416111311331231221612241131214711311121 +81111113211511511432132121116213113113251121234332111162711311121 diff --git a/barcode/src/test/resources/org/xbib/graphics/barcode/pdf417/normal-utf-8.png b/barcode/src/test/resources/org/xbib/graphics/barcode/pdf417/normal-utf-8.png new file mode 100644 index 0000000000000000000000000000000000000000..39fbc9c6e2e090adfd5c19cf1d7b77a4031ec40f GIT binary patch literal 32982 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 literal 0 HcmV?d00001 diff --git a/barcode/src/test/resources/org/xbib/graphics/barcode/pdf417/normal-utf-8.properties b/barcode/src/test/resources/org/xbib/graphics/barcode/pdf417/normal-utf-8.properties new file mode 100755 index 0000000..2e24968 --- /dev/null +++ b/barcode/src/test/resources/org/xbib/graphics/barcode/pdf417/normal-utf-8.properties @@ -0,0 +1,2 @@ +mode=NORMAL +content=12üƒœ˜Ë±‹3asdf23456789012asdf89012asdf89asdfaf2 diff --git a/barcode/src/test/resources/org/xbib/graphics/barcode/pdf417/truncated-basic-ecc-level-0.codewords b/barcode/src/test/resources/org/xbib/graphics/barcode/pdf417/truncated-basic-ecc-level-0.codewords new file mode 100755 index 0000000..6fcc10b --- /dev/null +++ b/barcode/src/test/resources/org/xbib/graphics/barcode/pdf417/truncated-basic-ecc-level-0.codewords @@ -0,0 +1,7 @@ +811111135111115221112136341113311 +811111135111112542111413113122611 +811111133111116321231143143211231 +811111132111425143211132241113231 +811111132111321631112612431313111 +811111134111411421421124244211211 +811111133112315132251121232113141 diff --git a/barcode/src/test/resources/org/xbib/graphics/barcode/pdf417/truncated-basic-ecc-level-0.png b/barcode/src/test/resources/org/xbib/graphics/barcode/pdf417/truncated-basic-ecc-level-0.png new file mode 100644 index 0000000000000000000000000000000000000000..2d68807ddb93af954eac18d0b4b227e432906ee4 GIT binary patch literal 7357 zcmeHMZA=?=9Ig;HrCo^00K1r~VT>$oWpa`^@lfm*gkfi#Nz0(+rAHgE4#Hf)>j*Lh zH(&xqx)o><6T28lTMDZ{*Of6DbTEn#T8@`wTV{at#gMgK25Iqs$Mmz`bb4Q!`*6AZ z?|0Ah{GR9O#gDk@dm{HmQYe%?86O<|m_i9Ng6rAm!4IYNi*tz-%FAD898LSAQfRrG zri(aC3%|L%J^R8yhUkKcb)Rlk?k(PaWEX9Re!o05(_!?y*-_Zw-fKQp7m7sXPaHhc z32A|^W)K(eoG|Ryy zE>xzz6U!~N_;Y;9rr(niVxYwg4w!OND%mel5n(Uy)v3pXWFAj*n)^^9zgucjhkL zfu$%6_Y>qn$cc~``A1l@Fb{~2te&V?ArA#{$Vq|4Kw=8)&u5x_kZ&gMYULQ; zo>aEt%DcZfx#G<8O=$X01XSI-asGE(xf}I;nxcEdPV4Bk%VxcGtp%3g<^wPQs6w^+ zL9_^-96776uKhHzBCq<_IQeucCaGf(Y885WjCmVX*0_Y*kxrraM(Bt1Kqp~L7UqG5 z1<%&=8vU$rrzI4vvn*Eyk2SfEY$08A)PT#_rfaYMC0F(?ojl0Ql@D!gHZZU=glPdo zr@=nwLE#&Yep>G1P2OOj{INe4Hm&Tb0PmL;WZPp9IA%>> z7lC_s%W|8$^Pv~-36M2?6A}Gg*r-v%2039SZx3~$jRq*i#E#fjGQ9Ny+f3doeK_l# z{k27KPP3o?y>(xV0fdb}InQ`TVQyWHHY>WVsf9TA^%CkNYI+qIit1!mJ}(K-ONXrP zuSghT%HlcIRlnd0AhgFQ{n9jts3ugx9|56caZ+HL&hX;ZL}p!R^8!&=XxJxvYR3{dkUTd;RPrSuq8t&HUWr3o31!Q z6Uaf)QF2f;3yG+|M?F{sCzU17X;i1zahG3ViofM6!IQsO3mYnlC9dgXfVQF7p7k>b z2?}+}lA-`j#$K1jL!d`^hp+*5eiJSC?tny_Lj(%-XSPF@c04PoilbONMnck?zDp-w zgyz^Vaur#fHX&DpADS8jHoULc*~pM1E}RE>RO?D2yGU}9vh+lgAzln`*xBiHS4&Ng zDlql=@hI#dHq_6iYk936pn;$l8QXCMjsA&|`7F73_(wb@En`aBTo2xus8oTG2Rpvv zHVVoSEex&9a%7n@uk?7``aEJ@Xqy7H$HtB`A;&_F<$sBm>bJf%c-YifEB?P^#xd?u J-TV3H{{YKDxF-Mr literal 0 HcmV?d00001 diff --git a/barcode/src/test/resources/org/xbib/graphics/barcode/pdf417/truncated-basic-ecc-level-0.properties b/barcode/src/test/resources/org/xbib/graphics/barcode/pdf417/truncated-basic-ecc-level-0.properties new file mode 100755 index 0000000..180375e --- /dev/null +++ b/barcode/src/test/resources/org/xbib/graphics/barcode/pdf417/truncated-basic-ecc-level-0.properties @@ -0,0 +1,3 @@ +mode=TRUNCATED +preferredEccLevel=0 +content=This is just a test. diff --git a/barcode/src/test/resources/org/xbib/graphics/barcode/pdf417/truncated-basic-ecc-level-1.codewords b/barcode/src/test/resources/org/xbib/graphics/barcode/pdf417/truncated-basic-ecc-level-1.codewords new file mode 100755 index 0000000..9638ffb --- /dev/null +++ b/barcode/src/test/resources/org/xbib/graphics/barcode/pdf417/truncated-basic-ecc-level-1.codewords @@ -0,0 +1,8 @@ +811111135111115221112136341113311 +811111136111123242111413113122611 +811111133111116321231143143211231 +811111132111425143211132241113231 +811111133111332331112612431313111 +811111134111411421421124244211211 +811111133112315115113132223323111 +811111131111623254211112111254211 diff --git a/barcode/src/test/resources/org/xbib/graphics/barcode/pdf417/truncated-basic-ecc-level-1.png b/barcode/src/test/resources/org/xbib/graphics/barcode/pdf417/truncated-basic-ecc-level-1.png new file mode 100644 index 0000000000000000000000000000000000000000..8ae69fe45efbb089ea957e196758b32a18790bbf GIT binary patch literal 8662 zcmeHNZA?>V80{qN!`6^(fd z)xOa*pPSRq@4eBHa4U`%^dPXWZfoB2r{W`V+z#qz9Rd)eXIUR+1h5QY{e34 zjn=h|j6vhGhwU}RxeLh{awWO_AX3f`XP5XI3@2@u96Na2+y!~w@*cO51v#6=F{8~> zyJK#v>zew`%nEvs54$ghhe6Jfj%ROs;l@B|s!o}kDy~vyb4+6yv69HS4~5=40d;}W z-22{h#i*HP2(_kYLv=$b?JdFu&9$z2i4rZ&p_*t+$g%nh3lo*iUiwmnZRNqLsfj2j zXUuBqO2K;DONC_(a`&n1Mmxlo4C}_l<>6?mJwMRHSa8hqRN}d*fhe{)Np_(9uv^{~ zEwtwCza4;uBAv8Vf2aG&PpLYII7{9nYBn@Z?eUUukZ`;#(_t1YLIwM<2&~7*2c$DB z4oKnV<*yDR`)6-Fxk)gt!EjIXYd*?HkJab_bEV^@uA!v4hvjzhjc?}960~K?uABQO zU_JaOqzKU8)l9Wj$$JO=G3>`^hkB+i7$k9TMJwHwIa~oa0C`+$WUX0e9Y{LbhOQdlIc3%0w z``&Rn_p*4Kz8Z*un@#Zkf~y&$Yc7lH7bmzCt%Czevdk=$Z%@RD?uX&~m^=uH2stDF z3v0j@pi|*T)>EbdSPx7$+YbxHdT_^0M~@9}FgO#~g0M=Z#2ZenaR84%W5O!<5n=6z z?uKrV_$=7^?H?h%j;^pI-FbdEq* z$2Qyrs34L+97w@fY38Vc8C%AK`=*)2NGWXIFc;(@NF>Oa_^;SuXb2CpVQ0WvU!7?C zmT&Cutk>fae+FdlTH31XO`;!Q^0*eQZ(4<~rzPtZkPd(h;2~cRY6(W5#}-6zS*mMm z6;Xj9adK{LLV|rJ1xvsj6c*vdM3i$fp6Cc%;;b}t0L%_W@#2$Ac z2X8^}dZN0ik45y28WGhb)x8U>P07BnOiomRV_uE-Ab3F$%q}S=YDYqrG-O<6Rx?splM^nKAqDv@f5%<{sHX7t#0V{dIMPh}wTI4X0 z!?3OlIR=*Xp+BBKs+E$J0o)&WY2e~8UG=0?F8$ z1;E)%vOvo9nf&qOgOH-@>!yP|6ljG>q`<8^Ia8?lMP#4dv-7}GW(#tp(T#twk#I02 KPW^G_nLhz@N^HFV literal 0 HcmV?d00001 diff --git a/barcode/src/test/resources/org/xbib/graphics/barcode/pdf417/truncated-basic-ecc-level-1.properties b/barcode/src/test/resources/org/xbib/graphics/barcode/pdf417/truncated-basic-ecc-level-1.properties new file mode 100755 index 0000000..e5e16ba --- /dev/null +++ b/barcode/src/test/resources/org/xbib/graphics/barcode/pdf417/truncated-basic-ecc-level-1.properties @@ -0,0 +1,3 @@ +mode=TRUNCATED +preferredEccLevel=1 +content=This is just a test. diff --git a/barcode/src/test/resources/org/xbib/graphics/barcode/pdf417/truncated-basic-ecc-level-2.codewords b/barcode/src/test/resources/org/xbib/graphics/barcode/pdf417/truncated-basic-ecc-level-2.codewords new file mode 100755 index 0000000..a138e32 --- /dev/null +++ b/barcode/src/test/resources/org/xbib/graphics/barcode/pdf417/truncated-basic-ecc-level-2.codewords @@ -0,0 +1,7 @@ +81111113511111523111214434111331121224231 +81111113511113231131226122115132141441111 +81111113111112461145131122341131611132211 +81111113211142512133322132241221233221131 +81111113211134141161112433114131351123111 +81111113511141221223224113311422222411411 +81111113311231512311233213314221233124111 diff --git a/barcode/src/test/resources/org/xbib/graphics/barcode/pdf417/truncated-basic-ecc-level-2.png b/barcode/src/test/resources/org/xbib/graphics/barcode/pdf417/truncated-basic-ecc-level-2.png new file mode 100644 index 0000000000000000000000000000000000000000..034e3d5c86175e0a900e45a63f0fdfde0d9cf489 GIT binary patch literal 9543 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 literal 0 HcmV?d00001 diff --git a/barcode/src/test/resources/org/xbib/graphics/barcode/pdf417/truncated-basic-ecc-level-3.properties b/barcode/src/test/resources/org/xbib/graphics/barcode/pdf417/truncated-basic-ecc-level-3.properties new file mode 100755 index 0000000..330d750 --- /dev/null +++ b/barcode/src/test/resources/org/xbib/graphics/barcode/pdf417/truncated-basic-ecc-level-3.properties @@ -0,0 +1,3 @@ +mode=TRUNCATED +preferredEccLevel=3 +content=This is just a test. diff --git a/barcode/src/test/resources/org/xbib/graphics/barcode/pdf417/truncated-basic-ecc-level-4.codewords b/barcode/src/test/resources/org/xbib/graphics/barcode/pdf417/truncated-basic-ecc-level-4.codewords new file mode 100755 index 0000000..766397b --- /dev/null +++ b/barcode/src/test/resources/org/xbib/graphics/barcode/pdf417/truncated-basic-ecc-level-4.codewords @@ -0,0 +1,11 @@ +8111111331111235211121363411133112122423231511221 +8111111341112125221151321414411151322112222243111 +8111111321111254611132212233123121421124244211211 +8111111311115152211513223312133111232116132423111 +8111111341114141141141141315411152221311112252131 +8111111341114213214213221133233111221334133214211 +8111111311123234141231321132321421121424122523111 +8111111351121322341123124422211152141121115221411 +8111111311122253231112522421124153123111112221441 +8111111311132522131125221114252133121232414212121 +8111111321124124312142221315411122114421231133131 diff --git a/barcode/src/test/resources/org/xbib/graphics/barcode/pdf417/truncated-basic-ecc-level-4.png b/barcode/src/test/resources/org/xbib/graphics/barcode/pdf417/truncated-basic-ecc-level-4.png new file mode 100644 index 0000000000000000000000000000000000000000..578b40d6caa7710fea49c16380ea5080323cd9a3 GIT binary patch literal 18509 zcmeHPe@v5S9%pjkodvf`7J9|um_`kmg~cB0%?1aDik_1#dUrtzLI+x0X*=s^4-hsm z;N4IM;$gH_nL3F%!1C)YwX~?54#Xl%Qwnce<^;ypmfk5pCdj<+_i1tdf4OA0y&-w? zh9=MZ{CJ=5r=K5B|MpH|Lhz!{MSgyM!J9X&-{$A{i$FiW=l<|Z_=n#=Di^f)`E7b} z^ZK|y{$*#aIj%mtA=tmq_2Hdg{dV)!^(VMbRJY5|yPiD$I^)S)dwXW7rXlrqtVA&< zG`&{a5@ZZ8s2*7&`uR%H36VHEp;TICvkluj%@O^}`8DnyE0>d1p?-c~l?OlW=k=aU zc~TV>B;m%(ua2jha&Ar@PEFcv<{&BfRQINr|;wWz@Gp$sod8^~F?UW)$v2Sqvko{;;i2hM|!GqKI zag2Xm*QET>HLYdMM2O2VBCJUnvX<>m%GDx*b`@&;Q@05Qj=1&_ z{9+!*OeXWHz!y1YBQP6**$A)^t`43qQofqj+GSu*eY~qQOu<7ga&z*cCu{4_q~-nR zBwexe2=J(_4*RfOTNPxS`zs+dT1s4PzsZ(87%wrtdqQ;3b_f{PDMi7zg5*0YV|2=u z*i$X9+art@49a^;IBToo3`)5&bo$bI}F;!j6F275>uW zK%)4$s!CNy@|uaMHlq6REeRe*?IJj`Q(h*wo)8O}@EG9{Z`Bp4oD z>Deetq?XT~LPVq|vz=<;@b087REu#J;2}XngQHl}pxIR&tzT+6YNSoq7VCk?RjB8w z95Xa#)tDEO*=tN0`EgW8R0eiTn*(-;*_oQu@*KL%-Sa*@P_j#CTL!RmPNxOesm%7c zD(aA<*#5Pva{89m>AKc9u4Xm%M0TzOP^hUJMRcVK;jt*mF;{OJ-r1g~3h9>=)O4fDl$Z6qWD;z7ZJV)_dBGHW_| zhVzC;k2x#dtNpo9$5;0AX}q4(m9pl=DTJHP1K`84x_b+Z0RrR?yR9cM2cRfu1mdtk zezj+KW_LbZ^Cm3s7dZ<5`Ls@xE|OlPAG8HYx`G6i&Ibgb=d`A`uu_xH;Jc1SbS~QR!5>@!AKnTHfrhpO)Jwy%`gH?6YkJQD9a9uE8Q~g3rq3(*p;wgy6P11tdjT_qe5_Q{1?h^2{#8iC zpcik|%PBTChSTRxcnYgWu#3Ie3l{J}hJi*i6Gtnp|hMW=-G`E$iyX7r5w zMif0n71N`cVoRGRos0Gogz5lPK5EIt$LjUpu)V~k)7+)ZBm^k~5JBRFF>^n=d9+|5 z0wPWnq8J)b!ECUx+I_s@8R3p-fry)Olc_q|BR~Xx1+#cCm+4#%Vwp@f6X4}zihYe? z1BCZk0rO6Jf%@TOKwa+8Re=1(Fj0up;F}?ADfZoo^3tVeB|oAfGE2xTA+rmA(7l*l z`2Su=l|m)p4>ALUhr_AmMV5&$u;ejK4RTX$3y4S)7Ha^9+<};6X-j+TPsj!30zg3a zgH>^fA>6<28^K_t2kjhCosuM=C5_cJu-1?}(3I+O+rL{#+=WfDcu?b2lul83Z#2nGaMi9RL94wi#P2+?p*{^T(JtK<$8 zK=bVNH?_zUW!*Bs1;AY}j`SuNQ1408IroEcbdoj#F+u^*5o!oOK<+>YJULzVVLIj} z>{WKaTHtxaSAqdmM;9bOG6MtWhk*QXK?gKO!}!Uao@0#mu%BEZRFP`{@Ix+(APxhH zmCPKWx(#T0LA214402$e1jbM9Ko*oZqP{O8ArEFjnFVEbD6>OB$p873T%=lPt$=a! zyAcrbP;xT-Jqqj@%%)*X%|08@3RPH=tuuosbO`9e<+fRJ-UnqZ1T)?Kd&fYMLj};A z^2R(INQ1pf5Bp0%$GQgl>tOUi&21ffFis8`p~{E=1QrW$C7>xl+=P#=A)1jqTr6!& z`$z~NcSA{|djovvY`1}&Y{H-cT2A1b0Ivf|1q_9R#4!{H89aaKbm4O{z^YJMik1ur zk!6t{a?XeuEVHuAYs~Cf`0CCSsGn~&Mx_n24J3k)i2-lIN^(G|^ZNJV85xbDm+kS$ zf}!Sl;SF^*2p*7pz;Ykb)wa~$U*JnU2=1t82+j?Ujz+P+yD;a?%k4mw4O%6siZBui zC$uXJWIAso;e?5%P_yKJsK3H@&x>xLv0;`1C{BsH(~HPJf1DuFdGi826Lt#1Bzpa$ z?f}!CH}Sv}pc{&$&??CzBw&yndYdEx?`=M1alu0oy%Nq}{cG#2Pcub{6kEj z!0*KHuyJrGG zN31b9(fa|-%IM3Bt?9j%+;{{y%nd44Wr&J}~owH;3}6_HS#FPFy2 z5^;BYzTvnkgAS!DILgriUnmW{o-9LQ9EfTcJ;e0EvhT~k1vP7{YVQNlYLFgwcLoU_ zntb6ma$Kps8Z^AScRSUb?6Bth@_hc>+7(V# zDIetWsk(+Rh;ukp-q0YpgGH2+4T7i!;_@IclMJ6PpBT%Mbgc!-x`eh^k3h1*;|nJ; z%kH$!r&HGibq)*hJiXHjwO{+Zo_~)w>{(GDOduYS<>9x_TwSo-Q#+p_)hcbsK z{UcV7SdyBJOLP|-mh$6ow@nv{-*(7Lm#!WO{KCTef>PCt=e(DcuDUUPC~+^%%T{w; zf!mU9)-`11^JUvJVq~SKb3JoCr`v{tVweT`u`W;n_nno0y4jk16n`gZ^M=Ir^}kOq F{ttVlamWAw literal 0 HcmV?d00001 diff --git a/barcode/src/test/resources/org/xbib/graphics/barcode/pdf417/truncated-basic-ecc-level-4.properties b/barcode/src/test/resources/org/xbib/graphics/barcode/pdf417/truncated-basic-ecc-level-4.properties new file mode 100755 index 0000000..ee031fa --- /dev/null +++ b/barcode/src/test/resources/org/xbib/graphics/barcode/pdf417/truncated-basic-ecc-level-4.properties @@ -0,0 +1,3 @@ +mode=TRUNCATED +preferredEccLevel=4 +content=This is just a test. diff --git a/barcode/src/test/resources/org/xbib/graphics/barcode/pdf417/truncated-basic-ecc-level-5.codewords b/barcode/src/test/resources/org/xbib/graphics/barcode/pdf417/truncated-basic-ecc-level-5.codewords new file mode 100755 index 0000000..a20be05 --- /dev/null +++ b/barcode/src/test/resources/org/xbib/graphics/barcode/pdf417/truncated-basic-ecc-level-5.codewords @@ -0,0 +1,16 @@ +811111135111125131112243341113311212242323151122112224141 +811111136111214114144111513221122222431131112612431313111 +811111133111126221421124244211212352121123521211235212111 +811111133112113514232212413213211312414115131411331411311 +811111132111422452131221122261212421421111211164221235111 +811111135111422114411411232113412113621141211161132412311 +811111131112333313222115223125111131341333111332122424111 +811111135112142121144221331321131112441312145121112311621 +811111132112226142124112142311322133113361223111112221441 +811111132113314221121523221511231513121331251221423212211 +811111131112421533223112113113522216311113115123341421111 +811111131113154111312432331251111113235113212341133312221 +811111132115112441211233121151512123131442311123211414221 +811111133113231313216121422122135113112333114131222241131 +811111131115124261112312411241132351131111213252311211621 +811111132211213531321511242115111114252113242113341121411 diff --git a/barcode/src/test/resources/org/xbib/graphics/barcode/pdf417/truncated-basic-ecc-level-5.png b/barcode/src/test/resources/org/xbib/graphics/barcode/pdf417/truncated-basic-ecc-level-5.png new file mode 100644 index 0000000000000000000000000000000000000000..a6f089dcf51cc5a18eb05f390036bf83a6fb554a GIT binary patch literal 30832 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 CIRlyi literal 0 HcmV?d00001 diff --git a/barcode/src/test/resources/org/xbib/graphics/barcode/pdf417/truncated-basic-ecc-level-6.properties b/barcode/src/test/resources/org/xbib/graphics/barcode/pdf417/truncated-basic-ecc-level-6.properties new file mode 100755 index 0000000..d95ba6d --- /dev/null +++ b/barcode/src/test/resources/org/xbib/graphics/barcode/pdf417/truncated-basic-ecc-level-6.properties @@ -0,0 +1,3 @@ +mode=TRUNCATED +preferredEccLevel=6 +content=This is just a test. diff --git a/barcode/src/test/resources/org/xbib/graphics/barcode/pdf417/truncated-basic-ecc-level-7.codewords b/barcode/src/test/resources/org/xbib/graphics/barcode/pdf417/truncated-basic-ecc-level-7.codewords new file mode 100755 index 0000000..2d20b49 --- /dev/null +++ b/barcode/src/test/resources/org/xbib/graphics/barcode/pdf417/truncated-basic-ecc-level-7.codewords @@ -0,0 +1,30 @@ +81111113111115164111215234111331121224232315112211222414132423114321113224111323311132511 +81111113411124224313131112221162261231111161112411611124421231311253113141222114332331111 +81111113111114441211144311532212134123212212611211423231111212451251241123531111611132211 +81111113311212342332211312152123241123312421121421413411321214312141341132221124111216141 +81111113211145213114312241123132412223123113211531133321132151131161122334223111421231311 +81111113411151221541212133111161113125313311511212241133131211441134122363112112223113321 +81111113111242422411233121221612112332232331241122231115131415112112314314142221212121261 +81111113511222315331211161111331115411311222612151332111511222314111252152112231411141411 +81111113511231132311621121321233211411522312611113111145112115331331213314441111212222511 +81111113111341422133322111242412231123322212213413313312211431411411212512122126412311321 +81111113111251241261132121123611311232234232311116124111312341213114211411134511113361111 +81111113611322114113421142124112121131621311134311112254216122126221311122341131231113511 +81111113311512313313113231113251233223112142311311415122112431232114152132241122323112141 +81111113311332226231121141133131321522114211121521153113131142145321131131112612124111431 +81111113111612412343112111441213131212431221133422341131215112143111116311221235312111531 +81111113221122342424121123322212322511211125241141311322211224321311424122113143111214161 +81111113311424115212112351231311431221221222511312135221112151151111613321123314412124121 +81111113421143111133223225211141135121131161321211121542421141132112611312211433115121151 +81111113312115222111142522112135111511162113112611333213212132333122132333111332233222121 +81111113111543116122112242142121216111323211322334132211215112412311321422114322611211321 +81111113212122521131233312221432214212235212311211341124121115421323133111441312511332111 +81111113112134232513122131311116211514211314232113122422122324122215132132121431223122141 +81111113321126113512211231314311451212113312322122133114211332141513411122123511321422121 +81111113321252111412114311311424132222415111412261112411233211321211114611131343234211221 +81111113221312241324221233111134313212142142331134121231342311215121124113222214121125231 +81111113121161322141611111422151411131334133311134122311111254212211331433213113216111321 +81111113121322516213211131411232153113211423113251133211121112453311116112441113113322321 +81111113121333313322122232311214221114241415111333131132122123151111424332412311221421321 +81111113312131156212113131112216421132314112212411311253123121612311321434122113331122141 +81111113212412411141212523311133113115233211521213411313312162111431122314512211124221231 diff --git a/barcode/src/test/resources/org/xbib/graphics/barcode/pdf417/truncated-basic-ecc-level-7.png b/barcode/src/test/resources/org/xbib/graphics/barcode/pdf417/truncated-basic-ecc-level-7.png new file mode 100644 index 0000000000000000000000000000000000000000..47bbb88a624684d6a516503fd2c8782f00cf83be GIT binary patch literal 92193 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 W*(q~llIP(6UR=BB2c{R&_WdvBT>=FF literal 0 HcmV?d00001 diff --git a/barcode/src/test/resources/org/xbib/graphics/barcode/pdf417/truncated-basic-ecc-level-7.properties b/barcode/src/test/resources/org/xbib/graphics/barcode/pdf417/truncated-basic-ecc-level-7.properties new file mode 100755 index 0000000..03f4f8b --- /dev/null +++ b/barcode/src/test/resources/org/xbib/graphics/barcode/pdf417/truncated-basic-ecc-level-7.properties @@ -0,0 +1,3 @@ +mode=TRUNCATED +preferredEccLevel=7 +content=This is just a test. diff --git a/barcode/src/test/resources/org/xbib/graphics/barcode/pdf417/truncated-basic-ecc-level-8.codewords b/barcode/src/test/resources/org/xbib/graphics/barcode/pdf417/truncated-basic-ecc-level-8.codewords new file mode 100755 index 0000000..d82f42e --- /dev/null +++ b/barcode/src/test/resources/org/xbib/graphics/barcode/pdf417/truncated-basic-ecc-level-8.codewords @@ -0,0 +1,41 @@ +8111111331112144111131363411133112122423231511221122241413242311432111322411132331113251213332213224122123322113142322121 +8111111341112521116111241161112411611124116111241161112411611124116111241161112444222111211531131221531244212112112111641 +8111111311112155316112121141414133125111111322522114115221541211311212611252312114312231121132616122311111312234112612311 +8111111311121416121222252321242121151124222214131422241111222216122234212321222312151214412213311331331212313214333213111 +8111111321115133111245121241215122134221142111611141225113135121411224211131135242111116521213212212412312145121113411511 +8111111331115411511133121142111611142152111321531261311212212243131321511612114111141144223212322123124214131241324111321 +8111111341131142232413111415111312133133323113131411232322142231111422241321241314213123213225113311214213141214323115111 +8111111341122322512113135312122113134113531212211231216111441141442221111131145141141411116111244125211142222113126211221 +8111111311123261325111222311125225411121211162131224214112111542145122111143313112311126221262111114144122131251223211331 +8111111311141216141112162222121531412213222214132321131442411212211414224211114351211241112522134122123222242221232322211 +8111111311125223124111432211311611154212121151241214512122114421331521113111413336112112321122154114212224113312111161331 +8111111311133161142213313242113113311224111221542244112113412222113222331115114311313242311153121323214114411312123221331 +8111111321151421121421241323221321211514113242221511323112141116322115213114113311113136111232341115111613223222142321131 +8111111331133321111541131211431431324211421312133211423111135123431115111441114144222111213111622112513241111216613111131 +8111111312111245132113331133142222331132331151121232122441114411311451113121611211623112124116112231113423111153213214311 +8111111312112424414111143242211211233124121221263232141113213223111514133131131433331211111523221213212523141123121115151 +8111111331143122341231215131231112144212221152314122221342111116532211123212251142121214311132241141124333152111412121151 +8111111332115311411251211222123411423132114313131521133112311522341151111163311115231131143221312231123311211236216222111 +8111111321212126141321232313122323211314212216121111323522312511333312113213133111333114212214141222423124221114122331231 +8111111341161211411513111223116121153212512214115214112111311154132161211163112251132131122252122312422111114315111351231 +8111111311212442144112131143222233511121131212431451221122116212235311113242113111613212121132611421224111514131142221411 +8111111312123233132124131412212412113432313124211124332131221125251221311231422222211513113342212124151112223223232124211 +8111111332113124311134225221121333213212121244123223311252122131116112232612311141141213121252224121241251112232114221511 +8111111311221136412251112212621112441113235121212121134321126113322211513232114122111154234112222111631222411421116331111 +8111111331222232413113222211233334121132322111251411242211161115121511152423111311133332222216112423212111243222132121161 +8111111351211115115112332113513133113123111254211153113212421142421114131152113323113115432122121431115131142411331521111 +8111111342134111142312314211431114221331121221531342212224321131214212233212116112331124162111323133114116321121122321421 +8111111331231124211222342315112231132241314122134241212115121511312512212212324121231413131224225121124112222215132322131 +8111111342122222311421141341124132213312115212324221211445131111313242113112261111124512432321112112341341242112241131141 +8111111311242142431141121211245121441221211262122152131212112253111315411121123641411141211162131242222212512114243112311 +8111111312142223124141131213161213111316221412232111213622131125512112412323111413222115132431211412111621112235114243111 +8111111321215222311422133413211241122421121352213113332121511241112251142412331131233311251232115232211111211164311123151 +8111111343114211431141121134223123421122212412411144212231116221235121211131314311221433213214311144141153113112321351111 +8111111312151511414221211111515221211514141232313124132111315132312511221415111321423212151322211411151322222223231131421 +8111111341222312411122241141124321125132311432211112532212316112214161115312122154211112421223211113431321133511421231311 +8111111312213152144111141321234112512114142112331343111313421411133222312451121112232142113223321421113412211433316113111 +8111111333112241311611311125241122131521122124142321323131412411241211243212143111223224241111252151411233311213151413111 +8111111311225312542111123311312316124111361121124211232211135222111152242411331211135321421215111321612132123123125311311 +8111111311312234211261134112431111121542113131431241313222211243152212312262211112313241114322225112311354113111134111151 +8111111333121232111332333423112132131232321121431313111621423212321122421321312414222312122133233141221331122242332311221 +8111111331234121612111232313412113115321432221122511311331233113111341154112151241132222113113523122422142141311411123231 diff --git a/barcode/src/test/resources/org/xbib/graphics/barcode/pdf417/truncated-basic-ecc-level-8.png b/barcode/src/test/resources/org/xbib/graphics/barcode/pdf417/truncated-basic-ecc-level-8.png new file mode 100644 index 0000000000000000000000000000000000000000..c800f58c101ff0be2e2bad04dd86d185a9756531 GIT binary patch literal 172929 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 literal 0 HcmV?d00001 diff --git a/barcode/src/test/resources/org/xbib/graphics/barcode/pdf417/truncated-basic-ecc-level-8.properties b/barcode/src/test/resources/org/xbib/graphics/barcode/pdf417/truncated-basic-ecc-level-8.properties new file mode 100755 index 0000000..cae02cc --- /dev/null +++ b/barcode/src/test/resources/org/xbib/graphics/barcode/pdf417/truncated-basic-ecc-level-8.properties @@ -0,0 +1,3 @@ +mode=TRUNCATED +preferredEccLevel=8 +content=This is just a test. diff --git a/barcode/src/test/resources/org/xbib/graphics/barcode/pdf417/truncated-basic-ecc-level-9.error b/barcode/src/test/resources/org/xbib/graphics/barcode/pdf417/truncated-basic-ecc-level-9.error new file mode 100755 index 0000000..3aba9d2 --- /dev/null +++ b/barcode/src/test/resources/org/xbib/graphics/barcode/pdf417/truncated-basic-ecc-level-9.error @@ -0,0 +1 @@ +ECC level must be between 0 and 8. diff --git a/barcode/src/test/resources/org/xbib/graphics/barcode/pdf417/truncated-basic-ecc-level-9.properties b/barcode/src/test/resources/org/xbib/graphics/barcode/pdf417/truncated-basic-ecc-level-9.properties new file mode 100755 index 0000000..0bbb11c --- /dev/null +++ b/barcode/src/test/resources/org/xbib/graphics/barcode/pdf417/truncated-basic-ecc-level-9.properties @@ -0,0 +1,3 @@ +mode=TRUNCATED +preferredEccLevel=9 +content=This is just a test. diff --git a/barcode/src/test/resources/org/xbib/graphics/barcode/pdf417/truncated-basic.codewords b/barcode/src/test/resources/org/xbib/graphics/barcode/pdf417/truncated-basic.codewords new file mode 100755 index 0000000..a138e32 --- /dev/null +++ b/barcode/src/test/resources/org/xbib/graphics/barcode/pdf417/truncated-basic.codewords @@ -0,0 +1,7 @@ +81111113511111523111214434111331121224231 +81111113511113231131226122115132141441111 +81111113111112461145131122341131611132211 +81111113211142512133322132241221233221131 +81111113211134141161112433114131351123111 +81111113511141221223224113311422222411411 +81111113311231512311233213314221233124111 diff --git a/barcode/src/test/resources/org/xbib/graphics/barcode/pdf417/truncated-basic.png b/barcode/src/test/resources/org/xbib/graphics/barcode/pdf417/truncated-basic.png new file mode 100644 index 0000000000000000000000000000000000000000..034e3d5c86175e0a900e45a63f0fdfde0d9cf489 GIT binary patch literal 9543 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 literal 0 HcmV?d00001 diff --git a/barcode/src/test/resources/org/xbib/graphics/barcode/postnet/planet-basic.properties b/barcode/src/test/resources/org/xbib/graphics/barcode/postnet/planet-basic.properties new file mode 100755 index 0000000..482a436 --- /dev/null +++ b/barcode/src/test/resources/org/xbib/graphics/barcode/postnet/planet-basic.properties @@ -0,0 +1,2 @@ +mode=PLANET +content=336699885526 diff --git a/barcode/src/test/resources/org/xbib/graphics/barcode/postnet/postnet-basic.codewords b/barcode/src/test/resources/org/xbib/graphics/barcode/postnet/postnet-basic.codewords new file mode 100755 index 0000000..6ff6604 --- /dev/null +++ b/barcode/src/test/resources/org/xbib/graphics/barcode/postnet/postnet-basic.codewords @@ -0,0 +1 @@ +LLLSSSSSSLLSSLSLSSLLSSLSSLSLSLSSLLSSLSSSLLSSLSSLSSLL diff --git a/barcode/src/test/resources/org/xbib/graphics/barcode/postnet/postnet-basic.png b/barcode/src/test/resources/org/xbib/graphics/barcode/postnet/postnet-basic.png new file mode 100644 index 0000000000000000000000000000000000000000..883c5862a3d5b9543dc244fcad58cf036cd604f7 GIT binary patch literal 3293 zcmeAS@N?(olHy`uVBq!ia0y~yUo10RMV{Vd}E0EEsKab0_BI^2R(VR@=ue2>%?M{t&c(UW&_tBY_8{$ zSNd%!++d;juI2vH6DNazvL<#ZJ@fOXlIg3}yg6USxL*^Pqa^9tj zdYg}O7OMZzbM>2dN}nTG#L_+3S`S2X2e;%0Jh7Sf%0-&JTX-~JMgwLv#f)a9(K2SV z@Exs{Myu=5cFgc<;Hi2|(fAk(Y%nHu3aNV4gaaF(5sO?ph3<<28?@6j0v8=AcLlZs fN0remK#uBnmNDNEV4J!g)Nk>0^>bP0l+XkKpclZ& literal 0 HcmV?d00001 diff --git a/barcode/src/test/resources/org/xbib/graphics/barcode/postnet/postnet-basic.properties b/barcode/src/test/resources/org/xbib/graphics/barcode/postnet/postnet-basic.properties new file mode 100755 index 0000000..390cba9 --- /dev/null +++ b/barcode/src/test/resources/org/xbib/graphics/barcode/postnet/postnet-basic.properties @@ -0,0 +1,2 @@ +mode=POSTNET +content=012345678 diff --git a/barcode/src/test/resources/org/xbib/graphics/barcode/postnet/postnet-module-width-2.codewords b/barcode/src/test/resources/org/xbib/graphics/barcode/postnet/postnet-module-width-2.codewords new file mode 100755 index 0000000..6ff6604 --- /dev/null +++ b/barcode/src/test/resources/org/xbib/graphics/barcode/postnet/postnet-module-width-2.codewords @@ -0,0 +1 @@ +LLLSSSSSSLLSSLSLSSLLSSLSSLSLSLSSLLSSLSSSLLSSLSSLSSLL diff --git a/barcode/src/test/resources/org/xbib/graphics/barcode/postnet/postnet-module-width-2.png b/barcode/src/test/resources/org/xbib/graphics/barcode/postnet/postnet-module-width-2.png new file mode 100644 index 0000000000000000000000000000000000000000..f4a9a12796d6576bc49c8e4f2689aeb5181e8be4 GIT binary patch literal 19309 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 literal 0 HcmV?d00001 diff --git a/barcode/src/test/resources/org/xbib/graphics/barcode/postnet/postnet-module-width-2.properties b/barcode/src/test/resources/org/xbib/graphics/barcode/postnet/postnet-module-width-2.properties new file mode 100755 index 0000000..0866492 --- /dev/null +++ b/barcode/src/test/resources/org/xbib/graphics/barcode/postnet/postnet-module-width-2.properties @@ -0,0 +1,4 @@ +mode=POSTNET +moduleWidth=2 +humanReadableLocation=BOTTOM +content=012345678 diff --git a/barcode/src/test/resources/org/xbib/graphics/barcode/postnet/postnet-readable-above.codewords b/barcode/src/test/resources/org/xbib/graphics/barcode/postnet/postnet-readable-above.codewords new file mode 100755 index 0000000..6ff6604 --- /dev/null +++ b/barcode/src/test/resources/org/xbib/graphics/barcode/postnet/postnet-readable-above.codewords @@ -0,0 +1 @@ +LLLSSSSSSLLSSLSLSSLLSSLSSLSLSLSSLLSSLSSSLLSSLSSLSSLL diff --git a/barcode/src/test/resources/org/xbib/graphics/barcode/postnet/postnet-readable-above.png b/barcode/src/test/resources/org/xbib/graphics/barcode/postnet/postnet-readable-above.png new file mode 100644 index 0000000000000000000000000000000000000000..cbd4f0fe32a44a1782c72cbf23c6aa5efcdc0e24 GIT binary patch literal 9554 zcmeHt=T{S5+%4Dvkw=OV5wOrZ2u8Xz=^&tn9_d9Olu#0SFQM1a0t5jOrHB$r00C)& z1Vkx90)#HTh7#b-1l7#Qc+RSYig(( zQc+#}cRYE8a-d>&>bOot#m%azs`SjyaxEV=dHZ=rl&pUb#|Ra5`;80dHgsj}d;zR1IqD40AC!Myi(MptY*NBXAy`) zFI7G@VCMdAQH18xdj0R|bPeCgI5c0%IxUNu!M#7%s5S~dV^)iWg5L1OF)sh#-e5&< zaxw_xWNn%WIb};c=>c$>cDUZf`fQ}z>`z*26r6U{O|HDDIth>_U9)UC{?0^Vmvw$x zpv0CSySalAnQn1DJ$RqCKjCRRMZE&4=}uXkeCW?kg5-88=6f!!+W=V~y-cfV;*^n1m$mVxn8%TTW#(MQ2CAC= zl@ocw4zl?{h+~D_*YhV=%4)=D8Ej_toAF7EN@ZXusD_bh!+9I$U)IuFk)G_) z`a!EuNEV|Q+_$GFx}!4jOHuFmM`d6tJ@#I2j=#b~NfWjQN6x#4JL3q_YG+06)ttOw zaJ8rUM7IS~1%C60%PY~d>Q2=xfqvt+_4s%=>^U%e!1SwNhPy}e2dy{#@XNtX-nGLn zmJY^Zv#T^Je%@Vz3OoI}el>QJvsmoKD(&iVQ9p5Fo+`jsmytkIg~56oOn@2ASIto% zHdh1Mvu%88;rQJ-1(@}8rC~(G36K>I4ftN#( z$G+O7MfkR_*jSDyWp!;DuIIaaX+qQaX{L&9J99t#Rd<=&n1i!x%JEAKFU*z6awH67 z;>W#sjxb0ss^ujv>RaP%{vmRLr}rEHi$cmrflU2`NqQ}xn+Aw1P6Km$T7@578MNLA zB?cdXH|m=fs*DwayK2rm^)8LVyn(jEn*xaUtkq|S4^XhcpW9ie`h^(3)F-n7gzKHU z%~LQUebNBj=G)YQ0`xHY_*W(?me6)shov47eLfR1%4QE?2D$9^<_2RN4}W*oa@4Q( zjI?rzCTiCrJ9A1+q{wr*s35z}?W}?vcmUBMmdzxbkf@t^)t_F({FzHt+>vR# zxt&`@^p&}`T0Q84&-?YVAl}TXG|oKFEG100gATS8JHan8u8QTQ zLt_iye*{(crfoIImU(rWG{34{XLg=$(JWQP?pY51JWKsF`Mx1gzlAUS*y+>^`6%>D zy|mNTs$Zl-;vnm`R?4#V)n8hF8f2;97hgvXpB8NWQZGx^<)MA z>Qp4CeFJ&1ta*VZ+)8uDpp$mt}mWs@>K(5N2aR!(mB6D*W zT)n`h9+$uQ3Id%_%cJjdP|ZQvjAFw9+MEYhfQFXE@&qTH$;T_+icA({2I-Se)i+}5 zgi9nw1_1*Fo+=yl;@==`tb?*?q8J@(@HXS5GCs-kJS=JXC8rJRD*|3fW~`P1SlznZ z5KAWx8r%)GCGyph16^Qgf$?&2blj?{6PbpriVqbkW&}^UhXelsBzNm}N11aS8m&|~kW6GsCn+kwf3tj_*h1a~NGA=h zx)z8o=aI6_p*!g*_ZyxntA23h'on*@IGaJOA9%w(H$*X(DjjjlQR?dzED0>xKuf>o)kGNR%*{&dk;)dxgc`8u8H;x8NQQy3&k@e6l~UOmn@huH;#F z;DUuW7XUxV+g0F^()Da_lcfOC80|rAVka{)drxUa8(N?UJ;U((R)I^Qo_WiDVRYh< z?+xJvGdyUc@-$xIu0h4mPes!Ix$a`7<(+3mcs^Xev;e=ah*P^|^s@sLP_n$$4t=eD}E)5-{PGlh=2V{=tA}KzF=t!}rHN=Q7zCrGU z>-@0|fy=q%KFmR_KRh|=Wus3VkPDNGq?E=sgd(zE5+bm;{K`OeG;6Dt@EwWfXdyza zXIji3^W-FgJ(c?1EXw>pvgUY_j_kAm3*@Y}rNr99!@O*G_3Z8rZ#)}L4S0NOxy3b^ z@#Y~$xJ1Sc2zi^<;OK!KI_p%UHTuy3%G2XH6#~R`_n)x%cm}1eSzD~=h200_)MS5d z4&nmm1}$8!WBXNhwkb@KU)YYGsEXmakqfU~w1khAZ{S_r3yN|#yvW|TQo+D?H=WRV zWpTscq5`k$YKr2{=KxyC^*}jpp%yp7^Xc{A*P`?HBKdz)3Oph)!+yg}SldUOKJmd_ zvw({E+51YR1PbFYW`PraaPm_9IHddlx89a#WCWZHUIYeYsqw~E)gB51@p6tY99KXM zFb_oL@?PsCpAwO8ue%om42r+ zR9E-0#PGDGVyf$^d$gB>->0Eb)uFFD{i5$P=_|bm%I&=uh94v%Zj`Y$_hEOp$9GpR z;yePmnJ3v$j5nZW%A4uz|fhSh8C>UzJykksmQ9o*z>TT%KneBWLvotY%L%!C1J`UudcWg@@ z``qEJZw&ZP(*4Q?^N&cI-D~Er3w?RPsPFp9a)}eICqRvaGm(@b55cw^rF1@lqX`#aB6qn%0m@ z$~L6^3q}I3TKhVL|lZ+N7H5sCsDvZSD08ndRAj87A+`V&lmkcSw#6R0Pv zH&0e4G>~3lOMO}8VU{9{qVSw{ph z?&>^YVTE4WfZmULVso6~q^2(1xGXPKQi;&-3FQs$#_C{D4SBiDh|-U4k8_E5fH2Wp z>+_%W1JCsZJi0h1TwDvucnmRUnd}CZd+hXM9zN$D+t%7NE0X>VcW}PzU2+3{phvcq z_whC(vhFkoCBGQbwe@j8=Z_@Vu_c@i91GvHl$8&{TIB5M^&Q#1i->gWFcJv!i5xIm zLOZ@6YHrX_f;?@R?6{hP1@F&enG38(_j4PdTlh@d#nY$y@bc(`N^zCpR`QS*a167_ z1NGLMwQ{?;QYRIu?w&RcfZBGpl_pj^O&3dX_0y7?BZtW)Q|5kuuCEp*rZ6z*L+1c^ zHHF+6`@}oOWPF5g6GS^Itq^c3#BOO!VdkzO=hm>ksJ3vOx_X_4!f-<;qiL&6y%jlC z`7=#E-kd#nisN*P4Fz{P{E&sho+1xG{g%^ERqjU-Y$!vTpX?70*)7~h4=#aNvEHN$ z&MScrvY-V~kPn?d+HUw8YvNk9!1UG(%a$pogfNaKiK_vjyi4sX@7ub!__k`Gb0x9G zGTI`s`tPBWxFz=az6Wf`5!dfPMZZsV640m%CBxw40tSsu|c-7e9G&3E;0bb7pE?g@xM&#ypM4xNmlUsto<BolTMG)2f)OU7lVxQe=&;ur4G z;KQvY<(}syiY57Q1evIDzU7>LLi5kR4@-s5-D4Cn_50YyJRjHzSbt1kHa%N5o@aY* zKu746um^qxQAz>rmkR7iqZ2KbB-wr5jbC_Yv#!0&w;MV6#r&nf=PBA>oWZel+>gId zYKj_@O-f&kI!*LCU__$xst#Ulygp)rH}b}>OVIRI6{Y{?9ZILk*ZLvau&JKjF4FVh zo!G+_r?p`n-HpS^FRKtOd-tKR7sL~DKI&Bea3g^TjvYx9;s&%SVl|g02OUs#z}94V zSR;Q{f@q22X>BxoSDY4ANjw-CLzKl8kWQuO^n|!;W!Ne(24$w8n&^;ku=wDi$u;sH#rl@=yLC7i} zj-m0|QJgv-sq;J$6yUT{lV?Fz?+W^i)8?^LwPxi$)|9kUW@xxoV0y$|`$G!6lJ#vIB(k{v={YXarLt$e zhFoB0teDtdd2ds=6p(b@SEw*kNU9{w-@^cAM_>7g;{;2 zOtEugbf67!Vv?x~bhOaql2kh1Cr?g38dI+|GSfb8YQc74Y#f{Z$w6vk2ss0~jExHn zc_59XDuo=%^t`5&Aw4vXpkO{0-E;V`jw1 z%=U=YBYxvbba`J;=jiv#`CZY3b(9#IH!0pdpu+bt)6V?(;6=_g;CG4OdrxHD@QvBa z(z$HbYmP=K*r%oJjSNx8x8@EsuZ%DH3s~Km+)RI4$Z$ut=H#nqqr!U5?zYcTLT?tY ziLcyq_GHMJ_tl`V(uOVN;w=-m$*0m_Lg)2LNP{j>GwP%KhkOMM3^-dCw784vruSgz zJdMni+zoLa&39TD7GGNRFXIoL{Q>yjX^22T~Eop}q%hmC~5pjqLqgyRXwoBDwOp)W}=nG~A7qSA8(F2h9H zxL?I#?RIvZ64)z>DttEl z58FjD^KL0RmY)nnxV=G%CgnJHbACLds+J>o0qa~RSk#7?>{;MbNYem$_Uo*)obIj3 z9Ir`%ZZ{!azI_Y6H7GTptcy!ti}!m*zDi1LVf0Vz)Bfn#0@JzDXyQdHFyAM2T%bPa&h6wL?PF{?>+&7XWrxplDJ^X3~)^K7ARZUuTvw^)4e(6+hvwW4z?4X%VC zAffA9U8)jJfpzA@MQ70$c$;FBiQv=vF@rWFj}D{a<=3c!bp3Yjz??X@Zd?Kt^&Glag9eDY2Ar^93R0GRF1 zCH;WeMchf?&m6U2*k%Q%j@(`|ZVg-9n?o{a3(*|57cp;kU-+oGZ{W9V^ucJ*^+>T> zO;)`NmN3(;7wFs;H%~|D<4XrK#&8~8S9eNiK#~=LUUNCr@2D7{0&}aep+1o@!l!ux zxG+%BoqA#P zr=516hsr)AP-Ny-ai!993WDc=>bvkN5 zCF%$jH3_|Dz}y+ps{uE%o-frnrB2uKpy;U^Vx(a7D3S+QGi5$2ZNpWoyO_@Y@NB5=eD!AOJde+|g zX@;Nr0qJTZ*fH(8!@xWXO=^Z4MPG~HD)F!)sk$2xYZZ-;A`QB|$q=6E5#9KX9DQXONp+c<1lWe;57#{7Y~N z;)|D4VXt8Og-Q`-&pCtbYggjKl=Vss?4XZdGBbP9#)o~DE=)G6<>Of$G(xIG(3o5B z&Gi(|vHx@PVq~6H@z1>wQBbt5x)^u3hJG_by7lpOotRhfW|tp!oc`VhZ6)<_iPV zk$E|4;#Se9_S^S#rhhBnRp;Q|_(S*X*7GNqFFaAYTlSgnO5S-|X(@zt`!)GUX;tLs zpOMOma$1Bxbmfw7YVgma%gmn6H##HO;j~^cVxbjuk+)&#*#GF9p0WO?=v?s~ z_iKD}KZN3FWOtbt{;@g-0?c6A^?;f08-qH`i Voe+bn|NU7?Q%z5`Qu$@r{{Yz@n!W%4 literal 0 HcmV?d00001 diff --git a/barcode/src/test/resources/org/xbib/graphics/barcode/postnet/postnet-readable-above.properties b/barcode/src/test/resources/org/xbib/graphics/barcode/postnet/postnet-readable-above.properties new file mode 100755 index 0000000..2c0e188 --- /dev/null +++ b/barcode/src/test/resources/org/xbib/graphics/barcode/postnet/postnet-readable-above.properties @@ -0,0 +1,3 @@ +mode=POSTNET +humanReadableLocation=TOP +content=012345678 diff --git a/barcode/src/test/resources/org/xbib/graphics/barcode/postnet/postnet-readable-below.codewords b/barcode/src/test/resources/org/xbib/graphics/barcode/postnet/postnet-readable-below.codewords new file mode 100755 index 0000000..6ff6604 --- /dev/null +++ b/barcode/src/test/resources/org/xbib/graphics/barcode/postnet/postnet-readable-below.codewords @@ -0,0 +1 @@ +LLLSSSSSSLLSSLSLSSLLSSLSSLSLSLSSLLSSLSSSLLSSLSSLSSLL diff --git a/barcode/src/test/resources/org/xbib/graphics/barcode/postnet/postnet-readable-below.png b/barcode/src/test/resources/org/xbib/graphics/barcode/postnet/postnet-readable-below.png new file mode 100644 index 0000000000000000000000000000000000000000..6f0380c0e225e0ceea70305e1098b0b250de7579 GIT binary patch literal 9563 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 PHKng(tX-z{@6-PQ!?Wcl literal 0 HcmV?d00001 diff --git a/barcode/src/test/resources/org/xbib/graphics/barcode/postnet/postnet-readable-below.properties b/barcode/src/test/resources/org/xbib/graphics/barcode/postnet/postnet-readable-below.properties new file mode 100755 index 0000000..b5bb3c4 --- /dev/null +++ b/barcode/src/test/resources/org/xbib/graphics/barcode/postnet/postnet-readable-below.properties @@ -0,0 +1,3 @@ +mode=POSTNET +humanReadableLocation=BOTTOM +content=012345678 diff --git a/barcode/src/test/resources/org/xbib/graphics/barcode/qrcode/version-01.codewords b/barcode/src/test/resources/org/xbib/graphics/barcode/qrcode/version-01.codewords new file mode 100755 index 0000000..374c6d5 --- /dev/null +++ b/barcode/src/test/resources/org/xbib/graphics/barcode/qrcode/version-01.codewords @@ -0,0 +1,21 @@ +711111117 +151322151 +1131124111311 +1131111511311 +1131142111311 +15122111151 +711111117 +0:29 +0211313213121 +312113112213 +02114211312111 +0113114111121111 +02111141141121 +0811132113 +72142212 +1511131233 +113111132112111 +1131144321 +1131112222131 +151222233 +72122211111 diff --git a/barcode/src/test/resources/org/xbib/graphics/barcode/qrcode/version-01.png b/barcode/src/test/resources/org/xbib/graphics/barcode/qrcode/version-01.png new file mode 100644 index 0000000000000000000000000000000000000000..8a0b7981313464d50a8d9f7f7daaf602213ef729 GIT binary patch literal 2457 zcmc&$eN<9+7^XaZg4xc_$zjJ>I@a6)cLpglDq9~jbDGUI5wx%rN)(Ff+SiRfWm(yg zJh#I@?PErpq$C1)P|9ddLKHPoP~(`|)bQ90ZQ+dFi|w5Kq5ETf?2miz@4e@}@AJGL z=Y8(!*q8|Kxqfp|D3o{P`nBJ{&nD!W4c}0H!_@^S)Es8y+AlZn-(qUmR3cwL->XsM zj&Arw@M-quME2qxr=OilUlQ#ed!FrvGRu4v4F^TwQP6V1;pI>kq~Mr^BOi^S#^g(^ zv)b4z3@h)fd=fc2Z8TxyglpBw_`+pXsX|>)h3QtOv(e#%{9V_2Lc4+H2xT(^=#z-r z&$dV7Offv`W!?rZnM(neQs2x6@9lV)TA7dp>ke)njpz+pG)!+0g4$o|WhAA61zpJy^mScd2h$uEtIhwN(eNX56DxkWad=;Z6aPR)_ z(X=SmaJ`M#{ob*|>|SQVk_K%*mot@uuvxUfAp704yiiW5rC4c3yhwODxzp|u7` z5hwLvENPR6g_+k7lxP^QR-uB)er|Lehiy_i@w_>?j->q%wlNH2;NB0EunTPKEyHj0 zlOZ!Mw@z_FmBP9{q{OM;>M^S`a9oAgWTBnWVHvpoQes6&;w9ewonz|-8HO8O)4{ly z_6QdzitFv$C*j*xHf?3_Ik#5X^O{F4Ic~lZP(e$I?LJ%BvwDxNrGVfCEnzy}331*EQSU+UsQok44)%_E$9##{CQop2F_cAVA+RN}{(?p2}$7M&pfCUEO zRKzQBxi*gPf_OVU>DUuTMhabBus}@(9Ns7>m#X_D+cN66+%4X1yTZ#>g&mqxTC)=- rV|nbrSNh3dV46gE@oCzBcO1hFROg0Py^qdC{<$L8#jGt^%{=}W43HN< literal 0 HcmV?d00001 diff --git a/barcode/src/test/resources/org/xbib/graphics/barcode/qrcode/version-01.properties b/barcode/src/test/resources/org/xbib/graphics/barcode/qrcode/version-01.properties new file mode 100755 index 0000000..25f91eb --- /dev/null +++ b/barcode/src/test/resources/org/xbib/graphics/barcode/qrcode/version-01.properties @@ -0,0 +1,2 @@ +preferredVersion=1 +content=ABC diff --git a/barcode/src/test/resources/org/xbib/graphics/barcode/qrcode/version-02.codewords b/barcode/src/test/resources/org/xbib/graphics/barcode/qrcode/version-02.codewords new file mode 100755 index 0000000..006ee30 --- /dev/null +++ b/barcode/src/test/resources/org/xbib/graphics/barcode/qrcode/version-02.codewords @@ -0,0 +1,25 @@ +7121347 +15121521151 +11311212221111311 +1131112811311 +11311231211111311 +15122232151 +7111111111117 +094138 +02113114111113121 +012211223212111111 +021325221121111 +12111111421131121 +01222111233211111 +03111132621112 +1131211221211411 +01112211111112131121 +1112529121 +0812321323 +7612111132 +1511522323 +113111113111711 +11311322121421 +11311122111131131 +15143112133 +75112411111 diff --git a/barcode/src/test/resources/org/xbib/graphics/barcode/qrcode/version-02.png b/barcode/src/test/resources/org/xbib/graphics/barcode/qrcode/version-02.png new file mode 100644 index 0000000000000000000000000000000000000000..7f95190173c99ad80898292bcbff6b6649843071 GIT binary patch literal 3468 zcmd5Q-dKQ z)h)1=4kOf!6H^d$Yn|Po3Hg$_2CRrCl7z5zFCiges9>eJSGGXn7#i3&_1bf@SQk#rl$Yz%iXJblQfiI z3ss33qB6=@sz@x^={K42s|^M&a@A}dQBMSo_Y%=I{M+D`9NjUGr-f6LXKePUHrQ9y zi1d9kZqax)(VGo>X#=85X~xY1qUP}(XwXJLy82u#1cV~d6?tehtT9y1pkhXgy}WAJ zrdj{A3>{Qw-$3z}C!5g`^s|~X)+cZ~qHzXRL4C55q0Gg#a^;gvuBwS8Qy6l%y+66H zzc{P*+W+hCGn%^K`Q#`A^@bVd-blf*3K%edcdDzT3%Tm-LC!;m|LeO+<(_%Cx4rd3~ zxF$j;u>?(jVBJ_!X}v$CC*%4On$NBOINbe{LER^7CKM&1I}c-!nrln1&t0jncyz+c zD6W6`@o0aK{OgD0D;a95{6>Fkkk)>O&z|1P_bwYWj4CA0WML^;E7cNuN27PYR~Yg-`W1sq;w z4mvg{;)PnImnbyjW!?kY&0*27Y*O4)aT{KuisaLaDQWq)^KWUZm%mXzCm z5aV5(6*Rf2A2NCzsZ!cEHF{>%3NYR2@$90!SoYNYZKFdnq@DHuG5lfOuASH^6{}A( z#nUPXDBBo*WngWuayl+glqj^8c2{_k-hA#a(C=!3d;C{FHgF;AozNJ|aT!|ug3t;n z$pm5yy64sReulO|x{gDqY&Lwyx*elVhEzraO%jE}x|X=2JVHsqA*1PW=kXDT=n}|S2ElPEsJ&-Y7t2OdB77JQcgY2P9p(K3TKj`j|9qD=|i=;<; z0yYg(w10}zY*1t|UDd+`#?<}nK0}d^wDS}4qh~!S2;5v~H*h-#WqIgWjI!zctU5WG z;@|+2EPH-g$ET14)xNgF(yaSk4;VsgX7jv4t&5{3?3-W#jlM7e{Z7fYh8%3F^V@65 ze(Z#eIZ}hR9Z&p@0yO7t{IpM}An4x`TX!R`&lju&)wsr*87IuJIyq1cCbUs;JP|$C z)@9O=Nuus}N;E|seuF74V^GK0>)EmFqQ%@s-wi4Es$rL&pw|RHCzu;>%b$c*0+OH% zPa)ODd0Hn?x0C9;;m)TnB*Hn`ZRo1#Y%rYTKA=?QG;)N@p_KsjhEoJRGS`qypPiX- zqVG_h5AP5aNU+A z1NCv}(r_T*o98v!Hac;7xZ}YUP#(~f9$!2f%&rH{kfatneOi|i+71E*YPxURxxFJL zz^+EJWd6O!*vX|&LB~MX65rwHFMI$zENdD+T9__B-w&!KQueC3v322J3FSQ3ujwm5 zy!yREWR+Hf3hI^J3ACxc!n?Gbo`d6?=y{?Ji5Ws87hsA?qRC>1$pdMacYKa%P>@E8l3@YS>m3}Cm=zO0EEP1Y@2(jeQb5b_Z}7hbFHqtD%9{ zEFiN7qCg#Z$OFegVrqYpxzZRWp(#>;H0nN$L zte=`(J(K0%9s7u9;&$TLd4AgD%t2{htlN=V8~jCwui|MvPP*>+`4@9rKiV4k%*x%j LKl}1Kr6>Lagknt$ literal 0 HcmV?d00001 diff --git a/barcode/src/test/resources/org/xbib/graphics/barcode/qrcode/version-02.properties b/barcode/src/test/resources/org/xbib/graphics/barcode/qrcode/version-02.properties new file mode 100755 index 0000000..bec8317 --- /dev/null +++ b/barcode/src/test/resources/org/xbib/graphics/barcode/qrcode/version-02.properties @@ -0,0 +1,2 @@ +preferredVersion=2 +content=ABC diff --git a/barcode/src/test/resources/org/xbib/graphics/barcode/qrcode/version-03.codewords b/barcode/src/test/resources/org/xbib/graphics/barcode/qrcode/version-03.codewords new file mode 100755 index 0000000..e354478 --- /dev/null +++ b/barcode/src/test/resources/org/xbib/graphics/barcode/qrcode/version-03.codewords @@ -0,0 +1,29 @@ +7214357 +15125134151 +11311211612211311 +113112111121111311311 +1131111211411311311 +15122263151 +71111111111111117 +0811331219 +02224611122114 +1124116121211122 +01224343622 +0211211241231221111 +01142941331 +114211122131121221 +1112413211112122111 +011212111111122614 +03121211511812 +0131113112132122112 +12123112114121312 +02419112211221 +012141322111631 +08121431131211 +7115151111113 +15141311211323 +1131152212631 +1131112221351113 +113111321231711 +15126313124 +75211132111112 diff --git a/barcode/src/test/resources/org/xbib/graphics/barcode/qrcode/version-03.png b/barcode/src/test/resources/org/xbib/graphics/barcode/qrcode/version-03.png new file mode 100644 index 0000000000000000000000000000000000000000..69434073fe6a8db6d677ab2f13cbf29cb77d3422 GIT binary patch literal 4515 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 G=Dz`Q*o6}S literal 0 HcmV?d00001 diff --git a/barcode/src/test/resources/org/xbib/graphics/barcode/qrcode/version-04.properties b/barcode/src/test/resources/org/xbib/graphics/barcode/qrcode/version-04.properties new file mode 100755 index 0000000..00c42d0 --- /dev/null +++ b/barcode/src/test/resources/org/xbib/graphics/barcode/qrcode/version-04.properties @@ -0,0 +1,2 @@ +preferredVersion=4 +content=ABC diff --git a/barcode/src/test/resources/org/xbib/graphics/barcode/qrcode/version-05.codewords b/barcode/src/test/resources/org/xbib/graphics/barcode/qrcode/version-05.codewords new file mode 100755 index 0000000..f1b4015 --- /dev/null +++ b/barcode/src/test/resources/org/xbib/graphics/barcode/qrcode/version-05.codewords @@ -0,0 +1,37 @@ +7211121221111221117 +1511339142151 +11311212211211241311311 +1131123136111511311 +11311211121221114411311 +15113332512111151 +7111111111111111111111117 +08132132211238 +04412114111113232311 +132142223134711 +311111313232511121121 +212231311311111113242 +112141211341121111121211 +04211112111212211121117 +11111151232423211131 +0324133311321: +1121232212312241421 +01122316121324224 +01121121113113333211212 +01113112132111421164 +0342321111224311231 +0513143123211111322 +01211121544141111321 +01131111142261132222 +02212232111212112131321 +13221421213323142 +062211211211111411121121 +0211131113232111113242 +2121221122311151621 +081421212623131 +7111111122164111131 +15111111461121235 +1131111112213321125211 +11311921112142331 +1131141132111222222121 +1512121212411231115 +723111341244121 diff --git a/barcode/src/test/resources/org/xbib/graphics/barcode/qrcode/version-05.png b/barcode/src/test/resources/org/xbib/graphics/barcode/qrcode/version-05.png new file mode 100644 index 0000000000000000000000000000000000000000..3a802ba29439f352d50c6f6ca94b20bd4d7d8bc3 GIT binary patch literal 7518 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 QKTZ#Q;{S2=f#YZXAH1i`X#fBK literal 0 HcmV?d00001 diff --git a/barcode/src/test/resources/org/xbib/graphics/barcode/qrcode/version-05.properties b/barcode/src/test/resources/org/xbib/graphics/barcode/qrcode/version-05.properties new file mode 100755 index 0000000..64b8035 --- /dev/null +++ b/barcode/src/test/resources/org/xbib/graphics/barcode/qrcode/version-05.properties @@ -0,0 +1,2 @@ +preferredVersion=5 +content=ABC diff --git a/barcode/src/test/resources/org/xbib/graphics/barcode/qrcode/version-06.codewords b/barcode/src/test/resources/org/xbib/graphics/barcode/qrcode/version-06.codewords new file mode 100755 index 0000000..1b43c1e --- /dev/null +++ b/barcode/src/test/resources/org/xbib/graphics/barcode/qrcode/version-06.codewords @@ -0,0 +1,41 @@ +716112145211117 +15111313436122151 +1131116111412111132111311 +11311;3113223111311 +11311332111331112511311 +15111112133111161111151 +71111111111111111111111111117 +08116311611< +0231111112222411121112323 +0221121111112311412112211122 +06211211311111121131111511 +0121273123111211421122 +11111112214211126211113111 +21221152251132111341 +11135223121521211121111 +6712513213235 +021311112117511242131 +0422112211111121214515 +02215211131311231211121111 +021215:111133235 +1113122133324115112111 +02155325122121211122 +1112262622113111143 +111112121521312211421122 +1211241113143123312211 +0112212322142151151211 +0523112313541111511 +03111241116212111421122 +1521231212112213111111131 +111524116221331115 +11113312132212231211121111 +1111211241111114313235 +1241211121231312136111 +0811112212511112111341 +73112223512111111111111 +15122121451123235 +113111331211212222731 +113111142153122311111211 +11311127222124422111 +151251113422121112122 +7321121112121311152111 diff --git a/barcode/src/test/resources/org/xbib/graphics/barcode/qrcode/version-06.png b/barcode/src/test/resources/org/xbib/graphics/barcode/qrcode/version-06.png new file mode 100644 index 0000000000000000000000000000000000000000..0234605f9ba926ffc249e9641bd82ef72b7251a2 GIT binary patch literal 8995 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 literal 0 HcmV?d00001 diff --git a/barcode/src/test/resources/org/xbib/graphics/barcode/ean/ean-8-basic.properties b/barcode/src/test/resources/org/xbib/graphics/barcode/ean/ean-8-basic.properties new file mode 100755 index 0000000..bbf49bd --- /dev/null +++ b/barcode/src/test/resources/org/xbib/graphics/barcode/ean/ean-8-basic.properties @@ -0,0 +1,2 @@ +mode=EAN8 +content=1234567+12345 diff --git a/barcode/src/test/resources/org/xbib/graphics/barcode/fonts/OkapiDejaVuSans.ttf b/barcode/src/test/resources/org/xbib/graphics/barcode/fonts/OkapiDejaVuSans.ttf new file mode 100755 index 0000000000000000000000000000000000000000..9642811618f109a3202ab5beb455a3dabf84b3a5 GIT binary patch literal 739756 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 K41(t}2~oIl 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