rtisvg

package module
v0.7.22 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Jul 21, 2026 License: MIT Imports: 16 Imported by: 0

README

Report Table Image (SVG)

Go Reference CI

rtisvg is a small Go library that converts rows returned by an SQL query into a reusable SVG report and a Telegram-friendly PNG image.

It is designed for SQLite applications, accepts an already-open *sql.DB, supports a dynamic number of query columns, and does not require Chromium, ImageMagick, CGO, or an external renderer.

Made with ❤️ by Xenon007 for X07.IT.

Install

go get github.com/x07-it/rtisvg@latest

The application chooses and imports its own SQLite driver. rtisvg only receives an open *sql.DB.

Quick start

package report

import (
    "bytes"
    "context"
    "database/sql"
    "embed"

    "github.com/x07-it/rtisvg"
)

//go:embed templates/*.svg
var templates embed.FS

func Build(ctx context.Context, db *sql.DB, category string) (*bytes.Reader, error) {
    result, err := rtisvg.Render(ctx, db, rtisvg.Request{
        Query: `
            SELECT position, participant, score, category, event_date
            FROM daily_report
            WHERE category = ?
            ORDER BY position
        `,
        Args: []any{category},
        Headers: []string{
            "№", "Participant", "Score", "Category", "Date",
        },
        Template: rtisvg.Template{
            FS:   templates,
            Path: "templates/report.svg",
        },
        Data: map[string]any{
            "Title": "Daily results",
        },
        PNG: rtisvg.PNGOptions{
            Scale:      2,
            Background: "#ffffff",
        },
    })
    if err != nil {
        return nil, err
    }

    return bytes.NewReader(result.PNG), nil
}

result.SVG contains the completed vector document. result.PNG contains the raster image. The result also reports row count, column count, logical SVG dimensions, PNG dimensions, and SQL column names.

Query columns

The number of columns is discovered at runtime through database/sql.Rows.Columns. Three, five, or more columns require no API changes.

Use SQL column names as headers
rtisvg.Request{
    Query: `SELECT * FROM daily_report ORDER BY event_date`,
}
Replace headers positionally
Headers: []string{"№", "Name", "Score", "Date"},
Configure columns
Columns: []rtisvg.Column{
    {Name: "position", Header: "№", Align: rtisvg.AlignCenter, Width: 100},
    {Name: "participant", Header: "Participant"},
    {Name: "score", Header: "Score", Align: rtisvg.AlignRight},
    {Name: "event_date", Header: "Date", Align: rtisvg.AlignCenter},
},

Headers and Columns are mutually exclusive. A zero column width receives an equal share of the remaining table width. If every column has an explicit width, the proportions are normalized to fill the table width.

SVG template

The template is parsed with Go's context-aware html/template package. SQL values are exposed as .Rows[].Cells[].Text and are escaped when inserted into SVG text or attributes.

<svg xmlns="http://www.w3.org/2000/svg"
     width="{{.Width}}" height="{{.Height}}"
     viewBox="0 0 {{.Width}} {{.Height}}">

  {{range .Headers}}
  <text x="{{.TextX}}" y="{{.TextY}}" text-anchor="{{.Anchor}}">
    {{.Text}}
  </text>
  {{end}}

  {{range .Rows}}
    {{range .Cells}}
    <text x="{{.TextX}}" y="{{.TextY}}" text-anchor="{{.Anchor}}">
      {{.Text}}
    </text>
    {{end}}
  {{end}}
</svg>

The complete example is in templates/report.svg.

Template root data
Field Meaning
.Width, .Height Logical SVG dimensions
.RowCount, .ColCount Report dimensions
.Headers Header cells with geometry
.Columns Resolved query-column metadata
.Rows Rows and cells with geometry and formatted values
.Data Caller-provided metadata such as title or date
.Layout Effective layout values

Available helpers: add, sub, mul, div, mod, even, odd, lower, upper, trim, and default.

Template sources

embed.FS or any fs.FS
Template: rtisvg.Template{
    FS:   templates,
    Path: "templates/report.svg",
},
Local file
Template: rtisvg.FileTemplate("./templates/report.svg"),

SQL and security boundary

