3
3
from PyPDF2 import PdfReader , PdfWriter , PdfMerger
4
4
from reportlab .pdfgen import canvas
5
5
from reportlab .lib .pagesizes import A4
6
+ from datetime import datetime
6
7
7
8
BASE_URL = "http://docs.nomana-it.fr"
8
9
TEMP_FOLDER = "pdf-temp"
13
14
14
15
# Hardcoded Navigation Structure
15
16
NAV = [
16
- {"title" : "Getting Started" , "path" : "liberty/getting-started.md" },
17
- {"title" : "Release Notes" , "path" : "liberty/release-notes.md" },
17
+ {"title" : "Getting Started" , "path" : "liberty/getting-started.md" , "description" : "Learn the basics of Liberty Framework." },
18
+ {"title" : "Release Notes" , "path" : "liberty/release-notes.md" , "description" : "See what's new in the latest release." },
18
19
{
19
20
"title" : "Installation" ,
21
+ "description" : "Step-by-step installation guides." ,
20
22
"children" : [
21
- {"title" : "Architecture" , "path" : "liberty/technical/architecture.md" },
22
- {"title" : "Docker Installation Guide" , "path" : "liberty/technical/installation.md" },
23
- {"title" : "Installation Tools Deployment Guide" , "path" : "liberty/technical/tools-deployment.md" },
24
- {"title" : "Liberty Deployment Guide" , "path" : "liberty/technical/liberty-deployment.md" },
25
- {"title" : "Create Linux Services" , "path" : "liberty/technical/linux-services.md" },
26
- {"title" : "Enable SSL with Traefik" , "path" : "liberty/technical/post-ssl.md" },
23
+ {"title" : "Architecture" , "path" : "liberty/technical/architecture.md" , "description" : "Understand the architecture of Liberty Framework." },
24
+ {"title" : "Docker Installation Guide" , "path" : "liberty/technical/installation.md" , "description" : "Set up Liberty Framework using Docker." },
25
+ {"title" : "Installation Tools Deployment Guide" , "path" : "liberty/technical/tools-deployment.md" , "description" : "Learn about tools for deploying Liberty Framework." },
26
+ {"title" : "Liberty Deployment Guide" , "path" : "liberty/technical/liberty-deployment.md" , "description" : "Guide to deploying Liberty Framework." },
27
+ {"title" : "Create Linux Services" , "path" : "liberty/technical/linux-services.md" , "description" : "Create Linux services for Liberty Framework." },
28
+ {"title" : "Enable SSL with Traefik" , "path" : "liberty/technical/post-ssl.md" , "description" : "Enable SSL using Traefik for enhanced security." },
27
29
]
28
30
},
29
31
{
30
32
"title" : "Nomasx-1" ,
33
+ "description" : "Guides and settings for Nomasx-1." ,
31
34
"children" : [
32
35
{
33
36
"title" : "Administrator's Guide" ,
37
+ "description" : "Administrator resources and tools." ,
34
38
"children" : [
35
- {"title" : "Global Settings" , "path" : "liberty/nomasx1/admin/global-settings.md" },
39
+ {"title" : "Global Settings" , "path" : "liberty/nomasx1/admin/global-settings.md" , "description" : "Manage global settings for Nomasx-1." },
36
40
]
37
41
}
38
42
]
@@ -48,14 +52,59 @@ def flatten_nav(nav, level=1, parent_number=""):
48
52
49
53
if "path" in item : # It's a direct page
50
54
page_path = item ["path" ].replace (".md" , "" ).replace ("docs/" , "" ).strip ("/" )
51
- pages .append ((full_title , page_path ))
55
+ pages .append ((full_title , page_path , item . get ( "description" , "" ) ))
52
56
if "children" in item : # It's a nested structure
53
57
# Add parent title as a standalone TOC entry
54
- pages .append ((full_title , None ))
58
+ pages .append ((full_title , None , item . get ( "description" , "" ) ))
55
59
# Add children recursively
56
60
pages .extend (flatten_nav (item ["children" ], level + 1 , f"{ number } ." ))
57
61
return pages
58
62
63
+ # Generate a title page for a chapter
64
+ def generate_title_page (title , description , output_file ):
65
+ title_html = f"""
66
+ <!DOCTYPE html>
67
+ <html lang="en">
68
+ <head>
69
+ <meta charset="UTF-8">
70
+ <title>{ title } </title>
71
+ <style>
72
+ body {{
73
+ font-family: Arial, sans-serif;
74
+ margin: 0;
75
+ padding: 0;
76
+ display: flex;
77
+ justify-content: center;
78
+ align-items: center;
79
+ height: 100vh;
80
+ background-color: #f9f9f9;
81
+ }}
82
+ .container {{
83
+ text-align: center;
84
+ padding: 20px;
85
+ }}
86
+ h1 {{
87
+ font-size: 2.5rem;
88
+ color: #333;
89
+ }}
90
+ p {{
91
+ font-size: 1.25rem;
92
+ color: #555;
93
+ }}
94
+ </style>
95
+ </head>
96
+ <body>
97
+ <div class="container">
98
+ <h1>{ title } </h1>
99
+ <p>{ description } </p>
100
+ </div>
101
+ </body>
102
+ </html>
103
+ """
104
+ with open (output_file , "w" ) as file :
105
+ file .write (title_html )
106
+ print (f"Title page generated: { output_file } " )
107
+
59
108
# Generate Table of Contents
60
109
def generate_toc (pages_with_numbers ):
61
110
toc_html = """<!DOCTYPE html>
@@ -101,7 +150,6 @@ def generate_toc(pages_with_numbers):
101
150
<ul>
102
151
"""
103
152
for title , page_number in pages_with_numbers :
104
- # Determine the indentation level based on numbering
105
153
level = title .count ("." ) - 1
106
154
class_level = f"level-{ level + 1 } "
107
155
toc_html += f'<li class="{ class_level } ">{ title } <span class="page-number">Page { page_number } </span></li>\n '
@@ -117,6 +165,9 @@ def generate_toc(pages_with_numbers):
117
165
print (f"TOC generated as { toc_file } " )
118
166
return toc_file
119
167
168
+ # The rest of the script remains unchanged...
169
+ # Include the `generate_title_page` function where necessary to add title pages before content pages.
170
+
120
171
def handle_cookie_consent (page ):
121
172
"""Automatically accept cookie consent if the dialog is present."""
122
173
try :
@@ -141,7 +192,7 @@ def add_page_numbers(input_file, output_file):
141
192
142
193
for i in range (len (reader .pages )):
143
194
c .setFont (font_name , font_size )
144
- c .drawRightString (width - 5 , height - 20 , f"Page { i + 1 } of { len (reader .pages )} " )
195
+ c .drawRightString (width - 5 , height - 22 , f"Page { i + 1 } of { len (reader .pages )} " )
145
196
c .showPage ()
146
197
c .save ()
147
198
@@ -168,40 +219,132 @@ def generate_pdf_with_cover_and_toc(base_url, pages_with_titles):
168
219
page_numbers = []
169
220
current_page = 1
170
221
pdf_paths = []
222
+ edited_date = datetime .now ().strftime ("%Y-%m-%d" )
171
223
172
224
# Generate individual pages and track page numbers
173
- for title , page_path in pages_with_titles :
174
- if page_path is None :
175
- page_numbers .append ((title , current_page ))
176
- continue
177
-
178
- url = f"{ base_url } /{ page_path } "
179
- output_path = os .path .join (TEMP_FOLDER , f"{ page_path .replace ('/' , '_' )} .pdf" )
180
- page = context .new_page ()
181
- page .goto (url , wait_until = "networkidle" )
182
- handle_cookie_consent (page )
183
-
184
- page .pdf (
185
- path = output_path ,
186
- format = "A4" ,
187
- print_background = True ,
188
- margin = {"top" : "1in" , "bottom" : "1in" },
189
- display_header_footer = True ,
190
- header_template = f'''
191
- <div style="font-size: 10px; padding-left: 20px; width: 100%; text-align: left;">
192
- { title }
193
- </div>
194
- '''
195
- )
196
-
197
- pdf_paths .append (output_path )
198
- reader = PdfReader (output_path )
199
- num_pages = len (reader .pages )
200
- page_numbers .append ((title , current_page ))
201
- current_page += num_pages
202
-
203
-
225
+ for title , page_path , description in pages_with_titles :
226
+ if description : # Generate a title page
227
+ title_page_path = os .path .join (TEMP_FOLDER , f"{ title .replace (' ' , '_' )} _title.html" )
228
+ title_pdf_path = os .path .join (TEMP_FOLDER , f"{ title .replace (' ' , '_' )} _title.pdf" )
229
+ generate_title_page (title , description , title_page_path )
230
+
231
+ title_page = context .new_page ()
232
+ title_page .goto (f"file://{ os .path .abspath (title_page_path )} " )
233
+ title_page .pdf (
234
+ path = title_pdf_path ,
235
+ format = "A4" ,
236
+ print_background = True ,
237
+ display_header_footer = True ,
238
+ header_template = f'''
239
+ <div style="
240
+ font-size: 12px;
241
+ padding-left: 20px;
242
+ padding-bottom: 5px;
243
+ width: 100%;
244
+ text-align: left;
245
+ font-weight: bold;
246
+ border-bottom: 0.5px solid #7393B3;
247
+ font-variant-caps: small-caps;
248
+ ">
249
+ { title }
250
+ </div>
251
+ ''' ,
252
+ footer_template = f'''
253
+ <div style="
254
+ font-size: 12px;
255
+ padding-left: 20px;
256
+ padding-right: 20px;
257
+ padding-bottom: 5px;
258
+ width: 100%;
259
+ display: flex;
260
+ justify-content: space-between;
261
+ align-items: center;
262
+ font-style: italic;">
263
+ <div style="text-align: right;">
264
+ © Nomana-IT | Edited: { edited_date }
265
+ </div>
266
+ </div>
267
+ '''
268
+ )
269
+ pdf_paths .append (title_pdf_path )
270
+ page_numbers .append ((title , current_page )) # Add only title pages to TOC
271
+ current_page += 1 # Increment page number for the title page
272
+
273
+ if page_path : # Generate content pages but do not add them to TOC
274
+ url = f"{ base_url } /{ page_path } "
275
+
276
+ output_path = os .path .join (TEMP_FOLDER , f"{ page_path .replace ('/' , '_' )} .pdf" )
277
+ page = context .new_page ()
278
+ page .goto (url , wait_until = "networkidle" )
279
+ handle_cookie_consent (page )
204
280
281
+ # Inject CSS to hide the title
282
+ page .add_style_tag (content = """
283
+ h1, .page-title {
284
+ display: none !important;
285
+ height: 0 !important;
286
+ margin: 0 !important;
287
+ padding: 0 !important;
288
+ }
289
+ h2 {
290
+ margin-top: 10px !important;
291
+ }
292
+ @page {
293
+ margin: 0.5in 0.1in 0.5in 0.1in; /* Default margin for all pages */
294
+ }
295
+
296
+ @page:first {
297
+ margin: 0 !important; /* Adjust or remove the margin for the first page */
298
+ padding: 0 !important;
299
+ }
300
+ """ )
301
+
302
+ page .pdf (
303
+ path = output_path ,
304
+ format = "A4" ,
305
+ print_background = True ,
306
+ margin = {"top" : "1in" , "bottom" : "1in" },
307
+ display_header_footer = True ,
308
+ header_template = f'''
309
+ <div style="
310
+ font-size: 12px;
311
+ padding-left: 20px;
312
+ padding-bottom: 5px;
313
+ width: 100%;
314
+ text-align: left;
315
+ font-weight: bold;
316
+ border-bottom: 0.5px solid #7393B3;
317
+ font-variant-caps: small-caps;
318
+ ">
319
+ { title }
320
+ </div>
321
+ ''' ,
322
+ footer_template = f'''
323
+ <div style="
324
+ font-size: 12px;
325
+ padding-left: 20px;
326
+ padding-right: 20px;
327
+ padding-bottom: 5px;
328
+ width: 100%;
329
+ display: flex;
330
+ justify-content: space-between;
331
+ align-items: center;
332
+ font-style: italic;">
333
+ <div style="text-align: left;">
334
+ © Nomana-IT | Edited: { edited_date }
335
+ </div>
336
+ <div style="text-align: right;">
337
+ { url }
338
+ </div>
339
+ </div>
340
+ '''
341
+ )
342
+ pdf_paths .append (output_path )
343
+ reader = PdfReader (output_path )
344
+ num_pages = len (reader .pages )
345
+ current_page += num_pages # Only update page numbers for content pages
346
+
347
+ # Generate the TOC PDF
205
348
toc_file = os .path .join (TEMP_FOLDER , "toc.pdf" )
206
349
generate_toc (page_numbers )
207
350
@@ -213,7 +356,7 @@ def generate_pdf_with_cover_and_toc(base_url, pages_with_titles):
213
356
print_background = True ,
214
357
)
215
358
216
-
359
+ # Merge PDFs: content, TOC, and cover page
217
360
merged_output = os .path .join (TEMP_FOLDER , PDF_NAME )
218
361
merge_pdfs (pdf_paths , merged_output )
219
362
add_page_numbers (merged_output , merged_output )
0 commit comments