Documentation
¶
Overview ¶
Package convert turns pictures into a PDF and a PDF back into pictures.
Everything either side of the PDF was already here: github.com/go-pdfkit/pdfkit writes one and can place an image on a page, and github.com/go-pdfkit/render draws a page into a raster. What was missing was the join — which is why this package is wiring and codecs rather than a new engine.
Formats ¶
Reading: PNG, JPEG and GIF from the standard library; BMP, TIFF and WebP from golang.org/x/image. Writing: PNG, JPEG, GIF, BMP and TIFF.
⛔ There is no WebP ENCODER in pure Go, so FromPDF refuses "webp" by name instead of quietly writing something else. A converter that answers a request it did not carry out is worse than one that says no.
What decides the page ¶
A picture has pixels and a DPI; a PDF page has points. ToPDF sizes each page to its picture at the DPI given, so a 300-dpi scan comes back the size of the paper it came off. Asking for a fixed page size instead fits the picture inside it, centred, never cropping and never stretching: see Options.Page.
Pure Go ¶
CGO-free, including GOOS=js. No C anywhere in the chain.
Index ¶
- Constants
- Variables
- func ArchiveToPDF(src []byte, opt Options) ([]byte, error)
- func Bundle(w io.Writer, files map[string][]byte) error
- func Decode(r io.Reader) (image.Image, string, error)
- func DecodeBytes(b []byte) (image.Image, string, error)
- func Encoders() []string
- func LooksLikeSVG(b []byte) bool
- func PageSizes() []string
- func ParsePageSize(s string) (pdfkit.PageSize, error)
- func RasterizeSVG(doc []byte, opt Options) (image.Image, error)
- func ReadersToPDF(rs []io.Reader, opt Options) ([]byte, error)
- func Redraw(pdf []byte, opt RedrawOptions) ([]byte, error)
- func SVGToPDF(doc []byte, opt Options) ([]byte, error)
- func ToArchive(w io.Writer, pdf []byte, opt RasterOptions, title string) error
- func ToPDF(images []image.Image, opt Options) ([]byte, error)
- func WriteTo(w io.Writer, images []image.Image, opt Options) error
- type Encoder
- type Options
- type Page
- type RasterOptions
- type RedrawOptions
- type ScannerEffect
Constants ¶
const DefaultMaxPixels = 100_000_000
DefaultMaxPixels is how large a picture Decode will read when no ceiling is given. A hundred megapixels is A4 at a thousand dots to the inch.
⛔ Said in bytes, because "a hundred megapixels" does not tell anybody what it permits: a decoded picture is four bytes a pixel, so this default lets a file of a few dozen bytes cost FOUR HUNDRED MEGABYTES. That is bounded, which is the property that matters and the one this package did not have — but a service taking pictures from strangers should set Options.MaxPixels to what its own pages actually need rather than inherit this.
Variables ¶
var ( // MaxArchiveEntries is how many files an archive may hold. // // It bounds an allocation made before anything is read, the same way // go-odf/odf's does: a ZIP's central directory costs about forty-six bytes // an entry, so a small file can declare a great many. MaxArchiveEntries = 4096 // MaxArchiveEntryBytes is how large any one entry may be once opened, and // MaxArchiveBytes how much the whole archive may come to. One entry under // the ceiling says nothing about a thousand of them. MaxArchiveEntryBytes uint64 = 128 << 20 MaxArchiveBytes uint64 = 512 << 20 )
An archive of pictures is what a comic book is: a ZIP whose entries are the pages, in the order their NAMES put them. CBZ, CBR's zipped cousin, and "here are the scans in a zip" are all the same thing. ⛔ Variables rather than constants, and that is not a style choice: a ceiling of half a gigabyte cannot be reached by a test that has to run in a second, so a constant here would be a guard nobody could exercise — and this package has already shipped two of those. A test lowers them; nothing else should.
Functions ¶
func ArchiveToPDF ¶ added in v0.4.0
ArchiveToPDF lays the pictures in a ZIP onto pages, in the order their names put them.
⛔ The ORDER is the whole job. A comic archive names its pages and nothing else records the sequence, so "page10.jpg" has to follow "page2.jpg" — which a byte-wise sort does not do, and which is the single defect every naive reader of this format has. See naturalLess.
Entries that are not pictures are skipped rather than refused: real archives carry a ComicInfo.xml, a thumbnail, a __MACOSX folder, a readme.
func Bundle ¶ added in v0.4.0
Bundle packs whole files into a ZIP, unchanged.
⛔ It converts NOTHING, and that is the point: it is the one tool here whose job is to leave its input alone. Several PDFs that have to travel together — a submission, a set of invoices, a chapter each — are a packaging problem, not a conversion one, and running them through a renderer to put them in a zip would lose every byte of what made them worth sending.
The names are the ones given, with their directories stripped: an archive whose entries are absolute paths from somebody's disk tells a stranger where the files lived.
func Decode ¶
Decode reads a picture and says what format it turned out to be.
⛔ It reads the CONTENT. A file's name is a claim its bytes do not have to honour: a scanner that writes JPEG into "page.png" is ordinary, and a converter that believed the extension would hand the encoder the wrong picture or refuse a file it can read perfectly well. It refuses a picture of more than DefaultMaxPixels pixels, and it refuses it by its HEADER — see decodeWithin for why that distinction is the guard.
func DecodeBytes ¶
DecodeBytes is Decode over a slice.
func Encoders ¶
func Encoders() []string
Encoders names every format FromPDF can write, in a stable order.
func LooksLikeSVG ¶ added in v0.4.0
LooksLikeSVG says whether these bytes are an SVG document.
⛔ By their CONTENT, like every other format this package reads. It is more than a prefix test because an SVG legitimately begins with an XML declaration, a doctype, a comment or a byte-order mark, and a file that opens with any of those is still an SVG — refusing it for want of a "<svg" in the first four bytes would refuse most of what Inkscape writes.
func PageSizes ¶
func PageSizes() []string
PageSizes names every paper size this package knows, in a stable order.
func ParsePageSize ¶
ParsePageSize reads a paper size: a name such as "a4", optionally with "-landscape", or "WIDTHxHEIGHT" in points.
⛔ "" is NOT an error and NOT a default size: it means "size each page to its own picture", which is a different instruction from "use A4". A set of scans of different sizes must not all be forced onto one paper because the caller said nothing.
func RasterizeSVG ¶ added in v0.4.0
RasterizeSVG draws an SVG and hands back the picture.
func ReadersToPDF ¶
ReadersToPDF decodes each reader and lays the pictures out as ToPDF does. The format of each is decided by its CONTENT, never by a name.
func Redraw ¶ added in v0.3.0
func Redraw(pdf []byte, opt RedrawOptions) ([]byte, error)
Redraw draws every page, changes the pixels, and lays the results back into a new PDF.
⛔ IT RASTERISES. What comes out has no text in it: no selection, no search, no copy, no screen reader, and a file many times larger. That is not a shortcoming of this implementation — changing the colours a page is PAINTED in means painting it — but it is a thing a caller has to have decided on purpose, which is why the function is named for what it does rather than for what it is for.
Where the operation can be done without redrawing, it belongs somewhere else: rotating, cropping, stamping and reordering are github.com/go-pdfkit/ops, and they keep the text.
func SVGToPDF ¶ added in v0.4.0
SVGToPDF draws an SVG document and lays it on a page.
⛔ IT RASTERISES. An SVG is vector and so is a PDF, so a reader could reasonably expect the paths to survive — and they do not. Turning one vector language into another means reimplementing a renderer's worth of semantics (gradients in two unit systems, clip paths, stroke joins, transforms, text layout), and a half-done translation produces a file that opens and is quietly wrong. Drawing it is a complete answer that is honest about its own resolution.
Options.DPI is what that resolution is: at 72 one SVG user unit is one point, and the drawing comes out the size the document says.
func ToArchive ¶ added in v0.4.0
ToArchive draws the pages and writes them into a ZIP: one picture per page, named so that they come back in order, with a ComicInfo.xml beside them.
⛔ The ComicInfo.xml is not decoration. It is what Komga, Kavita and Calibre read to know how many pages a book has and what it is called; without it a reader shows the archive as an untitled pile and has to open every entry to count it. A CBZ with no metadata is a CBZ a library will not shelve. The title, when given, goes into that metadata; it is the book's name, not a filename.
Types ¶
type Encoder ¶
Encoder is a picture format this package can WRITE. Reading is wider than writing — WebP decodes and does not encode — so the two are separate tables rather than one list with exceptions in it.
type Options ¶
type Options struct {
// DPI is how many pixels of the picture go to an inch of paper. Zero means
// 72, at which one pixel is one point and a picture comes out at the size
// a PDF viewer calls 100%.
//
// It is what turns pixels into a page: a 2480x3508 scan at 300 is A4, and
// the same scan at 72 is a page a metre tall. There is no right answer this
// package could pick for you, only a sane default.
DPI float64
// Page, when set, is the page size every picture is placed on, and the
// picture is FITTED inside it: scaled down to fit if it is too big, centred
// either way, never cropped and never stretched out of proportion.
//
// When it is the zero value each page is sized to its own picture, which is
// what a set of scans of different sizes needs.
Page pdfkit.PageSize
// Margin is how much paper is left around a fitted picture, in points. It
// is only consulted when Page is set, because a page sized to its picture
// has no room to leave.
Margin float64
// Title goes in the document information dictionary.
Title string
// MaxPixels refuses a picture larger than this, so that a file cannot
// decide how much memory this package uses. Zero means a hundred million,
// which is A4 at a thousand dots to the inch and well past any scanner.
MaxPixels int
}
Options says how pictures become pages.
type Page ¶
type Page struct {
Number int // one-based, as the file numbers it
Format string // the format actually written
Data []byte
}
Page is one drawn page and the bytes it encoded to.
func FromPDF ¶
func FromPDF(pdf []byte, opt RasterOptions) ([]Page, error)
FromPDF draws the pages of a PDF and encodes each one.
func FromPDFReader ¶
func FromPDFReader(r io.Reader, opt RasterOptions) ([]Page, error)
FromPDFReader is FromPDF over a reader, for a file on disk.
type RasterOptions ¶
type RasterOptions struct {
// DPI is dots per inch. Zero means 150, which is legible on a screen and
// not so large that a hundred-page file fills a disk.
DPI float64
// Pages selects which pages to draw, one-based, in the order given. Nil
// means every page in the file's own order.
Pages []int
// Format is what to encode as: png, jpeg, gif, bmp or tiff. Empty means
// png, which is lossless and what a page of text should be.
Format string
// Quality is the JPEG quality, 1 to 100. Zero means 85. It is ignored by
// every other format.
Quality int
// Password opens an encrypted file.
Password string
// MaxPixels refuses a page that would come out larger than this. Zero
// leaves it to the renderer, whose own default is a little over A4 at 600
// dots to the inch.
MaxPixels int
}
RasterOptions says how pages become pictures.
type RedrawOptions ¶ added in v0.3.0
type RedrawOptions struct {
RasterOptions
// Title goes in the new document's information dictionary.
Title string
// Greyscale drops the colour.
Greyscale bool
// Invert turns light into dark. It is what a reader wants at night, and
// what a plotter wants of a dark-background figure.
Invert bool
// Brightness shifts every channel, from -1 (black) to +1 (white). Zero
// leaves it alone.
Brightness float64
// Contrast multiplies the distance from mid-grey: 1 leaves it alone, 2
// doubles it, 0.5 halves it. Zero means 1 — ⛔ a zero that meant "no
// contrast at all" would turn every page into a flat grey rectangle for
// every caller who set only the other fields.
Contrast float64
// Background is the colour the page is painted on. nil leaves the
// renderer's own white.
//
// ⛔ It is handed to the RENDERER rather than composited afterwards, and
// the difference is the whole option. A first version painted the colour
// under the finished raster — which does nothing at all, because the
// renderer has already filled the page with opaque white and there is no
// transparency left to show through. The option was documented, shipped,
// and had no effect; a screenshot found it, a byte count did not.
Background *color.RGBA
// Scanner, when set, makes the page look like it went through one.
Scanner *ScannerEffect
}
RedrawOptions says what to do to the pixels. Everything is off by default: the zero value redraws the pages unchanged, which is `rasterize-pdf`.
type ScannerEffect ¶ added in v0.3.0
type ScannerEffect struct {
// Skew is how far the sheet sits off square, in degrees. A tenth to a
// degree is what a careless hand produces.
Skew float64
// Noise is how much grain, from 0 to 1.
Noise float64
// Fade lightens the page, as a worn lamp does, from 0 to 1.
Fade float64
// Seed makes the grain reproducible. ⛔ Zero means a FIXED seed, not a
// random one: two runs over the same file must produce the same bytes, or
// nothing downstream can be compared, cached or checksummed — and an
// effect that is different every time is one nobody can test.
Seed uint64
}
ScannerEffect is the small damage a flatbed does: the sheet is never quite straight, the lamp is never quite even, and the sensor adds grain.