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