"""Sheet sizes: paper in millimetres, and common screen, phone, tablet and projector resolutions in pixels. A screen size keeps its exact shape (aspect ratio) but is drawn on a sheet whose long side is SCREEN_LONG_MM, so line weights and lettering look the same as on paper; its SVG is then given the real pixel size as its width and height, so it shows at 1:1 on that screen. """ SCREEN_LONG_MM = 297.0 # key -> (label, width, height) in millimetres; as the thing is held: portrait for paper PAPERS = { 'a5': ('A5 (148 x 210 mm)', 148.0, 210.0), 'a4': ('A4 (210 x 297 mm)', 210.0, 297.0), 'a3': ('A3 (297 x 420 mm)', 297.0, 420.0), 'a2': ('A2 (420 x 594 mm)', 420.0, 594.0), 'a1': ('A1 (594 x 841 mm)', 594.0, 841.0), 'letter': ('US Letter (8.5 x 11 in)', 215.9, 279.4), 'tabloid': ('US Tabloid (11 x 17 in)', 279.4, 431.8), } # key -> (label, width, height) in pixels, in the shape the device is usually used in SCREENS = { 'phone-ios': ('Phone: iPhone 15 / 16 (1179 x 2556)', 1179, 2556), 'phone-ios-max': ('Phone: iPhone Pro Max (1290 x 2796)', 1290, 2796), 'phone-android': ('Phone: Android FHD+ (1080 x 2400)', 1080, 2400), 'phone-android-qhd': ('Phone: Android QHD+ (1440 x 3200)', 1440, 3200), 'tablet-ipad': ('Tablet: iPad 10.9 in (1640 x 2360)', 1640, 2360), 'tablet-ipad-pro': ('Tablet: iPad Pro 12.9 in (2048 x 2732)', 2048, 2732), 'tablet-android': ('Tablet: Android (1200 x 1920)', 1200, 1920), 'laptop-768': ('Laptop: 1366 x 768', 1366, 768), 'laptop-mba': ('Laptop: MacBook Air 13 in (2560 x 1664)', 2560, 1664), 'laptop-mbp': ('Laptop: MacBook Pro 14 in (3024 x 1964)', 3024, 1964), 'hd': ('Screen: HD 720p (1280 x 720)', 1280, 720), 'fhd': ('Screen: Full HD 1080p (1920 x 1080)', 1920, 1080), 'wuxga': ('Screen: WUXGA (1920 x 1200)', 1920, 1200), 'qhd': ('Screen: QHD 1440p (2560 x 1440)', 2560, 1440), 'uwqhd': ('Screen: Ultrawide 21:9 (3440 x 1440)', 3440, 1440), 'uhd': ('Screen: 4K UHD (3840 x 2160)', 3840, 2160), 'uhd8k': ('Screen: 8K UHD (7680 x 4320)', 7680, 4320), 'proj-xga': ('Projector: XGA 4:3 (1024 x 768)', 1024, 768), 'proj-wxga': ('Projector: WXGA 16:10 (1280 x 800)', 1280, 800), 'proj-1080': ('Projector: 1080p 16:9 (1920 x 1080)', 1920, 1080), 'proj-4k': ('Projector: DCI 4K cinema (4096 x 2160)', 4096, 2160), 'social-square': ('Social: square post (1080 x 1080)', 1080, 1080), 'social-story': ('Social: story / reel (1080 x 1920)', 1080, 1920), } CHOICES = [(v[0], k) for k, v in PAPERS.items()] + [(v[0], k) for k, v in SCREENS.items()] ORIENTATIONS = ['auto', 'portrait', 'landscape'] def is_screen(key: str) -> bool: return key in SCREENS def _oriented(w, h, orientation): if orientation == 'portrait': return (min(w, h), max(w, h)) if orientation == 'landscape': return (max(w, h), min(w, h)) return (w, h) def size_mm(key: str, orientation: str = 'auto') -> tuple: """-> (width, height) of the sheet in millimetres. 'auto' keeps the shape the paper or device is usually held in.""" if key in SCREENS: _, w, h = SCREENS[key]; s = SCREEN_LONG_MM / max(w, h) return _oriented(w * s, h * s, orientation) _, w, h = PAPERS.get(key, PAPERS['a4']) return _oriented(w, h, orientation) def size_px(key: str, orientation: str = 'auto'): """-> (width, height) in pixels for a screen size, or None for paper.""" if key not in SCREENS: return None _, w, h = SCREENS[key] return _oriented(w, h, orientation)