Coverage for src/rtflite/core/constants.py: 100%

69 statements  

« prev     ^ index     » next       coverage.py v7.10.5, created at 2025-08-25 22:35 +0000

1"""RTF constants and magic numbers consolidated in a single source of truth. 

2 

3This module eliminates magic numbers scattered throughout the codebase and provides 

4clear documentation for all RTF-related constants used in the library. 

5""" 

6 

7from collections.abc import Mapping 

8from typing import Final 

9 

10 

11class RTFConstants: 

12 """Core RTF constants for measurements, formatting, and control codes.""" 

13 

14 # === Measurement Constants === 

15 TWIPS_PER_INCH: Final[int] = 1440 

16 """Number of twips in one inch. RTF uses twips as the base unit.""" 

17 

18 POINTS_PER_INCH: Final[int] = 72 

19 """Number of points in one inch.""" 

20 

21 LINE_SPACING_FACTOR: Final[int] = 240 

22 """Factor used for line spacing calculations in RTF.""" 

23 

24 # === Default Dimensions === 

25 DEFAULT_BORDER_WIDTH: Final[int] = 15 

26 """Default border width in twips.""" 

27 

28 DEFAULT_CELL_HEIGHT: Final[float] = 0.15 

29 """Default cell height in inches.""" 

30 

31 DEFAULT_SPACE_BEFORE: Final[int] = 15 

32 """Default space before paragraph in points.""" 

33 

34 DEFAULT_SPACE_AFTER: Final[int] = 15 

35 """Default space after paragraph in points.""" 

36 

37 # === Font Sizes === 

38 DEFAULT_FONT_SIZE: Final[float] = 9 

39 """Default font size in points.""" 

40 

41 # === RTF Control Codes === 

42 class Control: 

43 """RTF control word constants.""" 

44 

45 # Text formatting 

46 SUPER: Final[str] = "\\super " 

47 SUB: Final[str] = "\\sub " 

48 LINE_BREAK: Final[str] = "\\line " 

49 PAGE_BREAK: Final[str] = "\\page" 

50 

51 # Document structure 

52 RTF_HEADER: Final[str] = "{\\rtf1\\ansi" 

53 FONT_TABLE_START: Final[str] = "{\\fonttbl" 

54 COLOR_TABLE_START: Final[str] = "{\\colortbl" 

55 

56 # Page formatting 

57 PAGE_NUMBER: Final[str] = "\\chpgn " 

58 TOTAL_PAGES: Final[str] = "\\totalpage " 

59 PAGE_FIELD: Final[str] = "{\\field{\\*\\fldinst NUMPAGES }} " 

60 

61 # Paragraph formatting 

62 PARAGRAPH_START: Final[str] = "\\pard" 

63 CELL_END: Final[str] = "\\cell" 

64 ROW_END: Final[str] = "\\row" 

65 

66 # === Format Codes === 

67 FORMAT_CODES: Final[Mapping[str, str]] = { 

68 "": "", 

69 "b": "\\b", # Bold 

70 "i": "\\i", # Italic 

71 "u": "\\ul", # Underline 

72 "s": "\\strike", # Strikethrough 

73 "^": "\\super", # Superscript 

74 "_": "\\sub", # Subscript 

75 } 

76 

77 # === Text Justification Codes === 

78 TEXT_JUSTIFICATION_CODES: Final[Mapping[str, str]] = { 

79 "": "", 

80 "l": "\\ql", # Left 

81 "c": "\\qc", # Center 

82 "r": "\\qr", # Right 

83 "d": "\\qd", # Distributed 

84 "j": "\\qj", # Justified 

85 } 

86 

87 # === Row Justification Codes === 

88 ROW_JUSTIFICATION_CODES: Final[Mapping[str, str]] = { 

89 "": "", 

90 "l": "\\trql", # Left 

91 "c": "\\trqc", # Center 

92 "r": "\\trqr", # Right 

93 } 

94 

95 # === Border Style Codes === 

