-
Notifications
You must be signed in to change notification settings - Fork 103
/
build.py
executable file
·761 lines (589 loc) · 22.1 KB
/
build.py
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
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
#!/usr/bin/python3
#
# Script to take the master document and ancillary files and create the
# finished manual/website.
#
# by James Hammons
# (C) 2020 Underground Software
#
# Contributors: Ed Ward
#
# Remnants (could go into the master document as the first header)
import os
import re
import shutil
import argparse
import datetime
# Global vars
global_bootstrap_path = '/bootstrap-3.3.7'
global_page_title = 'The Ardour Manual'
global_site_dir = './website/'
global_manual_url = 'http://manual.ardour.org'
global_githuburl = 'https://github.com/Ardour/manual/edit/master/include/'
global_screen_template = 'page-template.html'
global_onepage_template = 'onepage-template.html'
global_pdf_template = 'pdf-template.html'
global_master_doc = 'master-doc.txt'
global_pdflink = '<button class="btn btn-default" type="button" onclick="window.location.href=\'/manual.pdf\'"><span class="glyphicon glyphicon-book" aria-hidden="true"></span></button>'
from datetime import datetime
global_today = datetime.today().strftime('%Y-%m-%d')
global_redirects_file = 'uri_to_fix.txt'
# This matches all *non* letter/number, ' ', '.', '-', and '_' chars
cleanString = re.compile(r'[^a-zA-Z0-9 \._-]+')
# This matches new 'unbreakable' links, up to the closing quote or anchor
findLinks = re.compile(r'"@@[^#"]*[#"]')
#
# Create an all lowercase filename without special characters and with spaces
# replaced with dashes.
#
def MakeFilename(s):
global cleanString
# Clean up the file name, removing all non letter/number or " .-_" chars.
# Also, convert to lower case and replace all spaces with dashes.
fn = cleanString.sub('', s).lower().replace(' ', '-')
# Double dashes can creep in from the above replacement, so we check for
# that here.
fn = fn.replace('--', '-')
return fn
#
# Parse headers into a dictionary
#
def ParseHeader(fileObj):
header = {}
while (True):
hdrLine = fileObj.readline().rstrip('\r\n')
# Break out of the loop if we hit the end of header marker
if hdrLine.startswith('---'):
break
# Check to see that we have a well-formed header construct
match = re.findall(': ', hdrLine)
if match:
# Parse out foo: bar pairs & put into header dictionary
a = re.split(': ', hdrLine, 1)
header[a[0]] = a[1]
return header
#
# Turn a "part" name into an int
#
def PartToLevel(s):
lvl = {'part': 0, 'chapter': 1, 'subchapter': 2, 'section': 3, 'subsection': 4 }
if s in lvl:
return lvl[s]
return -1
#
# Converts a integer to a Roman numeral
#
def num2roman(num):
num_map = [(1000, 'M'), (900, 'CM'), (500, 'D'), (400, 'CD'), (100, 'C'), (90, 'XC'), (50, 'L'), (40, 'XL'), (10, 'X'), (9, 'IX'), (5, 'V'), (4, 'IV'), (1, 'I')]
roman = ''
while num > 0:
for i, r in num_map:
while num >= i:
roman += r
num -= i
return roman
#
# Capture the master document's structure (and content, if any) in a list
#
def GetFileStructure():
fs = []
fnames = [None] * 6
content = ''
grab = False
mf = open(global_master_doc)
for ln in mf:
if ln.startswith('---'):
# First, stuff any content that we may have read into the current
# header's dictionary
if grab:
fs[-1]['content'] = content
grab = False
content = ''
# Then, get the new header and do things to it
hdr = ParseHeader(mf)
level = PartToLevel(hdr['part'])
hdr['level'] = level
fnames[level] = MakeFilename(hdr['title'])
# Ickyness--user specified URIs
if 'uri' in hdr:
hdr['filename'] = hdr['uri']
else:
fullName = ''
for i in range(level + 1):
fullName = fullName + fnames[i] + '/'
# Strip trailing '/' on filename
hdr['filename'] = fullName[:-1]
fs.append(hdr)
if ('include' not in hdr) and (level > 0):
grab = True
else:
if grab:
content = content + ln
# Catch the last file, since it would be missed above
if grab:
fs[-1]['content'] = content
mf.close()
return fs
#
# Determine if a particular node has child nodes
#
def HaveChildren(fs, pos):
# If we're at the end of the list, there can be no children
if pos == len(fs) - 1:
return False
# If the next node is at a lower level than the current node, we have
# children.
if fs[pos]['level'] < fs[pos + 1]['level']:
return True
# Otherwise, no children at this node.
return False
#
# Get the children at this level, and return them in a list
#
def GetChildren(fs, pos):
children = []
pos = pos + 1
childLevel = fs[pos]['level']
while fs[pos]['level'] >= childLevel:
if fs[pos]['level'] == childLevel:
children.append(pos)
pos = pos + 1
# Sanity check
if pos == len(fs):
break
return children
#
# Get the parent at this level
#
def GetParent(fs, pos):
thisLevel = fs[pos]['level']
pos = pos - 1
while pos >= 0 and fs[pos]['level'] >= thisLevel:
pos = pos - 1
return pos
#
# Change the hierarchy of titles : <h1>-><hn>, <h2>-><hn+1>, so that the
# highest hierarchy level is maxlevel
#
def remapheader(txt, maxlevel):
maxlvl=1
# find the highest hierarchy level in the content
while maxlvl < 7 and txt.find('<h' + str(maxlvl)) == -1:
maxlvl += 1
# if there is a hierarchy, remap it so that the highest level is maxlevel
if maxlvl < 7:
for i in range(6, maxlvl-1, -1):
txt = txt.replace('<h' + str(i), '<h' + str(i + maxlevel - maxlvl))
txt = txt.replace('</h' + str(i), '</h' + str(i + maxlevel - maxlvl))
return txt
#
# Creates the BreadCrumbs
#
def GetBreadCrumbs(fs, pos):
breadcrumbs = '<li class="active">'+ fs[pos]['title'] + '</li>'
while pos >= 0:
pos = GetParent(fs, pos)
if pos >= 0:
breadcrumbs='<li><a href="/' + fs[pos]['filename'] + '/">'+ fs[pos]['title'] + '</a></li>'+ breadcrumbs
breadcrumbs = '<ul class="breadcrumb"><li><a href="/toc/index.html">Home</a></li>' + breadcrumbs + '</ul>'
return breadcrumbs
#
# Make an array of children attached to each node in the file structure
# (It's a quasi-tree structure, and can be traversed as such.)
#
def FindChildren(fs):
childArray = []
for i in range(len(fs)):
if HaveChildren(fs, i):
childArray.append(GetChildren(fs, i))
else:
childArray.append([])
return childArray
#
# Make an array of the top level nodes in the file structure
#
def FindTopLevelNodes(fs):
level0 = []
for i in range(len(fs)):
if fs[i]['level'] == 0:
level0.append(i)
return level0
#
# Find all header links and create a dictionary out of them
#
def FindInternalLinks(fs):
linkDict = {}
for hdr in fs:
if 'link' in hdr:
linkDict['"@@' + hdr['link'] + '"'] = '"/' + hdr['filename'] + '/"'
linkDict['"@@' + hdr['link'] + '#'] = '"/' + hdr['filename'] + '/index.html#'
return linkDict
#
# Same as above, but create anchors (for the one-page version)
#
def FindInternalAnchors(fs):
linkDict = {}
for hdr in fs:
if 'link' in hdr:
linkDict['"@@' + hdr['link'] + '"'] = '"#' + hdr['link'] + '"'
linkDict['"@@' + hdr['link'] + '#'] = '"#' + hdr['link'] + '"'
return linkDict
#
# Internal links are of the form '@@link-name', which are references to the
# 'link:' field in the part header. We have to find all occurrences and replace
# them with the appropriate link.
#
def FixInternalLinks(links, content, title):
global findLinks
match = findLinks.findall(content)
missing = []
if len(match) > 0:
for s in match:
if s in links:
content = content.replace(s, links[s])
else:
missing.append(s)
# Report missing link targets to the user (if any)
if len(missing) > 0:
print('\nMissing link target' + ('s' if len(missing) > 1 else '') + ' in "' + title + '":')
for s in missing:
print(' ' + s)
print()
return content
#
# Recursively build a list of links based on the location of the page we're
# looking at currently
#
def BuildList(lst, fs, pagePos, cList):
content = '<ul>\n'
for i in range(len(lst)):
curPos = lst[i]
nextPos = lst[i + 1] if i + 1 < len(lst) else len(fs)
active = ' class=active' if curPos == pagePos else ''
menuTitle = fs[curPos]['menu_title'] if 'menu_title' in fs[curPos] else fs[curPos]['title']
content = content + '\t<li' + active + '><a href="/' + fs[curPos]['filename'] + '/">' + menuTitle + '</a></li>\n'
# If the current page is our page, and it has children, enumerate them
if curPos == pagePos:
if len(cList[curPos]) > 0:
content = content + BuildList(cList[curPos], fs, -1, cList)
# Otherwise, if our page lies between the current one and the next,
# build a list of links from those nodes one level down.
elif (pagePos > curPos) and (pagePos < nextPos):
content = content + BuildList(cList[curPos], fs, pagePos, cList)
content = content + '</ul>\n'
return content
#
# Builds the sidebar for the one-page version
#
def BuildOnePageSidebar(fs):
content = '\n\n<ul class="toc">\n'
lvl = 0
levelNums = [0] * 5
for i in range(len(fs)):
# Handle Part/Chapter/subchapter/section/subsection numbering
level = fs[i]['level']
if level < 2:
levelNums[2] = 0
levelNums[level] = levelNums[level] + 1;
j = level
txtlevel = ''
while j > 0: #level 0 is the part number which is not shown
txtlevel = str(levelNums[j]) + '.' + txtlevel
j = j - 1
if len(txtlevel) > 0:
txtlevel = txtlevel[:-1] + ' - '
if 'link' in fs[i]:
anchor = fs[i]['link']
else:
anchor = fs[i]['filename']
while lvl < level:
content = content + '<ul class="toc">\n'
lvl = lvl + 1
while lvl > level:
content = content + '</ul>\n'
lvl = lvl - 1
content = content + '<li><a href="#' + anchor + '">' + txtlevel + fs[i]['title'] + '</a></li>\n'
content = content + '</ul>\n'
return content
#
# Create link sidebar given a position in the list.
#
def CreateLinkSidebar(fs, pos, childList):
# Build the list recursively from the top level nodes
content = BuildList(FindTopLevelNodes(fs), fs, pos, childList)
# Shove the TOC link and one file link at the top...
active = ' class=active' if pos < 0 else ''
content = content.replace('<ul>', '<ul><li' + active + '><a href="/toc/">Table of Contents</a></li>\n', 1)
return content
# Preliminaries
# We have command line arguments now, so deal with them
parser = argparse.ArgumentParser(description='A build script for the Ardour Manual')
parser.add_argument('-v', '--verbose', action='store_true', help='Display the high-level structure of the manual')
parser.add_argument('-q', '--quiet', action='store_true', help='Suppress all output (overrides -v)')
parser.add_argument('-d', '--devmode', action='store_true', help='Add content to pages to help developers debug them')
parser.add_argument('-p', '--pdf', action='store_true', help='Automatically generate PDF from content')
args = parser.parse_args()
verbose = args.verbose
noisy = not args.quiet
devmode = args.devmode
pdf = args.pdf
# --quiet overrides --verbose, so tell it to shut up if user did both
if not noisy:
verbose = False
level = 0
fileCount = 0
levelNums = [0] * 5
lastFile = ''
page = ''
onepage = ''
pdfpage = ''
toc = ''
pageNumber = 0
if noisy and devmode:
print('Devmode active: scribbling extra junk to the manual...')
if os.access(global_site_dir, os.F_OK):
if noisy:
print('Removing stale HTML data...')
shutil.rmtree(global_site_dir)
shutil.copytree('./source', global_site_dir)
# Read the template, and fix the stuff that's fixed for all pages
temp = open(global_screen_template)
template = temp.read()
temp.close()
template = template.replace('{{page.bootstrap_path}}', global_bootstrap_path)
template = template.replace('{{page.page_title}}', global_page_title)
if pdf:
template = template.replace('{{page.page_pdflink}}', global_pdflink)
else:
template = template.replace('{{page.page_pdflink}}', '')
# Same as above, but for the "One-Page" version
temp = open(global_onepage_template)
onepage = temp.read()
temp.close()
onepage = onepage.replace('{{page.bootstrap_path}}', global_bootstrap_path)
onepage = onepage.replace('{{page.page_title}}', global_page_title)
if pdf:
# Same as above, but for the PDF version
temp = open(global_pdf_template)
pdfpage = temp.read()
temp.close()
pdfpage = pdfpage.replace('{{page.page_title}}', global_page_title)
# Parse out the master document's structure into a dictionary list
fileStruct = GetFileStructure()
# Build a quasi-tree structure listing children at level + 1 for each node
nodeChildren = FindChildren(fileStruct)
# Create a dictionary for translation of internal links to real links
links = FindInternalLinks(fileStruct)
oplinks = FindInternalAnchors(fileStruct)
if noisy:
print('Found ' + str(len(links)) + ' internal link target', end='')
print('.') if len(links) == 1 else print('s.')
if noisy:
master = open(global_master_doc)
firstLine = master.readline().rstrip('\r\n')
master.close()
if firstLine == '<!-- exploded -->':
print('Parsing exploded file...')
elif firstLine == '<!-- imploded -->':
print('Parsing imploded file...')
else:
print('Parsing unknown type...')
# Here we go!
for header in fileStruct:
fileCount = fileCount + 1
content = ''
more = ''
lastLevel = level
level = header['level']
# Handle Part/Chapter/subchapter/section/subsection numbering
if level < 2:
levelNums[2] = 0
levelNums[level] = levelNums[level] + 1;
# This is totally unnecessary, but nice; besides which, you can capture
# the output to a file to look at later if you like :-)
if verbose:
for i in range(level):
print('\t', end='')
if (level == 0):
print('\nPart ' + num2roman(levelNums[0]) + ': ', end='')
elif (level == 1):
print('\n\tChapter ' + str(levelNums[1]) + ': ', end='')
print(header['title'])
# Handle TOC scriblings...
if level == 0:
toc = toc + '<h2>Part ' + num2roman(levelNums[level]) + ': ' + header['title'] + '</h2>\n';
elif level == 1:
toc = toc + '\t<p class="chapter">Ch. ' + str(levelNums[level]) + ': <a href="/' + header['filename'] + '/">' + header['title'] + '</a></p>\n'
elif level == 2:
toc = toc + '\t\t<p class="subchapter"><a href="/' + header['filename'] + '/">' + header['title'] + '</a></p>\n'
elif level == 3:
toc = toc + '<p class="section"><a href="/' + header['filename'] + '/">' + header['title'] + '</a></p>\n'
elif level == 4:
toc = toc + '<p class="subsection"><a href="/' + header['filename'] + '/">' + header['title'] + '</a></p>\n'
# Make the 'this thing contains...' stuff
if HaveChildren(fileStruct, pageNumber):
pages = GetChildren(fileStruct, pageNumber)
for pg in pages:
more = more + '<li>' + '<a href="/' + fileStruct[pg]['filename'] + '/">' + fileStruct[pg]['title'] + '</a>' + '</li>\n'
more = '<div id=subtopics>\n' + '<h2>This section contains the following topics:</h2>\n' + '<ul>\n' + more + '</ul>\n' + '</div>\n'
parent = GetParent(fileStruct, pageNumber)
# Make the 'Previous', 'Up' & 'Next' content
nLink = ''
pLink = ''
uLink = ''
if pageNumber > 0:
pLink = '<li class="previous"><a title="' + fileStruct[pageNumber - 1]['title'] + '" href="/' + fileStruct[pageNumber - 1]['filename'] + '/" class="previous"> ← Previous </a></li>'
if pageNumber < len(fileStruct) - 1:
nLink = '<li class="next"><a title="' + fileStruct[pageNumber + 1]['title'] + '" href="/' + fileStruct[pageNumber + 1]['filename'] + '/" class="next"> Next → </a></li>'
if level > 0:
uLink = '<li><a title="' + fileStruct[parent]['title'] + '" href="/' + fileStruct[parent]['filename'] + '/" class="active"> ↑ Up </a></li>'
else:
uLink = '<li><a title="Ardour Table of Contents" href="/toc/index.html" class="active"> ↑ Up </a></li>'
prevnext = '<ul class="pager">' + pLink + uLink + nLink + '</ul>'
# Make the BreadCrumbs
breadcrumbs = GetBreadCrumbs(fileStruct, pageNumber)
# Create the link sidebar
sidebar = CreateLinkSidebar(fileStruct, pageNumber, nodeChildren)
# Parts DO NOT have any content, they are ONLY an organizing construct!
# Chapters, subchapters, sections & subsections can all have content,
# but the basic fundamental organizing unit WRT content is still the
# chapter.
githubedit = ''
themechanger = '<span style="float:right;padding: 0 5px 5px 5px;position:relative;bottom:-5px;"><button class="theme-changer" onclick="changetheme()" title="Change Theme"></button></span>'
if level > 0:
if 'include' in header:
srcFile = open('include/' + header['include'])
githubedit = '<span style="float:right;padding:5px;"><a class="github-link" title="Edit in GitHub" href="' + global_githuburl + header['include'] + '"></a></span>'
content = srcFile.read()
srcFile.close()
# Get rid of any extant header in the include file
# (once this is accepted, we can nuke this bit, as content files
# will not have any headers or footers in them)
content = re.sub('---.*\n(.*\n)*---.*\n', '', content)
content = content.replace('{% children %}', '')
else:
if 'content' in header:
content = header['content']
else:
content = '[something went wrong]'
# Add header information to the page if in dev mode
if devmode:
devnote ='<aside style="background-color:indigo; color:white;">'
if 'filename' in header:
devnote = devnote + 'filename: ' + header['filename'] + '<br>'
if 'include' in header:
devnote = devnote + 'include: ' + header['include'] + '<br>'
if 'link' in header:
devnote = devnote + 'link: ' + header['link'] + '<br>'
content = devnote + '</aside>' + content
# ----- One page and PDF version -----
# Fix up any internal links
opcontent = FixInternalLinks(oplinks, content, header['title'])
opcontent = remapheader(opcontent, level+2)
# Create "one page" header
oph = '<h' + str(level+1) + ' class="clear" id="' + header[('link' if 'link' in header else 'filename')] +'">' + header['title'] + '</h' + str(level+1) + '>\n';
# Set up the actual page from the template
onepage = onepage.replace('{{ content }}', oph + '\n' + opcontent + '\n{{ content }}')
if pdf:
if not 'pdf-exclude' in header:
pdfpage = pdfpage.replace('{{ content }}', oph + '\n' + opcontent + '\n{{ content }}')
else:
pdfpage = pdfpage.replace('{{ content }}', oph + '\n' + 'Please refer to the <a href="' + global_manual_url + '/' + header['filename'] + '/">online manual</a>.\n{{ content }}')
# ----- Normal version -----
# Fix up any internal links
content = FixInternalLinks(links, content, header['title'])
# Set up the actual page from the template
if 'style' not in header:
page = re.sub("{% if page.style %}.*\n.*\n{% endif %}.*\n", "", template)
else:
page = template.replace('{{page.style}}', header['style'])
page = page.replace('{% if page.style %}', '')
page = page.replace('{% endif %}', '')
page = page.replace('{{ page.title }}', header['title'])
page = page.replace('{% tree %}', sidebar)
page = page.replace('{% prevnext %}', prevnext)
page = page.replace('{% githubedit %}', githubedit)
page = page.replace('{% themechanger %}', themechanger)
page = page.replace('{% breadcrumbs %}', breadcrumbs)
page = page.replace('{{ content }}', content + more)
# Create the directory for the index.html file to go into (we use makedirs,
# because we have to in order to accommodate the 'uri' keyword)
os.makedirs(global_site_dir + header['filename'], 0o775, exist_ok=True)
# Finally, write the file!
destFile = open(global_site_dir + header['filename'] + '/index.html', 'w')
destFile.write(page)
destFile.close()
# Save filename for next header...
lastFile = header['filename']
pageNumber = pageNumber + 1
# Finally, create the TOC
sidebar = CreateLinkSidebar(fileStruct, -1, nodeChildren)
page = re.sub("{% if page.style %}.*\n.*\n{% endif %}.*\n", "", template)
page = page.replace('{{ page.title }}', 'Ardour Table of Contents')
page = page.replace('{% tree %}', sidebar)
page = page.replace('{{ content }}', toc)
page = page.replace('{% prevnext %}', '')
page = page.replace('{% githubedit %}', '')
page = page.replace('{% themechanger %}', themechanger)
page = page.replace('{% breadcrumbs %}', '')
os.mkdir(global_site_dir + 'toc', 0o775)
tocFile = open(global_site_dir + 'toc/index.html', 'w')
tocFile.write(page)
tocFile.close()
# Create the one-page version of the documentation
onepageFile = open(global_site_dir + 'ardourmanual.html', 'w')
opsidebar = BuildOnePageSidebar(fileStruct) # create the link sidebar
onepage = onepage.replace('{% tree %}', opsidebar)
onepage = onepage.replace('{{ content }}', '') # cleans up the last spaceholder
onepageFile.write(onepage)
onepageFile.close()
if pdf:
if noisy:
print('Generating the PDF...')
import logging
logger = logging.getLogger('weasyprint')
logger.addHandler(logging.StreamHandler())
# Create the PDF version of the documentation
pdfpage = pdfpage.replace('{% tree %}', opsidebar) # create the TOC
pdfpage = pdfpage.replace('{{ content }}', '') # cleans up the last spaceholder
pdfpage = pdfpage.replace('{{ today }}', global_today)
pdfpage = pdfpage.replace('src="/images/', 'src="images/') # makes images links relative
pdfpage = pdfpage.replace('url(\'/images/', 'url(\'images/') # CSS images links relative
# Write it to disk (optional, can be removed)
pdfpageFile = open(global_site_dir + 'pdf.html', 'w')
pdfpageFile.write(pdfpage)
pdfpageFile.close()
# Generating the actual PDF with weasyprint (https://weasyprint.org/)
from weasyprint import HTML, __version__
if float(__version__) < 53.0: # handle an API change
from weasyprint.fonts import FontConfiguration
else:
from weasyprint.text.fonts import FontConfiguration
html_font_config = FontConfiguration()
doc = HTML(string = pdfpage, base_url = global_site_dir)
doc.write_pdf(global_site_dir + 'manual.pdf', font_config = html_font_config)
# URI redirects
if os.path.exists(global_redirects_file):
with open(global_redirects_file) as f:
cpt = 0;
for line in f:
line = line.strip();
if line == '':
continue #empty line
if line.startswith('//'):
continue #comment
params = line.split('|');
params[0] = params[0].strip(' \t/')
params[1] = params[1].strip(' \t')
# write an HTML the file with a refresh redirect
os.makedirs(global_site_dir + params[0], 0o775, exist_ok=True)
destFile = open(global_site_dir + params[0] + '/index.html', 'w')
newlink = FixInternalLinks(links, '"'+params[1]+'"', global_redirects_file)
newlink = newlink.strip('"')
page = '<html><head><meta http-equiv="refresh" content="0; url=' + newlink + '"></head><body><p>The page has moved to:<a href="' + newlink + '">this page</a></p></body></html>'
destFile.write(page)
destFile.close()
if noisy:
print('Generated ' + str(cpt) + ' redirection files.')
if noisy:
print('Processed ' + str(fileCount) + ' files.')