pdflinkcheck 1.1.73__py3-none-any.whl → 1.2.29__py3-none-any.whl

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 (186) hide show
  1. pdflinkcheck/__init__.py +88 -21
  2. pdflinkcheck/__main__.py +6 -0
  3. pdflinkcheck/analysis_pdfium.py +131 -0
  4. pdflinkcheck/{analyze_pymupdf.py → analysis_pymupdf.py} +109 -145
  5. pdflinkcheck/{analyze_pypdf.py → analysis_pypdf.py} +67 -37
  6. pdflinkcheck/cli.py +111 -116
  7. pdflinkcheck/data/I Have Questions.md +51 -0
  8. pdflinkcheck/data/LICENSE +20 -654
  9. pdflinkcheck/data/README.md +65 -67
  10. pdflinkcheck/data/icons/BoxArt-1080x1080.png +0 -0
  11. pdflinkcheck/data/icons/Logo-150x150.png +0 -0
  12. pdflinkcheck/data/icons/Logo-300x300.png +0 -0
  13. pdflinkcheck/data/icons/Logo-71x71.png +0 -0
  14. pdflinkcheck/data/icons/PosterArt-720x1080.png +0 -0
  15. pdflinkcheck/data/icons/SmallLogo-44x44.png +0 -0
  16. pdflinkcheck/data/icons/SplashScreen-620x300.png +0 -0
  17. pdflinkcheck/data/icons/StoreLogo-50x50.png +0 -0
  18. pdflinkcheck/data/icons/WideLogo-310x150.png +0 -0
  19. pdflinkcheck/data/icons/red_pdf_512px.ico +0 -0
  20. pdflinkcheck/data/pyproject.toml +25 -37
  21. pdflinkcheck/data/themes/forest/forest-dark/border-accent-hover.png +0 -0
  22. pdflinkcheck/data/themes/forest/forest-dark/border-accent.png +0 -0
  23. pdflinkcheck/data/themes/forest/forest-dark/border-basic.png +0 -0
  24. pdflinkcheck/data/themes/forest/forest-dark/border-hover.png +0 -0
  25. pdflinkcheck/data/themes/forest/forest-dark/border-invalid.png +0 -0
  26. pdflinkcheck/data/themes/forest/forest-dark/card.png +0 -0
  27. pdflinkcheck/data/themes/forest/forest-dark/check-accent.png +0 -0
  28. pdflinkcheck/data/themes/forest/forest-dark/check-basic.png +0 -0
  29. pdflinkcheck/data/themes/forest/forest-dark/check-hover.png +0 -0
  30. pdflinkcheck/data/themes/forest/forest-dark/check-tri-accent.png +0 -0
  31. pdflinkcheck/data/themes/forest/forest-dark/check-tri-basic.png +0 -0
  32. pdflinkcheck/data/themes/forest/forest-dark/check-tri-hover.png +0 -0
  33. pdflinkcheck/data/themes/forest/forest-dark/check-unsel-accent.png +0 -0
  34. pdflinkcheck/data/themes/forest/forest-dark/check-unsel-basic.png +0 -0
  35. pdflinkcheck/data/themes/forest/forest-dark/check-unsel-hover.png +0 -0
  36. pdflinkcheck/data/themes/forest/forest-dark/check-unsel-pressed.png +0 -0
  37. pdflinkcheck/data/themes/forest/forest-dark/combo-button-basic.png +0 -0
  38. pdflinkcheck/data/themes/forest/forest-dark/combo-button-focus.png +0 -0
  39. pdflinkcheck/data/themes/forest/forest-dark/combo-button-hover.png +0 -0
  40. pdflinkcheck/data/themes/forest/forest-dark/down.png +0 -0
  41. pdflinkcheck/data/themes/forest/forest-dark/empty.png +0 -0
  42. pdflinkcheck/data/themes/forest/forest-dark/hor-accent.png +0 -0
  43. pdflinkcheck/data/themes/forest/forest-dark/hor-basic.png +0 -0
  44. pdflinkcheck/data/themes/forest/forest-dark/hor-hover.png +0 -0
  45. pdflinkcheck/data/themes/forest/forest-dark/notebook.png +0 -0
  46. pdflinkcheck/data/themes/forest/forest-dark/off-accent.png +0 -0
  47. pdflinkcheck/data/themes/forest/forest-dark/off-basic.png +0 -0
  48. pdflinkcheck/data/themes/forest/forest-dark/off-hover.png +0 -0
  49. pdflinkcheck/data/themes/forest/forest-dark/on-accent.png +0 -0
  50. pdflinkcheck/data/themes/forest/forest-dark/on-basic.png +0 -0
  51. pdflinkcheck/data/themes/forest/forest-dark/on-hover.png +0 -0
  52. pdflinkcheck/data/themes/forest/forest-dark/radio-accent.png +0 -0
  53. pdflinkcheck/data/themes/forest/forest-dark/radio-basic.png +0 -0
  54. pdflinkcheck/data/themes/forest/forest-dark/radio-hover.png +0 -0
  55. pdflinkcheck/data/themes/forest/forest-dark/radio-tri-accent.png +0 -0
  56. pdflinkcheck/data/themes/forest/forest-dark/radio-tri-basic.png +0 -0
  57. pdflinkcheck/data/themes/forest/forest-dark/radio-tri-hover.png +0 -0
  58. pdflinkcheck/data/themes/forest/forest-dark/radio-unsel-accent.png +0 -0
  59. pdflinkcheck/data/themes/forest/forest-dark/radio-unsel-basic.png +0 -0
  60. pdflinkcheck/data/themes/forest/forest-dark/radio-unsel-hover.png +0 -0
  61. pdflinkcheck/data/themes/forest/forest-dark/radio-unsel-pressed.png +0 -0
  62. pdflinkcheck/data/themes/forest/forest-dark/rect-accent-hover.png +0 -0
  63. pdflinkcheck/data/themes/forest/forest-dark/rect-accent.png +0 -0
  64. pdflinkcheck/data/themes/forest/forest-dark/rect-basic.png +0 -0
  65. pdflinkcheck/data/themes/forest/forest-dark/rect-hover.png +0 -0
  66. pdflinkcheck/data/themes/forest/forest-dark/right.png +0 -0
  67. pdflinkcheck/data/themes/forest/forest-dark/scale-hor.png +0 -0
  68. pdflinkcheck/data/themes/forest/forest-dark/scale-vert.png +0 -0
  69. pdflinkcheck/data/themes/forest/forest-dark/separator.png +0 -0
  70. pdflinkcheck/data/themes/forest/forest-dark/sizegrip.png +0 -0
  71. pdflinkcheck/data/themes/forest/forest-dark/spin-button-down-basic.png +0 -0
  72. pdflinkcheck/data/themes/forest/forest-dark/spin-button-down-focus.png +0 -0
  73. pdflinkcheck/data/themes/forest/forest-dark/spin-button-up.png +0 -0
  74. pdflinkcheck/data/themes/forest/forest-dark/tab-accent.png +0 -0
  75. pdflinkcheck/data/themes/forest/forest-dark/tab-basic.png +0 -0
  76. pdflinkcheck/data/themes/forest/forest-dark/tab-hover.png +0 -0
  77. pdflinkcheck/data/themes/forest/forest-dark/thumb-hor-accent.png +0 -0
  78. pdflinkcheck/data/themes/forest/forest-dark/thumb-hor-basic.png +0 -0
  79. pdflinkcheck/data/themes/forest/forest-dark/thumb-hor-hover.png +0 -0
  80. pdflinkcheck/data/themes/forest/forest-dark/thumb-vert-accent.png +0 -0
  81. pdflinkcheck/data/themes/forest/forest-dark/thumb-vert-basic.png +0 -0
  82. pdflinkcheck/data/themes/forest/forest-dark/thumb-vert-hover.png +0 -0
  83. pdflinkcheck/data/themes/forest/forest-dark/tree-basic.png +0 -0
  84. pdflinkcheck/data/themes/forest/forest-dark/tree-pressed.png +0 -0
  85. pdflinkcheck/data/themes/forest/forest-dark/up.png +0 -0
  86. pdflinkcheck/data/themes/forest/forest-dark/vert-accent.png +0 -0
  87. pdflinkcheck/data/themes/forest/forest-dark/vert-basic.png +0 -0
  88. pdflinkcheck/data/themes/forest/forest-dark/vert-hover.png +0 -0
  89. pdflinkcheck/data/themes/forest/forest-dark.tcl +536 -0
  90. pdflinkcheck/data/themes/forest/forest-light/border-accent-hover.png +0 -0
  91. pdflinkcheck/data/themes/forest/forest-light/border-accent.png +0 -0
  92. pdflinkcheck/data/themes/forest/forest-light/border-basic.png +0 -0
  93. pdflinkcheck/data/themes/forest/forest-light/border-hover.png +0 -0
  94. pdflinkcheck/data/themes/forest/forest-light/border-invalid.png +0 -0
  95. pdflinkcheck/data/themes/forest/forest-light/card.png +0 -0
  96. pdflinkcheck/data/themes/forest/forest-light/check-accent.png +0 -0
  97. pdflinkcheck/data/themes/forest/forest-light/check-basic.png +0 -0
  98. pdflinkcheck/data/themes/forest/forest-light/check-hover.png +0 -0
  99. pdflinkcheck/data/themes/forest/forest-light/check-tri-accent.png +0 -0
  100. pdflinkcheck/data/themes/forest/forest-light/check-tri-basic.png +0 -0
  101. pdflinkcheck/data/themes/forest/forest-light/check-tri-hover.png +0 -0
  102. pdflinkcheck/data/themes/forest/forest-light/check-unsel-accent.png +0 -0
  103. pdflinkcheck/data/themes/forest/forest-light/check-unsel-basic.png +0 -0
  104. pdflinkcheck/data/themes/forest/forest-light/check-unsel-hover.png +0 -0
  105. pdflinkcheck/data/themes/forest/forest-light/check-unsel-pressed.png +0 -0
  106. pdflinkcheck/data/themes/forest/forest-light/combo-button-basic.png +0 -0
  107. pdflinkcheck/data/themes/forest/forest-light/combo-button-focus.png +0 -0
  108. pdflinkcheck/data/themes/forest/forest-light/combo-button-hover.png +0 -0
  109. pdflinkcheck/data/themes/forest/forest-light/down-focus.png +0 -0
  110. pdflinkcheck/data/themes/forest/forest-light/down.png +0 -0
  111. pdflinkcheck/data/themes/forest/forest-light/empty.png +0 -0
  112. pdflinkcheck/data/themes/forest/forest-light/hor-accent.png +0 -0
  113. pdflinkcheck/data/themes/forest/forest-light/hor-basic.png +0 -0
  114. pdflinkcheck/data/themes/forest/forest-light/hor-hover.png +0 -0
  115. pdflinkcheck/data/themes/forest/forest-light/notebook.png +0 -0
  116. pdflinkcheck/data/themes/forest/forest-light/off-accent.png +0 -0
  117. pdflinkcheck/data/themes/forest/forest-light/off-basic.png +0 -0
  118. pdflinkcheck/data/themes/forest/forest-light/off-hover.png +0 -0
  119. pdflinkcheck/data/themes/forest/forest-light/on-accent.png +0 -0
  120. pdflinkcheck/data/themes/forest/forest-light/on-basic.png +0 -0
  121. pdflinkcheck/data/themes/forest/forest-light/on-hover.png +0 -0
  122. pdflinkcheck/data/themes/forest/forest-light/radio-accent.png +0 -0
  123. pdflinkcheck/data/themes/forest/forest-light/radio-basic.png +0 -0
  124. pdflinkcheck/data/themes/forest/forest-light/radio-hover.png +0 -0
  125. pdflinkcheck/data/themes/forest/forest-light/radio-tri-accent.png +0 -0
  126. pdflinkcheck/data/themes/forest/forest-light/radio-tri-basic.png +0 -0
  127. pdflinkcheck/data/themes/forest/forest-light/radio-tri-hover.png +0 -0
  128. pdflinkcheck/data/themes/forest/forest-light/radio-unsel-accent.png +0 -0
  129. pdflinkcheck/data/themes/forest/forest-light/radio-unsel-basic.png +0 -0
  130. pdflinkcheck/data/themes/forest/forest-light/radio-unsel-hover.png +0 -0
  131. pdflinkcheck/data/themes/forest/forest-light/radio-unsel-pressed.png +0 -0
  132. pdflinkcheck/data/themes/forest/forest-light/rect-accent-hover.png +0 -0
  133. pdflinkcheck/data/themes/forest/forest-light/rect-accent.png +0 -0
  134. pdflinkcheck/data/themes/forest/forest-light/rect-basic.png +0 -0
  135. pdflinkcheck/data/themes/forest/forest-light/rect-hover.png +0 -0
  136. pdflinkcheck/data/themes/forest/forest-light/right-focus.png +0 -0
  137. pdflinkcheck/data/themes/forest/forest-light/right.png +0 -0
  138. pdflinkcheck/data/themes/forest/forest-light/scale-hor.png +0 -0
  139. pdflinkcheck/data/themes/forest/forest-light/scale-vert.png +0 -0
  140. pdflinkcheck/data/themes/forest/forest-light/separator.png +0 -0
  141. pdflinkcheck/data/themes/forest/forest-light/sizegrip.png +0 -0
  142. pdflinkcheck/data/themes/forest/forest-light/spin-button-down-basic.png +0 -0
  143. pdflinkcheck/data/themes/forest/forest-light/spin-button-down-focus.png +0 -0
  144. pdflinkcheck/data/themes/forest/forest-light/spin-button-up.png +0 -0
  145. pdflinkcheck/data/themes/forest/forest-light/tab-accent.png +0 -0
  146. pdflinkcheck/data/themes/forest/forest-light/tab-basic.png +0 -0
  147. pdflinkcheck/data/themes/forest/forest-light/tab-hover.png +0 -0
  148. pdflinkcheck/data/themes/forest/forest-light/thumb-hor-accent.png +0 -0
  149. pdflinkcheck/data/themes/forest/forest-light/thumb-hor-basic.png +0 -0
  150. pdflinkcheck/data/themes/forest/forest-light/thumb-hor-hover.png +0 -0
  151. pdflinkcheck/data/themes/forest/forest-light/thumb-vert-accent.png +0 -0
  152. pdflinkcheck/data/themes/forest/forest-light/thumb-vert-basic.png +0 -0
  153. pdflinkcheck/data/themes/forest/forest-light/thumb-vert-hover.png +0 -0
  154. pdflinkcheck/data/themes/forest/forest-light/tree-basic.png +0 -0
  155. pdflinkcheck/data/themes/forest/forest-light/tree-pressed.png +0 -0
  156. pdflinkcheck/data/themes/forest/forest-light/up.png +0 -0
  157. pdflinkcheck/data/themes/forest/forest-light/vert-accent.png +0 -0
  158. pdflinkcheck/data/themes/forest/forest-light/vert-basic.png +0 -0
  159. pdflinkcheck/data/themes/forest/forest-light/vert-hover.png +0 -0
  160. pdflinkcheck/data/themes/forest/forest-light.tcl +544 -0
  161. pdflinkcheck/datacopy.py +18 -1
  162. pdflinkcheck/dev.py +12 -25
  163. pdflinkcheck/environment.py +76 -0
  164. pdflinkcheck/gui.py +366 -457
  165. pdflinkcheck/helpers.py +88 -0
  166. pdflinkcheck/io.py +27 -23
  167. pdflinkcheck/report.py +692 -121
  168. pdflinkcheck/security.py +189 -0
  169. pdflinkcheck/splash.py +38 -0
  170. pdflinkcheck/stdlib_server.py +14 -20
  171. pdflinkcheck/stdlib_server_alt.py +571 -0
  172. pdflinkcheck/tk_utils.py +188 -0
  173. pdflinkcheck/update_msix_version.py +49 -0
  174. pdflinkcheck/validate.py +129 -218
  175. pdflinkcheck/version_info.py +6 -3
  176. {pdflinkcheck-1.1.73.dist-info → pdflinkcheck-1.2.29.dist-info}/METADATA +84 -81
  177. pdflinkcheck-1.2.29.dist-info/RECORD +183 -0
  178. pdflinkcheck-1.2.29.dist-info/WHEEL +5 -0
  179. {pdflinkcheck-1.1.73.dist-info → pdflinkcheck-1.2.29.dist-info}/entry_points.txt +0 -1
  180. pdflinkcheck-1.2.29.dist-info/licenses/LICENSE +27 -0
  181. pdflinkcheck-1.2.29.dist-info/licenses/LICENSE-MIT +9 -0
  182. pdflinkcheck-1.2.29.dist-info/top_level.txt +1 -0
  183. pdflinkcheck/analyze_pypdf_v2.py +0 -218
  184. pdflinkcheck-1.1.73.dist-info/RECORD +0 -21
  185. pdflinkcheck-1.1.73.dist-info/WHEEL +0 -4
  186. /pdflinkcheck-1.1.73.dist-info/licenses/LICENSE → /pdflinkcheck-1.2.29.dist-info/licenses/LICENSE-AGPL3 +0 -0
