File size: 4,979 Bytes
e68fc4b
 
 
 
611be2f
4c85f97
 
 
21bc43d
e68fc4b
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
987d7c4
e68fc4b
4d04ed4
987d7c4
 
 
 
 
 
 
e68fc4b
 
 
 
4c85f97
679df68
72673b1
4c85f97
 
72673b1
4c85f97
 
39ae163
 
4c85f97
 
 
39ae163
 
e68fc4b
 
 
 
21bc43d
4c85f97
 
 
611be2f
52f175a
679df68
52f175a
 
611be2f
21bc43d
87d950a
 
21bc43d
87d950a
 
e68fc4b
21bc43d
 
4c85f97
 
679df68
987d7c4
 
 
 
 
 
 
 
 
 
 
 
e68fc4b
 
 
 
 
 
 
 
4d04ed4
 
 
 
 
 
 
 
 
 
 
 
 
 
e68fc4b
4d04ed4
 
 
 
e68fc4b
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
import dash
from dash import dcc, html, Input, Output, State, callback
import dash_bootstrap_components as dbc
from dash.exceptions import PreventUpdate
import pikepdf
import requests
import io
import tempfile
import os
import base64
import threading

app = dash.Dash(__name__, external_stylesheets=[dbc.themes.BOOTSTRAP])

app.layout = dbc.Container([
    html.H1("PDF Compressor", className="my-4"),
    dbc.Card([
        dbc.CardBody([
            dcc.Upload(
                id='upload-pdf',
                children=html.Div([
                    'Drag and Drop or ',
                    html.A('Select PDF File')
                ]),
                style={
                    'width': '100%',
                    'height': '60px',
                    'lineHeight': '60px',
                    'borderWidth': '1px',
                    'borderStyle': 'dashed',
                    'borderRadius': '5px',
                    'textAlign': 'center',
                    'margin': '10px'
                },
                multiple=False
            ),
            dbc.Alert(id="upload-status", is_open=False, duration=4000, className="mt-3"),
            dbc.Input(id="url-input", placeholder="Or enter PDF URL", type="text", className="mt-3"),
            dbc.Button("Compress PDF", id="compress-btn", color="primary", className="mt-3"),
            dbc.Spinner(
                html.Div(id="compression-status", className="mt-3"),
                color="primary",
                type="border",
                fullscreen=True,
                fullscreen_style={"backgroundColor": "rgba(0, 0, 0, 0.3)"},
            ),
            dcc.Download(id="download-pdf"),
        ])
    ]),
])

def compress_pdf(input_file, url):
    if input_file is None and (url is None or url.strip() == ""):
        return None, "Please provide either a file or a URL."
    
    if input_file is not None and url and url.strip() != "":
        return None, "Please provide either a file or a URL, not both."
    
    try:
        if url and url.strip() != "":
            response = requests.get(url)
            response.raise_for_status()
            pdf_content = io.BytesIO(response.content)
            initial_size = len(response.content)
        else:
            content_type, content_string = input_file.split(',')
            decoded = base64.b64decode(content_string)
            pdf_content = io.BytesIO(decoded)
            initial_size = len(decoded)

        with tempfile.NamedTemporaryFile(delete=False, suffix=".pdf") as temp_file:
            temp_file_path = temp_file.name

        pdf = pikepdf.Pdf.open(pdf_content)
        
        compression_params = dict(compress_streams=True, object_stream_mode=pikepdf.ObjectStreamMode.generate)

        pdf.save(temp_file_path, **compression_params)

        compressed_size = os.path.getsize(temp_file_path)
        compression_ratio = compressed_size / initial_size
        compression_percentage = (1 - compression_ratio) * 100

        if compression_ratio >= 1 or compression_percentage < 5:
            os.remove(temp_file_path)
            return None, f"Unable to compress the PDF effectively. Original file returned. (Attempted compression: {compression_percentage:.2f}%)"

        return temp_file_path, f"PDF compressed successfully! Compression achieved: {compression_percentage:.2f}%"
    except Exception as e:
        return None, f"Error compressing PDF: {str(e)}"

@callback(
    Output("upload-status", "children"),
    Output("upload-status", "is_open"),
    Output("upload-status", "color"),
    Input("upload-pdf", "filename"),
    Input("upload-pdf", "contents"),
)
def update_upload_status(filename, contents):
    if filename is not None and contents is not None:
        return f"File uploaded: {filename}", True, "success"
    return "", False, "primary"

@callback(
    Output("compression-status", "children"),
    Output("download-pdf", "data"),
    Input("compress-btn", "n_clicks"),
    State("upload-pdf", "contents"),
    State("url-input", "value"),
    prevent_initial_call=True
)
def process_compress_and_download(n_clicks, file_content, url):
    if file_content is None and (url is None or url.strip() == ""):
        return "Please provide either a file or a URL.", None

    def compression_thread():
        nonlocal file_content, url
        output_file, message = compress_pdf(file_content, url)
        if output_file:
            with open(output_file, "rb") as file:
                compressed_content = file.read()
            os.remove(output_file)
            return message, dcc.send_bytes(compressed_content, "compressed.pdf")
        else:
            return message, None

    thread = threading.Thread(target=compression_thread)
    thread.start()
    thread.join()
    return compression_thread()

if __name__ == '__main__':
    print("Starting the Dash application...")
    app.run(debug=True, host='0.0.0.0', port=7860)
    print("Dash application has finished running.")