Skip to content

Commit

Permalink
split files and refactor
Browse files Browse the repository at this point in the history
  • Loading branch information
yeasir01 committed Jan 26, 2024
1 parent d571c2d commit b7cf290
Show file tree
Hide file tree
Showing 8 changed files with 510 additions and 372 deletions.
268 changes: 268 additions & 0 deletions src/BrotherSdk.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,268 @@
import {
getAbsolutePath,
getPrintOption,
getEncodingOption,
} from "./helpers";
import {
Data,
Config,
Encoding,
ImageOptions,
} from "./types";
import {
openTemplate,
closeTemplate,
populateObjectsInTemplate,
getPrinterName,
imageData,
getPrinters,
startPrint,
printOut,
endPrint,
exportTemplate,
} from "./adapter";

export default class BrotherSdk {
#ready: boolean;

templatePath: string;

exportDir: string;

// One mutation observer, regardless of instances.
static #observer: MutationObserver | undefined;

/**
* **Retrieve The List Of Installed Printers**
*
* asynchronously retrieves the list of installed printers compatible with the bpac SDK.
*
* @returns {Promise<string[]>}
* a promise that resolves to an array of installed printers
* compatible with the 'bpac' SDK.
* @throws {Error}
* will throw an err if the method fails.
*
*/
static async getPrinterList(): Promise<string[]> {
const printers = await getPrinters();
return printers;
}

/**
* @constructor
* Constructs a new instance of the BrotherSdk class, used
* for interacting with Brother SDK functionality.
* @param {Object} object
* @param {String} object.templatePath
* Specifies the path to the template file (supports various formats)
* - Local path: "c:/path/to/your/template.lbx"
* - UNC path: "\\server\share\template.lbx"
* - Remote URL: "http://yourserver.com/templates/label.lbx"
* @param {String} [object.exportDir = ""]
* The path for exporting generated templates.
*/
constructor({
templatePath,
exportDir,
}: {
templatePath: string;
exportDir?: string;
}) {
this.templatePath = templatePath;
this.exportDir = exportDir || "";
this.#ready = false;
this.#initialize();
}

#initialize() {
const targetNode = document.body;
const className = "bpac-extension-installed";

if (this.#ready) {
return;
}

if (targetNode.classList.contains(className)) {
this.#ready = true;
return;
}

if (BrotherSdk.#observer) {
return;
}

BrotherSdk.#observer = new MutationObserver(() => {
if (targetNode.classList.contains(className)) {
this.#ready = true;
BrotherSdk.#observer?.disconnect();
BrotherSdk.#observer = undefined;
}
});

BrotherSdk.#observer.observe(targetNode, {
attributes: true,
attributeFilter: ["class"],
});
}

async #isPrintReady(timeout: number = 3000): Promise<boolean> {
if (this.#ready) {
return true;
}

return new Promise((resolve, reject) => {
setTimeout(() => {
if (this.#ready) {
resolve(true);
} else {
reject(
new Error(
"Cannot establish printer communication: b-PAC extension missing or inactive. Install/enable.",
),
);
}
}, timeout);
});
}

/**
* **Method For Printing A Label**
*
* asynchronously print a label using the specified configurations.
*
* @param {Object} data
* an object containing key-value pairs, where each key represents an object ID,
* and the corresponding value is the text to be set on that object.
* @param {Object} config
* { object }
* @param {Number} [config.copies = 1]
* number of copies to be printed.
* @param {String} [config.printName = "BPAC-Document"]
* print document name.
* @param {String} [config.option = "default"]
* use the `PrinterOptions` enum for valid options. formally "quality"
*/
async print(data: Data, config: Config): Promise<boolean> {
const copies:number = config?.copies || 1;
const printName:string = config?.printName || "BPAC-Document";
const opts:number = getPrintOption(config?.option);

await this.#isPrintReady();

await openTemplate(this.templatePath);
await populateObjectsInTemplate(data);
await startPrint(printName, opts);
await printOut(copies);
await endPrint();
await closeTemplate();

return true;
}

/**
* **Method For Retrieving The Label's Image Data**
*
* asynchronously retrieves and returns Base64-encoded image data for a label.
*
* @param {object} data
* an object containing key-value pairs, where each key represents an object ID,
* and the corresponding value is the text to be set on that object.
* @param {object} options
* { object }
* @param {string} options.height
* if the vertical size (dpi) of the image to be acquired is specified as 0, it
* becomes a size that maintains the aspect ratio based on width.
* @param {string} options.width
* horizontal size (dpi) of the image to be acquired. If 0 is specified,
* it becomes a size that maintains the aspect ratio based on height.
* @returns {Promise<string>}
* a promise that resolves to a Base64 encoded string representing the image data.
*/
async getImageData(
data: Data,
options: ImageOptions,
): Promise<string> {
const height = options?.height || 0;
const width = options?.width || 0;

await this.#isPrintReady();
await openTemplate(this.templatePath);
await populateObjectsInTemplate(data);
const base64Data = await imageData(width, height);
await closeTemplate();

return `${base64Data}`;
}

