From 5236747cc6a1984904b8ac2ad8a2c68d89fed01f Mon Sep 17 00:00:00 2001 From: Adrian-George Bostan Date: Tue, 22 Jan 2019 19:11:14 +0200 Subject: [PATCH 01/10] Fix colorspace of scaled image --- pdf/model/optimize/image_ppi.go | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/pdf/model/optimize/image_ppi.go b/pdf/model/optimize/image_ppi.go index f337624f..4d969a8d 100644 --- a/pdf/model/optimize/image_ppi.go +++ b/pdf/model/optimize/image_ppi.go @@ -55,7 +55,8 @@ func scaleImage(stream *core.PdfObjectStream, scale float64) error { if err != nil { return err } - xImg.SetImage(i, xImg.ColorSpace) + + xImg.SetImage(i, model.NewPdfColorspaceDeviceRGB()) xImg.ToPdfObject() return nil } From 83052bbc623ead84507a9eaa8665d9b6269b015f Mon Sep 17 00:00:00 2001 From: Adrian-George Bostan Date: Tue, 22 Jan 2019 20:11:15 +0200 Subject: [PATCH 02/10] Account 1-bit/2-bit color images --- pdf/core/encoding.go | 6 ++++-- pdf/model/colorspace.go | 3 ++- pdf/model/image.go | 6 ++++-- 3 files changed, 10 insertions(+), 5 deletions(-) diff --git a/pdf/core/encoding.go b/pdf/core/encoding.go index 89fe27aa..98dc8ad2 100644 --- a/pdf/core/encoding.go +++ b/pdf/core/encoding.go @@ -27,6 +27,7 @@ import ( gocolor "image/color" "image/jpeg" "io" + "math" // Need two slightly different implementations of LZW (EarlyChange parameter). lzw0 "compress/lzw" @@ -1047,8 +1048,9 @@ func (enc *DCTEncoder) EncodeBytes(data []byte) ([]byte, error) { val := uint16(data[i])<<8 | uint16(data[i+1]) c = gocolor.Gray16{val} } else { - val := uint8(data[i] & 0xff) - c = gocolor.Gray{val} + // Account 1-bit/2-bit color images + val := uint32(data[i]) * 255 / uint32(math.Pow(2, float64(enc.BitsPerComponent))-1) + c = gocolor.Gray{uint8(val & 0xff)} } } else if enc.ColorComponents == 3 { if enc.BitsPerComponent == 16 { diff --git a/pdf/model/colorspace.go b/pdf/model/colorspace.go index a82006fe..17cc66a0 100644 --- a/pdf/model/colorspace.go +++ b/pdf/model/colorspace.go @@ -300,7 +300,8 @@ func (cs *PdfColorspaceDeviceGray) ImageToRGB(img Image) (Image, error) { var rgbSamples []uint32 for i := 0; i < len(samples); i++ { - grayVal := samples[i] + // Account 1-bit/2-bit color images + grayVal := samples[i] * 255 / uint32(math.Pow(2, float64(img.BitsPerComponent))-1) rgbSamples = append(rgbSamples, grayVal, grayVal, grayVal) } rgbImage.BitsPerComponent = 8 diff --git a/pdf/model/image.go b/pdf/model/image.go index 1577dc88..4318273a 100644 --- a/pdf/model/image.go +++ b/pdf/model/image.go @@ -13,6 +13,7 @@ import ( _ "image/gif" _ "image/png" "io" + "math" "github.com/unidoc/unidoc/common" . "github.com/unidoc/unidoc/pdf/core" @@ -170,8 +171,9 @@ func (img *Image) ToGoImage() (goimage.Image, error) { val := uint16(samples[i])<<8 | uint16(samples[i+1]) c = gocolor.Gray16{val} } else { - val := uint8(samples[i] & 0xff) - c = gocolor.Gray{val} + // Account 1-bit/2-bit color images + val := samples[i] * 255 / uint32(math.Pow(2, float64(img.BitsPerComponent))-1) + c = gocolor.Gray{uint8(val & 0xff)} } } else if img.ColorComponents == 3 { if img.BitsPerComponent == 16 { From 195105ee565d6d9d4188c39f88dd586f893122c7 Mon Sep 17 00:00:00 2001 From: Adrian-George Bostan Date: Wed, 23 Jan 2019 18:57:01 +0200 Subject: [PATCH 03/10] Improve code documentation --- pdf/core/encoding.go | 2 +- pdf/model/colorspace.go | 2 +- pdf/model/image.go | 2 +- pdf/model/optimize/image_ppi.go | 2 ++ 4 files changed, 5 insertions(+), 3 deletions(-) diff --git a/pdf/core/encoding.go b/pdf/core/encoding.go index 98dc8ad2..7d07c1e7 100644 --- a/pdf/core/encoding.go +++ b/pdf/core/encoding.go @@ -1048,7 +1048,7 @@ func (enc *DCTEncoder) EncodeBytes(data []byte) ([]byte, error) { val := uint16(data[i])<<8 | uint16(data[i+1]) c = gocolor.Gray16{val} } else { - // Account 1-bit/2-bit color images + // Account for 1-bit/2-bit color images. val := uint32(data[i]) * 255 / uint32(math.Pow(2, float64(enc.BitsPerComponent))-1) c = gocolor.Gray{uint8(val & 0xff)} } diff --git a/pdf/model/colorspace.go b/pdf/model/colorspace.go index 17cc66a0..ddab31f0 100644 --- a/pdf/model/colorspace.go +++ b/pdf/model/colorspace.go @@ -300,7 +300,7 @@ func (cs *PdfColorspaceDeviceGray) ImageToRGB(img Image) (Image, error) { var rgbSamples []uint32 for i := 0; i < len(samples); i++ { - // Account 1-bit/2-bit color images + // Account for 1-bit/2-bit color images. grayVal := samples[i] * 255 / uint32(math.Pow(2, float64(img.BitsPerComponent))-1) rgbSamples = append(rgbSamples, grayVal, grayVal, grayVal) } diff --git a/pdf/model/image.go b/pdf/model/image.go index 4318273a..494e468f 100644 --- a/pdf/model/image.go +++ b/pdf/model/image.go @@ -171,7 +171,7 @@ func (img *Image) ToGoImage() (goimage.Image, error) { val := uint16(samples[i])<<8 | uint16(samples[i+1]) c = gocolor.Gray16{val} } else { - // Account 1-bit/2-bit color images + // Account for 1-bit/2-bit color images. val := samples[i] * 255 / uint32(math.Pow(2, float64(img.BitsPerComponent))-1) c = gocolor.Gray{uint8(val & 0xff)} } diff --git a/pdf/model/optimize/image_ppi.go b/pdf/model/optimize/image_ppi.go index 4d969a8d..d61a838a 100644 --- a/pdf/model/optimize/image_ppi.go +++ b/pdf/model/optimize/image_ppi.go @@ -51,11 +51,13 @@ func scaleImage(stream *core.PdfObjectStream, scale float64) error { return fmt.Errorf("optimization is not supported for color space %s", xImg.ColorSpace.String()) } draw.CatmullRom.Scale(newImage, newImage.Bounds(), goimg, goimg.Bounds(), draw.Over, &draw.Options{}) + i, err = model.ImageHandling.NewImageFromGoImage(newImage) if err != nil { return err } + // NewImageFromGoImage always returns a RGBA image. xImg.SetImage(i, model.NewPdfColorspaceDeviceRGB()) xImg.ToPdfObject() return nil From b477c1430f5ef6e0081022e76190e69f0386ce62 Mon Sep 17 00:00:00 2001 From: Adrian-George Bostan Date: Thu, 24 Jan 2019 22:21:51 +0200 Subject: [PATCH 04/10] Add UpdateParams method to StreamEncoder --- pdf/core/encoding.go | 115 +++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 115 insertions(+) diff --git a/pdf/core/encoding.go b/pdf/core/encoding.go index 7d07c1e7..ab1ef230 100644 --- a/pdf/core/encoding.go +++ b/pdf/core/encoding.go @@ -58,6 +58,7 @@ type StreamEncoder interface { GetFilterName() string MakeDecodeParams() PdfObject MakeStreamDict() *PdfObjectDictionary + UpdateParams(params *PdfObjectDictionary) EncodeBytes(data []byte) ([]byte, error) DecodeBytes(encoded []byte) ([]byte, error) @@ -137,6 +138,29 @@ func (enc *FlateEncoder) MakeStreamDict() *PdfObjectDictionary { return dict } +// UpdateParams updates the parameter values of the encoder. +func (enc *FlateEncoder) UpdateParams(params *PdfObjectDictionary) { + predictor, err := GetNumberAsInt64(params.Get("Predictor")) + if err == nil { + enc.Predictor = int(predictor) + } + + bpc, err := GetNumberAsInt64(params.Get("BitsPerComponent")) + if err == nil { + enc.BitsPerComponent = int(bpc) + } + + columns, err := GetNumberAsInt64(params.Get("Width")) + if err == nil { + enc.Columns = int(columns) + } + + colorComponents, err := GetNumberAsInt64(params.Get("ColorComponents")) + if err == nil { + enc.Colors = int(colorComponents) + } +} + // Create a new flate decoder from a stream object, getting all the encoding parameters // from the DecodeParms stream object dictionary entry. func newFlateEncoderFromStream(streamObj *PdfObjectStream, decodeParams *PdfObjectDictionary) (*FlateEncoder, error) { @@ -508,6 +532,34 @@ func (enc *LZWEncoder) MakeStreamDict() *PdfObjectDictionary { return dict } +// UpdateParams updates the parameter values of the encoder. +func (enc *LZWEncoder) UpdateParams(params *PdfObjectDictionary) { + predictor, err := GetNumberAsInt64(params.Get("Predictor")) + if err == nil { + enc.Predictor = int(predictor) + } + + bpc, err := GetNumberAsInt64(params.Get("BitsPerComponent")) + if err == nil { + enc.BitsPerComponent = int(bpc) + } + + columns, err := GetNumberAsInt64(params.Get("Width")) + if err == nil { + enc.Columns = int(columns) + } + + colorComponents, err := GetNumberAsInt64(params.Get("ColorComponents")) + if err == nil { + enc.Colors = int(colorComponents) + } + + earlyChange, err := GetNumberAsInt64(params.Get("EarlyChange")) + if err == nil { + enc.EarlyChange = int(earlyChange) + } +} + // Create a new LZW encoder/decoder from a stream object, getting all the encoding parameters // from the DecodeParms stream object dictionary entry. func newLZWEncoderFromStream(streamObj *PdfObjectStream, decodeParams *PdfObjectDictionary) (*LZWEncoder, error) { @@ -825,6 +877,34 @@ func (enc *DCTEncoder) MakeStreamDict() *PdfObjectDictionary { return dict } +// UpdateParams updates the parameter values of the encoder. +func (enc *DCTEncoder) UpdateParams(params *PdfObjectDictionary) { + colorComponents, err := GetNumberAsInt64(params.Get("ColorComponents")) + if err == nil { + enc.ColorComponents = int(colorComponents) + } + + bpc, err := GetNumberAsInt64(params.Get("BitsPerComponent")) + if err == nil { + enc.BitsPerComponent = int(bpc) + } + + width, err := GetNumberAsInt64(params.Get("Width")) + if err == nil { + enc.Width = int(width) + } + + height, err := GetNumberAsInt64(params.Get("Height")) + if err == nil { + enc.Height = int(height) + } + + quality, err := GetNumberAsInt64(params.Get("Quality")) + if err == nil { + enc.Quality = int(quality) + } +} + // Create a new DCT encoder/decoder from a stream object, getting all the encoding parameters // from the stream object dictionary entry and the image data itself. // TODO: Support if used with other filters [ASCII85Decode FlateDecode DCTDecode]... @@ -1240,6 +1320,10 @@ func (enc *RunLengthEncoder) MakeStreamDict() *PdfObjectDictionary { return dict } +// UpdateParams updates the parameter values of the encoder. +func (enc *RunLengthEncoder) UpdateParams(params *PdfObjectDictionary) { +} + // ASCIIHexEncoder implements ASCII hex encoder/decoder. type ASCIIHexEncoder struct { } @@ -1265,6 +1349,10 @@ func (enc *ASCIIHexEncoder) MakeStreamDict() *PdfObjectDictionary { return dict } +// UpdateParams updates the parameter values of the encoder. +func (enc *ASCIIHexEncoder) UpdateParams(params *PdfObjectDictionary) { +} + func (enc *ASCIIHexEncoder) DecodeBytes(encoded []byte) ([]byte, error) { bufReader := bytes.NewReader(encoded) var inb []byte @@ -1339,6 +1427,10 @@ func (enc *ASCII85Encoder) MakeStreamDict() *PdfObjectDictionary { return dict } +// UpdateParams updates the parameter values of the encoder. +func (enc *ASCII85Encoder) UpdateParams(params *PdfObjectDictionary) { +} + // DecodeBytes decodes byte array with ASCII85. 5 ASCII characters -> 4 raw binary bytes func (enc *ASCII85Encoder) DecodeBytes(encoded []byte) ([]byte, error) { var decoded []byte @@ -1504,6 +1596,10 @@ func (enc *RawEncoder) MakeStreamDict() *PdfObjectDictionary { return MakeDict() } +// UpdateParams updates the parameter values of the encoder. +func (enc *RawEncoder) UpdateParams(params *PdfObjectDictionary) { +} + func (enc *RawEncoder) DecodeBytes(encoded []byte) ([]byte, error) { return encoded, nil } @@ -1537,6 +1633,10 @@ func (enc *CCITTFaxEncoder) MakeStreamDict() *PdfObjectDictionary { return MakeDict() } +// UpdateParams updates the parameter values of the encoder. +func (enc *CCITTFaxEncoder) UpdateParams(params *PdfObjectDictionary) { +} + func (enc *CCITTFaxEncoder) DecodeBytes(encoded []byte) ([]byte, error) { common.Log.Debug("Error: Attempting to use unsupported encoding %s", enc.GetFilterName()) return encoded, ErrNoCCITTFaxDecode @@ -1573,6 +1673,10 @@ func (enc *JBIG2Encoder) MakeStreamDict() *PdfObjectDictionary { return MakeDict() } +// UpdateParams updates the parameter values of the encoder. +func (enc *JBIG2Encoder) UpdateParams(params *PdfObjectDictionary) { +} + func (enc *JBIG2Encoder) DecodeBytes(encoded []byte) ([]byte, error) { common.Log.Debug("Error: Attempting to use unsupported encoding %s", enc.GetFilterName()) return encoded, ErrNoJBIG2Decode @@ -1609,6 +1713,10 @@ func (enc *JPXEncoder) MakeStreamDict() *PdfObjectDictionary { return MakeDict() } +// UpdateParams updates the parameter values of the encoder. +func (enc *JPXEncoder) UpdateParams(params *PdfObjectDictionary) { +} + func (enc *JPXEncoder) DecodeBytes(encoded []byte) ([]byte, error) { common.Log.Debug("Error: Attempting to use unsupported encoding %s", enc.GetFilterName()) return encoded, ErrNoJPXDecode @@ -1807,6 +1915,13 @@ func (enc *MultiEncoder) MakeStreamDict() *PdfObjectDictionary { return dict } +// UpdateParams updates the parameter values of the encoder. +func (enc *MultiEncoder) UpdateParams(params *PdfObjectDictionary) { + for _, encoder := range enc.encoders { + encoder.UpdateParams(params) + } +} + func (enc *MultiEncoder) DecodeBytes(encoded []byte) ([]byte, error) { decoded := encoded var err error From fc21d4820d3a59f947a87efce6a3814276c9ddad Mon Sep 17 00:00:00 2001 From: Adrian-George Bostan Date: Thu, 24 Jan 2019 22:23:05 +0200 Subject: [PATCH 05/10] Set Width, Height and BitsPerComponent safely for XObjectImage --- pdf/model/xobject.go | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/pdf/model/xobject.go b/pdf/model/xobject.go index 8b054f53..96d650b2 100644 --- a/pdf/model/xobject.go +++ b/pdf/model/xobject.go @@ -455,9 +455,14 @@ func (ximg *XObjectImage) SetImage(img *Image, cs PdfColorspace) error { ximg.Stream = encoded // Width, height and bits. - ximg.Width = &img.Width - ximg.Height = &img.Height - ximg.BitsPerComponent = &img.BitsPerComponent + w := img.Width + ximg.Width = &w + + h := img.Height + ximg.Height = &h + + bpc := img.BitsPerComponent + ximg.BitsPerComponent = &bpc // Guess colorspace if not explicitly set. if cs == nil { From c9d9b7dee7cfed159bbc7729f6ddfb2e640ef9c9 Mon Sep 17 00:00:00 2001 From: Adrian-George Bostan Date: Thu, 24 Jan 2019 22:23:54 +0200 Subject: [PATCH 06/10] Add NewGrayImageFromGoImage method --- pdf/model/image.go | 28 ++++++++++++++++++++++++++-- 1 file changed, 26 insertions(+), 2 deletions(-) diff --git a/pdf/model/image.go b/pdf/model/image.go index 494e468f..18bde79f 100644 --- a/pdf/model/image.go +++ b/pdf/model/image.go @@ -223,9 +223,12 @@ type ImageHandler interface { // Read any image type and load into a new Image object. Read(r io.Reader) (*Image, error) - // NewImageFromGoImage load a unidoc Image from a standard Go image structure. + // NewImageFromGoImage loads a RGB unidoc Image from a standard Go image structure. NewImageFromGoImage(goimg goimage.Image) (*Image, error) + // NewGrayImageFromGoImage loads a grayscale unidoc Image from a standard Go image structure. + NewGrayImageFromGoImage(goimg goimage.Image) (*Image, error) + // Compress an image. Compress(input *Image, quality int64) (*Image, error) } @@ -233,7 +236,7 @@ type ImageHandler interface { // DefaultImageHandler is the default implementation of the ImageHandler using the standard go library. type DefaultImageHandler struct{} -// NewImageFromGoImage creates a unidoc Image from a golang Image. +// NewImageFromGoImage creates a new RGB unidoc Image from a golang Image. func (ih DefaultImageHandler) NewImageFromGoImage(goimg goimage.Image) (*Image, error) { // Speed up jpeg encoding by converting to RGBA first. // Will not be required once the golang image/jpeg package is optimized. @@ -271,6 +274,27 @@ func (ih DefaultImageHandler) NewImageFromGoImage(goimg goimage.Image) (*Image, return &imag, nil } +// NewGrayImageFromGoImage creates a new grayscale unidoc Image from a golang Image. +func (ih DefaultImageHandler) NewGrayImageFromGoImage(goimg goimage.Image) (*Image, error) { + b := goimg.Bounds() + m := goimage.NewGray(goimage.Rect(0, 0, b.Dx(), b.Dy())) + draw.Draw(m, m.Bounds(), goimg, b.Min, draw.Src) + + data := []byte{} + for i := 0; i < len(m.Pix); i += 1 { + data = append(data, m.Pix[i]) + } + + imag := Image{} + imag.Width = int64(b.Dx()) + imag.Height = int64(b.Dy()) + imag.BitsPerComponent = 8 + imag.ColorComponents = 1 + imag.Data = data // buf.Bytes() + + return &imag, nil +} + // Read reads an image and loads into a new Image object with an RGB // colormap and 8 bits per component. func (ih DefaultImageHandler) Read(reader io.Reader) (*Image, error) { From 5ea259d0f3a277b1100861cbdf6db910419f8167 Mon Sep 17 00:00:00 2001 From: Adrian-George Bostan Date: Thu, 24 Jan 2019 22:24:34 +0200 Subject: [PATCH 07/10] Update image encoder on scale optimization --- pdf/model/optimize/image_ppi.go | 26 +++++++++++++++++++++++--- 1 file changed, 23 insertions(+), 3 deletions(-) diff --git a/pdf/model/optimize/image_ppi.go b/pdf/model/optimize/image_ppi.go index d61a838a..1b3825d8 100644 --- a/pdf/model/optimize/image_ppi.go +++ b/pdf/model/optimize/image_ppi.go @@ -10,6 +10,8 @@ import ( "image" "math" + goimage "image" + "github.com/unidoc/unidoc/common" "github.com/unidoc/unidoc/pdf/contentstream" "github.com/unidoc/unidoc/pdf/core" @@ -41,24 +43,42 @@ func scaleImage(stream *core.PdfObjectStream, scale float64) error { newW := int(math.RoundToEven(float64(i.Width) * scale)) newH := int(math.RoundToEven(float64(i.Height) * scale)) rect := image.Rect(0, 0, newW, newH) + var newImage draw.Image + var imageHandler func(goimage.Image) (*model.Image, error) + switch xImg.ColorSpace.String() { case "DeviceRGB": newImage = image.NewRGBA(rect) + imageHandler = model.ImageHandling.NewImageFromGoImage case "DeviceGray": newImage = image.NewGray(rect) + imageHandler = model.ImageHandling.NewGrayImageFromGoImage default: return fmt.Errorf("optimization is not supported for color space %s", xImg.ColorSpace.String()) } draw.CatmullRom.Scale(newImage, newImage.Bounds(), goimg, goimg.Bounds(), draw.Over, &draw.Options{}) - i, err = model.ImageHandling.NewImageFromGoImage(newImage) + i, err = imageHandler(newImage) if err != nil { return err } - // NewImageFromGoImage always returns a RGBA image. - xImg.SetImage(i, model.NewPdfColorspaceDeviceRGB()) + // Update image encoder + encoderParams := core.MakeDict() + encoderParams.Set("ColorComponents", core.MakeInteger(int64(i.ColorComponents))) + encoderParams.Set("BitsPerComponent", core.MakeInteger(i.BitsPerComponent)) + encoderParams.Set("Width", core.MakeInteger(i.Width)) + encoderParams.Set("Height", core.MakeInteger(i.Height)) + encoderParams.Set("Quality", core.MakeInteger(100)) + encoderParams.Set("Predictor", core.MakeInteger(1)) + + xImg.Filter.UpdateParams(encoderParams) + + // Update image + if err := xImg.SetImage(i, nil); err != nil { + return err + } xImg.ToPdfObject() return nil } From bf695505b17a00c56daf262bf8b2a932bf0d1ad7 Mon Sep 17 00:00:00 2001 From: Adrian-George Bostan Date: Thu, 24 Jan 2019 22:46:26 +0200 Subject: [PATCH 08/10] Prevent encoding infinite loop --- pdf/core/encoding.go | 16 +++++++++++----- pdf/model/optimize/image_ppi.go | 4 +--- 2 files changed, 12 insertions(+), 8 deletions(-) diff --git a/pdf/core/encoding.go b/pdf/core/encoding.go index ab1ef230..9424b993 100644 --- a/pdf/core/encoding.go +++ b/pdf/core/encoding.go @@ -27,7 +27,6 @@ import ( gocolor "image/color" "image/jpeg" "io" - "math" // Need two slightly different implementations of LZW (EarlyChange parameter). lzw0 "compress/lzw" @@ -1118,9 +1117,17 @@ func (enc *DCTEncoder) EncodeBytes(data []byte) ([]byte, error) { } // Draw the data on the image.. + if enc.BitsPerComponent < 8 { + enc.BitsPerComponent = 8 + } + + bytesPerColor := enc.ColorComponents * enc.BitsPerComponent / 8 + if bytesPerColor < 1 { + bytesPerColor = 1 + } + x := 0 y := 0 - bytesPerColor := enc.ColorComponents * enc.BitsPerComponent / 8 for i := 0; i+bytesPerColor-1 < len(data); i += bytesPerColor { var c gocolor.Color if enc.ColorComponents == 1 { @@ -1128,9 +1135,8 @@ func (enc *DCTEncoder) EncodeBytes(data []byte) ([]byte, error) { val := uint16(data[i])<<8 | uint16(data[i+1]) c = gocolor.Gray16{val} } else { - // Account for 1-bit/2-bit color images. - val := uint32(data[i]) * 255 / uint32(math.Pow(2, float64(enc.BitsPerComponent))-1) - c = gocolor.Gray{uint8(val & 0xff)} + val := uint8(data[i] & 0xff) + c = gocolor.Gray{val} } } else if enc.ColorComponents == 3 { if enc.BitsPerComponent == 16 { diff --git a/pdf/model/optimize/image_ppi.go b/pdf/model/optimize/image_ppi.go index 1b3825d8..f2529db3 100644 --- a/pdf/model/optimize/image_ppi.go +++ b/pdf/model/optimize/image_ppi.go @@ -10,8 +10,6 @@ import ( "image" "math" - goimage "image" - "github.com/unidoc/unidoc/common" "github.com/unidoc/unidoc/pdf/contentstream" "github.com/unidoc/unidoc/pdf/core" @@ -45,7 +43,7 @@ func scaleImage(stream *core.PdfObjectStream, scale float64) error { rect := image.Rect(0, 0, newW, newH) var newImage draw.Image - var imageHandler func(goimage.Image) (*model.Image, error) + var imageHandler func(image.Image) (*model.Image, error) switch xImg.ColorSpace.String() { case "DeviceRGB": From 8edd2fca27f906c08434cacb2427704ff851e043 Mon Sep 17 00:00:00 2001 From: Adrian-George Bostan Date: Fri, 25 Jan 2019 13:29:36 +0200 Subject: [PATCH 09/10] Minor refactoring --- pdf/model/colorspace.go | 1 - pdf/model/image.go | 20 +++++++++----------- pdf/model/optimize/image_ppi.go | 5 ++--- 3 files changed, 11 insertions(+), 15 deletions(-) diff --git a/pdf/model/colorspace.go b/pdf/model/colorspace.go index ddab31f0..2545fcfd 100644 --- a/pdf/model/colorspace.go +++ b/pdf/model/colorspace.go @@ -300,7 +300,6 @@ func (cs *PdfColorspaceDeviceGray) ImageToRGB(img Image) (Image, error) { var rgbSamples []uint32 for i := 0; i < len(samples); i++ { - // Account for 1-bit/2-bit color images. grayVal := samples[i] * 255 / uint32(math.Pow(2, float64(img.BitsPerComponent))-1) rgbSamples = append(rgbSamples, grayVal, grayVal, grayVal) } diff --git a/pdf/model/image.go b/pdf/model/image.go index 18bde79f..f41437ec 100644 --- a/pdf/model/image.go +++ b/pdf/model/image.go @@ -171,7 +171,6 @@ func (img *Image) ToGoImage() (goimage.Image, error) { val := uint16(samples[i])<<8 | uint16(samples[i+1]) c = gocolor.Gray16{val} } else { - // Account for 1-bit/2-bit color images. val := samples[i] * 255 / uint32(math.Pow(2, float64(img.BitsPerComponent))-1) c = gocolor.Gray{uint8(val & 0xff)} } @@ -277,22 +276,21 @@ func (ih DefaultImageHandler) NewImageFromGoImage(goimg goimage.Image) (*Image, // NewGrayImageFromGoImage creates a new grayscale unidoc Image from a golang Image. func (ih DefaultImageHandler) NewGrayImageFromGoImage(goimg goimage.Image) (*Image, error) { b := goimg.Bounds() - m := goimage.NewGray(goimage.Rect(0, 0, b.Dx(), b.Dy())) - draw.Draw(m, m.Bounds(), goimg, b.Min, draw.Src) + m := goimage.NewGray(b) + draw.Draw(m, b, goimg, b.Min, draw.Src) data := []byte{} for i := 0; i < len(m.Pix); i += 1 { data = append(data, m.Pix[i]) } - imag := Image{} - imag.Width = int64(b.Dx()) - imag.Height = int64(b.Dy()) - imag.BitsPerComponent = 8 - imag.ColorComponents = 1 - imag.Data = data // buf.Bytes() - - return &imag, nil + return &Image{ + Width: int64(b.Dx()), + Height: int64(b.Dy()), + BitsPerComponent: 8, + ColorComponents: 1, + Data: data, + }, nil } // Read reads an image and loads into a new Image object with an RGB diff --git a/pdf/model/optimize/image_ppi.go b/pdf/model/optimize/image_ppi.go index f2529db3..362cf62e 100644 --- a/pdf/model/optimize/image_ppi.go +++ b/pdf/model/optimize/image_ppi.go @@ -55,10 +55,9 @@ func scaleImage(stream *core.PdfObjectStream, scale float64) error { default: return fmt.Errorf("optimization is not supported for color space %s", xImg.ColorSpace.String()) } - draw.CatmullRom.Scale(newImage, newImage.Bounds(), goimg, goimg.Bounds(), draw.Over, &draw.Options{}) - i, err = imageHandler(newImage) - if err != nil { + draw.CatmullRom.Scale(newImage, newImage.Bounds(), goimg, goimg.Bounds(), draw.Over, &draw.Options{}) + if i, err = imageHandler(newImage); err != nil { return err } From dc19f8ef66fecf8821c572da0ec601d37da178c7 Mon Sep 17 00:00:00 2001 From: Adrian-George Bostan Date: Fri, 25 Jan 2019 13:42:11 +0200 Subject: [PATCH 10/10] Additional refactoring --- pdf/model/image.go | 11 +++-------- 1 file changed, 3 insertions(+), 8 deletions(-) diff --git a/pdf/model/image.go b/pdf/model/image.go index f41437ec..f45402c4 100644 --- a/pdf/model/image.go +++ b/pdf/model/image.go @@ -222,7 +222,7 @@ type ImageHandler interface { // Read any image type and load into a new Image object. Read(r io.Reader) (*Image, error) - // NewImageFromGoImage loads a RGB unidoc Image from a standard Go image structure. + // NewImageFromGoImage loads a RGBA unidoc Image from a standard Go image structure. NewImageFromGoImage(goimg goimage.Image) (*Image, error) // NewGrayImageFromGoImage loads a grayscale unidoc Image from a standard Go image structure. @@ -235,7 +235,7 @@ type ImageHandler interface { // DefaultImageHandler is the default implementation of the ImageHandler using the standard go library. type DefaultImageHandler struct{} -// NewImageFromGoImage creates a new RGB unidoc Image from a golang Image. +// NewImageFromGoImage creates a new RGBA unidoc Image from a golang Image. func (ih DefaultImageHandler) NewImageFromGoImage(goimg goimage.Image) (*Image, error) { // Speed up jpeg encoding by converting to RGBA first. // Will not be required once the golang image/jpeg package is optimized. @@ -279,17 +279,12 @@ func (ih DefaultImageHandler) NewGrayImageFromGoImage(goimg goimage.Image) (*Ima m := goimage.NewGray(b) draw.Draw(m, b, goimg, b.Min, draw.Src) - data := []byte{} - for i := 0; i < len(m.Pix); i += 1 { - data = append(data, m.Pix[i]) - } - return &Image{ Width: int64(b.Dx()), Height: int64(b.Dy()), BitsPerComponent: 8, ColorComponents: 1, - Data: data, + Data: m.Pix, }, nil }