Skip to content

Commit

Permalink
Merge ec88ea2 into 01a68db
Browse files Browse the repository at this point in the history
  • Loading branch information
oliverpool committed Aug 14, 2020
2 parents 01a68db + ec88ea2 commit c3ac15e
Show file tree
Hide file tree
Showing 2 changed files with 134 additions and 1 deletion.
71 changes: 71 additions & 0 deletions image.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
package canvas

import (
"bytes"
"image"
"image/jpeg"
"image/png"
"io"
)

// JPEGImage gives access to the raw bytes
type JPEGImage interface {
image.Image
JPEGBytes() []byte
}

type jpegImage struct {
bufferedImage
}

func (i jpegImage) JPEGBytes() []byte {
return i.bytes
}

// NewJPEGImage parses a reader to later give access to the JPEG raw bytes
func NewJPEGImage(r io.Reader) (JPEGImage, error) {
bi, err := newBufferedImage(jpeg.Decode, r)
if err != nil {
return nil, err
}
return jpegImage{bi}, nil
}

// PNGImage gives access to the raw bytes
type PNGImage interface {
image.Image
PNGBytes() []byte
}

type pngImage struct {
bufferedImage
}

func (i pngImage) PNGBytes() []byte {
return i.bytes
}

// NewPNGImage parses a reader to later give access to the PNG raw bytes
func NewPNGImage(r io.Reader) (PNGImage, error) {
bi, err := newBufferedImage(png.Decode, r)
if err != nil {
return nil, err
}
return pngImage{bi}, nil
}

// bufferedImage is a generic struct for holding specific decoders
type bufferedImage struct {
image.Image
bytes []byte
}

func newBufferedImage(decode func(io.Reader) (image.Image, error), r io.Reader) (bufferedImage, error) {
var buffer bytes.Buffer
r = io.TeeReader(r, &buffer)
img, err := decode(r)
return bufferedImage{
Image: img,
bytes: buffer.Bytes(),
}, err
}
64 changes: 63 additions & 1 deletion pdf/renderer.go
Original file line number Diff line number Diff line change
Expand Up @@ -302,6 +302,7 @@ type pdfStream struct {
const (
pdfFilterASCII85 pdfFilter = "ASCII85Decode"
pdfFilterFlate pdfFilter = "FlateDecode"
pdfFilterDCT pdfFilter = "DCTDecode"
)

func (w *pdfWriter) writeVal(i interface{}) {
Expand Down Expand Up @@ -376,6 +377,8 @@ func (w *pdfWriter) writeVal(i interface{}) {
}
}

// FIXME: multiple filters must be applied in reverse order
// For example, data encoded using LZW and ASCII base-85 encoding (in that order) shall be decoded using the following entry in the stream dictionary: EXAMPLE 2/Filter [ /ASCII85Decode /LZWDecode ]
b := v.stream
for _, filter := range filters {
var b2 bytes.Buffer
Expand All @@ -384,12 +387,19 @@ func (w *pdfWriter) writeVal(i interface{}) {
w := ascii85.NewEncoder(&b2)
w.Write(b)
w.Close()
b = b2.Bytes()
break
case pdfFilterFlate:
w := zlib.NewWriter(&b2)
w.Write(b)
w.Close()
b = b2.Bytes()
break
case pdfFilterDCT:
// This filter is used by JPEG images
// we consider that the buffer is already encoded
break
}
b = b2.Bytes()
}

v.dict["Length"] = len(b)
Expand Down Expand Up @@ -939,7 +949,20 @@ func (w *pdfPageWriter) DrawImage(img image.Image, enc canvas.ImageEncoding, m c
fmt.Fprintf(w, " %v %v %v %v %v %v cm /%v Do Q", dec(m[0][0]), dec(m[1][0]), dec(m[0][1]), dec(m[1][1]), dec(m[0][2]), dec(m[1][2]), name)
}

func imageConfig(img image.Image) image.Config {
bounds := img.Bounds()
return image.Config{
ColorModel: img.ColorModel(),
Width: bounds.Dx(),
Height: bounds.Dy(),
}
}

func (w *pdfPageWriter) embedImage(img image.Image, enc canvas.ImageEncoding) pdfName {
if i, ok := img.(canvas.JPEGImage); ok {
fmt.Println("JPG")
return w.embedJpeg(i.JPEGBytes(), imageConfig(i))
}
size := img.Bounds().Size()
sp := img.Bounds().Min // starting point
b := make([]byte, size.X*size.Y*3)
Expand Down Expand Up @@ -1002,6 +1025,45 @@ func (w *pdfPageWriter) embedImage(img image.Image, enc canvas.ImageEncoding) pd
return name
}

func (w *pdfPageWriter) embedJpeg(img []byte, config image.Config) pdfName {
dict := pdfDict{
"Type": pdfName("XObject"),
"Subtype": pdfName("Image"),
"Width": config.Width,
"Height": config.Height,

// "ColorSpace": pdfName("DeviceRGB"), // cs
"BitsPerComponent": 8, // bpc
// "Interpolate": true,
// "Filter": pdfFilterDCT, // f
"Filter": pdfFilterDCT, // f
}

switch config.ColorModel {
case color.GrayModel:
dict["ColorSpace"] = pdfName("DeviceGray")
case color.YCbCrModel:
dict["ColorSpace"] = pdfName("DeviceRGB")
case color.CMYKModel:
dict["ColorSpace"] = pdfName("DeviceCMYK")
dict["Decode"] = pdfArray([]interface{}{1, 0, 1, 0, 1, 0, 1, 0})
default:
panic("image JPEG buffer has unsupported color space: " + fmt.Sprint(config.ColorModel))
}

ref := w.pdf.writeObject(pdfStream{
dict: dict,
stream: img,
})

if _, ok := w.resources["XObject"]; !ok {
w.resources["XObject"] = pdfDict{}
}
name := pdfName(fmt.Sprintf("Im%d", len(w.resources["XObject"].(pdfDict))))
w.resources["XObject"].(pdfDict)[name] = ref
return name
}

func (w *pdfPageWriter) getOpacityGS(a float64) pdfName {
if name, ok := w.graphicsStates[a]; ok {
return name
Expand Down

0 comments on commit c3ac15e

Please sign in to comment.