fix wrong git ignore
This commit is contained in:
189
internal/pdf/creator/division.go
Normal file
189
internal/pdf/creator/division.go
Normal file
@@ -0,0 +1,189 @@
|
||||
package creator
|
||||
|
||||
import (
|
||||
"errors"
|
||||
|
||||
"gitea.tecamino.com/paadi/pdfmerge/internal/pdf/common"
|
||||
)
|
||||
|
||||
// Division is a container component which can wrap across multiple pages (unlike Block).
|
||||
// It can contain multiple Drawable components (currently supporting Paragraph and Image).
|
||||
//
|
||||
// The component stacking behavior is vertical, where the Drawables are drawn on top of each other.
|
||||
// Also supports horizontal stacking by activating the inline mode.
|
||||
type Division struct {
|
||||
components []VectorDrawable
|
||||
|
||||
// Positioning: relative / absolute.
|
||||
positioning positioning
|
||||
|
||||
// Margins to be applied around the block when drawing on Page.
|
||||
margins margins
|
||||
|
||||
// Controls whether the components are stacked horizontally
|
||||
inline bool
|
||||
}
|
||||
|
||||
// NewDivision returns a new Division container component.
|
||||
func NewDivision() *Division {
|
||||
return &Division{
|
||||
components: []VectorDrawable{},
|
||||
}
|
||||
}
|
||||
|
||||
// Inline returns whether the inline mode of the division is active.
|
||||
func (div *Division) Inline() bool {
|
||||
return div.inline
|
||||
}
|
||||
|
||||
// SetInline sets the inline mode of the division.
|
||||
func (div *Division) SetInline(inline bool) {
|
||||
div.inline = inline
|
||||
}
|
||||
|
||||
// Add adds a VectorDrawable to the Division container.
|
||||
// Currently supported VectorDrawables: *Paragraph, *StyledParagraph, *Image.
|
||||
func (div *Division) Add(d VectorDrawable) error {
|
||||
supported := false
|
||||
|
||||
switch d.(type) {
|
||||
case *Paragraph:
|
||||
supported = true
|
||||
case *StyledParagraph:
|
||||
supported = true
|
||||
case *Image:
|
||||
supported = true
|
||||
}
|
||||
|
||||
if !supported {
|
||||
return errors.New("unsupported type in Division")
|
||||
}
|
||||
|
||||
div.components = append(div.components, d)
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// Height returns the height for the Division component assuming all stacked on top of each other.
|
||||
func (div *Division) Height() float64 {
|
||||
y := 0.0
|
||||
yMax := 0.0
|
||||
for _, component := range div.components {
|
||||
compWidth, compHeight := component.Width(), component.Height()
|
||||
switch t := component.(type) {
|
||||
case *Paragraph:
|
||||
p := t
|
||||
compWidth += p.margins.left + p.margins.right
|
||||
compHeight += p.margins.top + p.margins.bottom
|
||||
}
|
||||
|
||||
// Vertical stacking.
|
||||
y += compHeight
|
||||
yMax = y
|
||||
}
|
||||
|
||||
return yMax
|
||||
}
|
||||
|
||||
// Width is not used. Not used as a Division element is designed to fill into available width depending on
|
||||
// context. Returns 0.
|
||||
func (div *Division) Width() float64 {
|
||||
return 0
|
||||
}
|
||||
|
||||
// GeneratePageBlocks generates the page blocks for the Division component.
|
||||
// Multiple blocks are generated if the contents wrap over multiple pages.
|
||||
func (div *Division) GeneratePageBlocks(ctx DrawContext) ([]*Block, DrawContext, error) {
|
||||
pageblocks := []*Block{}
|
||||
|
||||
origCtx := ctx
|
||||
|
||||
if div.positioning.isRelative() {
|
||||
// Update context.
|
||||
ctx.X += div.margins.left
|
||||
ctx.Y += div.margins.top
|
||||
ctx.Width -= div.margins.left + div.margins.right
|
||||
ctx.Height -= div.margins.top
|
||||
}
|
||||
|
||||
// Set the inline mode of the division to the context.
|
||||
ctx.Inline = div.inline
|
||||
|
||||
// Draw.
|
||||
divCtx := ctx
|
||||
tmpCtx := ctx
|
||||
var lineHeight float64
|
||||
|
||||
for _, component := range div.components {
|
||||
if ctx.Inline {
|
||||
// Check whether the component fits on the current line.
|
||||
if (ctx.X-divCtx.X)+component.Width() <= ctx.Width {
|
||||
ctx.Y = tmpCtx.Y
|
||||
ctx.Height = tmpCtx.Height
|
||||
} else {
|
||||
ctx.X = divCtx.X
|
||||
ctx.Width = divCtx.Width
|
||||
|
||||
tmpCtx.Y += lineHeight
|
||||
tmpCtx.Height -= lineHeight
|
||||
lineHeight = 0
|
||||
}
|
||||
}
|
||||
|
||||
newblocks, updCtx, err := component.GeneratePageBlocks(ctx)
|
||||
if err != nil {
|
||||
common.Log.Debug("error generating page blocks: %v", err)
|
||||
return nil, ctx, err
|
||||
}
|
||||
|
||||
if len(newblocks) < 1 {
|
||||
continue
|
||||
}
|
||||
|
||||
if len(pageblocks) > 0 {
|
||||
// If there are pageblocks already in place.
|
||||
// merge the first block in with current Block and append the rest.
|
||||
pageblocks[len(pageblocks)-1].mergeBlocks(newblocks[0])
|
||||
pageblocks = append(pageblocks, newblocks[1:]...)
|
||||
} else {
|
||||
pageblocks = append(pageblocks, newblocks[0:]...)
|
||||
}
|
||||
|
||||
// Apply padding/margins.
|
||||
if ctx.Inline {
|
||||
// Recalculate positions on page change.
|
||||
if ctx.Page != updCtx.Page {
|
||||
divCtx.Y = ctx.Margins.top
|
||||
divCtx.Height = ctx.PageHeight - ctx.Margins.top
|
||||
|
||||
tmpCtx.Y = divCtx.Y
|
||||
tmpCtx.Height = divCtx.Height
|
||||
lineHeight = updCtx.Height - divCtx.Height
|
||||
} else {
|
||||
// Calculate current line max height.
|
||||
if dl := ctx.Height - updCtx.Height; dl > lineHeight {
|
||||
lineHeight = dl
|
||||
}
|
||||
}
|
||||
} else {
|
||||
updCtx.X = ctx.X
|
||||
}
|
||||
|
||||
ctx = updCtx
|
||||
}
|
||||
|
||||
// Restore the original inline mode of the context.
|
||||
ctx.Inline = origCtx.Inline
|
||||
|
||||
if div.positioning.isRelative() {
|
||||
// Move back X to same start of line.
|
||||
ctx.X = origCtx.X
|
||||
}
|
||||
|
||||
if div.positioning.isAbsolute() {
|
||||
// If absolute: return original context.
|
||||
return pageblocks, origCtx, nil
|
||||
}
|
||||
|
||||
return pageblocks, ctx, nil
|
||||
}
|
||||
Reference in New Issue
Block a user