nicoaspra commited on
Commit
bd57041
·
1 Parent(s): 4ad1d10

footer, title

Browse files
Files changed (5) hide show
  1. .gitignore +1 -0
  2. 01_PDF_booklet.ipynb +988 -0
  3. README.md +5 -5
  4. app.py +0 -131
  5. ui_PDF_booklet.py +163 -0
.gitignore ADDED
@@ -0,0 +1 @@
 
 
1
+ .DS_Store
01_PDF_booklet.ipynb ADDED
@@ -0,0 +1,988 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ {
2
+ "cells": [
3
+ {
4
+ "cell_type": "code",
5
+ "execution_count": 25,
6
+ "metadata": {},
7
+ "outputs": [
8
+ {
9
+ "name": "stdout",
10
+ "output_type": "stream",
11
+ "text": [
12
+ "Front pages PDF saved as: booklet_front.pdf\n",
13
+ "Back pages PDF saved as: booklet_back.pdf\n"
14
+ ]
15
+ }
16
+ ],
17
+ "source": [
18
+ "import math\n",
19
+ "import io\n",
20
+ "from PyPDF2 import PdfReader, PdfWriter\n",
21
+ "\n",
22
+ "def create_booklet_with_blank_pages(pdf_path, output_front_path, output_back_path):\n",
23
+ " try:\n",
24
+ " # Step 1: Read the PDF using the file path\n",
25
+ " reader = PdfReader(pdf_path)\n",
26
+ " total_pages = len(reader.pages)\n",
27
+ "\n",
28
+ " # Step 2: Ensure the number of pages is divisible by 4 by adding blank pages if needed\n",
29
+ " writer = PdfWriter()\n",
30
+ " for page in reader.pages:\n",
31
+ " writer.add_page(page)\n",
32
+ "\n",
33
+ " # Calculate how many blank pages are needed\n",
34
+ " remainder = total_pages % 4\n",
35
+ " blank_pages_needed = (4 - remainder) if remainder > 0 else 0\n",
36
+ "\n",
37
+ " # Step 3: Add the necessary number of blank pages\n",
38
+ " for _ in range(blank_pages_needed):\n",
39
+ " writer.add_blank_page() # Add a blank page directly\n",
40
+ "\n",
41
+ " # Step 4: Write the new PDF with added blank pages to a BytesIO stream\n",
42
+ " modified_pdf = io.BytesIO()\n",
43
+ " writer.write(modified_pdf)\n",
44
+ " modified_pdf.seek(0)\n",
45
+ "\n",
46
+ " # Step 5: Create the front and back arrangement for booklet-style printing\n",
47
+ " modified_reader = PdfReader(modified_pdf)\n",
48
+ " total_pages = len(modified_reader.pages)\n",
49
+ "\n",
50
+ " # Create writers for the front and back pages\n",
51
+ " front_writer = PdfWriter()\n",
52
+ " back_writer = PdfWriter()\n",
53
+ "\n",
54
+ " # Arrange pages for the front: (e.g., 8 & 1, 6 & 3)\n",
55
+ " for i in range(total_pages // 4):\n",
56
+ " front_writer.add_page(modified_reader.pages[total_pages - 1 - (2 * i)])\n",
57
+ " front_writer.add_page(modified_reader.pages[2 * i])\n",
58
+ "\n",
59
+ " # Arrange pages for the back: (e.g., 2 & 7, 4 & 5)\n",
60
+ " for i in range(total_pages // 4):\n",
61
+ " back_writer.add_page(modified_reader.pages[2 * i + 1])\n",
62
+ " back_writer.add_page(modified_reader.pages[total_pages - 2 - (2 * i)])\n",
63
+ "\n",
64
+ " # Step 6: Write the front and back PDFs to the specified output paths\n",
65
+ " with open(output_front_path, \"wb\") as front_file:\n",
66
+ " front_writer.write(front_file)\n",
67
+ "\n",
68
+ " with open(output_back_path, \"wb\") as back_file:\n",
69
+ " back_writer.write(back_file)\n",
70
+ "\n",
71
+ " print(f\"Front pages PDF saved as: {output_front_path}\")\n",
72
+ " print(f\"Back pages PDF saved as: {output_back_path}\")\n",
73
+ "\n",
74
+ " except Exception as e:\n",
75
+ " print(f\"Error during PDF processing: {str(e)}\")\n",
76
+ "\n",
77
+ "# Example usage\n",
78
+ "pdf_path = \"input.pdf\" # Replace with your input PDF path\n",
79
+ "output_front_path = \"booklet_front.pdf\"\n",
80
+ "output_back_path = \"booklet_back.pdf\"\n",
81
+ "create_booklet_with_blank_pages(pdf_path, output_front_path, output_back_path)"
82
+ ]
83
+ },
84
+ {
85
+ "cell_type": "markdown",
86
+ "metadata": {},
87
+ "source": [
88
+ "## attempt to directly merge the 2 pages together"
89
+ ]
90
+ },
91
+ {
92
+ "cell_type": "code",
93
+ "execution_count": 48,
94
+ "metadata": {},
95
+ "outputs": [
96
+ {
97
+ "name": "stdout",
98
+ "output_type": "stream",
99
+ "text": [
100
+ "Front pages PDF saved as: booklet_front.pdf\n",
101
+ "Back pages PDF saved as: booklet_back.pdf\n"
102
+ ]
103
+ }
104
+ ],
105
+ "source": [
106
+ "import tempfile\n",
107
+ "from PyPDF2 import PdfReader, PdfWriter, PageObject\n",
108
+ "\n",
109
+ "def merge_two_pages_on_one(page1, page2):\n",
110
+ " \"\"\"\n",
111
+ " Merges two PDF pages into a single PDF page (side by side).\n",
112
+ " \"\"\"\n",
113
+ " # Create a new blank page with double width to place the two pages side by side\n",
114
+ " width = page1.mediabox.width + page2.mediabox.width\n",
115
+ " height = max(page1.mediabox.height, page2.mediabox.height)\n",
116
+ " \n",
117
+ " new_page = PageObject.create_blank_page(width=width, height=height)\n",
118
+ "\n",
119
+ " # Merge page1 on the left side\n",
120
+ " new_page.merge_page(page1)\n",
121
+ "\n",
122
+ " # Translate page2 to the right by the width of page1\n",
123
+ " page2_content = PageObject.create_blank_page(width=width, height=height)\n",
124
+ " page2_content.merge_page(page2)\n",
125
+ " page2_content.add_transformation([1, 0, 0, 1, page1.mediabox.width, 0]) # x translation by page1 width\n",
126
+ "\n",
127
+ " # Merge the translated page2 onto the new page\n",
128
+ " new_page.merge_page(page2_content)\n",
129
+ "\n",
130
+ " return new_page\n",
131
+ "\n",
132
+ "def create_booklet_with_two_pages_per_sheet(pdf_path, output_front_path, output_back_path):\n",
133
+ " try:\n",
134
+ " # Step 1: Read the PDF using the file path\n",
135
+ " reader = PdfReader(pdf_path)\n",
136
+ " total_pages = len(reader.pages)\n",
137
+ "\n",
138
+ " # Step 2: Ensure the number of pages is divisible by 4 by adding blank pages if needed\n",
139
+ " writer = PdfWriter()\n",
140
+ " for page in reader.pages:\n",
141
+ " writer.add_page(page)\n",
142
+ "\n",
143
+ " # Calculate how many blank pages are needed\n",
144
+ " remainder = total_pages % 4\n",
145
+ " blank_pages_needed = (4 - remainder) if remainder > 0 else 0\n",
146
+ "\n",
147
+ " # Step 3: Add the necessary number of blank pages\n",
148
+ " for _ in range(blank_pages_needed):\n",
149
+ " writer.add_blank_page() # Add a blank page directly\n",
150
+ "\n",
151
+ " # Step 4: Write the new PDF with added blank pages to a temporary file\n",
152
+ " with tempfile.NamedTemporaryFile(delete=False, suffix=\".pdf\") as temp_pdf_file:\n",
153
+ " writer.write(temp_pdf_file)\n",
154
+ " temp_pdf_path = temp_pdf_file.name\n",
155
+ "\n",
156
+ " # Read the modified PDF back\n",
157
+ " modified_reader = PdfReader(temp_pdf_path)\n",
158
+ " total_pages = len(modified_reader.pages)\n",
159
+ "\n",
160
+ " # Create writers for the front and back pages\n",
161
+ " front_writer = PdfWriter()\n",
162
+ " back_writer = PdfWriter()\n",
163
+ "\n",
164
+ " # Arrange pages for the front: (e.g., 8 & 1, 6 & 3)\n",
165
+ " for i in range(total_pages // 4):\n",
166
+ " page1 = modified_reader.pages[total_pages - 1 - (2 * i)]\n",
167
+ " page2 = modified_reader.pages[2 * i]\n",
168
+ "\n",
169
+ " # Merge the two pages onto one sheet\n",
170
+ " merged_page = merge_two_pages_on_one(page1, page2)\n",
171
+ " front_writer.add_page(merged_page)\n",
172
+ "\n",
173
+ " # Arrange pages for the back: (e.g., 2 & 7, 4 & 5)\n",
174
+ " for i in range(total_pages // 4):\n",
175
+ " page1 = modified_reader.pages[2 * i + 1]\n",
176
+ " page2 = modified_reader.pages[total_pages - 2 - (2 * i)]\n",
177
+ "\n",
178
+ " # Merge the two pages onto one sheet\n",
179
+ " merged_page = merge_two_pages_on_one(page1, page2)\n",
180
+ " back_writer.add_page(merged_page)\n",
181
+ "\n",
182
+ " # Step 5: Write the front and back PDFs to the specified output paths\n",
183
+ " with open(output_front_path, \"wb\") as front_file:\n",
184
+ " front_writer.write(front_file)\n",
185
+ "\n",
186
+ " with open(output_back_path, \"wb\") as back_file:\n",
187
+ " back_writer.write(back_file)\n",
188
+ "\n",
189
+ " print(f\"Front pages PDF saved as: {output_front_path}\")\n",
190
+ " print(f\"Back pages PDF saved as: {output_back_path}\")\n",
191
+ "\n",
192
+ " except Exception as e:\n",
193
+ " print(f\"Error during PDF processing: {str(e)}\")\n",
194
+ "\n",
195
+ "# Example usage\n",
196
+ "pdf_path = \"input.pdf\" # Replace with your input PDF path\n",
197
+ "output_front_path = \"booklet_front.pdf\"\n",
198
+ "output_back_path = \"booklet_back.pdf\"\n",
199
+ "create_booklet_with_two_pages_per_sheet(pdf_path, output_front_path, output_back_path)"
200
+ ]
201
+ },
202
+ {
203
+ "cell_type": "markdown",
204
+ "metadata": {},
205
+ "source": [
206
+ "# Booklet with reverse and normal back"
207
+ ]
208
+ },
209
+ {
210
+ "cell_type": "code",
211
+ "execution_count": 3,
212
+ "metadata": {},
213
+ "outputs": [
214
+ {
215
+ "name": "stdout",
216
+ "output_type": "stream",
217
+ "text": [
218
+ "Front pages PDF saved as: booklet_front.pdf\n",
219
+ "Back pages PDF saved as: booklet_back.pdf\n",
220
+ "Reversed PDF saved as: reversed_booklet_back.pdf\n"
221
+ ]
222
+ }
223
+ ],
224
+ "source": [
225
+ "import tempfile\n",
226
+ "from PyPDF2 import PdfReader, PdfWriter, PageObject\n",
227
+ "\n",
228
+ "def merge_two_pages_on_one(page1, page2):\n",
229
+ " \"\"\"\n",
230
+ " Merges two PDF pages into a single PDF page (side by side).\n",
231
+ " \"\"\"\n",
232
+ " # Create a new blank page with double width to place the two pages side by side\n",
233
+ " width = page1.mediabox.width + page2.mediabox.width\n",
234
+ " height = max(page1.mediabox.height, page2.mediabox.height)\n",
235
+ " \n",
236
+ " new_page = PageObject.create_blank_page(width=width, height=height)\n",
237
+ "\n",
238
+ " # Merge page1 on the left side\n",
239
+ " new_page.merge_page(page1)\n",
240
+ "\n",
241
+ " # Translate page2 to the right by the width of page1\n",
242
+ " page2_content = PageObject.create_blank_page(width=width, height=height)\n",
243
+ " page2_content.merge_page(page2)\n",
244
+ " page2_content.add_transformation([1, 0, 0, 1, page1.mediabox.width, 0]) # x translation by page1 width\n",
245
+ "\n",
246
+ " # Merge the translated page2 onto the new page\n",
247
+ " new_page.merge_page(page2_content)\n",
248
+ "\n",
249
+ " return new_page\n",
250
+ "\n",
251
+ "def reverse_pdf(input_pdf_path, output_pdf_path):\n",
252
+ " \"\"\"\n",
253
+ " Reverses the order of pages in a PDF.\n",
254
+ " \"\"\"\n",
255
+ " try:\n",
256
+ " # Step 1: Read the PDF using the file path\n",
257
+ " reader = PdfReader(input_pdf_path)\n",
258
+ " total_pages = len(reader.pages)\n",
259
+ "\n",
260
+ " # Step 2: Create a writer and add pages in reverse order\n",
261
+ " writer = PdfWriter()\n",
262
+ " for page_num in reversed(range(total_pages)):\n",
263
+ " writer.add_page(reader.pages[page_num])\n",
264
+ "\n",
265
+ " # Step 3: Write the reversed pages to the new PDF\n",
266
+ " with open(output_pdf_path, \"wb\") as output_file:\n",
267
+ " writer.write(output_file)\n",
268
+ "\n",
269
+ " print(f\"Reversed PDF saved as: {output_pdf_path}\")\n",
270
+ "\n",
271
+ " except Exception as e:\n",
272
+ " print(f\"Error during PDF reversing: {str(e)}\")\n",
273
+ "\n",
274
+ "def create_booklet_with_two_pages_per_sheet(pdf_path, output_front_path, output_back_path, output_reversed_back_path):\n",
275
+ " try:\n",
276
+ " # Step 1: Read the PDF using the file path\n",
277
+ " reader = PdfReader(pdf_path)\n",
278
+ " total_pages = len(reader.pages)\n",
279
+ "\n",
280
+ " # Step 2: Ensure the number of pages is divisible by 4 by adding blank pages if needed\n",
281
+ " writer = PdfWriter()\n",
282
+ " for page in reader.pages:\n",
283
+ " writer.add_page(page)\n",
284
+ "\n",
285
+ " # Calculate how many blank pages are needed\n",
286
+ " remainder = total_pages % 4\n",
287
+ " blank_pages_needed = (4 - remainder) if remainder > 0 else 0\n",
288
+ "\n",
289
+ " # Step 3: Add the necessary number of blank pages\n",
290
+ " for _ in range(blank_pages_needed):\n",
291
+ " writer.add_blank_page() # Add a blank page directly\n",
292
+ "\n",
293
+ " # Step 4: Write the new PDF with added blank pages to a temporary file\n",
294
+ " with tempfile.NamedTemporaryFile(delete=False, suffix=\".pdf\") as temp_pdf_file:\n",
295
+ " writer.write(temp_pdf_file)\n",
296
+ " temp_pdf_path = temp_pdf_file.name\n",
297
+ "\n",
298
+ " # Read the modified PDF back\n",
299
+ " modified_reader = PdfReader(temp_pdf_path)\n",
300
+ " total_pages = len(modified_reader.pages)\n",
301
+ "\n",
302
+ " # Create writers for the front and back pages\n",
303
+ " front_writer = PdfWriter()\n",
304
+ " back_writer = PdfWriter()\n",
305
+ "\n",
306
+ " # Arrange pages for the front: (e.g., 8 & 1, 6 & 3)\n",
307
+ " for i in range(total_pages // 4):\n",
308
+ " page1 = modified_reader.pages[total_pages - 1 - (2 * i)]\n",
309
+ " page2 = modified_reader.pages[2 * i]\n",
310
+ "\n",
311
+ " # Merge the two pages onto one sheet\n",
312
+ " merged_page = merge_two_pages_on_one(page1, page2)\n",
313
+ " front_writer.add_page(merged_page)\n",
314
+ "\n",
315
+ " # Arrange pages for the back: (e.g., 2 & 7, 4 & 5)\n",
316
+ " for i in range(total_pages // 4):\n",
317
+ " page1 = modified_reader.pages[2 * i + 1]\n",
318
+ " page2 = modified_reader.pages[total_pages - 2 - (2 * i)]\n",
319
+ "\n",
320
+ " # Merge the two pages onto one sheet\n",
321
+ " merged_page = merge_two_pages_on_one(page1, page2)\n",
322
+ " back_writer.add_page(merged_page)\n",
323
+ "\n",
324
+ " # Step 5: Write the front and back PDFs to the specified output paths\n",
325
+ " with open(output_front_path, \"wb\") as front_file:\n",
326
+ " front_writer.write(front_file)\n",
327
+ "\n",
328
+ " with open(output_back_path, \"wb\") as back_file:\n",
329
+ " back_writer.write(back_file)\n",
330
+ "\n",
331
+ " print(f\"Front pages PDF saved as: {output_front_path}\")\n",
332
+ " print(f\"Back pages PDF saved as: {output_back_path}\")\n",
333
+ "\n",
334
+ " # Step 6: Reverse the back PDF pages\n",
335
+ " reverse_pdf(output_back_path, output_reversed_back_path)\n",
336
+ "\n",
337
+ " except Exception as e:\n",
338
+ " print(f\"Error during PDF processing: {str(e)}\")\n",
339
+ "\n",
340
+ "# Example usage\n",
341
+ "pdf_path = \"Calculus_Module.pdf\" # Replace with your input PDF path\n",
342
+ "output_front_path = \"booklet_front.pdf\"\n",
343
+ "output_back_path = \"booklet_back.pdf\"\n",
344
+ "output_reversed_back_path = \"reversed_booklet_back.pdf\"\n",
345
+ "create_booklet_with_two_pages_per_sheet(pdf_path, output_front_path, output_back_path, output_reversed_back_path)"
346
+ ]
347
+ },
348
+ {
349
+ "cell_type": "markdown",
350
+ "metadata": {},
351
+ "source": [
352
+ "# Booklet Reversed Back"
353
+ ]
354
+ },
355
+ {
356
+ "cell_type": "code",
357
+ "execution_count": 50,
358
+ "metadata": {},
359
+ "outputs": [
360
+ {
361
+ "name": "stdout",
362
+ "output_type": "stream",
363
+ "text": [
364
+ "Front pages PDF saved as: booklet_front.pdf\n",
365
+ "Back pages PDF (already reversed) saved as: booklet_back.pdf\n"
366
+ ]
367
+ }
368
+ ],
369
+ "source": [
370
+ "import tempfile\n",
371
+ "from PyPDF2 import PdfReader, PdfWriter, PageObject\n",
372
+ "\n",
373
+ "def merge_two_pages_on_one(page1, page2):\n",
374
+ " \"\"\"\n",
375
+ " Merges two PDF pages into a single PDF page (side by side).\n",
376
+ " \"\"\"\n",
377
+ " # Create a new blank page with double width to place the two pages side by side\n",
378
+ " width = page1.mediabox.width + page2.mediabox.width\n",
379
+ " height = max(page1.mediabox.height, page2.mediabox.height)\n",
380
+ " \n",
381
+ " new_page = PageObject.create_blank_page(width=width, height=height)\n",
382
+ "\n",
383
+ " # Merge page1 on the left side\n",
384
+ " new_page.merge_page(page1)\n",
385
+ "\n",
386
+ " # Translate page2 to the right by the width of page1\n",
387
+ " page2_content = PageObject.create_blank_page(width=width, height=height)\n",
388
+ " page2_content.merge_page(page2)\n",
389
+ " page2_content.add_transformation([1, 0, 0, 1, page1.mediabox.width, 0]) # x translation by page1 width\n",
390
+ "\n",
391
+ " # Merge the translated page2 onto the new page\n",
392
+ " new_page.merge_page(page2_content)\n",
393
+ "\n",
394
+ " return new_page\n",
395
+ "\n",
396
+ "def create_booklet_with_two_pages_per_sheet(pdf_path, output_front_path, output_back_path):\n",
397
+ " try:\n",
398
+ " # Step 1: Read the PDF using the file path\n",
399
+ " reader = PdfReader(pdf_path)\n",
400
+ " total_pages = len(reader.pages)\n",
401
+ "\n",
402
+ " # Step 2: Ensure the number of pages is divisible by 4 by adding blank pages if needed\n",
403
+ " writer = PdfWriter()\n",
404
+ " for page in reader.pages:\n",
405
+ " writer.add_page(page)\n",
406
+ "\n",
407
+ " # Calculate how many blank pages are needed\n",
408
+ " remainder = total_pages % 4\n",
409
+ " blank_pages_needed = (4 - remainder) if remainder > 0 else 0\n",
410
+ "\n",
411
+ " # Step 3: Add the necessary number of blank pages\n",
412
+ " for _ in range(blank_pages_needed):\n",
413
+ " writer.add_blank_page() # Add a blank page directly\n",
414
+ "\n",
415
+ " # Step 4: Write the new PDF with added blank pages to a temporary file\n",
416
+ " with tempfile.NamedTemporaryFile(delete=False, suffix=\".pdf\") as temp_pdf_file:\n",
417
+ " writer.write(temp_pdf_file)\n",
418
+ " temp_pdf_path = temp_pdf_file.name\n",
419
+ "\n",
420
+ " # Read the modified PDF back\n",
421
+ " modified_reader = PdfReader(temp_pdf_path)\n",
422
+ " total_pages = len(modified_reader.pages)\n",
423
+ "\n",
424
+ " # Create writers for the front and back pages\n",
425
+ " front_writer = PdfWriter()\n",
426
+ " back_writer = PdfWriter()\n",
427
+ "\n",
428
+ " # Arrange pages for the front: (e.g., 8 & 1, 6 & 3)\n",
429
+ " for i in range(total_pages // 4):\n",
430
+ " page1 = modified_reader.pages[total_pages - 1 - (2 * i)]\n",
431
+ " page2 = modified_reader.pages[2 * i]\n",
432
+ "\n",
433
+ " # Merge the two pages onto one sheet\n",
434
+ " merged_page = merge_two_pages_on_one(page1, page2)\n",
435
+ " front_writer.add_page(merged_page)\n",
436
+ "\n",
437
+ " # Arrange pages for the back, but in reverse order directly: (e.g., 2 & 7, 4 & 5)\n",
438
+ " for i in reversed(range(total_pages // 4)):\n",
439
+ " page1 = modified_reader.pages[2 * i + 1]\n",
440
+ " page2 = modified_reader.pages[total_pages - 2 - (2 * i)]\n",
441
+ "\n",
442
+ " # Merge the two pages onto one sheet\n",
443
+ " merged_page = merge_two_pages_on_one(page1, page2)\n",
444
+ " back_writer.add_page(merged_page)\n",
445
+ "\n",
446
+ " # Step 5: Write the front and reversed back PDFs to the specified output paths\n",
447
+ " with open(output_front_path, \"wb\") as front_file:\n",
448
+ " front_writer.write(front_file)\n",
449
+ "\n",
450
+ " with open(output_back_path, \"wb\") as back_file:\n",
451
+ " back_writer.write(back_file)\n",
452
+ "\n",
453
+ " print(f\"Front pages PDF saved as: {output_front_path}\")\n",
454
+ " print(f\"Back pages PDF (already reversed) saved as: {output_back_path}\")\n",
455
+ "\n",
456
+ " except Exception as e:\n",
457
+ " print(f\"Error during PDF processing: {str(e)}\")\n",
458
+ "\n",
459
+ "# Example usage\n",
460
+ "pdf_path = \"input.pdf\" # Replace with your input PDF path\n",
461
+ "output_front_path = \"booklet_front.pdf\"\n",
462
+ "output_back_path = \"booklet_back.pdf\"\n",
463
+ "create_booklet_with_two_pages_per_sheet(pdf_path, output_front_path, output_back_path)"
464
+ ]
465
+ },
466
+ {
467
+ "cell_type": "markdown",
468
+ "metadata": {},
469
+ "source": [
470
+ "# With UI"
471
+ ]
472
+ },
473
+ {
474
+ "cell_type": "code",
475
+ "execution_count": 1,
476
+ "metadata": {},
477
+ "outputs": [
478
+ {
479
+ "name": "stderr",
480
+ "output_type": "stream",
481
+ "text": [
482
+ "/Users/nicoaspra/Documents/Professor/Jupyter Notebook/venv/lib/python3.12/site-packages/tqdm/auto.py:21: TqdmWarning: IProgress not found. Please update jupyter and ipywidgets. See https://ipywidgets.readthedocs.io/en/stable/user_install.html\n",
483
+ " from .autonotebook import tqdm as notebook_tqdm\n"
484
+ ]
485
+ },
486
+ {
487
+ "name": "stdout",
488
+ "output_type": "stream",
489
+ "text": [
490
+ "Running on local URL: http://127.0.0.1:7860\n",
491
+ "\n",
492
+ "To create a public link, set `share=True` in `launch()`.\n"
493
+ ]
494
+ },
495
+ {
496
+ "data": {
497
+ "text/html": [
498
+ "<div><iframe src=\"http://127.0.0.1:7860/\" width=\"100%\" height=\"500\" allow=\"autoplay; camera; microphone; clipboard-read; clipboard-write;\" frameborder=\"0\" allowfullscreen></iframe></div>"
499
+ ],
500
+ "text/plain": [
501
+ "<IPython.core.display.HTML object>"
502
+ ]
503
+ },
504
+ "metadata": {},
505
+ "output_type": "display_data"
506
+ },
507
+ {
508
+ "data": {
509
+ "text/plain": []
510
+ },
511
+ "execution_count": 1,
512
+ "metadata": {},
513
+ "output_type": "execute_result"
514
+ },
515
+ {
516
+ "name": "stderr",
517
+ "output_type": "stream",
518
+ "text": [
519
+ "/Users/nicoaspra/Documents/Professor/Jupyter Notebook/venv/lib/python3.12/site-packages/gradio/analytics.py:106: UserWarning: IMPORTANT: You are using gradio version 4.37.1, however version 5.0.1 is available, please upgrade. \n",
520
+ "--------\n",
521
+ " warnings.warn(\n"
522
+ ]
523
+ }
524
+ ],
525
+ "source": [
526
+ "import tempfile\n",
527
+ "import shutil\n",
528
+ "import gradio as gr\n",
529
+ "from PyPDF2 import PdfReader, PdfWriter, PageObject\n",
530
+ "\n",
531
+ "def merge_two_pages_on_one(page1, page2):\n",
532
+ " \"\"\"\n",
533
+ " Merges two PDF pages into a single PDF page (side by side).\n",
534
+ " \"\"\"\n",
535
+ " width = page1.mediabox.width + page2.mediabox.width\n",
536
+ " height = max(page1.mediabox.height, page2.mediabox.height)\n",
537
+ " \n",
538
+ " new_page = PageObject.create_blank_page(width=width, height=height)\n",
539
+ " new_page.merge_page(page1)\n",
540
+ "\n",
541
+ " page2_content = PageObject.create_blank_page(width=width, height=height)\n",
542
+ " page2_content.merge_page(page2)\n",
543
+ " page2_content.add_transformation([1, 0, 0, 1, page1.mediabox.width, 0])\n",
544
+ " new_page.merge_page(page2_content)\n",
545
+ "\n",
546
+ " return new_page\n",
547
+ "\n",
548
+ "def create_booklet_with_two_pages_per_sheet(pdf_file):\n",
549
+ " try:\n",
550
+ " # Step 1: Read the PDF from the uploaded file\n",
551
+ " reader = PdfReader(pdf_file)\n",
552
+ " total_pages = len(reader.pages)\n",
553
+ "\n",
554
+ " # Step 2: Ensure the number of pages is divisible by 4 by adding blank pages if needed\n",
555
+ " writer = PdfWriter()\n",
556
+ " for page in reader.pages:\n",
557
+ " writer.add_page(page)\n",
558
+ "\n",
559
+ " remainder = total_pages % 4\n",
560
+ " blank_pages_needed = (4 - remainder) if remainder > 0 else 0\n",
561
+ "\n",
562
+ " for _ in range(blank_pages_needed):\n",
563
+ " writer.add_blank_page()\n",
564
+ "\n",
565
+ " with tempfile.NamedTemporaryFile(delete=False, suffix=\".pdf\") as temp_pdf_file:\n",
566
+ " writer.write(temp_pdf_file)\n",
567
+ " temp_pdf_path = temp_pdf_file.name\n",
568
+ "\n",
569
+ " modified_reader = PdfReader(temp_pdf_path)\n",
570
+ " total_pages = len(modified_reader.pages)\n",
571
+ "\n",
572
+ " front_writer = PdfWriter()\n",
573
+ " back_writer = PdfWriter()\n",
574
+ "\n",
575
+ " for i in range(total_pages // 4):\n",
576
+ " page1 = modified_reader.pages[total_pages - 1 - (2 * i)]\n",
577
+ " page2 = modified_reader.pages[2 * i]\n",
578
+ " merged_page = merge_two_pages_on_one(page1, page2)\n",
579
+ " front_writer.add_page(merged_page)\n",
580
+ "\n",
581
+ " for i in reversed(range(total_pages // 4)):\n",
582
+ " page1 = modified_reader.pages[2 * i + 1]\n",
583
+ " page2 = modified_reader.pages[total_pages - 2 - (2 * i)]\n",
584
+ " merged_page = merge_two_pages_on_one(page1, page2)\n",
585
+ " back_writer.add_page(merged_page)\n",
586
+ "\n",
587
+ " # Save the front and back files with fixed names\n",
588
+ " front_pdf_path = \"booklet_front.pdf\"\n",
589
+ " back_pdf_path = \"booklet_back.pdf\"\n",
590
+ "\n",
591
+ " with open(front_pdf_path, \"wb\") as front_pdf_file:\n",
592
+ " front_writer.write(front_pdf_file)\n",
593
+ "\n",
594
+ " with open(back_pdf_path, \"wb\") as back_pdf_file:\n",
595
+ " back_writer.write(back_pdf_file)\n",
596
+ "\n",
597
+ " return front_pdf_path, back_pdf_path\n",
598
+ "\n",
599
+ " except Exception as e:\n",
600
+ " return str(e), None\n",
601
+ "\n",
602
+ "# Gradio UI\n",
603
+ "def gradio_interface():\n",
604
+ " with gr.Blocks() as demo:\n",
605
+ " gr.Markdown(\"## Booklet Creator\")\n",
606
+ " \n",
607
+ " with gr.Row():\n",
608
+ " pdf_input = gr.File(label=\"Upload PDF\", type=\"filepath\", file_types=[\".pdf\"])\n",
609
+ " \n",
610
+ " with gr.Row():\n",
611
+ " generate_button = gr.Button(\"Generate Booklet\")\n",
612
+ " \n",
613
+ " with gr.Row():\n",
614
+ " front_pdf_output = gr.File(label=\"Download Booklet Front PDF\")\n",
615
+ " back_pdf_output = gr.File(label=\"Download Booklet Back PDF\")\n",
616
+ " \n",
617
+ " # Button action\n",
618
+ " generate_button.click(\n",
619
+ " create_booklet_with_two_pages_per_sheet,\n",
620
+ " inputs=[pdf_input],\n",
621
+ " outputs=[front_pdf_output, back_pdf_output]\n",
622
+ " )\n",
623
+ "\n",
624
+ " gr.Markdown(\"---\")\n",
625
+ " gr.Markdown(\"Developed by **Aspra, N.**\")\n",
626
+ " \n",
627
+ " return demo\n",
628
+ "\n",
629
+ "# Run the Gradio interface\n",
630
+ "gradio_interface().launch()"
631
+ ]
632
+ },
633
+ {
634
+ "cell_type": "markdown",
635
+ "metadata": {},
636
+ "source": [
637
+ "## with UI and reversed front and back"
638
+ ]
639
+ },
640
+ {
641
+ "cell_type": "code",
642
+ "execution_count": 2,
643
+ "metadata": {},
644
+ "outputs": [
645
+ {
646
+ "name": "stdout",
647
+ "output_type": "stream",
648
+ "text": [
649
+ "* Running on local URL: http://127.0.0.1:7860\n",
650
+ "\n",
651
+ "To create a public link, set `share=True` in `launch()`.\n"
652
+ ]
653
+ },
654
+ {
655
+ "data": {
656
+ "text/html": [
657
+ "<div><iframe src=\"http://127.0.0.1:7860/\" width=\"100%\" height=\"500\" allow=\"autoplay; camera; microphone; clipboard-read; clipboard-write;\" frameborder=\"0\" allowfullscreen></iframe></div>"
658
+ ],
659
+ "text/plain": [
660
+ "<IPython.core.display.HTML object>"
661
+ ]
662
+ },
663
+ "metadata": {},
664
+ "output_type": "display_data"
665
+ },
666
+ {
667
+ "data": {
668
+ "text/plain": []
669
+ },
670
+ "execution_count": 2,
671
+ "metadata": {},
672
+ "output_type": "execute_result"
673
+ }
674
+ ],
675
+ "source": [
676
+ "import tempfile\n",
677
+ "import gradio as gr\n",
678
+ "from PyPDF2 import PdfReader, PdfWriter, PageObject\n",
679
+ "\n",
680
+ "def merge_two_pages_on_one(page1, page2):\n",
681
+ " \"\"\"\n",
682
+ " Merges two PDF pages into a single PDF page (side by side).\n",
683
+ " \"\"\"\n",
684
+ " width = page1.mediabox.width + page2.mediabox.width\n",
685
+ " height = max(page1.mediabox.height, page2.mediabox.height)\n",
686
+ " \n",
687
+ " new_page = PageObject.create_blank_page(width=width, height=height)\n",
688
+ " new_page.merge_page(page1)\n",
689
+ "\n",
690
+ " page2_content = PageObject.create_blank_page(width=width, height=height)\n",
691
+ " page2_content.merge_page(page2)\n",
692
+ " page2_content.add_transformation([1, 0, 0, 1, page1.mediabox.width, 0]) # x translation by page1 width\n",
693
+ "\n",
694
+ " new_page.merge_page(page2_content)\n",
695
+ "\n",
696
+ " return new_page\n",
697
+ "\n",
698
+ "def reverse_pdf(input_pdf_path, output_pdf_path):\n",
699
+ " \"\"\"\n",
700
+ " Reverses the order of pages in a PDF.\n",
701
+ " \"\"\"\n",
702
+ " try:\n",
703
+ " reader = PdfReader(input_pdf_path)\n",
704
+ " total_pages = len(reader.pages)\n",
705
+ "\n",
706
+ " writer = PdfWriter()\n",
707
+ " for page_num in reversed(range(total_pages)):\n",
708
+ " writer.add_page(reader.pages[page_num])\n",
709
+ "\n",
710
+ " with open(output_pdf_path, \"wb\") as output_file:\n",
711
+ " writer.write(output_file)\n",
712
+ "\n",
713
+ " return output_pdf_path\n",
714
+ "\n",
715
+ " except Exception as e:\n",
716
+ " return str(e)\n",
717
+ "\n",
718
+ "def create_booklet_with_two_pages_per_sheet(pdf_file):\n",
719
+ " try:\n",
720
+ " reader = PdfReader(pdf_file)\n",
721
+ " total_pages = len(reader.pages)\n",
722
+ "\n",
723
+ " writer = PdfWriter()\n",
724
+ " for page in reader.pages:\n",
725
+ " writer.add_page(page)\n",
726
+ "\n",
727
+ " remainder = total_pages % 4\n",
728
+ " blank_pages_needed = (4 - remainder) if remainder > 0 else 0\n",
729
+ "\n",
730
+ " for _ in range(blank_pages_needed):\n",
731
+ " writer.add_blank_page()\n",
732
+ "\n",
733
+ " with tempfile.NamedTemporaryFile(delete=False, suffix=\".pdf\") as temp_pdf_file:\n",
734
+ " writer.write(temp_pdf_file)\n",
735
+ " temp_pdf_path = temp_pdf_file.name\n",
736
+ "\n",
737
+ " modified_reader = PdfReader(temp_pdf_path)\n",
738
+ " total_pages = len(modified_reader.pages)\n",
739
+ "\n",
740
+ " front_writer = PdfWriter()\n",
741
+ " back_writer = PdfWriter()\n",
742
+ "\n",
743
+ " # Create front pages\n",
744
+ " for i in range(total_pages // 4):\n",
745
+ " page1 = modified_reader.pages[total_pages - 1 - (2 * i)]\n",
746
+ " page2 = modified_reader.pages[2 * i]\n",
747
+ " merged_page = merge_two_pages_on_one(page1, page2)\n",
748
+ " front_writer.add_page(merged_page)\n",
749
+ "\n",
750
+ " # Create back pages\n",
751
+ " for i in reversed(range(total_pages // 4)):\n",
752
+ " page1 = modified_reader.pages[2 * i + 1]\n",
753
+ " page2 = modified_reader.pages[total_pages - 2 - (2 * i)]\n",
754
+ " merged_page = merge_two_pages_on_one(page1, page2)\n",
755
+ " back_writer.add_page(merged_page)\n",
756
+ "\n",
757
+ " # Save front and back PDFs\n",
758
+ " front_pdf_path = \"booklet_front.pdf\"\n",
759
+ " back_pdf_path = \"booklet_back.pdf\"\n",
760
+ " reversed_back_pdf_path = \"reversed_booklet_back.pdf\"\n",
761
+ " reversed_front_pdf_path = \"reversed_booklet_front.pdf\"\n",
762
+ "\n",
763
+ " with open(front_pdf_path, \"wb\") as front_file:\n",
764
+ " front_writer.write(front_file)\n",
765
+ "\n",
766
+ " with open(back_pdf_path, \"wb\") as back_file:\n",
767
+ " back_writer.write(back_file)\n",
768
+ "\n",
769
+ " # Reverse the back and front PDF pages and save\n",
770
+ " reverse_pdf(back_pdf_path, reversed_back_pdf_path)\n",
771
+ " reverse_pdf(front_pdf_path, reversed_front_pdf_path)\n",
772
+ "\n",
773
+ " return front_pdf_path, back_pdf_path, reversed_front_pdf_path, reversed_back_pdf_path\n",
774
+ "\n",
775
+ " except Exception as e:\n",
776
+ " return str(e), None, None, None\n",
777
+ "\n",
778
+ "# Gradio UI\n",
779
+ "def gradio_interface():\n",
780
+ " with gr.Blocks() as demo:\n",
781
+ " gr.Markdown(\"## Booklet Creator and Page Reverser\")\n",
782
+ "\n",
783
+ " with gr.Row():\n",
784
+ " pdf_input = gr.File(label=\"Upload PDF\", type=\"filepath\", file_types=[\".pdf\"])\n",
785
+ "\n",
786
+ " with gr.Row():\n",
787
+ " generate_button = gr.Button(\"Generate Booklet\")\n",
788
+ "\n",
789
+ " with gr.Row():\n",
790
+ " front_pdf_output = gr.File(label=\"Download Booklet Front PDF\")\n",
791
+ " back_pdf_output = gr.File(label=\"Download Booklet Back PDF\")\n",
792
+ " with gr.Row():\n",
793
+ " reversed_front_pdf_output = gr.File(label=\"Download Reversed Booklet Front PDF\")\n",
794
+ " reversed_back_pdf_output = gr.File(label=\"Download Reversed Booklet Back PDF\")\n",
795
+ "\n",
796
+ " # Button action\n",
797
+ " generate_button.click(\n",
798
+ " create_booklet_with_two_pages_per_sheet,\n",
799
+ " inputs=[pdf_input],\n",
800
+ " outputs=[front_pdf_output, back_pdf_output, reversed_front_pdf_output, reversed_back_pdf_output]\n",
801
+ " )\n",
802
+ "\n",
803
+ " return demo\n",
804
+ "\n",
805
+ "# Run the Gradio interface\n",
806
+ "gradio_interface().launch()"
807
+ ]
808
+ },
809
+ {
810
+ "cell_type": "markdown",
811
+ "metadata": {},
812
+ "source": [
813
+ "# Streamlit"
814
+ ]
815
+ },
816
+ {
817
+ "cell_type": "code",
818
+ "execution_count": 3,
819
+ "metadata": {},
820
+ "outputs": [
821
+ {
822
+ "name": "stderr",
823
+ "output_type": "stream",
824
+ "text": [
825
+ "2025-01-09 22:03:05.797 WARNING streamlit.runtime.scriptrunner_utils.script_run_context: Thread 'MainThread': missing ScriptRunContext! This warning can be ignored when running in bare mode.\n",
826
+ "2025-01-09 22:03:05.835 \n",
827
+ " \u001b[33m\u001b[1mWarning:\u001b[0m to view this Streamlit app on a browser, run it with the following\n",
828
+ " command:\n",
829
+ "\n",
830
+ " streamlit run /Users/nicoaspra/Documents/Professor/Jupyter Notebook/.venv/lib/python3.13/site-packages/ipykernel_launcher.py [ARGUMENTS]\n",
831
+ "2025-01-09 22:03:05.835 Thread 'MainThread': missing ScriptRunContext! This warning can be ignored when running in bare mode.\n",
832
+ "2025-01-09 22:03:05.835 Thread 'MainThread': missing ScriptRunContext! This warning can be ignored when running in bare mode.\n",
833
+ "2025-01-09 22:03:05.836 Thread 'MainThread': missing ScriptRunContext! This warning can be ignored when running in bare mode.\n",
834
+ "2025-01-09 22:03:05.836 Thread 'MainThread': missing ScriptRunContext! This warning can be ignored when running in bare mode.\n",
835
+ "2025-01-09 22:03:05.836 Thread 'MainThread': missing ScriptRunContext! This warning can be ignored when running in bare mode.\n",
836
+ "2025-01-09 22:03:05.836 Thread 'MainThread': missing ScriptRunContext! This warning can be ignored when running in bare mode.\n"
837
+ ]
838
+ }
839
+ ],
840
+ "source": [
841
+ "import tempfile\n",
842
+ "from PyPDF2 import PdfReader, PdfWriter, PageObject\n",
843
+ "import streamlit as st\n",
844
+ "\n",
845
+ "def merge_two_pages_on_one(page1, page2):\n",
846
+ " \"\"\"\n",
847
+ " Merges two PDF pages into a single PDF page (side by side).\n",
848
+ " \"\"\"\n",
849
+ " width = page1.mediabox.width + page2.mediabox.width\n",
850
+ " height = max(page1.mediabox.height, page2.mediabox.height)\n",
851
+ " \n",
852
+ " new_page = PageObject.create_blank_page(width=width, height=height)\n",
853
+ " new_page.merge_page(page1)\n",
854
+ "\n",
855
+ " page2_content = PageObject.create_blank_page(width=width, height=height)\n",
856
+ " page2_content.merge_page(page2)\n",
857
+ " page2_content.add_transformation([1, 0, 0, 1, page1.mediabox.width, 0]) # x translation by page1 width\n",
858
+ "\n",
859
+ " new_page.merge_page(page2_content)\n",
860
+ "\n",
861
+ " return new_page\n",
862
+ "\n",
863
+ "def reverse_pdf(input_pdf_path, output_pdf_path):\n",
864
+ " \"\"\"\n",
865
+ " Reverses the order of pages in a PDF.\n",
866
+ " \"\"\"\n",
867
+ " try:\n",
868
+ " reader = PdfReader(input_pdf_path)\n",
869
+ " total_pages = len(reader.pages)\n",
870
+ "\n",
871
+ " writer = PdfWriter()\n",
872
+ " for page_num in reversed(range(total_pages)):\n",
873
+ " writer.add_page(reader.pages[page_num])\n",
874
+ "\n",
875
+ " with open(output_pdf_path, \"wb\") as output_file:\n",
876
+ " writer.write(output_file)\n",
877
+ "\n",
878
+ " return output_pdf_path\n",
879
+ "\n",
880
+ " except Exception as e:\n",
881
+ " return str(e)\n",
882
+ "\n",
883
+ "def create_booklet_with_two_pages_per_sheet(pdf_file):\n",
884
+ " try:\n",
885
+ " reader = PdfReader(pdf_file)\n",
886
+ " total_pages = len(reader.pages)\n",
887
+ "\n",
888
+ " writer = PdfWriter()\n",
889
+ " for page in reader.pages:\n",
890
+ " writer.add_page(page)\n",
891
+ "\n",
892
+ " remainder = total_pages % 4\n",
893
+ " blank_pages_needed = (4 - remainder) if remainder > 0 else 0\n",
894
+ "\n",
895
+ " for _ in range(blank_pages_needed):\n",
896
+ " writer.add_blank_page()\n",
897
+ "\n",
898
+ " with tempfile.NamedTemporaryFile(delete=False, suffix=\".pdf\") as temp_pdf_file:\n",
899
+ " writer.write(temp_pdf_file)\n",
900
+ " temp_pdf_path = temp_pdf_file.name\n",
901
+ "\n",
902
+ " modified_reader = PdfReader(temp_pdf_path)\n",
903
+ " total_pages = len(modified_reader.pages)\n",
904
+ "\n",
905
+ " front_writer = PdfWriter()\n",
906
+ " back_writer = PdfWriter()\n",
907
+ "\n",
908
+ " # Create front pages\n",
909
+ " for i in range(total_pages // 4):\n",
910
+ " page1 = modified_reader.pages[total_pages - 1 - (2 * i)]\n",
911
+ " page2 = modified_reader.pages[2 * i]\n",
912
+ " merged_page = merge_two_pages_on_one(page1, page2)\n",
913
+ " front_writer.add_page(merged_page)\n",
914
+ "\n",
915
+ " # Create back pages\n",
916
+ " for i in reversed(range(total_pages // 4)):\n",
917
+ " page1 = modified_reader.pages[2 * i + 1]\n",
918
+ " page2 = modified_reader.pages[total_pages - 2 - (2 * i)]\n",
919
+ " merged_page = merge_two_pages_on_one(page1, page2)\n",
920
+ " back_writer.add_page(merged_page)\n",
921
+ "\n",
922
+ " # Save front and back PDFs\n",
923
+ " front_pdf_path = \"booklet_front.pdf\"\n",
924
+ " back_pdf_path = \"booklet_back.pdf\"\n",
925
+ " reversed_back_pdf_path = \"reversed_booklet_back.pdf\"\n",
926
+ " reversed_front_pdf_path = \"reversed_booklet_front.pdf\"\n",
927
+ "\n",
928
+ " with open(front_pdf_path, \"wb\") as front_file:\n",
929
+ " front_writer.write(front_file)\n",
930
+ "\n",
931
+ " with open(back_pdf_path, \"wb\") as back_file:\n",
932
+ " back_writer.write(back_file)\n",
933
+ "\n",
934
+ " # Reverse the back and front PDF pages and save\n",
935
+ " reverse_pdf(back_pdf_path, reversed_back_pdf_path)\n",
936
+ " reverse_pdf(front_pdf_path, reversed_front_pdf_path)\n",
937
+ "\n",
938
+ " return front_pdf_path, back_pdf_path, reversed_front_pdf_path, reversed_back_pdf_path\n",
939
+ "\n",
940
+ " except Exception as e:\n",
941
+ " return str(e), None, None, None\n",
942
+ "\n",
943
+ "# Streamlit UI\n",
944
+ "st.title(\"Booklet Creator and Page Reverser\")\n",
945
+ "\n",
946
+ "uploaded_file = st.file_uploader(\"Upload PDF\", type=[\"pdf\"])\n",
947
+ "\n",
948
+ "if uploaded_file is not None:\n",
949
+ " with tempfile.NamedTemporaryFile(delete=False, suffix=\".pdf\") as temp_pdf:\n",
950
+ " temp_pdf.write(uploaded_file.read())\n",
951
+ " temp_pdf_path = temp_pdf.name\n",
952
+ "\n",
953
+ " if st.button(\"Generate Booklet\"):\n",
954
+ " front_pdf_path, back_pdf_path, reversed_front_pdf_path, reversed_back_pdf_path = create_booklet_with_two_pages_per_sheet(temp_pdf_path)\n",
955
+ "\n",
956
+ " if front_pdf_path and back_pdf_path:\n",
957
+ " st.success(\"Booklet generated successfully!\")\n",
958
+ " st.download_button(\"Download Booklet Front PDF\", open(front_pdf_path, \"rb\"), \"booklet_front.pdf\")\n",
959
+ " st.download_button(\"Download Booklet Back PDF\", open(back_pdf_path, \"rb\"), \"booklet_back.pdf\")\n",
960
+ " st.download_button(\"Download Reversed Booklet Front PDF\", open(reversed_front_pdf_path, \"rb\"), \"reversed_booklet_front.pdf\")\n",
961
+ " st.download_button(\"Download Reversed Booklet Back PDF\", open(reversed_back_pdf_path, \"rb\"), \"reversed_booklet_back.pdf\")\n",
962
+ " else:\n",
963
+ " st.error(\"An error occurred while generating the booklet.\")"
964
+ ]
965
+ }
966
+ ],
967
+ "metadata": {
968
+ "kernelspec": {
969
+ "display_name": ".venv",
970
+ "language": "python",
971
+ "name": "python3"
972
+ },
973
+ "language_info": {
974
+ "codemirror_mode": {
975
+ "name": "ipython",
976
+ "version": 3
977
+ },
978
+ "file_extension": ".py",
979
+ "mimetype": "text/x-python",
980
+ "name": "python",
981
+ "nbconvert_exporter": "python",
982
+ "pygments_lexer": "ipython3",
983
+ "version": "3.13.0"
984
+ }
985
+ },
986
+ "nbformat": 4,
987
+ "nbformat_minor": 2
988
+ }
README.md CHANGED
@@ -1,11 +1,11 @@
1
  ---
