Add support for vertical alignment of styled paragraphs inside table cells (#69)

This commit is contained in:
Adrian-George Bostan 2019-05-31 01:01:56 +03:00 committed by Gunnsteinn Hall
parent 8425bf7c8f
commit 3bae2f6035
3 changed files with 135 additions and 4 deletions

View File

@ -226,6 +226,49 @@ func (p *StyledParagraph) Height() float64 {
return height return height
} }
// getLineHeight returns both the capheight and the font size based height of
// the line with the specified index.
func (p *StyledParagraph) getLineHeight(idx int) (capHeight, height float64) {
if p.lines == nil || len(p.lines) == 0 {
p.wrapText()
}
if idx < 0 || idx > len(p.lines)-1 {
common.Log.Debug("ERROR: invalid paragraph line index %d. Returning 0, 0", idx)
return 0, 0
}
line := p.lines[idx]
for _, chunk := range line {
descriptor, err := chunk.Style.Font.GetFontDescriptor()
if err != nil {
common.Log.Debug("ERROR: Unable to get font descriptor")
}
var fontCapHeight float64
if descriptor != nil {
if fontCapHeight, err = descriptor.GetCapHeight(); err != nil {
common.Log.Debug("ERROR: Unable to get font CapHeight: %v", err)
}
}
if int(fontCapHeight) <= 0 {
common.Log.Debug("WARN: CapHeight not available - setting to 1000")
fontCapHeight = 1000
}
h := fontCapHeight / 1000.0 * chunk.Style.FontSize * p.lineHeight
if h > capHeight {
capHeight = h
}
h = p.lineHeight * chunk.Style.FontSize
if h > height {
height = h
}
}
return capHeight, height
}
// getTextWidth calculates the text width as if all in one line (not taking // getTextWidth calculates the text width as if all in one line (not taking
// wrapping into account). // wrapping into account).
func (p *StyledParagraph) getTextWidth() float64 { func (p *StyledParagraph) getTextWidth() float64 {

View File

@ -798,3 +798,66 @@ func TestStyledLinkRotation(t *testing.T) {
t.Fatalf("Fail: %v\n", err) t.Fatalf("Fail: %v\n", err)
} }
} }
func TestStyledParagraphTableVerticalAlignment(t *testing.T) {
c := New()
fontRegular := newStandard14Font(t, model.CourierName)
createTable := func(c *Creator, text string, align CellVerticalAlignment, fontSize float64) {
textStyle := c.NewTextStyle()
textStyle.Font = fontRegular
textStyle.FontSize = fontSize
table := c.NewTable(1)
table.SetMargins(0, 0, 5, 5)
cell := table.NewCell()
sp := c.NewStyledParagraph()
textChunk := sp.Append(text)
textChunk.Style = textStyle
cell.SetVerticalAlignment(align)
cell.SetContent(sp)
cell.SetBorder(CellBorderSideAll, CellBorderStyleSingle, 1)
if err := c.Draw(table); err != nil {
t.Fatalf("Error drawing: %v", err)
}
}
chunks := []string{
"TR",
"Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Lacus viverra vitae congue eu consequat. Cras adipiscing enim eu turpis. Lectus magna fringilla urna porttitor. Condimentum id venenatis a condimentum. Quis ipsum suspendisse ultrices gravida dictum fusce. In fermentum posuere urna nec tincidunt.",
}
alignments := []struct {
Label string
Alignment CellVerticalAlignment
}{
{"Top alignment", CellVerticalAlignmentTop},
{"Middle alignment", CellVerticalAlignmentMiddle},
{"Bottom alignment", CellVerticalAlignmentBottom},
}
for _, chunk := range chunks {
for _, alignment := range alignments {
c.NewPage()
sp := c.NewStyledParagraph()
sp.Append(alignment.Label).Style.FontSize = 16
sp.SetMargins(0, 0, 0, 5)
if err := c.Draw(sp); err != nil {
t.Fatalf("Error drawing: %v", err)
}
for i := 4; i <= 20; i += 2 {
createTable(c, chunk, alignment.Alignment, float64(i))
}
}
}
// Write output file.
testWriteAndRender(t, c, "styled_paragraph_table_vertical_align.pdf")
}

View File

@ -516,8 +516,10 @@ func (table *Table) GeneratePageBlocks(ctx DrawContext) ([]*Block, DrawContext,
} }
if cell.content != nil { if cell.content != nil {
// content width. cw := cell.content.Width() // content width.
cw := cell.content.Width() ch := cell.content.Height() // content height.
vertOffset := 0.0
switch t := cell.content.(type) { switch t := cell.content.(type) {
case *Paragraph: case *Paragraph:
if t.enableWrap { if t.enableWrap {
@ -527,6 +529,26 @@ func (table *Table) GeneratePageBlocks(ctx DrawContext) ([]*Block, DrawContext,
if t.enableWrap { if t.enableWrap {
cw = t.getMaxLineWidth() / 1000.0 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 - t.defaultStyle.FontSize*t.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 *Table: case *Table:
cw = w cw = w
case *List: case *List:
@ -553,8 +575,9 @@ func (table *Table) GeneratePageBlocks(ctx DrawContext) ([]*Block, DrawContext,
} }
} }
ctx.Y += vertOffset
// Account for vertical alignment. // Account for vertical alignment.
ch := cell.content.Height() // content height.
switch cell.verticalAlignment { switch cell.verticalAlignment {
case CellVerticalAlignmentTop: case CellVerticalAlignmentTop:
// Default: do nothing. // Default: do nothing.
@ -567,7 +590,7 @@ func (table *Table) GeneratePageBlocks(ctx DrawContext) ([]*Block, DrawContext,
case CellVerticalAlignmentBottom: case CellVerticalAlignmentBottom:
if h > ch { if h > ch {
ctx.Y = ctx.Y + h - ch ctx.Y = ctx.Y + h - ch
ctx.Height = ch ctx.Height = h
} }
} }
@ -575,6 +598,8 @@ func (table *Table) GeneratePageBlocks(ctx DrawContext) ([]*Block, DrawContext,
if err != nil { if err != nil {
common.Log.Debug("ERROR: %v", err) common.Log.Debug("ERROR: %v", err)
} }
ctx.Y -= vertOffset
} }
ctx.Y += h ctx.Y += h