comparison MoinMoin/formatter/text_docbook.py @ 3189:c13eca15e247

Completely reworked table support, including: - both html and wiki markup for attributes - horizontal alignment - vertical alignment - cell spanning multiple columns - cell spanning multiple rows - column widths in percent (not displayed by yelp)
author Mikko Virkkil? <mvirkkil@cc.hut.fi>
date Wed, 05 Mar 2008 01:39:04 +0100
parents fd905ed64dff
children a8d2fdac4662
comparison
equal deleted inserted replaced
3188:fd905ed64dff 3189:c13eca15e247
199 return "" 199 return ""
200 200
201 self.cur = self.cur.parentNode 201 self.cur = self.cur.parentNode
202 return "" 202 return ""
203 203
204 def _addEmptyNode(self, name, attributes=()):
205 node = self.doc.createElement(name)
206 self.cur.appendChild(node)
207 if len(attributes) > 0:
208 for name, value in attributes:
209 node.setAttribute(name, value)
210
211 def _getTableCellCount(self, attrs=()):
212 cols = 1
213 if attrs and attrs.has_key('colspan'):
214 s1 = attrs['colspan']
215 s1 = str(s1).replace('"', '')
216 cols = int(s1)
217 return cols
218
219 def _addTableCellDefinition(self, attrs=()):
220 # Check number of columns
221 cols = self._getTableCellCount(attrs)
222 # Find node tgroup
223 actNode = self.cur
224 numberExistingColumns = 0
225 while actNode and actNode.nodeName != 'tgroup':
226 actNode = actNode.parentNode
227 # Number of existing columns
228 nodeBefore = self.cur
229 if actNode:
230 nodeBefore = actNode.firstChild
231 while nodeBefore and nodeBefore.nodeName != 'tbody':
232 nodeBefore = nodeBefore.nextSibling
233 numberExistingColumns += 1
234
235 while cols >= 1:
236 # Create new node
237 numberExistingColumns += 1
238 nnode = self.doc.createElement("colspec")
239 nnode.setAttribute('colname', 'xxx' + str(numberExistingColumns))
240 # Add node
241 if actNode:
242 actNode.insertBefore(nnode, nodeBefore)
243 else:
244 self.cur.insertBefore(nnode, nodeBefore)
245 cols -= 1
246 # Set new number of columns for tgroup
247 self.cur.parentNode.parentNode.parentNode.setAttribute('cols', str(numberExistingColumns))
248 return ""
249
250
251 ### Inline ########################################################## 204 ### Inline ##########################################################
252 205
253 def _handleFormatting(self, name, on, attributes=()): 206 def _handleFormatting(self, name, on, attributes=()):
254 # We add all the elements we create to the list of elements that should not contain a section 207 # We add all the elements we create to the list of elements that should not contain a section
255 if name not in self.section_should_break: 208 if name not in self.section_should_break:
465 return self.request.theme.make_icon(text) 418 return self.request.theme.make_icon(text)
466 419
467 def icon(self, type): 420 def icon(self, type):
468 return '' # self.request.theme.make_icon(type) 421 return '' # self.request.theme.make_icon(type)
469 422
470 ### Tables ##########################################################
471
472 #FIXME: We should copy code from text_html.py for attr handling
473
474 def table(self, on, attrs=None, **kw):
475 sanitized_attrs = []
476 if attrs and attrs.has_key('id'):
477 sanitized_attrs[id] = attrs['id']
478
479 self._handleNode("table", on, sanitized_attrs)
480 if on:
481 self._addEmptyNode("caption") #dtd for table requires caption
482 self._handleNode("tgroup", on)
483 self._handleNode("tbody", on)
484 return ""
485
486 def table_row(self, on, attrs=None, **kw):
487 self.table_current_row_cells = 0
488 sanitized_attrs = []
489 if attrs and attrs.has_key('id'):
490 sanitized_attrs[id] = attrs['id']
491 return self._handleNode("row", on, sanitized_attrs)
492
493 def table_cell(self, on, attrs=None, **kw):
494 # Finish row definition
495 sanitized_attrs = []
496 if attrs and attrs.has_key('id'):
497 sanitized_attrs[id] = attrs['id']
498 # Get number of newly added columns
499 startCount = self.table_current_row_cells
500 addedCellsCount = self._getTableCellCount(attrs)
501 self.table_current_row_cells += addedCellsCount
502 ret = self._handleNode("entry", on, sanitized_attrs)
503 if self.cur.parentNode == self.cur.parentNode.parentNode.firstChild:
504 self._addTableCellDefinition(attrs)
505 # Set cell join if any
506 if addedCellsCount > 1:
507 startString = "xxx" + str(startCount)
508 stopString = "xxx" + str(startCount + addedCellsCount - 1)
509 self.cur.setAttribute("namest", startString)
510 self.cur.setAttribute("nameend", stopString)
511 return ret
512 423
513 ### Code ############################################################ 424 ### Code ############################################################
514 425
515 def code_area(self, on, code_id, code_type='code', show=0, start=-1, step=-1): 426 def code_area(self, on, code_id, code_type='code', show=0, start=-1, step=-1):
516 show = show and 'numbered' or 'unnumbered' 427 show = show and 'numbered' or 'unnumbered'
596 targetNode = self.cur 507 targetNode = self.cur
597 title = self.doc.createElement("title") 508 title = self.doc.createElement("title")
598 title.appendChild(self.doc.createTextNode(titleTxt)) 509 title.appendChild(self.doc.createTextNode(titleTxt))
599 targetNode.appendChild(title) 510 targetNode.appendChild(title)
600 511
512 def _convertStylesToDict(self, styles):
513 '''Takes the CSS styling information and converts it to a dict'''
514 attrs = {}
515 for s in styles.split(";"):
516 if s.strip(' "') == "":
517 continue
518 (key, value) = s.split(":", 1)
519 key = key.strip(' "')
520 value = value.strip(' "')
521
522 if key == 'vertical-align':
523 key = 'valign'
524 elif key == 'text-align':
525 key = 'align'
526 elif key == 'background-color':
527 key = 'bgcolor'
528
529 attrs[key] = value
530 return attrs
531
601 532
602 ### Not supported ################################################### 533 ### Not supported ###################################################
603 534
604 def rule(self, size=0, **kw): 535 def rule(self, size=0, **kw):
605 return "" 536 return ""
608 return "" 539 return ""
609 540
610 def big(self, on, **kw): 541 def big(self, on, **kw):
611 return "" 542 return ""
612 543
544 ### Tables ##########################################################
545
546 def table(self, on, attrs=(), **kw):
547 if(on):
548 self.curtable = Table(self, self.doc, self.cur, attrs)
549 self.cur = self.curtable.tableNode
550 else:
551 self.cur = self.curtable.finalizeTable()
552 self.curtable = None
553 return ""
554
555 def table_row(self, on, attrs=(), **kw):
556 if(on):
557 self.cur = self.curtable.addRow(attrs)
558 return ""
559
560 def table_cell(self, on, attrs=(), **kw):
561 if(on):
562 self.cur = self.curtable.addCell(attrs)
563 return ""
564
565 class Table:
566 '''The Table class is used as a helper for collecting information about
567 what kind of table we are building. When all relelvant data is gathered
568 it calculates the different spans of the cells and columns.
569 '''
570
571 def __init__(self, formatter, doc, parent, args):
572 self.formatter = formatter
573 self.doc = doc
574
575 self.tableNode = self.doc.createElement('informaltable')
576 parent.appendChild(self.tableNode)
577 self.colWidths = {}
578 self.tgroup = self.doc.createElement('tgroup')
579 # Bug in yelp, the two lines below don't affect rendering
580 #self.tgroup.setAttribute('rowsep', '1')
581 #self.tgroup.setAttribute('colsep', '1')
582 self.curColumn = 0
583 self.maxColumn = 0
584 self.row = None
585 self.tableNode.appendChild(self.tgroup)
586
587 self.tbody = self.doc.createElement('tbody') # Note: This gets appended in finalizeTable
588
589 def finalizeTable(self):
590 """Calculates the final width of the whole table and the width of each
591 column. Adds the colspec-elements and applies the colwidth attributes.
592 Inserts the tbody element to the tgroup and returns the tables container
593 element.
594
595 A lot of the information is gathered from the style attributes passed
596 to the functions
597 """
598 self.tgroup.setAttribute('cols', str(self.maxColumn))
599 for colnr in range(0, self.maxColumn):
600 colspecElem = self.doc.createElement('colspec')
601 colspecElem.setAttribute('colname', 'col_%s' % str(colnr))
602 if self.colWidths.has_key(str(colnr)) and self.colWidths[str(colnr)] != "1*":
603 colspecElem.setAttribute('colwidth', self.colWidths[str(colnr)])
604 self.tgroup.appendChild(colspecElem)
605 self.tgroup.appendChild(self.tbody)
606 return self.tableNode.parentNode
607
608 def addRow(self, args):
609 self.curColumn = 0
610 self.row = self.doc.createElement('row')
611 # Bug in yelp, doesn't affect the outcome.
612 #self.row.setAttribute("rowsep", "1") #Rows should have lines between them
613 self.tbody.appendChild(self.row)
614 return self.row
615
616 def addCell(self, args):
617 cell = self.doc.createElement('entry')
618 cell.setAttribute('rowsep', '1')
619 cell.setAttribute('colsep', '1')
620
621 self.row.appendChild(cell)
622
623 args = self._convertStyleAttributes(args)
624 self._handleSimpleCellAttributes(cell, args)
625 self._handleColWidth(args)
626 self.curColumn += self._handleColSpan(cell, args)
627
628 self.maxColumn = max(self.curColumn, self.maxColumn)
629
630 return cell
631
632 def _handleColWidth(self, args):
633 if not args.has_key("width"):
634 return
635 args["width"] = args["width"].strip('"')
636 if not args["width"].endswith("%"):
637 self.formatter._emitComment("Width %s not supported" % args["width"])
638 return
639
640 self.colWidths[str(self.curColumn)] = args["width"][:-1] + "*"
641
642 def _handleColSpan(self, element, args):
643 """Returns the number of colums this entry spans"""
644 if not args or not args.has_key('colspan'):
645 return 1
646 assert(element.nodeName == "entry")
647 extracols = int(args['colspan'].strip('"')) - 1
648 element.setAttribute('namest', "col_" + str(self.curColumn))
649 element.setAttribute('nameend', "col_" + str(self.curColumn + extracols))
650 return 1 + extracols
651
652 def _handleSimpleCellAttributes(self, element, args):
653 safe_values_for = {'valign': ('top', 'middle', 'bottom'),
654 'align': ('left', 'center', 'right'),
655 }
656 if not args:
657 return
658 assert(element.nodeName == "entry")
659
660 if args.has_key('rowspan'):
661 extrarows = int(args['rowspan'].strip('"')) - 1
662 element.setAttribute('morerows', str(extrarows))
663
664 if args.has_key('align'):
665 value = args['align'].strip('"')
666 if value in safe_values_for['align']:
667 element.setAttribute('align', value)
668 else:
669 self.formatter._emitComment("Alignment %s not supported" % value)
670 pass
671
672 if args.has_key('valign'):
673 value = args['valign'].strip('"')
674 if value in safe_values_for['valign']:
675 element.setAttribute('valign', value)
676 else:
677 self.formatter._emitComment("Vertical alignment %s not supported" % value)
678 pass
679
680 def _convertStyleAttributes(self, argslist):
681 if not argslist.has_key('style'):
682 return argslist
683 styles = self.formatter._convertStylesToDict(argslist['style'].strip('"'))
684 argslist.update(styles)
685
686 return argslist