2
- title: Create PDF Booklet
3
- emoji: 🦀
4
  colorFrom: yellow
5
  colorTo: green
6
- sdk: gradio
7
- sdk_version: 5.0.0
8
- app_file: app.py
9
  pinned: false
10
  ---
11
 
 
1
  ---
2
+ title: Print-Ready PDF Booklet Generator
3
+ emoji: 📖
4
  colorFrom: yellow
5
  colorTo: green
6
+ sdk: streamlit
7
+ sdk_version: 1.40.1
8
+ app_file: ui_PDF_booklet.py
9
  pinned: false
10
  ---
11
 
app.py DELETED
@@ -1,131 +0,0 @@
1
- import tempfile
2
- import gradio as gr
3
- from PyPDF2 import PdfReader, PdfWriter, PageObject
4
-
5
- def merge_two_pages_on_one(page1, page2):
6
- """
7
- Merges two PDF pages into a single PDF page (side by side).
8
- """
9
- width = page1.mediabox.width + page2.mediabox.width
10
- height = max(page1.mediabox.height, page2.mediabox.height)
11
-
12
- new_page = PageObject.create_blank_page(width=width, height=height)
13
- new_page.merge_page(page1)
14
-
15
- page2_content = PageObject.create_blank_page(width=width, height=height)
16
- page2_content.merge_page(page2)
17
- page2_content.add_transformation([1, 0, 0, 1, page1.mediabox.width, 0]) # x translation by page1 width
18
-
19
- new_page.merge_page(page2_content)
20
-
21
- return new_page
22
-
23
- def reverse_pdf(input_pdf_path, output_pdf_path):
24
- """
25
- Reverses the order of pages in a PDF.
26
- """
27
- try:
28
- reader = PdfReader(input_pdf_path)
29
- total_pages = len(reader.pages)
30
-
31
- writer = PdfWriter()
32
- for page_num in reversed(range(total_pages)):
33
- writer.add_page(reader.pages[page_num])
34
-
35
- with open(output_pdf_path, "wb") as output_file:
36
- writer.write(output_file)
37
-
38
- return output_pdf_path
39
-
40
- except Exception as e:
41
- return str(e)
42
-
43
- def create_booklet_with_two_pages_per_sheet(pdf_file):
44
- try:
45
- reader = PdfReader(pdf_file)
46
- total_pages = len(reader.pages)
47
-
48
- writer = PdfWriter()
49
- for page in reader.pages:
50
- writer.add_page(page)
51
-
52
- remainder = total_pages % 4
53
- blank_pages_needed = (4 - remainder) if remainder > 0 else 0
54
-
55
- for _ in range(blank_pages_needed):
56
- writer.add_blank_page()
57
-
58
- with tempfile.NamedTemporaryFile(delete=False, suffix=".pdf") as temp_pdf_file:
59
- writer.write(temp_pdf_file)
60
- temp_pdf_path = temp_pdf_file.name
61
-
62
- modified_reader = PdfReader(temp_pdf_path)
63
- total_pages = len(modified_reader.pages)
64
-
65
- front_writer = PdfWriter()
66
- back_writer = PdfWriter()
67
-
68
- # Create front pages
69
- for i in range(total_pages // 4):
70
- page1 = modified_reader.pages[total_pages - 1 - (2 * i)]
71
- page2 = modified_reader.pages[2 * i]
72
- merged_page = merge_two_pages_on_one(page1, page2)
73
- front_writer.add_page(merged_page)
74
-
75
- # Create back pages
76
- for i in reversed(range(total_pages // 4)):
77
- page1 = modified_reader.pages[2 * i + 1]
78
- page2 = modified_reader.pages[total_pages - 2 - (2 * i)]
79
- merged_page = merge_two_pages_on_one(page1, page2)
80
- back_writer.add_page(merged_page)
81
-
82
- # Save front and back PDFs
83
- front_pdf_path = "booklet_front.pdf"
84
- back_pdf_path = "booklet_back.pdf"
85
- reversed_back_pdf_path = "reversed_booklet_back.pdf"
86
- reversed_front_pdf_path = "reversed_booklet_front.pdf"
87
-
88
- with open(front_pdf_path, "wb") as front_file:
89
- front_writer.write(front_file)
90
-
91
- with open(back_pdf_path, "wb") as back_file:
92
- back_writer.write(back_file)
93
-
94
- # Reverse the back and front PDF pages and save
95
- reverse_pdf(back_pdf_path, reversed_back_pdf_path)
96
- reverse_pdf(front_pdf_path, reversed_front_pdf_path)
97
-
98
- return front_pdf_path, back_pdf_path, reversed_front_pdf_path, reversed_back_pdf_path
99
-
100
- except Exception as e:
101
- return str(e), None, None, None
102
-
103
- # Gradio UI
104
- def gradio_interface():
105
- with gr.Blocks() as demo:
106
- gr.Markdown("## Booklet Creator and Page Reverser")
107
-
108
- with gr.Row():
109
- pdf_input = gr.File(label="Upload PDF", type="filepath", file_types=[".pdf"])
110
-
111
- with gr.Row():
112
- generate_button = gr.Button("Generate Booklet")
113
-
114
- with gr.Row():
115
- front_pdf_output = gr.File(label="Download Booklet Front PDF")
116
- back_pdf_output = gr.File(label="Download Booklet Back PDF")
117
- with gr.Row():
118
- reversed_front_pdf_output = gr.File(label="Download Reversed Booklet Front PDF")
119
- reversed_back_pdf_output = gr.File(label="Download Reversed Booklet Back PDF")
120
-
121
- # Button action
122
- generate_button.click(
123
- create_booklet_with_two_pages_per_sheet,
124
- inputs=[pdf_input],
125
- outputs=[front_pdf_output, back_pdf_output, reversed_front_pdf_output, reversed_back_pdf_output]
126
- )
127
-
128
- return demo
129
-
130
- # Run the Gradio interface
131
- gradio_interface().launch()
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
ui_PDF_booklet.py ADDED
@@ -0,0 +1,163 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import io
2
+ from PyPDF2 import PdfReader, PdfWriter, PageObject
3
+ import streamlit as st
4
+
5
+ def validate_pdf(file_stream):
6
+ """
7
+ Validates if the file is a proper PDF by trying to read it.
8
+ """
9
+ try:
10
+ PdfReader(file_stream)
11
+ return True
12
+ except Exception:
13
+ return False
14
+
15
+ def merge_two_pages_on_one(page1, page2):
16
+ """
17
+ Merges two PDF pages into a single PDF page (side by side).
18
+ """
19
+ width = page1.mediabox.width + page2.mediabox.width
20
+ height = max(page1.mediabox.height, page2.mediabox.height)
21
+
22
+ new_page = PageObject.create_blank_page(width=width, height=height)
23
+ new_page.merge_page(page1)
24
+
25
+ page2_content = PageObject.create_blank_page(width=width, height=height)
26
+ page2_content.merge_page(page2)
27
+ page2_content.add_transformation([1, 0, 0, 1, page1.mediabox.width, 0]) # x translation by page1 width
28
+
29
+ new_page.merge_page(page2_content)
30
+
31
+ return new_page
32
+
33
+ def reverse_pdf(input_pdf_stream):
34
+ """
35
+ Reverses the order of pages in a PDF and returns the reversed file stream.
36
+ """
37
+ reader = PdfReader(input_pdf_stream)
38
+ writer = PdfWriter()
39
+
40
+ for page_num in reversed(range(len(reader.pages))):
41
+ writer.add_page(reader.pages[page_num])
42
+
43
+ output_stream = io.BytesIO()
44
+ writer.write(output_stream)
45
+ output_stream.seek(0)
46
+ return output_stream
47
+
48
+ def create_booklet_with_two_pages_per_sheet(pdf_file):
49
+ """
50
+ Processes the uploaded PDF to create a booklet with two pages per sheet.
51
+ """
52
+ input_pdf_stream = io.BytesIO(pdf_file.read())
53
+
54
+ # Validate the PDF file
55
+ if not validate_pdf(input_pdf_stream):
56
+ raise ValueError("Uploaded file is not a valid PDF.")
57
+
58
+ input_pdf_stream.seek(0)
59
+ reader = PdfReader(input_pdf_stream)
60
+ total_pages = len(reader.pages)
61
+
62
+ writer = PdfWriter()
63
+ for page in reader.pages:
64
+ writer.add_page(page)
65
+
66
+ remainder = total_pages % 4
67
+ blank_pages_needed = (4 - remainder) if remainder > 0 else 0
68
+
69
+ for _ in range(blank_pages_needed):
70
+ writer.add_blank_page()
71
+
72
+ temp_pdf_stream = io.BytesIO()
73
+ writer.write(temp_pdf_stream)
74
+ temp_pdf_stream.seek(0)
75
+
76
+ modified_reader = PdfReader(temp_pdf_stream)
77
+ total_pages = len(modified_reader.pages)
78
+
79
+ front_writer = PdfWriter()
80
+ back_writer = PdfWriter()
81
+
82
+ # Create front pages
83
+ for i in range(total_pages // 4):
84
+ page1 = modified_reader.pages[total_pages - 1 - (2 * i)]
85
+ page2 = modified_reader.pages[2 * i]
86
+ merged_page = merge_two_pages_on_one(page1, page2)
87
+ front_writer.add_page(merged_page)
88
+
89
+ # Create back pages
90
+ for i in reversed(range(total_pages // 4)):
91
+ page1 = modified_reader.pages[2 * i + 1]
92
+ page2 = modified_reader.pages[total_pages - 2 - (2 * i)]
93
+ merged_page = merge_two_pages_on_one(page1, page2)
94
+ back_writer.add_page(merged_page)
95
+
96
+ # Prepare output streams
97
+ front_pdf_stream = io.BytesIO()
98
+ back_pdf_stream = io.BytesIO()
99
+ reversed_front_pdf_stream = io.BytesIO()
100
+ reversed_back_pdf_stream = io.BytesIO()
101
+
102
+ front_writer.write(front_pdf_stream)
103
+ back_writer.write(back_pdf_stream)
104
+
105
+ front_pdf_stream.seek(0)
106
+ back_pdf_stream.seek(0)
107
+
108
+ # Reverse PDFs
109
+ reversed_front_pdf_stream = reverse_pdf(front_pdf_stream)
110
+ reversed_back_pdf_stream = reverse_pdf(back_pdf_stream)
111
+
112
+ return front_pdf_stream, back_pdf_stream, reversed_front_pdf_stream, reversed_back_pdf_stream
113
+
114
+ # Streamlit UI
115
+ # st.title("Booklet Generator: Create Print-Ready PDFs")
116
+ st.title("Print-Ready PDF Booklet Generator")
117
+ st.write("""
118
+ This tool simplifies the process of creating booklet-style PDFs. It generates booklet-ready files designed for back-to-back printing. Additionally, it provides multiple versions, including reversed formats, to accommodate all printer configurations seamlessly.
119
+
120
+ Ideal for printing books, modules, and other documents, this tool specifically helps save paper while ensuring efficient, professional-quality results.
121
+ """)
122
+
123
+ # Initialize session state
124
+ if "pdf_outputs" not in st.session_state:
125
+ st.session_state["pdf_outputs"] = None
126
+
127
+ uploaded_file = st.file_uploader("Upload PDF", type=["pdf"])
128
+
129
+ if uploaded_file is not None and st.button("Generate Booklet"):
130
+ with st.spinner("Processing..."):
131
+ try:
132
+ front_pdf, back_pdf, reversed_front_pdf, reversed_back_pdf = create_booklet_with_two_pages_per_sheet(uploaded_file)
133
+ st.session_state["pdf_outputs"] = {
134
+ "front_pdf": front_pdf,
135
+ "back_pdf": back_pdf,
136
+ "reversed_front_pdf": reversed_front_pdf,
137
+ "reversed_back_pdf": reversed_back_pdf,
138
+ }
139
+ st.success("Booklet generated successfully!")
140
+ except Exception as e:
141
+ st.error(f"Error: {str(e)}")
142
+
143
+ # Add a separator before downloads
144
+ st.divider() # Adds a horizontal line
145
+
146
+ # Display download buttons if PDFs are available
147
+ if st.session_state["pdf_outputs"] is not None:
148
+ outputs = st.session_state["pdf_outputs"]
149
+ st.download_button("Download Booklet Front PDF", outputs["front_pdf"], "booklet_front.pdf", "application/pdf")
150
+ st.download_button("Download Booklet Back PDF", outputs["back_pdf"], "booklet_back.pdf", "application/pdf")
151
+ st.download_button("Download Reversed Booklet Front PDF", outputs["reversed_front_pdf"], "reversed_booklet_front.pdf", "application/pdf")
152
+ st.download_button("Download Reversed Booklet Back PDF", outputs["reversed_back_pdf"], "reversed_booklet_back.pdf", "application/pdf")
153
+
154
+
155
+ # Add footer
156
+ st.markdown("""
157
+ ---
158
+ <div style="text-align: center; font-size: small;">
159
+ Developed by <strong>Aspra, N.</strong> | © 2025 All Rights Reserved
160
+ </div>
161
+ """, unsafe_allow_html=True)
162
+
163
+ # streamlit run 241009_PDF_booklet/ui_PDF_booklet.py