Skip to content
PDF to Image Custom Encoding

PDF to Image Custom Encoding

UniPDF decodes most image filters itself, but not JPEG2000. core.JPXEncoder is a stub: every method returns core.ErrNoJPXDecode, so a page whose images use JPXDecode cannot be rasterized until you supply a decoder. RegisterCustomStreamEncoder is the hook for that, and it works for any filter name, not just JPX.

Despite the page title, the custom encoder affects the input side. The rendered output is still written by the image device, as PNG or JPEG.

Doing it

core.RegisterCustomStreamEncoder(core.StreamEncodingFilterNameJPX, jpeg2k.NewCustomJPXEncoder())

device := render.NewImageDevice()
if err := device.RenderToPath(page, "page.png"); err != nil {
    return err
}

Registration is global and keyed by filter name, so one call before you start reading covers every stream in every document. NewEncoderFromStream checks the registry before its own switch statement, which means a registered encoder also overrides a filter UniPDF does support, Flate included. Register only the filters you mean to take over.

The value you pass has to implement core.StreamEncoder: GetFilterName, MakeDecodeParams, MakeStreamDict, UpdateParams, EncodeBytes, DecodeBytes and DecodeStream. Decoding only needs DecodeStream and DecodeBytes to work; the example’s EncodeBytes returns an error, which is fine because rendering never encodes.

DecodeStream receives the whole stream object, not just its bytes, and that matters for JPX. A JPEG2000 codestream carries its own color space, but when the image dictionary has a /ColorSpace entry, the PDF spec says the dictionary wins. The example reads that entry first and uses it to decide how to interpret the samples, then delegates to DecodeBytes.

What comes back is raw samples, not a file. The example returns one, three or four components per pixel at the image’s bit depth, row by row, with no header, matching the /ColorSpace and /BitsPerComponent the image dictionary already declares.

Limitations

The example decoder wraps ImageMagick through gopkg.in/gographics/imagick.v2, so it needs cgo and an ImageMagick installation. That is why the filename ends in _cgo. It handles gray, RGB, sRGB and CMYK at 8 or 16 bits per component and returns an error for any other color space or bit depth. An indexed PDF color space is special-cased: the image is converted to gray and negated before the samples are read out.

Registration is process-wide and there is no call to remove an entry. In a program that reads documents of mixed provenance, whatever you register applies to all of them.

Run the example

pdf_image_render_custom_encoder_cgo.go registers jpeg2k.NewCustomJPXEncoder and then renders exactly like the plain example. The interesting file is jpeg2k/lib_jpeg2k_encoder.go, which holds the CustomJPXEncoder implementation.

git clone https://github.com/unidoc/unipdf-examples.git
cd unipdf-examples/render
go run pdf_image_render_custom_encoder_cgo.go OUTPUT_DIR INPUT.pdf

If this is your first time using UniPDF, follow the getting started guide to create an API key and set up your development environment.

View the full source
Last updated on