mirror of
https://github.com/unidoc/unipdf.git
synced 2025-05-02 22:17:06 +08:00
Merge pull request #167 from unidoc/creator-tablecell-wrapping-fix
Support wrapped text in table cells
This commit is contained in:
commit
86b17c41d9
@ -947,6 +947,96 @@ func TestTable(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
func TestTableCellWrapping(t *testing.T) {
|
||||
c := New()
|
||||
c.NewPage()
|
||||
|
||||
table := NewTable(4) // Mx4 table
|
||||
// Default, equal column sizes (4x0.25)...
|
||||
table.SetColumnWidths(0.5, 0.2, 0.2, 0.1)
|
||||
|
||||
cell := table.NewCell()
|
||||
p := NewParagraph("A Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.")
|
||||
cell.SetContent(p)
|
||||
cell.SetBorder(CellBorderStyleBox, 1)
|
||||
p.SetEnableWrap(true)
|
||||
p.SetWidth(cell.Width(c.Context()))
|
||||
p.SetTextAlignment(TextAlignmentJustify)
|
||||
|
||||
cell = table.NewCell()
|
||||
cell.SetBorder(CellBorderStyleBox, 1)
|
||||
p = NewParagraph("B Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur.")
|
||||
p.SetEnableWrap(true)
|
||||
p.SetTextAlignment(TextAlignmentRight)
|
||||
cell.SetContent(p)
|
||||
|
||||
cell = table.NewCell()
|
||||
p = NewParagraph("C Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.")
|
||||
p.SetEnableWrap(true)
|
||||
cell.SetContent(p)
|
||||
cell.SetBorder(CellBorderStyleBox, 1)
|
||||
|
||||
cell = table.NewCell()
|
||||
p = NewParagraph("1,4")
|
||||
cell.SetContent(p)
|
||||
cell.SetBorder(CellBorderStyleBox, 1)
|
||||
|
||||
cell = table.NewCell()
|
||||
p = NewParagraph("2,1")
|
||||
cell.SetContent(p)
|
||||
cell.SetBorder(CellBorderStyleBox, 1)
|
||||
|
||||
cell = table.NewCell()
|
||||
p = NewParagraph("2,2")
|
||||
cell.SetContent(p)
|
||||
cell.SetBorder(CellBorderStyleBox, 1)
|
||||
|
||||
cell = table.NewCell()
|
||||
p = NewParagraph("2,2")
|
||||
cell.SetContent(p)
|
||||
cell.SetBorder(CellBorderStyleBox, 1)
|
||||
|
||||
//table.SkipCells(1) // Skip over 2,3.
|
||||
|
||||
cell = table.NewCell()
|
||||
cell.SetBorder(CellBorderStyleBox, 1)
|
||||
//p = NewParagraph("D Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.")
|
||||
p = NewParagraph("X")
|
||||
p.SetEnableWrap(true)
|
||||
cell.SetContent(p)
|
||||
|
||||
// Skip over two rows.
|
||||
table.SkipRows(2)
|
||||
cell = table.NewCell()
|
||||
cell.SetBorder(CellBorderStyleBox, 1)
|
||||
p = NewParagraph("4,4")
|
||||
cell.SetContent(p)
|
||||
|
||||
// Move down 3 rows, 2 to the left.
|
||||
table.SkipOver(3, -2)
|
||||
cell = table.NewCell()
|
||||
p = NewParagraph("7,2")
|
||||
cell.SetContent(p)
|
||||
cell.SetBackgroundColor(ColorRGBFrom8bit(255, 0, 0))
|
||||
|
||||
table.SkipRows(1)
|
||||
cell = table.NewCell()
|
||||
cell.SetBorder(CellBorderStyleBox, 1)
|
||||
p = NewParagraph("This is\nnewline\nwrapped\n\nmulti")
|
||||
p.SetEnableWrap(true)
|
||||
cell.SetContent(p)
|
||||
|
||||
err := c.Draw(table)
|
||||
if err != nil {
|
||||
t.Fatalf("Error drawing: %v", err)
|
||||
}
|
||||
|
||||
err = c.WriteToFile("/tmp/tablecell_wrap.pdf")
|
||||
if err != nil {
|
||||
t.Fatalf("Fail: %v\n", err)
|
||||
}
|
||||
}
|
||||
|
||||
// Test creating and drawing a table.
|
||||
func TestBorderedTable(t *testing.T) {
|
||||
table := NewTable(4) // Mx4 table
|
||||
|
@ -45,6 +45,11 @@ type Paragraph struct {
|
||||
enableWrap bool
|
||||
wrapWidth float64
|
||||
|
||||
// defaultWrap defines whether wrapping has been defined explictly or whether default behavior should
|
||||
// be observed. Default behavior depends on context: normally wrap is expected, except for example in
|
||||
// table cells wrapping is off by default.
|
||||
defaultWrap bool
|
||||
|
||||
// Rotation angle (degrees).
|
||||
angle float64
|
||||
|
||||
@ -77,6 +82,7 @@ func NewParagraph(text string) *Paragraph {
|
||||
|
||||
// TODO: Can we wrap intellectually, only if given width is known?
|
||||
p.enableWrap = true
|
||||
p.defaultWrap = true
|
||||
p.SetColor(ColorRGBFrom8bit(0, 0, 0))
|
||||
p.alignment = TextAlignmentLeft
|
||||
p.angle = 0
|
||||
@ -130,6 +136,7 @@ func (p *Paragraph) Text() string {
|
||||
// SetEnableWrap sets the line wrapping enabled flag.
|
||||
func (p *Paragraph) SetEnableWrap(enableWrap bool) {
|
||||
p.enableWrap = enableWrap
|
||||
p.defaultWrap = false
|
||||
}
|
||||
|
||||
// SetColor set the color of the Paragraph text.
|
||||
@ -213,6 +220,11 @@ func (p *Paragraph) getTextWidth() float64 {
|
||||
return -1 // XXX/FIXME: return error.
|
||||
}
|
||||
|
||||
// Ignore newline for this.. Handles as if all in one line.
|
||||
if glyph == "controlLF" {
|
||||
continue
|
||||
}
|
||||
|
||||
metrics, found := p.textFont.GetGlyphCharMetrics(glyph)
|
||||
if !found {
|
||||
common.Log.Debug("Glyph char metrics not found! %s\n", glyph)
|
||||
@ -247,6 +259,17 @@ func (p *Paragraph) wrapText() error {
|
||||
return errors.New("Glyph not found for rune") // XXX/FIXME: return error.
|
||||
}
|
||||
|
||||
// Newline wrapping.
|
||||
if glyph == "controlLF" {
|
||||
// Moves to next line.
|
||||
p.textLines = append(p.textLines, string(line))
|
||||
line = []rune{}
|
||||
lineWidth = 0
|
||||
widths = []float64{}
|
||||
glyphs = []string{}
|
||||
continue
|
||||
}
|
||||
|
||||
metrics, found := p.textFont.GetGlyphCharMetrics(glyph)
|
||||
if !found {
|
||||
common.Log.Debug("Glyph char metrics not found! %s\n", glyph)
|
||||
@ -421,6 +444,9 @@ func drawParagraphOnBlock(blk *Block, p *Paragraph, ctx DrawContext) (DrawContex
|
||||
spaces++
|
||||
continue
|
||||
}
|
||||
if glyph == "controlLF" {
|
||||
continue
|
||||
}
|
||||
metrics, found := p.textFont.GetGlyphCharMetrics(glyph)
|
||||
if !found {
|
||||
common.Log.Debug("Unsupported glyph %s in font\n", glyph)
|
||||
|
@ -168,6 +168,50 @@ func (table *Table) GeneratePageBlocks(ctx DrawContext) ([]*Block, DrawContext,
|
||||
// Start row keeps track of starting row (wraps to 0 on new page).
|
||||
startrow := 0
|
||||
|
||||
// Prepare for drawing: Calculate cell dimensions, row, cell heights.
|
||||
for _, 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]
|
||||
}
|
||||
|
||||
// For text: Calculate width, height, wrapping within available space if specified.
|
||||
if p, isp := cell.content.(*Paragraph); isp {
|
||||
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
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Draw cells.
|
||||
// row height, cell height
|
||||
for _, cell := range table.cells {
|
||||
// Get total width fraction
|
||||
@ -505,14 +549,11 @@ func (cell *TableCell) Width(ctx DrawContext) float64 {
|
||||
func (cell *TableCell) SetContent(vd VectorDrawable) error {
|
||||
switch t := vd.(type) {
|
||||
case *Paragraph:
|
||||
// Default paragraph settings in table:
|
||||
t.SetEnableWrap(false) // No wrapping.
|
||||
h := cell.table.rowHeights[cell.row-1]
|
||||
nh := t.Height() * 1.5 // Default multiplier 1.5.
|
||||
// Increase height if needed.
|
||||
if nh > h {
|
||||
cell.table.SetRowHeight(cell.row, nh)
|
||||
if t.defaultWrap {
|
||||
// Default paragraph settings in table: no wrapping.
|
||||
t.enableWrap = false // No wrapping.
|
||||
}
|
||||
|
||||
cell.content = vd
|
||||
default:
|
||||
common.Log.Debug("Error: unsupported cell content type %T\n", vd)
|
||||
|
Loading…
x
Reference in New Issue
Block a user