quantumpdf 0.2.0__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.
- quantumpdf/__init__.py +0 -0
- quantumpdf/application/__init__.py +0 -0
- quantumpdf/application/config.py +7 -0
- quantumpdf/common/__init__.py +0 -0
- quantumpdf/common/basic_functions/__init__.py +0 -0
- quantumpdf/common/basic_functions/os_funs.py +15 -0
- quantumpdf/common/pdf_functions/__init__.py +0 -0
- quantumpdf/common/pdf_functions/img2pdf_funs.py +31 -0
- quantumpdf/common/pdf_functions/pikepdf_funs.py +12 -0
- quantumpdf/common/pdf_functions/pypdf2_funs.py +43 -0
- quantumpdf/common/pdf_functions/use_pdf2image.py +42 -0
- quantumpdf/gui/__init__.py +0 -0
- quantumpdf/gui/gui.py +224 -0
- quantumpdf/help.py +23 -0
- quantumpdf/main.py +61 -0
- quantumpdf/sections/__init__.py +0 -0
- quantumpdf/sections/image_to_pdf/__init__.py +0 -0
- quantumpdf/sections/image_to_pdf/main.py +17 -0
- quantumpdf/sections/pdf_to_image/__init__.py +0 -0
- quantumpdf/sections/pdf_to_image/gui_support.py +0 -0
- quantumpdf/sections/pdf_to_image/main.py +7 -0
- quantumpdf/version.py +16 -0
- quantumpdf-0.2.0.dist-info/METADATA +48 -0
- quantumpdf-0.2.0.dist-info/RECORD +28 -0
- quantumpdf-0.2.0.dist-info/WHEEL +5 -0
- quantumpdf-0.2.0.dist-info/entry_points.txt +2 -0
- quantumpdf-0.2.0.dist-info/licenses/LICENSE +21 -0
- quantumpdf-0.2.0.dist-info/top_level.txt +1 -0
quantumpdf/__init__.py
ADDED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
"""
|
|
2
|
+
common/basic_functions/os_funs.py
|
|
3
|
+
|
|
4
|
+
Author: Benevant Mathew
|
|
5
|
+
Date: 2026-06-21
|
|
6
|
+
"""
|
|
7
|
+
import os
|
|
8
|
+
import platform
|
|
9
|
+
|
|
10
|
+
def get_user_profile():
|
|
11
|
+
if platform.system()=='Windows':
|
|
12
|
+
out=os.environ['USERPROFILE']
|
|
13
|
+
else:
|
|
14
|
+
out=os.path.expanduser('~')
|
|
15
|
+
return(out)
|
|
File without changes
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
"""
|
|
2
|
+
common/pdf_functions/img2pdf_funs.py
|
|
3
|
+
|
|
4
|
+
Author: Benevant Mathew
|
|
5
|
+
Date: 2026-06-21
|
|
6
|
+
"""
|
|
7
|
+
import img2pdf
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
class Img2pdfCore:
|
|
11
|
+
def __init__(self):
|
|
12
|
+
"""
|
|
13
|
+
Initialize the Img2pdfCore class.
|
|
14
|
+
"""
|
|
15
|
+
pass
|
|
16
|
+
def images_to_pdf(self, image_paths, output_pdf_path):
|
|
17
|
+
"""
|
|
18
|
+
Convert a list of images into a single PDF.
|
|
19
|
+
:param image_paths: List of paths to the image files.
|
|
20
|
+
:param output_pdf_path: Path to save the generated PDF file.
|
|
21
|
+
"""
|
|
22
|
+
try:
|
|
23
|
+
# Open all image files in binary mode
|
|
24
|
+
image_files = [open(image_path, 'rb') for image_path in image_paths]
|
|
25
|
+
# Write the images to a PDF file
|
|
26
|
+
with open(output_pdf_path, 'wb') as pdf_file:
|
|
27
|
+
pdf_file.write(img2pdf.convert(image_files))
|
|
28
|
+
finally:
|
|
29
|
+
# Close all opened image files
|
|
30
|
+
for file in image_files:
|
|
31
|
+
file.close()
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
"""
|
|
2
|
+
common/pdf_functions/pikepdf_funs.py
|
|
3
|
+
|
|
4
|
+
Author: Benevant Mathew
|
|
5
|
+
Date: 2026-06-21
|
|
6
|
+
"""
|
|
7
|
+
import pikepdf
|
|
8
|
+
|
|
9
|
+
def decode_pdf(input_pdf_path,output_pdf_path,password):
|
|
10
|
+
pdf = pikepdf.open(input_pdf_path, password=password)
|
|
11
|
+
pdf.save(output_pdf_path)
|
|
12
|
+
print('function completed for {}'.format(input_pdf_path))
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
"""
|
|
2
|
+
common/pdf_functions/pypdf2_funs.py
|
|
3
|
+
|
|
4
|
+
Author: Benevant Mathew
|
|
5
|
+
Date: 2026-06-21
|
|
6
|
+
"""
|
|
7
|
+
import PyPDF2
|
|
8
|
+
|
|
9
|
+
def save_pages_of_pdf(page_nos,file,outputfile):
|
|
10
|
+
#PyPDF2
|
|
11
|
+
# page_nos should in comma seperated
|
|
12
|
+
reader = PyPDF2.PdfReader(file)
|
|
13
|
+
writer = PyPDF2.PdfWriter()
|
|
14
|
+
|
|
15
|
+
# Get the pages from the input string
|
|
16
|
+
pages=[int(x) for x in page_nos.split(',')]
|
|
17
|
+
|
|
18
|
+
# Add specified pages to the writer
|
|
19
|
+
for n in pages:
|
|
20
|
+
writer.add_page(reader.pages[n - 1]) # Use reader.pages to access the page
|
|
21
|
+
output = open(outputfile,'wb')
|
|
22
|
+
writer.write(output)
|
|
23
|
+
output.close()
|
|
24
|
+
|
|
25
|
+
def merge_pdfs(pdf_list, output_file):
|
|
26
|
+
# Create a PDF writer object
|
|
27
|
+
pdf_writer = PyPDF2.PdfWriter()
|
|
28
|
+
|
|
29
|
+
# Loop through all the PDF files
|
|
30
|
+
for pdf in pdf_list:
|
|
31
|
+
# Open the current PDF file in binary read mode
|
|
32
|
+
with open(pdf, 'rb') as file:
|
|
33
|
+
# Create a PDF reader object for the current PDF
|
|
34
|
+
pdf_reader = PyPDF2.PdfReader(file)
|
|
35
|
+
|
|
36
|
+
# Append each page of the PDF to the writer object
|
|
37
|
+
for page_num in range(len(pdf_reader.pages)):
|
|
38
|
+
page = pdf_reader.pages[page_num]
|
|
39
|
+
pdf_writer.add_page(page)
|
|
40
|
+
|
|
41
|
+
# Write the combined PDF to the output file
|
|
42
|
+
with open(output_file, 'wb') as output_pdf:
|
|
43
|
+
pdf_writer.write(output_pdf)
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
"""
|
|
2
|
+
common/pdf_functions/use_pdf2image.py
|
|
3
|
+
|
|
4
|
+
Author: Benevant Mathew
|
|
5
|
+
Date: 2026-06-21
|
|
6
|
+
"""
|
|
7
|
+
import os
|
|
8
|
+
import fitz # pymupdf
|
|
9
|
+
from PIL import Image
|
|
10
|
+
class Pdf2imageCore:
|
|
11
|
+
def __init__(self):
|
|
12
|
+
"""
|
|
13
|
+
Initialize Pdf2imageCore class.
|
|
14
|
+
"""
|
|
15
|
+
pass
|
|
16
|
+
def pdf2image_fun(self, filelist,output_dir, dpi):
|
|
17
|
+
"""
|
|
18
|
+
Convert PDF files into images.
|
|
19
|
+
|
|
20
|
+
:param filelist: List of PDF file paths (or a single file path as a string).
|
|
21
|
+
:param output_dir: Directory where the output images will be saved.
|
|
22
|
+
:param dpi: DPI (dots per inch) for image quality.
|
|
23
|
+
:return: Dictionary with PDF file paths as keys and lists of image file paths as values.
|
|
24
|
+
"""
|
|
25
|
+
if isinstance(filelist, str):
|
|
26
|
+
filelist = [filelist]
|
|
27
|
+
out_dict = {}
|
|
28
|
+
for filepath in filelist:
|
|
29
|
+
doc = fitz.open(filepath) # Open PDF using pymupdf
|
|
30
|
+
filename=os.path.basename(filepath)
|
|
31
|
+
basename = filename[:-4]
|
|
32
|
+
out_dict[filepath] = []
|
|
33
|
+
# out file dir
|
|
34
|
+
output_file_dir=os.path.join(output_dir,basename)
|
|
35
|
+
os.makedirs(output_file_dir,exist_ok=True)
|
|
36
|
+
for i, page in enumerate(doc):
|
|
37
|
+
pix = page.get_pixmap(matrix=fitz.Matrix(dpi / 72, dpi / 72)) # Adjust DPI
|
|
38
|
+
img = Image.frombytes("RGB", [pix.width, pix.height], pix.samples)
|
|
39
|
+
newpath = os.path.join(output_file_dir, f'{basename}_page_{i}.jpg')
|
|
40
|
+
img.save(newpath, 'JPEG')
|
|
41
|
+
out_dict[filepath].append(newpath)
|
|
42
|
+
return out_dict
|
|
File without changes
|
quantumpdf/gui/gui.py
ADDED
|
@@ -0,0 +1,224 @@
|
|
|
1
|
+
"""
|
|
2
|
+
gui/gui.py
|
|
3
|
+
|
|
4
|
+
Author: Benevant Mathew
|
|
5
|
+
Date: 2026-06-21
|
|
6
|
+
"""
|
|
7
|
+
import tkinter as tk
|
|
8
|
+
from pathlib import Path
|
|
9
|
+
from tkinter import filedialog, messagebox, ttk
|
|
10
|
+
|
|
11
|
+
from quantumpdf.application.config import dpi_value
|
|
12
|
+
from quantumpdf.common.pdf_functions.pikepdf_funs import decode_pdf
|
|
13
|
+
from quantumpdf.common.pdf_functions.pypdf2_funs import merge_pdfs, save_pages_of_pdf
|
|
14
|
+
from quantumpdf.sections.pdf_to_image.main import convert_pdf2images
|
|
15
|
+
from quantumpdf.version import __app_name__, __version__
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
WINDOW_TITLE = f"{__app_name__} v{__version__}"
|
|
19
|
+
WINDOW_SIZE = "780x560"
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
class QuantumPDFApp:
|
|
23
|
+
def __init__(self, root: tk.Tk):
|
|
24
|
+
self.root = root
|
|
25
|
+
self.merge_files: tuple[str, ...] = ()
|
|
26
|
+
self.subset_file = ""
|
|
27
|
+
self.decode_file = ""
|
|
28
|
+
self.convert_files: tuple[str, ...] = ()
|
|
29
|
+
|
|
30
|
+
self.root.title(WINDOW_TITLE)
|
|
31
|
+
self.root.geometry(WINDOW_SIZE)
|
|
32
|
+
self.root.minsize(720, 520)
|
|
33
|
+
|
|
34
|
+
self._configure_theme()
|
|
35
|
+
self._build_layout()
|
|
36
|
+
|
|
37
|
+
def _configure_theme(self) -> None:
|
|
38
|
+
style = ttk.Style(self.root)
|
|
39
|
+
style.theme_use("clam")
|
|
40
|
+
style.configure("App.TFrame", background="#f4f7fb")
|
|
41
|
+
style.configure("Header.TFrame", background="#172033")
|
|
42
|
+
style.configure("HeaderTitle.TLabel", background="#172033", foreground="#ffffff", font=("Arial", 22, "bold"))
|
|
43
|
+
style.configure("HeaderSubTitle.TLabel", background="#172033", foreground="#b6c2d9", font=("Arial", 10))
|
|
44
|
+
style.configure("Section.TFrame", background="#ffffff", relief="flat")
|
|
45
|
+
style.configure("SectionTitle.TLabel", background="#ffffff", foreground="#172033", font=("Arial", 15, "bold"))
|
|
46
|
+
style.configure("Body.TLabel", background="#ffffff", foreground="#263248", font=("Arial", 10))
|
|
47
|
+
style.configure("Muted.TLabel", background="#ffffff", foreground="#637089", font=("Arial", 9))
|
|
48
|
+
style.configure("Footer.TLabel", background="#f4f7fb", foreground="#637089", font=("Arial", 9))
|
|
49
|
+
style.configure("Accent.TButton", font=("Arial", 10, "bold"), padding=(12, 6))
|
|
50
|
+
style.configure("TNotebook", background="#f4f7fb", borderwidth=0)
|
|
51
|
+
style.configure("TNotebook.Tab", font=("Arial", 10, "bold"), padding=(14, 8))
|
|
52
|
+
|
|
53
|
+
def _build_layout(self) -> None:
|
|
54
|
+
container = ttk.Frame(self.root, style="App.TFrame", padding=14)
|
|
55
|
+
container.pack(fill="both", expand=True)
|
|
56
|
+
|
|
57
|
+
header = ttk.Frame(container, style="Header.TFrame", padding=(18, 14))
|
|
58
|
+
header.pack(fill="x", pady=(0, 12))
|
|
59
|
+
ttk.Label(header, text=__app_name__, style="HeaderTitle.TLabel").pack(anchor="w")
|
|
60
|
+
ttk.Label(
|
|
61
|
+
header,
|
|
62
|
+
text=f"PDF merge, subset, decode, and conversion tools • Version {__version__}",
|
|
63
|
+
style="HeaderSubTitle.TLabel",
|
|
64
|
+
).pack(anchor="w", pady=(3, 0))
|
|
65
|
+
|
|
66
|
+
self.notebook = ttk.Notebook(container)
|
|
67
|
+
self.notebook.pack(fill="both", expand=True)
|
|
68
|
+
|
|
69
|
+
self._build_merge_tab()
|
|
70
|
+
self._build_subset_tab()
|
|
71
|
+
self._build_decode_tab()
|
|
72
|
+
self._build_convert_tab()
|
|
73
|
+
|
|
74
|
+
ttk.Label(container, text=WINDOW_TITLE, style="Footer.TLabel").pack(anchor="e", pady=(8, 0))
|
|
75
|
+
|
|
76
|
+
def _make_tab(self, title: str, description: str) -> ttk.Frame:
|
|
77
|
+
tab = ttk.Frame(self.notebook, style="Section.TFrame", padding=20)
|
|
78
|
+
self.notebook.add(tab, text=title)
|
|
79
|
+
ttk.Label(tab, text=title, style="SectionTitle.TLabel").pack(anchor="w")
|
|
80
|
+
ttk.Label(tab, text=description, style="Muted.TLabel").pack(anchor="w", pady=(2, 16))
|
|
81
|
+
return tab
|
|
82
|
+
|
|
83
|
+
def _make_file_label(self, parent: ttk.Frame, empty_text: str = "No file selected") -> ttk.Label:
|
|
84
|
+
label = ttk.Label(parent, text=empty_text, style="Muted.TLabel", justify="left", wraplength=680)
|
|
85
|
+
label.pack(fill="x", pady=(8, 12))
|
|
86
|
+
return label
|
|
87
|
+
|
|
88
|
+
def _make_entry_row(self, parent: ttk.Frame, label_text: str, browse_command) -> ttk.Entry:
|
|
89
|
+
ttk.Label(parent, text=label_text, style="Body.TLabel").pack(anchor="w")
|
|
90
|
+
row = ttk.Frame(parent, style="Section.TFrame")
|
|
91
|
+
row.pack(fill="x", pady=(5, 12))
|
|
92
|
+
entry = ttk.Entry(row)
|
|
93
|
+
entry.pack(side="left", fill="x", expand=True)
|
|
94
|
+
ttk.Button(row, text="Browse", command=lambda: browse_command(entry)).pack(side="left", padx=(8, 0))
|
|
95
|
+
return entry
|
|
96
|
+
|
|
97
|
+
def _select_pdf_files(self, title: str) -> tuple[str, ...]:
|
|
98
|
+
files = filedialog.askopenfilenames(title=title, filetypes=[("PDF files", "*.pdf")])
|
|
99
|
+
return tuple(files) if files else ()
|
|
100
|
+
|
|
101
|
+
def _select_pdf_file(self, title: str) -> str:
|
|
102
|
+
return filedialog.askopenfilename(title=title, filetypes=[("PDF files", "*.pdf")])
|
|
103
|
+
|
|
104
|
+
def _save_as_pdf(self, entry: ttk.Entry) -> None:
|
|
105
|
+
output_file = filedialog.asksaveasfilename(
|
|
106
|
+
title="Save PDF As",
|
|
107
|
+
defaultextension=".pdf",
|
|
108
|
+
filetypes=[("PDF files", "*.pdf")],
|
|
109
|
+
)
|
|
110
|
+
if output_file:
|
|
111
|
+
entry.delete(0, tk.END)
|
|
112
|
+
entry.insert(0, output_file)
|
|
113
|
+
|
|
114
|
+
def _select_output_folder(self, entry: ttk.Entry) -> None:
|
|
115
|
+
output_folder = filedialog.askdirectory(title="Select Output Folder")
|
|
116
|
+
if output_folder:
|
|
117
|
+
entry.delete(0, tk.END)
|
|
118
|
+
entry.insert(0, output_folder)
|
|
119
|
+
|
|
120
|
+
def _build_merge_tab(self) -> None:
|
|
121
|
+
tab = self._make_tab("Merge", "Combine multiple PDF files into one output document.")
|
|
122
|
+
ttk.Button(tab, text="Select PDF Files", style="Accent.TButton", command=self._select_merge_files).pack(anchor="w")
|
|
123
|
+
self.merge_files_label = self._make_file_label(tab, "No PDFs selected")
|
|
124
|
+
self.merge_output_entry = self._make_entry_row(tab, "Output PDF", self._save_as_pdf)
|
|
125
|
+
ttk.Button(tab, text="Merge PDFs", style="Accent.TButton", command=self._merge_pdfs).pack(anchor="w", pady=(8, 0))
|
|
126
|
+
|
|
127
|
+
def _build_subset_tab(self) -> None:
|
|
128
|
+
tab = self._make_tab("Subset", "Extract selected pages from a PDF into a new file.")
|
|
129
|
+
ttk.Button(tab, text="Select PDF File", style="Accent.TButton", command=self._select_subset_file).pack(anchor="w")
|
|
130
|
+
self.subset_file_label = self._make_file_label(tab)
|
|
131
|
+
self.subset_output_entry = self._make_entry_row(tab, "Output PDF", self._save_as_pdf)
|
|
132
|
+
ttk.Label(tab, text="Pages, comma separated", style="Body.TLabel").pack(anchor="w")
|
|
133
|
+
self.pages_entry = ttk.Entry(tab)
|
|
134
|
+
self.pages_entry.pack(fill="x", pady=(5, 12))
|
|
135
|
+
ttk.Button(tab, text="Extract Subset", style="Accent.TButton", command=self._extract_subset).pack(anchor="w", pady=(8, 0))
|
|
136
|
+
|
|
137
|
+
def _build_decode_tab(self) -> None:
|
|
138
|
+
tab = self._make_tab("Decode", "Unlock an encrypted PDF using its password.")
|
|
139
|
+
ttk.Button(tab, text="Select PDF File", style="Accent.TButton", command=self._select_decode_file).pack(anchor="w")
|
|
140
|
+
self.decode_file_label = self._make_file_label(tab)
|
|
141
|
+
self.decode_output_entry = self._make_entry_row(tab, "Output PDF", self._save_as_pdf)
|
|
142
|
+
ttk.Label(tab, text="Password", style="Body.TLabel").pack(anchor="w")
|
|
143
|
+
self.password_entry = ttk.Entry(tab, show="*")
|
|
144
|
+
self.password_entry.pack(fill="x", pady=(5, 12))
|
|
145
|
+
ttk.Button(tab, text="Decode PDF", style="Accent.TButton", command=self._decode_pdf).pack(anchor="w", pady=(8, 0))
|
|
146
|
+
|
|
147
|
+
def _build_convert_tab(self) -> None:
|
|
148
|
+
tab = self._make_tab("Convert", "Render PDF pages to image files.")
|
|
149
|
+
ttk.Button(tab, text="Select PDF Files", style="Accent.TButton", command=self._select_convert_files).pack(anchor="w")
|
|
150
|
+
self.convert_files_label = self._make_file_label(tab, "No PDFs selected")
|
|
151
|
+
self.convert_output_entry = self._make_entry_row(tab, "Output Folder", self._select_output_folder)
|
|
152
|
+
ttk.Button(tab, text="Convert PDFs to Images", style="Accent.TButton", command=self._convert_pdfs_to_images).pack(anchor="w", pady=(8, 0))
|
|
153
|
+
|
|
154
|
+
def _select_merge_files(self) -> None:
|
|
155
|
+
files = self._select_pdf_files("Select PDF files to merge")
|
|
156
|
+
if files:
|
|
157
|
+
self.merge_files = files
|
|
158
|
+
self.merge_files_label.config(text="\n".join(files))
|
|
159
|
+
|
|
160
|
+
def _select_subset_file(self) -> None:
|
|
161
|
+
selected_file = self._select_pdf_file("Select PDF file")
|
|
162
|
+
if selected_file:
|
|
163
|
+
self.subset_file = selected_file
|
|
164
|
+
self.subset_file_label.config(text=selected_file)
|
|
165
|
+
|
|
166
|
+
def _select_decode_file(self) -> None:
|
|
167
|
+
selected_file = self._select_pdf_file("Select PDF file")
|
|
168
|
+
if selected_file:
|
|
169
|
+
self.decode_file = selected_file
|
|
170
|
+
self.decode_file_label.config(text=selected_file)
|
|
171
|
+
|
|
172
|
+
def _select_convert_files(self) -> None:
|
|
173
|
+
files = self._select_pdf_files("Select PDF files to convert")
|
|
174
|
+
if files:
|
|
175
|
+
self.convert_files = files
|
|
176
|
+
self.convert_files_label.config(text="\n".join(files))
|
|
177
|
+
|
|
178
|
+
def _require_output_path(self, entry: ttk.Entry) -> str:
|
|
179
|
+
return entry.get().strip()
|
|
180
|
+
|
|
181
|
+
def _merge_pdfs(self) -> None:
|
|
182
|
+
output_file = self._require_output_path(self.merge_output_entry)
|
|
183
|
+
if not self.merge_files or not output_file:
|
|
184
|
+
messagebox.showwarning("Missing input", "Please select PDF files and specify an output PDF.")
|
|
185
|
+
return
|
|
186
|
+
merge_pdfs(self.merge_files, output_file)
|
|
187
|
+
messagebox.showinfo("Success", f"Merged PDF saved to:\n{output_file}")
|
|
188
|
+
|
|
189
|
+
def _extract_subset(self) -> None:
|
|
190
|
+
output_file = self._require_output_path(self.subset_output_entry)
|
|
191
|
+
pages = self.pages_entry.get().strip()
|
|
192
|
+
if not self.subset_file or not output_file or not pages:
|
|
193
|
+
messagebox.showwarning("Missing input", "Please select a PDF, enter pages, and specify an output PDF.")
|
|
194
|
+
return
|
|
195
|
+
save_pages_of_pdf(pages, self.subset_file, output_file)
|
|
196
|
+
messagebox.showinfo("Success", f"PDF subset saved to:\n{output_file}")
|
|
197
|
+
|
|
198
|
+
def _decode_pdf(self) -> None:
|
|
199
|
+
output_file = self._require_output_path(self.decode_output_entry)
|
|
200
|
+
password = self.password_entry.get()
|
|
201
|
+
if not self.decode_file or not output_file or not password:
|
|
202
|
+
messagebox.showwarning("Missing input", "Please select a PDF, enter a password, and specify an output PDF.")
|
|
203
|
+
return
|
|
204
|
+
decode_pdf(self.decode_file, output_file, password)
|
|
205
|
+
messagebox.showinfo("Success", f"Decoded PDF saved to:\n{output_file}")
|
|
206
|
+
|
|
207
|
+
def _convert_pdfs_to_images(self) -> None:
|
|
208
|
+
output_folder = self._require_output_path(self.convert_output_entry)
|
|
209
|
+
if not self.convert_files or not output_folder:
|
|
210
|
+
messagebox.showwarning("Missing input", "Please select PDF files and an output folder.")
|
|
211
|
+
return
|
|
212
|
+
Path(output_folder).mkdir(parents=True, exist_ok=True)
|
|
213
|
+
convert_pdf2images(self.convert_files, output_folder, dpi_value)
|
|
214
|
+
messagebox.showinfo("Success", f"Images saved under:\n{output_folder}")
|
|
215
|
+
|
|
216
|
+
|
|
217
|
+
def main() -> None:
|
|
218
|
+
root = tk.Tk()
|
|
219
|
+
QuantumPDFApp(root)
|
|
220
|
+
root.mainloop()
|
|
221
|
+
|
|
222
|
+
|
|
223
|
+
if __name__ == "__main__":
|
|
224
|
+
main()
|
quantumpdf/help.py
ADDED
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
"""
|
|
2
|
+
quantumpdf/help.py
|
|
3
|
+
|
|
4
|
+
Author: Benevant Mathew
|
|
5
|
+
Date: 2026-06-21
|
|
6
|
+
"""
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
def print_help() -> None:
|
|
10
|
+
help_message = """
|
|
11
|
+
Usage: quantumpdf [OPTIONS]
|
|
12
|
+
|
|
13
|
+
QuantumPDF desktop tools for merging, subsetting, decoding, and converting PDFs.
|
|
14
|
+
|
|
15
|
+
Options:
|
|
16
|
+
--version, -v Show the version of quantumpdf and exit
|
|
17
|
+
--help, -h Show this help message and exit
|
|
18
|
+
--email, -e Show email and exit
|
|
19
|
+
--author, -a Show author and exit
|
|
20
|
+
--date, -d Show release date and exit
|
|
21
|
+
(No arguments) Launch the GUI application
|
|
22
|
+
"""
|
|
23
|
+
print(help_message)
|
quantumpdf/main.py
ADDED
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
"""
|
|
2
|
+
quantumpdf/main.py
|
|
3
|
+
|
|
4
|
+
Author: Benevant Mathew
|
|
5
|
+
Date: 2026-06-21
|
|
6
|
+
"""
|
|
7
|
+
|
|
8
|
+
import argparse
|
|
9
|
+
import sys
|
|
10
|
+
|
|
11
|
+
from quantumpdf.gui.gui import main as run_gui
|
|
12
|
+
from quantumpdf.help import print_help
|
|
13
|
+
from quantumpdf.version import __author__, __email__, __release_date__, __version__
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
def parse_arguments():
|
|
17
|
+
"""Parse command line arguments."""
|
|
18
|
+
parser = argparse.ArgumentParser(
|
|
19
|
+
prog="quantumpdf",
|
|
20
|
+
description="QuantumPDF desktop PDF tools.",
|
|
21
|
+
add_help=False,
|
|
22
|
+
)
|
|
23
|
+
|
|
24
|
+
parser.add_argument("-h", "--help", action="store_true")
|
|
25
|
+
parser.add_argument("-v", "--version", action="store_true")
|
|
26
|
+
parser.add_argument("-a", "--author", action="store_true")
|
|
27
|
+
parser.add_argument("-e", "--email", action="store_true")
|
|
28
|
+
parser.add_argument("-d", "--date", action="store_true")
|
|
29
|
+
|
|
30
|
+
return parser.parse_args()
|
|
31
|
+
|
|
32
|
+
|
|
33
|
+
def main() -> None:
|
|
34
|
+
"""quantumpdf command line entry point."""
|
|
35
|
+
args = parse_arguments()
|
|
36
|
+
|
|
37
|
+
if args.help:
|
|
38
|
+
print_help()
|
|
39
|
+
sys.exit(0)
|
|
40
|
+
|
|
41
|
+
if args.version:
|
|
42
|
+
print(f"version {__version__}")
|
|
43
|
+
sys.exit(0)
|
|
44
|
+
|
|
45
|
+
if args.author:
|
|
46
|
+
print(f"Author {__author__}")
|
|
47
|
+
sys.exit(0)
|
|
48
|
+
|
|
49
|
+
if args.email:
|
|
50
|
+
print(f"Mailto {__email__}")
|
|
51
|
+
sys.exit(0)
|
|
52
|
+
|
|
53
|
+
if args.date:
|
|
54
|
+
print(f"Release Date {__release_date__}")
|
|
55
|
+
sys.exit(0)
|
|
56
|
+
|
|
57
|
+
run_gui()
|
|
58
|
+
|
|
59
|
+
|
|
60
|
+
if __name__ == "__main__":
|
|
61
|
+
main()
|
|
File without changes
|
|
File without changes
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import os
|
|
2
|
+
|
|
3
|
+
from quantumpdf.common.pdf_functions.img2pdf_funs import Img2pdfCore
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
def images_to_pdf_for_dcai(img_file_paths, output_pdf_path):
|
|
7
|
+
img2pdf_obj = Img2pdfCore()
|
|
8
|
+
img2pdf_obj.images_to_pdf(img_file_paths, output_pdf_path)
|
|
9
|
+
|
|
10
|
+
def get_image_files_in_folder(folder_path):
|
|
11
|
+
"""Return sorted list of image files in the given folder."""
|
|
12
|
+
valid_exts = {'.jpg', '.jpeg', '.png'}
|
|
13
|
+
return sorted(
|
|
14
|
+
os.path.join(folder_path, fname)
|
|
15
|
+
for fname in os.listdir(folder_path)
|
|
16
|
+
if os.path.splitext(fname)[1].lower() in valid_exts
|
|
17
|
+
)
|
|
File without changes
|
|
File without changes
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
from quantumpdf.common.pdf_functions.use_pdf2image import Pdf2imageCore
|
|
2
|
+
|
|
3
|
+
def convert_pdf2images(filepaths,output_dir,img_dpi):
|
|
4
|
+
# calling pdf2image module
|
|
5
|
+
pdf2image_obj=Pdf2imageCore()
|
|
6
|
+
converted_images=pdf2image_obj.pdf2image_fun(filepaths,output_dir=output_dir,dpi=img_dpi)
|
|
7
|
+
return converted_images
|
quantumpdf/version.py
ADDED
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
"""
|
|
2
|
+
quantumpdf/version.py
|
|
3
|
+
|
|
4
|
+
Author: Benevant Mathew
|
|
5
|
+
Date: 2026-06-21
|
|
6
|
+
"""
|
|
7
|
+
|
|
8
|
+
__version__ = "0.2.0"
|
|
9
|
+
__app_name__ = "QuantumPDF"
|
|
10
|
+
__app_folder__ = "quantumpdf"
|
|
11
|
+
__author__ = "Benevant Mathew"
|
|
12
|
+
__email__ = "benevantmathewv@gmail.com"
|
|
13
|
+
__release_date__ = "21-06-2026"
|
|
14
|
+
|
|
15
|
+
if __name__ == "__main__":
|
|
16
|
+
print(__version__)
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: quantumpdf
|
|
3
|
+
Version: 0.2.0
|
|
4
|
+
Summary: QuantumPDF desktop tools for merging, subsetting, decoding, and converting PDFs.
|
|
5
|
+
Author-email: Benevant Mathew <benevantmathewv@gmail.com>
|
|
6
|
+
License-Expression: MIT
|
|
7
|
+
Project-URL: Homepage, https://github.com/benevantmathew/pdf_editor
|
|
8
|
+
Classifier: Programming Language :: Python :: 3
|
|
9
|
+
Classifier: Natural Language :: English
|
|
10
|
+
Classifier: Operating System :: OS Independent
|
|
11
|
+
Classifier: Topic :: Multimedia :: Graphics :: Graphics Conversion
|
|
12
|
+
Classifier: Topic :: Office/Business
|
|
13
|
+
Classifier: Topic :: Utilities
|
|
14
|
+
Requires-Python: >=3.13
|
|
15
|
+
Description-Content-Type: text/markdown
|
|
16
|
+
License-File: LICENSE
|
|
17
|
+
Requires-Dist: img2pdf>=0.6.1
|
|
18
|
+
Requires-Dist: pikepdf>=10.5.1
|
|
19
|
+
Requires-Dist: pillow>=11.0.0
|
|
20
|
+
Requires-Dist: pymupdf>=1.27.2.3
|
|
21
|
+
Requires-Dist: pypdf2>=3.0.1
|
|
22
|
+
Dynamic: license-file
|
|
23
|
+
|
|
24
|
+
# QuantumPDF
|
|
25
|
+
|
|
26
|
+
QuantumPDF (`quantumpdf`) is a desktop GUI toolkit for common PDF operations.
|
|
27
|
+
|
|
28
|
+
Features:
|
|
29
|
+
- Merge multiple PDFs
|
|
30
|
+
- Extract selected pages from a PDF
|
|
31
|
+
- Decode password-protected PDFs
|
|
32
|
+
- Convert PDF pages to images
|
|
33
|
+
|
|
34
|
+
Install:
|
|
35
|
+
|
|
36
|
+
```bash
|
|
37
|
+
pip install quantumpdf
|
|
38
|
+
quantumpdf
|
|
39
|
+
```
|
|
40
|
+
|
|
41
|
+
Metadata commands:
|
|
42
|
+
|
|
43
|
+
```bash
|
|
44
|
+
quantumpdf --version
|
|
45
|
+
quantumpdf --author
|
|
46
|
+
quantumpdf --email
|
|
47
|
+
quantumpdf --date
|
|
48
|
+
```
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
quantumpdf/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
2
|
+
quantumpdf/help.py,sha256=lEAtG20TcY-nOoxjPMKr7IinFmJ2r__bA7towMPlec0,577
|
|
3
|
+
quantumpdf/main.py,sha256=xgKd026qUdwaSmAYimEX7q1kDlgHuyJlvswjUYR7qS4,1365
|
|
4
|
+
quantumpdf/version.py,sha256=mkUO6fVNUFA7UogwV37C7tt61bEjgSTN4NxdVQI10IE,307
|
|
5
|
+
quantumpdf/application/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
6
|
+
quantumpdf/application/config.py,sha256=or-gEKB_VSSRP3NJWVvN-mzZfOau06QlvoczubrT5I4,87
|
|
7
|
+
quantumpdf/common/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
8
|
+
quantumpdf/common/basic_functions/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
9
|
+
quantumpdf/common/basic_functions/os_funs.py,sha256=RHEcFRXqomj5kevzP4VfYgQy-2Edeb_V_msrcy7vmrc,251
|
|
10
|
+
quantumpdf/common/pdf_functions/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
11
|
+
quantumpdf/common/pdf_functions/img2pdf_funs.py,sha256=dM-cwVUhamIWwX_wprdVjxtV1LJACpIXtn5q1pP3-Pc,926
|
|
12
|
+
quantumpdf/common/pdf_functions/pikepdf_funs.py,sha256=kSWX9ea7wTezRAjcBIoasnz8KRiKUNL4UEV76cSBVVM,301
|
|
13
|
+
quantumpdf/common/pdf_functions/pypdf2_funs.py,sha256=R3pqr7-BsWKRIwke4Mp1mriRLCQSQbtH2IzqdF_bIko,1310
|
|
14
|
+
quantumpdf/common/pdf_functions/use_pdf2image.py,sha256=FLn8BEBVsvcgIBZdhChy8gDaWr2I4s7AsZ1_Vt0cWoU,1577
|
|
15
|
+
quantumpdf/gui/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
16
|
+
quantumpdf/gui/gui.py,sha256=mjh7TI6LfIiX9jvnaDBwmJPPppUk169zUuwcJB8V27M,10902
|
|
17
|
+
quantumpdf/sections/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
18
|
+
quantumpdf/sections/image_to_pdf/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
19
|
+
quantumpdf/sections/image_to_pdf/main.py,sha256=-88rceC4ip0wMXP4BwwsmU6bS_8PlwkTZc4LW5hMMug,562
|
|
20
|
+
quantumpdf/sections/pdf_to_image/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
21
|
+
quantumpdf/sections/pdf_to_image/gui_support.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
22
|
+
quantumpdf/sections/pdf_to_image/main.py,sha256=94RgoMbxsHvW65kGmFO391IRtMUiMWCdlQY_OsVWnEA,302
|
|
23
|
+
quantumpdf-0.2.0.dist-info/licenses/LICENSE,sha256=LEjWxl8qZVisoLsVfJAGZkDNn_LfWeQttVU9csO6NB8,1072
|
|
24
|
+
quantumpdf-0.2.0.dist-info/METADATA,sha256=wYyescz6sNc4CP0OW4jnEFTj9rB1-3uwnle72ue7cSQ,1218
|
|
25
|
+
quantumpdf-0.2.0.dist-info/WHEEL,sha256=aeYiig01lYGDzBgS8HxWXOg3uV61G9ijOsup-k9o1sk,91
|
|
26
|
+
quantumpdf-0.2.0.dist-info/entry_points.txt,sha256=_ASqyD5iQo6-lVovQFGMt97rC0XDIWQiq1bY8-iMU8M,52
|
|
27
|
+
quantumpdf-0.2.0.dist-info/top_level.txt,sha256=xLqaZ1QBHdVT6uDmfKDxhmF_K8yLXUCwp1EqwE1cf-U,11
|
|
28
|
+
quantumpdf-0.2.0.dist-info/RECORD,,
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Benevant Mathew
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
quantumpdf
|