diff --git a/README.md b/README.md index 76bf71e..11a4a93 100644 --- a/README.md +++ b/README.md @@ -40,6 +40,10 @@ Requires **python >= 3.6** | scale | Scale a selection of pages of a pdf file. | | split | Split a pdf file at specified indices. +## Commands + +### decrypt + ``` Usage: pypdf-cli decrypt [OPTIONS] INPUT_FILE @@ -54,6 +58,9 @@ Options: --help Show this message and exit. ``` + +### delete + ``` Usage: pypdf-cli delete [OPTIONS] INPUT_FILE @@ -62,18 +69,19 @@ Usage: pypdf-cli delete [OPTIONS] INPUT_FILE INPUT_FILE is the location of the pdf file you wish to delete pages from. Options: - -i, --select-index INTEGER Single index. Enter an integer number. - -r, --select-range TEXT Range of indices. Enter as list with 2 elements - without spaces or wrap in quotation marks. E.g. - [1,3] or "[1, 3]" - -l, --select-list TEXT List of indices. Enter list without spaces or - wrap in quotation marks. E.g. [1,2,3] or "[1, 2, - 3]" - -o, --output PATH Optional location of the output pdf file. - WARNING: overwrites existing files. - --help Show this message and exit. + -i, --select-index INT Single index. Enter an integer number. + -r, --select-range INT RANGE Range of indices. Enter as list with 2 + elements without spaces or wrap in quotation + marks. E.g. 1-3. + -l, --select-list INT LIST List of indices. Enter list without spaces or + wrap in quotation marks. E.g. 1,2,3. + -o, --output PATH Optional location of the output pdf file. + WARNING: overwrites existing files. + --help Show this message and exit. ``` +### encrypt + ``` Usage: pypdf-cli encrypt [OPTIONS] INPUT_FILE @@ -95,6 +103,9 @@ Options: --help Show this message and exit. ``` + +### extract + ``` Usage: pypdf-cli extract [OPTIONS] INPUT_FILE @@ -103,18 +114,20 @@ Usage: pypdf-cli extract [OPTIONS] INPUT_FILE INPUT_FILE is the location of the pdf file you wish to extract pages from. Options: - -i, --select-index INTEGER Single index. Enter an integer number. - -r, --select-range TEXT Range of indices. Enter as list with 2 elements - without spaces or wrap in quotation marks. E.g. - [1,3] or "[1, 3]" - -l, --select-list TEXT List of indices. Enter list without spaces or - wrap in quotation marks. E.g. [1,2,3] or "[1, 2, - 3]" - -o, --output PATH Optional location of the output pdf file. - WARNING: overwrites existing files. - --help Show this message and exit. + -i, --select-index INT Single index. Enter an integer number. + -r, --select-range INT RANGE Range of indices. Enter as list with 2 + elements without spaces or wrap in quotation + marks. E.g. 1-3. + -l, --select-list INT LIST List of indices. Enter list without spaces or + wrap in quotation marks. E.g. 1,2,3. + -o, --output PATH Optional location of the output pdf file. + WARNING: overwrites existing files. + --help Show this message and exit. ``` + +### info + ``` Usage: pypdf-cli info [OPTIONS] INPUT_FILE @@ -126,6 +139,9 @@ Options: --help Show this message and exit. ``` + +### insert + ``` Usage: pypdf-cli insert [OPTIONS] INPUT_FILES... @@ -137,13 +153,14 @@ Usage: pypdf-cli insert [OPTIONS] INPUT_FILES... the first. Options: - -o, --output PATH Optional location of the output pdf file. - WARNING: overwrites existing files. - -i, --select-index INTEGER Single index. Enter an integer number. - [required] - --help Show this message and exit. + -o, --output PATH Optional location of the output pdf file. WARNING: + overwrites existing files. + -i, --select-index INT Single index. Enter an integer number. [required] + --help Show this message and exit. ``` +### merge + ``` Usage: pypdf-cli merge [OPTIONS] [INPUT_FILES]... @@ -158,6 +175,9 @@ Options: --help Show this message and exit. ``` + +### remove + ``` Usage: pypdf-cli remove [OPTIONS] INPUT_FILE @@ -175,6 +195,9 @@ Options: --help Show this message and exit. ``` + +### reverse + ``` Usage: pypdf-cli reverse [OPTIONS] INPUT_FILE @@ -188,6 +211,9 @@ Options: --help Show this message and exit. ``` + +### rotate + ``` Usage: pypdf-cli rotate [OPTIONS] INPUT_FILE @@ -196,21 +222,22 @@ Usage: pypdf-cli rotate [OPTIONS] INPUT_FILE INPUT_FILE is the location of the pdf file you wish to rotate. Options: - -i, --select-index INTEGER Single index. Enter an integer number. - -r, --select-range TEXT Range of indices. Enter as list with 2 elements - without spaces or wrap in quotation marks. E.g. - [1,3] or "[1, 3]" - -l, --select-list TEXT List of indices. Enter list without spaces or - wrap in quotation marks. E.g. [1,2,3] or "[1, 2, - 3]" - -o, --output PATH Optional location of the output pdf file. - WARNING: overwrites existing files. - -a, --all Select every index. - --angle INTEGER Angle to rotate pages clockwise. Must be - increment of 90. [required] - --help Show this message and exit. + -i, --select-index INT Single index. Enter an integer number. + -r, --select-range INT RANGE Range of indices. Enter as list with 2 + elements without spaces or wrap in quotation + marks. E.g. 1-3. + -l, --select-list INT LIST List of indices. Enter list without spaces or + wrap in quotation marks. E.g. 1,2,3. + -o, --output PATH Optional location of the output pdf file. + WARNING: overwrites existing files. + -a, --all Select every index. + --angle INTEGER Angle to rotate pages clockwise. Must be + increment of 90. [required] + --help Show this message and exit. ``` +### scale + ``` Usage: pypdf-cli scale [OPTIONS] INPUT_FILE @@ -220,25 +247,26 @@ Usage: pypdf-cli scale [OPTIONS] INPUT_FILE INPUT_FILE is the location of the pdf file you wish to scale. Options: - -i, --select-index INTEGER Single index. Enter an integer number. - -r, --select-range TEXT Range of indices. Enter as list with 2 elements - without spaces or wrap in quotation marks. E.g. - [1,3] or "[1, 3]" - -l, --select-list TEXT List of indices. Enter list without spaces or - wrap in quotation marks. E.g. [1,2,3] or "[1, 2, - 3]" - -o, --output PATH Optional location of the output pdf file. - WARNING: overwrites existing files. - -a, --all Select every index. - --scale-to Whether to change width and height of pages to a - flat value. - --horizontal FLOAT Horizontal factor or value to scale pages by or - to. [required] - --vertical FLOAT Vertical factor or value to scale pages by or - to. [required] - --help Show this message and exit. + -i, --select-index INT Single index. Enter an integer number. + -r, --select-range INT RANGE Range of indices. Enter as list with 2 + elements without spaces or wrap in quotation + marks. E.g. 1-3. + -l, --select-list INT LIST List of indices. Enter list without spaces or + wrap in quotation marks. E.g. 1,2,3. + -o, --output PATH Optional location of the output pdf file. + WARNING: overwrites existing files. + -a, --all Select every index. + --scale-to Whether to change width and height of pages to + a flat value. + --horizontal FLOAT Horizontal factor or value to scale pages by + or to. [required] + --vertical FLOAT Vertical factor or value to scale pages by or + to. [required] + --help Show this message and exit. ``` +### split + ``` Usage: pypdf-cli split [OPTIONS] INPUT_FILE @@ -251,15 +279,14 @@ Usage: pypdf-cli split [OPTIONS] INPUT_FILE INPUT_FILE is the location of the pdf file you want to split. Options: - -i, --select-index INTEGER Single index. Enter an integer number. - -r, --select-range TEXT Range of indices. Enter as list with 2 elements - without spaces or wrap in quotation marks. E.g. - [1,3] or "[1, 3]" - -l, --select-list TEXT List of indices. Enter list without spaces or - wrap in quotation marks. E.g. [1,2,3] or "[1, 2, - 3]" - -o, --output PATH Optional location of the output pdf file. - WARNING: overwrites existing files. - -a, --all Select every index. - --help Show this message and exit. -```| \ No newline at end of file + -i, --select-index INT Single index. Enter an integer number. + -r, --select-range INT RANGE Range of indices. Enter as list with 2 + elements without spaces or wrap in quotation + marks. E.g. 1-3. + -l, --select-list INT LIST List of indices. Enter list without spaces or + wrap in quotation marks. E.g. 1,2,3. + -o, --output PATH Optional location of the output pdf file. + WARNING: overwrites existing files. + -a, --all Select every index. + --help Show this message and exit. +``` diff --git a/src/pypdfcli.py b/src/pypdfcli.py index 3e74dfc..20bede6 100644 --- a/src/pypdfcli.py +++ b/src/pypdfcli.py @@ -1,4 +1,3 @@ -from ast import literal_eval from os import makedirs from os.path import basename, dirname @@ -8,107 +7,7 @@ from pkg_resources import get_distribution -# Make custom Option classes to parse input - -class ListOption(click.Option): - - def type_cast_value(self, ctx, values): - return cast_values(cast_list, values) - - -class RangeOption(click.Option): - - def type_cast_value(self, ctx, values): - return cast_values(cast_range, values) - - -class IndexOption(click.Option): - - def type_cast_value(self, ctx, values): - return cast_values(cast_index, values) - - -def cast_values(cast_func, values): - """ - Cast values according to a specified function. - Catch trivial case and exceptions here. - """ - - if values is None: - return set() - try: - return cast_func(values) - except TypeError: - raise click.BadParameter(values) - except ValueError: - raise click.BadParameter(values) - except SyntaxError: - raise click.BadParameter(values) - - -def cast_list(values): - """ - Cast a cli list of str or Any to a python set of integer. - """ - - # values is an iterable because multiple is enabled - parsed = [] - for value in values: - - # parse value with ast module - # very sensitive process and likely to raise errors - literal = literal_eval(value) - - # enforce list of integers - if type(literal) != list or any(type(x) != int for x in literal): - raise TypeError - - # decrease values by 1 to accommodate PyPDF - parsed += [x - 1 for x in literal] - - # indices are unique - return set(parsed) - - -def cast_range(values): - """ - Cast a cli list of str or Any of length 2 to a python set of - integer by decoding the input to a python range. - """ - - # values is an iterable because multiple is enabled - parsed = [] - for value in values: - - # parse value with ast module - # very sensitive process and likely to raise errors - literal = literal_eval(value) - - # enforce list of integers with length 2 that encodes a range - if type(literal) != list or len(literal) != 2: - raise TypeError - - # decrease values by 1 to accommodate PyPDF - # decode list to range and build list from it - parsed += list(range(literal[0] - 1, literal[1])) - - # indices are unique - return set(parsed) - - -def cast_index(values): - """ - Cast a cli list of integers to a python set of integer. - """ - - # values is an iterable because multiple can be enabled - # or a single integer because multiple can be disabled - if type(values) == int: - return values - 1 - - # indices are unique - return set(int(x) - 1 for x in values) - +# auxiliary functions def generate_output_name(input_name, output_name, default): """ @@ -137,15 +36,21 @@ def generate_output_name(input_name, output_name, default): return basename(input_name)[:-4] + '_' + default + '.pdf' -def generate_selection(selection_sets, validation, non_empty=True): +def generate_selection(selection_collection, validation, non_empty=True): """ Verify selection and sum selections up. """ - # sum selections up + def append_selection(result, xs): + if isinstance(xs, set): + result |= xs + else: + for x in xs: + append_selection(result, x) + + # recursively append selections selection = set() - for selection_set in selection_sets: - selection |= selection_set + append_selection(selection, selection_collection) # verify existence of any selection if required if non_empty and len(selection) == 0: @@ -153,7 +58,7 @@ def generate_selection(selection_sets, validation, non_empty=True): # verify selection by provided function if not all(map(validation, selection)): - raise click.BadParameter(message='Invalid selection.') + raise click.BadParameter(message=f'Invalid selection: {sorted([s + 1 for s in selection])!r}') return selection @@ -203,16 +108,65 @@ def write(writer, output): writer.write(f) +# click functions + @click.group() @click.version_option(get_distribution('pypdf-cli').version) def cli(): pass +# Make custom Option classes to parse input +# Decrement user indices to match PyPDF indices + +class ListType(click.ParamType): + name = "INT LIST" + + def convert(self, value, param, ctx): + + try: + selection = set(int(v) - 1 for v in value.split(',')) + except ValueError: + self.fail(f'{value!r} is not a valid list of integers. Use as 1,2,3,4.', param, ctx) + + return selection + + +class RangeType(click.ParamType): + name = "INT RANGE" + + def convert(self, value, param, ctx): + + try: + splits = value.split('-') + assert len(splits) == 2 + selection = set(range(int(splits[0]) - 1, int(splits[1]))) + except (ValueError, AssertionError): + self.fail(f'{value!r} is not a valid range of integers. Use as 1-4.', param, ctx) + + return selection + + +class IntType(click.ParamType): + name = "INT" + + def convert(self, value, param, ctx): + + try: + selection = {int(value) - 1} + except ValueError: + self.fail(f'{value!r} is not a valid integer.', param, ctx) + + return selection + + +LIST_TYPE = ListType() +RANGE_TYPE = RangeType() +INT_TYPE = IntType() + OUTPUT_HELP = 'Optional location of the output pdf file. WARNING: overwrites existing files.' -LIST_HELP = 'List of indices. Enter list without spaces or wrap in quotation marks. E.g. [1,2,3] or \"[1, 2, 3]\"' -RANGE_HELP = 'Range of indices. Enter as list with 2 elements without spaces or wrap in quotation marks.' \ - ' E.g. [1,3] or \"[1, 3]\"' +LIST_HELP = 'List of indices. Enter list without spaces or wrap in quotation marks. E.g. 1,2,3.' +RANGE_HELP = 'Range of indices. Enter as list with 2 elements without spaces or wrap in quotation marks. E.g. 1-3.' INDEX_HELP = 'Single index. Enter an integer number.' ALL_HELP = 'Select every index.' @@ -224,12 +178,14 @@ def common_options(f): f = click.argument('input-file', type=click.Path(exists=True))(f) f = click.option('--output', '-o', type=click.Path(dir_okay=True), help=OUTPUT_HELP)(f) - f = click.option('--select-list', '-l', type=click.STRING, cls=ListOption, multiple=True, help=LIST_HELP)(f) - f = click.option('--select-range', '-r', type=click.STRING, cls=RangeOption, multiple=True, help=RANGE_HELP)(f) - f = click.option('--select-index', '-i', type=click.INT, cls=IndexOption, multiple=True, help=INDEX_HELP)(f) + f = click.option('--select-list', '-l', type=LIST_TYPE, multiple=True, help=LIST_HELP)(f) + f = click.option('--select-range', '-r', type=RANGE_TYPE, multiple=True, help=RANGE_HELP)(f) + f = click.option('--select-index', '-i', type=INT_TYPE, multiple=True, help=INDEX_HELP)(f) return f +# commands + @click.command() @common_options def delete(input_file, output, select_list, select_range, select_index): @@ -295,7 +251,7 @@ def extract(input_file, output, select_list, select_range, select_index): @click.command() @click.argument('input-files', nargs=2, type=click.Path(exists=True)) @click.option('--output', '-o', type=click.Path(), help=OUTPUT_HELP) -@click.option('--select-index', '-i', type=click.INT, cls=IndexOption, required=True, help=INDEX_HELP) +@click.option('--select-index', '-i', type=INT_TYPE, required=True, help=INDEX_HELP) def insert(input_files, output, select_index): """ Insert a second pdf file into a pdf file at a specified index. diff --git a/tests/test_pypdfcli.py b/tests/test_pypdfcli.py index ebbf532..b0fd115 100644 --- a/tests/test_pypdfcli.py +++ b/tests/test_pypdfcli.py @@ -12,75 +12,6 @@ TEST_OUT = join(TEST_DIR, 'test.pdf') -def test_cast_values(): - assert cast_values(None, None) == set() - - -def test_cast_list(): - with pytest.raises(ValueError): - cast_list(["[a]"]) - with pytest.raises(TypeError): - cast_list(["['a']"]) - with pytest.raises(TypeError): - cast_list(["[1,'a']"]) - with pytest.raises(TypeError): - cast_list(["[1,2.0]"]) - - assert cast_list([]) == set() - assert cast_list(["[]"]) == set() - assert cast_list(["[]", "[]"]) == set() - assert cast_list(["[1]"]) == {0} - assert cast_list(["[1,2]"]) == {0, 1} - assert cast_list(["[1]", "[2]"]) == {0, 1} - assert cast_list(["[]", "[1,2]"]) == {0, 1} - assert cast_list(["[]", "[1]"]) == {0} - assert cast_list(["[1]", "[1]"]) == {0} - - -def test_cast_range(): - with pytest.raises(ValueError): - cast_range(["[a]"]) - with pytest.raises(TypeError): - cast_range(["['a']"]) - with pytest.raises(TypeError): - cast_range(["[1,'a']"]) - with pytest.raises(TypeError): - cast_range(["[1,3]", "[1,'a']"]) - with pytest.raises(TypeError): - cast_range(["[1,3]", "[]"]) - with pytest.raises(TypeError): - cast_range(["[1,2,3]", "[1,2]"]) - with pytest.raises(TypeError): - cast_range(["[1,2.0]"]) - with pytest.raises(TypeError): - cast_range(["[]"]) - with pytest.raises(TypeError): - cast_range(["[1]"]) - - assert cast_range([]) == set() - assert cast_range(["[1,2]"]) == {0, 1} - assert cast_range(["[1,1]"]) == {0} - assert cast_range(["[1,2]", "[1,2]"]) == {0, 1} - assert cast_range(["[2,5]"]) == {1, 2, 3, 4} - assert cast_range(["[1,2]", "[2,5]"]) == {0, 1, 2, 3, 4} - - -def test_cast_index(): - with pytest.raises(ValueError): - cast_index(["a"]) - with pytest.raises(ValueError): - cast_index(["1", "a"]) - with pytest.raises(ValueError): - cast_index(["1.0"]) - with pytest.raises(ValueError): - cast_index([""]) - - assert cast_index([]) == set() - assert cast_index(["1"]) == {0} - assert cast_index(["1", "1"]) == {0} - assert cast_index(["1", "2"]) == {0, 1} - - def test_generate_output_name(): with pytest.raises(click.BadParameter): generate_output_name('missing extension', 'missing extension', 'default') @@ -145,7 +76,7 @@ def even(x): assert generate_selection([{2}], even) == {2} assert generate_selection([{2, 4}], even) == {2, 4} assert generate_selection([{2}, set()], even) == {2} - assert generate_selection([{2, 4}, {6}], even) == {2, 4, 6} + assert generate_selection([[{2, 4}, {8}], {6}], even) == {2, 4, 6, 8} def test_validate_index(): @@ -193,6 +124,54 @@ def test_buffer_number(): assert buffer_number(0, 1) == '1' +def test_convert_list(): + result = RUNNER.invoke(delete, [TEST_FILE, '-o', TEST_OUT]) + assert result.exit_code == 2 + + result = RUNNER.invoke(delete, [TEST_FILE, '-o', TEST_OUT, '-l', '1,a']) + assert result.exit_code == 2 + + result = RUNNER.invoke(delete, [TEST_FILE, '-o', TEST_OUT, '-l', '1,2.0']) + assert result.exit_code == 2 + + result = RUNNER.invoke(delete, [TEST_FILE, '-o', TEST_OUT, '-l', '1']) + assert result.exit_code == 0 + + result = RUNNER.invoke(delete, [TEST_FILE, '-o', TEST_OUT, '-l', '1,2,3']) + assert result.exit_code == 0 + + +def test_convert_range(): + result = RUNNER.invoke(delete, [TEST_FILE, '-o', TEST_OUT, '-r', '1']) + assert result.exit_code == 2 + + result = RUNNER.invoke(delete, [TEST_FILE, '-o', TEST_OUT, '-r', '1-a']) + assert result.exit_code == 2 + + result = RUNNER.invoke(delete, [TEST_FILE, '-o', TEST_OUT, '-r', '1-2.0']) + assert result.exit_code == 2 + + result = RUNNER.invoke(delete, [TEST_FILE, '-o', TEST_OUT, '-r', '1-2-3']) + assert result.exit_code == 2 + + result = RUNNER.invoke(delete, [TEST_FILE, '-o', TEST_OUT, '-r', '1,2']) + assert result.exit_code == 2 + + result = RUNNER.invoke(delete, [TEST_FILE, '-o', TEST_OUT, '-r', '1-2']) + assert result.exit_code == 0 + + +def test_convert_int(): + result = RUNNER.invoke(delete, [TEST_FILE, '-o', TEST_OUT, '-i', 'a']) + assert result.exit_code == 2 + + result = RUNNER.invoke(delete, [TEST_FILE, '-o', TEST_OUT, '-i', '2.0']) + assert result.exit_code == 2 + + result = RUNNER.invoke(delete, [TEST_FILE, '-o', TEST_OUT, '-i', '1']) + assert result.exit_code == 0 + + def test_delete(): # result.exit_code = 0 -> okay # result.exit_code = 2 -> error raised @@ -202,39 +181,39 @@ def test_delete(): for i in range(res_reader.numPages): assert res_reader.getPage(i).extractText().startswith(f'page{i + 2}') - RUNNER.invoke(delete, [TEST_FILE, '-o', TEST_OUT, '-l', '[1, 2, 3, 4]']) + RUNNER.invoke(delete, [TEST_FILE, '-o', TEST_OUT, '-l', '1,2,3,4']) res_reader = PdfFileReader(open(TEST_OUT, 'rb')) for i in range(res_reader.numPages): assert res_reader.getPage(i).extractText().startswith(f'page{i + 5}') - RUNNER.invoke(delete, [TEST_FILE, '-o', TEST_OUT, '-r', '[10, 12]']) + RUNNER.invoke(delete, [TEST_FILE, '-o', TEST_OUT, '-r', '10-12']) res_reader = PdfFileReader(open(TEST_OUT, 'rb')) for i in range(res_reader.numPages): assert res_reader.getPage(i).extractText().startswith(f'page{i + 1}') - RUNNER.invoke(delete, [TEST_FILE, '-o', TEST_OUT, '-r', '[10, 12]', '-l', '[1, 2, 3, 4]']) + RUNNER.invoke(delete, [TEST_FILE, '-o', TEST_OUT, '-r', '10-12', '-l', '1,2,3,4']) res_reader = PdfFileReader(open(TEST_OUT, 'rb')) for i in range(res_reader.numPages): assert res_reader.getPage(i).extractText().startswith(f'page{i + 5}') - RUNNER.invoke(delete, [TEST_FILE, '-o', TEST_OUT, '-r', '[5, 10]']) + RUNNER.invoke(delete, [TEST_FILE, '-o', TEST_OUT, '-r', '5-10']) res_reader = PdfFileReader(open(TEST_OUT, 'rb')) for i in range(4): assert res_reader.getPage(i).extractText().startswith(f'page{i + 1}') for i in range(4, 6): assert res_reader.getPage(i).extractText().startswith(f'page{i + 7}') - RUNNER.invoke(delete, [TEST_FILE, '-o', TEST_OUT, '-r', '[5, 10]', '-i', '1']) + RUNNER.invoke(delete, [TEST_FILE, '-o', TEST_OUT, '-r', '5-10', '-i', '1']) res_reader = PdfFileReader(open(TEST_OUT, 'rb')) for i in range(3): assert res_reader.getPage(i).extractText().startswith(f'page{i + 2}') for i in range(3, 5): assert res_reader.getPage(i).extractText().startswith(f'page{i + 8}') - result = RUNNER.invoke(delete, [TEST_FILE, '-o', TEST_OUT, '-r', '[1, 12]']) + result = RUNNER.invoke(delete, [TEST_FILE, '-o', TEST_OUT, '-r', '1-12']) assert result.exit_code == 2 - result = RUNNER.invoke(delete, [TEST_FILE, '-o', TEST_OUT, '-r', '[2, 12]', '-i', '1']) + result = RUNNER.invoke(delete, [TEST_FILE, '-o', TEST_OUT, '-r', '2-12', '-i', '1']) assert result.exit_code == 2 @@ -247,19 +226,19 @@ def test_extract(): assert res_reader.numPages == 1 assert res_reader.getPage(0).extractText().startswith('page1') - RUNNER.invoke(extract, [TEST_FILE, '-o', TEST_OUT, '-l', '[1, 2, 3, 4]']) + RUNNER.invoke(extract, [TEST_FILE, '-o', TEST_OUT, '-l', '1,2,3,4']) res_reader = PdfFileReader(open(TEST_OUT, 'rb')) assert res_reader.numPages == 4 for i in range(res_reader.numPages): assert res_reader.getPage(i).extractText().startswith(f'page{i + 1}') - RUNNER.invoke(extract, [TEST_FILE, '-o', TEST_OUT, '-r', '[10, 12]']) + RUNNER.invoke(extract, [TEST_FILE, '-o', TEST_OUT, '-r', '10-12']) res_reader = PdfFileReader(open(TEST_OUT, 'rb')) assert res_reader.numPages == 3 for i in range(res_reader.numPages): assert res_reader.getPage(i).extractText().startswith(f'page{i + 10}') - RUNNER.invoke(extract, [TEST_FILE, '-o', TEST_OUT, '-r', '[10, 12]', '-l', '[1, 2, 3, 4]']) + RUNNER.invoke(extract, [TEST_FILE, '-o', TEST_OUT, '-r', '10-12', '-l', '1,2,3,4']) res_reader = PdfFileReader(open(TEST_OUT, 'rb')) assert res_reader.numPages == 7 for i in range(4): @@ -267,17 +246,17 @@ def test_extract(): for i in range(4, 7): assert res_reader.getPage(i).extractText().startswith(f'page{i + 6}') - RUNNER.invoke(extract, [TEST_FILE, '-o', TEST_OUT, '-r', '[5, 10]', '-i', '1']) + RUNNER.invoke(extract, [TEST_FILE, '-o', TEST_OUT, '-r', '5-10', '-i', '1']) res_reader = PdfFileReader(open(TEST_OUT, 'rb')) assert res_reader.numPages == 7 assert res_reader.getPage(0).extractText().startswith('page1') for i in range(1, 7): assert res_reader.getPage(i).extractText().startswith(f'page{i + 4}') - result = RUNNER.invoke(extract, [TEST_FILE, '-o', TEST_OUT, '-r', '[1, 12]']) + result = RUNNER.invoke(extract, [TEST_FILE, '-o', TEST_OUT, '-r', '1-12']) assert result.exit_code == 2 - result = RUNNER.invoke(extract, [TEST_FILE, '-o', TEST_OUT, '-r', '[2, 12]', '-i', '1']) + result = RUNNER.invoke(extract, [TEST_FILE, '-o', TEST_OUT, '-r', '2-12', '-i', '1']) assert result.exit_code == 2 @@ -343,7 +322,7 @@ def test_split(): for i in range(0, 9): assert res2.getPage(i).extractText().startswith(f'page{i + 4}') - RUNNER.invoke(split, [TEST_FILE, '-o', TEST_OUT, '-l', '[3, 9]']) + RUNNER.invoke(split, [TEST_FILE, '-o', TEST_OUT, '-l', '3,9']) test_out_base = TEST_OUT[:-4] res1 = PdfFileReader(open(test_out_base + '_1.pdf', 'rb')) for i in range(0, 3): @@ -441,7 +420,7 @@ def test_rotate(): current_angle = rotate_obj if isinstance(rotate_obj, int) else rotate_obj.getObject() assert current_angle == -90 - RUNNER.invoke(rotate, [TEST_FILE, '-o', TEST_OUT, '-r', '[1,6]', '--angle', 90]) + RUNNER.invoke(rotate, [TEST_FILE, '-o', TEST_OUT, '-r', '1-6', '--angle', 90]) res = PdfFileReader(open(TEST_OUT, 'rb')) for i in range(6): page = res.getPage(i) @@ -462,7 +441,7 @@ def test_scale(): result = RUNNER.invoke(scale, [TEST_FILE, '-o', TEST_OUT, '-a', '--vertical', 2.0, '--horizontal', 2.0]) assert result.exit_code == 0 - result = RUNNER.invoke(scale, [TEST_FILE, '-o', TEST_OUT, '-r', '[1,6]', '--vertical', 2.0, '--horizontal', 2.0]) + result = RUNNER.invoke(scale, [TEST_FILE, '-o', TEST_OUT, '-r', '1-6', '--vertical', 2.0, '--horizontal', 2.0]) assert result.exit_code == 0 result = RUNNER.invoke(scale, [TEST_FILE, '-o', TEST_OUT, '-a', '--vertical', 256.0, '--horizontal', 256.0,