rtisvg is not a sandbox for arbitrary SQL.

  • The SQL statement must be authored by the application developer.
  • End-user values must be passed through Request.Args and SQL placeholders.
  • Do not accept a raw query from Telegram, HTTP, CLI input, or another untrusted source.
  • By default, the query runs on a dedicated *sql.Conn with SQLite PRAGMA query_only = ON; the previous value is restored afterward.
  • Generated SVG is parsed and rejects active elements, event-handler attributes, XML directives, and external references.
  • The SVG template itself is trusted application code. Data returned by SQL is treated as untrusted text.

See SECURITY.md for the complete model.

PNG renderer

The default renderer is pure Go. It uses oksvg and rasterx for SVG geometry and a bundled Go font for table text, including common Latin and Cyrillic characters.

The renderer intentionally targets report-style SVG rather than a browser-complete SVG implementation. Supported templates should prefer:

  • svg, g, rect, line, circle, ellipse, polygon/path geometry;
  • solid fills, strokes, opacity, simple gradients supported by oksvg;
  • text and basic tspan with numeric x and y;
  • translate, uniform positive scale, and equivalent non-skewed matrix transforms for text.

Not supported for text: rotation, skew, browser CSS layout, remote web fonts, foreignObject, JavaScript, animation, or external resources.

A different renderer can be injected with RenderWith or NewWithRasterizer without changing SQL/template code.

SVG-only rendering

result, err := rtisvg.RenderSVG(ctx, db, request)

This executes the query and template but skips PNG generation.

Limits

Defaults protect the process from accidental oversized reports:

  • 10,000 rows;
  • 4 MiB template;
  • 16 MiB generated SVG;
  • 64 MiB generated PNG;
  • 32 million raster pixels after scaling.

Override them through Request.Limits. A negative MaxRows disables the row-count limit.

License

MIT. See LICENSE.

Copyright (c) 2026 X07.IT.

Documentation

Overview

Package rtisvg renders rows returned by an SQL query into an SVG template and optionally rasterizes the result to PNG.

The package accepts an already-open *sql.DB. It does not choose a database driver, open a database, or manage application credentials.

Project: https://github.com/x07-it/rtisvg

Made with ❤️ by Xenon007 for X07.IT.

Index

Constants

This section is empty.

Variables

View Source
var (
	ErrNilDatabase       = errors.New("rtisvg: nil database")
	ErrEmptyQuery        = errors.New("rtisvg: empty SQL query")
	ErrInvalidTemplate   = errors.New("rtisvg: invalid template")
	ErrInvalidColumns    = errors.New("rtisvg: invalid columns")
	ErrInvalidLayout     = errors.New("rtisvg: invalid layout")
	ErrUnsafeSVG         = errors.New("rtisvg: unsafe SVG")
	ErrLimitExceeded     = errors.New("rtisvg: limit exceeded")
	ErrNilRasterizer     = errors.New("rtisvg: nil rasterizer")
	ErrRasterUnavailable = errors.New("rtisvg: PNG rasterizer unavailable in this build")
)

Functions

This section is empty.

Types

type Align

type Align string

Align controls horizontal cell alignment.

const (
	AlignLeft   Align = "left"
	AlignCenter Align = "center"
	AlignRight  Align = "right"
)

type CellData

type CellData struct {
	RowIndex    int
	ColumnIndex int
	ColumnName  string
	Text        string
	Raw         any
	Align       Align
	Anchor      string

	X      float64
	Y      float64
	Width  float64
	Height float64
	TextX  float64
	TextY  float64
}

CellData describes one query value and its computed cell geometry.

type Column

type Column struct {
	// Name optionally asserts the expected SQL column name.
	Name string
	// Header is displayed in the table header. An empty value uses the SQL name.
	Header string
	// Align defaults to AlignLeft.
	Align Align
	// Width is the logical SVG width. Zero shares the remaining width equally.
	Width float64
	// Formatter overrides the default SQL value formatter.
	Formatter FormatFunc
}

Column customizes one query column. Columns are positional and must match the order returned by the SELECT statement.

type ColumnData

type ColumnData struct {
	Index   int
	Name    string
	Header  string
	Align   Align
	Anchor  string
	X       float64
	Width   float64
	CenterX float64
	Right   float64
}

ColumnData contains computed geometry for a query column.

type FormatFunc

type FormatFunc func(value any) (string, error)

FormatFunc converts a scanned SQL value into text for the SVG template.

func TimeFormat

func TimeFormat(layout string) FormatFunc

TimeFormat returns a formatter for time.Time values. Nil values remain empty.

type HeaderData

