From 0c8207167f72490979890cde46e6899f473e7246 Mon Sep 17 00:00:00 2001 From: FriedrichFroebel Date: Wed, 24 Jan 2024 19:41:33 +0100 Subject: [PATCH] add tests for examples --- .github/workflows/ci.yml | 2 +- examples/djvu-crop-text | 8 +- examples/djvu-dump-text | 6 +- examples/djvu2png | 27 +++--- setup.py | 6 ++ tests/__init__.py | 0 tests/examples/__init__.py | 0 tests/examples/test_djvu2png.py | 32 ++++++++ tests/examples/test_djvu_crop_text.py | 18 ++++ tests/examples/test_djvu_dump_text.py | 17 ++++ tests/images/test0_crop-text.txt | 11 +++ tests/images/test0_dump-text.txt | 114 ++++++++++++++++++++++++++ tests/images/test1_foreground.png | Bin 0 -> 319 bytes tests/images/test1_mask.png | Bin 0 -> 329 bytes tests/test_decode.py | 4 +- tests/tools.py | 4 + 16 files changed, 230 insertions(+), 19 deletions(-) create mode 100644 tests/__init__.py create mode 100644 tests/examples/__init__.py create mode 100644 tests/examples/test_djvu2png.py create mode 100644 tests/examples/test_djvu_crop_text.py create mode 100644 tests/examples/test_djvu_dump_text.py create mode 100644 tests/images/test0_crop-text.txt create mode 100644 tests/images/test0_dump-text.txt create mode 100644 tests/images/test1_foreground.png create mode 100644 tests/images/test1_mask.png diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 7046f3a..af1e836 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -89,7 +89,7 @@ jobs: python -m pip install --upgrade flake8 pep8-naming - name: run flake8 run: - python -m flake8 tests/ doc/ setup.py + python -m flake8 tests/ doc/ examples/ setup.py - name: run pycodestyle run: python -m pycodestyle djvu/ diff --git a/examples/djvu-crop-text b/examples/djvu-crop-text index ece1971..e5ab5e4 100755 --- a/examples/djvu-crop-text +++ b/examples/djvu-crop-text @@ -14,8 +14,6 @@ # or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for # more details. -from __future__ import print_function - import argparse import os import sys @@ -24,8 +22,10 @@ import djvu.const import djvu.decode import djvu.sexpr + EMPTY_TEXT_SEXPR = djvu.sexpr.Expression([djvu.const.TEXT_ZONE_PAGE, 0, 0, 0, 0, '']) + class ArgumentParser(argparse.ArgumentParser): def __init__(self): @@ -49,6 +49,7 @@ class ArgumentParser(argparse.ArgumentParser): self.error('Unable to parse page numbers') return options + def crop_text(sexpr, width, height): if isinstance(sexpr, djvu.sexpr.ListExpression) and len(sexpr) >= 5: tp = sexpr[0] @@ -67,6 +68,7 @@ def crop_text(sexpr, width, height): else: return sexpr + class Context(djvu.decode.Context): def handle_message(self, message): @@ -100,12 +102,14 @@ class Context(djvu.decode.Context): self.process_page(page).print_into(sed_file) sed_file.write('\n.\n\n') + def main(): parser = ArgumentParser() options = parser.parse_args() context = Context() context.process(options.path, options.pages) + if __name__ == '__main__': main() diff --git a/examples/djvu-dump-text b/examples/djvu-dump-text index 58a9514..8b63728 100755 --- a/examples/djvu-dump-text +++ b/examples/djvu-dump-text @@ -14,14 +14,13 @@ # or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for # more details. -from __future__ import print_function - import argparse import os import sys import djvu.decode + def print_text(sexpr, level=0): if level > 0: print(' ' * (2 * level - 1), end=' ') @@ -34,6 +33,7 @@ def print_text(sexpr, level=0): else: print(sexpr) + class Context(djvu.decode.Context): def handle_message(self, message): @@ -50,6 +50,7 @@ class Context(djvu.decode.Context): page.get_info() print_text(page.text.sexpr) + def main(): ap = argparse.ArgumentParser() ap.add_argument('path', metavar='DJVU-FILE') @@ -57,6 +58,7 @@ def main(): context = Context() context.process(options.path) + if __name__ == '__main__': main() diff --git a/examples/djvu2png b/examples/djvu2png index 9360452..c1309a4 100755 --- a/examples/djvu2png +++ b/examples/djvu2png @@ -14,20 +14,23 @@ # or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for # more details. -from __future__ import print_function - import argparse import os import sys -import cairo +try: + import cairo # pycairo +except ImportError: + import cairocffi as cairo import djvu.decode import numpy -cairo_pixel_format = cairo.FORMAT_ARGB32 -djvu_pixel_format = djvu.decode.PixelFormatRgbMask(0xFF0000, 0xFF00, 0xFF, bpp=32) -djvu_pixel_format.rows_top_to_bottom = 1 -djvu_pixel_format.y_top_to_bottom = 0 + +CAIRO_PIXEL_FORMAT = cairo.FORMAT_ARGB32 +DJVU_PIXEL_FORMAT = djvu.decode.PixelFormatRgbMask(0xFF0000, 0xFF00, 0xFF, bpp=32) +DJVU_PIXEL_FORMAT.rows_top_to_bottom = 1 +DJVU_PIXEL_FORMAT.y_top_to_bottom = 0 + class Context(djvu.decode.Context): @@ -45,21 +48,22 @@ class Context(djvu.decode.Context): page_job = page.decode(wait=True) width, height = page_job.size rect = (0, 0, width, height) - bytes_per_line = cairo.ImageSurface.format_stride_for_width(cairo_pixel_format, width) + bytes_per_line = cairo.ImageSurface.format_stride_for_width(CAIRO_PIXEL_FORMAT, width) assert bytes_per_line % 4 == 0 color_buffer = numpy.zeros((height, bytes_per_line // 4), dtype=numpy.uint32) - page_job.render(mode, rect, rect, djvu_pixel_format, row_alignment=bytes_per_line, buffer=color_buffer) + page_job.render(mode, rect, rect, DJVU_PIXEL_FORMAT, row_alignment=bytes_per_line, buffer=color_buffer) mask_buffer = numpy.zeros((height, bytes_per_line // 4), dtype=numpy.uint32) if mode == djvu.decode.RENDER_FOREGROUND: - page_job.render(djvu.decode.RENDER_MASK_ONLY, rect, rect, djvu_pixel_format, row_alignment=bytes_per_line, buffer=mask_buffer) + page_job.render(djvu.decode.RENDER_MASK_ONLY, rect, rect, DJVU_PIXEL_FORMAT, row_alignment=bytes_per_line, buffer=mask_buffer) mask_buffer <<= 24 color_buffer |= mask_buffer color_buffer ^= 0xFF000000 - surface = cairo.ImageSurface.create_for_data(color_buffer, cairo_pixel_format, width, height) + surface = cairo.ImageSurface.create_for_data(color_buffer, CAIRO_PIXEL_FORMAT, width, height) surface.write_to_png(png_path) # Multi-page documents are not yet supported: break + def main(): parser = argparse.ArgumentParser() parser.set_defaults(mode=djvu.decode.RENDER_COLOR) @@ -73,6 +77,7 @@ def main(): context = Context() context.process(options.djvu_path, options.png_path, options.mode) + if __name__ == '__main__': main() diff --git a/setup.py b/setup.py index 67f9e2b..1905589 100644 --- a/setup.py +++ b/setup.py @@ -248,6 +248,12 @@ def make_release_tree(self, base_dir, files): 'flake8', 'pep8-naming', ], + 'examples': [ + # djvu2png + # 'cairocffi', # Broken: https://github.com/Kozea/cairocffi/issues/223 + 'pycairo', + 'numpy', + ] }, **meta ) diff --git a/tests/__init__.py b/tests/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/tests/examples/__init__.py b/tests/examples/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/tests/examples/test_djvu2png.py b/tests/examples/test_djvu2png.py new file mode 100644 index 0000000..ce3ed97 --- /dev/null +++ b/tests/examples/test_djvu2png.py @@ -0,0 +1,32 @@ +import os +import subprocess +from tempfile import NamedTemporaryFile + +from tests.tools import EXAMPLES, IMAGES, TestCase + + +class Djvu2PngTestCase(TestCase): + def check(self, mode: str): + with NamedTemporaryFile(suffix='.png') as outfile: + subprocess.run( + [ + os.path.join(EXAMPLES, 'djvu2png'), + f'--{mode}', + os.path.join(IMAGES, 'test1.djvu'), + outfile.name + ] + ) + with open(os.path.join(IMAGES, f'test1_{mode}.png'), mode='rb') as fd: + expected = fd.read() + outfile.seek(0) + self.assertEqual(expected, outfile.read()) + + def test_foreground(self): + self.check('foreground') + + def test_background(self): + # Sample files have no background. + pass + + def test_mask(self): + self.check('mask') diff --git a/tests/examples/test_djvu_crop_text.py b/tests/examples/test_djvu_crop_text.py new file mode 100644 index 0000000..b2a15b5 --- /dev/null +++ b/tests/examples/test_djvu_crop_text.py @@ -0,0 +1,18 @@ +import os +import subprocess + +from tests.tools import EXAMPLES, IMAGES, TestCase + + +class DjvuCropTextTestCase(TestCase): + def test_djvu_dump_text(self): + stdout = subprocess.check_output( + [ + os.path.join(EXAMPLES, 'djvu-crop-text'), + os.path.join(IMAGES, 'test0.djvu'), + ], + stderr=subprocess.PIPE, + ) + with open(os.path.join(IMAGES, 'test0_crop-text.txt'), mode='rb') as fd: + expected = fd.read() + self.assertEqual(expected, stdout) diff --git a/tests/examples/test_djvu_dump_text.py b/tests/examples/test_djvu_dump_text.py new file mode 100644 index 0000000..bbe1d4c --- /dev/null +++ b/tests/examples/test_djvu_dump_text.py @@ -0,0 +1,17 @@ +import os +import subprocess + +from tests.tools import EXAMPLES, IMAGES, TestCase + + +class DjvuDumpTextTestCase(TestCase): + def test_djvu_dump_text(self): + stdout = subprocess.check_output( + [ + os.path.join(EXAMPLES, 'djvu-dump-text'), + os.path.join(IMAGES, 'test0.djvu'), + ] + ) + with open(os.path.join(IMAGES, 'test0_dump-text.txt'), mode='rb') as fd: + expected = fd.read() + self.assertEqual(expected, stdout) diff --git a/tests/images/test0_crop-text.txt b/tests/images/test0_crop-text.txt new file mode 100644 index 0000000..51120b7 --- /dev/null +++ b/tests/images/test0_crop-text.txt @@ -0,0 +1,11 @@ +remove-txt +select 1 +set-txt +(page 0 0 2550 3300 (line 466 2712 978 2777 (word 466 2727 493 2776 "1") (word 570 2726 773 2775 "Lorem") (word 794 2712 978 2777 "ipsum")) (line 463 2604 2079 2649 (word 463 2604 573 2649 "Optio") (word 593 2604 859 2649 "reprehenderit") (word 878 2615 1062 2649 "molestias") (word 1085 2615 1180 2644 "amet") (word 1200 2604 1368 2649 "aliquam,") (word 1393 2604 1574 2649 "similique") (word 1594 2604 1830 2649 "doloremque") (word 1851 2604 1938 2649 "fuga") (word 1957 2615 2079 2649 "labore")) (line 462 2544 2081 2589 (word 462 2544 688 2589 "voluptatum") (word 701 2544 933 2589 "voluptatem,") (word 949 2555 1135 2589 "commodi") (word 1149 2544 1256 2589 "culpa") (word 1270 2544 1446 2589 "voluptas,") (word 1462 2555 1587 2589 "officia") (word 1601 2555 1738 2584 "tenetur") (word 1751 2544 1918 2589 "expedita") (word 1932 2544 2081 2589 "quidem")) (line 461 2483 2078 2528 (word 461 2494 521 2528 "hic") (word 534 2483 683 2528 "repellat") (word 695 2494 884 2528 "molestiae") (word 898 2483 977 2528 "quis") (word 993 2494 1204 2517 "accusamus") (word 1219 2494 1362 2528 "dolores") (word 1376 2483 1629 2528 "repudiandae,") (word 1644 2483 1793 2528 "quidem") (word 1805 2494 1842 2528 "in") (word 1856 2494 1900 2528 "ad") (word 1914 2483 2078 2528 "voluptas")) (line 462 2423 1561 2468 (word 462 2423 621 2468 "eligendi") (word 635 2434 790 2468 "maiores") (word 805 2423 946 2468 "placeat") (word 959 2434 1003 2457 "ex") (word 1018 2434 1244 2463 "consectetur") (word 1258 2434 1292 2463 "at") (word 1305 2434 1443 2463 "tenetur") (word 1457 2433 1561 2463 "amet.")) (line 1264 375 1278 409 (word 1264 375 1278 409 "1"))) +. + +select 2 +set-txt +(page 0 0 2550 3300 (line 462 2712 910 2777 (word 462 2727 495 2776 "2") (word 571 2712 910 2777 "Hyperlinks")) (line 462 2599 714 2641 (word 462 2599 532 2641 "2.1") (word 597 2599 714 2640 "local")) (line 464 2505 544 2540 (word 464 2505 544 2540 "\342\206\2221")) (line 462 2358 772 2400 (word 462 2358 535 2400 "2.2") (word 598 2358 772 2397 "remote")) (line 463 2256 964 2298 (word 463 2256 964 2298 "http://jwilk.net/")) (line 1260 375 1282 409 (word 1260 375 1282 409 "2"))) +. + diff --git a/tests/images/test0_dump-text.txt b/tests/images/test0_dump-text.txt new file mode 100644 index 0000000..08a14ab --- /dev/null +++ b/tests/images/test0_dump-text.txt @@ -0,0 +1,114 @@ +page [0, 0, 2550, 3300] + line [466, 2712, 978, 2777] + word [466, 2727, 493, 2776] + "1" + word [570, 2726, 773, 2775] + "Lorem" + word [794, 2712, 978, 2777] + "ipsum" + line [463, 2604, 2079, 2649] + word [463, 2604, 573, 2649] + "Optio" + word [593, 2604, 859, 2649] + "reprehenderit" + word [878, 2615, 1062, 2649] + "molestias" + word [1085, 2615, 1180, 2644] + "amet" + word [1200, 2604, 1368, 2649] + "aliquam," + word [1393, 2604, 1574, 2649] + "similique" + word [1594, 2604, 1830, 2649] + "doloremque" + word [1851, 2604, 1938, 2649] + "fuga" + word [1957, 2615, 2079, 2649] + "labore" + line [462, 2544, 2081, 2589] + word [462, 2544, 688, 2589] + "voluptatum" + word [701, 2544, 933, 2589] + "voluptatem," + word [949, 2555, 1135, 2589] + "commodi" + word [1149, 2544, 1256, 2589] + "culpa" + word [1270, 2544, 1446, 2589] + "voluptas," + word [1462, 2555, 1587, 2589] + "officia" + word [1601, 2555, 1738, 2584] + "tenetur" + word [1751, 2544, 1918, 2589] + "expedita" + word [1932, 2544, 2081, 2589] + "quidem" + line [461, 2483, 2078, 2528] + word [461, 2494, 521, 2528] + "hic" + word [534, 2483, 683, 2528] + "repellat" + word [695, 2494, 884, 2528] + "molestiae" + word [898, 2483, 977, 2528] + "quis" + word [993, 2494, 1204, 2517] + "accusamus" + word [1219, 2494, 1362, 2528] + "dolores" + word [1376, 2483, 1629, 2528] + "repudiandae," + word [1644, 2483, 1793, 2528] + "quidem" + word [1805, 2494, 1842, 2528] + "in" + word [1856, 2494, 1900, 2528] + "ad" + word [1914, 2483, 2078, 2528] + "voluptas" + line [462, 2423, 1561, 2468] + word [462, 2423, 621, 2468] + "eligendi" + word [635, 2434, 790, 2468] + "maiores" + word [805, 2423, 946, 2468] + "placeat" + word [959, 2434, 1003, 2457] + "ex" + word [1018, 2434, 1244, 2463] + "consectetur" + word [1258, 2434, 1292, 2463] + "at" + word [1305, 2434, 1443, 2463] + "tenetur" + word [1457, 2433, 1561, 2463] + "amet." + line [1264, 375, 1278, 409] + word [1264, 375, 1278, 409] + "1" +page [0, 0, 2550, 3300] + line [462, 2712, 910, 2777] + word [462, 2727, 495, 2776] + "2" + word [571, 2712, 910, 2777] + "Hyperlinks" + line [462, 2599, 714, 2641] + word [462, 2599, 532, 2641] + "2.1" + word [597, 2599, 714, 2640] + "local" + line [464, 2505, 544, 2540] + word [464, 2505, 544, 2540] + "\342\206\2221" + line [462, 2358, 772, 2400] + word [462, 2358, 535, 2400] + "2.2" + word [598, 2358, 772, 2397] + "remote" + line [463, 2256, 964, 2298] + word [463, 2256, 964, 2298] + "http://jwilk.net/" + line [1260, 375, 1282, 409] + word [1260, 375, 1282, 409] + "2" diff --git a/tests/images/test1_foreground.png b/tests/images/test1_foreground.png new file mode 100644 index 0000000000000000000000000000000000000000..6c6c037a710968acb5bb166514ab3c8fd4789b96 GIT binary patch literal 319 zcmeAS@N?(olHy`uVBq!ia0vp^4nS(0#+?cEerZ!&D44%SC~1?i9@kPz{zG(@1H&8kAGP5 zy|?mMWBq6u-`XR4KVR{5=S-#^DWZR^3bJxV`(bDo(>dL5nS`dcBZEt>I)%DMw@ zm?p0Bf6t@+gW<)g%02uEZ%%7>9(tTCx-3vIgLU(%{f?U##e%)@{VDUg(=v9+vETWD P{%7!X^>bP0l+XkK)vAQ) literal 0 HcmV?d00001 diff --git a/tests/images/test1_mask.png b/tests/images/test1_mask.png new file mode 100644 index 0000000000000000000000000000000000000000..8857c9a9a91a321aab5bf165b70ee931f6bdf6bf GIT binary patch literal 329 zcmV-P0k-~$P)_pqC6TR6RJCQ+ zhh2bG1;ADTuvGwT6#!f50lx5G6p`-9HLY~t+Jg)d;9h$7+#`2k07j(;DAWTUoB)7P zSusJ~dW7bdJwN-MG4K%@t-7i@ZHNhg=-pGu9ayfh$0nqo#09`s0bI0fO50819sU#p z$LV8@?_hotL+qE{5<#IMQ)S_RpKo-n{&j&GxXG5oqy=p67+-N@J~0JaK% bt@HptxO{I+h$EeT00000NkvXXu0mjfT_KC| literal 0 HcmV?d00001 diff --git a/tests/test_decode.py b/tests/test_decode.py index bfd0770..ece4b20 100644 --- a/tests/test_decode.py +++ b/tests/test_decode.py @@ -74,6 +74,7 @@ ) from tools import ( + IMAGES, interim_locale, get_changelog_version, locale_encoding, @@ -85,9 +86,6 @@ ) -IMAGES = os.path.join(os.path.dirname(__file__), 'images', '') - - class DecodeTestCase(TestCase): def run_command(self, *cmd, **kwargs): stdin = kwargs.pop('stdin', None) diff --git a/tests/tools.py b/tests/tools.py index 54e7bd5..d03e878 100644 --- a/tests/tools.py +++ b/tests/tools.py @@ -41,6 +41,10 @@ def get_changelog_version(): return line.split()[1].strip('()') +IMAGES = os.path.join(os.path.dirname(__file__), 'images', '') +EXAMPLES = os.path.join(os.path.dirname(__file__), '..', 'examples', '') + + class TestCase(_TestCase): SkipTest = SkipTest maxDiff = None