mirror of
https://github.com/unidoc/unipdf.git
synced 2025-05-01 22:17:29 +08:00
Merge branch 'master' into v3
This commit is contained in:
commit
cc44dd2552
@ -2,12 +2,11 @@ package core
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/unidoc/unidoc/common"
|
||||
)
|
||||
|
||||
func init() {
|
||||
common.SetLogger(common.NewConsoleLogger(common.LogLevelTrace))
|
||||
// Uncomment when debugging to get trace logging output - to follow flow.
|
||||
// common.SetLogger(common.NewConsoleLogger(common.LogLevelTrace))
|
||||
}
|
||||
|
||||
// Fuzz tests based on findings with go-fuzz.
|
||||
@ -74,7 +73,7 @@ endstream
|
||||
|
||||
// Slightly more complex case where the reference number is incorrect, but still points to the same object.
|
||||
func TestFuzzSelfReference2(t *testing.T) {
|
||||
common.SetLogger(common.NewConsoleLogger(common.LogLevelTrace))
|
||||
// common.SetLogger(common.NewConsoleLogger(common.LogLevelTrace))
|
||||
|
||||
rawText := `13 0 obj
|
||||
<< /Length 12 0 R >>
|
||||
|
@ -1043,6 +1043,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
|
||||
|
@ -42,6 +42,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
|
||||
|
||||
@ -74,6 +79,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
|
||||
@ -124,6 +130,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.
|
||||
@ -207,6 +214,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)
|
||||
@ -241,6 +253,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 (%s)\n", glyph, string(val))
|
||||
@ -417,6 +440,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)
|
||||
|
@ -12,8 +12,9 @@ import (
|
||||
)
|
||||
|
||||
func init() {
|
||||
// Uncomment when debugging to get debug or trace logging output.
|
||||
//common.SetLogger(common.NewConsoleLogger(common.LogLevelDebug))
|
||||
common.SetLogger(common.NewConsoleLogger(common.LogLevelTrace))
|
||||
//common.SetLogger(common.NewConsoleLogger(common.LogLevelTrace))
|
||||
}
|
||||
|
||||
// cmap1Data represents a basic CMap.
|
||||
@ -58,8 +59,6 @@ const cmap1Data = `
|
||||
|
||||
// TestCMapParser tests basic loading of a simple CMap.
|
||||
func TestCMapParser1(t *testing.T) {
|
||||
common.SetLogger(common.NewConsoleLogger(common.LogLevelTrace))
|
||||
|
||||
cmap, err := LoadCmapFromDataCID([]byte(cmap1Data))
|
||||
if err != nil {
|
||||
t.Error("Failed: ", err)
|
||||
@ -152,7 +151,7 @@ const cmap2Data = `
|
||||
// TestCMapParser2 tests a bug that came up when 2-byte character codes had the higher byte set to 0,
|
||||
// e.g. 0x0080, and the character map was not taking the number of bytes of the input codemap into account.
|
||||
func TestCMapParser2(t *testing.T) {
|
||||
common.SetLogger(common.NewConsoleLogger(common.LogLevelTrace))
|
||||
//common.SetLogger(common.NewConsoleLogger(common.LogLevelTrace))
|
||||
|
||||
cmap, err := LoadCmapFromDataCID([]byte(cmap2Data))
|
||||
if err != nil {
|
||||
@ -245,7 +244,7 @@ const cmapData3 = `
|
||||
|
||||
// TestCMapParser3 test case of a CMap with mixed number of 1 and 2 bytes in the codespace range.
|
||||
func TestCMapParser3(t *testing.T) {
|
||||
common.SetLogger(common.NewConsoleLogger(common.LogLevelTrace))
|
||||
//common.SetLogger(common.NewConsoleLogger(common.LogLevelTrace))
|
||||
|
||||
cmap, err := LoadCmapFromDataCID([]byte(cmapData3))
|
||||
if err != nil {
|
||||
|
@ -8,12 +8,12 @@ package model
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/unidoc/unidoc/common"
|
||||
"github.com/unidoc/unidoc/pdf/core"
|
||||
)
|
||||
|
||||
func init() {
|
||||
common.SetLogger(common.NewConsoleLogger(common.LogLevelTrace))
|
||||
// Uncomment when debugging to get trace logging output - to follow flow.
|
||||
// common.SetLogger(common.NewConsoleLogger(common.LogLevelTrace))
|
||||
}
|
||||
|
||||
// Test for an endless recursive loop in
|
||||
|
Loading…
x
Reference in New Issue
Block a user