背景

codesoft6 编写的ceshi.lab文件
在这里插入图片描述

系统自带打印机Microsoft Print to PDF

代码

工具类

package printer

import (
	"errors"
	"fmt"
	"runtime"
	"time"

	ole "github.com/go-ole/go-ole"
	"github.com/go-ole/go-ole/oleutil"
)

// Label is a struct for recv label information
// template is label abs-path, printer is printer name, cause
// printer name never dupplicate in one computer/server, so using
// printer name for the output device, quantity is for the label copy num.
type Label struct {
	Template string            //标签路径(绝对路径)
	Printer  string            //打印机名称
	Data     map[string]string //标签内参数
	Quantity int               //打印份数
}

// AppWorkStation is for mult-gorounting, when you using the mult-goroutings, you
// need care about the max number of the workstation can't exceed the max num. of
// cpu core this function is limited by ole working principle. if out of the cores,
// it will cause ole object can't been close properly.
func AppWorkStation(msg chan Label, max time.Duration) {

	runtime.LockOSThread()
	defer runtime.UnlockOSThread()

	ole.CoInitializeEx(0, ole.COINIT_MULTITHREADED)
	defer ole.CoUninitialize()

	app, e := newApplication()
	if e != nil {
		fmt.Println(e)
		return
	}
	for {
		select {
		case lb := <-msg:
			fmt.Println(lb)
			e = app.Print(lb)
			if e != nil {
				fmt.Println(e)
			}
		case <-time.After(max):
			fmt.Println("app", max.String())
			app.Close()
			// fmt.Println("app", max.String())
			return
		}
	}
}

// Application is for application env.
type Application struct {
	oleobj *ole.IUnknown
	appInterface
}

func newApplication() (r *Application, e error) {
	r = &Application{}
	r.oleobj, e = oleutil.CreateObject("Lppx2.Application")
	if e != nil {
		return
	}
	lb, e := r.oleobj.QueryInterface(ole.IID_IDispatch)
	if e != nil {
		return
	}
	r.appInterface = appInterface{}
	r.appInterface.oleobj = lb
	r.appInterface.Documents = Documents{}
	r.appInterface.Documents.oleobj = oleutil.MustGetProperty(lb, "Documents").ToIDispatch()
	r.appInterface.Documents.openDocs = make(map[string]Document)
	return
}

// Print is function for print a label.
func (app Application) Print(l Label) (e error) {
	if d, ok := app.openDocs[l.Template]; ok {
		e = d.print(l)
	} else {
		d, e = app.Open(l.Template)
		if e != nil {
			return
		}
		e = d.print(l)
	}
	return
}

// Close app self
func (app Application) Close() {
	app.appInterface.close()
	app.oleobj.Release()
	// ole.CoUninitialize()
}

type appInterface struct {
	oleobj *ole.IDispatch
	Documents
}

func (appif appInterface) close() {
	appif.Documents.Close()
	appif.oleobj.Release()
}

// Documents is an collection object for all label document
type Documents struct {
	oleobj   *ole.IDispatch
	openDocs map[string]Document
}

// Close all the documents in the documents objects, in ole, documents
// is a collection object, in this package implment as map to keep those
// open document.
func (docs Documents) Close() {
	// release open docs
	for k, v := range docs.openDocs {
		if &v != nil {
			v.Close()
			delete(docs.openDocs, k)
		}
	}
	// release self
	docs.oleobj.Release()
}

// Open a label in `lab` path, and return a Document object
func (docs *Documents) Open(lab string) (doc Document, e error) {
	t := oleutil.MustCallMethod(docs.oleobj, "open", lab, true).ToIDispatch()
	if t == nil {
		e = errors.New("label not exists!")
		return
	}

	if docs.openDocs == nil {
		docs.openDocs = make(map[string]Document)
	}
	doc = Document{}
	doc.oleobj = t
	doc.Variables.init(doc.oleobj)
	doc.Printer.init(doc.oleobj)
	docs.openDocs[lab] = doc
	return
}

// Document is a label template
type Document struct {
	oleobj *ole.IDispatch
	Variables
	Printer
}

// Close and release resources
func (doc Document) Close() {
	// close open docs
	doc.Printer.Close()
	oleutil.MustCallMethod(doc.oleobj, "Close")
}

// Print out
func (doc Document) print(l Label) (e error) {
	if doc.oleobj == nil {
		e = errors.New("Docment ole object is not exists")
		return
	}
	// switch printer
	fmt.Println(doc.Printer)
	e = doc.Printer.SwitchTo(l.Printer)
	if e != nil {
		return
	}
	// set parameters
	for k, v := range l.Data {
		i := doc.Items[k]
		i.Set(v)
	}
	// print
	_, e = oleutil.CallMethod(doc.oleobj, "PrintDocument", l.Quantity)
	return
}

