Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
22 changes: 20 additions & 2 deletions slip39/layout/components.py
Original file line number Diff line number Diff line change
Expand Up @@ -213,6 +213,13 @@ def element( self ):
Coordinate = namedtuple( 'Coordinate', ('x', 'y') )


# A SLIP-39 mnemonic cell is eg. "12 advocate": up to MNEM_CHARS (2 digits, a space, an 8-letter word)
# of monospaced font, each MONO_WIDTH of the font size wide; fit within MNEM_FIT of the column width.
MNEM_CHARS = 11
MONO_WIDTH = .6
MNEM_FIT = .9


def layout_card(
card_size: Coordinate,
card_margin: int,
Expand Down Expand Up @@ -314,6 +321,17 @@ def layout_card(
assert num_mnemonics in MNEM_ROWS_COLS, \
f"Invalid SLIP-39 mnemonic word count: {num_mnemonics}"
rows,cols = MNEM_ROWS_COLS[num_mnemonics]

def mnem_size( region, size_ratio ):
"""The mnemonic font size (points): from the height of each row, unless that is too wide for
the longest "NN word" (MNEM_CHARS mono characters) to fit in the width of each column."""
return min(
region.h / rows * PT_IN * size_ratio,
region.w / cols * PT_IN * MNEM_FIT / ( MNEM_CHARS * MONO_WIDTH ),
)

mnem_fontsize = mnem_size( card_mnemonics, 9/16 )
mnem_fontsize_big = mnem_size( card_mnemonics_big, 12/16 )
for r in range( rows ):
for c in range( cols ):
card_mnemonics.add_region_proportional(
Expand All @@ -325,7 +343,7 @@ def layout_card(
y2 = (r+1)/rows,
font = 'mono',
bold = True,
size_ratio = 9/16,
size = mnem_fontsize,
)
)
card_mnemonics_big.add_region_proportional(
Expand All @@ -337,7 +355,7 @@ def layout_card(
y2 = (r+1)/rows,
font = 'mono',
bold = True,
size_ratio = 12/16,
size = mnem_fontsize_big,
)
)

Expand Down
15 changes: 14 additions & 1 deletion slip39/output_test.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,8 @@
from fpdf import FPDF, FlexTemplate

from .layout import Region, Text, Image, Box, Coordinate
from .defaults import MM_IN
from .layout.components import layout_card, MNEM_CHARS, MONO_WIDTH
from .defaults import MM_IN, PT_IN, CARD_SIZES, MNEM_ROWS_COLS


def test_Region():
Expand Down Expand Up @@ -73,3 +74,15 @@ def test_Region():
tpl.render( offsetx = card_size.x * MM_IN, offsety = card_size.y * MM_IN )

#pdf.output( "test.pdf" ) # To view results in test.pdf, uncomment


def test_card_mnemonics_fit():
"""The longest mnemonic cell ("12 advocate") must fit within its column, for every card size."""
for card_size,((card_h,card_w),card_margin) in CARD_SIZES.items():
for num_mnemonics in MNEM_ROWS_COLS:
card = layout_card( Coordinate( y=card_h, x=card_w ), card_margin, num_mnemonics )
for e in card.elements():
if e['name'].startswith( 'mnem-' ):
cell_pt = ( e['x2'] - e['x1'] ) / MM_IN * PT_IN
assert e['size'] * MONO_WIDTH * MNEM_CHARS <= cell_pt, \
f"{card_size} card, {num_mnemonics} words: {e['name']} {e['size']:.2f}pt too big for a {cell_pt:.1f}pt column"
Loading