multisplit
[iramuteq] / HTML.py
1 # -*- coding: utf-8 -*-
2 #modification pour python 3 : Laurent Mérat, 6x7 - mai 2020
3
4 """
5 HTML.py - v0.04 2009-07-28 Philippe Lagadec
6
7 This module provides a few classes to easily generate HTML code such as tables
8 and lists.
9
10 Project website: http://www.decalage.info/python/html
11
12 License: CeCILL (open-source GPL compatible), see source code for details.
13          http://www.cecill.info
14 """
15
16 __version__ = '0.04'
17 __date__    = '2009-07-28'
18 __author__  = 'Philippe Lagadec'
19
20 #--- LICENSE ------------------------------------------------------------------
21
22 # Copyright Philippe Lagadec - see http://www.decalage.info/contact for contact info
23 #
24 # This module provides a few classes to easily generate HTML tables and lists.
25 #
26 # This software is governed by the CeCILL license under French law and
27 # abiding by the rules of distribution of free software.  You can  use,
28 # modify and/or redistribute the software under the terms of the CeCILL
29 # license as circulated by CEA, CNRS and INRIA at the following URL
30 # "http://www.cecill.info".
31 #
32 # A copy of the CeCILL license is also provided in these attached files:
33 # Licence_CeCILL_V2-en.html and Licence_CeCILL_V2-fr.html
34 #
35 # As a counterpart to the access to the source code and  rights to copy,
36 # modify and redistribute granted by the license, users are provided only
37 # with a limited warranty  and the software's author, the holder of the
38 # economic rights, and the successive licensors have only limited
39 # liability.
40 #
41 # In this respect, the user's attention is drawn to the risks associated
42 # with loading,  using,  modifying and/or developing or reproducing the
43 # software by the user in light of its specific status of free software,
44 # that may mean  that it is complicated to manipulate,  and  that  also
45 # therefore means  that it is reserved for developers  and  experienced
46 # professionals having in-depth computer knowledge. Users are therefore
47 # encouraged to load and test the software's suitability as regards their
48 # requirements in conditions enabling the security of their systems and/or
49 # data to be ensured and,  more generally, to use and operate it in the
50 # same conditions as regards security.
51 #
52 # The fact that you are presently reading this means that you have had
53 # knowledge of the CeCILL license and that you accept its terms.
54
55
56 #--- CHANGES ------------------------------------------------------------------
57
58 # 2008-10-06 v0.01 PL: - First version
59 # 2008-10-13 v0.02 PL: - added cellspacing and cellpadding to table
60 #                      - added functions to ease one-step creation of tables
61 #                        and lists
62 # 2009-07-21 v0.03 PL: - added column attributes and styles (first attempt)
63 #                        (thanks to an idea submitted by Michal Cernoevic)
64 # 2009-07-28 v0.04 PL: - improved column styles, workaround for Mozilla
65
66
67 #-------------------------------------------------------------------------------
68 #TODO:
69 # - method to return a generator (yield each row) instead of a single string
70 # - unicode support (input and output)
71 # - escape text in cells (optional)
72 # - constants for standard colors
73 # - use lxml to generate well-formed HTML ?
74 # - add classes/functions to generate a HTML page, paragraphs, headings, etc...
75
76
77 #--- THANKS --------------------------------------------------------------------
78
79 # - Michal Cernoevic, for the idea of column styles.
80
81 #--- REFERENCES ----------------------------------------------------------------
82
83 # HTML 4.01 specs: http://www.w3.org/TR/html4/struct/tables.html
84
85 # Colors: http://www.w3.org/TR/html4/types.html#type-color
86
87 # Columns alignement and style, one of the oldest and trickiest bugs in Mozilla:
88 # https://bugzilla.mozilla.org/show_bug.cgi?id=915
89
90
91 #--- CONSTANTS -----------------------------------------------------------------
92
93 # Table style to get thin black lines in Mozilla/Firefox instead of 3D borders
94 TABLE_STYLE_THINBORDER = "border: 1px solid #000000; border-collapse: collapse;"
95 #TABLE_STYLE_THINBORDER = "border: 1px solid #000000;"
96
97
98 #=== CLASSES ===================================================================
99
100 class TableCell (object):
101     """
102     a TableCell object is used to create a cell in a HTML table. (TD or TH)
103
104     Attributes:
105     - text: text in the cell (may contain HTML tags). May be any object which
106             can be converted to a string using str().
107     - header: bool, false for a normal data cell (TD), true for a header cell (TH)
108     - bgcolor: str, background color
109     - width: str, width
110     - align: str, horizontal alignement (left, center, right, justify or char)
111     - char: str, alignment character, decimal point if not specified
112     - charoff: str, see HTML specs
113     - valign: str, vertical alignment (top|middle|bottom|baseline)
114     - style: str, CSS style
115     - attribs: dict, additional attributes for the TD/TH tag
116
117     Reference: http://www.w3.org/TR/html4/struct/tables.html#h-11.2.6
118     """
119
120     def __init__(self, text="", bgcolor=None, header=False, width=None,
121                 align=None, char=None, charoff=None, valign=None, style=None,
122                 attribs=None):
123         """TableCell constructor"""
124         self.text    = text
125         self.bgcolor = bgcolor
126         self.header  = header
127         self.width   = width
128         self.align   = align
129         self.char    = char
130         self.charoff = charoff
131         self.valign  = valign
132         self.style   = style
133         self.attribs = attribs
134         if attribs==None:
135             self.attribs = {}
136
137     def __str__(self):
138         """return the HTML code for the table cell as a string"""
139         attribs_str = ""
140         if self.bgcolor: self.attribs['bgcolor'] = self.bgcolor
141         if self.width:   self.attribs['width']   = self.width
142         if self.align:   self.attribs['align']   = self.align
143         if self.char:    self.attribs['char']    = self.char
144         if self.charoff: self.attribs['charoff'] = self.charoff
145         if self.valign:  self.attribs['valign']  = self.valign
146         if self.style:   self.attribs['style']   = self.style
147         for attr in self.attribs:
148             attribs_str += ' %s="%s"' % (attr, self.attribs[attr])
149         if self.text:
150             text = str(self.text)
151         else:
152             # An empty cell should at least contain a non-breaking space
153             text = ' '
154         if self.header:
155             return '  <TH%s>%s</TH>\n' % (attribs_str, text)
156         else:
157             return '  <TD%s>%s</TD>\n' % (attribs_str, text)
158
159 #-------------------------------------------------------------------------------
160
161 class TableRow (object):
162     """
163     a TableRow object is used to create a row in a HTML table. (TR tag)
164
165     Attributes:
166     - cells: list, tuple or any iterable, containing one string or TableCell
167              object for each cell
168     - header: bool, true for a header row (TH), false for a normal data row (TD)
169     - bgcolor: str, background color
170     - col_align, col_valign, col_char, col_charoff, col_styles: see Table class
171     - attribs: dict, additional attributes for the TR tag
172
173     Reference: http://www.w3.org/TR/html4/struct/tables.html#h-11.2.5
174     """
175
176     def __init__(self, cells=None, bgcolor=None, header=False, attribs=None,
177                 col_align=None, col_valign=None, col_char=None,
178                 col_charoff=None, col_styles=None):
179         """TableCell constructor"""
180         self.bgcolor     = bgcolor
181         self.cells       = cells
182         self.header      = header
183         self.col_align   = col_align
184         self.col_valign  = col_valign
185         self.col_char    = col_char
186         self.col_charoff = col_charoff
187         self.col_styles  = col_styles
188         self.attribs     = attribs
189         if attribs==None:
190             self.attribs = {}
191
192     def __str__(self):
193         """return the HTML code for the table row as a string"""
194         attribs_str = ""
195         if self.bgcolor: self.attribs['bgcolor'] = self.bgcolor
196         for attr in self.attribs:
197             attribs_str += ' %s="%s"' % (attr, self.attribs[attr])
198         result = ' <TR%s>\n' % attribs_str
199         for cell in self.cells:
200             col = self.cells.index(cell)    # cell column index
201             if not isinstance(cell, TableCell):
202                 cell = TableCell(cell, header=self.header)
203             # apply column alignment if specified:
204             if self.col_align and cell.align==None:
205                 cell.align = self.col_align[col]
206             if self.col_char and cell.char==None:
207                 cell.char = self.col_char[col]
208             if self.col_charoff and cell.charoff==None:
209                 cell.charoff = self.col_charoff[col]
210             if self.col_valign and cell.valign==None:
211                 cell.valign = self.col_valign[col]
212             # apply column style if specified:
213             if self.col_styles and cell.style==None:
214                 cell.style = self.col_styles[col]
215             result += str(cell)
216         result += ' </TR>\n'
217         return result
218
219 #-------------------------------------------------------------------------------
220
221 class Table (object):
222     """
223     a Table object is used to create a HTML table. (TABLE tag)
224
225     Attributes:
226     - rows: list, tuple or any iterable, containing one iterable or TableRow
227             object for each row
228     - header_row: list, tuple or any iterable, containing the header row (optional)
229     - border: str or int, border width
230     - style: str, table style in CSS syntax (thin black borders by default)
231     - width: str, width of the table on the page
232     - attribs: dict, additional attributes for the TABLE tag
233     - col_width: list or tuple defining width for each column
234     - col_align: list or tuple defining horizontal alignment for each column
235     - col_char: list or tuple defining alignment character for each column
236     - col_charoff: list or tuple defining charoff attribute for each column
237     - col_valign: list or tuple defining vertical alignment for each column
238     - col_styles: list or tuple of HTML styles for each column
239
240     Reference: http://www.w3.org/TR/html4/struct/tables.html#h-11.2.1
241     """
242
243     def __init__(self, rows=None, border='1', style=None, width=None,
244                 cellspacing=None, cellpadding=4, attribs=None, header_row=None,
245                 col_width=None, col_align=None, col_valign=None,
246                 col_char=None, col_charoff=None, col_styles=None):
247         """TableCell constructor"""
248         self.border = border
249         self.style = style
250         # style for thin borders by default
251         if style == None: self.style = TABLE_STYLE_THINBORDER
252         self.width       = width
253         self.cellspacing = cellspacing
254         self.cellpadding = cellpadding
255         self.header_row  = header_row
256         self.rows        = rows
257         if not rows: self.rows = []
258         self.attribs     = attribs
259         if not attribs: self.attribs = {}
260         self.col_width   = col_width
261         self.col_align   = col_align
262         self.col_char    = col_char
263         self.col_charoff = col_charoff
264         self.col_valign  = col_valign
265         self.col_styles  = col_styles
266
267     def __str__(self):
268         """return the HTML code for the table as a string"""
269         attribs_str = ""
270         if self.border: self.attribs['border'] = self.border
271         if self.style:  self.attribs['style'] = self.style
272         if self.width:  self.attribs['width'] = self.width
273         if self.cellspacing:  self.attribs['cellspacing'] = self.cellspacing
274         if self.cellpadding:  self.attribs['cellpadding'] = self.cellpadding
275         for attr in self.attribs:
276             attribs_str += ' %s="%s"' % (attr, self.attribs[attr])
277         result = '<TABLE%s>\n' % attribs_str
278         # insert column tags and attributes if specified:
279         if self.col_width:
280             for width in self.col_width:
281                 result += '  <COL width="%s">\n' % width
282         # The following code would also generate column attributes for style
283         # and alignement according to HTML4 specs,
284         # BUT it is not supported completely (only width) on Mozilla Firefox:
285         # see https://bugzilla.mozilla.org/show_bug.cgi?id=915
286 ##        n_cols = max(len(self.col_styles), len(self.col_width),
287 ##                     len(self.col_align), len(self.col_valign))
288 ##        for i in range(n_cols):
289 ##            col = ''
290 ##            try:
291 ##                if self.col_styles[i]:
292 ##                    col += ' style="%s"' % self.col_styles[i]
293 ##            except: pass
294 ##            try:
295 ##                if self.col_width[i]:
296 ##                    col += ' width="%s"' % self.col_width[i]
297 ##            except: pass
298 ##            try:
299 ##                if self.col_align[i]:
300 ##                    col += ' align="%s"' % self.col_align[i]
301 ##            except: pass
302 ##            try:
303 ##                if self.col_valign[i]:
304 ##                    col += ' valign="%s"' % self.col_valign[i]
305 ##            except: pass
306 ##            result += '<COL%s>\n' % col
307         # First insert a header row if specified:
308         if self.header_row:
309             if not isinstance(self.header_row, TableRow):
310                 result += str(TableRow(self.header_row, header=True))
311             else:
312                 result += str(self.header_row)
313         # Then all data rows:
314         for row in self.rows:
315             if not isinstance(row, TableRow):
316                 row = TableRow(row)
317             # apply column alignments  and styles to each row if specified:
318             # (Mozilla bug workaround)
319             if self.col_align and not row.col_align:
320                 row.col_align = self.col_align
321             if self.col_char and not row.col_char:
322                 row.col_char = self.col_char
323             if self.col_charoff and not row.col_charoff:
324                 row.col_charoff = self.col_charoff
325             if self.col_valign and not row.col_valign:
326                 row.col_valign = self.col_valign
327             if self.col_styles and not row.col_styles:
328                 row.col_styles = self.col_styles
329             result += str(row)
330         result += '</TABLE>'
331         return result
332
333
334 #-------------------------------------------------------------------------------
335
336 class List (object):
337     """
338     a List object is used to create an ordered or unordered list in HTML.
339     (UL/OL tag)
340
341     Attributes:
342     - lines: list, tuple or any iterable, containing one string for each line
343     - ordered: bool, choice between an ordered (OL) or unordered list (UL)
344     - attribs: dict, additional attributes for the OL/UL tag
345
346     Reference: http://www.w3.org/TR/html4/struct/lists.html
347     """
348
349     def __init__(self, lines=None, ordered=False, start=None, attribs=None):
350         """List constructor"""
351         if lines:
352             self.lines = lines
353         else:
354             self.lines = []
355         self.ordered = ordered
356         self.start = start
357         if attribs:
358             self.attribs = attribs
359         else:
360             self.attribs = {}
361
362     def __str__(self):
363         """return the HTML code for the list as a string"""
364         attribs_str = ""
365         if self.start:  self.attribs['start'] = self.start
366         for attr in self.attribs:
367             attribs_str += ' %s="%s"' % (attr, self.attribs[attr])
368         if self.ordered: tag = 'OL'
369         else:            tag = 'UL'
370         result = '<%s%s>\n' % (tag, attribs_str)
371         for line in self.lines:
372             result += ' <LI>%s\n' % str(line)
373         result += '</%s>\n' % tag
374         return result
375
376
377 ##class Link (object):
378 ##    """
379 ##    a Link object is used to create link in HTML. (<a> tag)
380 ##
381 ##    Attributes:
382 ##    - text: str, text of the link
383 ##    - url: str, URL of the link
384 ##    - attribs: dict, additional attributes for the A tag
385 ##
386 ##    Reference: http://www.w3.org/TR/html4
387 ##    """
388 ##
389 ##    def __init__(self, text, url=None, attribs=None):
390 ##        """Link constructor"""
391 ##        self.text = text
392 ##        self.url = url
393 ##        if attribs:
394 ##            self.attribs = attribs
395 ##        else:
396 ##            self.attribs = {}
397 ##
398 ##    def __str__(self):
399 ##        """return the HTML code for the link as a string"""
400 ##        attribs_str = ""
401 ##        if self.url:  self.attribs['href'] = self.url
402 ##        for attr in self.attribs:
403 ##            attribs_str += ' %s="%s"' % (attr, self.attribs[attr])
404 ##        return '<a%s>%s</a>' % (attribs_str, text)
405
406
407 #=== FUNCTIONS ================================================================
408
409 # much simpler definition of a link as a function:
410 def Link(text, url):
411     return '<a href="%s">%s</a>' % (url, text)
412
413 def link(text, url):
414     return '<a href="%s">%s</a>' % (url, text)
415
416 def table(*args, **kwargs):
417     'return HTML code for a table as a string. See Table class for parameters.'
418     return str(Table(*args, **kwargs))
419
420 def list(*args, **kwargs):
421     'return HTML code for a list as a string. See List class for parameters.'
422     return str(List(*args, **kwargs))
423
424
425 #=== MAIN =====================================================================
426
427 # Show sample usage when this file is launched as a script.
428
429 if __name__ == '__main__':
430
431     # open an HTML file to show output in a browser
432     f = open('test.html', 'w')
433
434     t = Table()
435     t.rows.append(TableRow(['A', 'B', 'C'], header=True))
436     t.rows.append(TableRow(['D', 'E', 'F']))
437     t.rows.append(('i', 'j', 'k'))
438     f.write(str(t) + '<p>\n')
439     print(str(t))
440     print('-'*79)
441
442     t2 = Table([
443             ('1', '2'),
444             ['3', '4']
445         ], width='100%', header_row=('col1', 'col2'),
446         col_width=('', '75%'))
447     f.write(str(t2) + '<p>\n')
448     print(t2)
449     print('-'*79)
450
451     t2.rows.append(['5', '6'])
452     t2.rows[1][1] = TableCell('new', bgcolor='red')
453     t2.rows.append(TableRow(['7', '8'], attribs={'align': 'center'}))
454     f.write(str(t2) + '<p>\n')
455     print(t2)
456     print('-'*79)
457
458     # sample table with column attributes and styles:
459     table_data = [
460             ['Smith',       'John',         30,    4.5],
461             ['Carpenter',   'Jack',         47,    7],
462             ['Johnson',     'Paul',         62,    10.55],
463         ]
464     htmlcode = HTML.table(table_data,
465         header_row = ['Last name',   'First name',   'Age', 'Score'],
466         col_width=['', '20%', '10%', '10%'],
467         col_align=['left', 'center', 'right', 'char'],
468         col_styles=['font-size: large', '', 'font-size: small', 'background-color:yellow'])
469     f.write(htmlcode + '<p>\n')
470     print(htmlcode)
471     print('-'*79)
472
473     def gen_table_squares(n):
474         """
475         Generator to create table rows for integers from 1 to n
476         """
477 ##        # First, header row:
478 ##        yield TableRow(('x', 'square(x)'), header=True, bgcolor='blue')
479 ##        # Then all rows:
480         for x in range(1, n+1):
481             yield (x, x*x)
482
483     t = Table(rows=gen_table_squares(10), header_row=('x', 'square(x)'))
484     f.write(str(t) + '<p>\n')
485
486     print('-'*79)
487     l = List(['aaa', 'bbb', 'ccc'])
488     f.write(str(l) + '<p>\n')
489     l.ordered = True
490     f.write(str(l) + '<p>\n')
491     l.start=10
492     f.write(str(l) + '<p>\n')
493
494     f.close()