generated from LPBeaulieu/Typewriter-OCR-TintypeText
-
Notifications
You must be signed in to change notification settings - Fork 1
/
Copy pathprintanotebook.py
2813 lines (2646 loc) · 175 KB
/
printanotebook.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
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
#**PRINTING TIPS ON LASER PRINTER** In my case, using a Brother HL-L5200DW
#printer, the best printing results were obtained at the highest resolution
#(HQ1200 dpi), with the "text" mode selected (and not the "graphics" mode).
import glob
import numpy as np
import os
from PIL import Image, ImageDraw, ImageFont, ImageOps
import re
import sys
from datetime import date
import math
cwd = os.getcwd()
#The "problem" variable is initialized to "False"
#and will be set to "True" should the code encounter
#any problems, in order to give the user relevant error
#messages along the way.
problem = False
title = None
author = None
#If the title needs to be split
#in order to fit in the title page,
#the value of "asjusted_title_rtf"
#will be set to the string containing
#a "\line" linebreak RTF command and
#will be updated in text[title_index].
#A similar approach is taken for the
#title and author name on the cover.
adjusted_title_cover = None
adjusted_author_cover = None
#"spine_text" is initialized as "None",
#and a value can be supplied by the user
#should they want to use different text
#than the abbreviated author name, followed
#by a hyphen and the book title.
spine_text = None
#The users may choose to perforate their notebook
#pages and covers to bind them using standard
#ring binders or a discbound option. If so,
#the value of "perforated_cover" will be set to "True".
perforated_cover = False
#Should the cover title need to be split,
#the default line spacing in-between title lines
#is initialized at 5 pixels, and may be altered
#by the user.
cover_title_line_spacing = 5
cover_author_line_spacing = 4
cover_box_color = None
cover_text_color = None
cover_trim_width = 0.25
#The "cover_line" variable determines whether
#a dark border will be present on the cover,
#before the white trim. The default setting
#includes such a border, but as the users
#may wish to trim their pages using a stack
#page guillotine cutter, and the presence of
#a dark line would likely leave behind some
#uneven line after cutting, they may wish to remove
#such a line by passing the argument "no_cover_line"
#when running the Python code.
cover_line = True
#An extra 20 pixels are added to the cover width,
#to account for binding irregularities and the
#thickness of the glue:
cover_extra_pixels = 20
#The "pixels_from_bottom_cover_spine" variable
#determines how many pixels are added to the
#starting "y" coordinate (in the rotated image)
#from the bottom of the spine box to reach the
#point where the spine text will start to be written.
#Negative values will bring the text down.
pixels_from_bottom_cover_spine = 3
#A similar approach is taken with the variable
#"pixels_from_left_cover_spine" to determine how
#many pixels are added to the starting "x" coordinate
#(in the rotated image) from the left edge of the
#spine box to reach the point where the spine
#text will start to be written. Negative values
#will bring the text left.
pixels_from_left_cover_spine = 0
#The "pixels_from_top_cover_title_box" variable
#determines how many pixels are added to the
#starting "y" coordinate (in the unrotated image)
#from the top of the cover title box to reach the
#point where the cover title text will start to be
#written. Negative values will bring the text up.
pixels_from_top_cover_title_box = 10
#A similar approach is taken with the variable
#"pixels_from_left_cover_title_box" to determine how
#many pixels are added to the starting "x" coordinate
#(in the unrotated image) from the left edge of the
#cover title box to reach the point where the cover
#title text will start to be written. Negative
#values will bring the text left.
pixels_from_left_cover_title_box = 0
number_of_pages = 192
inches_per_ream_500_pages = None
cm_per_ream_500_pages = None
grayscale = False
#The "cover_title_font_size" is initialized
#at 125 pixels and the code will determine the largest
#font size that fits within the front cover box.
#The user can specify another starting value for
#"cover_title_font_size".
cover_title_font_size = 125
#The spacing on the cover in-between
#the title and the author name will be
#a certain proportion of the cover title
#height and is set to 20% by default.
cover_spacing_title_height_ratio = 0.20
#The "max_author_title_font_ratio" variables
#determines the max ratio between the title
#headings font size and that of the author name,
#to provide a starting font size while automatically
#adjusting the font size to the available space.
max_author_title_font_ratio = 0.75
#The "cover_author_font_size" is initialized
#at 94 pixels and the code will determine the largest
#font size that fits within the front cover box.
#The user can specify another starting
#value for "cover_author_font_size"
cover_author_font_size = 94
#Similarly, the "spine_font_size"
#default value is set at 100 pixels,
#and the code will determine the largest
#font size that fits within the spine.
#The user can specify another starting
#value for "spine_font_size".
spine_font_size = 100
no_merging = False
list_of_page_images = []
#The "heading_text_left" and "heading_text_right" variables
#are initialized to "None" and will be set to the user's
#input text. These two variables may have the same value,
#if the user input the titles with "heading_text:Title".
#The default font sizes for the header is set at 75 px
#and that of the footer containing the page numbers is
#initialized at 60 px. The user can change these values,
#along with the text color "heading_text_color" and
#"page_numbers_text_color".
heading_text_left = None
heading_text_right = None
heading_font_size = 75
heading_text_color = "LightSlateGrey"
page_numbers_font_size = 60
page_numbers_text_color = "LightSteelBlue"
#The headers can either be located in the outer corners
#of the pages "heading_corner", or centerd (default setting).
heading_corner = False
#All of the pages included in the table of contents
#are listed in "TOC_pages_list", with a default of
#eight pages. It is important that the number of pages
#for the table of contents be an even number, so that
#the first of the numbered notebook pages lands on
#a right-hand page. For this reason, when the user
#inputs an odd-numbered value for "TOC_pages", it
#will automatically be incremented by one page count.
TOC_pages = 8
TOC_pages_list = list(range(1,9))
#The font size for the "Subject" heading in the table of contents
#("TOC_subject_text") is set to sixty pixels by default and the color
#is set to "LightSlateGrey", both of which may be changed by the user.
#The same goes for the "Pages" heading. The user can also change the
#text of these headings, as well as that of the "Content" heading.
TOC_subject_font_size = 60
TOC_subject_text = "Subject"
TOC_subject_text_color = "LightSlateGrey"
TOC_pages_font_size = 60
TOC_pages_text = "Pages"
TOC_pages_text_color = "LightSlateGrey"
TOC_line_spacing = None
TOC_heading_font_size = 75
TOC_heading_text = "Content"
TOC_heading_text_color = "LightSlateGrey"
dot_y_coordinates = None
line_y_coordinates = None
line_y_coordinates_TOC = None
line_y_coordinates_graph = None
#The "page_numbers" variable is set to "None", and will
#be initialized at 1 and incremented by 1 every time a
#page number is written in the footer, should the user
#add the page numbering arguments. The "page_numbers_left",
#would only add page numbering on the left pages, and vice
#versa for "page_numbers_right". The starting page number
#is set to 1.
page_numbers = None
page_number = 1
page_numbers_left = None
page_numbers_right = None
#The "heading_top_margin_y_pixel" maps to the "y"
#pixel where the heading text is written.
heading_top_margin_y_pixel = 0.60*300
#Similarly, "page_numbers_bottom_margin_y_pixel" maps to the
#"y" pixel where the page numbers are written.
#The variable "page_numbers_bottom_margin_y_pixel" designates
#the "y" coordinate mapping to the vertical middle point of the
#page numbers. By default, this variable is set as "None", and the
#user can either set it manually, or the code will determine whether there
#is sufficient space to vertically center the page numbers in the space
#in-between the last horizontal line and the bottom of the page. Should
#there be less than 75 pixels below the page number for it to be
#vertically centered, the code will automatically bring the text up,
#such that the lowest "y" pixel of the page number is above 75 pixels
#from the bottom of the page, thereby respecting the default 0.25 inch
#non-printable area for most printers.
page_numbers_bottom_margin_y_pixel = None
#The "top_margin_y_pixel" maps to the "y" pixel
#where the lines or dots start being drawin on
#the pages.
top_margin_y_pixel = 0.95*300
#Similarly, the "bottom_margin_y_pixel" maps to
#the "y" pixel where the lines and dots end.
bottom_margin_y_pixel = 2550-(0.60*300)
#The variables "left_margin_x_pixel" and
#"right_margin_x_pixel" map to the "x"
#pixels where the lines and dots start and
#stop being drawn on the pages, respectively.
left_margin_x_pixel = 0.25*300
right_margin_x_pixel = 3300-(0.25*300)
#The "gutter_margin_width_pixels" designates the
#width (in pixels) of the gutter margins of the
#notebook. They are set to the pixel equivalent
#of an eighth of an inch, so they won't be noticeable
#when opening a bound book.
gutter_margin_width_pixels = 0.125*300
#The various page formattings (lines,
#graph paper, dots) are initialized as
#False, so that the notebook would have
#blank page by default, unless the user
#specifies a certing formatting element.
#Of note, the formatting elements can be
#applied only to odd (right) pages, only
#to even (left) pages, or to both pages
#(variables without mention of right or left).
college_ruled = False
college_ruled_left = False
college_ruled_right = False
wide_ruled = False
wide_ruled_left = False
wide_ruled_right = False
custom_ruled = False
custom_ruled_left = False
custom_ruled_right = False
custom_line_distance_inches = None
graph_paper = False
graph_paper_left = False
graph_paper_right = False
dot_grid = False
dot_grid_left = False
dot_grid_right = False
#ScriptReader is another github repo that
#enables the user to train a OCR convoluted
#neural network model on their handwriting,
#using customized dot grid sheets generated
#with PrintANotebook. If the user enters the
#appropriate parameters, "scriptreader" will
#be set to "True".
scriptreader_left = False
scriptreader_right = False
scriptreader = False
#The "scriptreader_acetate" option will generate
#notebook pages with dot grids for printing on
#acetates (preferably on a laser printer), such
#that the pages may be written on with an erasable
#marker, submitted to OCR and erased afterwards.
#The user would need to print the pages up to the halfway
#point on the acetates, then flip the stack of acetates
#and print the remaining pages on the other side, thus
#allowing to generate two right hand pages with the same
#gutter margin from a single acetate sheet. A vertical
#dotted line will indicate where the user should cut the acetate,
#in order to later perforate the half-letter acetate sheets
#and use them in a binder.
scriptreader_acetate = False
#The list of line indices where characters will be segmented
#if the ScriptReader option is selected ("text_line_numbers")
#is initialized including the zero index, as the first line of text needs to be
#on the first line, and then at a regular interval thereafter after that. There
#is a default of three empty lines in-between every line of text, to minimize
#the overlapping of ascenders and descenders of adjacent text lines.
text_line_numbers = None
#The "dot_y_shift_down" variable stores the
#amount of pixels that will be added to the
#starting "y" coordinate. The value of
#"dot_y_shift_down" is only different than
#zero if there are no ruled lines in the notebook,
#so that the dots may line up with TOC ruled lines.
dot_y_shift_down = 0
#The user can choose to add a design to one or
#both pages by adding the JPEG image to the
#working folder, of which the file name starts
#with "page", so that the code might distinguish
#it from the cover image (of which the file name
#begins with "cover"). The user would then pass in
#"custom_template_both_pages", "custom_template_left_page"
#or "custom_template_right_page" when running the code.
custom_template_left_page = False
custom_template_right_page = False
#The line and graph lines colors are
#set to "Gainsboro" by default and
#may be changed by the user.
line_color = "Gainsboro"
TOC_line_color = "Gainsboro"
graph_line_color = "Gainsboro"
#If the user defines the dot fill color
#as white, the outline color can be some
#other color
dot_fill_color = "LightSlateGrey"
dot_outline_color = "LightSlateGrey"
#The diameter of the dots (in pixels)
#is defined by the variable
#"dot_diameter_pixels".
dot_diameter_pixels = 5
#If the user defines the dot fill color
#as white, the outline color can be some
#other color, and the "dot_line_width"
#will determine the boldness of that
#outline.
dot_line_width = 1
#The spacing in-between dots is
#set to a fifth of an inch by
#default and may be changed by
#the user.
inches_between_dots = 0.20
#The line width of ruled lines
#and graph lines are set to 5
#pixels by default an may be
#changed by the user.
line_width = 5
TOC_line_width = 5
graph_line_width = 5
#The number of squares per inch
#("squares_per_inch") is set to
#"None" by default and the user
#must specify this value when
#selecting the graph paper
#formatting. For example, for
#four squares per inch, with a
#bold line every four squares and
#with a bold line thickness 1.75 times
#that of the default 5 pixels, the
#user would enter "graph_paper:4:4:1.75",
#where the first number is "squares_per_inch",
#the second digit is "bold_line_every_n_squares",
#and the final number is "line_boldness_factor"
squares_per_inch = None
line_boldness_factor = 1
bold_line_every_n_squares = 5
#The default left and right
#margins on the cover page are set to 0.75 inches
#from the edges of the half-letter page (5.5 inches wide).
#The left margin can be determined by subtracting the space
#in-between the margins (4.75 inches) from the right edge
#pixel count: (4200 - 4.75*300 = 2775 px)
left_margin_cover_textbox = 2775
#The right margin can simply be calculated given the pixel
#width of the canvas (4220-0.75*300 = 3995 px)
right_margin_cover_textbox = 3995
#The top margin of the text box on the cover page can
#be determined by adding a 25% of the vertical
#pixels to the starting y corrdinate of 0. (0+(2550/4)).
top_margin_cover_textbox = 640
if len(sys.argv) > 1:
#The "try/except" statement will
#intercept any "ValueErrors" and
#ask the users to correctly enter
#the desired values for the variables
#directly after the colon separating
#the variable name from the value.
try:
for i in range(1, len(sys.argv)):
if sys.argv[i][:6] == "title:":
title = sys.argv[i][6:]
elif sys.argv[i][:7] == "author:":
if len(sys.argv[i][7:]) > 3 and sys.argv[i][7:10].lower() == "by ":
author = sys.argv[i][10:]
author_names = re.split(r"( )", author)
for j in range(len(author_names)):
if author_names[j].lower() != "by":
author_names[j] == author_names[j].capitalize()
author = "".join(author_names).strip()
elif len(sys.argv[i][7:]) > 3 and sys.argv[i][7:10] != "by ":
author = sys.argv[i][7:].strip()
author_names = re.split(r"( )", author)
for j in range(len(author_names)):
author_names[j] == author_names[j].capitalize()
author = "".join(author_names).strip()
else:
author = sys.argv[i][7:].strip()
elif sys.argv[i].lower()[:16] == "number_of_pages:":
number_of_pages = int(sys.argv[i].lower()[16:].strip())
elif sys.argv[i].lower()[:26] == "inches_per_ream_500_pages:":
make_cover = True
inches_per_ream_500_pages = float(sys.argv[i][26:].strip())
elif sys.argv[i].lower()[:22] == "cm_per_ream_500_pages:":
make_cover = True
cm_per_ream_500_pages = float(sys.argv[i][22:].strip())
inches_per_ream_500_pages = cm_per_ream_500_pages/2.54
elif sys.argv[i].strip().lower() == "grayscale" or sys.argv[i].strip().lower() == "greyscale":
grayscale = True
elif sys.argv[i].lower()[:16] == "cover_box_color:":
cover_box_color = sys.argv[i].lower()[16:].strip()
elif sys.argv[i].lower()[:17] == "cover_text_color:":
cover_text_color = sys.argv[i].lower()[17:].strip()
elif sys.argv[i].lower()[:22] == "cover_title_font_size:":
cover_title_font_size = round(sys.argv[i][22:].strip())
elif sys.argv[i].lower()[:23] == "cover_author_font_size:":
cover_author_font_size = round(sys.argv[i][23:].strip())
elif sys.argv[i].lower()[:16] == "spine_font_size:":
spine_font_size = round(sys.argv[i][16:].strip())
elif sys.argv[i].lower()[:33] == "cover_spacing_title_height_ratio:":
cover_spacing_title_height_ratio = float(sys.argv[i][33:].strip())
elif sys.argv[i].strip().lower()[:17] == "cover_trim_width:":
cover_trim_width = float(sys.argv[i][17:].strip())
elif sys.argv[i].strip().lower()[:20] == "cover_trim_width_cm:":
cover_trim_width = float(sys.argv[i][20:].strip())/2.54
elif sys.argv[i].strip().lower()[:13] == "no_cover_line":
cover_line = False
elif sys.argv[i].strip().lower()[:19] == "cover_extra_inches:":
inches = float(sys.argv[i].strip()[19:])
cover_extra_pixels = round(inches*300)
elif sys.argv[i].strip().lower()[:15] == "cover_extra_cm:":
cm = float(sys.argv[i].strip()[15:])
cover_extra_pixels = round(cm/2.54*300)
elif sys.argv[i].strip().lower()[:31] == "pixels_from_bottom_cover_spine:":
pixels_from_bottom_cover_spine = int(sys.argv[i].strip()[31:])
elif sys.argv[i].strip().lower()[:29] == "pixels_from_left_cover_spine:":
pixels_from_left_cover_spine = int(sys.argv[i].strip()[29:])
elif sys.argv[i].strip().lower()[:32] == "pixels_from_top_cover_title_box:":
pixels_from_top_cover_title_box = int(sys.argv[i].strip()[32:])
elif sys.argv[i].strip().lower()[:33] == "pixels_from_left_cover_title_box:":
pixels_from_left_cover_title_box = int(sys.argv[i].strip()[33:])
elif sys.argv[i].strip().lower()[:11] == "spine_text:":
spine_text = sys.argv[i].strip()[11:]
#The elif statements below are specific to PrintANotebook
elif sys.argv[i].lower()[:12] == "left_margin:":
inches = float(sys.argv[i][12:].strip())
left_margin_x_pixel = round(inches*300)
elif sys.argv[i].lower()[:13] == "right_margin:":
inches = float(sys.argv[i][13:].strip())
right_margin_x_pixel = 3300-round(inches*300)
elif sys.argv[i].lower()[:11] == "top_margin:":
inches = float(sys.argv[i][11:].strip())
top_margin_y_pixel = round(inches*300)
elif sys.argv[i].lower()[:14] == "bottom_margin:":
inches = float(sys.argv[i][14:].strip())
bottom_margin_y_pixel = 2550-round(inches*300)
elif sys.argv[i].lower()[:14] == "gutter_margin:":
gutter_margin_width_pixels = round(float(sys.argv[i].lower()[14:].strip())*300)
elif sys.argv[i].lower()[:19] == "heading_top_margin:":
inches = float(sys.argv[i][19:].strip())
heading_top_margin_y_pixel = round(inches*300)
elif sys.argv[i].lower()[:27] == "page_numbers_bottom_margin:":
inches = float(sys.argv[i][27:].strip())
page_numbers_bottom_margin_y_pixel = 2550-round(inches*300)
elif sys.argv[i].strip().lower()[:18] == "heading_text_left:":
heading_text_left = sys.argv[i][18:]
elif sys.argv[i].strip().lower()[:19] == "heading_text_right:":
heading_text_right = sys.argv[i][19:]
elif sys.argv[i].strip().lower()[:13] == "heading_text:":
heading_text_left = sys.argv[i][13:]
heading_text_right = sys.argv[i][13:]
elif sys.argv[i].lower()[:19] == "heading_text_color:":
heading_text_color = sys.argv[i].lower()[19:].strip()
if heading_text_color[0] == "(":
heading_text_color = "rgb" + heading_text_color
elif sys.argv[i].lower()[:14] == "heading_corner":
heading_corner = True
elif sys.argv[i].strip().lower()[:22] == "toc_heading_font_size:":
TOC_heading_font_size = int(sys.argv[i].strip()[22:])
elif sys.argv[i].strip().lower()[:17] == "toc_heading_text:":
TOC_heading_text = sys.argv[i][17:]
elif sys.argv[i].lower()[:23] == "toc_heading_text_color:":
TOC_heading_text_color = sys.argv[i].lower()[23:].strip()
if TOC_heading_text_color[0] == "(":
TOC_heading_text_color = "rgb" + TOC_heading_text_color
elif sys.argv[i].strip().lower()[:18] == "heading_font_size:":
heading_font_size = int(sys.argv[i].strip()[18:])
elif sys.argv[i].strip().lower()[:18] == "heading_left_pages":
heading_left_pages = True
elif sys.argv[i].strip().lower()[:19] == "heading_right_pages":
heading_right_pages = True
elif sys.argv[i].lower()[:24] == "page_numbers_text_color:":
page_numbers_text_color = sys.argv[i].lower()[24:].strip()
if page_numbers_text_color[0] == "(":
page_numbers_text_color = "rgb" + page_numbers_text_color
elif sys.argv[i].strip().lower()[:23] == "page_numbers_font_size:":
page_numbers_font_size = int(sys.argv[i].strip()[23:])
elif sys.argv[i].strip().lower()[:17] == "page_numbers_left":
page_numbers_left = True
elif sys.argv[i].strip().lower()[:18] == "page_numbers_right":
page_numbers_right = True
elif sys.argv[i].strip().lower()[:12] == "page_numbers":
page_numbers = True
elif sys.argv[i].strip().lower()[:18] == "college_ruled_left":
college_ruled_left = True
elif sys.argv[i].strip().lower()[:19] == "college_ruled_right":
college_ruled_right = True
elif sys.argv[i].strip().lower()[:13] == "college_ruled":
college_ruled = True
elif sys.argv[i].strip().lower()[:15] == "wide_ruled_left":
wide_ruled_left = True
elif sys.argv[i].strip().lower()[:16] == "wide_ruled_right":
wide_ruled_right = True
elif sys.argv[i].strip().lower()[:10] == "wide_ruled":
wide_ruled = True
elif sys.argv[i].strip().lower()[:18] == "custom_ruled_left:":
custom_line_distance_inches = float(sys.argv[i].strip()[18:])
custom_ruled_left = True
elif sys.argv[i].strip().lower()[:19] == "custom_ruled_right:":
custom_line_distance_inches = float(sys.argv[i].strip()[19:])
custom_ruled_right = True
elif sys.argv[i].strip().lower()[:13] == "custom_ruled:":
custom_line_distance_inches = float(sys.argv[i].strip()[13:])
custom_ruled = True
elif sys.argv[i].strip().lower()[:17] == "graph_paper_left:":
graph_paper_left = True
arguments = sys.argv[i].strip()[17:].split(":")
if arguments != [""]:
for j in range(len(arguments)):
if j == 0:
squares_per_inch = int(arguments[j])
elif j == 1:
bold_line_every_n_squares = int(arguments[j])
elif j == 2:
line_boldness_factor = float(arguments[j])
elif sys.argv[i].strip().lower()[:18] == "graph_paper_right:":
graph_paper_right = True
arguments = sys.argv[i].strip()[18:].split(":")
if arguments != [""]:
for j in range(len(arguments)):
if j == 0:
squares_per_inch = int(arguments[j])
elif j == 1:
bold_line_every_n_squares = int(arguments[j])
elif j == 2:
line_boldness_factor = float(arguments[j])
elif sys.argv[i].strip().lower()[:12] == "graph_paper:":
graph_paper = True
arguments = sys.argv[i].strip()[12:].split(":")
if arguments != [""]:
for j in range(len(arguments)):
if j == 0:
squares_per_inch = int(arguments[j])
elif j == 1:
bold_line_every_n_squares = int(arguments[j])
elif j == 2:
line_boldness_factor = float(arguments[j])
elif (sys.argv[i].strip().lower()[:14] == "dot_grid_left:" or
sys.argv[i].strip().lower()[:13] == "dot_grid_left"):
dot_grid_left = True
arguments = sys.argv[i].strip()[14:].split(":")
if arguments != [""]:
for j in range(len(arguments)):
if j == 0:
inches_between_dots = float(arguments[j])
elif j == 1:
dot_diameter_pixels = int(arguments[j])
elif j == 2:
dot_line_width = int(arguments[j])
elif (sys.argv[i].strip().lower()[:15] == "dot_grid_right:" or
sys.argv[i].strip().lower()[:14] == "dot_grid_right"):
dot_grid_right = True
arguments = sys.argv[i].strip()[15:].split(":")
if arguments != [""]:
for j in range(len(arguments)):
if j == 0:
inches_between_dots = float(arguments[j])
elif j == 1:
dot_diameter_pixels = int(arguments[j])
elif j == 2:
dot_line_width = int(arguments[j])
elif sys.argv[i].lower()[:21] == "scriptreader_acetate:":
perforated_cover = True
scriptreader_right = True
scriptreader_acetate = True
#The counter "current_acetate_page_number" will keep track
#of the acetate sheet number (two pages per acetate sheet),
#in order to only draw a vertical line on one side of the acetate
#sheet (as an indicator of where to cut the acetate in half).
#Otherwise, if the lines were drawn on both sides of the
#acetate, it would end up looking messy, as they would likely
#not line up.
current_acetate_page_number = 0
#If the user has selected to print some custom
#dot grid pages for use in the handwriting OCR
#application ScriptReader, they will likely want
#to perforate the pages for binding, and so a wider
#gutter margins of 0.75 inch is included by default,
#which may be overriden if the user has specified
#a different gutter margin as the fifth argument.
gutter_margin_width_pixels = 0.75*300
arguments = sys.argv[i].strip()[21:].split(":")
if arguments != [""]:
for j in range(len(arguments)):
if j == 0:
inches_between_dots = float(arguments[j])
elif j == 1:
dot_diameter_pixels = int(arguments[j])
elif j == 2:
dot_line_width = int(arguments[j])
elif j == 3:
lines_between_text = int(arguments[j])
elif j == 4:
gutter_margin_width_pixels = round(float(arguments[j])*300)
elif sys.argv[i].lower()[:18] == "scriptreader_left:":
perforated_cover = True
scriptreader_left = True
#If the user has selected to print some custom
#dot grid pages for use in the handwriting OCR
#application ScriptReader, they will likely want
#to perforate the pages for binding, and so a wider
#gutter margins of 0.75 inch is included by default,
#which may be overriden if the user has specified
#a different gutter margin as the fifth argument.
gutter_margin_width_pixels = 0.75*300
arguments = sys.argv[i].strip()[18:].split(":")
if arguments != [""]:
for j in range(len(arguments)):
if j == 0:
inches_between_dots = float(arguments[j])
elif j == 1:
dot_diameter_pixels = int(arguments[j])
elif j == 2:
dot_line_width = int(arguments[j])
elif j == 3:
lines_between_text = int(arguments[j])
elif j == 4:
gutter_margin_width_pixels = round(float(arguments[j])*300)
elif sys.argv[i].lower()[:19] == "scriptreader_right:":
perforated_cover = True
scriptreader_right = True
gutter_margin_width_pixels = 0.75*300
arguments = sys.argv[i].strip()[19:].split(":")
if arguments != [""]:
for j in range(len(arguments)):
if j == 0:
inches_between_dots = float(arguments[j])
elif j == 1:
dot_diameter_pixels = int(arguments[j])
elif j == 2:
dot_line_width = int(arguments[j])
elif j == 3:
lines_between_text = int(arguments[j])
elif j == 4:
gutter_margin_width_pixels = round(float(arguments[j])*300)
elif sys.argv[i].lower()[:13] == "scriptreader:":
perforated_cover = True
scriptreader = True
gutter_margin_width_pixels = 0.75*300
arguments = sys.argv[i].strip()[13:].split(":")
if arguments != [""]:
for j in range(len(arguments)):
if j == 0:
inches_between_dots = float(arguments[j])
elif j == 1:
dot_diameter_pixels = int(arguments[j])
elif j == 2:
dot_line_width = int(arguments[j])
elif j == 3:
lines_between_text = int(arguments[j])
elif j == 4:
gutter_margin_width_pixels = round(float(arguments[j])*300)
elif sys.argv[i].lower()[:19] == "lines_between_text:":
lines_between_text = int(sys.argv[j].lower()[19:].strip())
elif (sys.argv[i].strip().lower()[:9] == "dot_grid:" or
sys.argv[i].strip().lower()[:8] == "dot_grid"):
dot_grid = True
arguments = sys.argv[i].strip()[9:].split(":")
if arguments != [""]:
for j in range(len(arguments)):
if j == 0:
inches_between_dots = float(arguments[j])
elif j == 1:
dot_diameter_pixels = int(arguments[j])
elif j == 2:
dot_line_width = int(arguments[j])
elif sys.argv[i].strip().lower()[:25] == "custom_template_left_page":
custom_template_left_page = True
elif sys.argv[i].strip().lower()[:26] == "custom_template_right_page":
custom_template_right_page = True
elif sys.argv[i].strip().lower()[:26] == "custom_template_both_pages":
custom_template_left_page = True
custom_template_right_page = True
elif sys.argv[i].strip().lower()[:15] == "dot_fill_color:":
dot_fill_color = sys.argv[i].strip().lower()[15:]
if dot_fill_color[0] == "(":
dot_fill_color = "rgb" + dot_fill_color
elif sys.argv[i].strip().lower()[:18] == "dot_outline_color:":
dot_outline_color = sys.argv[i].strip().lower()[18:]
if dot_outline_color[0] == "(":
dot_outline_color = "rgb" + dot_outline_color
elif sys.argv[i].strip().lower()[:17] == "graph_line_color:":
graph_line_color = sys.argv[i].strip().lower()[17:]
if graph_line_color[0] == "(":
graph_line_color = "rgb" + graph_line_color
elif sys.argv[i].strip().lower()[:17] == "graph_line_width:":
graph_line_width = int(sys.argv[i].strip()[17:])
elif sys.argv[i].strip().lower()[:15] == "toc_line_color:":
TOC_line_color = sys.argv[i].strip().lower()[15:]
if TOC_line_color[0] == "(":
TOC_line_color = "rgb" + TOC_line_color
elif sys.argv[i].strip().lower()[:11] == "line_color:":
line_color = sys.argv[i].strip().lower()[11:]
if line_color[0] == "(":
line_color = "rgb" + line_color
elif sys.argv[i].strip().lower()[:15] == "toc_line_width:":
TOC_line_width = int(sys.argv[i].strip()[15:])
elif sys.argv[i].strip().lower()[:11] == "line_width:":
line_width = int(sys.argv[i].strip()[11:])
elif sys.argv[i].strip().lower()[:18] == "toc_pages_spacing:":
arguments = sys.argv[i].strip()[18:].split(":")
if len(arguments) > 0:
for j in range(len(arguments)):
if j == 0:
TOC_pages = int(arguments[j])
if TOC_pages == 0:
TOC_pages_list = []
break
#The number of TOC pages needs to be
#even in order for the first numbered
#page to land on a right-hand page.
#Therefore, if "TOC_pages" is odd_numbered,
#it will be incremented by one page count.
elif TOC_pages%2 != 0:
TOC_pages+=1
TOC_pages_list = list(range(1, TOC_pages+1))
elif j == 1:
TOC_line_spacing = float(arguments[j])
elif sys.argv[i].strip().lower()[:10] == "no_merging":
no_merging = True
elif sys.argv[i].strip().lower()[:22] == "toc_subject_font_size:":
TOC_subject_font_size = int(sys.argv[i].strip()[22:])
elif sys.argv[i].strip().lower()[:23] == "toc_subject_text_color:":
TOC_subject_text_color = sys.argv[i].strip()[23:]
if TOC_subject_text_color[0] == "(":
TOC_subject_text_color = "rgb" + TOC_subject_text_color
elif sys.argv[i].strip().lower()[:17] == "toc_subject_text:":
TOC_subject_text = sys.argv[i].strip()[17:]
elif sys.argv[i].strip().lower()[:20] == "toc_pages_font_size:":
TOC_pages_font_size = int(sys.argv[i].strip()[20:])
elif sys.argv[i].strip().lower()[:21] == "toc_pages_text_color:":
TOC_pages_text_color = sys.argv[i].strip()[21:]
if TOC_pages_text_color[0] == "(":
TOC_pages_text_color = "rgb" + TOC_pages_text_color
elif sys.argv[i].strip().lower()[:15] == "toc_pages_text:":
TOC_pages_text = sys.argv[i].strip()[15:]
except Exception as e:
print(e)
problem = True
print("\nPlease enter the name of the parameter you wish to alter, followed " +
"by a colon, and the desired setting directly after the colon. For example, " +
'to set the font size of the heading text to 100 pixels, you would enter: "heading_font_size:100.\n')
#The user can select their own background image as well and the text box fill color on the cover page will
#be determined from the complementary color to one of the darkest pixels in the image. The text color on
#the cover page will be taken from the lightest pixel on the canvas. Moreover, the user can choose to add
#a design to one or both pages by adding the JPEG image to the working folder, of which the file name starts
#with "page", so that the code might distinguish it from the cover image (of which the file name begins with
#"cover").
path_jpeg = os.path.join(cwd, "*.jpg")
jpeg_files = glob.glob(path_jpeg)
if jpeg_files == []:
print('\nPlease include a JPEG file containing the image that you ' +
'wish to use as a background for the book cover in the working folder. Also, please ' +
'make sure that the provided background image is in JPEG format, ' +
"with a resolution of 300 ppi and a canvas size of US Legal dimensions in " +
'landscape mode (width of 4200 pixels and height of 2550 pixels) and that the ' +
'file name starts with "cover". Alternatively, for a perforated cover used in ' +
'binders, you would need to provide a background image with a resolution of 300 ppi and ' +
'a canvas size of US Letter dimensions in landscape mode (width of 3300 pixels and height ' +
'of 2550 pixels), with a file name starting with "perforated cover".\n\n' +
"Moreover, if you wish to add an image template to your notebook, the image " +
"needs to have a resolution of 300 ppi and a canvas size of US Letter dimensions in " +
"landscape mode (width of 3300 pixels and height of 2550 pixels), with margins according " +
"to the specifications (default left and margins 1/4 inch, top margin 1 inch and bottom " +
'margin 3/4 inch). Also, make sure to add the prefix "left page" or "right page" to the jpeg file name.')
problem = True
else:
cover_background_img = None
page_background_img = None
for i in range(len(jpeg_files)):
if os.path.split(jpeg_files[i])[-1][:16].lower() in ["perforated cover", "perforated_cover"]:
cover_background_img = jpeg_files[i]
perforated_cover = True
elif os.path.split(jpeg_files[i])[-1][:5].lower() == "cover":
cover_background_img = jpeg_files[i]
elif os.path.split(jpeg_files[i])[-1][:9].lower() in ["left page", "left_page"]:
left_page_background_img = jpeg_files[i]
elif os.path.split(jpeg_files[i])[-1][:10].lower() in ["right page", "right_page"]:
right_page_background_img = jpeg_files[i]
if (cover_background_img == None or (len(jpeg_files) > 1 and
left_page_background_img == None and right_page_background_img == None)):
print('\nPlease include a JPEG file containing the image that you ' +
'wish to use as a background for the book cover in the working folder. Also, please ' +
'make sure that the provided background image is in JPEG format, ' +
"with a resolution of 300 ppi and a canvas size of US Legal dimensions in " +
'landscape mode (width of 4200 pixels and height of 2550 pixels) and that the ' +
'file name starts with "cover". Alternatively, for a perforated cover used in ' +
'binders, you would need to provide a background image with a resolution of 300 ppi and ' +
'a canvas size of US Letter dimensions in landscape mode (width of 3300 pixels and height ' +
'of 2550 pixels), with a file name starting with "perforated cover".\n\n' +
"Moreover, if you wish to add an image template to your notebook, the image " +
"needs to have a resolution of 300 ppi and a canvas size of US Letter dimensions in " +
"landscape mode (width of 3300 pixels and height of 2550 pixels), with margins according " +
"to the specifications (default left and margins 1/4 inch, top margin 1 inch and bottom " +
'margin 3/4 inch). Also, make sure to add the prefix "left page" or "right page" to the jpeg file name.')
problem = True
#The cover True Type Font file (".ttf") is automatically retrieved
#within the "Cover font TTF file" folder.
path_ttf = os.path.join(cwd, "Cover font TTF file", "*.ttf")
ttf_files = glob.glob(path_ttf)
if ttf_files == []:
print("\nPlease include a True Type Font (.ttf) file containing " +
'the font you wish to use on the cover page in the folder "Cover font TTF file"')
problem = True
elif len(ttf_files) > 1:
print("\nPlease include only one True Type Font (.ttf) file containing " +
'the font you wish to use on the cover page in the folder "Cover font TTF file".')
problem = True
else:
cover_font = ttf_files[0]
#The header and footer True Type Font file (".ttf") is automatically retrieved
#within the "Header and footer font TTF file" folder.
path_ttf = os.path.join(cwd, "Header and footer font TTF file", "*.ttf")
ttf_files = glob.glob(path_ttf)
if ttf_files == []:
print("\nPlease include a True Type Font (.ttf) file containing " +
'the font you wish to use on the cover page in the folder "Header and footer font TTF file"')
problem = True
elif len(ttf_files) > 1:
print("\nPlease include only one True Type Font (.ttf) file containing " +
'the font you wish to use on the cover page in the folder "Header and footer font TTF file".')
problem = True
else:
heading_font = ImageFont.truetype(ttf_files[0], heading_font_size)
page_numbers_font = ImageFont.truetype(ttf_files[0], page_numbers_font_size)
TOC_heading_font = ImageFont.truetype(ttf_files[0], TOC_heading_font_size)
TOC_pages_font = ImageFont.truetype(ttf_files[0], TOC_pages_font_size)
TOC_subject_font = ImageFont.truetype(ttf_files[0], TOC_subject_font_size)
if (problem == False and title != None and number_of_pages
not in [0, None] and (inches_per_ream_500_pages not in [0, None]
or perforated_cover == True)):
#In order for the booklet numbering to allow for duplex printing,
#the sum of the TOC pages and the notebook pages needs to be
#dividable by four (as one sheet of paper contains two pages on
#each side). If this is not the case, the "number_of_pages" will
#be incremented by one page until this criterion is met.
#This is only done if "scriptreader_acetate == False", as the
#page numbering proceeds differently in this case.
if (number_of_pages + TOC_pages)%4 != 0 and scriptreader_acetate == False:
while (number_of_pages + TOC_pages)%4 != 0:
number_of_pages += 1
elif scriptreader_acetate == True:
if heading_text_right == None:
heading_text_right = "Write on this side"
if page_numbers_text_color == "LightSteelBlue":
page_numbers_text_color = "LightSlateGrey"
if dot_fill_color == "LightSlateGrey":
dot_fill_color = "DimGrey"
if dot_outline_color == "LightSlateGrey":
dot_outline_color = "DimGrey"
#As the pages will only be printed on the left-hand pages, the
#total number of TOC pages "TOC_pages" and notebook pages "number_of_pages"
#both need to be multiplied by two to reflect what the user actually wants.
#The code will actually generate the pages on the right-hand pages, but
#flip each image so that they are printed on the left-hand pages. This way,
#the user can write with erasable ink on the side that was not printed, with
#the text displayed in the right order.
TOC_pages = 2* TOC_pages
TOC_pages_list = list(range(1,TOC_pages+1))
number_of_pages = 2*number_of_pages
#The "total_number_of_pages" will be useful in determining
#the thickness of the spine later on in the code.
total_number_of_pages = number_of_pages + TOC_pages
#If only the "TOC_line_width" was specified by the user,
#the code will apply that value to "line_width" as well,
#so that any other ruled lines may line up with the TOC.
if TOC_line_width != 5 and line_width == 5:
line_width = TOC_line_width
#Similarly, if the user has only specified a "line_width"
#different than the default value of 5 pixels, then the
#"TOC_line_width" will be set to the same value for the
#same reason.
elif line_width != 5 and TOC_line_width == 5:
TOC_line_width = line_width
#The line spacing for the table of contents is harmonized with
#other line spacings, if ruled lines or dot grids were selected
#within the notebook, with preference being biven to ruled lines.
#This will enable users to use the "high five" notebook organizing
#system (see https://www.highfivehq.com/ for more information).
if (TOC_line_spacing == None and (college_ruled == True or
college_ruled_left == True or college_ruled_right == True)):
TOC_line_spacing = 9/32
elif (TOC_line_spacing == None and (wide_ruled == True or
wide_ruled_left == True or wide_ruled_right == True)):
TOC_line_spacing = 11/32
elif (TOC_line_spacing == None and (custom_ruled == True or
custom_ruled_left == True or custom_ruled_right == True)):
TOC_line_spacing = custom_line_distance_inches
elif TOC_line_spacing == None and (dot_grid == True or
dot_grid_left == True or dot_grid_right == True):
TOC_line_spacing = inches_between_dots
elif TOC_line_spacing == None:
TOC_line_spacing = 9/32
#The variable "page_numbers_bottom_margin_y_pixel" designates
#the "y" coordinate mapping to the vertical middle point of the
#page numbers. By default, this variable is set as "None", and the
#user can either set it manually, or the code will determine whether there
#is sufficient space to vertically center the page numbers in the space
#in-between the last horizontal line and the bottom of the page. Should
#there be less than 75 pixels below the page number for it to be
#vertically centered, the code will automatically bring the text up,
#such that the lowest "y" pixel of the page number is above 75 pixels
#from the bottom of the page, thereby respecting the default 0.25 inch
#non-printable area for most printers.
#If the user didn't provide manually the vertical distance from the
#bottom of the page at which the page numbers will be written, then
#"page_numbers_bottom_margin_y_pixel" will still be "None". This
#variable is then set such as the bottom "y" coordinate of the text
#will be 75 pixels (or 0.25 inches) from the bottom of the page.
#The variable "page_numbers_bottom_margin_y_pixel" will be reevaluated
#later on in the code to automatically center the page numbers vertically,
#if applicable.
#It was found that the page numbers written with the Baskerville font at 60 px of font size
#were in fact 40 px in height, most likely because the Baskerville typeface leaves 10 px above
#and below the characters for diacritics. Therefore, the correction factor of 2/3 is applied when
#determining the value of the "y" coordinate of the middle point of the page numbers
#("page_numbers_bottom_margin_y_pixel"). This correction factor may not be valid for other typefaces,
#but the the user always has the option of overriding this code by providing a value for
#"page_numbers_bottom_margin_y_pixel" when running the Python code.
if page_numbers_bottom_margin_y_pixel == None:
page_numbers_bottom_margin_y_pixel = 2550-(75 + (page_numbers_font_size/2)*2/3)
#The "ImageDraw" module will load the default background image (which
#the user can change by selecting another image in the working folder and
#passing in its name as an additional argument, when calling the Python code.
font_title = ImageFont.truetype(cover_font, cover_title_font_size)
image = Image.open(cover_background_img)
if perforated_cover == False:
#Some extra pixels are subtracted from "left_margin_cover_textbox",
#(35 pixels by default), as there seems to be 3 mm missing on both
#sides of the cover due to binding irregularities and the thickness of
#the glue: (3 mm * inch/25.4 mm * 4200 pixels/14 inch = 35 pixels).
#By subtracting some pixels, the cover title box is shifted towards
#the left.
left_margin_cover_textbox -= cover_extra_pixels
#The same applies to the "right_margin_cover_textbox"
right_margin_cover_textbox -= cover_extra_pixels
#The space between the left edge of the textbox
#and the start of the text on the x axis is set to 100 pixels,
#so the text will start drawing at "left_margin_cover_textbox + 100" pixels
left_margin_cover_text = left_margin_cover_textbox + 100
#The space between the right edge of the textbox
#and the end of the text on the x axis is set to 100 pixels,
#so the text will start drawing at "right_margin_cover_textbox - 100" pixels
right_margin_cover_text = right_margin_cover_textbox - 100
#The space between the top margin of the textbox
#and where the top edge of the text on the y axis
#is set to 50 pixels: "top_margin_cover_textbox+100"