96 BORDER_CODES: Final[Mapping[str, str]] = { 

97 "single": "\\brdrs", 

98 "double": "\\brdrdb", 

99 "thick": "\\brdrth", 

100 "dotted": "\\brdrdot", 

101 "dashed": "\\brdrdash", 

102 "small-dash": "\\brdrdashsm", 

103 "dash-dotted": "\\brdrdashd", 

104 "dash-dot-dotted": "\\brdrdashdd", 

105 "triple": "\\brdrtriple", 

106 "wavy": "\\brdrwavy", 

107 "double-wavy": "\\brdrwavydb", 

108 "striped": "\\brdrengrave", 

109 "embossed": "\\brdremboss", 

110 "engraved": "\\brdrengrave", 

111 "frame": "\\brdrframe", 

112 "": "", # No border 

113 } 

114 

115 # === Vertical Alignment Codes === 

116 VERTICAL_ALIGNMENT_CODES: Final[Mapping[str, str]] = { 

117 "top": "\\clvertalt", 

118 "center": "\\clvertalc", 

119 "bottom": "\\clvertalb", 

120 "merge_first": "\\clvertalc\\clvmgf", 

121 "merge_rest": "\\clvertalc\\clvmrg", 

122 "": "", 

123 } 

124 

125 # === Character Conversion Mapping === 

126 RTF_CHAR_MAPPING: Final[Mapping[str, str]] = { 

127 "^": "\\super ", 

128 "_": "\\sub ", 

129 ">=": "\\geq ", 

130 "<=": "\\leq ", 

131 "\n": "\\line ", 

132 "\\pagenumber": "\\chpgn ", 

133 "\\totalpage": "\\totalpage ", 

134 "\\pagefield": "{\\field{\\*\\fldinst NUMPAGES }} ", 

135 } 

136 

137 

138class RTFDefaults: 

139 """Default values for RTF document configuration.""" 

140 

141 # === Page Settings === 

142 ORIENTATION: Final[str] = "portrait" 

143 BORDER_FIRST: Final[str] = "double" 

144 BORDER_LAST: Final[str] = "double" 

145 USE_COLOR: Final[bool] = False 

146 

147 # === Text Settings === 

148 TEXT_FONT: Final[int] = 1 

149 TEXT_ALIGNMENT: Final[str] = "l" # Left 

150 TEXT_HYPHENATION: Final[bool] = True 

151 TEXT_CONVERT: Final[bool] = True # Enable LaTeX to Unicode conversion 

152 

153 # === Table Settings === 

154 TABLE_ALIGNMENT: Final[str] = "c" # Center 

155 

156 # === Color Defaults === 

157 @classmethod 

158 def get_default_colors(cls) -> Mapping[str, str]: 

159 """Get all colors from the comprehensive color table.""" 

160 from rtflite.dictionary.color_table import name_to_rtf 

161 

162 return name_to_rtf 

163 

164 # Provide DEFAULT_COLORS as a cached property for backward compatibility 

165 _default_colors_cache = None 

166 

167 @classmethod 

168 def DEFAULT_COLORS(cls) -> Mapping[str, str]: 

169 """Get all colors from the comprehensive color table (cached).""" 

170 if cls._default_colors_cache is None: 

171 cls._default_colors_cache = cls.get_default_colors() 

172 return cls._default_colors_cache 

173 

174 

175class RTFMeasurements: 

176 """Utility class for RTF measurement conversions.""" 

177 

178 @staticmethod 

179 def inch_to_twip(inches: float) -> int: 

180 """Convert inches to twips. 

181 

182 Args: 

183 inches: Length in inches 

184 

185 Returns: 

186 Length in twips (1/1440 of an inch) 

187 """ 

188 return round(inches * RTFConstants.TWIPS_PER_INCH) 

189 

190 @staticmethod 

191 def twip_to_inch(twips: int) -> float: 

192 """Convert twips to inches. 

193 

194 Args: 

195 twips: Length in twips 

196 

197 Returns: 

198 Length in inches 

199 """ 

200 return twips / RTFConstants.TWIPS_PER_INCH 

201 

202 @staticmethod 

203 def point_to_halfpoint(points: float) -> int: 

204 """Convert points to half-points for RTF font sizes. 

205 

206 Args: 

207 points: Font size in points 

208 

209 Returns: 

210 Font size in half-points (RTF format) 

211 """ 

212 return int(points * 2)