├── fox_face.png
├── background_image.png
├── table_with_class.pdf
├── table_with_function.pdf
├── table.py
├── part_1.py
├── part_2.py
├── table_class.py
├── part_3.py
├── part_4.py
├── create_table_fpdf2.py
├── table_function.py
├── chp2.txt
└── chp1.txt
/fox_face.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/bvalgard/create-pdf-with-python-fpdf2/HEAD/fox_face.png
--------------------------------------------------------------------------------
/background_image.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/bvalgard/create-pdf-with-python-fpdf2/HEAD/background_image.png
--------------------------------------------------------------------------------
/table_with_class.pdf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/bvalgard/create-pdf-with-python-fpdf2/HEAD/table_with_class.pdf
--------------------------------------------------------------------------------
/table_with_function.pdf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/bvalgard/create-pdf-with-python-fpdf2/HEAD/table_with_function.pdf
--------------------------------------------------------------------------------
/table.py:
--------------------------------------------------------------------------------
1 | from fpdf import FPDF, HTMLMixin
2 |
3 | class PDF(FPDF, HTMLMixin):
4 | pass
5 |
6 | pdf = PDF()
7 | pdf.add_page()
8 |
9 |
10 | # specify font
11 | pdf.set_font('helvetica', '', 16)
12 |
13 | pdf.cell(0,0, 'A cell with some words')
14 |
15 | pdf.write_html("""
16 |
17 |
18 |
19 | | ID |
20 | Name |
21 |
22 |
23 |
24 |
25 | | 1 |
26 | Alice |
27 |
28 |
29 | | 2 |
30 | Bob |
31 |
32 |
33 |
34 |
35 | """)
36 |
37 | pdf.output("pdf_table.pdf")
38 |
--------------------------------------------------------------------------------
/part_1.py:
--------------------------------------------------------------------------------
1 | from fpdf import FPDF
2 |
3 | # create FPDF object
4 | # Layout ('P','L')
5 | # Unit ('mm', 'cm', 'in')
6 | # format ('A3', 'A4' (default), 'A5', 'Letter', 'Legal', (100,150))
7 | pdf = FPDF('P', 'mm', 'Letter')
8 |
9 | # Add a page
10 | pdf.add_page()
11 |
12 | # specify font
13 | # fonts ('times', 'courier', 'helvetica', 'symbol', 'zpfdingbats')
14 | # 'B' (bold), 'U' (underline), 'I' (italics), '' (regular), combination (i.e., ('BU'))
15 | pdf.set_font('helvetica', 'BIU', 16)
16 | pdf.set_text_color(220,50,50)
17 | # Add text
18 | # w = width
19 | # h = height
20 | # txt = your text
21 | # ln (0 False; 1 True - move cursor down to next line)
22 | # border (0 False; 1 True - add border around cell)
23 | pdf.cell(120, 100, 'Hello World!', ln=True, border=True)
24 |
25 | pdf.set_font('times', '', 12)
26 | pdf.cell(80, 10, 'Good Bye World!')
27 |
28 | pdf.output('pdf_1.pdf')
29 |
--------------------------------------------------------------------------------
/part_2.py:
--------------------------------------------------------------------------------
1 | from fpdf import FPDF
2 |
3 | class PDF(FPDF):
4 | def header(self):
5 | # Logo
6 | self.image('fox_face.png', 10, 8, 25)
7 | # font
8 | self.set_font('helvetica', 'B', 20)
9 | # Padding
10 | self.cell(80)
11 | # Title
12 | self.cell(30, 10, 'Title', border=True, ln=1, align='C')
13 | # Line break
14 | self.ln(20)
15 |
16 | # Page footer
17 | def footer(self):
18 | # Set position of the footer
19 | self.set_y(-15)
20 | # set font
21 | self.set_font('helvetica', 'I', 8)
22 | # Page number
23 | self.cell(0, 10, f'Page {self.page_no()}/{{nb}}', align='C')
24 |
25 | # Create a PDF object
26 | pdf = PDF('P', 'mm', 'Letter')
27 |
28 | # get total page numbers
29 | pdf.alias_nb_pages()
30 |
31 | # Set auto page break
32 | pdf.set_auto_page_break(auto = True, margin = 15)
33 |
34 | #Add Page
35 | pdf.add_page()
36 |
37 | # specify font
38 | pdf.set_font('helvetica', 'BIU', 16)
39 |
40 | pdf.set_font('times', '', 12)
41 |
42 | for i in range(1, 41):
43 | pdf.cell(0, 10, f'This is line {i} :D', ln=1)
44 |
45 | pdf.output('pdf_2.pdf')
46 |
--------------------------------------------------------------------------------
/table_class.py:
--------------------------------------------------------------------------------
1 | from create_table_fpdf2 import PDF
2 |
3 |
4 | data = [
5 | ["First name", "Last name", "Age", "City",], # 'testing','size'],
6 | ["Jules", "Smith", "34", "San Juan",], # 'testing','size'],
7 | ["Mary", "Ramos", "45", "Orlando",], # 'testing','size'],
8 | ["Carlson", "Banks", "19", "Los Angeles",], # 'testing','size'],
9 | ["Lucas", "Cimon", "31", "Saint-Mahturin-sur-Loire",], # 'testing','size'],
10 | ]
11 |
12 | data_as_dict = {"First name": ["Jules","Mary","Carlson","Lucas"],
13 | "Last name": ["Smith","Ramos","Banks","Cimon"],
14 | "Age": [34,'45','19','31']
15 | }
16 |
17 |
18 | pdf = PDF()
19 | pdf.add_page()
20 | pdf.set_font("Times", size=10)
21 |
22 | pdf.create_table(table_data = data,title='I\'m the first title', cell_width='even')
23 | pdf.ln()
24 |
25 | pdf.create_table(table_data = data,title='I start at 25',cell_width='uneven',x_start=25)
26 | pdf.ln()
27 |
28 | pdf.create_table(table_data = data,title="I'm in the middle",cell_width=22,x_start='C')
29 | pdf.ln()
30 |
31 | pdf.create_table(table_data = data_as_dict,title='Is my text red',align_header='R', align_data='R', cell_width=[15,15,10,45,], x_start='C', emphasize_data=['45','Jules'], emphasize_style='BIU',emphasize_color=(255,0,0))
32 | pdf.ln()
33 |
34 |
35 | pdf.output('table_class.pdf')
--------------------------------------------------------------------------------
/part_3.py:
--------------------------------------------------------------------------------
1 | from fpdf import FPDF
2 |
3 | title = '20,000 Leagues Under the Sea'
4 |
5 | class PDF(FPDF):
6 | def header(self):
7 | # font
8 | self.set_font('helvetica', 'B', 15)
9 | # Calculate width of title and position
10 | title_w = self.get_string_width(title) + 6
11 | doc_w = self.w
12 | self.set_x((doc_w - title_w) / 2)
13 | # colors of frame, background, and text
14 | self.set_draw_color(0, 80, 180) # border = blue
15 | self.set_fill_color(230, 230, 0) # background = yellow
16 | self.set_text_color(220, 50, 50) # text = red
17 | # Thickness of frame (border)
18 | self.set_line_width(1)
19 | # Title
20 | self.cell(title_w, 10, title, border=1, ln=1, align='C', fill=1)
21 | # Line break
22 | self.ln(10)
23 |
24 | # Page footer
25 | def footer(self):
26 | # Set position of the footer
27 | self.set_y(-15)
28 | # set font
29 | self.set_font('helvetica', 'I', 8)
30 | # Set font color grey
31 | self.set_text_color(169,169,169)
32 | # Page number
33 | self.cell(0, 10, f'Page {self.page_no()}', align='C')
34 |
35 | # Adding chapter title to start of each chapter
36 | def chapter_title(self, ch_num, ch_title):
37 | # set font
38 | self.set_font('helvetica', '', 12)
39 | # background color
40 | self.set_fill_color(200, 220, 255)
41 | # Chapter title
42 | chapter_title = f'Chapter {ch_num} : {ch_title}'
43 | self.cell(0, 5, chapter_title, ln=1, fill=1)
44 | # line break
45 | self.ln()
46 |
47 | # Chapter content
48 | def chapter_body(self, name):
49 | # read text file
50 | with open(name, 'rb') as fh:
51 | txt = fh.read().decode('latin-1')
52 | # set font
53 | self.set_font('times', '', 12)
54 | # insert text
55 | self.multi_cell(0, 5, txt)
56 | # line break
57 | self.ln()
58 | # end each chapter
59 | self.set_font('times', 'I', 12)
60 | self.cell(0, 5, 'END OF CHAPTER')
61 |
62 | def print_chapter(self, ch_num, ch_title, name):
63 | self.add_page()
64 | self.chapter_title(ch_num, ch_title)
65 | self.chapter_body(name)
66 |
67 | # Create a PDF object
68 | pdf = PDF('P', 'mm', 'Letter')
69 |
70 | # get total page numbers
71 | pdf.alias_nb_pages()
72 |
73 | # Set auto page break
74 | pdf.set_auto_page_break(auto = True, margin = 15)
75 |
76 | # Add Page
77 | pdf.add_page()
78 |
79 |
80 | pdf.print_chapter(1, 'A RUNAWAY REEF', 'chp1.txt')
81 | pdf.print_chapter(2, 'THE PROS AND CONS', 'chp2.txt')
82 |
83 | pdf.output('pdf_3.pdf')
84 |
--------------------------------------------------------------------------------
/part_4.py:
--------------------------------------------------------------------------------
1 | from fpdf import FPDF
2 |
3 | title = '20,000 Leagues Under the Sea'
4 |
5 |
6 | class PDF(FPDF):
7 | def header(self):
8 | # font
9 | self.set_font('helvetica', 'B', 15)
10 | # Calculate width of title and position
11 | title_w = self.get_string_width(title) + 6
12 | doc_w = self.w
13 | self.set_x((doc_w - title_w) / 2)
14 | # colors of frame, background, and text
15 | self.set_draw_color(0, 80, 180) # border = blue
16 | self.set_fill_color(230, 230, 0) # background = yellow
17 | self.set_text_color(220, 50, 50) # text = red
18 | # Thickness of frame (border)
19 | self.set_line_width(1)
20 | # Title
21 | self.cell(title_w, 10, title, border=1, ln=1, align='C', fill=1)
22 | # Line break
23 | self.ln(10)
24 |
25 | # Page footer
26 | def footer(self):
27 | # Set position of the footer
28 | self.set_y(-15)
29 | # set font
30 | self.set_font('helvetica', 'I', 8)
31 | # Set font color grey
32 | self.set_text_color(169,169,169)
33 | # Page number
34 | self.cell(0, 10, f'Page {self.page_no()}', align='C')
35 |
36 | # Adding chapter title to start of each chapter
37 | def chapter_title(self, ch_num, ch_title, link):
38 | # Set link location
39 | self.set_link(link)
40 | # set font
41 | self.set_font('helvetica', '', 12)
42 | # background color
43 | self.set_fill_color(200, 220, 255)
44 | # Chapter title
45 | chapter_title = f'Chapter {ch_num} : {ch_title}'
46 | self.cell(0, 5, chapter_title, ln=1, fill=1)
47 | # line break
48 | self.ln()
49 |
50 | # Chapter content
51 | def chapter_body(self, name):
52 | # read text file
53 | with open(name, 'rb') as fh:
54 | txt = fh.read().decode('latin-1')
55 | # set font
56 | self.set_font('times', '', 12)
57 | # insert text
58 | self.multi_cell(0, 5, txt)
59 | # line break
60 | self.ln()
61 | # end each chapter
62 | self.set_font('times', 'I', 12)
63 | self.cell(0, 5, 'END OF CHAPTER')
64 |
65 | def print_chapter(self, ch_num, ch_title, name, link):
66 | self.add_page()
67 | self.chapter_title(ch_num, ch_title, link)
68 | self.chapter_body(name)
69 |
70 | # Create a PDF object
71 | pdf = PDF('P', 'mm', 'Letter')
72 |
73 | # metadata
74 | pdf.set_title(title)
75 | pdf.set_author('Jules Verne')
76 |
77 | # Create Links
78 | website = 'http://www.gutenberg.org/cache/epub/164/pg164.txt'
79 | ch1_link = pdf.add_link()
80 | ch2_link = pdf.add_link()
81 |
82 |
83 | # Set auto page break
84 | pdf.set_auto_page_break(auto = True, margin = 15)
85 |
86 | # Add Page
87 | pdf.add_page()
88 | pdf.image('background_image.png', x = -0.5, w = pdf.w + 1)
89 |
90 | # Attach Links
91 | pdf.cell(0, 10, 'Text Source', ln = 1, link = website)
92 | pdf.cell(0, 10, 'Chapter 1', ln = 1, link = ch1_link)
93 | pdf.cell(0, 10, 'Chapter 2', ln = 1, link = ch2_link)
94 |
95 |
96 |
97 | pdf.print_chapter(1, 'A RUNAWAY REEF', 'chp1.txt', ch1_link)
98 | pdf.print_chapter(2, 'THE PROS AND CONS', 'chp2.txt', ch2_link)
99 |
100 | pdf.output('pdf_4.pdf')
101 |
--------------------------------------------------------------------------------
/create_table_fpdf2.py:
--------------------------------------------------------------------------------
1 | from fpdf import FPDF
2 |
3 | class PDF(FPDF):
4 | def create_table(self, table_data, title='', data_size = 10, title_size=12, align_data='L', align_header='L', cell_width='even', x_start='x_default',emphasize_data=[], emphasize_style=None,emphasize_color=(0,0,0)):
5 | """
6 | table_data:
7 | list of lists with first element being list of headers
8 | title:
9 | (Optional) title of table (optional)
10 | data_size:
11 | the font size of table data
12 | title_size:
13 | the font size fo the title of the table
14 | align_data:
15 | align table data
16 | L = left align
17 | C = center align
18 | R = right align
19 | align_header:
20 | align table data
21 | L = left align
22 | C = center align
23 | R = right align
24 | cell_width:
25 | even: evenly distribute cell/column width
26 | uneven: base cell size on lenght of cell/column items
27 | int: int value for width of each cell/column
28 | list of ints: list equal to number of columns with the widht of each cell / column
29 | x_start:
30 | where the left edge of table should start
31 | emphasize_data:
32 | which data elements are to be emphasized - pass as list
33 | emphasize_style: the font style you want emphaized data to take
34 | emphasize_color: emphasize color (if other than black)
35 |
36 | """
37 | default_style = self.font_style
38 | if emphasize_style == None:
39 | emphasize_style = default_style
40 | # default_font = self.font_family
41 | # default_size = self.font_size_pt
42 | # default_style = self.font_style
43 | # default_color = self.color # This does not work
44 |
45 | # Get Width of Columns
46 | def get_col_widths():
47 | col_width = cell_width
48 | if col_width == 'even':
49 | col_width = self.epw / len(data[0]) - 1 # distribute content evenly # epw = effective page width (width of page not including margins)
50 | elif col_width == 'uneven':
51 | col_widths = []
52 |
53 | # searching through columns for largest sized cell (not rows but cols)
54 | for col in range(len(table_data[0])): # for every row
55 | longest = 0
56 | for row in range(len(table_data)):
57 | cell_value = str(table_data[row][col])
58 | value_length = self.get_string_width(cell_value)
59 | if value_length > longest:
60 | longest = value_length
61 | col_widths.append(longest + 4) # add 4 for padding
62 | col_width = col_widths
63 |
64 |
65 |
66 | ### compare columns
67 |
68 | elif isinstance(cell_width, list):
69 | col_width = cell_width # TODO: convert all items in list to int
70 | else:
71 | # TODO: Add try catch
72 | col_width = int(col_width)
73 | return col_width
74 |
75 | # Convert dict to lol
76 | # Why? because i built it with lol first and added dict func after
77 | # Is there performance differences?
78 | if isinstance(table_data, dict):
79 | header = [key for key in table_data]
80 | data = []
81 | for key in table_data:
82 | value = table_data[key]
83 | data.append(value)
84 | # need to zip so data is in correct format (first, second, third --> not first, first, first)
85 | data = [list(a) for a in zip(*data)]
86 |
87 | else:
88 | header = table_data[0]
89 | data = table_data[1:]
90 |
91 | line_height = self.font_size * 2.5
92 |
93 | col_width = get_col_widths()
94 | self.set_font(size=title_size)
95 |
96 | # Get starting position of x
97 | # Determin width of table to get x starting point for centred table
98 | if x_start == 'C':
99 | table_width = 0
100 | if isinstance(col_width, list):
101 | for width in col_width:
102 | table_width += width
103 | else: # need to multiply cell width by number of cells to get table width
104 | table_width = col_width * len(table_data[0])
105 | # Get x start by subtracting table width from pdf width and divide by 2 (margins)
106 | margin_width = self.w - table_width
107 | # TODO: Check if table_width is larger than pdf width
108 |
109 | center_table = margin_width / 2 # only want width of left margin not both
110 | x_start = center_table
111 | self.set_x(x_start)
112 | elif isinstance(x_start, int):
113 | self.set_x(x_start)
114 | elif x_start == 'x_default':
115 | x_start = self.set_x(self.l_margin)
116 |
117 |
118 | # TABLE CREATION #
119 |
120 | # add title
121 | if title != '':
122 | self.multi_cell(0, line_height, title, border=0, align='j', ln=3, max_line_height=self.font_size)
123 | self.ln(line_height) # move cursor back to the left margin
124 |
125 | self.set_font(size=data_size)
126 | # add header
127 | y1 = self.get_y()
128 | if x_start:
129 | x_left = x_start
130 | else:
131 | x_left = self.get_x()
132 | x_right = self.epw + x_left
133 | if not isinstance(col_width, list):
134 | if x_start:
135 | self.set_x(x_start)
136 | for datum in header:
137 | self.multi_cell(col_width, line_height, datum, border=0, align=align_header, ln=3, max_line_height=self.font_size)
138 | x_right = self.get_x()
139 | self.ln(line_height) # move cursor back to the left margin
140 | y2 = self.get_y()
141 | self.line(x_left,y1,x_right,y1)
142 | self.line(x_left,y2,x_right,y2)
143 |
144 | for row in data:
145 | if x_start: # not sure if I need this
146 | self.set_x(x_start)
147 | for datum in row:
148 | if datum in emphasize_data:
149 | self.set_text_color(*emphasize_color)
150 | self.set_font(style=emphasize_style)
151 | self.multi_cell(col_width, line_height, datum, border=0, align=align_data, ln=3, max_line_height=self.font_size)
152 | self.set_text_color(0,0,0)
153 | self.set_font(style=default_style)
154 | else:
155 | self.multi_cell(col_width, line_height, datum, border=0, align=align_data, ln=3, max_line_height=self.font_size) # ln = 3 - move cursor to right with same vertical offset # this uses an object named self
156 | self.ln(line_height) # move cursor back to the left margin
157 |
158 | else:
159 | if x_start:
160 | self.set_x(x_start)
161 | for i in range(len(header)):
162 | datum = header[i]
163 | self.multi_cell(col_width[i], line_height, datum, border=0, align=align_header, ln=3, max_line_height=self.font_size)
164 | x_right = self.get_x()
165 | self.ln(line_height) # move cursor back to the left margin
166 | y2 = self.get_y()
167 | self.line(x_left,y1,x_right,y1)
168 | self.line(x_left,y2,x_right,y2)
169 |
170 |
171 | for i in range(len(data)):
172 | if x_start:
173 | self.set_x(x_start)
174 | row = data[i]
175 | for i in range(len(row)):
176 | datum = row[i]
177 | if not isinstance(datum, str):
178 | datum = str(datum)
179 | adjusted_col_width = col_width[i]
180 | if datum in emphasize_data:
181 | self.set_text_color(*emphasize_color)
182 | self.set_font(style=emphasize_style)
183 | self.multi_cell(adjusted_col_width, line_height, datum, border=0, align=align_data, ln=3, max_line_height=self.font_size)
184 | self.set_text_color(0,0,0)
185 | self.set_font(style=default_style)
186 | else:
187 | self.multi_cell(adjusted_col_width, line_height, datum, border=0, align=align_data, ln=3, max_line_height=self.font_size) # ln = 3 - move cursor to right with same vertical offset # this uses an object named self
188 | self.ln(line_height) # move cursor back to the left margin
189 | y3 = self.get_y()
190 | self.line(x_left,y3,x_right,y3)
--------------------------------------------------------------------------------
/table_function.py:
--------------------------------------------------------------------------------
1 | from fpdf import FPDF
2 |
3 | def create_table(table_data, title='', data_size = 10, title_size=12, align_data='L', align_header='L', cell_width='even', x_start='x_default',emphasize_data=[], emphasize_style=None, emphasize_color=(0,0,0)):
4 | """
5 | table_data:
6 | list of lists with first element being list of headers
7 | title:
8 | (Optional) title of table (optional)
9 | data_size:
10 | the font size of table data
11 | title_size:
12 | the font size fo the title of the table
13 | align_data:
14 | align table data
15 | L = left align
16 | C = center align
17 | R = right align
18 | align_header:
19 | align table data
20 | L = left align
21 | C = center align
22 | R = right align
23 | cell_width:
24 | even: evenly distribute cell/column width
25 | uneven: base cell size on lenght of cell/column items
26 | int: int value for width of each cell/column
27 | list of ints: list equal to number of columns with the widht of each cell / column
28 | x_start:
29 | where the left edge of table should start
30 | emphasize_data:
31 | which data elements are to be emphasized - pass as list
32 | emphasize_style: the font style you want emphaized data to take
33 | emphasize_color: emphasize color (if other than black)
34 |
35 | """
36 | default_style = pdf.font_style
37 | if emphasize_style == None:
38 | emphasize_style = default_style
39 | # default_font = pdf.font_family
40 | # default_size = pdf.font_size_pt
41 | # default_style = pdf.font_style
42 | # default_color = pdf.color # This does not work
43 |
44 | # Get Width of Columns
45 | def get_col_widths():
46 | col_width = cell_width
47 | if col_width == 'even':
48 | col_width = pdf.epw / len(data[0]) - 1 # distribute content evenly # epw = effective page width (width of page not including margins)
49 | elif col_width == 'uneven':
50 | col_widths = []
51 |
52 | # searching through columns for largest sized cell (not rows but cols)
53 | for col in range(len(table_data[0])): # for every row
54 | longest = 0
55 | for row in range(len(table_data)):
56 | cell_value = str(table_data[row][col])
57 | value_length = pdf.get_string_width(cell_value)
58 | if value_length > longest:
59 | longest = value_length
60 | col_widths.append(longest + 4) # add 4 for padding
61 | col_width = col_widths
62 |
63 |
64 |
65 | ### compare columns
66 |
67 | elif isinstance(cell_width, list):
68 | col_width = cell_width # TODO: convert all items in list to int
69 | else:
70 | # TODO: Add try catch
71 | col_width = int(col_width)
72 | return col_width
73 |
74 | # Convert dict to lol
75 | # Why? because i built it with lol first and added dict func after
76 | # Is there performance differences?
77 | if isinstance(table_data, dict):
78 | header = [key for key in table_data]
79 | data = []
80 | for key in table_data:
81 | value = table_data[key]
82 | data.append(value)
83 | # need to zip so data is in correct format (first, second, third --> not first, first, first)
84 | data = [list(a) for a in zip(*data)]
85 |
86 | else:
87 | header = table_data[0]
88 | data = table_data[1:]
89 |
90 | line_height = pdf.font_size * 2.5
91 |
92 | col_width = get_col_widths()
93 | pdf.set_font(size=title_size)
94 |
95 | # Get starting position of x
96 | # Determin width of table to get x starting point for centred table
97 | if x_start == 'C':
98 | table_width = 0
99 | if isinstance(col_width, list):
100 | for width in col_width:
101 | table_width += width
102 | else: # need to multiply cell width by number of cells to get table width
103 | table_width = col_width * len(table_data[0])
104 | # Get x start by subtracting table width from pdf width and divide by 2 (margins)
105 | margin_width = pdf.w - table_width
106 | # TODO: Check if table_width is larger than pdf width
107 |
108 | center_table = margin_width / 2 # only want width of left margin not both
109 | x_start = center_table
110 | pdf.set_x(x_start)
111 | elif isinstance(x_start, int):
112 | pdf.set_x(x_start)
113 | elif x_start == 'x_default':
114 | x_start = pdf.set_x(pdf.l_margin)
115 |
116 |
117 | # TABLE CREATION #
118 |
119 | # add title
120 | if title != '':
121 | pdf.multi_cell(0, line_height, title, border=0, align='j', ln=3, max_line_height=pdf.font_size)
122 | pdf.ln(line_height) # move cursor back to the left margin
123 |
124 | pdf.set_font(size=data_size)
125 | # add header
126 | y1 = pdf.get_y()
127 | if x_start:
128 | x_left = x_start
129 | else:
130 | x_left = pdf.get_x()
131 | x_right = pdf.epw + x_left
132 | if not isinstance(col_width, list):
133 | if x_start:
134 | pdf.set_x(x_start)
135 | for datum in header:
136 | pdf.multi_cell(col_width, line_height, datum, border=0, align=align_header, ln=3, max_line_height=pdf.font_size)
137 | x_right = pdf.get_x()
138 | pdf.ln(line_height) # move cursor back to the left margin
139 | y2 = pdf.get_y()
140 | pdf.line(x_left,y1,x_right,y1)
141 | pdf.line(x_left,y2,x_right,y2)
142 |
143 | for row in data:
144 | if x_start: # not sure if I need this
145 | pdf.set_x(x_start)
146 | for datum in row:
147 | if datum in emphasize_data:
148 | pdf.set_text_color(*emphasize_color)
149 | pdf.set_font(style=emphasize_style)
150 | pdf.multi_cell(col_width, line_height, datum, border=0, align=align_data, ln=3, max_line_height=pdf.font_size)
151 | pdf.set_text_color(0,0,0)
152 | pdf.set_font(style=default_style)
153 | else:
154 | pdf.multi_cell(col_width, line_height, datum, border=0, align=align_data, ln=3, max_line_height=pdf.font_size) # ln = 3 - move cursor to right with same vertical offset # this uses an object named pdf
155 | pdf.ln(line_height) # move cursor back to the left margin
156 |
157 | else:
158 | if x_start:
159 | pdf.set_x(x_start)
160 | for i in range(len(header)):
161 | datum = header[i]
162 | pdf.multi_cell(col_width[i], line_height, datum, border=0, align=align_header, ln=3, max_line_height=pdf.font_size)
163 | x_right = pdf.get_x()
164 | pdf.ln(line_height) # move cursor back to the left margin
165 | y2 = pdf.get_y()
166 | pdf.line(x_left,y1,x_right,y1)
167 | pdf.line(x_left,y2,x_right,y2)
168 |
169 |
170 | for i in range(len(data)):
171 | if x_start:
172 | pdf.set_x(x_start)
173 | row = data[i]
174 | for i in range(len(row)):
175 | datum = row[i]
176 | if not isinstance(datum, str):
177 | datum = str(datum)
178 | adjusted_col_width = col_width[i]
179 | if datum in emphasize_data:
180 | pdf.set_text_color(*emphasize_color)
181 | pdf.set_font(style=emphasize_style)
182 | pdf.multi_cell(adjusted_col_width, line_height, datum, border=0, align=align_data, ln=3, max_line_height=pdf.font_size)
183 | pdf.set_text_color(0,0,0)
184 | pdf.set_font(style=default_style)
185 | else:
186 | pdf.multi_cell(adjusted_col_width, line_height, datum, border=0, align=align_data, ln=3, max_line_height=pdf.font_size) # ln = 3 - move cursor to right with same vertical offset # this uses an object named pdf
187 | pdf.ln(line_height) # move cursor back to the left margin
188 | y3 = pdf.get_y()
189 | pdf.line(x_left,y3,x_right,y3)
190 |
191 |
192 |
193 | data = [
194 | ["First name", "Last name", "Age", "City",], # 'testing','size'],
195 | ["Jules", "Smith", "34", "San Juan",], # 'testing','size'],
196 | ["Mary", "Ramos", "45", "Orlando",], # 'testing','size'],
197 | ["Carlson", "Banks", "19", "Los Angeles",], # 'testing','size'],
198 | ["Lucas", "Cimon", "31", "Saint-Mahturin-sur-Loire",], # 'testing','size'],
199 | ]
200 |
201 | data_as_dict = {"First name": ["Jules","Mary","Carlson","Lucas"],
202 | "Last name": ["Smith","Ramos","Banks","Cimon"],
203 | "Age": [34,'45','19','31']
204 | }
205 |
206 |
207 | pdf = FPDF()
208 | pdf.add_page()
209 | pdf.set_font("Times", size=10)
210 |
211 | create_table(table_data = data,title='I\'m the first title', cell_width='even')
212 | pdf.ln()
213 | create_table(table_data = data,title='my title is the best title',cell_width='uneven',x_start=25)
214 | pdf.ln()
215 | create_table(table_data = data,title='my title is the best title',cell_width=22,x_start=50)
216 | pdf.ln()
217 |
218 | create_table(table_data = data_as_dict,title='Is my text red',align_header='R', align_data='R', cell_width=[15,15,10,45,], x_start='C', emphasize_data=['45','Jules'], emphasize_style='BIU',emphasize_color=(255,0,0))
219 | pdf.ln()
220 |
221 |
222 | # create_table(table_data = data_as_dict,align_header='R', align_data='R', cell_width=[15,15,10,45,], x_start='C')
223 |
224 |
225 | pdf.output('table_function.pdf')
226 |
227 | # Need to create obejct as pdf
--------------------------------------------------------------------------------
/chp2.txt:
--------------------------------------------------------------------------------
1 | At the period when these events took place, I had just returned from a
2 | scientific research in the disagreeable territory of Nebraska, in the
3 | United States. In virtue of my office as Assistant Professor in the
4 | Museum of Natural History in Paris, the French Government had attached
5 | me to that expedition. After six months in Nebraska, I arrived in New
6 | York towards the end of March, laden with a precious collection. My
7 | departure for France was fixed for the first days in May. Meanwhile I
8 | was occupying myself in classifying my mineralogical, botanical, and
9 | zoological riches, when the accident happened to the Scotia.
10 |
11 | I was perfectly up in the subject which was the question of the day.
12 | How could I be otherwise? I had read and reread all the American and
13 | European papers without being any nearer a conclusion. This mystery
14 | puzzled me. Under the impossibility of forming an opinion, I jumped
15 | from one extreme to the other. That there really was something could
16 | not be doubted, and the incredulous were invited to put their finger on
17 | the wound of the Scotia.
18 |
19 | On my arrival at New York the question was at its height. The theory
20 | of the floating island, and the unapproachable sandbank, supported by
21 | minds little competent to form a judgment, was abandoned. And, indeed,
22 | unless this shoal had a machine in its stomach, how could it change its
23 | position with such astonishing rapidity?
24 |
25 | From the same cause, the idea of a floating hull of an enormous wreck
26 | was given up.
27 |
28 | There remained, then, only two possible solutions of the question,
29 | which created two distinct parties: on one side, those who were for a
30 | monster of colossal strength; on the other, those who were for a
31 | submarine vessel of enormous motive power.
32 |
33 | But this last theory, plausible as it was, could not stand against
34 | inquiries made in both worlds. That a private gentleman should have
35 | such a machine at his command was not likely. Where, when, and how was
36 | it built? and how could its construction have been kept secret?
37 | Certainly a Government might possess such a destructive machine. And
38 | in these disastrous times, when the ingenuity of man has multiplied the
39 | power of weapons of war, it was possible that, without the knowledge of
40 | others, a State might try to work such a formidable engine.
41 |
42 | But the idea of a war machine fell before the declaration of
43 | Governments. As public interest was in question, and transatlantic
44 | communications suffered, their veracity could not be doubted. But how
45 | admit that the construction of this submarine boat had escaped the
46 | public eye? For a private gentleman to keep the secret under such
47 | circumstances would be very difficult, and for a State whose every act
48 | is persistently watched by powerful rivals, certainly impossible.
49 |
50 | Upon my arrival in New York several persons did me the honour of
51 | consulting me on the phenomenon in question. I had published in France
52 | a work in quarto, in two volumes, entitled Mysteries of the Great
53 | Submarine Grounds. This book, highly approved of in the learned world,
54 | gained for me a special reputation in this rather obscure branch of
55 | Natural History. My advice was asked. As long as I could deny the
56 | reality of the fact, I confined myself to a decided negative. But
57 | soon, finding myself driven into a corner, I was obliged to explain
58 | myself point by point. I discussed the question in all its forms,
59 | politically and scientifically; and I give here an extract from a
60 | carefully-studied article which I published in the number of the 30th
61 | of April. It ran as follows:
62 |
63 | "After examining one by one the different theories, rejecting all other
64 | suggestions, it becomes necessary to admit the existence of a marine
65 | animal of enormous power.
66 |
67 | "The great depths of the ocean are entirely unknown to us. Soundings
68 | cannot reach them. What passes in those remote depths--what beings
69 | live, or can live, twelve or fifteen miles beneath the surface of the
70 | waters--what is the organisation of these animals, we can scarcely
71 | conjecture. However, the solution of the problem submitted to me may
72 | modify the form of the dilemma. Either we do know all the varieties of
73 | beings which people our planet, or we do not. If we do NOT know them
74 | all--if Nature has still secrets in the deeps for us, nothing is more
75 | conformable to reason than to admit the existence of fishes, or
76 | cetaceans of other kinds, or even of new species, of an organisation
77 | formed to inhabit the strata inaccessible to soundings, and which an
78 | accident of some sort has brought at long intervals to the upper level
79 | of the ocean.
80 |
81 | "If, on the contrary, we DO know all living kinds, we must necessarily
82 | seek for the animal in question amongst those marine beings already
83 | classed; and, in that case, I should be disposed to admit the existence
84 | of a gigantic narwhal.
85 |
86 | "The common narwhal, or unicorn of the sea, often attains a length of
87 | sixty feet. Increase its size fivefold or tenfold, give it strength
88 | proportionate to its size, lengthen its destructive weapons, and you
89 | obtain the animal required. It will have the proportions determined by
90 | the officers of the Shannon, the instrument required by the perforation
91 | of the Scotia, and the power necessary to pierce the hull of the
92 | steamer.
93 |
94 | "Indeed, the narwhal is armed with a sort of ivory sword, a halberd,
95 | according to the expression of certain naturalists. The principal tusk
96 | has the hardness of steel. Some of these tusks have been found buried
97 | in the bodies of whales, which the unicorn always attacks with success.
98 | Others have been drawn out, not without trouble, from the bottoms of
99 | ships, which they had pierced through and through, as a gimlet pierces
100 | a barrel. The Museum of the Faculty of Medicine of Paris possesses one
101 | of these defensive weapons, two yards and a quarter in length, and
102 | fifteen inches in diameter at the base.
103 |
104 | "Very well! suppose this weapon to be six times stronger and the animal
105 | ten times more powerful; launch it at the rate of twenty miles an hour,
106 | and you obtain a shock capable of producing the catastrophe required.
107 | Until further information, therefore, I shall maintain it to be a
108 | sea-unicorn of colossal dimensions, armed not with a halberd, but with
109 | a real spur, as the armoured frigates, or the `rams' of war, whose
110 | massiveness and motive power it would possess at the same time. Thus
111 | may this puzzling phenomenon be explained, unless there be something
112 | over and above all that one has ever conjectured, seen, perceived, or
113 | experienced; which is just within the bounds of possibility."
114 |
115 | These last words were cowardly on my part; but, up to a certain point,
116 | I wished to shelter my dignity as professor, and not give too much
117 | cause for laughter to the Americans, who laugh well when they do laugh.
118 | I reserved for myself a way of escape. In effect, however, I admitted
119 | the existence of the "monster." My article was warmly discussed, which
120 | procured it a high reputation. It rallied round it a certain number of
121 | partisans. The solution it proposed gave, at least, full liberty to
122 | the imagination. The human mind delights in grand conceptions of
123 | supernatural beings. And the sea is precisely their best vehicle, the
124 | only medium through which these giants (against which terrestrial
125 | animals, such as elephants or rhinoceroses, are as nothing) can be
126 | produced or developed.
127 |
128 | The industrial and commercial papers treated the question chiefly from
129 | this point of view. The Shipping and Mercantile Gazette, the Lloyd's
130 | List, the Packet-Boat, and the Maritime and Colonial Review, all papers
131 | devoted to insurance companies which threatened to raise their rates of
132 | premium, were unanimous on this point. Public opinion had been
133 | pronounced. The United States were the first in the field; and in New
134 | York they made preparations for an expedition destined to pursue this
135 | narwhal. A frigate of great speed, the Abraham Lincoln, was put in
136 | commission as soon as possible. The arsenals were opened to Commander
137 | Farragut, who hastened the arming of his frigate; but, as it always
138 | happens, the moment it was decided to pursue the monster, the monster
139 | did not appear. For two months no one heard it spoken of. No ship met
140 | with it. It seemed as if this unicorn knew of the plots weaving around
141 | it. It had been so much talked of, even through the Atlantic cable,
142 | that jesters pretended that this slender fly had stopped a telegram on
143 | its passage and was making the most of it.
144 |
145 | So when the frigate had been armed for a long campaign, and provided
146 | with formidable fishing apparatus, no one could tell what course to
147 | pursue. Impatience grew apace, when, on the 2nd of July, they learned
148 | that a steamer of the line of San Francisco, from California to
149 | Shanghai, had seen the animal three weeks before in the North Pacific
150 | Ocean. The excitement caused by this news was extreme. The ship was
151 | revictualled and well stocked with coal.
152 |
153 | Three hours before the Abraham Lincoln left Brooklyn pier, I received a
154 | letter worded as follows:
155 |
156 | To M. ARONNAX, Professor in the Museum of Paris, Fifth Avenue Hotel,
157 | New York.
158 |
159 | SIR,--If you will consent to join the Abraham Lincoln in this
160 | expedition, the Government of the United States will with pleasure see
161 | France represented in the enterprise. Commander Farragut has a cabin
162 | at your disposal.
163 |
164 | Very cordially yours, J.B. HOBSON, Secretary of Marine.
--------------------------------------------------------------------------------
/chp1.txt:
--------------------------------------------------------------------------------
1 | The year 1866 was signalised by a remarkable incident, a mysterious and
2 | puzzling phenomenon, which doubtless no one has yet forgotten. Not to
3 | mention rumours which agitated the maritime population and excited the
4 | public mind, even in the interior of continents, seafaring men were
5 | particularly excited. Merchants, common sailors, captains of vessels,
6 | skippers, both of Europe and America, naval officers of all countries,
7 | and the Governments of several States on the two continents, were
8 | deeply interested in the matter.
9 |
10 | For some time past vessels had been met by "an enormous thing," a long
11 | object, spindle-shaped, occasionally phosphorescent, and infinitely
12 | larger and more rapid in its movements than a whale.
13 |
14 | The facts relating to this apparition (entered in various log-books)
15 | agreed in most respects as to the shape of the object or creature in
16 | question, the untiring rapidity of its movements, its surprising power
17 | of locomotion, and the peculiar life with which it seemed endowed. If
18 | it was a whale, it surpassed in size all those hitherto classified in
19 | science. Taking into consideration the mean of observations made at
20 | divers times--rejecting the timid estimate of those who assigned to
21 | this object a length of two hundred feet, equally with the exaggerated
22 | opinions which set it down as a mile in width and three in length--we
23 | might fairly conclude that this mysterious being surpassed greatly all
24 | dimensions admitted by the learned ones of the day, if it existed at
25 | all. And that it DID exist was an undeniable fact; and, with that
26 | tendency which disposes the human mind in favour of the marvellous, we
27 | can understand the excitement produced in the entire world by this
28 | supernatural apparition. As to classing it in the list of fables, the
29 | idea was out of the question.
30 |
31 | On the 20th of July, 1866, the steamer Governor Higginson, of the
32 | Calcutta and Burnach Steam Navigation Company, had met this moving mass
33 | five miles off the east coast of Australia. Captain Baker thought at
34 | first that he was in the presence of an unknown sandbank; he even
35 | prepared to determine its exact position when two columns of water,
36 | projected by the mysterious object, shot with a hissing noise a hundred
37 | and fifty feet up into the air. Now, unless the sandbank had been
38 | submitted to the intermittent eruption of a geyser, the Governor
39 | Higginson had to do neither more nor less than with an aquatic mammal,
40 | unknown till then, which threw up from its blow-holes columns of water
41 | mixed with air and vapour.
42 |
43 | Similar facts were observed on the 23rd of July in the same year, in
44 | the Pacific Ocean, by the Columbus, of the West India and Pacific Steam
45 | Navigation Company. But this extraordinary creature could transport
46 | itself from one place to another with surprising velocity; as, in an
47 | interval of three days, the Governor Higginson and the Columbus had
48 | observed it at two different points of the chart, separated by a
49 | distance of more than seven hundred nautical leagues.
50 |
51 | Fifteen days later, two thousand miles farther off, the Helvetia, of
52 | the Compagnie-Nationale, and the Shannon, of the Royal Mail Steamship
53 | Company, sailing to windward in that portion of the Atlantic lying
54 | between the United States and Europe, respectively signalled the
55 | monster to each other in 42° 15' N. lat. and 60° 35' W. long.
56 | In these simultaneous observations they thought themselves justified in
57 | estimating the minimum length of the mammal at more than three hundred
58 | and fifty feet, as the Shannon and Helvetia were of smaller dimensions
59 | than it, though they measured three hundred feet over all.
60 |
61 | Now the largest whales, those which frequent those parts of the sea
62 | round the Aleutian, Kulammak, and Umgullich islands, have never
63 | exceeded the length of sixty yards, if they attain that.
64 |
65 | In every place of great resort the monster was the fashion. They sang
66 | of it in the cafes, ridiculed it in the papers, and represented it on
67 | the stage. All kinds of stories were circulated regarding it. There
68 | appeared in the papers caricatures of every gigantic and imaginary
69 | creature, from the white whale, the terrible "Moby Dick" of sub-arctic
70 | regions, to the immense kraken, whose tentacles could entangle a ship
71 | of five hundred tons and hurry it into the abyss of the ocean. The
72 | legends of ancient times were even revived.
73 |
74 | Then burst forth the unending argument between the believers and the
75 | unbelievers in the societies of the wise and the scientific journals.
76 | "The question of the monster" inflamed all minds. Editors of
77 | scientific journals, quarrelling with believers in the supernatural,
78 | spilled seas of ink during this memorable campaign, some even drawing
79 | blood; for from the sea-serpent they came to direct personalities.
80 |
81 | During the first months of the year 1867 the question seemed buried,
82 | never to revive, when new facts were brought before the public. It was
83 | then no longer a scientific problem to be solved, but a real danger
84 | seriously to be avoided. The question took quite another shape. The
85 | monster became a small island, a rock, a reef, but a reef of indefinite
86 | and shifting proportions.
87 |
88 | On the 5th of March, 1867, the Moravian, of the Montreal Ocean Company,
89 | finding herself during the night in 27° 30' lat. and 72° 15'
90 | long., struck on her starboard quarter a rock, marked in no chart for
91 | that part of the sea. Under the combined efforts of the wind and its
92 | four hundred horse power, it was going at the rate of thirteen knots.
93 | Had it not been for the superior strength of the hull of the Moravian,
94 | she would have been broken by the shock and gone down with the 237
95 | passengers she was bringing home from Canada.
96 |
97 | The accident happened about five o'clock in the morning, as the day was
98 | breaking. The officers of the quarter-deck hurried to the after-part
99 | of the vessel. They examined the sea with the most careful attention.
100 | They saw nothing but a strong eddy about three cables' length distant,
101 | as if the surface had been violently agitated. The bearings of the
102 | place were taken exactly, and the Moravian continued its route without
103 | apparent damage. Had it struck on a submerged rock, or on an enormous
104 | wreck? They could not tell; but, on examination of the ship's bottom
105 | when undergoing repairs, it was found that part of her keel was broken.
106 |
107 | This fact, so grave in itself, might perhaps have been forgotten like
108 | many others if, three weeks after, it had not been re-enacted under
109 | similar circumstances. But, thanks to the nationality of the victim of
110 | the shock, thanks to the reputation of the company to which the vessel
111 | belonged, the circumstance became extensively circulated.
112 |
113 | The 13th of April, 1867, the sea being beautiful, the breeze
114 | favourable, the Scotia, of the Cunard Company's line, found herself in
115 | 15° 12' long. and 45° 37' lat. She was going at the speed of
116 | thirteen knots and a half.
117 |
118 | At seventeen minutes past four in the afternoon, whilst the passengers
119 | were assembled at lunch in the great saloon, a slight shock was felt on
120 | the hull of the Scotia, on her quarter, a little aft of the port-paddle.
121 |
122 | The Scotia had not struck, but she had been struck, and seemingly by
123 | something rather sharp and penetrating than blunt. The shock had been
124 | so slight that no one had been alarmed, had it not been for the shouts
125 | of the carpenter's watch, who rushed on to the bridge, exclaiming, "We
126 | are sinking! we are sinking!" At first the passengers were much
127 | frightened, but Captain Anderson hastened to reassure them. The danger
128 | could not be imminent. The Scotia, divided into seven compartments by
129 | strong partitions, could brave with impunity any leak. Captain
130 | Anderson went down immediately into the hold. He found that the sea
131 | was pouring into the fifth compartment; and the rapidity of the influx
132 | proved that the force of the water was considerable. Fortunately this
133 | compartment did not hold the boilers, or the fires would have been
134 | immediately extinguished. Captain Anderson ordered the engines to be
135 | stopped at once, and one of the men went down to ascertain the extent
136 | of the injury. Some minutes afterwards they discovered the existence
137 | of a large hole, two yards in diameter, in the ship's bottom. Such a
138 | leak could not be stopped; and the Scotia, her paddles half submerged,
139 | was obliged to continue her course. She was then three hundred miles
140 | from Cape Clear, and, after three days' delay, which caused great
141 | uneasiness in Liverpool, she entered the basin of the company.
142 |
143 | The engineers visited the Scotia, which was put in dry dock. They
144 | could scarcely believe it possible; at two yards and a half below
145 | water-mark was a regular rent, in the form of an isosceles triangle.
146 | The broken place in the iron plates was so perfectly defined that it
147 | could not have been more neatly done by a punch. It was clear, then,
148 | that the instrument producing the perforation was not of a common stamp
149 | and, after having been driven with prodigious strength, and piercing an
150 | iron plate 1 3/8 inches thick, had withdrawn itself by a backward
151 | motion.
152 |
153 | Such was the last fact, which resulted in exciting once more the
154 | torrent of public opinion. From this moment all unlucky casualties
155 | which could not be otherwise accounted for were put down to the monster.
156 |
157 | Upon this imaginary creature rested the responsibility of all these
158 | shipwrecks, which unfortunately were considerable; for of three
159 | thousand ships whose loss was annually recorded at Lloyd's, the number
160 | of sailing and steam-ships supposed to be totally lost, from the
161 | absence of all news, amounted to not less than two hundred!
162 |
163 | Now, it was the "monster" who, justly or unjustly, was accused of their
164 | disappearance, and, thanks to it, communication between the different
165 | continents became more and more dangerous. The public demanded sharply
166 | that the seas should at any price be relieved from this formidable
167 | cetacean.[1]
168 |
169 |
170 | [1] Member of the whale family.
--------------------------------------------------------------------------------