github.com/unidoc/unidoc@v2.2.0+incompatible/pdf/creator/subchapter.go (about)

     1  /*
     2   * This file is subject to the terms and conditions defined in
     3   * file 'LICENSE.md', which is part of this source code package.
     4   */
     5  
     6  package creator
     7  
     8  import (
     9  	"fmt"
    10  
    11  	"github.com/unidoc/unidoc/common"
    12  	"github.com/unidoc/unidoc/pdf/model/fonts"
    13  )
    14  
    15  // Subchapter simply represents a sub chapter pertaining to a specific Chapter.  It can contain multiple
    16  // Drawables, just like a chapter.
    17  type Subchapter struct {
    18  	chapterNum    int
    19  	subchapterNum int
    20  	title         string
    21  	heading       *Paragraph
    22  
    23  	contents []Drawable
    24  
    25  	// Show chapter numbering
    26  	showNumbering bool
    27  
    28  	// Include in TOC.
    29  	includeInTOC bool
    30  
    31  	// Positioning: relative / absolute.
    32  	positioning positioning
    33  
    34  	// Absolute coordinates (when in absolute mode).
    35  	xPos, yPos float64
    36  
    37  	// Margins to be applied around the block when drawing on Page.
    38  	margins margins
    39  
    40  	// Reference to the creator's TOC.
    41  	toc *TableOfContents
    42  }
    43  
    44  // NewSubchapter creates a new Subchapter under Chapter ch with specified title.
    45  // All other parameters are set to their defaults.
    46  func (c *Creator) NewSubchapter(ch *Chapter, title string) *Subchapter {
    47  	subchap := &Subchapter{}
    48  
    49  	ch.subchapters++
    50  	subchap.subchapterNum = ch.subchapters
    51  
    52  	subchap.chapterNum = ch.number
    53  	subchap.title = title
    54  
    55  	heading := fmt.Sprintf("%d.%d %s", subchap.chapterNum, subchap.subchapterNum, title)
    56  	p := NewParagraph(heading)
    57  
    58  	p.SetFontSize(14)
    59  	p.SetFont(fonts.NewFontHelvetica()) // bold?
    60  
    61  	subchap.showNumbering = true
    62  	subchap.includeInTOC = true
    63  
    64  	subchap.heading = p
    65  	subchap.contents = []Drawable{}
    66  
    67  	// Add subchapter to ch.
    68  	ch.Add(subchap)
    69  
    70  	// Keep a reference for toc.
    71  	subchap.toc = c.toc
    72  
    73  	return subchap
    74  }
    75  
    76  // SetShowNumbering sets a flag to indicate whether or not to show chapter numbers as part of title.
    77  func (subchap *Subchapter) SetShowNumbering(show bool) {
    78  	if show {
    79  		heading := fmt.Sprintf("%d.%d. %s", subchap.chapterNum, subchap.subchapterNum, subchap.title)
    80  		subchap.heading.SetText(heading)
    81  	} else {
    82  		heading := fmt.Sprintf("%s", subchap.title)
    83  		subchap.heading.SetText(heading)
    84  	}
    85  	subchap.showNumbering = show
    86  }
    87  
    88  // SetIncludeInTOC sets a flag to indicate whether or not to include in the table of contents.
    89  func (subchap *Subchapter) SetIncludeInTOC(includeInTOC bool) {
    90  	subchap.includeInTOC = includeInTOC
    91  }
    92  
    93  // GetHeading returns the Subchapter's heading Paragraph to address style (font type, size, etc).
    94  func (subchap *Subchapter) GetHeading() *Paragraph {
    95  	return subchap.heading
    96  }
    97  
    98  // Set absolute coordinates.
    99  /*
   100  func (subchap *subchapter) SetPos(x, y float64) {
   101  	subchap.positioning = positionAbsolute
   102  	subchap.xPos = x
   103  	subchap.yPos = y
   104  }
   105  */
   106  
   107  // SetMargins sets the Subchapter's margins (left, right, top, bottom).
   108  // These margins are typically not needed as the Creator's page margins are used preferably.
   109  func (subchap *Subchapter) SetMargins(left, right, top, bottom float64) {
   110  	subchap.margins.left = left
   111  	subchap.margins.right = right
   112  	subchap.margins.top = top
   113  	subchap.margins.bottom = bottom
   114  }
   115  
   116  // GetMargins returns the Subchapter's margins: left, right, top, bottom.
   117  func (subchap *Subchapter) GetMargins() (float64, float64, float64, float64) {
   118  	return subchap.margins.left, subchap.margins.right, subchap.margins.top, subchap.margins.bottom
   119  }
   120  
   121  // Add adds a new Drawable to the chapter.
   122  // The currently supported Drawables are: *Paragraph, *Image, *Block, *Table.
   123  func (subchap *Subchapter) Add(d Drawable) {
   124  	switch d.(type) {
   125  	case *Chapter, *Subchapter:
   126  		common.Log.Debug("Error: Cannot add chapter or subchapter to a subchapter")
   127  	case *Paragraph, *Image, *Block, *Table, *PageBreak:
   128  		subchap.contents = append(subchap.contents, d)
   129  	default:
   130  		common.Log.Debug("Unsupported: %T", d)
   131  	}
   132  }
   133  
   134  // GeneratePageBlocks generates the page blocks.  Multiple blocks are generated if the contents wrap over
   135  // multiple pages. Implements the Drawable interface.
   136  func (subchap *Subchapter) GeneratePageBlocks(ctx DrawContext) ([]*Block, DrawContext, error) {
   137  	origCtx := ctx
   138  
   139  	if subchap.positioning.isRelative() {
   140  		// Update context.
   141  		ctx.X += subchap.margins.left
   142  		ctx.Y += subchap.margins.top
   143  		ctx.Width -= subchap.margins.left + subchap.margins.right
   144  		ctx.Height -= subchap.margins.top
   145  	}
   146  
   147  	blocks, ctx, err := subchap.heading.GeneratePageBlocks(ctx)
   148  	if err != nil {
   149  		return blocks, ctx, err
   150  	}
   151  	if len(blocks) > 1 {
   152  		ctx.Page++ // did not fit - moved to next Page.
   153  	}
   154  	if subchap.includeInTOC {
   155  		// Add to TOC.
   156  		subchap.toc.add(subchap.title, subchap.chapterNum, subchap.subchapterNum, ctx.Page)
   157  	}
   158  
   159  	for _, d := range subchap.contents {
   160  		newBlocks, c, err := d.GeneratePageBlocks(ctx)
   161  		if err != nil {
   162  			return blocks, ctx, err
   163  		}
   164  		if len(newBlocks) < 1 {
   165  			continue
   166  		}
   167  
   168  		// The first block is always appended to the last..
   169  		blocks[len(blocks)-1].mergeBlocks(newBlocks[0])
   170  		blocks = append(blocks, newBlocks[1:]...)
   171  
   172  		ctx = c
   173  	}
   174  
   175  	if subchap.positioning.isRelative() {
   176  		// Move back X to same start of line.
   177  		ctx.X = origCtx.X
   178  	}
   179  
   180  	if subchap.positioning.isAbsolute() {
   181  		// If absolute: return original context.
   182  		return blocks, origCtx, nil
   183  
   184  	}
   185  
   186  	return blocks, ctx, nil
   187  }