190 lines
5.4 KiB
Go
190 lines
5.4 KiB
Go
package pdfrender
|
|
|
|
import (
|
|
"fmt"
|
|
|
|
"github.com/pdfcpu/pdfcpu/pkg/api"
|
|
)
|
|
|
|
// RenderPageWithContinuations renders a template page and all necessary continuation pages
|
|
// Returns a slice of PDF bytes, one for each page (main + continuations)
|
|
func RenderPageWithContinuations(
|
|
viewModel *CharacterSheetViewModel,
|
|
templateName string,
|
|
startPageNumber int,
|
|
date string,
|
|
loader *TemplateLoader,
|
|
renderer *PDFRenderer,
|
|
) ([][]byte, error) {
|
|
var pdfs [][]byte
|
|
templateSet := DefaultA4QuerTemplateSet()
|
|
paginator := NewPaginator(templateSet)
|
|
|
|
// Build data map from view model
|
|
dataMap := map[string]interface{}{
|
|
"skills": viewModel.Skills,
|
|
"weapons": viewModel.Weapons,
|
|
"spells": viewModel.Spells,
|
|
"equipment": viewModel.Equipment,
|
|
"magicItems": viewModel.MagicItems,
|
|
}
|
|
|
|
// Use unified pagination for all templates
|
|
distributions, err := paginator.PaginateMultiList(dataMap, templateName)
|
|
if err != nil {
|
|
return nil, fmt.Errorf("failed to paginate: %w", err)
|
|
}
|
|
|
|
// If no distributions (empty data), render single empty page
|
|
if len(distributions) == 0 {
|
|
pageData, err := PreparePaginatedPageData(viewModel, templateName, startPageNumber, date)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
html, err := loader.RenderTemplateWithInlinedResources(templateName, pageData)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
pdf, err := renderer.RenderHTMLToPDF(html)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
return [][]byte{pdf}, nil
|
|
}
|
|
|
|
// Render each distributed page
|
|
for i, dist := range distributions {
|
|
pageData := &PageData{
|
|
Character: viewModel.Character,
|
|
Attributes: viewModel.Attributes,
|
|
DerivedValues: viewModel.DerivedValues,
|
|
GameResults: viewModel.GameResults,
|
|
Meta: PageMeta{
|
|
Date: date,
|
|
PageNumber: startPageNumber + i,
|
|
},
|
|
}
|
|
|
|
// Populate page data from distribution
|
|
populatePageDataFromDistribution(pageData, dist)
|
|
|
|
// Render the page
|
|
html, err := loader.RenderTemplateWithInlinedResources(dist.TemplateName, pageData)
|
|
if err != nil {
|
|
return nil, fmt.Errorf("failed to render %s: %w", dist.TemplateName, err)
|
|
}
|
|
|
|
pdf, err := renderer.RenderHTMLToPDF(html)
|
|
if err != nil {
|
|
return nil, fmt.Errorf("failed to generate PDF for %s: %w", dist.TemplateName, err)
|
|
}
|
|
|
|
pdfs = append(pdfs, pdf)
|
|
}
|
|
|
|
return pdfs, nil
|
|
}
|
|
|
|
// populatePageDataFromDistribution populates PageData from a distribution
|
|
// This replaces the hardcoded switch statements for each template type
|
|
func populatePageDataFromDistribution(pageData *PageData, dist PageDistribution) {
|
|
// Populate data based on block names in distribution
|
|
for blockName, data := range dist.Data {
|
|
switch blockName {
|
|
// Skills blocks
|
|
case "skills_column1":
|
|
if skills, ok := data.([]SkillViewModel); ok {
|
|
pageData.SkillsColumn1 = skills
|
|
pageData.Skills = append(pageData.Skills, skills...)
|
|
}
|
|
case "skills_column2":
|
|
if skills, ok := data.([]SkillViewModel); ok {
|
|
pageData.SkillsColumn2 = skills
|
|
pageData.Skills = append(pageData.Skills, skills...)
|
|
}
|
|
case "skills_column3":
|
|
if skills, ok := data.([]SkillViewModel); ok {
|
|
pageData.SkillsColumn3 = skills
|
|
pageData.Skills = append(pageData.Skills, skills...)
|
|
}
|
|
case "skills_column4":
|
|
if skills, ok := data.([]SkillViewModel); ok {
|
|
pageData.SkillsColumn4 = skills
|
|
pageData.Skills = append(pageData.Skills, skills...)
|
|
}
|
|
case "skills_learned":
|
|
if skills, ok := data.([]SkillViewModel); ok {
|
|
pageData.SkillsLearned = skills
|
|
}
|
|
case "skills_learned_1":
|
|
if skills, ok := data.([]SkillViewModel); ok {
|
|
pageData.SkillsLearned1 = skills
|
|
}
|
|
case "skills_learned_2":
|
|
if skills, ok := data.([]SkillViewModel); ok {
|
|
pageData.SkillsLearned2 = skills
|
|
}
|
|
case "skills_unlearned":
|
|
if skills, ok := data.([]SkillViewModel); ok {
|
|
// Add to general Skills list for template compatibility
|
|
pageData.Skills = append(pageData.Skills, skills...)
|
|
}
|
|
case "skills_languages":
|
|
if skills, ok := data.([]SkillViewModel); ok {
|
|
pageData.SkillsLanguage = skills
|
|
}
|
|
|
|
// Weapons blocks
|
|
case "weapons_main":
|
|
if weapons, ok := data.([]WeaponViewModel); ok {
|
|
pageData.Weapons = weapons
|
|
}
|
|
|
|
// Spells blocks
|
|
case "spells_column1":
|
|
if spells, ok := data.([]SpellViewModel); ok {
|
|
pageData.SpellsLeft = spells
|
|
pageData.Spells = append(pageData.Spells, spells...)
|
|
}
|
|
case "spells_column2":
|
|
if spells, ok := data.([]SpellViewModel); ok {
|
|
pageData.SpellsRight = spells
|
|
pageData.Spells = append(pageData.Spells, spells...)
|
|
}
|
|
|
|
// Equipment blocks
|
|
case "equipment_worn":
|
|
if equipment, ok := data.([]EquipmentViewModel); ok {
|
|
pageData.Equipment = append(pageData.Equipment, equipment...)
|
|
}
|
|
case "equipment_carried":
|
|
if equipment, ok := data.([]EquipmentViewModel); ok {
|
|
pageData.Equipment = append(pageData.Equipment, equipment...)
|
|
}
|
|
|
|
// Magic items
|
|
case "magic_items":
|
|
if items, ok := data.([]MagicItemViewModel); ok {
|
|
pageData.MagicItems = items
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
// MergePDFs merges multiple PDF byte slices into a single PDF
|
|
func MergePDFs(pdfList [][]byte, outputPath string) error {
|
|
if len(pdfList) == 0 {
|
|
return fmt.Errorf("no PDFs to merge")
|
|
}
|
|
|
|
if len(pdfList) == 1 {
|
|
// Single PDF, just write it
|
|
return nil
|
|
}
|
|
|
|
// Use pdfcpu to merge - this is a placeholder, actual implementation
|
|
// would need to save individual PDFs first, then merge them
|
|
// For now, this function signature is defined for future use
|
|
return api.MergeCreateFile(nil, outputPath, false, nil)
|
|
}
|