18c2ecf20Sopenharmony_ci#!/usr/bin/env python3 28c2ecf20Sopenharmony_ci# -*- coding: utf-8; mode: python -*- 38c2ecf20Sopenharmony_ci# pylint: disable=C0330, R0903, R0912 48c2ecf20Sopenharmony_ci 58c2ecf20Sopenharmony_ciu""" 68c2ecf20Sopenharmony_ci flat-table 78c2ecf20Sopenharmony_ci ~~~~~~~~~~ 88c2ecf20Sopenharmony_ci 98c2ecf20Sopenharmony_ci Implementation of the ``flat-table`` reST-directive. 108c2ecf20Sopenharmony_ci 118c2ecf20Sopenharmony_ci :copyright: Copyright (C) 2016 Markus Heiser 128c2ecf20Sopenharmony_ci :license: GPL Version 2, June 1991 see linux/COPYING for details. 138c2ecf20Sopenharmony_ci 148c2ecf20Sopenharmony_ci The ``flat-table`` (:py:class:`FlatTable`) is a double-stage list similar to 158c2ecf20Sopenharmony_ci the ``list-table`` with some additional features: 168c2ecf20Sopenharmony_ci 178c2ecf20Sopenharmony_ci * *column-span*: with the role ``cspan`` a cell can be extended through 188c2ecf20Sopenharmony_ci additional columns 198c2ecf20Sopenharmony_ci 208c2ecf20Sopenharmony_ci * *row-span*: with the role ``rspan`` a cell can be extended through 218c2ecf20Sopenharmony_ci additional rows 228c2ecf20Sopenharmony_ci 238c2ecf20Sopenharmony_ci * *auto span* rightmost cell of a table row over the missing cells on the 248c2ecf20Sopenharmony_ci right side of that table-row. With Option ``:fill-cells:`` this behavior 258c2ecf20Sopenharmony_ci can changed from *auto span* to *auto fill*, which automaticly inserts 268c2ecf20Sopenharmony_ci (empty) cells instead of spanning the last cell. 278c2ecf20Sopenharmony_ci 288c2ecf20Sopenharmony_ci Options: 298c2ecf20Sopenharmony_ci 308c2ecf20Sopenharmony_ci * header-rows: [int] count of header rows 318c2ecf20Sopenharmony_ci * stub-columns: [int] count of stub columns 328c2ecf20Sopenharmony_ci * widths: [[int] [int] ... ] widths of columns 338c2ecf20Sopenharmony_ci * fill-cells: instead of autospann missing cells, insert missing cells 348c2ecf20Sopenharmony_ci 358c2ecf20Sopenharmony_ci roles: 368c2ecf20Sopenharmony_ci 378c2ecf20Sopenharmony_ci * cspan: [int] additionale columns (*morecols*) 388c2ecf20Sopenharmony_ci * rspan: [int] additionale rows (*morerows*) 398c2ecf20Sopenharmony_ci""" 408c2ecf20Sopenharmony_ci 418c2ecf20Sopenharmony_ci# ============================================================================== 428c2ecf20Sopenharmony_ci# imports 438c2ecf20Sopenharmony_ci# ============================================================================== 448c2ecf20Sopenharmony_ci 458c2ecf20Sopenharmony_ciimport sys 468c2ecf20Sopenharmony_ci 478c2ecf20Sopenharmony_cifrom docutils import nodes 488c2ecf20Sopenharmony_cifrom docutils.parsers.rst import directives, roles 498c2ecf20Sopenharmony_cifrom docutils.parsers.rst.directives.tables import Table 508c2ecf20Sopenharmony_cifrom docutils.utils import SystemMessagePropagation 518c2ecf20Sopenharmony_ci 528c2ecf20Sopenharmony_ci# ============================================================================== 538c2ecf20Sopenharmony_ci# common globals 548c2ecf20Sopenharmony_ci# ============================================================================== 558c2ecf20Sopenharmony_ci 568c2ecf20Sopenharmony_ci__version__ = '1.0' 578c2ecf20Sopenharmony_ci 588c2ecf20Sopenharmony_ciPY3 = sys.version_info[0] == 3 598c2ecf20Sopenharmony_ciPY2 = sys.version_info[0] == 2 608c2ecf20Sopenharmony_ci 618c2ecf20Sopenharmony_ciif PY3: 628c2ecf20Sopenharmony_ci # pylint: disable=C0103, W0622 638c2ecf20Sopenharmony_ci unicode = str 648c2ecf20Sopenharmony_ci basestring = str 658c2ecf20Sopenharmony_ci 668c2ecf20Sopenharmony_ci# ============================================================================== 678c2ecf20Sopenharmony_cidef setup(app): 688c2ecf20Sopenharmony_ci# ============================================================================== 698c2ecf20Sopenharmony_ci 708c2ecf20Sopenharmony_ci app.add_directive("flat-table", FlatTable) 718c2ecf20Sopenharmony_ci roles.register_local_role('cspan', c_span) 728c2ecf20Sopenharmony_ci roles.register_local_role('rspan', r_span) 738c2ecf20Sopenharmony_ci 748c2ecf20Sopenharmony_ci return dict( 758c2ecf20Sopenharmony_ci version = __version__, 768c2ecf20Sopenharmony_ci parallel_read_safe = True, 778c2ecf20Sopenharmony_ci parallel_write_safe = True 788c2ecf20Sopenharmony_ci ) 798c2ecf20Sopenharmony_ci 808c2ecf20Sopenharmony_ci# ============================================================================== 818c2ecf20Sopenharmony_cidef c_span(name, rawtext, text, lineno, inliner, options=None, content=None): 828c2ecf20Sopenharmony_ci# ============================================================================== 838c2ecf20Sopenharmony_ci # pylint: disable=W0613 848c2ecf20Sopenharmony_ci 858c2ecf20Sopenharmony_ci options = options if options is not None else {} 868c2ecf20Sopenharmony_ci content = content if content is not None else [] 878c2ecf20Sopenharmony_ci nodelist = [colSpan(span=int(text))] 888c2ecf20Sopenharmony_ci msglist = [] 898c2ecf20Sopenharmony_ci return nodelist, msglist 908c2ecf20Sopenharmony_ci 918c2ecf20Sopenharmony_ci# ============================================================================== 928c2ecf20Sopenharmony_cidef r_span(name, rawtext, text, lineno, inliner, options=None, content=None): 938c2ecf20Sopenharmony_ci# ============================================================================== 948c2ecf20Sopenharmony_ci # pylint: disable=W0613 958c2ecf20Sopenharmony_ci 968c2ecf20Sopenharmony_ci options = options if options is not None else {} 978c2ecf20Sopenharmony_ci content = content if content is not None else [] 988c2ecf20Sopenharmony_ci nodelist = [rowSpan(span=int(text))] 998c2ecf20Sopenharmony_ci msglist = [] 1008c2ecf20Sopenharmony_ci return nodelist, msglist 1018c2ecf20Sopenharmony_ci 1028c2ecf20Sopenharmony_ci 1038c2ecf20Sopenharmony_ci# ============================================================================== 1048c2ecf20Sopenharmony_ciclass rowSpan(nodes.General, nodes.Element): pass # pylint: disable=C0103,C0321 1058c2ecf20Sopenharmony_ciclass colSpan(nodes.General, nodes.Element): pass # pylint: disable=C0103,C0321 1068c2ecf20Sopenharmony_ci# ============================================================================== 1078c2ecf20Sopenharmony_ci 1088c2ecf20Sopenharmony_ci# ============================================================================== 1098c2ecf20Sopenharmony_ciclass FlatTable(Table): 1108c2ecf20Sopenharmony_ci# ============================================================================== 1118c2ecf20Sopenharmony_ci 1128c2ecf20Sopenharmony_ci u"""FlatTable (``flat-table``) directive""" 1138c2ecf20Sopenharmony_ci 1148c2ecf20Sopenharmony_ci option_spec = { 1158c2ecf20Sopenharmony_ci 'name': directives.unchanged 1168c2ecf20Sopenharmony_ci , 'class': directives.class_option 1178c2ecf20Sopenharmony_ci , 'header-rows': directives.nonnegative_int 1188c2ecf20Sopenharmony_ci , 'stub-columns': directives.nonnegative_int 1198c2ecf20Sopenharmony_ci , 'widths': directives.positive_int_list 1208c2ecf20Sopenharmony_ci , 'fill-cells' : directives.flag } 1218c2ecf20Sopenharmony_ci 1228c2ecf20Sopenharmony_ci def run(self): 1238c2ecf20Sopenharmony_ci 1248c2ecf20Sopenharmony_ci if not self.content: 1258c2ecf20Sopenharmony_ci error = self.state_machine.reporter.error( 1268c2ecf20Sopenharmony_ci 'The "%s" directive is empty; content required.' % self.name, 1278c2ecf20Sopenharmony_ci nodes.literal_block(self.block_text, self.block_text), 1288c2ecf20Sopenharmony_ci line=self.lineno) 1298c2ecf20Sopenharmony_ci return [error] 1308c2ecf20Sopenharmony_ci 1318c2ecf20Sopenharmony_ci title, messages = self.make_title() 1328c2ecf20Sopenharmony_ci node = nodes.Element() # anonymous container for parsing 1338c2ecf20Sopenharmony_ci self.state.nested_parse(self.content, self.content_offset, node) 1348c2ecf20Sopenharmony_ci 1358c2ecf20Sopenharmony_ci tableBuilder = ListTableBuilder(self) 1368c2ecf20Sopenharmony_ci tableBuilder.parseFlatTableNode(node) 1378c2ecf20Sopenharmony_ci tableNode = tableBuilder.buildTableNode() 1388c2ecf20Sopenharmony_ci # SDK.CONSOLE() # print --> tableNode.asdom().toprettyxml() 1398c2ecf20Sopenharmony_ci if title: 1408c2ecf20Sopenharmony_ci tableNode.insert(0, title) 1418c2ecf20Sopenharmony_ci return [tableNode] + messages 1428c2ecf20Sopenharmony_ci 1438c2ecf20Sopenharmony_ci 1448c2ecf20Sopenharmony_ci# ============================================================================== 1458c2ecf20Sopenharmony_ciclass ListTableBuilder(object): 1468c2ecf20Sopenharmony_ci# ============================================================================== 1478c2ecf20Sopenharmony_ci 1488c2ecf20Sopenharmony_ci u"""Builds a table from a double-stage list""" 1498c2ecf20Sopenharmony_ci 1508c2ecf20Sopenharmony_ci def __init__(self, directive): 1518c2ecf20Sopenharmony_ci self.directive = directive 1528c2ecf20Sopenharmony_ci self.rows = [] 1538c2ecf20Sopenharmony_ci self.max_cols = 0 1548c2ecf20Sopenharmony_ci 1558c2ecf20Sopenharmony_ci def buildTableNode(self): 1568c2ecf20Sopenharmony_ci 1578c2ecf20Sopenharmony_ci colwidths = self.directive.get_column_widths(self.max_cols) 1588c2ecf20Sopenharmony_ci if isinstance(colwidths, tuple): 1598c2ecf20Sopenharmony_ci # Since docutils 0.13, get_column_widths returns a (widths, 1608c2ecf20Sopenharmony_ci # colwidths) tuple, where widths is a string (i.e. 'auto'). 1618c2ecf20Sopenharmony_ci # See https://sourceforge.net/p/docutils/patches/120/. 1628c2ecf20Sopenharmony_ci colwidths = colwidths[1] 1638c2ecf20Sopenharmony_ci stub_columns = self.directive.options.get('stub-columns', 0) 1648c2ecf20Sopenharmony_ci header_rows = self.directive.options.get('header-rows', 0) 1658c2ecf20Sopenharmony_ci 1668c2ecf20Sopenharmony_ci table = nodes.table() 1678c2ecf20Sopenharmony_ci tgroup = nodes.tgroup(cols=len(colwidths)) 1688c2ecf20Sopenharmony_ci table += tgroup 1698c2ecf20Sopenharmony_ci 1708c2ecf20Sopenharmony_ci 1718c2ecf20Sopenharmony_ci for colwidth in colwidths: 1728c2ecf20Sopenharmony_ci colspec = nodes.colspec(colwidth=colwidth) 1738c2ecf20Sopenharmony_ci # FIXME: It seems, that the stub method only works well in the 1748c2ecf20Sopenharmony_ci # absence of rowspan (observed by the html buidler, the docutils-xml 1758c2ecf20Sopenharmony_ci # build seems OK). This is not extraordinary, because there exists 1768c2ecf20Sopenharmony_ci # no table directive (except *this* flat-table) which allows to 1778c2ecf20Sopenharmony_ci # define coexistent of rowspan and stubs (there was no use-case 1788c2ecf20Sopenharmony_ci # before flat-table). This should be reviewed (later). 1798c2ecf20Sopenharmony_ci if stub_columns: 1808c2ecf20Sopenharmony_ci colspec.attributes['stub'] = 1 1818c2ecf20Sopenharmony_ci stub_columns -= 1 1828c2ecf20Sopenharmony_ci tgroup += colspec 1838c2ecf20Sopenharmony_ci stub_columns = self.directive.options.get('stub-columns', 0) 1848c2ecf20Sopenharmony_ci 1858c2ecf20Sopenharmony_ci if header_rows: 1868c2ecf20Sopenharmony_ci thead = nodes.thead() 1878c2ecf20Sopenharmony_ci tgroup += thead 1888c2ecf20Sopenharmony_ci for row in self.rows[:header_rows]: 1898c2ecf20Sopenharmony_ci thead += self.buildTableRowNode(row) 1908c2ecf20Sopenharmony_ci 1918c2ecf20Sopenharmony_ci tbody = nodes.tbody() 1928c2ecf20Sopenharmony_ci tgroup += tbody 1938c2ecf20Sopenharmony_ci 1948c2ecf20Sopenharmony_ci for row in self.rows[header_rows:]: 1958c2ecf20Sopenharmony_ci tbody += self.buildTableRowNode(row) 1968c2ecf20Sopenharmony_ci return table 1978c2ecf20Sopenharmony_ci 1988c2ecf20Sopenharmony_ci def buildTableRowNode(self, row_data, classes=None): 1998c2ecf20Sopenharmony_ci classes = [] if classes is None else classes 2008c2ecf20Sopenharmony_ci row = nodes.row() 2018c2ecf20Sopenharmony_ci for cell in row_data: 2028c2ecf20Sopenharmony_ci if cell is None: 2038c2ecf20Sopenharmony_ci continue 2048c2ecf20Sopenharmony_ci cspan, rspan, cellElements = cell 2058c2ecf20Sopenharmony_ci 2068c2ecf20Sopenharmony_ci attributes = {"classes" : classes} 2078c2ecf20Sopenharmony_ci if rspan: 2088c2ecf20Sopenharmony_ci attributes['morerows'] = rspan 2098c2ecf20Sopenharmony_ci if cspan: 2108c2ecf20Sopenharmony_ci attributes['morecols'] = cspan 2118c2ecf20Sopenharmony_ci entry = nodes.entry(**attributes) 2128c2ecf20Sopenharmony_ci entry.extend(cellElements) 2138c2ecf20Sopenharmony_ci row += entry 2148c2ecf20Sopenharmony_ci return row 2158c2ecf20Sopenharmony_ci 2168c2ecf20Sopenharmony_ci def raiseError(self, msg): 2178c2ecf20Sopenharmony_ci error = self.directive.state_machine.reporter.error( 2188c2ecf20Sopenharmony_ci msg 2198c2ecf20Sopenharmony_ci , nodes.literal_block(self.directive.block_text 2208c2ecf20Sopenharmony_ci , self.directive.block_text) 2218c2ecf20Sopenharmony_ci , line = self.directive.lineno ) 2228c2ecf20Sopenharmony_ci raise SystemMessagePropagation(error) 2238c2ecf20Sopenharmony_ci 2248c2ecf20Sopenharmony_ci def parseFlatTableNode(self, node): 2258c2ecf20Sopenharmony_ci u"""parses the node from a :py:class:`FlatTable` directive's body""" 2268c2ecf20Sopenharmony_ci 2278c2ecf20Sopenharmony_ci if len(node) != 1 or not isinstance(node[0], nodes.bullet_list): 2288c2ecf20Sopenharmony_ci self.raiseError( 2298c2ecf20Sopenharmony_ci 'Error parsing content block for the "%s" directive: ' 2308c2ecf20Sopenharmony_ci 'exactly one bullet list expected.' % self.directive.name ) 2318c2ecf20Sopenharmony_ci 2328c2ecf20Sopenharmony_ci for rowNum, rowItem in enumerate(node[0]): 2338c2ecf20Sopenharmony_ci row = self.parseRowItem(rowItem, rowNum) 2348c2ecf20Sopenharmony_ci self.rows.append(row) 2358c2ecf20Sopenharmony_ci self.roundOffTableDefinition() 2368c2ecf20Sopenharmony_ci 2378c2ecf20Sopenharmony_ci def roundOffTableDefinition(self): 2388c2ecf20Sopenharmony_ci u"""Round off the table definition. 2398c2ecf20Sopenharmony_ci 2408c2ecf20Sopenharmony_ci This method rounds off the table definition in :py:member:`rows`. 2418c2ecf20Sopenharmony_ci 2428c2ecf20Sopenharmony_ci * This method inserts the needed ``None`` values for the missing cells 2438c2ecf20Sopenharmony_ci arising from spanning cells over rows and/or columns. 2448c2ecf20Sopenharmony_ci 2458c2ecf20Sopenharmony_ci * recount the :py:member:`max_cols` 2468c2ecf20Sopenharmony_ci 2478c2ecf20Sopenharmony_ci * Autospan or fill (option ``fill-cells``) missing cells on the right 2488c2ecf20Sopenharmony_ci side of the table-row 2498c2ecf20Sopenharmony_ci """ 2508c2ecf20Sopenharmony_ci 2518c2ecf20Sopenharmony_ci y = 0 2528c2ecf20Sopenharmony_ci while y < len(self.rows): 2538c2ecf20Sopenharmony_ci x = 0 2548c2ecf20Sopenharmony_ci 2558c2ecf20Sopenharmony_ci while x < len(self.rows[y]): 2568c2ecf20Sopenharmony_ci cell = self.rows[y][x] 2578c2ecf20Sopenharmony_ci if cell is None: 2588c2ecf20Sopenharmony_ci x += 1 2598c2ecf20Sopenharmony_ci continue 2608c2ecf20Sopenharmony_ci cspan, rspan = cell[:2] 2618c2ecf20Sopenharmony_ci # handle colspan in current row 2628c2ecf20Sopenharmony_ci for c in range(cspan): 2638c2ecf20Sopenharmony_ci try: 2648c2ecf20Sopenharmony_ci self.rows[y].insert(x+c+1, None) 2658c2ecf20Sopenharmony_ci except: # pylint: disable=W0702 2668c2ecf20Sopenharmony_ci # the user sets ambiguous rowspans 2678c2ecf20Sopenharmony_ci pass # SDK.CONSOLE() 2688c2ecf20Sopenharmony_ci # handle colspan in spanned rows 2698c2ecf20Sopenharmony_ci for r in range(rspan): 2708c2ecf20Sopenharmony_ci for c in range(cspan + 1): 2718c2ecf20Sopenharmony_ci try: 2728c2ecf20Sopenharmony_ci self.rows[y+r+1].insert(x+c, None) 2738c2ecf20Sopenharmony_ci except: # pylint: disable=W0702 2748c2ecf20Sopenharmony_ci # the user sets ambiguous rowspans 2758c2ecf20Sopenharmony_ci pass # SDK.CONSOLE() 2768c2ecf20Sopenharmony_ci x += 1 2778c2ecf20Sopenharmony_ci y += 1 2788c2ecf20Sopenharmony_ci 2798c2ecf20Sopenharmony_ci # Insert the missing cells on the right side. For this, first 2808c2ecf20Sopenharmony_ci # re-calculate the max columns. 2818c2ecf20Sopenharmony_ci 2828c2ecf20Sopenharmony_ci for row in self.rows: 2838c2ecf20Sopenharmony_ci if self.max_cols < len(row): 2848c2ecf20Sopenharmony_ci self.max_cols = len(row) 2858c2ecf20Sopenharmony_ci 2868c2ecf20Sopenharmony_ci # fill with empty cells or cellspan? 2878c2ecf20Sopenharmony_ci 2888c2ecf20Sopenharmony_ci fill_cells = False 2898c2ecf20Sopenharmony_ci if 'fill-cells' in self.directive.options: 2908c2ecf20Sopenharmony_ci fill_cells = True 2918c2ecf20Sopenharmony_ci 2928c2ecf20Sopenharmony_ci for row in self.rows: 2938c2ecf20Sopenharmony_ci x = self.max_cols - len(row) 2948c2ecf20Sopenharmony_ci if x and not fill_cells: 2958c2ecf20Sopenharmony_ci if row[-1] is None: 2968c2ecf20Sopenharmony_ci row.append( ( x - 1, 0, []) ) 2978c2ecf20Sopenharmony_ci else: 2988c2ecf20Sopenharmony_ci cspan, rspan, content = row[-1] 2998c2ecf20Sopenharmony_ci row[-1] = (cspan + x, rspan, content) 3008c2ecf20Sopenharmony_ci elif x and fill_cells: 3018c2ecf20Sopenharmony_ci for i in range(x): 3028c2ecf20Sopenharmony_ci row.append( (0, 0, nodes.comment()) ) 3038c2ecf20Sopenharmony_ci 3048c2ecf20Sopenharmony_ci def pprint(self): 3058c2ecf20Sopenharmony_ci # for debugging 3068c2ecf20Sopenharmony_ci retVal = "[ " 3078c2ecf20Sopenharmony_ci for row in self.rows: 3088c2ecf20Sopenharmony_ci retVal += "[ " 3098c2ecf20Sopenharmony_ci for col in row: 3108c2ecf20Sopenharmony_ci if col is None: 3118c2ecf20Sopenharmony_ci retVal += ('%r' % col) 3128c2ecf20Sopenharmony_ci retVal += "\n , " 3138c2ecf20Sopenharmony_ci else: 3148c2ecf20Sopenharmony_ci content = col[2][0].astext() 3158c2ecf20Sopenharmony_ci if len (content) > 30: 3168c2ecf20Sopenharmony_ci content = content[:30] + "..." 3178c2ecf20Sopenharmony_ci retVal += ('(cspan=%s, rspan=%s, %r)' 3188c2ecf20Sopenharmony_ci % (col[0], col[1], content)) 3198c2ecf20Sopenharmony_ci retVal += "]\n , " 3208c2ecf20Sopenharmony_ci retVal = retVal[:-2] 3218c2ecf20Sopenharmony_ci retVal += "]\n , " 3228c2ecf20Sopenharmony_ci retVal = retVal[:-2] 3238c2ecf20Sopenharmony_ci return retVal + "]" 3248c2ecf20Sopenharmony_ci 3258c2ecf20Sopenharmony_ci def parseRowItem(self, rowItem, rowNum): 3268c2ecf20Sopenharmony_ci row = [] 3278c2ecf20Sopenharmony_ci childNo = 0 3288c2ecf20Sopenharmony_ci error = False 3298c2ecf20Sopenharmony_ci cell = None 3308c2ecf20Sopenharmony_ci target = None 3318c2ecf20Sopenharmony_ci 3328c2ecf20Sopenharmony_ci for child in rowItem: 3338c2ecf20Sopenharmony_ci if (isinstance(child , nodes.comment) 3348c2ecf20Sopenharmony_ci or isinstance(child, nodes.system_message)): 3358c2ecf20Sopenharmony_ci pass 3368c2ecf20Sopenharmony_ci elif isinstance(child , nodes.target): 3378c2ecf20Sopenharmony_ci target = child 3388c2ecf20Sopenharmony_ci elif isinstance(child, nodes.bullet_list): 3398c2ecf20Sopenharmony_ci childNo += 1 3408c2ecf20Sopenharmony_ci cell = child 3418c2ecf20Sopenharmony_ci else: 3428c2ecf20Sopenharmony_ci error = True 3438c2ecf20Sopenharmony_ci break 3448c2ecf20Sopenharmony_ci 3458c2ecf20Sopenharmony_ci if childNo != 1 or error: 3468c2ecf20Sopenharmony_ci self.raiseError( 3478c2ecf20Sopenharmony_ci 'Error parsing content block for the "%s" directive: ' 3488c2ecf20Sopenharmony_ci 'two-level bullet list expected, but row %s does not ' 3498c2ecf20Sopenharmony_ci 'contain a second-level bullet list.' 3508c2ecf20Sopenharmony_ci % (self.directive.name, rowNum + 1)) 3518c2ecf20Sopenharmony_ci 3528c2ecf20Sopenharmony_ci for cellItem in cell: 3538c2ecf20Sopenharmony_ci cspan, rspan, cellElements = self.parseCellItem(cellItem) 3548c2ecf20Sopenharmony_ci if target is not None: 3558c2ecf20Sopenharmony_ci cellElements.insert(0, target) 3568c2ecf20Sopenharmony_ci row.append( (cspan, rspan, cellElements) ) 3578c2ecf20Sopenharmony_ci return row 3588c2ecf20Sopenharmony_ci 3598c2ecf20Sopenharmony_ci def parseCellItem(self, cellItem): 3608c2ecf20Sopenharmony_ci # search and remove cspan, rspan colspec from the first element in 3618c2ecf20Sopenharmony_ci # this listItem (field). 3628c2ecf20Sopenharmony_ci cspan = rspan = 0 3638c2ecf20Sopenharmony_ci if not len(cellItem): 3648c2ecf20Sopenharmony_ci return cspan, rspan, [] 3658c2ecf20Sopenharmony_ci for elem in cellItem[0]: 3668c2ecf20Sopenharmony_ci if isinstance(elem, colSpan): 3678c2ecf20Sopenharmony_ci cspan = elem.get("span") 3688c2ecf20Sopenharmony_ci elem.parent.remove(elem) 3698c2ecf20Sopenharmony_ci continue 3708c2ecf20Sopenharmony_ci if isinstance(elem, rowSpan): 3718c2ecf20Sopenharmony_ci rspan = elem.get("span") 3728c2ecf20Sopenharmony_ci elem.parent.remove(elem) 3738c2ecf20Sopenharmony_ci continue 3748c2ecf20Sopenharmony_ci return cspan, rspan, cellItem[:] 375