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 }