shellscripts 0.23.1__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.
- shell_scripts/__init__.py +5 -0
- shell_scripts/__main__.py +5 -0
- shell_scripts/commands/__init__.py +81 -0
- shell_scripts/commands/_dc_common.py +138 -0
- shell_scripts/commands/ai_install.py +453 -0
- shell_scripts/commands/bin_links.py +78 -0
- shell_scripts/commands/claude.py +56 -0
- shell_scripts/commands/clean.py +98 -0
- shell_scripts/commands/codex.py +107 -0
- shell_scripts/commands/copilot.py +54 -0
- shell_scripts/commands/dicom2jpg.py +98 -0
- shell_scripts/commands/dicomviewer.py +83 -0
- shell_scripts/commands/diff_cmd.py +50 -0
- shell_scripts/commands/doxygen_cmd.py +159 -0
- shell_scripts/commands/edit_cmd.py +50 -0
- shell_scripts/commands/gemini.py +54 -0
- shell_scripts/commands/kiro.py +54 -0
- shell_scripts/commands/opencode.py +54 -0
- shell_scripts/commands/pdf_crop.py +520 -0
- shell_scripts/commands/pdf_merge.py +180 -0
- shell_scripts/commands/pdf_split_by_format.py +243 -0
- shell_scripts/commands/pdf_split_by_toc.py +197 -0
- shell_scripts/commands/pdf_tiler_090.py +61 -0
- shell_scripts/commands/pdf_tiler_100.py +61 -0
- shell_scripts/commands/pdf_toc_clean.py +175 -0
- shell_scripts/commands/pi.py +61 -0
- shell_scripts/commands/req_cmd.py +362 -0
- shell_scripts/commands/tests_cmd.py +87 -0
- shell_scripts/commands/ubuntu_dark_theme.py +64 -0
- shell_scripts/commands/venv_cmd.py +82 -0
- shell_scripts/commands/video2h264.py +89 -0
- shell_scripts/commands/video2h265.py +87 -0
- shell_scripts/commands/view_cmd.py +50 -0
- shell_scripts/commands/vscode_cmd.py +42 -0
- shell_scripts/commands/vsinsider_cmd.py +42 -0
- shell_scripts/config.py +328 -0
- shell_scripts/core.py +324 -0
- shell_scripts/utils.py +455 -0
- shell_scripts/version_check.py +323 -0
- shellscripts-0.23.1.dist-info/METADATA +310 -0
- shellscripts-0.23.1.dist-info/RECORD +45 -0
- shellscripts-0.23.1.dist-info/WHEEL +5 -0
- shellscripts-0.23.1.dist-info/entry_points.txt +3 -0
- shellscripts-0.23.1.dist-info/licenses/LICENSE +674 -0
- shellscripts-0.23.1.dist-info/top_level.txt +1 -0
|
@@ -0,0 +1,243 @@
|
|
|
1
|
+
#!/usr/bin/env python3
|
|
2
|
+
import os
|
|
3
|
+
import re
|
|
4
|
+
import subprocess
|
|
5
|
+
import tempfile
|
|
6
|
+
|
|
7
|
+
from shell_scripts.utils import require_commands, print_error
|
|
8
|
+
|
|
9
|
+
PROGRAM = "shellscripts"
|
|
10
|
+
DESCRIPTION = "Split PDF into parts by page format changes."
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
def print_help(version):
|
|
14
|
+
print(
|
|
15
|
+
f"Usage: {PROGRAM} pdf-split-by-format <file1.pdf> [file2.pdf ...] ({version})"
|
|
16
|
+
)
|
|
17
|
+
print()
|
|
18
|
+
print("pdf-split-by-format options:")
|
|
19
|
+
print(" <files...> - Input PDF files to split.")
|
|
20
|
+
print(" --help - Show this help message.")
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
def _get_page_formats(pdf, total_pages):
|
|
24
|
+
r = subprocess.run(
|
|
25
|
+
["pdfinfo", "-f", "1", "-l", str(total_pages), pdf],
|
|
26
|
+
capture_output=True,
|
|
27
|
+
text=True,
|
|
28
|
+
stderr=subprocess.DEVNULL,
|
|
29
|
+
)
|
|
30
|
+
formats = []
|
|
31
|
+
for line in r.stdout.splitlines():
|
|
32
|
+
if re.search(r"Page\s+.*size:", line):
|
|
33
|
+
fmt = re.sub(r"Page\s+\d+\s+size:\s*", "", line).strip()
|
|
34
|
+
formats.append(fmt)
|
|
35
|
+
return formats
|
|
36
|
+
|
|
37
|
+
|
|
38
|
+
def _get_total_pages(pdf):
|
|
39
|
+
r = subprocess.run(
|
|
40
|
+
["pdfinfo", pdf],
|
|
41
|
+
capture_output=True,
|
|
42
|
+
text=True,
|
|
43
|
+
stderr=subprocess.DEVNULL,
|
|
44
|
+
)
|
|
45
|
+
for line in r.stdout.splitlines():
|
|
46
|
+
if line.startswith("Pages:"):
|
|
47
|
+
return int(line.split(":")[1].strip())
|
|
48
|
+
return None
|
|
49
|
+
|
|
50
|
+
|
|
51
|
+
def _has_toc(pdf):
|
|
52
|
+
try:
|
|
53
|
+
with tempfile.NamedTemporaryFile(suffix=".pdf", delete=True) as tmp:
|
|
54
|
+
tmp_path = tmp.name
|
|
55
|
+
subprocess.run(
|
|
56
|
+
["qpdf", "--object-streams=disable", pdf, tmp_path],
|
|
57
|
+
capture_output=True,
|
|
58
|
+
)
|
|
59
|
+
r = subprocess.run(
|
|
60
|
+
["pdftk", tmp_path, "dump_data"],
|
|
61
|
+
capture_output=True,
|
|
62
|
+
text=True,
|
|
63
|
+
)
|
|
64
|
+
os.unlink(tmp_path)
|
|
65
|
+
return "BookmarkBegin" in r.stdout
|
|
66
|
+
except Exception:
|
|
67
|
+
return False
|
|
68
|
+
|
|
69
|
+
|
|
70
|
+
def _extract_toc_for_range(data_file, start, end):
|
|
71
|
+
entries = []
|
|
72
|
+
with open(data_file) as f:
|
|
73
|
+
content = f.read()
|
|
74
|
+
|
|
75
|
+
level = title = page = ""
|
|
76
|
+
for line in content.splitlines():
|
|
77
|
+
if line == "BookmarkBegin":
|
|
78
|
+
if level and page:
|
|
79
|
+
p = int(page)
|
|
80
|
+
if start <= p <= end:
|
|
81
|
+
entries.append((level, title, str(p - start + 1)))
|
|
82
|
+
level = title = page = ""
|
|
83
|
+
elif line.startswith("BookmarkTitle: "):
|
|
84
|
+
title = line[15:]
|
|
85
|
+
elif line.startswith("BookmarkLevel: "):
|
|
86
|
+
level = line.split()[1]
|
|
87
|
+
elif line.startswith("BookmarkPageNumber: "):
|
|
88
|
+
page = line.split()[1]
|
|
89
|
+
if level and page:
|
|
90
|
+
p = int(page)
|
|
91
|
+
if start <= p <= end:
|
|
92
|
+
entries.append((level, title, str(p - start + 1)))
|
|
93
|
+
|
|
94
|
+
result = []
|
|
95
|
+
for lv, ti, pg in entries:
|
|
96
|
+
result.append("BookmarkBegin")
|
|
97
|
+
if ti:
|
|
98
|
+
result.append(f"BookmarkTitle: {ti}")
|
|
99
|
+
if lv:
|
|
100
|
+
result.append(f"BookmarkLevel: {lv}")
|
|
101
|
+
result.append(f"BookmarkPageNumber: {pg}")
|
|
102
|
+
return "\n".join(result)
|
|
103
|
+
|
|
104
|
+
|
|
105
|
+
def _apply_toc(output_file, toc_content):
|
|
106
|
+
if not toc_content.strip():
|
|
107
|
+
return
|
|
108
|
+
with tempfile.NamedTemporaryFile(mode="w", suffix=".info", delete=False) as f:
|
|
109
|
+
f.write(toc_content)
|
|
110
|
+
info_file = f.name
|
|
111
|
+
try:
|
|
112
|
+
with tempfile.NamedTemporaryFile(suffix=".pdf", delete=False) as stripped_f:
|
|
113
|
+
stripped = stripped_f.name
|
|
114
|
+
with tempfile.NamedTemporaryFile(suffix=".pdf", delete=False) as toc_f:
|
|
115
|
+
with_toc = toc_f.name
|
|
116
|
+
|
|
117
|
+
r1 = subprocess.run(
|
|
118
|
+
["qpdf", "--empty", "--pages", output_file, "1-z", "--", stripped],
|
|
119
|
+
capture_output=True,
|
|
120
|
+
)
|
|
121
|
+
if r1.returncode == 0:
|
|
122
|
+
r2 = subprocess.run(
|
|
123
|
+
["pdftk", stripped, "update_info", info_file, "output", with_toc],
|
|
124
|
+
capture_output=True,
|
|
125
|
+
)
|
|
126
|
+
if r2.returncode == 0:
|
|
127
|
+
os.replace(with_toc, output_file)
|
|
128
|
+
else:
|
|
129
|
+
os.unlink(with_toc)
|
|
130
|
+
print(f" [!] Warning: unable to update TOC for '{output_file}'.")
|
|
131
|
+
os.unlink(stripped)
|
|
132
|
+
except Exception:
|
|
133
|
+
pass
|
|
134
|
+
finally:
|
|
135
|
+
os.unlink(info_file)
|
|
136
|
+
|
|
137
|
+
|
|
138
|
+
def run(args):
|
|
139
|
+
if not args:
|
|
140
|
+
print_error("No PDF files specified.")
|
|
141
|
+
print_help("")
|
|
142
|
+
return 1
|
|
143
|
+
|
|
144
|
+
require_commands("qpdf", "pdfinfo", "pdftk")
|
|
145
|
+
|
|
146
|
+
for input_pdf in args:
|
|
147
|
+
print("=" * 50)
|
|
148
|
+
|
|
149
|
+
if not os.path.isfile(input_pdf):
|
|
150
|
+
print(f"Warning: File '{input_pdf}' does not exist. Skipping...")
|
|
151
|
+
continue
|
|
152
|
+
|
|
153
|
+
print(f"Processing: {input_pdf}")
|
|
154
|
+
basename = os.path.splitext(os.path.basename(input_pdf))[0]
|
|
155
|
+
|
|
156
|
+
total_pages = _get_total_pages(input_pdf)
|
|
157
|
+
if total_pages is None:
|
|
158
|
+
print(f"Error: Cannot read pages from '{input_pdf}'. Skipping...")
|
|
159
|
+
continue
|
|
160
|
+
|
|
161
|
+
print(f"Analyzing {total_pages} pages...")
|
|
162
|
+
formats = _get_page_formats(input_pdf, total_pages)
|
|
163
|
+
|
|
164
|
+
unique = set(formats)
|
|
165
|
+
if len(unique) <= 1:
|
|
166
|
+
print(
|
|
167
|
+
f"-> INFO: All pages have the same format: [{formats[0] if formats else 'unknown'}]."
|
|
168
|
+
)
|
|
169
|
+
print("-> No splitting needed. Moving to next...")
|
|
170
|
+
continue
|
|
171
|
+
|
|
172
|
+
has_toc = _has_toc(input_pdf)
|
|
173
|
+
data_file = None
|
|
174
|
+
if has_toc:
|
|
175
|
+
with tempfile.NamedTemporaryFile(suffix=".pdf", delete=False) as tmp:
|
|
176
|
+
tmp_pdf = tmp.name
|
|
177
|
+
subprocess.run(
|
|
178
|
+
["qpdf", "--object-streams=disable", input_pdf, tmp_pdf],
|
|
179
|
+
capture_output=True,
|
|
180
|
+
)
|
|
181
|
+
data_file = tempfile.mktemp()
|
|
182
|
+
subprocess.run(
|
|
183
|
+
["pdftk", tmp_pdf, "dump_data", "output", data_file],
|
|
184
|
+
capture_output=True,
|
|
185
|
+
)
|
|
186
|
+
os.unlink(tmp_pdf)
|
|
187
|
+
|
|
188
|
+
start_page = 1
|
|
189
|
+
chunk_num = 1
|
|
190
|
+
prev_format = formats[0]
|
|
191
|
+
|
|
192
|
+
for i in range(1, total_pages):
|
|
193
|
+
current_format = formats[i]
|
|
194
|
+
if current_format != prev_format:
|
|
195
|
+
end_page = i
|
|
196
|
+
out_name = f"{basename}_{chunk_num:02d}.pdf"
|
|
197
|
+
print(
|
|
198
|
+
f"-> Generating: {out_name} (Pages {start_page} - {end_page}) [Format: {prev_format}]"
|
|
199
|
+
)
|
|
200
|
+
subprocess.run(
|
|
201
|
+
[
|
|
202
|
+
"qpdf",
|
|
203
|
+
input_pdf,
|
|
204
|
+
"--pages",
|
|
205
|
+
".",
|
|
206
|
+
f"{start_page}-{end_page}",
|
|
207
|
+
"--",
|
|
208
|
+
out_name,
|
|
209
|
+
],
|
|
210
|
+
)
|
|
211
|
+
if has_toc and data_file:
|
|
212
|
+
toc = _extract_toc_for_range(data_file, start_page, end_page)
|
|
213
|
+
_apply_toc(out_name, toc)
|
|
214
|
+
|
|
215
|
+
start_page = i + 1
|
|
216
|
+
prev_format = current_format
|
|
217
|
+
chunk_num += 1
|
|
218
|
+
|
|
219
|
+
out_name = f"{basename}_{chunk_num:02d}.pdf"
|
|
220
|
+
print(
|
|
221
|
+
f"-> Generating: {out_name} (Pages {start_page} - {total_pages}) [Format: {prev_format}]"
|
|
222
|
+
)
|
|
223
|
+
subprocess.run(
|
|
224
|
+
[
|
|
225
|
+
"qpdf",
|
|
226
|
+
input_pdf,
|
|
227
|
+
"--pages",
|
|
228
|
+
".",
|
|
229
|
+
f"{start_page}-{total_pages}",
|
|
230
|
+
"--",
|
|
231
|
+
out_name,
|
|
232
|
+
],
|
|
233
|
+
)
|
|
234
|
+
if has_toc and data_file:
|
|
235
|
+
toc = _extract_toc_for_range(data_file, start_page, total_pages)
|
|
236
|
+
_apply_toc(out_name, toc)
|
|
237
|
+
os.unlink(data_file)
|
|
238
|
+
|
|
239
|
+
print(f"-> Splitting of '{input_pdf}' complete: generated {chunk_num} files.")
|
|
240
|
+
|
|
241
|
+
print("=" * 50)
|
|
242
|
+
print("Batch processing completed successfully!")
|
|
243
|
+
return 0
|
|
@@ -0,0 +1,197 @@
|
|
|
1
|
+
#!/usr/bin/env python3
|
|
2
|
+
import os
|
|
3
|
+
import re
|
|
4
|
+
import subprocess
|
|
5
|
+
import tempfile
|
|
6
|
+
|
|
7
|
+
from shell_scripts.utils import require_commands, print_error
|
|
8
|
+
|
|
9
|
+
PROGRAM = "shellscripts"
|
|
10
|
+
DESCRIPTION = "Split PDF into chapters by TOC level-1 entries."
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
def print_help(version):
|
|
14
|
+
print(f"Usage: {PROGRAM} pdf-split-by-toc <document.pdf> ({version})")
|
|
15
|
+
print()
|
|
16
|
+
print("pdf-split-by-toc options:")
|
|
17
|
+
print(" <document.pdf> - Input PDF file (required).")
|
|
18
|
+
print(" --help - Show this help message.")
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
def _parse_level1_toc(dump_content):
|
|
22
|
+
entries = []
|
|
23
|
+
level = title = page = ""
|
|
24
|
+
for line in dump_content.splitlines():
|
|
25
|
+
if line == "BookmarkBegin":
|
|
26
|
+
if level == "1" and page:
|
|
27
|
+
entries.append((int(page), title))
|
|
28
|
+
level = title = page = ""
|
|
29
|
+
elif line.startswith("BookmarkTitle: "):
|
|
30
|
+
title = line[15:]
|
|
31
|
+
elif line.startswith("BookmarkLevel: "):
|
|
32
|
+
level = line.split()[1]
|
|
33
|
+
elif line.startswith("BookmarkPageNumber: "):
|
|
34
|
+
page = line.split()[1]
|
|
35
|
+
if level == "1" and page:
|
|
36
|
+
entries.append((int(page), title))
|
|
37
|
+
return entries
|
|
38
|
+
|
|
39
|
+
|
|
40
|
+
def _extract_toc_for_range(dump_content, start, end):
|
|
41
|
+
lines = []
|
|
42
|
+
level = title = page = ""
|
|
43
|
+
for line in dump_content.splitlines():
|
|
44
|
+
if line == "BookmarkBegin":
|
|
45
|
+
if level and page:
|
|
46
|
+
p = int(page)
|
|
47
|
+
if start <= p <= end:
|
|
48
|
+
lines.append("BookmarkBegin")
|
|
49
|
+
if title:
|
|
50
|
+
lines.append(f"BookmarkTitle: {title}")
|
|
51
|
+
if level:
|
|
52
|
+
lines.append(f"BookmarkLevel: {level}")
|
|
53
|
+
lines.append(f"BookmarkPageNumber: {p - start + 1}")
|
|
54
|
+
level = title = page = ""
|
|
55
|
+
elif line.startswith("BookmarkTitle: "):
|
|
56
|
+
title = line[15:]
|
|
57
|
+
elif line.startswith("BookmarkLevel: "):
|
|
58
|
+
level = line.split()[1]
|
|
59
|
+
elif line.startswith("BookmarkPageNumber: "):
|
|
60
|
+
page = line.split()[1]
|
|
61
|
+
if level and page:
|
|
62
|
+
p = int(page)
|
|
63
|
+
if start <= p <= end:
|
|
64
|
+
lines.append("BookmarkBegin")
|
|
65
|
+
if title:
|
|
66
|
+
lines.append(f"BookmarkTitle: {title}")
|
|
67
|
+
if level:
|
|
68
|
+
lines.append(f"BookmarkLevel: {level}")
|
|
69
|
+
lines.append(f"BookmarkPageNumber: {p - start + 1}")
|
|
70
|
+
return "\n".join(lines)
|
|
71
|
+
|
|
72
|
+
|
|
73
|
+
def _sanitize_title(title):
|
|
74
|
+
safe = re.sub(r"[^A-Za-z0-9]", "-", title)
|
|
75
|
+
safe = re.sub(r"-+", "-", safe)
|
|
76
|
+
return safe.strip("-")
|
|
77
|
+
|
|
78
|
+
|
|
79
|
+
def _apply_toc_to_file(output_file, toc_content):
|
|
80
|
+
if not toc_content.strip():
|
|
81
|
+
return
|
|
82
|
+
with tempfile.NamedTemporaryFile(mode="w", suffix=".info", delete=False) as f:
|
|
83
|
+
f.write(toc_content)
|
|
84
|
+
info_file = f.name
|
|
85
|
+
try:
|
|
86
|
+
stripped = tempfile.mktemp(suffix=".pdf")
|
|
87
|
+
with_toc = tempfile.mktemp(suffix=".pdf")
|
|
88
|
+
r1 = subprocess.run(
|
|
89
|
+
["qpdf", "--empty", "--pages", output_file, "1-z", "--", stripped],
|
|
90
|
+
capture_output=True,
|
|
91
|
+
)
|
|
92
|
+
if r1.returncode == 0:
|
|
93
|
+
r2 = subprocess.run(
|
|
94
|
+
["pdftk", stripped, "update_info", info_file, "output", with_toc],
|
|
95
|
+
capture_output=True,
|
|
96
|
+
)
|
|
97
|
+
if r2.returncode == 0:
|
|
98
|
+
os.replace(with_toc, output_file)
|
|
99
|
+
else:
|
|
100
|
+
if os.path.exists(with_toc):
|
|
101
|
+
os.unlink(with_toc)
|
|
102
|
+
print(f" [!] Warning: unable to update TOC for '{output_file}'.")
|
|
103
|
+
if os.path.exists(stripped):
|
|
104
|
+
os.unlink(stripped)
|
|
105
|
+
finally:
|
|
106
|
+
os.unlink(info_file)
|
|
107
|
+
|
|
108
|
+
|
|
109
|
+
def run(args):
|
|
110
|
+
if not args or args[0].startswith("-"):
|
|
111
|
+
print_error("Input PDF file required.")
|
|
112
|
+
print_help("")
|
|
113
|
+
return 1
|
|
114
|
+
|
|
115
|
+
require_commands("pdftk", "qpdf")
|
|
116
|
+
|
|
117
|
+
input_file = args[0]
|
|
118
|
+
if not os.path.isfile(input_file):
|
|
119
|
+
print_error(f"File not found: {input_file}")
|
|
120
|
+
return 1
|
|
121
|
+
|
|
122
|
+
basename = os.path.splitext(os.path.basename(input_file))[0]
|
|
123
|
+
|
|
124
|
+
with tempfile.TemporaryDirectory() as tmp_dir:
|
|
125
|
+
tmp_pdf = os.path.join(tmp_dir, "prepared.pdf")
|
|
126
|
+
data_file = os.path.join(tmp_dir, "data.txt")
|
|
127
|
+
|
|
128
|
+
print("Preparing document (disabling Object Streams for TOC reading)...")
|
|
129
|
+
r = subprocess.run(
|
|
130
|
+
["qpdf", "--object-streams=disable", input_file, tmp_pdf],
|
|
131
|
+
capture_output=True,
|
|
132
|
+
)
|
|
133
|
+
if r.returncode != 0:
|
|
134
|
+
print_error("Error during initial processing with qpdf.")
|
|
135
|
+
return 1
|
|
136
|
+
|
|
137
|
+
print("Extracting TOC data...")
|
|
138
|
+
subprocess.run(
|
|
139
|
+
["pdftk", tmp_pdf, "dump_data", "output", data_file],
|
|
140
|
+
capture_output=True,
|
|
141
|
+
)
|
|
142
|
+
|
|
143
|
+
with open(data_file) as f:
|
|
144
|
+
dump_content = f.read()
|
|
145
|
+
|
|
146
|
+
if "BookmarkBegin" not in dump_content:
|
|
147
|
+
print_error("No TOC found even after decompression.")
|
|
148
|
+
print("The PDF may use 'Named Destinations' instead of explicit page numbers.")
|
|
149
|
+
return 1
|
|
150
|
+
|
|
151
|
+
total_pages = None
|
|
152
|
+
for line in dump_content.splitlines():
|
|
153
|
+
if line.startswith("NumberOfPages:"):
|
|
154
|
+
total_pages = int(line.split()[1])
|
|
155
|
+
break
|
|
156
|
+
|
|
157
|
+
if total_pages is None:
|
|
158
|
+
print_error("Cannot read total pages.")
|
|
159
|
+
return 1
|
|
160
|
+
|
|
161
|
+
print(f"Document has {total_pages} total pages.")
|
|
162
|
+
|
|
163
|
+
toc_entries = _parse_level1_toc(dump_content)
|
|
164
|
+
if not toc_entries:
|
|
165
|
+
print_error("TOC found, but no valid level-1 entries with explicit pages.")
|
|
166
|
+
return 1
|
|
167
|
+
|
|
168
|
+
num_sections = len(toc_entries)
|
|
169
|
+
print(f"Found {num_sections} level-1 chapters. Starting split...")
|
|
170
|
+
|
|
171
|
+
for i, (start_page, title) in enumerate(toc_entries):
|
|
172
|
+
if i + 1 < num_sections:
|
|
173
|
+
next_page = toc_entries[i + 1][0]
|
|
174
|
+
end_page = max(next_page - 1, start_page)
|
|
175
|
+
else:
|
|
176
|
+
end_page = total_pages
|
|
177
|
+
|
|
178
|
+
safe_title = _sanitize_title(title)
|
|
179
|
+
index_padded = f"{i + 1:02d}"
|
|
180
|
+
output_name = f"{basename}_{index_padded}_{safe_title}.pdf"
|
|
181
|
+
|
|
182
|
+
print(f"-> Creating: {output_name} (Pages {start_page} - {end_page})")
|
|
183
|
+
|
|
184
|
+
r = subprocess.run(
|
|
185
|
+
["qpdf", input_file, "--pages", input_file,
|
|
186
|
+
f"{start_page}-{end_page}", "--", output_name],
|
|
187
|
+
)
|
|
188
|
+
if r.returncode != 0:
|
|
189
|
+
print(f" [!] Error extracting section '{title}'")
|
|
190
|
+
continue
|
|
191
|
+
|
|
192
|
+
toc_content = _extract_toc_for_range(dump_content, start_page, end_page)
|
|
193
|
+
_apply_toc_to_file(output_name, toc_content)
|
|
194
|
+
|
|
195
|
+
print("---")
|
|
196
|
+
print("Split completed successfully!")
|
|
197
|
+
return 0
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
#!/usr/bin/env python3
|
|
2
|
+
import subprocess
|
|
3
|
+
from pathlib import Path
|
|
4
|
+
|
|
5
|
+
from shell_scripts.utils import require_commands, print_error
|
|
6
|
+
|
|
7
|
+
PROGRAM = "shellscripts"
|
|
8
|
+
DESCRIPTION = "Tile PDF to A4 pages at 90% scale using plakativ."
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
def print_help(version):
|
|
12
|
+
print(f"Usage: {PROGRAM} pdf-tiler-090 <input.pdf> ({version})")
|
|
13
|
+
print()
|
|
14
|
+
print("pdf-tiler-090 options:")
|
|
15
|
+
print(" <input.pdf> - Input PDF file (required).")
|
|
16
|
+
print(" --help - Show this help message.")
|
|
17
|
+
print()
|
|
18
|
+
print("Outputs <basename>_tiled-A4.pdf in the same directory as the input.")
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
def run(args):
|
|
22
|
+
"""@brief Execute `pdf-tiler-090` command with blocking subprocess launch.
|
|
23
|
+
|
|
24
|
+
@details Validates input argument and file presence, builds `plakativ`
|
|
25
|
+
command vector with fixed A4/0.90 parameters, then executes it via
|
|
26
|
+
`subprocess.run` while inheriting stdio streams.
|
|
27
|
+
@param args {list[str]} Command arguments excluding command token.
|
|
28
|
+
@return {int} `1` on validation failure; child process return code otherwise.
|
|
29
|
+
@satisfies REQ-029, REQ-055, REQ-056, REQ-064
|
|
30
|
+
"""
|
|
31
|
+
|
|
32
|
+
if not args or args[0].startswith("-"):
|
|
33
|
+
print_error("Input PDF file required.")
|
|
34
|
+
print_help("")
|
|
35
|
+
return 1
|
|
36
|
+
|
|
37
|
+
require_commands("plakativ")
|
|
38
|
+
|
|
39
|
+
input_file = Path(args[0])
|
|
40
|
+
if not input_file.is_file():
|
|
41
|
+
print_error(f"File not found: {input_file}")
|
|
42
|
+
return 1
|
|
43
|
+
|
|
44
|
+
output_file = input_file.parent / f"{input_file.stem}_tiled-A4.pdf"
|
|
45
|
+
|
|
46
|
+
cmd = [
|
|
47
|
+
"plakativ",
|
|
48
|
+
"--factor",
|
|
49
|
+
"0.90",
|
|
50
|
+
"--pagesize",
|
|
51
|
+
"A4",
|
|
52
|
+
"--border",
|
|
53
|
+
"5mm",
|
|
54
|
+
"--cutting-guides",
|
|
55
|
+
"-o",
|
|
56
|
+
str(output_file),
|
|
57
|
+
str(input_file),
|
|
58
|
+
]
|
|
59
|
+
require_commands(cmd[0])
|
|
60
|
+
result = subprocess.run(cmd)
|
|
61
|
+
return result.returncode
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
#!/usr/bin/env python3
|
|
2
|
+
import subprocess
|
|
3
|
+
from pathlib import Path
|
|
4
|
+
|
|
5
|
+
from shell_scripts.utils import require_commands, print_error
|
|
6
|
+
|
|
7
|
+
PROGRAM = "shellscripts"
|
|
8
|
+
DESCRIPTION = "Tile PDF to A4 pages at original A1 size using plakativ."
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
def print_help(version):
|
|
12
|
+
print(f"Usage: {PROGRAM} pdf-tiler-100 <input.pdf> ({version})")
|
|
13
|
+
print()
|
|
14
|
+
print("pdf-tiler-100 options:")
|
|
15
|
+
print(" <input.pdf> - Input PDF file (required).")
|
|
16
|
+
print(" --help - Show this help message.")
|
|
17
|
+
print()
|
|
18
|
+
print("Outputs <basename>_tiled-A4.pdf in the same directory as the input.")
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
def run(args):
|
|
22
|
+
"""@brief Execute `pdf-tiler-100` command with blocking subprocess launch.
|
|
23
|
+
|
|
24
|
+
@details Validates input argument and file presence, builds `plakativ`
|
|
25
|
+
command vector with fixed A1 source size and A4 tiling parameters, then
|
|
26
|
+
executes it via `subprocess.run` while inheriting stdio streams.
|
|
27
|
+
@param args {list[str]} Command arguments excluding command token.
|
|
28
|
+
@return {int} `1` on validation failure; child process return code otherwise.
|
|
29
|
+
@satisfies REQ-029, REQ-055, REQ-056, REQ-064
|
|
30
|
+
"""
|
|
31
|
+
|
|
32
|
+
if not args or args[0].startswith("-"):
|
|
33
|
+
print_error("Input PDF file required.")
|
|
34
|
+
print_help("")
|
|
35
|
+
return 1
|
|
36
|
+
|
|
37
|
+
require_commands("plakativ")
|
|
38
|
+
|
|
39
|
+
input_file = Path(args[0])
|
|
40
|
+
if not input_file.is_file():
|
|
41
|
+
print_error(f"File not found: {input_file}")
|
|
42
|
+
return 1
|
|
43
|
+
|
|
44
|
+
output_file = input_file.parent / f"{input_file.stem}_tiled-A4.pdf"
|
|
45
|
+
|
|
46
|
+
cmd = [
|
|
47
|
+
"plakativ",
|
|
48
|
+
"--size",
|
|
49
|
+
"A1",
|
|
50
|
+
"--pagesize",
|
|
51
|
+
"A4",
|
|
52
|
+
"--border",
|
|
53
|
+
"5mm",
|
|
54
|
+
"--cutting-guides",
|
|
55
|
+
"-o",
|
|
56
|
+
str(output_file),
|
|
57
|
+
str(input_file),
|
|
58
|
+
]
|
|
59
|
+
require_commands(cmd[0])
|
|
60
|
+
result = subprocess.run(cmd)
|
|
61
|
+
return result.returncode
|