Add support for rotated appendices: implement 90° CCW image rotation for portrait pages, enhance table rendering logic, and update diagram handling directives.
This commit is contained in:
+97
-3
@@ -2,7 +2,12 @@ package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"image"
|
||||
"image/draw"
|
||||
_ "image/jpeg"
|
||||
"image/png"
|
||||
"log"
|
||||
"os"
|
||||
"sort"
|
||||
|
||||
"github.com/go-pdf/fpdf"
|
||||
@@ -142,7 +147,7 @@ func (r *IHKRenderer) RenderAppendices() {
|
||||
r.pdf.SetFont("Helvetica", "", dinFontBody)
|
||||
for i, app := range r.appendices {
|
||||
r.pdf.CellFormat(0, dinLineHtBody,
|
||||
r.tr(fmt.Sprintf("Anlage %d: %s", i+1, app.Title)), "", 1, "L", false, 0, "")
|
||||
r.tr(fmt.Sprintf("Anlage %s: %s", toRoman(i+1), app.Title)), "", 1, "L", false, 0, "")
|
||||
}
|
||||
|
||||
// Save portrait dimensions and standard margins for post-landscape restoration.
|
||||
@@ -164,12 +169,16 @@ func (r *IHKRenderer) RenderAppendices() {
|
||||
|
||||
r.pdf.SetFont("Helvetica", "B", dinFontBody)
|
||||
r.pdf.CellFormat(0, dinLineHtBody,
|
||||
r.tr(fmt.Sprintf("Anlage %d: %s", i+1, app.Title)), "", 1, "L", false, 0, "")
|
||||
r.tr(fmt.Sprintf("Anlage %s: %s", toRoman(i+1), app.Title)), "", 1, "L", false, 0, "")
|
||||
r.pdf.Ln(dinSpaceAfterHeading)
|
||||
|
||||
switch app.Kind {
|
||||
case AppendixKindImage:
|
||||
r.renderAppendixImage(app.Path)
|
||||
if app.Rotated {
|
||||
r.renderAppendixImageRotated(app.Path)
|
||||
} else {
|
||||
r.renderAppendixImage(app.Path)
|
||||
}
|
||||
case AppendixKindTable:
|
||||
r.renderTableBody(app.TableData, "")
|
||||
case AppendixKindCode:
|
||||
@@ -215,6 +224,91 @@ func (r *IHKRenderer) renderAppendixImage(path string) {
|
||||
fpdf.ImageOptions{ReadDpi: true}, 0, "")
|
||||
}
|
||||
|
||||
// renderAppendixImageRotated places an image on a portrait page rotated 90° CW.
|
||||
// The image is pre-rotated in memory so that a wide (landscape-ratio) diagram
|
||||
// fills the page height. The reader tilts the page 90° CW to read it normally.
|
||||
func (r *IHKRenderer) renderAppendixImageRotated(path string) {
|
||||
rotatedPath, err := rotateImageCW(path)
|
||||
if err != nil {
|
||||
log.Printf("warning: could not rotate image %q: %v — falling back to normal", path, err)
|
||||
r.renderAppendixImage(path)
|
||||
return
|
||||
}
|
||||
|
||||
r.renderAppendixImageWithPath(rotatedPath)
|
||||
}
|
||||
|
||||
// rotateImageCW decodes a PNG/JPEG from disk, rotates it 90° clockwise, writes
|
||||
// the result to a temp file, and returns the temp file path.
|
||||
// The caller owns the temp file (it is left on disk; fpdf reads it lazily).
|
||||
func rotateImageCW(src string) (string, error) {
|
||||
f, err := os.Open(src)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
defer f.Close()
|
||||
|
||||
img, _, err := image.Decode(f)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
b := img.Bounds()
|
||||
// 90° CW: new width = old height, new height = old width.
|
||||
rotated := image.NewRGBA(image.Rect(0, 0, b.Max.Y-b.Min.Y, b.Max.X-b.Min.X))
|
||||
draw.Draw(rotated, rotated.Bounds(), image.White, image.Point{}, draw.Src)
|
||||
for y := b.Min.Y; y < b.Max.Y; y++ {
|
||||
for x := b.Min.X; x < b.Max.X; x++ {
|
||||
// 90° CW: new(newW-1-y, x) = old(x, y)
|
||||
rotated.Set(b.Max.Y-1-y, x-b.Min.X, img.At(x, y))
|
||||
}
|
||||
}
|
||||
|
||||
tmp, err := os.CreateTemp("", "ihk_rotated_*.png")
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
defer tmp.Close()
|
||||
|
||||
if err := png.Encode(tmp, rotated); err != nil {
|
||||
os.Remove(tmp.Name())
|
||||
return "", err
|
||||
}
|
||||
return tmp.Name(), nil
|
||||
}
|
||||
|
||||
// renderAppendixImageWithPath is identical to renderAppendixImage but accepts
|
||||
// an arbitrary path (used for pre-rotated temp files).
|
||||
func (r *IHKRenderer) renderAppendixImageWithPath(path string) {
|
||||
info := r.ensureImageRegistered(path)
|
||||
if info == nil {
|
||||
r.pdf.CellFormat(0, dinLineHtBody, r.tr("[Image could not be loaded: "+path+"]"),
|
||||
"1", 1, "C", false, 0, "")
|
||||
return
|
||||
}
|
||||
|
||||
_, pageH := r.pdf.GetPageSize()
|
||||
lm, _, _, bm := r.pdf.GetMargins()
|
||||
uw := r.usableWidth()
|
||||
availH := pageH - r.pdf.GetY() - bm - 10
|
||||
|
||||
imgW := info.Width() * ptToMM
|
||||
imgH := info.Height() * ptToMM
|
||||
|
||||
displayW := uw
|
||||
displayH := imgH * (displayW / imgW)
|
||||
if displayH > availH {
|
||||
scale := availH / displayH
|
||||
displayH = availH
|
||||
displayW = displayW * scale
|
||||
}
|
||||
|
||||
posX := lm + (uw-displayW)/2
|
||||
r.pdf.ImageOptions(path, posX, r.pdf.GetY(), displayW, displayH, false,
|
||||
fpdf.ImageOptions{ReadDpi: true}, 0, "")
|
||||
r.pdf.SetY(r.pdf.GetY() + displayH + 2)
|
||||
}
|
||||
|
||||
// RenderAbbreviations renders the list of abbreviations from the YAML config.
|
||||
// It is placed after the TOC and uses Roman page numbering.
|
||||
func (r *IHKRenderer) RenderAbbreviations() {
|
||||
|
||||
Reference in New Issue
Block a user