@@ -0,0 +1,188 @@
1
+ # src/pdflinkcheck/tk_utils.py
2
+ import tkinter as tk
3
+ import subprocess
4
+ import re
5
+ import platform
6
+
7
+
8
+
9
+ def get_primary_monitor_geometry():
10
+ """
11
+ Queries xrandr to find the actual primary monitor's dimensions and offsets.
12
+ Returns (width, height, x_offset, y_offset) or None.
13
+
14
+ Not used.
15
+ """
16
+ try:
17
+ # Query xrandr for the primary monitor
18
+ result = subprocess.run(['xrandr', '--query'], capture_output=True, text=True, check=True)
19
+ # Look for a line like: "DP-0 connected primary 1920x1080+1200+0"
20
+ match = re.search(r'(\d+)x(\d+)\+(\d+)\+(\d+)', re.search(r'^.*primary.*$', result.stdout, re.M).group())
21
+ if match:
22
+ return map(int, match.groups())
23
+ except Exception:
24
+ return None
25
+
26
+ def center_window_on_primary_stable(window: tk.Toplevel | tk.Tk, width: int, height: int):
27
+ """
28
+ Docstring for center_window_on_primary_stable
29
+
30
+ :param window: Description
31
+ :type window: tk.Toplevel | tk.Tk
32
+ :param width: Description
33
+ :type width: int
34
+ :param height: Description
35
+ :type height: int
36
+
37
+ Not used.
38
+ """
39
+ window.update_idletasks()
40
+
41
+ # 1. Try to assess via X11/XRandR (Best for WSL2)
42
+ geom = get_primary_monitor_geometry()
43
+
44
+ if geom:
45
+ pw, ph, px, py = geom
46
+ print(f"[DEBUG] XRandR Primary: {pw}x{ph} at +{px}+{py}")
47
+ else:
48
+ # 2. Fallback: Center on Mouse Pointer (Best for Multi-monitor without XRandR)
49
+ # Since we can't find 'Primary', we put it where the user's attention is.
50
+ pw, ph = window.winfo_screenwidth(), window.winfo_screenheight()
51
+ px, py = 0, 0
52
+
53
+ # If it's a giant span, let's just use the pointer as the anchor
54
+ if pw > 2500:
55
+ pointer_x = window.winfo_pointerx()
56
+ pointer_y = window.winfo_pointery()
57
+ # We treat a 1920x1080 box around the pointer as our 'virtual primary'
58
+ px, py = pointer_x - 960, pointer_y - 540
59
+ pw, ph = 1920, 1080
60
+
61
+ # 3. Final Math
62
+ x = px + (pw // 2) - (width // 2)
63
+ y = py + (ph // 2) - (height // 2)
64
+
65
+ # Final clamp to ensure it's not off-screen
66
+ x = max(0, x)
67
+ y = max(0, y)
68
+
69
+ print(f"[DEBUG] Final Positioning: x={x}, y={y}")
70
+ window.geometry(f"{width}x{height}+{int(x)}+{int(y)}")
71
+
72
+
73
+ def get_monitor_geometries():
74
+ """
75
+ Queries xrandr to find all connected monitor dimensions and offsets.
76
+ Returns a list of dicts: [{'w', 'h', 'x', 'y', 'is_primary'}]
77
+ Essential for WSL2/WSLg multi-monitor accuracy.
78
+
79
+ Active.
80
+ """
81
+ monitors = []
82
+ os_name = platform.system()
83
+
84
+ # --- LINUX / WSL2 Logic ---
85
+ if os_name == "Linux":
86
+ try:
87
+ # Run xrandr
88
+ xrandr_result = subprocess.run(['xrandr', '--query'], capture_output=True, text=True, check=True)
89
+ #print(f"xrandr_result = {xrandr_result}")
90
+ # Regex to find: "1920x1080+1920+0" or "1920x1080+0+0"
91
+ # We look for lines that contain 'connected' and a geometry string
92
+ lines = xrandr_result.stdout.splitlines()
93
+ for line in lines:
94
+ if " connected " in line:
95
+ is_primary = "primary" in line
96
+ match = re.search(r'(\d+)x(\d+)\+(\d+)\+(\d+)', line)
97
+ if match:
98
+ w, h, x, y = map(int, match.groups())
99
+ monitors.append({
100
+ 'w': w, 'h': h, 'x': x, 'y': y,
101
+ 'is_primary': is_primary
102
+ })
103
+ except Exception as e:
104
+ print(f"[DEBUG] xrandr query failed: {e}")
105
+
106
+ # --- WINDOWS Native Logic ---
107
+ if os_name == "Windows":
108
+ # On native Windows, we can use ctypes to call GetSystemMetrics
109
+ # or rely on the fact that the Primary monitor is almost always at 0,0
110
+ # and its size is reported by winfo_screenwidth if we don't have multiple monitors
111
+ # (For true multi-monitor on native Windows, win32api is usually needed)
112
+ pass
113
+
114
+ return monitors
115
+
116
+ def center_window_on_primary_goose(window: tk.Toplevel | tk.Tk, width: int, height: int):
117
+ """
118
+ Standardizes window centering by identifying the physical monitor
119
+ bounds and offsets.
120
+
121
+ :param window: Description
122
+ :type window: tk.Toplevel | tk.Tk
123
+ :param width: Description
124
+ :type width: int
125
+ :param height: Description
126
+ :type height: int
127
+
128
+ Active.
129
+ """
130
+ window.update_idletasks()
131
+
132
+ monitors = get_monitor_geometries()
133
+ target_monitor = None
134
+
135
+ if monitors:
136
+ # 1. Prefer the one explicitly marked 'primary'
137
+ target_monitor = next((m for m in monitors if m['is_primary']), None)
138
+
139
+ # 2. Fallback to the first monitor (usually the one at +0+0)
140
+ if not target_monitor:
141
+ target_monitor = monitors[0]
142
+
143
+ print(f"[DEBUG] Assessed Monitor: {target_monitor['w']}x{target_monitor['h']} at +{target_monitor['x']}+{target_monitor['y']}")
144
+ else:
145
+ print("[DEBUG] No monitors found via xrandr. Falling back to screenwidth.")
146
+ # Total fallback: use winfo_screenwidth but assume 1080p width
147
+ # to avoid the L-gap if it's clearly a massive span.
148
+ sw = window.winfo_screenwidth()
149
+ sh = window.winfo_screenheight()
150
+ target_monitor = {
151
+ 'w': 1920 if sw > 2500 else sw,
152
+ 'h': 1080 if sh > 2000 else sh,
153
+ 'x': 0, 'y': 0
154
+ }
155
+
156
+ # 3. Calculate Center relative to the identified monitor's geometry
157
+ x = target_monitor['x'] + (target_monitor['w'] // 2) - (width // 2)
158
+ y = target_monitor['y'] + (target_monitor['h'] // 2) - (height // 2)
159
+
160
+ print(f"[DEBUG] Final Positioning: x={x}, y={y}")
161
+ window.geometry(f"{width}x{height}+{int(x)}+{int(y)}")
162
+
163
+ def center_window_on_primary(window: tk.Toplevel | tk.Tk, width: int, height: int):
164
+ window.update_idletasks()
165
+ monitors = get_monitor_geometries()
166
+
167
+ target = None
168
+ if monitors:
169
+ target = next((m for m in monitors if m['is_primary']), monitors[0])
170
+
171
+ if target:
172
+ # Use the precisely assessed hardware monitor
173
+ x = target['x'] + (target['w'] // 2) - (width // 2)
174
+ y = target['y'] + (target['h'] // 2) - (height // 2)
175
+ else:
176
+ # Fallback for Windows/Mac where xrandr doesn't exist
177
+ # We use wm_maxsize which is surprisingly accurate for the 'Primary' on Windows/Mac
178
+ pw, ph = window.wm_maxsize()
179
+
180
+ # If maxsize is also reporting the huge span (rare on native),
181
+ # then we use your 1920/1080 safe-zone heuristic.
182
+ if pw > 2500:
183
+ pw, ph = 1920, 1080
184
+
185
+ x = (pw // 2) - (width // 2)
186
+ y = (ph // 2) - (height // 2)
187
+
188
+ window.geometry(f"{width}x{height}+{int(x)}+{int(y)}")
@@ -0,0 +1,49 @@
1
+ # src/pdflinkcheck/update_msix_version.py
2
+ from __future__ import annotations
3
+ from pathlib import Path
4
+ from pdflinkcheck.version_info import get_version_from_pyproject
5
+
6
+ PROJECT_ROOT = Path(__file__).resolve().parent.parent.parent
7
+
8
+ UNVERSIONED_MANIFEST = PROJECT_ROOT / "msix" / "AppxManifest_unversioned.xml"
9
+ OUTPUT_MANIFEST = PROJECT_ROOT / "msix" / "AppxManifest.xml"
10
+
11
+
12
+ PLACEHOLDER = "@@VERSION_PLACEHOLDER@@"
13
+
14
+
15
+ def generate_versioned_manifest(version):
16
+
17
+ # Pad to four parts: 1.1 -> 1.1.0.0, 1.1.92 -> 1.1.92.0
18
+ parts = version.split(".")
19
+ if len(parts) == 2:
20
+ parts += ["0", "0"]
21
+ elif len(parts) == 3:
22
+ parts.append("0")
23
+ elif len(parts) > 4:
24
+ raise ValueError(f"Version has too many parts: {version}")
25
+
26
+ msix_version = ".".join(parts[:4])
27
+
28
+ if not UNVERSIONED_MANIFEST.exists():
29
+ raise FileNotFoundError(f"Unversioned manifest not found: {UNVERSIONED_MANIFEST}")
30
+
31
+ text = UNVERSIONED_MANIFEST.read_text(encoding="utf-8")
32
+
33
+ placeholder_full = f'Version="{PLACEHOLDER}"'
34
+
35
+ if placeholder_full not in text:
36
+ raise ValueError(f"Placeholder {placeholder_full} not found in the unversioned manifest!")
37
+
38
+ updated_text = text.replace(placeholder_full, f'Version="{msix_version}"')
39
+
40
+ # Ensure the directory exists and write the new manifest
41
+ OUTPUT_MANIFEST.parent.mkdir(parents=True, exist_ok=True)
42
+ OUTPUT_MANIFEST.write_text(updated_text, encoding="utf-8")
43
+
44
+ print(f"Successfully generated AppxManifest.xml with version {msix_version}")
45
+
46
+
47
+ if __name__ == "__main__":
48
+ version = get_version_from_pyproject()
49
+ generate_versioned_manifest(version)