type HeaderData struct {
	Index  int
	Name   string
	Text   string
	Align  Align
	Anchor string

	X      float64
	Y      float64
	Width  float64
	Height float64
	TextX  float64
	TextY  float64
}

HeaderData describes one rendered header cell.

type Layout

type Layout struct {
	Width        float64
	PaddingX     float64
	Top          float64
	HeaderHeight float64
	RowHeight    float64
	Bottom       float64
	CellPadding  float64
}

Layout controls generated table geometry in logical SVG pixels.

type Limits

type Limits struct {
	MaxRows          int
	MaxTemplateBytes int64
	MaxSVGBytes      int
	MaxPNGBytes      int
	// MaxPixels limits Width*Height after PNG scaling.
	MaxPixels int64
}

Limits protects a process from unexpectedly large reports. Zero uses the package default. A negative MaxRows disables only the row-count limit.

type PNGOptions

type PNGOptions struct {
	// Scale multiplies PNG pixel dimensions. Zero means 1.
	Scale float64
	// Background is an optional SVG/CSS color. Empty means transparent.
	Background string
}

PNGOptions controls raster output.

type RasterizeOptions

type RasterizeOptions struct {
	Width      int
	Height     int
	Scale      float64
	Background string
}

RasterizeOptions are supplied to a Rasterizer after the SVG has been rendered.

type Rasterizer

type Rasterizer interface {
	Rasterize(ctx context.Context, svg []byte, options RasterizeOptions) ([]byte, error)
}

Rasterizer converts a completed SVG document to PNG.

type Renderer

type Renderer struct {
	// contains filtered or unexported fields
}

Renderer renders reports with a configurable PNG rasterizer.

func New

func New() *Renderer

New creates a renderer using the package's pure-Go PNG implementation.

func NewWithRasterizer

func NewWithRasterizer(rasterizer Rasterizer) *Renderer

NewWithRasterizer creates a renderer with a caller-supplied rasterizer.

func (*Renderer) Render

func (r *Renderer) Render(ctx context.Context, db *sql.DB, req Request) (Result, error)

Render creates SVG and PNG output.

type Request

type Request struct {
	Query string
	Args  []any

	// Headers is the compact positional form. It is mutually exclusive with Columns.
	Headers []string
	// Columns is the extended positional form. It is mutually exclusive with Headers.
	Columns []Column

	Template Template
	Data     map[string]any
	Layout   Layout
	PNG      PNGOptions
	Limits   Limits

	// DisableQueryOnly skips the SQLite PRAGMA query_only guard. It should be used
	// only for drivers that do not implement that SQLite pragma.
	DisableQueryOnly bool
}

Request describes one report render.

type Result

type Result struct {
	PNG []byte
	SVG []byte

	// Width and Height are logical SVG dimensions.
	Width  int
	Height int
	// PNGWidth and PNGHeight are raster pixel dimensions. They are zero for RenderSVG.
	PNGWidth  int
	PNGHeight int

	Rows        int
	Columns     int
	ColumnNames []string
}

Result contains both reusable vector output and Telegram-friendly PNG output.

func Render

func Render(ctx context.Context, db *sql.DB, req Request) (Result, error)

Render creates SVG and PNG output using the default pure-Go rasterizer.

func RenderSVG

func RenderSVG(ctx context.Context, db *sql.DB, req Request) (Result, error)

RenderSVG executes the query and template but skips PNG rasterization.

func RenderWith

func RenderWith(ctx context.Context, db *sql.DB, req Request, rasterizer Rasterizer) (Result, error)

RenderWith creates SVG and PNG output using a caller-supplied rasterizer.

type RowData

type RowData struct {
	Index   int
	Number  int
	Y       float64
	Height  float64
	CenterY float64
	Cells   []CellData
}

RowData describes one query row.

type Template

type Template struct {
	FS   fs.FS
	Path string
}

Template identifies an SVG Go template in an fs.FS.

func FileTemplate

func FileTemplate(path string) Template

FileTemplate creates a Template for one file on the local filesystem.

type TemplateData

type TemplateData struct {
	Width  float64
	Height float64

	RowCount int
	ColCount int

	Headers []HeaderData
	Columns []ColumnData
	Rows    []RowData
	Data    map[string]any
	Layout  Layout
}

TemplateData is the root object available inside an SVG Go template.

Directories

Path Synopsis
internal

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL