pytex-preprocessor 0.4.0__tar.gz → 0.4.1__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (132) hide show
  1. {pytex_preprocessor-0.4.0 → pytex_preprocessor-0.4.1}/PKG-INFO +1 -1
  2. {pytex_preprocessor-0.4.0 → pytex_preprocessor-0.4.1}/pyproject.toml +1 -1
  3. {pytex_preprocessor-0.4.0 → pytex_preprocessor-0.4.1}/src/pytex/model/math.py +38 -3
  4. {pytex_preprocessor-0.4.0 → pytex_preprocessor-0.4.1}/src/pytex_analyze/optimize.py +21 -3
  5. {pytex_preprocessor-0.4.0 → pytex_preprocessor-0.4.1}/src/pytex_preprocessor.egg-info/PKG-INFO +1 -1
  6. {pytex_preprocessor-0.4.0 → pytex_preprocessor-0.4.1}/README.md +0 -0
  7. {pytex_preprocessor-0.4.0 → pytex_preprocessor-0.4.1}/setup.cfg +0 -0
  8. {pytex_preprocessor-0.4.0 → pytex_preprocessor-0.4.1}/src/pytex/__init__.py +0 -0
  9. {pytex_preprocessor-0.4.0 → pytex_preprocessor-0.4.1}/src/pytex/commands/__init__.py +0 -0
  10. {pytex_preprocessor-0.4.0 → pytex_preprocessor-0.4.1}/src/pytex/commands/biblatex.py +0 -0
  11. {pytex_preprocessor-0.4.0 → pytex_preprocessor-0.4.1}/src/pytex/commands/builtin.py +0 -0
  12. {pytex_preprocessor-0.4.0 → pytex_preprocessor-0.4.1}/src/pytex/commands/captions.py +0 -0
  13. {pytex_preprocessor-0.4.0 → pytex_preprocessor-0.4.1}/src/pytex/commands/cleveref.py +0 -0
  14. {pytex_preprocessor-0.4.0 → pytex_preprocessor-0.4.1}/src/pytex/commands/colors.py +0 -0
  15. {pytex_preprocessor-0.4.0 → pytex_preprocessor-0.4.1}/src/pytex/commands/conditionals.py +0 -0
  16. {pytex_preprocessor-0.4.0 → pytex_preprocessor-0.4.1}/src/pytex/commands/counters.py +0 -0
  17. {pytex_preprocessor-0.4.0 → pytex_preprocessor-0.4.1}/src/pytex/commands/definitions.py +0 -0
  18. {pytex_preprocessor-0.4.0 → pytex_preprocessor-0.4.1}/src/pytex/commands/floats.py +0 -0
  19. {pytex_preprocessor-0.4.0 → pytex_preprocessor-0.4.1}/src/pytex/commands/font.py +0 -0
  20. {pytex_preprocessor-0.4.0 → pytex_preprocessor-0.4.1}/src/pytex/commands/fontawesome.py +0 -0
  21. {pytex_preprocessor-0.4.0 → pytex_preprocessor-0.4.1}/src/pytex/commands/fontspec.py +0 -0
  22. {pytex_preprocessor-0.4.0 → pytex_preprocessor-0.4.1}/src/pytex/commands/geometry.py +0 -0
  23. {pytex_preprocessor-0.4.0 → pytex_preprocessor-0.4.1}/src/pytex/commands/glossaries.py +0 -0
  24. {pytex_preprocessor-0.4.0 → pytex_preprocessor-0.4.1}/src/pytex/commands/graphics.py +0 -0
  25. {pytex_preprocessor-0.4.0 → pytex_preprocessor-0.4.1}/src/pytex/commands/hooks.py +0 -0
  26. {pytex_preprocessor-0.4.0 → pytex_preprocessor-0.4.1}/src/pytex/commands/hyperref.py +0 -0
  27. {pytex_preprocessor-0.4.0 → pytex_preprocessor-0.4.1}/src/pytex/commands/lengths.py +0 -0
  28. {pytex_preprocessor-0.4.0 → pytex_preprocessor-0.4.1}/src/pytex/commands/listings.py +0 -0
  29. {pytex_preprocessor-0.4.0 → pytex_preprocessor-0.4.1}/src/pytex/commands/mdframed.py +0 -0
  30. {pytex_preprocessor-0.4.0 → pytex_preprocessor-0.4.1}/src/pytex/commands/picture.py +0 -0
  31. {pytex_preprocessor-0.4.0 → pytex_preprocessor-0.4.1}/src/pytex/commands/setspace.py +0 -0
  32. {pytex_preprocessor-0.4.0 → pytex_preprocessor-0.4.1}/src/pytex/commands/tables.py +0 -0
  33. {pytex_preprocessor-0.4.0 → pytex_preprocessor-0.4.1}/src/pytex/helpers/__init__.py +0 -0
  34. {pytex_preprocessor-0.4.0 → pytex_preprocessor-0.4.1}/src/pytex/helpers/coerce.py +0 -0
  35. {pytex_preprocessor-0.4.0 → pytex_preprocessor-0.4.1}/src/pytex/helpers/parenting.py +0 -0
  36. {pytex_preprocessor-0.4.0 → pytex_preprocessor-0.4.1}/src/pytex/helpers/sanitize.py +0 -0
  37. {pytex_preprocessor-0.4.0 → pytex_preprocessor-0.4.1}/src/pytex/helpers/with_package.py +0 -0
  38. {pytex_preprocessor-0.4.0 → pytex_preprocessor-0.4.1}/src/pytex/interface/__init__.py +0 -0
  39. {pytex_preprocessor-0.4.0 → pytex_preprocessor-0.4.1}/src/pytex/interface/control_sequence.py +0 -0
  40. {pytex_preprocessor-0.4.0 → pytex_preprocessor-0.4.1}/src/pytex/interface/package.py +0 -0
  41. {pytex_preprocessor-0.4.0 → pytex_preprocessor-0.4.1}/src/pytex/interface/tex.py +0 -0
  42. {pytex_preprocessor-0.4.0 → pytex_preprocessor-0.4.1}/src/pytex/model/__init__.py +0 -0
  43. {pytex_preprocessor-0.4.0 → pytex_preprocessor-0.4.1}/src/pytex/model/color.py +0 -0
  44. {pytex_preprocessor-0.4.0 → pytex_preprocessor-0.4.1}/src/pytex/model/comment.py +0 -0
  45. {pytex_preprocessor-0.4.0 → pytex_preprocessor-0.4.1}/src/pytex/model/concat.py +0 -0
  46. {pytex_preprocessor-0.4.0 → pytex_preprocessor-0.4.1}/src/pytex/model/control_sequence.py +0 -0
  47. {pytex_preprocessor-0.4.0 → pytex_preprocessor-0.4.1}/src/pytex/model/document.py +0 -0
  48. {pytex_preprocessor-0.4.0 → pytex_preprocessor-0.4.1}/src/pytex/model/document_class.py +0 -0
  49. {pytex_preprocessor-0.4.0 → pytex_preprocessor-0.4.1}/src/pytex/model/empty.py +0 -0
  50. {pytex_preprocessor-0.4.0 → pytex_preprocessor-0.4.1}/src/pytex/model/environment.py +0 -0
  51. {pytex_preprocessor-0.4.0 → pytex_preprocessor-0.4.1}/src/pytex/model/image.py +0 -0
  52. {pytex_preprocessor-0.4.0 → pytex_preprocessor-0.4.1}/src/pytex/model/include.py +0 -0
  53. {pytex_preprocessor-0.4.0 → pytex_preprocessor-0.4.1}/src/pytex/model/length.py +0 -0
  54. {pytex_preprocessor-0.4.0 → pytex_preprocessor-0.4.1}/src/pytex/model/package.py +0 -0
  55. {pytex_preprocessor-0.4.0 → pytex_preprocessor-0.4.1}/src/pytex/model/raw.py +0 -0
  56. {pytex_preprocessor-0.4.0 → pytex_preprocessor-0.4.1}/src/pytex/packages.py +0 -0
  57. {pytex_preprocessor-0.4.0 → pytex_preprocessor-0.4.1}/src/pytex/registry.py +0 -0
  58. {pytex_preprocessor-0.4.0 → pytex_preprocessor-0.4.1}/src/pytex/template.py +0 -0
  59. {pytex_preprocessor-0.4.0 → pytex_preprocessor-0.4.1}/src/pytex_analyze/__init__.py +0 -0
  60. {pytex_preprocessor-0.4.0 → pytex_preprocessor-0.4.1}/src/pytex_analyze/analyze.py +0 -0
  61. {pytex_preprocessor-0.4.0 → pytex_preprocessor-0.4.1}/src/pytex_builder/__init__.py +0 -0
  62. {pytex_preprocessor-0.4.0 → pytex_preprocessor-0.4.1}/src/pytex_builder/build.py +0 -0
  63. {pytex_preprocessor-0.4.0 → pytex_preprocessor-0.4.1}/src/pytex_builder/console.py +0 -0
  64. {pytex_preprocessor-0.4.0 → pytex_preprocessor-0.4.1}/src/pytex_builder/render.py +0 -0
  65. {pytex_preprocessor-0.4.0 → pytex_preprocessor-0.4.1}/src/pytex_builder/tectonic.py +0 -0
  66. {pytex_preprocessor-0.4.0 → pytex_preprocessor-0.4.1}/src/pytex_builder/tex2py.py +0 -0
  67. {pytex_preprocessor-0.4.0 → pytex_preprocessor-0.4.1}/src/pytex_builder/tree.py +0 -0
  68. {pytex_preprocessor-0.4.0 → pytex_preprocessor-0.4.1}/src/pytex_builder/variants.py +0 -0
  69. {pytex_preprocessor-0.4.0 → pytex_preprocessor-0.4.1}/src/pytex_hsrtreport/__init__.py +0 -0
  70. {pytex_preprocessor-0.4.0 → pytex_preprocessor-0.4.1}/src/pytex_hsrtreport/assets/fonts/Blender/Blender-Bold.ttf +0 -0
  71. {pytex_preprocessor-0.4.0 → pytex_preprocessor-0.4.1}/src/pytex_hsrtreport/assets/fonts/Blender/Blender-BoldItalic.ttf +0 -0
  72. {pytex_preprocessor-0.4.0 → pytex_preprocessor-0.4.1}/src/pytex_hsrtreport/assets/fonts/Blender/Blender-Book.ttf +0 -0
  73. {pytex_preprocessor-0.4.0 → pytex_preprocessor-0.4.1}/src/pytex_hsrtreport/assets/fonts/Blender/Blender-BookItalic.ttf +0 -0
  74. {pytex_preprocessor-0.4.0 → pytex_preprocessor-0.4.1}/src/pytex_hsrtreport/assets/fonts/Blender/Blender-Medium.ttf +0 -0
  75. {pytex_preprocessor-0.4.0 → pytex_preprocessor-0.4.1}/src/pytex_hsrtreport/assets/fonts/Blender/Blender-MediumItalic.ttf +0 -0
  76. {pytex_preprocessor-0.4.0 → pytex_preprocessor-0.4.1}/src/pytex_hsrtreport/assets/fonts/Blender/Blender-Strong.ttf +0 -0
  77. {pytex_preprocessor-0.4.0 → pytex_preprocessor-0.4.1}/src/pytex_hsrtreport/assets/fonts/Blender/Blender-Thin.ttf +0 -0
  78. {pytex_preprocessor-0.4.0 → pytex_preprocessor-0.4.1}/src/pytex_hsrtreport/assets/fonts/Blender/Blender-ThinItalic.ttf +0 -0
  79. {pytex_preprocessor-0.4.0 → pytex_preprocessor-0.4.1}/src/pytex_hsrtreport/assets/fonts/DIN/DIN-Black.ttf +0 -0
  80. {pytex_preprocessor-0.4.0 → pytex_preprocessor-0.4.1}/src/pytex_hsrtreport/assets/fonts/DIN/DIN-Bold.ttf +0 -0
  81. {pytex_preprocessor-0.4.0 → pytex_preprocessor-0.4.1}/src/pytex_hsrtreport/assets/fonts/DIN/DIN-BoldItalic.ttf +0 -0
  82. {pytex_preprocessor-0.4.0 → pytex_preprocessor-0.4.1}/src/pytex_hsrtreport/assets/fonts/DIN/DIN-Italic.ttf +0 -0
  83. {pytex_preprocessor-0.4.0 → pytex_preprocessor-0.4.1}/src/pytex_hsrtreport/assets/fonts/DIN/DIN-Medium.ttf +0 -0
  84. {pytex_preprocessor-0.4.0 → pytex_preprocessor-0.4.1}/src/pytex_hsrtreport/assets/fonts/DIN/DIN-Regular.ttf +0 -0
  85. {pytex_preprocessor-0.4.0 → pytex_preprocessor-0.4.1}/src/pytex_hsrtreport/assets/fonts/Times New Roman.ttf +0 -0
  86. {pytex_preprocessor-0.4.0 → pytex_preprocessor-0.4.1}/src/pytex_hsrtreport/assets/logos/ASTA.svg +0 -0
  87. {pytex_preprocessor-0.4.0 → pytex_preprocessor-0.4.1}/src/pytex_hsrtreport/assets/logos/DUMMY.png +0 -0
  88. {pytex_preprocessor-0.4.0 → pytex_preprocessor-0.4.1}/src/pytex_hsrtreport/assets/logos/DUMMY_FOOT.png +0 -0
  89. {pytex_preprocessor-0.4.0 → pytex_preprocessor-0.4.1}/src/pytex_hsrtreport/assets/logos/ECHO.svg +0 -0
  90. {pytex_preprocessor-0.4.0 → pytex_preprocessor-0.4.1}/src/pytex_hsrtreport/assets/logos/HSRT.pdf +0 -0
  91. {pytex_preprocessor-0.4.0 → pytex_preprocessor-0.4.1}/src/pytex_hsrtreport/assets/logos/INF.pdf +0 -0
  92. {pytex_preprocessor-0.4.0 → pytex_preprocessor-0.4.1}/src/pytex_hsrtreport/assets/logos/STUPA.pdf +0 -0
  93. {pytex_preprocessor-0.4.0 → pytex_preprocessor-0.4.1}/src/pytex_hsrtreport/assets/logos/Skyline.pdf +0 -0
  94. {pytex_preprocessor-0.4.0 → pytex_preprocessor-0.4.1}/src/pytex_hsrtreport/boxes.py +0 -0
  95. {pytex_preprocessor-0.4.0 → pytex_preprocessor-0.4.1}/src/pytex_hsrtreport/citations.py +0 -0
  96. {pytex_preprocessor-0.4.0 → pytex_preprocessor-0.4.1}/src/pytex_hsrtreport/cleveref_names.py +0 -0
  97. {pytex_preprocessor-0.4.0 → pytex_preprocessor-0.4.1}/src/pytex_hsrtreport/colors.py +0 -0
  98. {pytex_preprocessor-0.4.0 → pytex_preprocessor-0.4.1}/src/pytex_hsrtreport/document.py +0 -0
  99. {pytex_preprocessor-0.4.0 → pytex_preprocessor-0.4.1}/src/pytex_hsrtreport/fonts.py +0 -0
  100. {pytex_preprocessor-0.4.0 → pytex_preprocessor-0.4.1}/src/pytex_hsrtreport/glossary.py +0 -0
  101. {pytex_preprocessor-0.4.0 → pytex_preprocessor-0.4.1}/src/pytex_hsrtreport/hyperref_config.py +0 -0
  102. {pytex_preprocessor-0.4.0 → pytex_preprocessor-0.4.1}/src/pytex_hsrtreport/listings.py +0 -0
  103. {pytex_preprocessor-0.4.0 → pytex_preprocessor-0.4.1}/src/pytex_hsrtreport/logos.py +0 -0
  104. {pytex_preprocessor-0.4.0 → pytex_preprocessor-0.4.1}/src/pytex_hsrtreport/pagebreak.py +0 -0
  105. {pytex_preprocessor-0.4.0 → pytex_preprocessor-0.4.1}/src/pytex_hsrtreport/pagesetup.py +0 -0
  106. {pytex_preprocessor-0.4.0 → pytex_preprocessor-0.4.1}/src/pytex_hsrtreport/tex/pagesetup.tex +0 -0
  107. {pytex_preprocessor-0.4.0 → pytex_preprocessor-0.4.1}/src/pytex_hsrtreport/titlepage.py +0 -0
  108. {pytex_preprocessor-0.4.0 → pytex_preprocessor-0.4.1}/src/pytex_hsrtreport/variants.py +0 -0
  109. {pytex_preprocessor-0.4.0 → pytex_preprocessor-0.4.1}/src/pytex_hsrtreport/voting.py +0 -0
  110. {pytex_preprocessor-0.4.0 → pytex_preprocessor-0.4.1}/src/pytex_hsrtreport/watermark.py +0 -0
  111. {pytex_preprocessor-0.4.0 → pytex_preprocessor-0.4.1}/src/pytex_hsrtreport/wordcount.py +0 -0
  112. {pytex_preprocessor-0.4.0 → pytex_preprocessor-0.4.1}/src/pytex_koma/__init__.py +0 -0
  113. {pytex_preprocessor-0.4.0 → pytex_preprocessor-0.4.1}/src/pytex_koma/commands.py +0 -0
  114. {pytex_preprocessor-0.4.0 → pytex_preprocessor-0.4.1}/src/pytex_koma/document.py +0 -0
  115. {pytex_preprocessor-0.4.0 → pytex_preprocessor-0.4.1}/src/pytex_markdown/__init__.py +0 -0
  116. {pytex_preprocessor-0.4.0 → pytex_preprocessor-0.4.1}/src/pytex_markdown/convert.py +0 -0
  117. {pytex_preprocessor-0.4.0 → pytex_preprocessor-0.4.1}/src/pytex_markdown/escape.py +0 -0
  118. {pytex_preprocessor-0.4.0 → pytex_preprocessor-0.4.1}/src/pytex_preprocessor.egg-info/SOURCES.txt +0 -0
  119. {pytex_preprocessor-0.4.0 → pytex_preprocessor-0.4.1}/src/pytex_preprocessor.egg-info/dependency_links.txt +0 -0
  120. {pytex_preprocessor-0.4.0 → pytex_preprocessor-0.4.1}/src/pytex_preprocessor.egg-info/entry_points.txt +0 -0
  121. {pytex_preprocessor-0.4.0 → pytex_preprocessor-0.4.1}/src/pytex_preprocessor.egg-info/requires.txt +0 -0
  122. {pytex_preprocessor-0.4.0 → pytex_preprocessor-0.4.1}/src/pytex_preprocessor.egg-info/top_level.txt +0 -0
  123. {pytex_preprocessor-0.4.0 → pytex_preprocessor-0.4.1}/src/pytex_protocol/__init__.py +0 -0
  124. {pytex_preprocessor-0.4.0 → pytex_preprocessor-0.4.1}/src/pytex_protocol/convert.py +0 -0
  125. {pytex_preprocessor-0.4.0 → pytex_preprocessor-0.4.1}/src/pytex_protocol/document.py +0 -0
  126. {pytex_preprocessor-0.4.0 → pytex_preprocessor-0.4.1}/src/pytex_protocol/entries.py +0 -0
  127. {pytex_preprocessor-0.4.0 → pytex_preprocessor-0.4.1}/src/pytex_protocol/frontmatter.py +0 -0
  128. {pytex_preprocessor-0.4.0 → pytex_preprocessor-0.4.1}/src/pytex_protocol/header.py +0 -0
  129. {pytex_preprocessor-0.4.0 → pytex_preprocessor-0.4.1}/src/pytex_protocol/shortcodes.py +0 -0
  130. {pytex_preprocessor-0.4.0 → pytex_preprocessor-0.4.1}/src/pytex_protocol/signatures.py +0 -0
  131. {pytex_preprocessor-0.4.0 → pytex_preprocessor-0.4.1}/src/pytex_tikz/__init__.py +0 -0
  132. {pytex_preprocessor-0.4.0 → pytex_preprocessor-0.4.1}/src/pytex_tikz/tikz.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: pytex-preprocessor
3
- Version: 0.4.0
3
+ Version: 0.4.1
4
4
  Summary: Type-safe LaTeX document generation with Python
5
5
  Author-email: Frederik Beimgraben <frederik@beimgraben.net>
6
6
  Requires-Python: >=3.13
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
4
4
 
5
5
  [project]
6
6
  name = "pytex-preprocessor"
7
- version = "0.4.0"
7
+ version = "0.4.1"
8
8
  authors = [
9
9
  { name="Frederik Beimgraben", email="frederik@beimgraben.net" },
10
10
  ]
@@ -67,11 +67,46 @@ __all__ = [
67
67
  ]
68
68
 
69
69
 
70
+ def _restrip(raw: Raw, how: str) -> Raw:
71
+ content = raw.content.lstrip() if how == "l" else raw.content.rstrip()
72
+ return Raw(content, raw.namespace, raw.allow_replacements)
73
+
74
+
75
+ def _is_blank(node: TeX | str) -> bool:
76
+ return isinstance(node, Raw) and node.content.strip() == ""
77
+
78
+
79
+ def _trim(body: TeX | str) -> TeX | str:
80
+ """Strip whitespace just inside the math delimiters.
81
+
82
+ TeX math mode ignores leading/trailing spaces and newlines, so this is
83
+ render-equivalent; it also avoids a blank line (`\\par`) sneaking inside the
84
+ delimiters, which is a hard LaTeX error. Only the outer boundary is touched
85
+ (interior whitespace can be significant, e.g. in `\\text{}` or alignments).
86
+ """
87
+ if isinstance(body, str):
88
+ return body.strip()
89
+ if isinstance(body, Raw):
90
+ return Raw(body.content.strip(), body.namespace, body.allow_replacements)
91
+ if isinstance(body, Concat):
92
+ elements = list(body.elements)
93
+ while elements and _is_blank(elements[0]):
94
+ elements.pop(0)
95
+ while elements and _is_blank(elements[-1]):
96
+ elements.pop()
97
+ if elements and isinstance(elements[0], Raw):
98
+ elements[0] = _restrip(elements[0], "l")
99
+ if elements and isinstance(elements[-1], Raw):
100
+ elements[-1] = _restrip(elements[-1], "r")
101
+ return Concat(*elements)
102
+ return body
103
+
104
+
70
105
  @Registry.add
71
106
  def Math(body: TeX | str) -> TeX:
72
107
  return Concat(
73
108
  ControlSequence("(", ()),
74
- body,
109
+ _trim(body),
75
110
  ControlSequence(")", ()),
76
111
  )
77
112
 
@@ -79,14 +114,14 @@ def Math(body: TeX | str) -> TeX:
79
114
  @Registry.add
80
115
  def InlineMath(body: TeX | str) -> TeX:
81
116
  """Dollar-delimited inline math: ``$body$``."""
82
- return Concat(Raw("$"), body, Raw("$"))
117
+ return Concat(Raw("$"), _trim(body), Raw("$"))
83
118
 
84
119
 
85
120
  @Registry.add
86
121
  def DisplayMath(body: TeX | str) -> TeX:
87
122
  return Concat(
88
123
  ControlSequence("[", ()),
89
- body,
124
+ _trim(body),
90
125
  ControlSequence("]", ()),
91
126
  )
92
127
 
@@ -130,10 +130,24 @@ def _native(raw: Raw) -> TeX:
130
130
  return raw
131
131
 
132
132
 
133
+ _MATH_OPEN = re.compile(r"(\\[\[(])\s+")
134
+ _MATH_CLOSE = re.compile(r"\s+(\\[\])])")
135
+
136
+
137
+ def _strip_math_ws(text: str) -> str:
138
+ r"""Drop whitespace just inside ``\[..\]`` and ``\(..\)``.
139
+
140
+ TeX math mode ignores it, so two strings that differ only there render the
141
+ same — this lets the tokenizer accept a `DisplayMath`/`Math` candidate
142
+ (which trims that whitespace) as equivalent to the original.
143
+ """
144
+ return _MATH_CLOSE.sub(r"\1", _MATH_OPEN.sub(r"\1", text))
145
+
146
+
133
147
  def _tokenize(raw: Raw) -> TeX | None:
134
148
  """Split a `Raw` into literal text and the native nodes for the constructs
135
- in `_TOKEN`. Returns `None` when nothing matches or the result would not
136
- render identically."""
149
+ in `_TOKEN`. Returns `None` when nothing matches or the result would change
150
+ the rendered output (math-delimiter whitespace aside — TeX ignores that)."""
137
151
  content = raw.content
138
152
  namespace = pytex_namespace(raw.namespace or {})
139
153
  parts: list[TeX] = []
@@ -148,7 +162,11 @@ def _tokenize(raw: Raw) -> TeX | None:
148
162
  if cursor < len(content):
149
163
  parts.append(Raw(content[cursor:], allow_replacements=False))
150
164
  candidate = Concat(*(_optimize(part) for part in parts))
151
- return candidate if candidate.rendered == raw.rendered else None
165
+ rendered = candidate.rendered
166
+ target = raw.rendered
167
+ if rendered == target or _strip_math_ws(rendered) == _strip_math_ws(target):
168
+ return candidate
169
+ return None
152
170
 
153
171
 
154
172
  def _token_node(
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: pytex-preprocessor
3
- Version: 0.4.0
3
+ Version: 0.4.1
4
4
  Summary: Type-safe LaTeX document generation with Python
5
5
  Author-email: Frederik Beimgraben <frederik@beimgraben.net>
6
6
  Requires-Python: >=3.13