- Исправлена двойная обработка ITableRow через reflection - Исправлен выход за границы изображения при отрисовке последнего разделителя - Добавлена защита от пустых данных (header, blocks) - Добавлена compile-time проверка интерфейса ITableRow - Переименован tablle_block_style.go → table_block_style.go - Добавлены комментарии на русском ко всем функциям и типам - Написана документация README.md с примерами использования - Добавлен CLAUDE.md Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
116 lines
3.1 KiB
Go
116 lines
3.1 KiB
Go
package image_table
|
||
|
||
import (
|
||
"image"
|
||
"image/color"
|
||
"image/draw"
|
||
|
||
"golang.org/x/image/font"
|
||
"golang.org/x/image/font/opentype"
|
||
"golang.org/x/image/math/fixed"
|
||
)
|
||
|
||
// Document — документ, состоящий из нескольких таблиц с заголовками
|
||
type Document struct {
|
||
Blocks []TableBlock
|
||
}
|
||
|
||
// Цвет заголовка блока
|
||
var titleColor = color.RGBA{0x22, 0x22, 0x20, 255}
|
||
|
||
// Высота заголовка блока (над таблицей)
|
||
const blockTitleHeight = 30
|
||
|
||
// Вертикальный отступ между блоками
|
||
const blockSpacing = 20
|
||
|
||
// Шрифт для заголовков блоков (12pt)
|
||
var globalFaceBig font.Face
|
||
|
||
// Инициализация увеличенного шрифта для заголовков блоков
|
||
func init() {
|
||
f, err := opentype.Parse(jbTTF)
|
||
if err != nil {
|
||
panic(err)
|
||
}
|
||
|
||
globalFaceBig, err = opentype.NewFace(f, &opentype.FaceOptions{
|
||
Size: 12,
|
||
DPI: 96,
|
||
Hinting: font.HintingFull,
|
||
})
|
||
if err != nil {
|
||
panic(err)
|
||
}
|
||
}
|
||
|
||
// drawBigText рисует текст увеличенным шрифтом (для заголовков блоков)
|
||
func drawBigText(img *image.RGBA, x, y int, text string) {
|
||
d := &font.Drawer{
|
||
Dst: img,
|
||
Src: image.NewUniform(titleColor),
|
||
Face: globalFaceBig,
|
||
Dot: fixed.P(x, y),
|
||
}
|
||
d.DrawString(text)
|
||
}
|
||
|
||
// RenderDocument рендерит документ — несколько именованных таблиц на одном изображении.
|
||
// Каждый блок содержит заголовок и таблицу. Блоки располагаются вертикально с отступами.
|
||
func RenderDocument(doc Document) image.Image {
|
||
// Если нет блоков — возвращаем пустое изображение
|
||
if len(doc.Blocks) == 0 {
|
||
return image.NewRGBA(image.Rect(0, 0, 1, 1))
|
||
}
|
||
|
||
// Рендерим каждую таблицу отдельно и считаем общие размеры
|
||
width := 0
|
||
height := 0
|
||
|
||
blockImages := make([]image.Image, len(doc.Blocks))
|
||
|
||
for i, block := range doc.Blocks {
|
||
tableImg := DrawTableWarm(block.Header, block.Rows)
|
||
blockImages[i] = tableImg
|
||
|
||
w := tableImg.Bounds().Dx()
|
||
if w > width {
|
||
width = w
|
||
}
|
||
|
||
height += blockTitleHeight + tableImg.Bounds().Dy() + blockSpacing
|
||
}
|
||
|
||
// Внешние отступы документа
|
||
margin := 20
|
||
|
||
// Создаём итоговое изображение
|
||
img := image.NewRGBA(image.Rect(0, 0, width+margin*2, height+margin*2))
|
||
|
||
// Белый фон документа
|
||
draw.Draw(img, img.Bounds(), &image.Uniform{color.White}, image.Point{}, draw.Src)
|
||
|
||
y := margin
|
||
|
||
// Отрисовка блоков
|
||
for i, block := range doc.Blocks {
|
||
// Заголовок блока
|
||
drawBigText(img, margin, y+blockTitleHeight-10, block.Title)
|
||
y += blockTitleHeight
|
||
|
||
// Таблица блока
|
||
tableImg := blockImages[i]
|
||
draw.Draw(
|
||
img,
|
||
image.Rect(margin, y, margin+tableImg.Bounds().Dx(), y+tableImg.Bounds().Dy()),
|
||
tableImg,
|
||
image.Point{},
|
||
draw.Over,
|
||
)
|
||
|
||
y += tableImg.Bounds().Dy() + blockSpacing
|
||
}
|
||
|
||
return img
|
||
}
|