计算表格宽度的可变列宽的算法

时间:2014-03-05 19:00:46

标签: algorithm math

我需要找出一个算法来计算列宽的优化大小,如下所示:

  • 表的宽度固定为页面的大小
  • 列内的数据将是可变的,因此列的宽度是可变的
  • 必须优化宽度以知道何时包装列以及何时不

所以给出以下数据:

'From' => '03/06/2014',
'To' => '03/06/2014',
'First Name' => 'John Doe',
'Status' => 'approved',
'Type' => 'PTO',
'Amount' => '8 Hours',
'Notes' => 'Oops! Who knew I would need one more day. This should be all I need over the next week to support my trip.'

如何计算最佳色谱柱宽度,以便“注释”列不会将其他宽度压缩到低于可接受的宽度?

Sample of Problem

更新:我目前知道网页的宽度&字体的宽度,所以我可以计算每列的最大宽度要求。它应该填充页面上的可用空间。但是,除非必要,否则我希望列不包装。像这样:

enter image description here

3 个答案:

答案 0 :(得分:7)

一个简单的解决方案是为您的列分配属性;例如,您的Notes列可能是flexible。然后,您可以计算所有行上每列的最大宽度,为所有非灵活列设置该宽度,然后将剩余空间均匀分布(或者可能按其最大宽度加权)到灵活列。

但您也可以尝试使用一些简单的条件找出属性:

  • 如果列中的任何条目中包含空格,则列可以是自动换行的。在您的示例中,无法包装日期以及可能的状态和类型条目。该名称不应在正常情况下包装,但如果名称很长或者给出了多个名称,则可以包装。应该包括备注栏。
  • 如果列的最大宽度超过,例如,如果所有尺寸均匀分布,则单元格的宽度应该是灵活的。

然后按照上述说明:计算所有非柔性列的宽度。检查是否有足够的空间;如果没有,也可以使可包装的柱子变得灵活。然后计算柔性单元格的宽度,按其最大宽度加权。

可能的伪代码算法如下。它可以自由地使用各种启发式方法,所以你应该把它当作一粒盐。您可以根据用例调整这些条件,但很难满足所有可能的情况。

function layout(table[], width, gutter, col[])

    var maxw[col.length]        # max. text width over all rows
    var maxl[col.length]        # max. width of longest word
    var flex[col.length]        # is column flexible?
    var wrap[col.length]        # can column be wrapped?
    var colw[col.length]        # final width of columns

    foreach row in table:
        for i = 0 to col.length:
            cell = row[i]
            maxw[i] = max(maxw[i], textwidth(cell))
            if cell.find(" "):
                maxl[i] = max(maxl[i], wordwidth(cell))

    var left = width - (col.length - 1) * gutter
    var avg = left / col.length
    var nflex = 0

    # determine whether columns should be flexible and assign
    # width of non-flexible cells

    for i = 0 to col.length:
        flex[i] = (maxw[i] > 2 * avg)       # ???
        if flex[i]:
            nflex++
        else:
            colw[i] = maxw[i]
            left -= colw[i]

    # if there is not enough space, make columns that could
    # be word-wrapped flexible, too

    if left < nflex * avg:
        for i = 0 to col.length:
            if !flex[i] and wrap[i]:
                left += width[i]
                colw[i] = 0
                flex[i] = true
                nflex += 1

    # Calculate weights for flexible columns. The max width
    # is capped at the page width to treat columns that have to 
    # be wrapped more or less equal

    var tot = 0
    for i = 0 to col.length:
        if flex[i]:
            maxw[i] = min(maxw[i], width)      # ???
            tot += maxw[i]

    # Now assign the actual width for flexible columns. Make
    # sure that it is at least as long as the longest word length

    for i = 0 to col.length:
        if flex[i]:
            colw[i] = left * maxw[i] / tot
            colw[i] = max(colw[i], maxl[i])
            left -= colw[i]

    return colw

答案 1 :(得分:1)

W3C在CSS 3 Tables Algorithms中发布了类似这样的东西的算法。

我在HTML4.1 specs中找到了一个我已成功使用并且实现起来非常简单的简单算法:

  

然后使用最小和最大单元格宽度来确定   列的相应最小和最大宽度。这些在   转,用于查找表的最小和最大宽度。   请注意,单元格可以包含嵌套表,但这并不复杂   代码显着。下一步是指定列宽   根据可用空间(即电流之间的空间)   左右边距)。

     

对于跨越多列的单元格,一种简单的方法包括   将最小/最大宽度均匀地分配给每个成分   列。稍微复杂的方法是使用最小/最大宽度   未扩展的单元格来衡量跨度宽度的分配方式。   实验表明,两种方法的混合可以提供良好的效果   各种表格的结果。

     

需要包含表格边框和单元间边距   指定列宽。有三种情况:

     
      
  • 最小表格宽度等于或大于可用空间。在这种情况下,指定最小宽度并允许用户水平滚动。对于转换为盲文,有必要通过引用包含其全部内容的注释来替换单元格。按惯例,这些出现在表格之前。
  •   
  • 最大表格宽度适合可用空间。在这种情况下,请将列设置为其最大宽度。
  •   
  • 表的最大宽度大于可用空间,但最小表宽度较小。在这种情况下,找到可用空间和最小工作台宽度之间的差异,让我们称之为W.也可以调用D表格的最大和最小宽度之间的差异。
      对于每列,设d为该列的最大和最小宽度之间的差异。现在将列的宽度设置为最小宽度加上D乘以D乘以D.这使得最小和最大宽度之间差异较大的列比差异较小的列宽。
  •   

答案 2 :(得分:0)

我使用带有14列表的ITextPDF遇到了与此类似的问题。数据是可变的,其中一些列可以包装,而其他列则不能。

我的解决方案是使用split(“”)找到每列中最大的单词。这减少了单词,日期或数字减半的几率。这是代码。抱歉,我没有时间将其编辑为更通用的格式,希望无论如何它都能帮到某人。

//This array will store the largest word found in each of the 14 columns
int[] maxStringLengthPerColumn = new int[14];
for(int i = 0; i < maxStringLengthPerColumn.length; i++)
    maxStringLengthPerColumn[i]=0;

//for each row in table...

ArrayList<PdfPRow> rows = table.getRows();
for(int a = 0; a < rows.size(); a++){


    //for each cell in row
    PdfPCell[] cellsInRow = rows.get(a).getCells();
    for(int b = 0; b < cellsInRow.length; b++){

        //Split cell contents at " " and find longest word in each cell
        String[] splitCell = cellsInRow[b].getPhrase().getContent().split(" ");


        //find the longest string left after split
        int largestStringSize = 0;
        for(int c = 0; c < splitCell.length; c++){
            if(splitCell[c].length()>largestStringSize){
                largestStringSize=splitCell[c].length();
            }
        }

        if(largestStringSize>maxStringLengthPerColumn[b]){
            //I found that adding 4 to the value worked, change this number to fine tune.
            maxStringLengthPerColumn[b] = largestStringSize + 4;
        }
    }
}

/*The pdf library can set width with just an array, you may need to 
convert these values to something else depending on the application. For 
example if you have a width of 800 pixels, the width of col1 would be 
maxStringLengthPerColumn[0] / (sum of maxString0 - 13) * 800*/

table.setWidths(maxStringLengthPerColumn);