clone

fifteen puzzle games for the terminal, with daily puzzles, weekly ladders and xp, built on bubble tea

bug/architecture-test-trim-path
puzzletea / export / pdf / render_plan.go
render_plan.go 3.7K
1package pdfexport23type bookletRenderPlan struct {4 puzzlePages int5 includeCover bool6 padPages int7}89type logicalPageKind int1011const (12 logicalPageCoverOutside logicalPageKind = iota13 logicalPageCoverBlank14 logicalPageTitle15 logicalPagePuzzle16 logicalPagePad17)1819type logicalPage struct {20 Number int21 Kind logicalPageKind22 PuzzleIndex int23 OutsideSlice coverOutsideSlice24 ShowFooter bool25}2627type duplexBookletSpread struct {28 LeftPage int29 RightPage int30}3132type duplexBookletSheet struct {33 Front duplexBookletSpread34 Back duplexBookletSpread35}3637func newBookletRenderPlan(puzzlePages int, includeCover bool) bookletRenderPlan {38 extraPages := 1 // title page39 if includeCover {40 extraPages += 441 }4243 return bookletRenderPlan{44 puzzlePages: puzzlePages,45 includeCover: includeCover,46 padPages: saddleStitchPadCount(puzzlePages + extraPages),47 }48}4950func (p bookletRenderPlan) totalPagesWithoutPadding() int {51 total := p.puzzlePages + 1 // title page52 if p.includeCover {53 total += 454 }55 return total56}5758func (p bookletRenderPlan) totalPages() int {59 return p.totalPagesWithoutPadding() + p.padPages60}6162func (p bookletRenderPlan) titlePageNumber() int {63 if p.includeCover {64 return 365 }66 return 167}6869func (p bookletRenderPlan) puzzleStartPage() int {70 return p.titlePageNumber() + 171}7273func (p bookletRenderPlan) firstPadPage() int {74 return p.puzzleStartPage() + p.puzzlePages75}7677func (p bookletRenderPlan) coverPageNumbers() (frontOutside, frontInside, backInside, backOutside int, ok bool) {78 if !p.includeCover {79 return 0, 0, 0, 0, false80 }8182 total := p.totalPages()83 return 1, 2, total - 1, total, true84}8586func (p bookletRenderPlan) footerExcludedPages() map[int]struct{} {87 excluded := map[int]struct{}{88 p.titlePageNumber(): {},89 }9091 for page := p.firstPadPage(); page < p.firstPadPage()+p.padPages; page++ {92 excluded[page] = struct{}{}93 }9495 frontOutside, frontInside, backInside, backOutside, ok := p.coverPageNumbers()96 if ok {97 excluded[frontOutside] = struct{}{}98 excluded[frontInside] = struct{}{}99 excluded[backInside] = struct{}{}100 excluded[backOutside] = struct{}{}101 }102103 return excluded104}105106func buildLogicalPages(puzzlePages int, includeCover bool) []logicalPage {107 plan := newBookletRenderPlan(puzzlePages, includeCover)108 excluded := plan.footerExcludedPages()109 pages := make([]logicalPage, 0, plan.totalPages())110111 appendPage := func(kind logicalPageKind, puzzleIndex int, slice coverOutsideSlice) {112 pageNo := len(pages) + 1113 _, skipFooter := excluded[pageNo]114 pages = append(pages, logicalPage{115 Number: pageNo,116 Kind: kind,117 PuzzleIndex: puzzleIndex,118 OutsideSlice: slice,119 ShowFooter: !skipFooter,120 })121 }122123 if includeCover {124 appendPage(logicalPageCoverOutside, -1, coverOutsideFront)125 appendPage(logicalPageCoverBlank, -1, coverOutsideFront)126 }127128 appendPage(logicalPageTitle, -1, coverOutsideFront)129130 for i := range puzzlePages {131 appendPage(logicalPagePuzzle, i, coverOutsideFront)132 }133 for range plan.padPages {134 appendPage(logicalPagePad, -1, coverOutsideFront)135 }136137 if includeCover {138 appendPage(logicalPageCoverBlank, -1, coverOutsideBack)139 appendPage(logicalPageCoverOutside, -1, coverOutsideBack)140 }141142 return pages143}144145func duplexBookletSheets(totalPages int) []duplexBookletSheet {146 if totalPages <= 0 || totalPages%4 != 0 {147 return nil148 }149150 sheets := make([]duplexBookletSheet, 0, totalPages/4)151 for sheetIndex := 0; sheetIndex < totalPages/4; sheetIndex++ {152 sheets = append(sheets, duplexBookletSheet{153 Front: duplexBookletSpread{154 LeftPage: totalPages - 2*sheetIndex,155 RightPage: 1 + 2*sheetIndex,156 },157 Back: duplexBookletSpread{158 LeftPage: 2 + 2*sheetIndex,159 RightPage: totalPages - 1 - 2*sheetIndex,160 },161 })162 }163 return sheets164}