From 9b87a30f0671d13b9d4f76780b41eb9e87883935 Mon Sep 17 00:00:00 2001
From: Dan Meyers <danmeyers00@gmail.com>
Date: Mon, 7 Aug 2017 23:54:14 -0400
Subject: make barcode scaling optional, provide a method to get unscaled
 barcode dimensions

---
 contrib/barcode/barcode.go      | 64 +++++++++++++++++++++++++++++++++++------
 contrib/barcode/barcode_test.go | 51 ++++++++++++++++++++++++++++----
 2 files changed, 102 insertions(+), 13 deletions(-)

diff --git a/contrib/barcode/barcode.go b/contrib/barcode/barcode.go
index 9f8d3ff..26f99c3 100644
--- a/contrib/barcode/barcode.go
+++ b/contrib/barcode/barcode.go
@@ -56,12 +56,9 @@ type barcodePdf interface {
 	SetError(err error)
 }
 
-// Barcode puts a registered barcode in the current page.
-//
-// The size should be specified in the units used to create the PDF document.
-//
-// Positioning with x, y and flow is inherited from Fpdf.Image().
-func Barcode(pdf barcodePdf, code string, x, y, w, h float64, flow bool) {
+// printBarcode internally prints the scaled or unscaled barcode to the PDF. Used by both
+// Barcode() and BarcodeUnscalable().
+func printBarcode(pdf barcodePdf, code string, x, y float64, w, h *float64, flow bool) {
 	barcodes.Lock()
 	unscaled, ok := barcodes.cache[code]
 	barcodes.Unlock()
@@ -76,10 +73,18 @@ func Barcode(pdf barcodePdf, code string, x, y, w, h float64, flow bool) {
 	info := pdf.GetImageInfo(bname)
 
 	if info == nil {
+		scaleToWidth := unscaled.Bounds().Dx()
+		scaleToHeight := unscaled.Bounds().Dy()
+		if w != nil {
+			scaleToWidth = int(convertTo96Dpi(pdf, *w))
+		}
+		if h != nil {
+			scaleToHeight = int(convertTo96Dpi(pdf, *h))
+		}
 		bcode, err := barcode.Scale(
 			unscaled,
-			int(convertTo96Dpi(pdf, w)),
-			int(convertTo96Dpi(pdf, h)),
+			scaleToWidth,
+			scaleToHeight,
 		)
 
 		if err != nil {
@@ -95,6 +100,43 @@ func Barcode(pdf barcodePdf, code string, x, y, w, h float64, flow bool) {
 	}
 
 	pdf.Image(bname, x, y, 0, 0, flow, "jpg", 0, "")
+
+}
+
+// BarcodeUnscalable puts a registered barcode in the current page.
+//
+// Its arguments work in the same way as that of Barcode(). However, it allows for an unscaled
+// barcode in the width and/or height dimensions. This can be useful if you want to prevent
+// side effects of upscaling.
+func BarcodeUnscalable(pdf barcodePdf, code string, x, y float64, w, h *float64, flow bool) {
+	printBarcode(pdf, code, x, y, w, h, flow)
+}
+
+// Barcode puts a registered barcode in the current page.
+//
+// The size should be specified in the units used to create the PDF document.
+// If width or height are left unspecfied, the barcode is not scaled in the unspecified dimensions.
+//
+// Positioning with x, y and flow is inherited from Fpdf.Image().
+func Barcode(pdf barcodePdf, code string, x, y, w, h float64, flow bool) {
+	printBarcode(pdf, code, x, y, &w, &h, flow)
+}
+
+// GetUnscaledBarcodeDimensions returns the width and height of the
+// unscaled barcode associated with the given code.
+func GetUnscaledBarcodeDimensions(pdf barcodePdf, code string) (w, h float64) {
+	barcodes.Lock()
+	unscaled, ok := barcodes.cache[code]
+	barcodes.Unlock()
+
+	if !ok {
+		err := errors.New("Barcode not found")
+		pdf.SetError(err)
+		return
+	}
+
+	return convertFrom96Dpi(pdf, float64(unscaled.Bounds().Dx())),
+		convertFrom96Dpi(pdf, float64(unscaled.Bounds().Dy()))
 }
 
 // Register registers a barcode but does not put it on the page. Use Barcode()
@@ -248,3 +290,9 @@ func registerScaledBarcode(pdf barcodePdf, code string, bcode barcode.Barcode) e
 func convertTo96Dpi(pdf barcodePdf, value float64) float64 {
 	return value * pdf.GetConversionRatio() / 72 * 96
 }
+
+// convertFrom96Dpi converts the given value, which is based on a 96 DPI value
+// required for an Image, to a 72 DPI value like the rest of the PDF document.
+func convertFrom96Dpi(pdf barcodePdf, value float64) float64 {
+	return value / pdf.GetConversionRatio() * 72 / 96
+}
diff --git a/contrib/barcode/barcode_test.go b/contrib/barcode/barcode_test.go
index 5b6c8cf..0a40a09 100644
--- a/contrib/barcode/barcode_test.go
+++ b/contrib/barcode/barcode_test.go
@@ -27,7 +27,9 @@ func ExampleRegister() {
 
 	if err == nil {
 		key := barcode.Register(bcode)
-		barcode.Barcode(pdf, key, 15, 15, 100, 10, false)
+		var width float64 = 100
+		var height float64 = 10.0
+		barcode.BarcodeUnscalable(pdf, key, 15, 15, &width, &height, false)
 	}
 
 	err = pdf.OutputFileAndClose(fileStr)
@@ -40,7 +42,9 @@ func ExampleRegisterCodabar() {
 	pdf := createPdf()
 
 	key := barcode.RegisterCode128(pdf, "codabar")
-	barcode.Barcode(pdf, key, 15, 15, 100, 10, false)
+	var width float64 = 100
+	var height float64 = 10
+	barcode.BarcodeUnscalable(pdf, key, 15, 15, &width, &height, false)
 
 	fileStr := example.Filename("contrib_barcode_RegisterCodabar")
 	err := pdf.OutputFileAndClose(fileStr)
@@ -118,7 +122,7 @@ func ExampleRegisterQR() {
 	pdf := createPdf()
 
 	key := barcode.RegisterQR(pdf, "qrcode", qr.H, qr.Unicode)
-	barcode.Barcode(pdf, key, 15, 15, 20, 20, false)
+	barcode.Barcode(pdf, key, 15, 15, 100, 10, false)
 
 	fileStr := example.Filename("contrib_barcode_RegisterQR")
 	err := pdf.OutputFileAndClose(fileStr)
@@ -131,7 +135,7 @@ func ExampleRegisterTwoOfFive() {
 	pdf := createPdf()
 
 	key := barcode.RegisterTwoOfFive(pdf, "1234567895", true)
-	barcode.Barcode(pdf, key, 15, 15, 100, 20, false)
+	barcode.Barcode(pdf, key, 15, 15, 100, 10, false)
 
 	fileStr := example.Filename("contrib_barcode_RegisterTwoOfFive")
 	err := pdf.OutputFileAndClose(fileStr)
@@ -144,7 +148,7 @@ func ExampleRegisterPdf417() {
 	pdf := createPdf()
 
 	key := barcode.RegisterPdf417(pdf, "1234567895", 10, 5)
-	barcode.Barcode(pdf, key, 15, 15, 100, 20, false)
+	barcode.Barcode(pdf, key, 15, 15, 100, 10, false)
 
 	fileStr := example.Filename("contrib_barcode_RegisterPdf417")
 	err := pdf.OutputFileAndClose(fileStr)
@@ -158,3 +162,40 @@ func TestRegisterCode128(t *testing.T) {
 	pdf := createPdf()
 	barcode.RegisterCode128(pdf, "Invalid character: é")
 }
+
+// Shows that the barcode may be scaled or not by providing optional heights and widths.
+func TestBarcodeUnscalable(t *testing.T) {
+	pdf := createPdf()
+
+	key := barcode.RegisterCode128(pdf, "code128")
+	var width float64 = 100
+	var height float64 = 10
+	barcode.BarcodeUnscalable(pdf, key, 15, 15, &width, &height, false)
+	barcode.BarcodeUnscalable(pdf, key, 15, 35, nil, &height, false)
+	barcode.BarcodeUnscalable(pdf, key, 15, 55, &width, nil, false)
+	barcode.BarcodeUnscalable(pdf, key, 15, 75, nil, nil, false)
+
+	fileStr := example.Filename("contrib_barcode_Barcode")
+	err := pdf.OutputFileAndClose(fileStr)
+	example.Summary(err, fileStr)
+	// Output:
+	// Successfully generated ../../pdf/contrib_barcode_Barcode.pdf
+}
+
+// Shows that the width and height returned by the function match that of the barcode
+func TestGetUnscaledBarcodeDimensions(t *testing.T) {
+	pdf := createPdf()
+
+	key := barcode.RegisterQR(pdf, "qrcode", qr.H, qr.Unicode)
+	barcode.BarcodeUnscalable(pdf, key, 15, 15, nil, nil, false)
+	w, h := barcode.GetUnscaledBarcodeDimensions(pdf, key)
+
+	pdf.SetDrawColor(255, 0, 0)
+	pdf.Line(15, 15, 15+w, 15+h)
+
+	fileStr := example.Filename("contrib_barcode_GetBarcodeDimensions")
+	err := pdf.OutputFileAndClose(fileStr)
+	example.Summary(err, fileStr)
+	// Output:
+	// Successfully generated ../../pdf/contrib_barcode_GetBarcodeDimensions.pdf
+}
-- 
cgit v1.2.1-24-ge1ad