"Fossies" - the Fresh Open Source Software Archive

Member "xhtml2pdf-0.2.5/xhtml2pdf/tables.py" (29 Sep 2020, 12089 Bytes) of package /linux/www/xhtml2pdf-0.2.5.tar.gz:


As a special service "Fossies" has tried to format the requested source page into HTML format using (guessed) Python source code syntax highlighting (style: standard) with prefixed line numbers. Alternatively you can here view or download the uninterpreted source code file. For more information about "tables.py" see the Fossies "Dox" file reference documentation and the latest Fossies "Diffs" side-by-side code changes report: 0.2.4_vs_0.2.5.

    1 # -*- coding: utf-8 -*-
    2 import copy
    3 import logging
    4 
    5 from reportlab.platypus.tables import TableStyle
    6 import six
    7 
    8 from xhtml2pdf.tags import pisaTag
    9 from xhtml2pdf.util import getSize, getBorderStyle, getAlign, set_value
   10 from xhtml2pdf.xhtml2pdf_reportlab import PmlTable, PmlKeepInFrame
   11 
   12 
   13 # Copyright 2010 Dirk Holtwick, holtwick.it
   14 #
   15 # Licensed under the Apache License, Version 2.0 (the "License");
   16 # you may not use this file except in compliance with the License.
   17 # You may obtain a copy of the License at
   18 #
   19 #     http://www.apache.org/licenses/LICENSE-2.0
   20 #
   21 # Unless required by applicable law or agreed to in writing, software
   22 # distributed under the License is distributed on an "AS IS" BASIS,
   23 # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
   24 # See the License for the specific language governing permissions and
   25 # limitations under the License.
   26 log = logging.getLogger("xhtml2pdf")
   27 
   28 
   29 def _width(value=None):
   30     if value is None:
   31         return None
   32     value = str(value)
   33     if value.endswith("%"):
   34         return value
   35     return getSize(value)
   36 
   37 
   38 def _height(value=None):
   39     if value is None:
   40         return None
   41     value = str(value)
   42     if value.endswith("%"):
   43         return value
   44     return getSize(value)
   45 
   46 
   47 class TableData:
   48 
   49     def __init__(self):
   50         self.data = []
   51         self.styles = []
   52         self.span = []
   53         self.mode = ""
   54         self.padding = 0
   55         self.col = 0
   56 
   57     def add_cell(self, data=None):
   58         self.col += 1
   59         self.data[len(self.data) - 1].append(data)
   60 
   61     def add_style(self, data):
   62         self.styles.append(copy.copy(data))
   63 
   64     def add_empty(self, x, y):
   65         self.span.append((x, y))
   66 
   67     def get_data(self):
   68         data = self.data
   69         for x, y in self.span:
   70             # Loop through all the spans that are inside the boundaries of our
   71             # tables. If the y-coordinate is valid, we insert an empty cell.
   72             # As for the x coordinate, we somehow don't care.
   73             if y < len(data):
   74                 data[y].insert(x, '')
   75         return data
   76 
   77     def add_cell_styles(self, c, begin, end, mode="td"):
   78         self.mode = mode.upper()
   79         if c.frag.backColor and mode != "tr":  # XXX Stimmt das so?
   80             self.add_style(('BACKGROUND', begin, end, c.frag.backColor))
   81 
   82         if 0:
   83             log.debug("%r", (
   84                 begin,
   85                 end,
   86                 c.frag.borderTopWidth,
   87                 c.frag.borderTopStyle,
   88                 c.frag.borderTopColor,
   89                 c.frag.borderBottomWidth,
   90                 c.frag.borderBottomStyle,
   91                 c.frag.borderBottomColor,
   92                 c.frag.borderLeftWidth,
   93                 c.frag.borderLeftStyle,
   94                 c.frag.borderLeftColor,
   95                 c.frag.borderRightWidth,
   96                 c.frag.borderRightStyle,
   97                 c.frag.borderRightColor,
   98             ))
   99 
  100         if getBorderStyle(c.frag.borderTopStyle) and c.frag.borderTopWidth and c.frag.borderTopColor is not None:
  101             self.add_style(('LINEABOVE', begin, (end[0], begin[1]),
  102                             c.frag.borderTopWidth,
  103                             c.frag.borderTopColor,
  104                             "squared"))
  105         if getBorderStyle(c.frag.borderLeftStyle) and c.frag.borderLeftWidth and c.frag.borderLeftColor is not None:
  106             self.add_style(('LINEBEFORE', begin, (begin[0], end[1]),
  107                             c.frag.borderLeftWidth,
  108                             c.frag.borderLeftColor,
  109                             "squared"))
  110         if getBorderStyle(c.frag.borderRightStyle) and c.frag.borderRightWidth and c.frag.borderRightColor is not None:
  111             self.add_style(('LINEAFTER', (end[0], begin[1]), end,
  112                             c.frag.borderRightWidth,
  113                             c.frag.borderRightColor,
  114                             "squared"))
  115         if getBorderStyle(
  116                 c.frag.borderBottomStyle) and c.frag.borderBottomWidth and c.frag.borderBottomColor is not None:
  117             self.add_style(('LINEBELOW', (begin[0], end[1]), end,
  118                             c.frag.borderBottomWidth,
  119                             c.frag.borderBottomColor,
  120                             "squared"))
  121         self.add_style(
  122             ('LEFTPADDING', begin, end, c.frag.paddingLeft or self.padding))
  123         self.add_style(
  124             ('RIGHTPADDING', begin, end, c.frag.paddingRight or self.padding))
  125         self.add_style(
  126             ('TOPPADDING', begin, end, c.frag.paddingTop or self.padding))
  127         self.add_style(
  128             ('BOTTOMPADDING', begin, end, c.frag.paddingBottom or self.padding))
  129 
  130 
  131 class pisaTagTABLE(pisaTag):
  132 
  133     def set_borders(self, frag, attrs):
  134 
  135         set_value(frag,
  136                   ('borderLeftColor', 'borderRightColor', 'borderTopColor',
  137                    'borderBottomColor'), attrs.bordercolor)
  138 
  139         set_value(frag,
  140                   ('borderLeftWidth', 'borderRightWidth', 'borderTopWidth',
  141                    'borderBottomWidth'),
  142                   attrs.border)
  143         set_value(frag,
  144                   ('borderBottomStyle', 'borderLeftStyle', 'borderTopStyle',
  145                    'borderRightStyle'),
  146                   "solid"
  147                   )
  148 
  149     def start(self, c):
  150         c.addPara()
  151 
  152         attrs = self.attr
  153 
  154         c.tableData, self.tableData = TableData(), c.tableData
  155         tdata = c.tableData
  156 
  157         if attrs.border and attrs.bordercolor:
  158             self.set_borders(c.frag, attrs)
  159 
  160         tdata.padding = attrs.cellpadding
  161         tdata.add_cell_styles(c, (0, 0), (-1, - 1), "table")
  162         tdata.align = attrs.align.upper()
  163         tdata.col = 0
  164         tdata.row = 0
  165         tdata.colw = []
  166         tdata.rowh = []
  167         tdata.repeat = attrs.repeat
  168         tdata.width = _width(attrs.width)
  169 
  170     def end(self, c):
  171         tdata = c.tableData
  172         data = tdata.get_data()
  173 
  174         # Add missing columns so that each row has the same count of columns
  175         # This prevents errors in Reportlab table
  176 
  177         try:
  178             maxcols = max([len(row) for row in data] or [0])
  179         except ValueError:
  180             log.warning(c.warning("<table> rows seem to be inconsistent"))
  181             maxcols = [0]
  182 
  183         for i, row in enumerate(data):
  184             data[i] += [''] * (maxcols - len(row))
  185 
  186         log.debug("Col widths: {}".format(list(tdata.colw)))
  187         if tdata.data:
  188             # log.debug("Table styles %r", tdata.styles)
  189             t = PmlTable(
  190                 data,
  191                 colWidths=tdata.colw,
  192                 rowHeights=tdata.rowh,
  193                 # totalWidth = tdata.width,
  194                 splitByRow=1,
  195                 # repeatCols = 1,
  196                 repeatRows=tdata.repeat,
  197                 hAlign=tdata.align,
  198                 vAlign='TOP',
  199                 style=TableStyle(tdata.styles))
  200             t.totalWidth = _width(tdata.width)
  201             t.spaceBefore = c.frag.spaceBefore
  202             t.spaceAfter = c.frag.spaceAfter
  203 
  204             # XXX Maybe we need to copy some more properties?
  205             t.keepWithNext = c.frag.keepWithNext
  206             # t.hAlign = tdata.align
  207             c.addStory(t)
  208         else:
  209             log.warning(c.warning("<table> is empty"))
  210 
  211         # Cleanup and re-swap table data
  212         c.clearFrag()
  213         c.tableData, self.tableData = self.tableData, None
  214 
  215 
  216 class pisaTagTR(pisaTag):
  217 
  218     def start(self, c):
  219         tdata = c.tableData
  220         row = tdata.row
  221         begin = (0, row)
  222         end = (-1, row)
  223 
  224         tdata.add_cell_styles(c, begin, end, "tr")
  225         c.frag.vAlign = self.attr.valign or c.frag.vAlign
  226         if c.frag.backColor:
  227             tdata.add_style(('BACKGROUND', begin, end, c.frag.backColor))
  228             
  229         tdata.col = 0
  230         tdata.data.append([])
  231 
  232     def end(self, c):
  233         c.tableData.row += 1
  234 
  235 
  236 class pisaTagTD(pisaTag):
  237 
  238     def start(self, c):
  239 
  240         if self.attr.align is not None:
  241             c.frag.alignment = getAlign(self.attr.align)
  242 
  243         c.clearFrag()
  244         self.story = c.swapStory()
  245 
  246         attrs = self.attr
  247 
  248         tdata = c.tableData
  249 
  250         cspan = attrs.colspan
  251         rspan = attrs.rowspan
  252 
  253         row = tdata.row
  254         col = tdata.col
  255         while 1:
  256             for x, y in tdata.span:
  257                 if x == col and y == row:
  258                     col += 1
  259                     tdata.col += 1
  260             break
  261 
  262         begin = (col, row)
  263         end = (col, row)
  264         if cspan:
  265             end = (end[0] + cspan - 1, end[1])
  266         if rspan:
  267             end = (end[0], end[1] + rspan - 1)
  268         if begin != end:
  269             tdata.add_style(('SPAN', begin, end))
  270             for x in six.moves.range(begin[0], end[0] + 1):
  271                 for y in six.moves.range(begin[1], end[1] + 1):
  272                     if x != begin[0] or y != begin[1]:
  273                         tdata.add_empty(x, y)
  274 
  275         # Set Border and padding styles
  276         tdata.add_cell_styles(c, begin, end, "td")
  277 
  278         # Calculate widths
  279         # Add empty placeholders for new columns
  280         if (col + 1) > len(tdata.colw):
  281             tdata.colw = tdata.colw + \
  282                 ((col + 1 - len(tdata.colw)) * [_width()])
  283 
  284         # Get value of with, if no spanning
  285         if not cspan:
  286             width = c.frag.width or self.attr.width
  287             # If is value, the set it in the right place in the arry
  288             if width is not None:
  289                 tdata.colw[col] = _width(width)
  290                 log.debug("Col {} has width {}".format(col, width))
  291             else:
  292                 # If there are no child nodes, nothing within the column can change the
  293                 # width.  Set the column width to the sum of the right and left padding
  294                 # rather than letting it default.
  295                 log.debug(width)
  296                 if len(self.node.childNodes) == 0:
  297                     width = c.frag.paddingLeft + c.frag.paddingRight
  298                     log.debug("Col {} has width {}".format(col, width))
  299                     if width:
  300                         tdata.colw[col] = _width(width)
  301                 else:
  302                     # Child nodes are present, we cannot do anything about the
  303                     # width except set it externally.
  304                     pass
  305 
  306         # Calculate heights
  307         if row + 1 > len(tdata.rowh):
  308             tdata.rowh = tdata.rowh + \
  309                 ((row + 1 - len(tdata.rowh)) * [_width()])
  310         if not rspan:
  311             height = c.frag.height or self.attr.get('height', None)
  312             if height is not None:
  313                 tdata.rowh[row] = _height(height)
  314                 tdata.add_style(('FONTSIZE', begin, end, 1.0))
  315                 tdata.add_style(('LEADING', begin, end, 1.0))
  316 
  317         # Vertical align
  318         valign = self.attr.valign or c.frag.vAlign
  319         if valign is not None:
  320             tdata.add_style(('VALIGN', begin, end, valign.upper()))
  321 
  322         # Reset border, otherwise the paragraph block will have borders too
  323         frag = c.frag
  324 
  325         set_value(frag,
  326                   ('borderLeftWidth', 'borderRightWidth',
  327                    'borderTopWidth', 'borderBottomWidth'),
  328                   0)
  329 
  330         set_value(frag,
  331                   ('borderLeftColor', 'borderLeftStyle', 'borderRightColor',
  332                    'borderRightStyle', 'borderTopColor', 'borderTopStyle',
  333                    'borderBottomColor', 'borderBottomStyle'),
  334                   None
  335                   )
  336 
  337     def end(self, c):
  338         tdata = c.tableData
  339 
  340         c.addPara()
  341         cell = c.story
  342 
  343         # Keep in frame if needed since Reportlab does no split inside of cells
  344         if not c.frag.insideStaticFrame:
  345             # tdata.keepinframe["content"] = cell
  346             mode = c.cssAttr.get("-pdf-keep-in-frame-mode", "shrink")
  347             # keepInFrame mode is passed to Platypus for rendering
  348             cell = PmlKeepInFrame(
  349                 maxWidth=0,
  350                 maxHeight=0,
  351                 mode=mode,
  352                 content=cell)
  353 
  354         c.swapStory(self.story)
  355 
  356         tdata.add_cell(cell)
  357 
  358 
  359 class pisaTagTH(pisaTagTD):
  360     pass