commit d1a64da3e989cc822c984f2980468313795f5493 Author: cheetah Date: Wed Jan 8 00:35:51 2025 +0100 first commit diff --git a/go.mod b/go.mod new file mode 100644 index 0000000..1faa9d1 --- /dev/null +++ b/go.mod @@ -0,0 +1,5 @@ +module git.cheetah.cat/cheetah/go-snake-label + +go 1.22.5 + +require gopkg.in/gographics/imagick.v2 v2.7.0 diff --git a/go.sum b/go.sum new file mode 100644 index 0000000..b284d31 --- /dev/null +++ b/go.sum @@ -0,0 +1,2 @@ +gopkg.in/gographics/imagick.v2 v2.7.0 h1:Acluvnk5MhtETFX4EVnt9NbjC32ROaaC3bx6nJueHJk= +gopkg.in/gographics/imagick.v2 v2.7.0/go.mod h1:/QVPLV/iKdNttRKthmDkeeGg+vdHurVEPc8zkU0XgBk= diff --git a/lib/common.go b/lib/common.go new file mode 100644 index 0000000..53a1917 --- /dev/null +++ b/lib/common.go @@ -0,0 +1,52 @@ +package lib + +import "gopkg.in/gographics/imagick.v2/imagick" + +func ConvertPDF2PNG(pdfFile, pngFile string, dpi int, rot int) (err error) { + mw := imagick.NewMagickWand() + defer mw.Destroy() + + err = mw.SetResolution(float64(dpi), float64(dpi)) + if err != nil { + return err + } + + err = mw.ReadImage(pdfFile) + if err != nil { + return err + } + + if mw.GetNumberImages() > 1 { + mw = mw.MergeImageLayers(imagick.IMAGE_LAYER_FLATTEN) + } + + drawingWand := imagick.NewDrawingWand() + defer drawingWand.Destroy() + pixelWand := imagick.NewPixelWand() + defer pixelWand.Destroy() + pixelWand.SetColor("white") + drawingWand.SetFillColor(pixelWand) + + err = mw.RotateImage(pixelWand, float64(rot)) + if err != nil { + return err + } + + //mw.SetIteratorIndex(0) + err = mw.SetImageAlphaChannel(imagick.ALPHA_CHANNEL_DEACTIVATE) + if err != nil { + return err + } + + err = mw.SetImageFormat("png") + if err != nil { + return err + } + + err = mw.WriteImage(pngFile) + if err != nil { + return err + } + // + return nil +} diff --git a/lib/dhl.go b/lib/dhl.go new file mode 100644 index 0000000..f5e6b97 --- /dev/null +++ b/lib/dhl.go @@ -0,0 +1,301 @@ +package lib + +import ( + "fmt" + + "gopkg.in/gographics/imagick.v2/imagick" +) + +func DHLPrivat(inputFile, outputFile string) (err error) { + canvasWidth := uint(1642) + canvasHeight := uint(696) + // + srcWand := imagick.NewMagickWand() + defer srcWand.Destroy() + + if err = srcWand.ReadImage(inputFile); err != nil { + return err + } + + var ( + barcodeSizeX int = 710 + barcodeSizeY int = 320 + scSize int = 296 + ) + sections := []struct { + srcX, srcY, srcW, srcH, + tgtX, tgtY, tgtW, tgtH, + rot int + }{ + + { // Kopf + 1964, 106, 1124, 94, + 0, 0, 890, 74, + 0, + }, + { // Adresse + 1964, 210, 785, 625, + 0, 95, 580, 465, + 0, + }, + { // Sicherheitscode + 2763, 215, scSize, scSize, + 594, 90, scSize, scSize, + 0, + }, + { // Sicherheitscode Text + 3075, 244, 20, 194, + 645, 395, 200, 20, + 270, + }, + { // Bahntransport + 2802, 679, 234, 154, + 666, 465, 152, 100, + 0, + }, + { //Sendungsdaten + 1964, 933, 1124, 152, + 0, 576, 890, 120, + 0, + }, + { // Leitcode/Routingcode + 2181, 1526, barcodeSizeX, barcodeSizeY, + 930, 20, barcodeSizeX, barcodeSizeY, + 0, + }, + { // Identcode/Sendungsnummer + 2181, 1940, barcodeSizeX, barcodeSizeY, + 930, 376, barcodeSizeX, barcodeSizeY, + 0, + }, + } + + outputWand := imagick.NewMagickWand() + defer outputWand.Destroy() + drawingWand := imagick.NewDrawingWand() + defer drawingWand.Destroy() + pixelWand := imagick.NewPixelWand() + defer pixelWand.Destroy() + + // Set canvas size for the output image + pixelWand.SetColor("white") + drawingWand.SetFillColor(pixelWand) + err = outputWand.NewImage(canvasWidth, canvasHeight, pixelWand) + if err != nil { + return err + } + + for i, section := range sections { + cropped := srcWand.Clone() + defer cropped.Destroy() + + if err := cropped.CropImage(uint(section.srcW), uint(section.srcH), int(section.srcX), int(section.srcY)); err != nil { + return fmt.Errorf("failed to crop section %d: %v", i, err) + } + + if section.rot != 0 { + err = cropped.RotateImage(pixelWand, float64(section.rot)) + if err != nil { + return fmt.Errorf("failed to rotate section %d: %v", i, err) + } + } + // Resize cropped section (if needed) + if err := cropped.ResizeImage(uint(section.tgtW), uint(section.tgtH), imagick.FILTER_LANCZOS, 1); err != nil { + return fmt.Errorf("failed to resize section %d: %v", i, err) + } + + //cropped.WriteImage(fmt.Sprintf("section%d.png", i)) + // Composite the cropped section onto the output canvas + if err := outputWand.CompositeImage(cropped, imagick.COMPOSITE_OP_COPY, int(section.tgtX), int(section.tgtY)); err != nil { + return fmt.Errorf("failed to composite section %d: %v", i, err) + } + } + { // Line + strokeWand := imagick.NewPixelWand() + defer strokeWand.Destroy() + strokeWand.SetColor("black") + lineWand := imagick.NewDrawingWand() + defer lineWand.Destroy() + + lineWand.SetStrokeColor(strokeWand) + lineWand.Line(910, 0, 910, float64(canvasHeight)) + + err = outputWand.DrawImage(lineWand) + if err != nil { + return err + } + } + + err = outputWand.SetImageFormat("png") + if err != nil { + return err + } + err = outputWand.WriteImage(outputFile) + if err != nil { + return err + } + // + return nil +} +func DHLPrivatInternational(inputFile, outputFile string) (err error) { + canvasWidth := uint(2232) + canvasHeight := uint(696) + // + srcWand := imagick.NewMagickWand() + defer srcWand.Destroy() + + if err = srcWand.ReadImage(inputFile); err != nil { + return err + } + + var ( + barcodeSizeX int = 1124 + barcodeSizeY int = 280 + scSize int = 296 + ) + sections := []struct { + srcX, srcY, srcW, srcH, + tgtX, tgtY, tgtW, tgtH, + rot int + }{ + { // Kopf + 1964, 106, 1124, 94, + 0, 0, 890, 74, + 0, + }, + { // Adresse + 1964, 210, 785, 625, + 0, 95, 580, 465, + 0, + }, + { // Sicherheitscode + 2763, 215, scSize, scSize, + 594, 90, scSize, scSize, + 0, + }, + { // Sicherheitscode Text + 3075, 244, 20, 194, + 645, 395, 200, 20, + 270, + }, + { // Telefonnummer & E-Mail + 2768, 743, 300, 60, + 590, 495, 300, 60, + 0, + }, + { //Sendungsdaten + 1964, 933, 1124, 152, + 0, 576, 890, 120, + 0, + }, + { // Icons / Go Green + 1964, 854, 1124, 70, + 920, 696, 696, 43, + 270, + }, + { // Tracked + 1964, 1197, 1124, 122, + 986, 696, 696, 76, + 270, + }, + { // Tracked Icon + 2846, 530, 130, 202, + 930, 10, 130, 202, + 0, + }, + { // Unzustellbarkeit + 1964, 1422, barcodeSizeX, 70, + 1100, 4, barcodeSizeX, 70, + 0, + }, + { // Leitcode/Routingcode + 1964, 1634, barcodeSizeX, barcodeSizeY, + 1100, 100, barcodeSizeX, barcodeSizeY, + 0, + }, + { // Category Letter + 1964, 2000, 160, barcodeSizeY - 20, + 1100, 416, 160, barcodeSizeY - 20, + 0, + }, + { // Identcode/Sendungsnummer + 1964, 2048 + 260, 160, 20, + 1100, 416 + 260, 160, 20, + 0, + }, + { // Identcode/Sendungsnummer (2) + 1964 + 160, 2048, barcodeSizeX - 160, barcodeSizeY, + 1100 + 160, 416, barcodeSizeX - 160, barcodeSizeY, + 0, + }, + } + + outputWand := imagick.NewMagickWand() + defer outputWand.Destroy() + drawingWand := imagick.NewDrawingWand() + defer drawingWand.Destroy() + pixelWand := imagick.NewPixelWand() + defer pixelWand.Destroy() + + // Set canvas size for the output image + pixelWand.SetColor("white") + drawingWand.SetFillColor(pixelWand) + err = outputWand.NewImage(canvasWidth, canvasHeight, pixelWand) + if err != nil { + return err + } + + for i, section := range sections { + cropped := srcWand.Clone() + defer cropped.Destroy() + + if err := cropped.CropImage(uint(section.srcW), uint(section.srcH), int(section.srcX), int(section.srcY)); err != nil { + return fmt.Errorf("failed to crop section %d: %v", i, err) + } + + if section.rot != 0 { + err = cropped.RotateImage(pixelWand, float64(section.rot)) + if err != nil { + return fmt.Errorf("failed to rotate section %d: %v", i, err) + } + } + // Resize cropped section (if needed) + if err := cropped.ResizeImage(uint(section.tgtW), uint(section.tgtH), imagick.FILTER_LANCZOS, 1); err != nil { + return fmt.Errorf("failed to resize section %d: %v", i, err) + } + + // Composite the cropped section onto the output canvas + if err := outputWand.CompositeImage(cropped, imagick.COMPOSITE_OP_COPY, int(section.tgtX), int(section.tgtY)); err != nil { + return fmt.Errorf("failed to composite section %d: %v", i, err) + } + } + { // Line 1 + strokeWand := imagick.NewPixelWand() + defer strokeWand.Destroy() + strokeWand.SetColor("black") + lineWand := imagick.NewDrawingWand() + defer lineWand.Destroy() + + lineWand.SetStrokeColor(strokeWand) + lineWand.Line(910, 0, 910, float64(canvasHeight)) + lineWand.Line(920, 230, 1070, 230) + lineWand.Line(970, 240, 970, float64(canvasHeight)) + lineWand.Line(1080, 0, 1080, float64(canvasHeight)) + + err = outputWand.DrawImage(lineWand) + if err != nil { + return err + } + } + + err = outputWand.SetImageFormat("png") + if err != nil { + return err + } + err = outputWand.WriteImage(outputFile) + if err != nil { + return err + } + // + return nil +} diff --git a/main.go b/main.go new file mode 100644 index 0000000..43ab91b --- /dev/null +++ b/main.go @@ -0,0 +1,74 @@ +package main + +import ( + "encoding/binary" + "encoding/hex" + "flag" + "fmt" + "math/rand" + "os" + "path" + "time" + + "git.cheetah.cat/cheetah/go-snake-label/lib" + "gopkg.in/gographics/imagick.v2/imagick" +) + +var src = rand.New(rand.NewSource(time.Now().UnixNano())) + +func RandStringBytesMaskImprSrc(n int) string { + b := make([]byte, (n+1)/2) + if _, err := src.Read(b); err != nil { + panic(err) + } + return hex.EncodeToString(b)[:n] +} + +func tempPNGPath() string { + return path.Join(os.TempDir(), fmt.Sprintf(RandStringBytesMaskImprSrc(16), ".png")) +} + +// sudo apt-get install libmagickwand-dev +func main() { + imagick.Initialize() + defer imagick.Terminate() + + inputFile := flag.String("input", "", "input filename") + outputFile := flag.String("output", "", "output filename") + modeSelect := flag.String("mode", "dhlprivat", "mode select (dhlprivat, dhlprivatint)") + + flag.Parse() + + if len(*inputFile) < 3 { + panic("invalid input file") + } + if len(*outputFile) == 0 { + panic("invalid output file") + } + + tempFileName := tempPNGPath() + defer os.Remove(tempFileName) + + outputFileName := *outputFile + outputSTDOUT := false + if outputFileName == "-" || outputFileName == "/dev/stdout" { + outputSTDOUT = true + outputFileName = tempPNGPath() + defer os.Remove(outputFileName) + } + switch *modeSelect { + case "dhlprivat": + // scale: 4, // optional, defaults to 4 (288dpi) -- 4.1666 for 300dpi + lib.ConvertPDF2PNG(*inputFile, tempFileName, 288, 90) + lib.DHLPrivat(tempFileName, outputFileName) + case "dhlprivatint": + lib.ConvertPDF2PNG(*inputFile, tempFileName, 288, 90) + lib.DHLPrivatInternational(tempFileName, outputFileName) + } + + if outputSTDOUT { + outputBytes, _ := os.ReadFile(outputFileName) + binary.Write(os.Stdout, binary.LittleEndian, outputBytes) + } + +}