from dataclasses import dataclass from pathlib import Path ROOT = Path(__file__).resolve().parents[1] DATA_DIR = ROOT / "data" DECKS_DIR = DATA_DIR / "decks" FONTS_DIR = DATA_DIR / "fonts" PRINT_DIR = ROOT / "print" TEMP_DIR = PRINT_DIR / "temp" RENDER_DIR = TEMP_DIR / "rendered" PAGES_DIR = TEMP_DIR / "pages" PLAYER_COLORS = ["#00ea0e", "#ead500", "#0abdc6", "#ea2b00", "#ea00d9"] OFFICE_SINGLE_STARTER_COLOR = "#0abdc6" BASEMENT_SINGLE_STARTER_COLOR = "#ea00d9" CARD_DECKS = { "encounter": ["encounterCards"], "office": ["darkWebCompanyDeck", "startingCompanyDeck"], "basement": ["darkWebBasementDeck", "startingBasementDeck"], } DECK_GROUPS = {deck_name: group for group, deck_names in CARD_DECKS.items() for deck_name in deck_names} TITLE_WIDTH_LIMITS = { "encounter": 0.94, "office": 0.81, "basement": 0.98, } TEXT_AREA_TOP_CROPPED_RATIOS = { "encounter": 0.552, "office": 0.557, "basement": 0.542, } TEXT_AREA_BOTTOM_CROPPED_RATIO = 0.965 FLAVOUR_BLOCK_BOTTOM_SHIFT_CROPPED_RATIO = 0.0061 STARTER_DECKS = {"startingCompanyDeck", "startingBasementDeck"} CHARACTER_DECK = "characters" BOARD_DECK = "gameBoard" CHECK_COLORS = { "VOICE": "#0abdc6", "MIND": "#ea00d9", "WILL": "#ead500", } PAPER_SIZES_MM = { "a3": (297.0, 420.0), "a4": (210.0, 297.0), "letter": (215.9, 279.4), "legal": (215.9, 355.6), "tabloid": (279.4, 431.8), } CARD_SIZE_OPTIONS_MM = { "poker": (63.5, 88.9), "bridge": (56.0, 87.0), "mini-euro": (44.0, 68.0), "tarot": (70.0, 120.0), } TEXT_INDENT_CHARS = 2 ITALIC_SHEAR = 0.18 BOLD_OFFSETS = ((1, 0), (0, 1), (1, 1)) FIXED_CROP = (70, 73, 70, 73) TOP_BOTTOM_EDGE_REPLACE_PX = 10 DEFAULT_SOURCE_CROP_PX = 37.5 TEXT_SUPERSAMPLING = 3 MAX_WORKERS = 8 PDF_COMPRESSION_LEVEL = 9 @dataclass(frozen=True) class BuildConfig: paper_name: str paper_mm: tuple[float, float] dpi: int crop_black_borders: bool card_mm: tuple[float, float] board_mode: str board_custom_mm: tuple[float, float] | None gamma_enabled: bool gamma: float black_point: float project_version: str starter_unique_colors: bool rent_raised_count: int margin_mm: float bleed_mm: float clean_edge_mm: float output_pdf_name: str delete_temp: bool | None text_supersampling: int render_only: bool = False @dataclass(frozen=True) class PrintItem: path: Path group: str label: str image_type: str is_starter: bool = False sort_label: str = "" bleed_path: Path | None = None layout_rotation: int | None = None def mm_to_px(mm: float, dpi: int) -> int: return round(mm / 25.4 * dpi) def worker_count(task_count: int) -> int: return max(1, min(MAX_WORKERS, task_count))