// Printer implement from ole
type Printer struct {
	oleobject *ole.IDispatch
	fullName  string
	//我的打印机没这两个参数
	// marginLeft string
	// marginTop  string
}

func (p *Printer) init(obj *ole.IDispatch) {
	p.oleobject = oleutil.MustGetProperty(obj, "Printer").ToIDispatch()

	p.fullName = oleutil.MustGetProperty(p.oleobject, "FullName").ToString()
	// p.marginLeft = oleutil.MustGetProperty(p.oleobject, "MarginLeft").ToString()
	// p.marginTop = oleutil.MustGetProperty(p.oleobject, "MarginTop").ToString()

}

// SwitchTo printer with printer name
func (p *Printer) SwitchTo(prt string) (e error) {
	t := oleutil.MustCallMethod(p.oleobject, "SwitchTo", prt)
	if t.Val == 0 {
		e = errors.New("printer not exists")
		return
	}
	p.fullName = prt
	return
}

//我的打印机没这两个参数
// // MarginLeft read printer parameter: marginleft
// func (p *Printer) MarginLeft() (r string) {
// 	return p.marginLeft
// }

// // SetMarginLeft set printer parameter: marginleft
// func (p *Printer) SetMarginLeft(v string) {
// 	oleutil.MustPutProperty(p.oleobject, "MarginLeft", v)
// }

// // MarginTop read printer parameter: margintop
// func (p *Printer) MarginTop() (r string) {
// 	return p.marginTop
// }

// // SetMarginTop set printer parameter: margintop
// func (p *Printer) SetMarginTop(v string) {
// 	oleutil.MustPutProperty(p.oleobject, "MarginTop", v)
// }

// Close self
func (p *Printer) Close() {
	p.oleobject.Release()
}

// Variables including in the label. it contains several parts, form variables, free variables
// and so on, please check activex document to find details. here just implement the free
// variables.
type Variables struct {
	oleobj *ole.IDispatch
	Items  map[string]Item
}

func (v *Variables) init(obj *ole.IDispatch) {
	if v == nil {
		v = &Variables{}
	}
	v.oleobj = oleutil.MustGetProperty(obj, "Variables").ToIDispatch()
	v.Items = make(map[string]Item)

	ct := int(oleutil.MustGetProperty(v.oleobj, "Count").Val)
	fmt.Println(ct)
	for i := 1; i <= ct; i++ {
		t := oleutil.MustCallMethod(v.oleobj, "item", i).ToIDispatch()
		itm := &Item{}
		itm.init(t)
		v.Items[itm.Name] = *itm
	}
	// v.FreeVariables.init(v.oleobj)
}

// FreeVariables is only using  in label for now
type FreeVariables struct {
	oleobj *ole.IDispatch
	Items  map[string]Item
}

func (fv *FreeVariables) init(obj *ole.IDispatch) {
	fv.oleobj = oleutil.MustGetProperty(obj, "FreeVariables").ToIDispatch()
	fv.Items = make(map[string]Item)

	// variables num.
	// index start from 1 not 0.
	ct := int(oleutil.MustGetProperty(fv.oleobj, "Count").Val)

	for i := 1; i <= ct; i++ {
		t := oleutil.MustCallMethod(fv.oleobj, "item", i).ToIDispatch()
		itm := &Item{}
		itm.init(t)
		fv.Items[itm.Name] = *itm
	}
}

// Item in variables
type Item struct {
	oleobj *ole.IDispatch
	Name   string
	Value  string
}

func (itm *Item) init(obj *ole.IDispatch) {
	if itm == nil {
		itm = &Item{}
	}
	itm.oleobj = obj
	itm.Name = oleutil.MustGetProperty(itm.oleobj, "Name").ToString()
	itm.Value = oleutil.MustGetProperty(itm.oleobj, "Value").ToString()
}

// Val is a function to read item's value
func (itm *Item) Val() string {
	return itm.Value
}

// Set is using to setting item's value
func (itm *Item) Set(val string) {
	oleutil.PutProperty(itm.oleobj, "Value", val)
}

测试
 func main() {
	lb := codesoft.Label{}
	lb.Template = "D:\\ceshiPrinter\\ceshi.Lab"
	lb.Quantity = 1
	lb.Data = map[string]string{"VAR0": "wfugui", "VAR1": "www.wfugui.com"}
	// lb.Printer = "Fax"
	// lb.Printer = "Microsoft XPS Document Writer"
	// lb.Printer = "Microsoft Print to PDF"
	lb.Printer = "Microsoft Print to PDF"

	msg := make(chan codesoft.Label, 100)
	go codesoft.AppWorkStation(msg, time.Second*10)
	msg <- lb

	lb.Data = map[string]string{"VAR0": "zxc", "VAR1": "www.wfugui.com"}
	msg <- lb
	
	time.Sleep(time.Minute * 1)
}