1 Star 0 Fork 0

coodder / unipdf

加入 Gitee
与超过 1200万 开发者一起发现、参与优秀开源项目,私有仓库也完全免费 :)
免费加入
克隆/下载
table.go 26.51 KB
一键复制 编辑 原始数据 按行查看 历史
jhonm 提交于 2023-08-07 15:31 . init
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974
package creator
import (
"errors"
"math"
"sort"
"gitee.com/coodder/unipdf/common"
"gitee.com/coodder/unipdf/contentstream/draw"
"gitee.com/coodder/unipdf/core"
"gitee.com/coodder/unipdf/model"
)
// Table allows organizing content in an rows X columns matrix, which can spawn across multiple pages.
type Table struct {
// Number of rows and columns.
rows int
cols int
// Current cell. Current cell in the table.
// For 4x4 table, if in the 2nd row, 3rd column, then
// curCell = 4+3 = 7
curCell int
// Column width fractions: should add up to 1.
colWidths []float64
// Row heights.
rowHeights []float64
// Default row height.
defaultRowHeight float64
// Content cells.
cells []*TableCell
// Positioning: relative / absolute.
positioning positioning
// Absolute coordinates (when in absolute mode).
xPos, yPos float64
// Margins to be applied around the block when drawing on Page.
margins margins
// Specifies whether the table has a header.
hasHeader bool
// Header rows.
headerStartRow int
headerEndRow int
}
// newTable create a new Table with a specified number of columns.
func newTable(cols int) *Table {
t := &Table{
cols: cols,
defaultRowHeight: 10.0,
colWidths: []float64{},
rowHeights: []float64{},
cells: []*TableCell{},
}
t.resetColumnWidths()
return t
}
// SetColumnWidths sets the fractional column widths.
// Each width should be in the range 0-1 and is a fraction of the table width.
// The number of width inputs must match number of columns, otherwise an error is returned.
func (table *Table) SetColumnWidths(widths ...float64) error {
if len(widths) != table.cols {
common.Log.Debug("Mismatching number of widths and columns")
return errors.New("range check error")
}
table.colWidths = widths
return nil
}
func (table *Table) resetColumnWidths() {
table.colWidths = []float64{}
colWidth := float64(1.0) / float64(table.cols)
// Initialize column widths as all equal.
for i := 0; i < table.cols; i++ {
table.colWidths = append(table.colWidths, colWidth)
}
}
// Height returns the total height of all rows.
func (table *Table) Height() float64 {
sum := float64(0.0)
for _, h := range table.rowHeights {
sum += h
}
return sum
}
// Width is not used. Not used as a Table element is designed to fill into
// available width depending on the context. Returns 0.
func (table *Table) Width() float64 {
return 0
}
// SetMargins sets the Table's left, right, top, bottom margins.
func (table *Table) SetMargins(left, right, top, bottom float64) {
table.margins.left = left
table.margins.right = right
table.margins.top = top
table.margins.bottom = bottom
}
// GetMargins returns the left, right, top, bottom Margins.
func (table *Table) GetMargins() (float64, float64, float64, float64) {
return table.margins.left, table.margins.right, table.margins.top, table.margins.bottom
}
// GetRowHeight returns the height of the specified row.
func (table *Table) GetRowHeight(row int) (float64, error) {
if row < 1 || row > len(table.rowHeights) {
return 0, errors.New("range check error")
}
return table.rowHeights[row-1], nil
}
// SetRowHeight sets the height for a specified row.
func (table *Table) SetRowHeight(row int, h float64) error {
if row < 1 || row > len(table.rowHeights) {
return errors.New("range check error")
}
table.rowHeights[row-1] = h
return nil
}
// Rows returns the total number of rows the table has.
func (table *Table) Rows() int {
return table.rows
}
// Cols returns the total number of columns the table has.
func (table *Table) Cols() int {
return table.cols
}
// CurRow returns the currently active cell's row number.
func (table *Table) CurRow() int {
curRow := (table.curCell-1)/table.cols + 1
return curRow
}
// CurCol returns the currently active cell's column number.
func (table *Table) CurCol() int {
curCol := (table.curCell-1)%(table.cols) + 1
return curCol
}
// SetPos sets the Table's positioning to absolute mode and specifies the upper-left corner
// coordinates as (x,y).
// Note that this is only sensible to use when the table does not wrap over multiple pages.
// TODO: Should be able to set width too (not just based on context/relative positioning mode).
func (table *Table) SetPos(x, y float64) {
table.positioning = positionAbsolute
table.xPos = x
table.yPos = y
}
// SetHeaderRows turns the selected table rows into headers that are repeated
// for every page the table spans. startRow and endRow are inclusive.
func (table *Table) SetHeaderRows(startRow, endRow int) error {
if startRow <= 0 {
return errors.New("header start row must be greater than 0")
}
if endRow <= 0 {
return errors.New("header end row must be greater than 0")
}
if startRow > endRow {
return errors.New("header start row must be less than or equal to the end row")
}
table.hasHeader = true
table.headerStartRow = startRow
table.headerEndRow = endRow
return nil
}
// AddSubtable copies the cells of the subtable in the table, starting with the
// specified position. The table row and column indices are 1-based, which
// makes the position of the first cell of the first row of the table 1,1.
// The table is automatically extended if the subtable exceeds its columns.
// This can happen when the subtable has more columns than the table or when
// one or more columns of the subtable starting from the specified position
// exceed the last column of the table.
func (table *Table) AddSubtable(row, col int, subtable *Table) {
for _, cell := range subtable.cells {
c := &TableCell{}
*c = *cell
c.table = table
// Adjust added cell column. Add extra columns to the table to
// accomodate the new cell, if needed.
c.col += col - 1
if colsLeft := table.cols - (c.col - 1); colsLeft < c.colspan {
table.cols += c.colspan - colsLeft
table.resetColumnWidths()
common.Log.Debug("Table: subtable exceeds destination table. Expanding table to %d columns.", table.cols)
}
// Extend number of rows, if needed.
c.row += row - 1
subRowHeight := subtable.rowHeights[cell.row-1]
if c.row > table.rows {
for c.row > table.rows {
table.rows++
table.rowHeights = append(table.rowHeights, table.defaultRowHeight)
}
table.rowHeights[c.row-1] = subRowHeight
} else {
table.rowHeights[c.row-1] = math.Max(table.rowHeights[c.row-1], subRowHeight)
}
table.cells = append(table.cells, c)
}
// Sort cells by row, column.
sort.Slice(table.cells, func(i, j int) bool {
rowA := table.cells[i].row
rowB := table.cells[j].row
if rowA < rowB {
return true
}
if rowA > rowB {
return false
}
return table.cells[i].col < table.cells[j].col
})
}
// GeneratePageBlocks generate the page blocks. Multiple blocks are generated if the contents wrap
// over multiple pages.
// Implements the Drawable interface.
func (table *Table) GeneratePageBlocks(ctx DrawContext) ([]*Block, DrawContext, error) {
var blocks []*Block
block := NewBlock(ctx.PageWidth, ctx.PageHeight)
origCtx := ctx
if table.positioning.isAbsolute() {
ctx.X = table.xPos
ctx.Y = table.yPos
} else {
// Relative mode: add margins.
ctx.X += table.margins.left
ctx.Y += table.margins.top
ctx.Width -= table.margins.left + table.margins.right
ctx.Height -= table.margins.bottom + table.margins.top
}
tableWidth := ctx.Width
// Store table's upper left corner.
ulX := ctx.X
ulY := ctx.Y
ctx.Height = ctx.PageHeight - ctx.Y - ctx.Margins.bottom
origHeight := ctx.Height
// Start row keeps track of starting row (wraps to 0 on new page).
startrow := 0
// Indices of the first and the last header cells.
startHeaderCell := -1
endHeaderCell := -1
// Prepare for drawing: Calculate cell dimensions, row, cell heights.
for cellIdx, cell := range table.cells {
// Get total width fraction
wf := float64(0.0)
for i := 0; i < cell.colspan; i++ {
wf += table.colWidths[cell.col+i-1]
}
// Get x pos relative to table upper left corner.
xrel := float64(0.0)
for i := 0; i < cell.col-1; i++ {
xrel += table.colWidths[i] * tableWidth
}
// Get y pos relative to table upper left corner.
yrel := float64(0.0)
for i := startrow; i < cell.row-1; i++ {
yrel += table.rowHeights[i]
}
// Calculate the width out of available width.
w := wf * tableWidth
// Get total height.
h := float64(0.0)
for i := 0; i < cell.rowspan; i++ {
h += table.rowHeights[cell.row+i-1]
}
// Calculate header cell range.
if table.hasHeader {
if cell.row >= table.headerStartRow && cell.row <= table.headerEndRow {
if startHeaderCell < 0 {
startHeaderCell = cellIdx
}
endHeaderCell = cellIdx
}
}
// For text: Calculate width, height, wrapping within available space if specified.
switch t := cell.content.(type) {
case *Paragraph:
p := t
if p.enableWrap {
p.SetWidth(w - cell.indent)
}
newh := p.Height() + p.margins.bottom + p.margins.bottom
newh += 0.5 * p.fontSize * p.lineHeight // TODO: Make the top margin configurable?
if newh > h {
diffh := newh - h
// Add diff to last row.
table.rowHeights[cell.row+cell.rowspan-2] += diffh
}
case *StyledParagraph:
sp := t
if sp.enableWrap {
sp.SetWidth(w - cell.indent)
}
newh := sp.Height() + sp.margins.top + sp.margins.bottom
newh += 0.5 * sp.getTextHeight() // TODO: Make the top margin configurable?
if newh > h {
diffh := newh - h
// Add diff to last row.
table.rowHeights[cell.row+cell.rowspan-2] += diffh
}
case *htmlBlock:
t.SetWidth(w - cell.indent)
newh := t.Height()
if newh > h {
diffh := newh - h
// Add diff to last row.
table.rowHeights[cell.row+cell.rowspan-2] += diffh
}
case *HTMLContent:
t.SetWidth(w - cell.indent)
newh := t.Height()
if newh > h {
diffh := newh - h
// Add diff to last row.
table.rowHeights[cell.row+cell.rowspan-2] += diffh
}
case *Image:
img := t
newh := img.Height() + img.margins.top + img.margins.bottom
if newh > h {
diffh := newh - h
// Add diff to last row.
table.rowHeights[cell.row+cell.rowspan-2] += diffh
}
case *Table:
tbl := t
newh := tbl.Height() + tbl.margins.top + tbl.margins.bottom
if newh > h {
diffh := newh - h
// Add diff to last row.
table.rowHeights[cell.row+cell.rowspan-2] += diffh
}
case *List:
lst := t
newh := lst.tableHeight(w-cell.indent) + lst.margins.top + lst.margins.bottom
if newh > h {
diffh := newh - h
// Add diff to last row.
table.rowHeights[cell.row+cell.rowspan-2] += diffh
}
case *Division:
div := t
ctx := DrawContext{
X: xrel,
Y: yrel,
Width: w,
}
// Mock call to generate page blocks.
divBlocks, updCtx, err := div.GeneratePageBlocks(ctx)
if err != nil {
return nil, ctx, err
}
if len(divBlocks) > 1 {
// Wraps across page, make cell reach all the way to bottom of current page.
newh := ctx.Height - h
if newh > h {
diffh := newh - h
// Add diff to last row.
table.rowHeights[cell.row+cell.rowspan-2] += diffh
}
}
newh := div.Height() + div.margins.top + div.margins.bottom
_ = updCtx
// Get available width and height.
if newh > h {
diffh := newh - h
// Add diff to last row.
table.rowHeights[cell.row+cell.rowspan-2] += diffh
}
}
}
// Draw cells.
// row height, cell height
var drawingHeaders bool
var resumeIdx, resumeStartRow int
for cellIdx := 0; cellIdx < len(table.cells); cellIdx++ {
cell := table.cells[cellIdx]
// Get total width fraction
wf := float64(0.0)
for i := 0; i < cell.colspan; i++ {
wf += table.colWidths[cell.col+i-1]
}
// Get x pos relative to table upper left corner.
xrel := float64(0.0)
for i := 0; i < cell.col-1; i++ {
xrel += table.colWidths[i] * tableWidth
}
// Get y pos relative to table upper left corner.
yrel := float64(0.0)
for i := startrow; i < cell.row-1; i++ {
yrel += table.rowHeights[i]
}
// Calculate the width out of available width.
w := wf * tableWidth
// Get total height.
h := float64(0.0)
for i := 0; i < cell.rowspan; i++ {
h += table.rowHeights[cell.row+i-1]
}
ctx.Height = origHeight - yrel
if h > ctx.Height {
// Go to next page.
blocks = append(blocks, block)
block = NewBlock(ctx.PageWidth, ctx.PageHeight)
ulX = ctx.Margins.left
ulY = ctx.Margins.top
ctx.Height = ctx.PageHeight - ctx.Margins.top - ctx.Margins.bottom
ctx.Page++
origHeight = ctx.Height
startrow = cell.row - 1
yrel = 0
// Save state and jump back to the first header cell.
if table.hasHeader && startHeaderCell >= 0 {
resumeIdx = cellIdx
cellIdx = startHeaderCell - 1
resumeStartRow = startrow
startrow = table.headerStartRow - 1
drawingHeaders = true
continue
}
}
// Height should be how much space there is left of the page.
ctx.Width = w
ctx.X = ulX + xrel
ctx.Y = ulY + yrel
// Creating border
border := newBorder(ctx.X, ctx.Y, w, h)
if cell.backgroundColor != nil {
r := cell.backgroundColor.R()
g := cell.backgroundColor.G()
b := cell.backgroundColor.B()
border.SetFillColor(ColorRGBFromArithmetic(r, g, b))
}
border.LineStyle = cell.borderLineStyle
border.styleLeft = cell.borderStyleLeft
border.styleRight = cell.borderStyleRight
border.styleTop = cell.borderStyleTop
border.styleBottom = cell.borderStyleBottom
if cell.borderColorLeft != nil {
border.SetColorLeft(ColorRGBFromArithmetic(cell.borderColorLeft.R(), cell.borderColorLeft.G(), cell.borderColorLeft.B()))
}
if cell.borderColorBottom != nil {
border.SetColorBottom(ColorRGBFromArithmetic(cell.borderColorBottom.R(), cell.borderColorBottom.G(), cell.borderColorBottom.B()))
}
if cell.borderColorRight != nil {
border.SetColorRight(ColorRGBFromArithmetic(cell.borderColorRight.R(), cell.borderColorRight.G(), cell.borderColorRight.B()))
}
if cell.borderColorTop != nil {
border.SetColorTop(ColorRGBFromArithmetic(cell.borderColorTop.R(), cell.borderColorTop.G(), cell.borderColorTop.B()))
}
border.SetWidthBottom(cell.borderWidthBottom)
border.SetWidthLeft(cell.borderWidthLeft)
border.SetWidthRight(cell.borderWidthRight)
border.SetWidthTop(cell.borderWidthTop)
err := block.Draw(border)
if err != nil {
common.Log.Debug("ERROR: %v", err)
}
if cell.content != nil {
cw := cell.content.Width() // content width.
ch := cell.content.Height() // content height.
vertOffset := 0.0
switch t := cell.content.(type) {
case *Paragraph:
if t.enableWrap {
cw = t.getMaxLineWidth() / 1000.0
}
case *StyledParagraph:
if t.enableWrap {
cw = t.getMaxLineWidth() / 1000.0
}
// Calculate the height of the paragraph.
lineCapHeight, lineHeight := t.getLineHeight(0)
if len(t.lines) == 1 {
ch = lineCapHeight
} else {
ch = ch - lineHeight + lineCapHeight
}
// Account for the top offset the paragraph adds.
vertOffset = lineCapHeight - lineHeight
switch cell.verticalAlignment {
case CellVerticalAlignmentTop:
// Add a bit of space from the top border of the cell.
vertOffset += lineCapHeight * 0.5
case CellVerticalAlignmentBottom:
// Add a bit of space from the bottom border of the cell.
vertOffset -= lineCapHeight * 0.5
}
case *HTMLContent:
cw = w
case *Table:
cw = w
case *List:
cw = w
}
// Account for horizontal alignment:
switch cell.horizontalAlignment {
case CellHorizontalAlignmentLeft:
// Account for indent.
ctx.X += cell.indent
ctx.Width -= cell.indent
case CellHorizontalAlignmentCenter:
// Difference between available space and content space.
dw := w - cw
if dw > 0 {
ctx.X += dw / 2
ctx.Width -= dw / 2
}
case CellHorizontalAlignmentRight:
if w > cw {
ctx.X = ctx.X + w - cw - cell.indent
ctx.Width = cw
}
}
ctx.Y += vertOffset
// Account for vertical alignment.
switch cell.verticalAlignment {
case CellVerticalAlignmentTop:
// Default: do nothing.
case CellVerticalAlignmentMiddle:
dh := h - ch
if dh > 0 {
ctx.Y += dh / 2
ctx.Height -= dh / 2
}
case CellVerticalAlignmentBottom:
if h > ch {
ctx.Y = ctx.Y + h - ch
ctx.Height = h
}
}
err := block.DrawWithContext(cell.content, ctx)
if err != nil {
common.Log.Debug("ERROR: %v", err)
}
ctx.Y -= vertOffset
}
ctx.Y += h
// Resume previous state after headers have been rendered.
if drawingHeaders && cellIdx+1 > endHeaderCell {
// Account for the height of the rendered headers.
ulY += yrel + h
origHeight -= h + yrel
startrow = resumeStartRow
cellIdx = resumeIdx - 1
drawingHeaders = false
}
}
blocks = append(blocks, block)
if table.positioning.isAbsolute() {
return blocks, origCtx, nil
}
// Relative mode.
// Move back X after.
ctx.X = origCtx.X
// Return original width.
ctx.Width = origCtx.Width
// Add the bottom margin.
ctx.Y += table.margins.bottom
return blocks, ctx, nil
}
// CellBorderStyle defines the table cell's border style.
type CellBorderStyle int
// Currently supported table styles are: None (no border) and boxed (line along each side).
const (
CellBorderStyleNone CellBorderStyle = iota // no border
// Borders along all sides (boxed).
CellBorderStyleSingle
CellBorderStyleDouble
)
// CellBorderSide defines the table cell's border side.
type CellBorderSide int
const (
// CellBorderSideLeft adds border on the left side of the table.
CellBorderSideLeft CellBorderSide = iota
// CellBorderSideRight adds a border on the right side of the table.
CellBorderSideRight
// CellBorderSideTop adds a border on the top side of the table.
CellBorderSideTop
// CellBorderSideBottom adds a border on the bottom side of the table.
CellBorderSideBottom
// CellBorderSideAll adds borders on all sides of the table.
CellBorderSideAll
)
// CellHorizontalAlignment defines the table cell's horizontal alignment.
type CellHorizontalAlignment int
// Table cells have three horizontal alignment modes: left, center and right.
const (
// CellHorizontalAlignmentLeft aligns cell content on the left (with specified indent); unused space on the right.
CellHorizontalAlignmentLeft CellHorizontalAlignment = iota
// CellHorizontalAlignmentCenter aligns cell content in the middle (unused space divided equally on the left/right).
CellHorizontalAlignmentCenter
// CellHorizontalAlignmentRight aligns the cell content on the right; unsued space on the left.
CellHorizontalAlignmentRight
)
// CellVerticalAlignment defines the table cell's vertical alignment.
type CellVerticalAlignment int
// Table cells have three vertical alignment modes: top, middle and bottom.
const (
// CellVerticalAlignmentTop aligns cell content vertically to the top; unused space below.
CellVerticalAlignmentTop CellVerticalAlignment = iota
// CellVerticalAlignmentMiddle aligns cell content in the middle; unused space divided equally above and below.
CellVerticalAlignmentMiddle
// CellVerticalAlignmentBottom aligns cell content on the bottom; unused space above.
CellVerticalAlignmentBottom
)
// TableCell defines a table cell which can contain a Drawable as content.
type TableCell struct {
// Background
backgroundColor *model.PdfColorDeviceRGB
borderLineStyle draw.LineStyle
// border
borderStyleLeft CellBorderStyle
borderColorLeft *model.PdfColorDeviceRGB
borderWidthLeft float64
borderStyleBottom CellBorderStyle
borderColorBottom *model.PdfColorDeviceRGB
borderWidthBottom float64
borderStyleRight CellBorderStyle
borderColorRight *model.PdfColorDeviceRGB
borderWidthRight float64
borderStyleTop CellBorderStyle
borderColorTop *model.PdfColorDeviceRGB
borderWidthTop float64
// The row and column which the cell starts from.
row, col int
// Row, column span.
rowspan int
colspan int
// Each cell can contain 1 drawable.
content VectorDrawable
// Alignment
horizontalAlignment CellHorizontalAlignment
verticalAlignment CellVerticalAlignment
// Left indent.
indent float64
// Table reference
table *Table
}
// NewCell makes a new cell and inserts it into the table at the current position.
func (table *Table) NewCell() *TableCell {
return table.newCell(1)
}
// MultiColCell makes a new cell with the specified column span and inserts it
// into the table at the current position.
func (table *Table) MultiColCell(colspan int) *TableCell {
return table.newCell(colspan)
}
func (table *Table) newCell(colspan int) *TableCell {
table.curCell++
curRow := (table.curCell-1)/table.cols + 1
for curRow > table.rows {
table.rows++
table.rowHeights = append(table.rowHeights, table.defaultRowHeight)
}
curCol := (table.curCell-1)%(table.cols) + 1
cell := &TableCell{}
cell.row = curRow
cell.col = curCol
cell.rowspan = 1
// Default left indent
cell.indent = 5
cell.borderStyleLeft = CellBorderStyleNone
cell.borderLineStyle = draw.LineStyleSolid
// Alignment defaults.
cell.horizontalAlignment = CellHorizontalAlignmentLeft
cell.verticalAlignment = CellVerticalAlignmentTop
cell.borderWidthLeft = 0
cell.borderWidthBottom = 0
cell.borderWidthRight = 0
cell.borderWidthTop = 0
col := ColorBlack
cell.borderColorLeft = model.NewPdfColorDeviceRGB(col.ToRGB())
cell.borderColorBottom = model.NewPdfColorDeviceRGB(col.ToRGB())
cell.borderColorRight = model.NewPdfColorDeviceRGB(col.ToRGB())
cell.borderColorTop = model.NewPdfColorDeviceRGB(col.ToRGB())
// Set column span.
if colspan < 1 {
common.Log.Debug("Table: cell colspan less than 1 (%d). Setting cell colspan to 1.", colspan)
colspan = 1
}
remainingCols := table.cols - (cell.col - 1)
if colspan > remainingCols {
common.Log.Debug("Table: cell colspan (%d) exceeds remaining row cols (%d). Adjusting colspan.", colspan, remainingCols)
colspan = remainingCols
}
cell.colspan = colspan
table.curCell += colspan - 1
table.cells = append(table.cells, cell)
// Keep reference to the table.
cell.table = table
return cell
}
// SkipCells skips over a specified number of cells in the table.
func (table *Table) SkipCells(num int) {
if num < 0 {
common.Log.Debug("Table: cannot skip back to previous cells")
return
}
table.curCell += num
}
// SkipRows skips over a specified number of rows in the table.
func (table *Table) SkipRows(num int) {
ncells := num*table.cols - 1
if ncells < 0 {
common.Log.Debug("Table: cannot skip back to previous cells")
return
}
table.curCell += ncells
}
// SkipOver skips over a specified number of rows and cols.
func (table *Table) SkipOver(rows, cols int) {
ncells := rows*table.cols + cols - 1
if ncells < 0 {
common.Log.Debug("Table: cannot skip back to previous cells")
return
}
table.curCell += ncells
}
// SetIndent sets the cell's left indent.
func (cell *TableCell) SetIndent(indent float64) {
cell.indent = indent
}
// SetHorizontalAlignment sets the cell's horizontal alignment of content.
// Can be one of:
// - CellHorizontalAlignmentLeft
// - CellHorizontalAlignmentCenter
// - CellHorizontalAlignmentRight
func (cell *TableCell) SetHorizontalAlignment(halign CellHorizontalAlignment) {
cell.horizontalAlignment = halign
}
// SetVerticalAlignment set the cell's vertical alignment of content.
// Can be one of:
// - CellHorizontalAlignmentTop
// - CellHorizontalAlignmentMiddle
// - CellHorizontalAlignmentBottom
func (cell *TableCell) SetVerticalAlignment(valign CellVerticalAlignment) {
cell.verticalAlignment = valign
}
// SetBorder sets the cell's border style.
func (cell *TableCell) SetBorder(side CellBorderSide, style CellBorderStyle, width float64) {
if style == CellBorderStyleSingle && side == CellBorderSideAll {
cell.borderStyleLeft = CellBorderStyleSingle
cell.borderWidthLeft = width
cell.borderStyleBottom = CellBorderStyleSingle
cell.borderWidthBottom = width
cell.borderStyleRight = CellBorderStyleSingle
cell.borderWidthRight = width
cell.borderStyleTop = CellBorderStyleSingle
cell.borderWidthTop = width
} else if style == CellBorderStyleDouble && side == CellBorderSideAll {
cell.borderStyleLeft = CellBorderStyleDouble
cell.borderWidthLeft = width
cell.borderStyleBottom = CellBorderStyleDouble
cell.borderWidthBottom = width
cell.borderStyleRight = CellBorderStyleDouble
cell.borderWidthRight = width
cell.borderStyleTop = CellBorderStyleDouble
cell.borderWidthTop = width
} else if (style == CellBorderStyleSingle || style == CellBorderStyleDouble) && side == CellBorderSideLeft {
cell.borderStyleLeft = style
cell.borderWidthLeft = width
} else if (style == CellBorderStyleSingle || style == CellBorderStyleDouble) && side == CellBorderSideBottom {
cell.borderStyleBottom = style
cell.borderWidthBottom = width
} else if (style == CellBorderStyleSingle || style == CellBorderStyleDouble) && side == CellBorderSideRight {
cell.borderStyleRight = style
cell.borderWidthRight = width
} else if (style == CellBorderStyleSingle || style == CellBorderStyleDouble) && side == CellBorderSideTop {
cell.borderStyleTop = style
cell.borderWidthTop = width
}
}
// SetBorderColor sets the cell's border color.
func (cell *TableCell) SetBorderColor(col Color) {
cell.borderColorLeft = model.NewPdfColorDeviceRGB(col.ToRGB())
cell.borderColorBottom = model.NewPdfColorDeviceRGB(col.ToRGB())
cell.borderColorRight = model.NewPdfColorDeviceRGB(col.ToRGB())
cell.borderColorTop = model.NewPdfColorDeviceRGB(col.ToRGB())
}
// SetBorderLineStyle sets border style (currently dashed or plain).
func (cell *TableCell) SetBorderLineStyle(style draw.LineStyle) {
cell.borderLineStyle = style
}
// SetBackgroundColor sets the cell's background color.
func (cell *TableCell) SetBackgroundColor(col Color) {
cell.backgroundColor = model.NewPdfColorDeviceRGB(col.ToRGB())
}
// Width returns the cell's width based on the input draw context.
func (cell *TableCell) Width(ctx DrawContext) float64 {
fraction := float64(0.0)
for j := 0; j < cell.colspan; j++ {
fraction += cell.table.colWidths[cell.col+j-1]
}
w := ctx.Width * fraction
return w
}
// SetContent sets the cell's content. The content is a VectorDrawable, i.e. a Drawable with a known height and width.
// The currently supported VectorDrawable is: *Paragraph, *StyledParagraph.
func (cell *TableCell) SetContent(vd VectorDrawable) error {
switch t := vd.(type) {
case *Paragraph:
if t.defaultWrap {
// Enable wrapping by default.
t.enableWrap = true
}
cell.content = vd
case *StyledParagraph:
if t.defaultWrap {
// Enable wrapping by default.
t.enableWrap = true
}
cell.content = vd
case *Image:
cell.content = vd
case *Table:
cell.content = vd
case *List:
cell.content = vd
case *Division:
cell.content = vd
case *htmlBlock:
cell.content = vd
case *HTMLContent:
cell.content = vd
default:
common.Log.Debug("ERROR: unsupported cell content type %T", vd)
return core.ErrTypeError
}
return nil
}
Go
1
https://gitee.com/coodder/unipdf.git
git@gitee.com:coodder/unipdf.git
coodder
unipdf
unipdf
v1.2.0

搜索帮助