diff options
| -rw-r--r-- | README.md | 6 | ||||
| -rw-r--r-- | def.go | 15 | ||||
| -rw-r--r-- | doc.go | 6 | ||||
| -rw-r--r-- | fpdf.go | 148 | 
4 files changed, 140 insertions, 35 deletions
| @@ -199,8 +199,10 @@ package presentation and tests. Templating is adapted by Marcus Downing from  the FPDF_Tpl library created by Jan Slabon and Setasign. Jelmer Snoeck  contributed packages that generate a variety of barcodes and help with  registering images on the web. Additionally, he augmented the basic HTML -functionality with aligned text. Bruno Michel has provided valuable assistance -with the code. +functionality with aligned text. Kent Quirk implemented backwards-compatible +support for reading DPI from images that support it, and for setting DPI +manually and then having it properly taken into account when calculating image +size. Bruno Michel has provided valuable assistance with the code.  ##Roadmap @@ -71,6 +71,7 @@ type ImageInfoType struct {  	dp    string  	trns  []int  	scale float64 // document scaling factor +	dpi   float64  }  // PointConvert returns the value of pt, expressed in points (1/72 inch), as a @@ -97,17 +98,25 @@ func (f *Fpdf) UnitToPointConvert(u float64) (pt float64) {  // Extent returns the width and height of the image in the units of the Fpdf  // object.  func (info *ImageInfoType) Extent() (wd, ht float64) { -	return info.w / info.scale, info.h / info.scale +	return info.Width(), info.Height()  }  // Width returns the width of the image in the units of the Fpdf object.  func (info *ImageInfoType) Width() float64 { -	return info.w / info.scale +	return info.w / (info.scale * info.dpi / 72)  }  // Height returns the height of the image in the units of the Fpdf object.  func (info *ImageInfoType) Height() float64 { -	return info.h / info.scale +	return info.h / (info.scale * info.dpi / 72) +} + +// SetDpi sets the dots per inch for an image. PNG images MAY have their dpi +// set automatically, if the image specifies it. DPI information is not +// currently available automatically for JPG and GIF images, so if it's +// important to you, you can set it here. It defaults to 72 dpi. +func (info *ImageInfoType) SetDpi(dpi float64) { +	info.dpi = dpi  }  type fontFileType struct { @@ -217,8 +217,10 @@ package presentation and tests. Templating is adapted by Marcus Downing from  the FPDF_Tpl library created by Jan Slabon and Setasign. Jelmer Snoeck  contributed packages that generate a variety of barcodes and help with  registering images on the web. Additionally, he augmented the basic HTML -functionality with aligned text. Bruno Michel has provided valuable assistance -with the code. +functionality with aligned text. Kent Quirk implemented backwards-compatible +support for reading DPI from images that support it, and for setting DPI +manually and then having it properly taken into account when calculating image +size. Bruno Michel has provided valuable assistance with the code.  Roadmap @@ -2250,6 +2250,16 @@ func (f *Fpdf) imageOut(info *ImageInfoType, x, y, w, h float64, flow bool, link  		w = -96  		h = -96  	} +	if w == -1 { +		// Set image width to whatever value for dpi we read +		// from the image or that was set manually +		w = -info.dpi +	} +	if h == -1 { +		// Set image height to whatever value for dpi we read +		// from the image or that was set manually +		h = -info.dpi +	}  	if w < 0 {  		w = -info.w * 72.0 / w / f.k  	} @@ -2287,11 +2297,30 @@ func (f *Fpdf) imageOut(info *ImageInfoType, x, y, w, h float64, flow bool, link  	}  } -// Image puts a JPEG, PNG or GIF image in the current page. The size it will -// take on the page can be specified in different ways. If both w and h are 0, -// the image is rendered at 96 dpi. If either w or h is zero, it will be +// Image puts a JPEG, PNG or GIF image in the current page. +// +// Deprecated in favor of ImageOptions -- see that function for +// details on the behavior of arguments +func (f *Fpdf) Image(imageNameStr string, x, y, w, h float64, flow bool, tp string, link int, linkStr string) { +	options := ImageOptions{ +		ReadDpi:   false, +		ImageType: tp, +	} +	f.ImageOptions(imageNameStr, x, y, w, h, flow, options, link, linkStr) +} + +// ImageOptions puts a JPEG, PNG or GIF image in the current page. The size it +// will take on the page can be specified in different ways. If both w and h +// are 0, the image is rendered at 96 dpi. If either w or h is zero, it will be  // calculated from the other dimension so that the aspect ratio is maintained. -// If w and h are negative, their absolute values indicate their dpi extents. +// If w and/or h are -1, the dpi for that dimension will be read from the +// ImageInfoType object. PNG files can contain dpi information, and if present, +// this information will be populated in the ImageInfoType object and used in +// Width, Height, and Extent calculations. Otherwise, the SetDpi function can +// be used to change the dpi from the default of 72. +// +// If w and h are any other negative value, their absolute values +// indicate their dpi extents.  //  // Supported JPEG formats are 24 bit, 32 bit and gray scale. Supported PNG  // formats are 24 bit, indexed color, and 8 bit indexed gray scale. If a GIF @@ -2313,18 +2342,14 @@ func (f *Fpdf) imageOut(info *ImageInfoType, x, y, w, h float64, flow bool, link  // If flow is true, the current y value is advanced after placing the image and  // a page break may be made if necessary.  // -// tp specifies the image format. Possible values are (case insensitive): -// "JPG", "JPEG", "PNG" and "GIF". If not specified, the type is inferred from -// the file extension. -//  // If link refers to an internal page anchor (that is, it is non-zero; see  // AddLink()), the image will be a clickable internal link. Otherwise, if  // linkStr specifies a URL, the image will be a clickable external link. -func (f *Fpdf) Image(imageNameStr string, x, y, w, h float64, flow bool, tp string, link int, linkStr string) { +func (f *Fpdf) ImageOptions(imageNameStr string, x, y, w, h float64, flow bool, options ImageOptions, link int, linkStr string) {  	if f.err != nil {  		return  	} -	info := f.RegisterImage(imageNameStr, tp) +	info := f.RegisterImageOptions(imageNameStr, options)  	if f.err != nil {  		return  	} @@ -2333,12 +2358,41 @@ func (f *Fpdf) Image(imageNameStr string, x, y, w, h float64, flow bool, tp stri  }  // RegisterImageReader registers an image, reading it from Reader r, adding it +// to the PDF file but not adding it to the page. +// +// This function is now deprecated in favor of RegisterImageOptionsReader +func (f *Fpdf) RegisterImageReader(imgName, tp string, r io.Reader) (info *ImageInfoType) { +	options := ImageOptions{ +		ReadDpi:   false, +		ImageType: tp, +	} +	return f.RegisterImageOptionsReader(imgName, options, r) +} + +// ImageOptions provides a place to hang any options we want to use while +// parsing an image. +// +// ImageType's possible values are (case insensitive): +// "JPG", "JPEG", "PNG" and "GIF". If empty, the type is inferred from +// the file extension. +// +// ReadDpi defines whether to attempt to automatically read the image +// dpi information from the image file. Normally, this should be set +// to true (understanding that not all images will have this info +// available). However, for backwards compatibility with previous +// versions of the API, it defaults to false. +type ImageOptions struct { +	ImageType string +	ReadDpi   bool +} + +// RegisterImageOptionsReader registers an image, reading it from Reader r, adding it  // to the PDF file but not adding it to the page. Use Image() with the same  // name to add the image to the page. Note that tp should be specified in this  // case.  // -// See Image() for restrictions on the image and the "tp" parameters. -func (f *Fpdf) RegisterImageReader(imgName, tp string, r io.Reader) (info *ImageInfoType) { +// See Image() for restrictions on the image and the options parameters. +func (f *Fpdf) RegisterImageOptionsReader(imgName string, options ImageOptions, r io.Reader) (info *ImageInfoType) {  	// Thanks, Ivan Daniluk, for generalizing this code to use the Reader interface.  	if f.err != nil {  		return @@ -2349,23 +2403,23 @@ func (f *Fpdf) RegisterImageReader(imgName, tp string, r io.Reader) (info *Image  	}  	// First use of this image, get info -	if tp == "" { +	if options.ImageType == "" {  		f.err = fmt.Errorf("image type should be specified if reading from custom reader")  		return  	} -	tp = strings.ToLower(tp) -	if tp == "jpeg" { -		tp = "jpg" +	options.ImageType = strings.ToLower(options.ImageType) +	if options.ImageType == "jpeg" { +		options.ImageType = "jpg"  	} -	switch tp { +	switch options.ImageType {  	case "jpg":  		info = f.parsejpg(r)  	case "png": -		info = f.parsepng(r) +		info = f.parsepng(r, options.ReadDpi)  	case "gif":  		info = f.parsegif(r)  	default: -		f.err = fmt.Errorf("unsupported image type: %s", tp) +		f.err = fmt.Errorf("unsupported image type: %s", options.ImageType)  	}  	if f.err != nil {  		return @@ -2379,9 +2433,24 @@ func (f *Fpdf) RegisterImageReader(imgName, tp string, r io.Reader) (info *Image  // RegisterImage registers an image, adding it to the PDF file but not adding  // it to the page. Use Image() with the same filename to add the image to the  // page. Note that Image() calls this function, so this function is only +// necessary if you need information about the image before placing it. +// +// This function is now deprecated in favor of RegisterImageOptions. +// See Image() for restrictions on the image and the "tp" parameters. +func (f *Fpdf) RegisterImage(fileStr, tp string) (info *ImageInfoType) { +	options := ImageOptions{ +		ReadDpi:   false, +		ImageType: tp, +	} +	return f.RegisterImageOptions(fileStr, options) +} + +// RegisterImageOptions registers an image, adding it to the PDF file but not +// adding it to the page. Use Image() with the same filename to add the image +// to the page. Note that Image() calls this function, so this function is only  // necessary if you need information about the image before placing it. See  // Image() for restrictions on the image and the "tp" parameters. -func (f *Fpdf) RegisterImage(fileStr, tp string) (info *ImageInfoType) { +func (f *Fpdf) RegisterImageOptions(fileStr string, options ImageOptions) (info *ImageInfoType) {  	info, ok := f.images[fileStr]  	if ok {  		return @@ -2395,16 +2464,16 @@ func (f *Fpdf) RegisterImage(fileStr, tp string) (info *ImageInfoType) {  	defer file.Close()  	// First use of this image, get info -	if tp == "" { +	if options.ImageType == "" {  		pos := strings.LastIndex(fileStr, ".")  		if pos < 0 {  			f.err = fmt.Errorf("image file has no extension and no type was specified: %s", fileStr)  			return  		} -		tp = fileStr[pos+1:] +		options.ImageType = fileStr[pos+1:]  	} -	return f.RegisterImageReader(fileStr, tp, file) +	return f.RegisterImageOptionsReader(fileStr, options, file)  }  // GetImageInfo returns information about the registered image specified by @@ -2682,7 +2751,8 @@ func be16(buf []byte) int {  }  func (f *Fpdf) newImageInfo() *ImageInfoType { -	return &ImageInfoType{scale: f.k} +	// default dpi to 72 unless told otherwise +	return &ImageInfoType{scale: f.k, dpi: 72}  }  // Extract info from io.Reader with JPEG data @@ -2722,13 +2792,13 @@ func (f *Fpdf) parsejpg(r io.Reader) (info *ImageInfoType) {  }  // Extract info from a PNG data -func (f *Fpdf) parsepng(r io.Reader) (info *ImageInfoType) { +func (f *Fpdf) parsepng(r io.Reader, readdpi bool) (info *ImageInfoType) {  	buf, err := bufferFromReader(r)  	if err != nil {  		f.err = err  		return  	} -	return f.parsepngstream(buf) +	return f.parsepngstream(buf, readdpi)  }  func (f *Fpdf) readBeInt32(buf *bytes.Buffer) (val int32) { @@ -2747,7 +2817,7 @@ func (f *Fpdf) readByte(buf *bytes.Buffer) (val byte) {  	return  } -func (f *Fpdf) parsepngstream(buf *bytes.Buffer) (info *ImageInfoType) { +func (f *Fpdf) parsepngstream(buf *bytes.Buffer, readdpi bool) (info *ImageInfoType) {  	info = f.newImageInfo()  	// 	Check signature  	if string(buf.Next(8)) != "\x89PNG\x0d\x0a\x1a\x0a" { @@ -2834,6 +2904,28 @@ func (f *Fpdf) parsepngstream(buf *bytes.Buffer) (info *ImageInfoType) {  		case "IEND":  			// dbg("IEND")  			loop = false +		case "pHYs": +			// dbg("pHYs") +			// png files theoretically support different x/y dpi +			// but we ignore files like this +			// but if they're the same then we can stamp our info +			// object with it +			x := int(f.readBeInt32(buf)) +			y := int(f.readBeInt32(buf)) +			units := buf.Next(1)[0] +			// fmt.Printf("got a pHYs block, x=%d, y=%d, u=%d, readdpi=%t\n", +			// x, y, int(units), readdpi) +			// only modify the info block if the user wants us to +			if x == y && readdpi { +				switch units { +				// if units is 1 then measurement is px/meter +				case 1: +					info.dpi = float64(x) / 39.3701 // inches per meter +				default: +					info.dpi = float64(x) +				} +			} +			_ = buf.Next(4)  		default:  			// dbg("default")  			_ = buf.Next(n + 4) @@ -2927,7 +3019,7 @@ func (f *Fpdf) parsegif(r io.Reader) (info *ImageInfoType) {  		f.err = err  		return  	} -	return f.parsepngstream(pngBuf) +	return f.parsepngstream(pngBuf, false)  }  // Begin a new object | 
