From ad4891589d5146cd0411cb27d275089709652a4d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fran=C3=A7ois=20Degros?= <57880064+fdegros@users.noreply.github.com> Date: Sun, 25 Oct 2026 16:18:41 +1100 Subject: [PATCH] Accept only the supported paper sizes in --paper --paper handed any name straight to the PDF library, so eg. `--paper a5` was accepted, even though only Letter, Legal, A4 and Photo are supported (PAPER_FORMATS, and the sizes offered by the GUI). A5 is too small to hold a Paper Wallet (7.75 inches wide, vs. 5.8 inches for a portrait A5 page), which ended in a ZeroDivisionError (#10); and `--paper photo` failed with "Unknown page format: photo", as the Photo size is not a paper name the PDF library knows. Convert --paper (case-insensitively) through PAPER_FORMATS, and refuse other sizes up front with a one-line message: error: argument --paper: Unsupported paper size 'a5'; use one of: Letter, Legal, A4, Photo Add main_test.py. See #10. --- README.org | 2 +- slip39/main.py | 18 +++++++++++++++--- slip39/main_test.py | 31 +++++++++++++++++++++++++++++++ 3 files changed, 47 insertions(+), 4 deletions(-) create mode 100644 slip39/main_test.py diff --git a/README.org b/README.org index 5685a82..5023662 100644 --- a/README.org +++ b/README.org @@ -513,7 +513,7 @@ slip39 --help 2>&1 | sed 's/^/: /' # (just for output formatting) : quarter, photo or '(,),' (default: : business) : --no-card Disable PDF SLIP-39 mnemonic card output - : --paper PAPER Paper size (default: Letter) + : --paper PAPER Paper size; Letter, Legal, A4, Photo (default: Letter) : --cover Produce PDF SLIP-39 cover page : --no-cover Disable PDF SLIP-39 cover page : --identifier IDENTIFIER diff --git a/slip39/main.py b/slip39/main.py index cad8959..5bc6681 100644 --- a/slip39/main.py +++ b/slip39/main.py @@ -28,7 +28,7 @@ from .util import log_cfg, log_level, input_secure from .layout import write_pdfs from .defaults import ( # noqa: F401 - ANONYMOUS, CARD, CARD_SIZES, PAPER, WALLET, WALLET_SIZES, + ANONYMOUS, CARD, CARD_SIZES, PAPER, PAPER_FORMATS, WALLET, WALLET_SIZES, BITS, BITS_DEFAULT, FILENAME_FORMAT, FILENAME_KEYWORDS, @@ -43,6 +43,18 @@ log = logging.getLogger( __package__ ) +def paper_format( name ): + """Convert a (case-insensitive) paper name into its PAPER_FORMATS value (a paper format name, or + (width, height) in mm). Other sizes aren't supported: the cards and Paper Wallets must fit on the + page, and eg. A5 is too small for a Paper Wallet. + + """ + for paper,fmt in PAPER_FORMATS.items(): + if paper.lower() == name.lower(): + return fmt + raise argparse.ArgumentTypeError( f"Unsupported paper size {name!r}; use one of: {', '.join( PAPER_FORMATS )}" ) + + def main( argv=None ): ap = argparse.ArgumentParser( description = "Create and output SLIP-39 encoded Seeds and Paper Wallets to a PDF file.", @@ -115,8 +127,8 @@ def main( argv=None ): ap.add_argument( '--no-card', dest="card", action='store_false', help="Disable PDF SLIP-39 mnemonic card output" ) ap.add_argument( '--paper', - default=None, - help=f"Paper size (default: {PAPER})" ) + default=None, type=paper_format, + help=f"Paper size; {', '.join( PAPER_FORMATS )} (default: {PAPER})" ) ap.add_argument( '--cover', dest="cover_page", action='store_true', default=True, help="Produce PDF SLIP-39 cover page" ) diff --git a/slip39/main_test.py b/slip39/main_test.py new file mode 100644 index 0000000..22f46ac --- /dev/null +++ b/slip39/main_test.py @@ -0,0 +1,31 @@ +import argparse +import pytest + +from .defaults import PAPER_FORMATS +from .main import main, paper_format + + +def test_paper_format(): + """Paper sizes are case-insensitive names from PAPER_FORMATS, converted to their format value.""" + assert paper_format( 'a4' ) == paper_format( 'A4' ) == PAPER_FORMATS['A4'] + assert paper_format( 'photo' ) == PAPER_FORMATS['Photo'] # eg. (w,h) in mm; not a paper name known to the PDF library + for unsupported in ( 'a5', 'a3', 'tabloid', '' ): + with pytest.raises( argparse.ArgumentTypeError ) as exc: + paper_format( unsupported ) + assert "Unsupported paper size" in str( exc.value ) + assert all( paper in str( exc.value ) for paper in PAPER_FORMATS ) + + +def test_main_paper( tmp_path, monkeypatch, capsys ): + monkeypatch.chdir( tmp_path ) + # An unsupported paper size is refused up front, w/ a one-line message + with pytest.raises( SystemExit ) as exc: + main( [ '-q', '-c', 'BTC', '--paper', 'a5', 'Test' ] ) + assert exc.value.code == 2 + assert "Unsupported paper size 'a5'" in capsys.readouterr().err + assert not list( tmp_path.glob( '*.pdf' )) + + # Supported sizes are produced + for paper in ( 'a4', 'Photo' ): + assert main( [ '-q', '-c', 'BTC', '--paper', paper, '-o', f"paper-{paper}", 'Test' ] ) == 0 + assert ( tmp_path / f"paper-{paper}.pdf" ).stat().st_size > 0