Coverage for src/rtflite/strwidth.py: 100%
29 statements
« prev ^ index » next coverage.py v7.6.10, created at 2025-02-03 15:40 +0000
« prev ^ index » next coverage.py v7.6.10, created at 2025-02-03 15:40 +0000
1import importlib.resources as pkg_resources
2from typing import Literal, overload
4from PIL import ImageFont
6import rtflite.fonts
8Unit = Literal["in", "mm", "px"]
10FontName = Literal[
11 "Times New Roman",
12 "Times New Roman Greek",
13 "Arial Greek",
14 "Arial",
15 "Helvetica",
16 "Calibri",
17 "Georgia",
18 "Cambria",
19 "Courier New",
20 "Symbol",
21]
23_FONT_PATHS = {
24 "Times New Roman": "liberation/LiberationSerif-Regular.ttf",
25 "Times New Roman Greek": "liberation/LiberationSerif-Regular.ttf",
26 "Arial Greek": "liberation/LiberationSans-Regular.ttf",
27 "Arial": "liberation/LiberationSans-Regular.ttf",
28 "Helvetica": "liberation/LiberationSans-Regular.ttf",
29 "Calibri": "cros/Carlito-Regular.ttf",
30 "Georgia": "cros/Gelasio-Regular.ttf",
31 "Cambria": "cros/Caladea-Regular.ttf",
32 "Courier New": "liberation/LiberationMono-Regular.ttf",
33 "Symbol": "liberation/LiberationSerif-Regular.ttf",
34}
36# Add type number type
37FontNumber = Literal[1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
39# Define bidirectional mappings
40RTF_FONT_NUMBERS = {
41 "Times New Roman": 1,
42 "Times New Roman Greek": 2,
43 "Arial Greek": 3,
44 "Arial": 4,
45 "Helvetica": 5,
46 "Calibri": 6,
47 "Georgia": 7,
48 "Cambria": 8,
49 "Courier New": 9,
50 "Symbol": 10,
51}
53RTF_FONT_NAMES: dict[int, FontName] = {v: k for k, v in RTF_FONT_NUMBERS.items()}
56@overload
57def get_string_width(
58 text: str,
59 font_name: FontName = "Times New Roman",
60 font_size: int = 12,
61 unit: Unit = "in",
62 dpi: float = 72.0,
63) -> float: ...
66@overload
67def get_string_width(
68 text: str,
69 font_type: FontNumber,
70 font_size: int = 12,
71 unit: Unit = "in",
72 dpi: float = 72.0,
73) -> float: ...
76def get_string_width(
77 text: str,
78 font: FontName | FontNumber = "Times New Roman",
79 font_size: int = 12,
80 unit: Unit = "in",
81 dpi: float = 72.0,
82) -> float:
83 """
84 Calculate the width of a string for a given font and size.
85 Uses metric-compatible fonts that match the metrics of common proprietary fonts.
87 Args:
88 text: The string to measure.
89 font: RTF font name or RTF font number (1-10).
90 font_size: Font size in points.
91 unit: Unit to return the width in.
92 dpi: Dots per inch for unit conversion.
94 Returns:
95 Width of the string in the specified unit.
97 Raises:
98 ValueError: If an unsupported font name/number or unit is provided.
99 """
100 # Convert font type number to name if needed
101 if isinstance(font, int):
102 if font not in RTF_FONT_NAMES:
103 raise ValueError(f"Unsupported font number: {font}")
104 font_name = RTF_FONT_NAMES[font]
105 else:
106 font_name = font
108 if font_name not in _FONT_PATHS:
109 raise ValueError(f"Unsupported font name: {font_name}")
111 font_path = pkg_resources.files(rtflite.fonts) / _FONT_PATHS[font_name]
112 font = ImageFont.truetype(str(font_path), size=font_size)
113 width_px = font.getlength(text)
115 conversions = {
116 "px": lambda x: x,
117 "in": lambda x: x / dpi,
118 "mm": lambda x: (x / dpi) * 25.4,
119 }
121 if unit not in conversions:
122 raise ValueError(f"Unsupported unit: {unit}")
124 return conversions[unit](width_px)