/**
* **Retrieve The Printer's Name**
*
* asynchronously retrieves the printers name for the current context.
*
* @returns {Promise<string>}
* a promise that resolves with the name of the printer.
* @throws {Error}
* fails to get the printer name.
*
*/
async getPrinterName(): Promise<string> {
await this.#isPrintReady();
await openTemplate(this.templatePath);
const printer = getPrinterName();
await closeTemplate();
return printer;
}

/**
* **Export Label To File**
*
* asynchronously populate & export a copy of the file in various formats.
*
* @param {Object} data
* an object containing key-value pairs, where each key represents
* an object ID, and the corresponding value is the text to be set on that object.
* @param {String} [filePathOrFileName = ""]
* provide a file name or absolute path.
* - "myLabel.lbx" to be store in exportDir.
* - "C:/Templates/myLabel.lbx" to override the exportDir.
* @param {("default" | "lbx" | "lbl" | "lbi" | "bmp" | "paf")} [encoding = "default"]
* file encoding type.
* @param {Number} [resolution = 0]
* provide a resolution in dpi used for the conversion into bitmap format.
* Specifies the resolution of the output device.
* (Screen: 72 or 96; output to SC-2000: 600)
* If a value of 0 is specified, the printer resolution is used.
*
* The resolution param is only valid for LBI and BMP formats.
* @returns {Promise<boolean>}
* @throws {Error}
* fails to export.
*/
async export(
data: Data = {},
filePathOrFileName: string = "",
encoding: Encoding = Encoding.Default,
resolution: number = 0,
): Promise<boolean> {
const fileType = getEncodingOption(encoding);
const path:string = getAbsolutePath(
this.exportDir,
filePathOrFileName,
);

await this.#isPrintReady();
await openTemplate(this.templatePath);
await populateObjectsInTemplate(data);
const status = await exportTemplate(fileType, path, resolution);
await closeTemplate();

if (status) {
return true;
}

throw new Error(
"Export failed: Please check the export directory and filename.",
);
}
}
108 changes: 108 additions & 0 deletions src/adapter.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,108 @@
import * as bpac from "./vendor/bpac-v3.4";
import { Data, ObjectTypes } from "./types";

const doc = bpac.IDocument;

export const openTemplate = async (path: string): Promise<boolean> => {
const isOpen = await doc.Open(path);

if (isOpen) {
return true;
}

throw new Error(
"Failed to open template file, please check path and try again.",
);
};

export const getPrinterName = async (): Promise<string> => {
const printerName: string = await doc.GetPrinterName();
return printerName;
};

export const getPrinters = async (): Promise<string[]> => {
const obj = await doc.GetPrinter();
const printers = await obj.GetInstalledPrinters();
return printers;
};

// Fix these params
export const startPrint = async (printName:string, options:number): Promise<boolean> => {
const isComplete = await doc.StartPrint(printName, options);
return isComplete;
};

export const printOut = async (copies: number): Promise<boolean> => {
const isComplete = await doc.PrintOut(copies, 0);
return isComplete;
};

export const endPrint = async () => {
const isComplete = await doc.EndPrint();
return isComplete;
};

export const imageData = async (width:number, height:number): Promise<string> => {
const data = await doc.GetImageData(4, width, height);
return data;
};

export const closeTemplate = async (): Promise<boolean> => {
const isClosed = await doc.Close();

if (isClosed) {
return true;
}

throw new Error("Failed to close template file.");
};

export const exportTemplate = async (
type:number,
destination:string,
resolution:number,
): Promise<boolean> => {
const isComplete = await doc.Export(type, destination, resolution);
return isComplete;
};

export const populateObjectsInTemplate = async (data: Data): Promise<boolean> => {
const keys: string[] = Object.keys(data);

// eslint-disable-next-line no-restricted-syntax
for (const prop of keys) {
const value = data[prop];
const obj = await doc.GetObject(prop);

if (!obj) {
await closeTemplate();
throw new Error(
`There is no object in the specified template with the name of "${prop}".`,
);
}

const type = await obj.Type;

switch (type) {
case ObjectTypes.Text:
obj.Text = value;
break;
case ObjectTypes.Image:
await obj.SetData(0, value, 4);
break;
case ObjectTypes.Datetime:
await obj.SetData(0, value);
break;
case ObjectTypes.Barcode:
await doc.SetBarcodeData(0, value);
break;
case ObjectTypes.Clipart:
await obj.SetData(0, value, 0);
break;
default:
throw new Error(`Unknown type for "${prop}" prop.`);
}
}

return true;
};
Loading

0 comments on commit b7cf290

Please sign in to comment.