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

68 statements  

« prev     ^ index     » next       coverage.py v7.10.3, created at 2025-08-14 16: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 typing import Dict, Final 

8 

9 

10class RTFConstants: 

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

12 

13 # === Measurement Constants === 

14 TWIPS_PER_INCH: Final[int] = 1440 

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

16 

17 POINTS_PER_INCH: Final[int] = 72 

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

19 

20 LINE_SPACING_FACTOR: Final[int] = 240 

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

22 

23 # === Default Dimensions === 

24 DEFAULT_BORDER_WIDTH: Final[int] = 15 

25 """Default border width in twips.""" 

26 

27 DEFAULT_CELL_HEIGHT: Final[float] = 0.15 

28 """Default cell height in inches.""" 

29 

30 DEFAULT_SPACE_BEFORE: Final[int] = 15 

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

32 

33 DEFAULT_SPACE_AFTER: Final[int] = 15 

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

35 

36 # === Font Sizes === 

37 DEFAULT_FONT_SIZE: Final[float] = 9 

38 """Default font size in points.""" 

39 

40 # === RTF Control Codes === 

41 class Control: 

42 """RTF control word constants.""" 

43 

44 # Text formatting 

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

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

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

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

49 

50 # Document structure 

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

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

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

54 

55 # Page formatting 

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

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

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

59 

60 # Paragraph formatting 

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

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

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

64 

65 # === Format Codes === 

66 FORMAT_CODES: Final[Dict[str, str]] = { 

67 "": "", 

68 "b": "\\b", # Bold 

69 "i": "\\i", # Italic 

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

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

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

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

74 } 

75 

76 # === Text Justification Codes === 

77 TEXT_JUSTIFICATION_CODES: Final[Dict[str, str]] = { 

78 "": "", 

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

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

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

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

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

84 } 

85 

86 # === Row Justification Codes === 

87 ROW_JUSTIFICATION_CODES: Final[Dict[str, str]] = { 

88 "": "", 

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

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

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

92 } 

93 

94 # === Border Style Codes === 

95 BORDER_CODES: Final[Dict[str, str]] = { 

96 "single": "\\brdrs", 

97 "double": "\\brdrdb", 

98 "thick": "\\brdrth", 

99 "dotted": "\\brdrdot", 

100 "dashed": "\\brdrdash", 

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

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

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

104 "triple": "\\brdrtriple", 

105 "wavy": "\\brdrwavy", 

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

107 "striped": "\\brdrengrave", 

108 "embossed": "\\brdremboss", 

109 "engraved": "\\brdrengrave", 

110 "frame": "\\brdrframe", 

111 "": "", # No border 

112 } 

113 

114 # === Vertical Alignment Codes === 

115 VERTICAL_ALIGNMENT_CODES: Final[Dict[str, str]] = { 

116 "top": "\\clvertalt", 

117 "center": "\\clvertalc", 

118 "bottom": "\\clvertalb", 

119 "merge_first": "\\clvertalc\\clvmgf", 

120 "merge_rest": "\\clvertalc\\clvmrg", 

121 "": "", 

122 } 

123 

124 # === Character Conversion Mapping === 

125 RTF_CHAR_MAPPING: Final[Dict[str, str]] = { 

126 "^": "\\super ", 

127 "_": "\\sub ", 

128 ">=": "\\geq ", 

129 "<=": "\\leq ", 

130 "\n": "\\line ", 

131 "\\pagenumber": "\\chpgn ", 

132 "\\totalpage": "\\totalpage ", 

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

134 } 

135 

136 

137class RTFDefaults: 

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

139 

140 # === Page Settings === 

141 ORIENTATION: Final[str] = "portrait" 

142 BORDER_FIRST: Final[str] = "double" 

143 BORDER_LAST: Final[str] = "double" 

144 USE_COLOR: Final[bool] = False 

145 

146 # === Text Settings === 

147 TEXT_FONT: Final[int] = 1 

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

149 TEXT_HYPHENATION: Final[bool] = True 

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

151 

152 # === Table Settings === 

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

154 

155 # === Color Defaults === 

156 @classmethod 

157 def get_default_colors(cls) -> Dict[str, str]: 

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

159 from rtflite.dictionary.color_table import name_to_rtf 

160 

161 return name_to_rtf 

162 

163 # Provide DEFAULT_COLORS as a cached property for backward compatibility 

164 _default_colors_cache = None 

165 

166 @classmethod 

167 def DEFAULT_COLORS(cls) -> Dict[str, str]: 

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

169 if cls._default_colors_cache is None: 

170 cls._default_colors_cache = cls.get_default_colors() 

171 return cls._default_colors_cache 

172 

173 

174class RTFMeasurements: 

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

176 

177 @staticmethod 

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

179 """Convert inches to twips. 

180 

181 Args: 

182 inches: Length in inches 

183 

184 Returns: 

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

186 """ 

187 return round(inches * RTFConstants.TWIPS_PER_INCH) 

188 

189 @staticmethod 

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

191 """Convert twips to inches. 

192 

193 Args: 

194 twips: Length in twips 

195 

196 Returns: 

197 Length in inches 

198 """ 

199 return twips / RTFConstants.TWIPS_PER_INCH 

200 

201 @staticmethod 

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

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

204 

205 Args: 

206 points: Font size in points 

207 

208 Returns: 

209 Font size in half-points (RTF format) 

210 """ 

211 return int(points * 2)