commit cae04eead5c7cfd79e4a0c56ba243fe344eea2f1 Author: Developer <91611@user.local> Date: Mon May 18 17:52:09 2026 +0800 init diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..b9d7f25 --- /dev/null +++ b/.gitignore @@ -0,0 +1,33 @@ +# Miscellaneous +*.class +*.log +*.pyc +*.swp +.DS_Store +.atom/ +.build/ +.buildlog/ +.history +.svn/ +.swiftpm/ +migrate_working_dir/ + +# IntelliJ related +*.iml +*.ipr +*.iws +.idea/ + +# The .vscode folder contains launch configuration and tasks you configure in +# VS Code which you may wish to be included in version control, so this line +# is commented out by default. +#.vscode/ + +# Flutter/Dart/Pub related +# Libraries should not include pubspec.lock, per https://dart.dev/guides/libraries/private-files#pubspeclock. +/pubspec.lock +**/doc/api/ +.dart_tool/ +.flutter-plugins-dependencies +/build/ +/coverage/ diff --git a/.metadata b/.metadata new file mode 100644 index 0000000..e771d71 --- /dev/null +++ b/.metadata @@ -0,0 +1,30 @@ +# This file tracks properties of this Flutter project. +# Used by Flutter tool to assess capabilities and perform upgrades etc. +# +# This file should be version controlled and should not be manually edited. + +version: + revision: "db50e20168db8fee486b9abf32fc912de3bc5b6a" + channel: "stable" + +project_type: plugin + +# Tracks metadata for the flutter migrate command +migration: + platforms: + - platform: root + create_revision: db50e20168db8fee486b9abf32fc912de3bc5b6a + base_revision: db50e20168db8fee486b9abf32fc912de3bc5b6a + - platform: android + create_revision: db50e20168db8fee486b9abf32fc912de3bc5b6a + base_revision: db50e20168db8fee486b9abf32fc912de3bc5b6a + + # User provided section + + # List of Local paths (relative to this file) that should be + # ignored by the migrate tool. + # + # Files that are not part of the templates will be ignored by default. + unmanaged_files: + - 'lib/main.dart' + - 'ios/Runner.xcodeproj/project.pbxproj' diff --git a/CHANGELOG.md b/CHANGELOG.md new file mode 100644 index 0000000..41cc7d8 --- /dev/null +++ b/CHANGELOG.md @@ -0,0 +1,3 @@ +## 0.0.1 + +* TODO: Describe initial release. diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..ba75c69 --- /dev/null +++ b/LICENSE @@ -0,0 +1 @@ +TODO: Add your license here. diff --git a/README.md b/README.md new file mode 100644 index 0000000..7d3195e --- /dev/null +++ b/README.md @@ -0,0 +1,15 @@ +# printer + +A new Flutter project. + +## Getting Started + +This project is a starting point for a Flutter +[plug-in package](https://flutter.dev/to/develop-plugins), +a specialized package that includes platform-specific implementation code for +Android and/or iOS. + +For help getting started with Flutter development, view the +[online documentation](https://docs.flutter.dev), which offers tutorials, +samples, guidance on mobile development, and a full API reference. + diff --git a/analysis_options.yaml b/analysis_options.yaml new file mode 100644 index 0000000..a5744c1 --- /dev/null +++ b/analysis_options.yaml @@ -0,0 +1,4 @@ +include: package:flutter_lints/flutter.yaml + +# Additional information about this file can be found at +# https://dart.dev/guides/language/analysis-options diff --git a/android/.gitignore b/android/.gitignore new file mode 100644 index 0000000..161bdcd --- /dev/null +++ b/android/.gitignore @@ -0,0 +1,9 @@ +*.iml +.gradle +/local.properties +/.idea/workspace.xml +/.idea/libraries +.DS_Store +/build +/captures +.cxx diff --git a/android/build.gradle.kts b/android/build.gradle.kts new file mode 100644 index 0000000..9fca796 --- /dev/null +++ b/android/build.gradle.kts @@ -0,0 +1,91 @@ +group = "com.xiarui.printer" +version = "1.0" + +buildscript { + repositories { + google() + mavenCentral() + } + + dependencies { + classpath("com.android.tools.build:gradle:8.11.1") + } +} + +allprojects { + repositories { + google() + mavenCentral() + flatDir { + dirs("libs") + } + } +} + +plugins { + id("com.android.library") +} + +android { + namespace = "com.xiarui.printer" + + compileSdk = 36 + + compileOptions { + sourceCompatibility = JavaVersion.VERSION_17 + targetCompatibility = JavaVersion.VERSION_17 + } + + defaultConfig { + minSdk = 24 + ndk { + abiFilters += listOf("arm64-v8a", "armeabi-v7a", "x86", "x86_64") + } + } + + packaging { + jniLibs { + pickFirsts += listOf( + "lib/arm64-v8a/libjnidispatch.so", + "lib/armeabi-v7a/libjnidispatch.so", + "lib/x86/libjnidispatch.so", + "lib/x86_64/libjnidispatch.so" + ) + } + } + + buildTypes { + release { + isMinifyEnabled = true + proguardFiles( + getDefaultProguardFile("proguard-android-optimize.txt"), + "proguard-rules.pro" + ) + } + } + + testOptions { + unitTests.all { + it.outputs.upToDateWhen { false } + + it.testLogging { + events("passed", "skipped", "failed", "standardOut", "standardError") + showStandardStreams = true + } + } + } +} + +dependencies { + testImplementation("junit:junit:4.13.2") + testImplementation("org.mockito:mockito-core:5.0.0") + implementation(files("libs/autoreplyprint.aar")) +} + +// Skip AAR bundling — local .aar dependency prevents bundle* tasks from succeeding. +// The AAR is available at runtime via flatDir repo; intermediate outputs are sufficient. +tasks.whenTaskAdded { + if (name.startsWith("bundleDebugAar") || name.startsWith("bundleReleaseAar")) { + enabled = false + } +} diff --git a/android/libs/autoreplyprint.aar b/android/libs/autoreplyprint.aar new file mode 100644 index 0000000..8f3e770 Binary files /dev/null and b/android/libs/autoreplyprint.aar differ diff --git a/android/proguard-rules.pro b/android/proguard-rules.pro new file mode 100644 index 0000000..1c4c8ff --- /dev/null +++ b/android/proguard-rules.pro @@ -0,0 +1,8 @@ +# Keep Flutter plugin classes +-keep class com.xiarui.printer.** { *; } +-keep class io.flutter.embedding.engine.plugins.** { *; } + +# Keep third-party SDKs +-keep class com.caysn.autoreplyprint.** { *; } +-keep class com.sun.jna.** { *; } +-keep class com.lvrenyang.** { *; } diff --git a/android/settings.gradle b/android/settings.gradle new file mode 100644 index 0000000..b4decdc --- /dev/null +++ b/android/settings.gradle @@ -0,0 +1 @@ +rootProject.name = 'printer' diff --git a/android/settings.gradle.kts b/android/settings.gradle.kts new file mode 100644 index 0000000..b4decdc --- /dev/null +++ b/android/settings.gradle.kts @@ -0,0 +1 @@ +rootProject.name = 'printer' diff --git a/android/src/main/AndroidManifest.xml b/android/src/main/AndroidManifest.xml new file mode 100644 index 0000000..29e4f99 --- /dev/null +++ b/android/src/main/AndroidManifest.xml @@ -0,0 +1,3 @@ + + diff --git a/android/src/main/java/com/xiarui/printer/PortHandleManager.java b/android/src/main/java/com/xiarui/printer/PortHandleManager.java new file mode 100644 index 0000000..892e147 --- /dev/null +++ b/android/src/main/java/com/xiarui/printer/PortHandleManager.java @@ -0,0 +1,186 @@ +package com.xiarui.printer; + +import com.caysn.autoreplyprint.AutoReplyPrint; +import com.sun.jna.Pointer; + +import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.atomic.AtomicInteger; + +/** + * Manages port handles for the Flutter plugin. + *

+ * Provides thread-safe registration, retrieval, and cleanup of native port handles. + * Each registered Pointer is assigned a unique integer handle starting from 1. + *

+ */ +public class PortHandleManager { + + /** + * Interface for port operations to enable unit testing without native library. + */ + public interface PortOperations { + /** + * Close a port. + * + * @param handle native pointer to the port + * @return true if successfully closed, false otherwise + */ + boolean closePort(Pointer handle); + + /** + * Check if a port is opened. + * + * @param handle native pointer to the port + * @return true if port is open, false otherwise + */ + boolean isPortOpened(Pointer handle); + } + + /** + * Default PortOperations implementation using AutoReplyPrint native SDK. + */ + private static class NativePortOperations implements PortOperations { + @Override + public boolean closePort(Pointer handle) { + return AutoReplyPrint.INSTANCE.CP_Port_Close(handle); + } + + @Override + public boolean isPortOpened(Pointer handle) { + return AutoReplyPrint.INSTANCE.CP_Port_IsOpened(handle); + } + } + + // Singleton instance + private static volatile PortHandleManager instance; + + // Handle registry: integer handle -> native Pointer + private final ConcurrentHashMap handleRegistry = new ConcurrentHashMap<>(); + + // Atomic counter starting from 1 + private final AtomicInteger counter = new AtomicInteger(1); + + // Port operations abstraction for testability + private final PortOperations portOps; + + /** + * Private constructor. Use {@link #getInstance()} for production, + * or inject custom PortOperations for testing. + */ + private PortHandleManager() { + this(new NativePortOperations()); + } + + /** + * Constructor with injectable PortOperations for testing. + * + * @param portOps port operations implementation + */ + PortHandleManager(PortOperations portOps) { + this.portOps = portOps; + } + + /** + * Returns the singleton instance using native AutoReplyPrint operations. + * + * @return singleton PortHandleManager + */ + public static PortHandleManager getInstance() { + PortHandleManager localInstance = instance; + if (localInstance == null) { + synchronized (PortHandleManager.class) { + localInstance = instance; + if (localInstance == null) { + instance = localInstance = new PortHandleManager(); + } + } + } + return localInstance; + } + + /** + * Registers a native port pointer and returns a unique integer handle. + *

+ * If the pointer is null or has a zero native value, registration fails + * and returns -1. + *

+ * + * @param pointer native port pointer to register + * @return positive integer handle on success, -1 on failure + */ + public int registerHandle(Pointer pointer) { + if (pointer == null || Pointer.nativeValue(pointer) == 0) { + return -1; + } + int handle = counter.getAndIncrement(); + handleRegistry.put(handle, pointer); + return handle; + } + + /** + * Retrieves the native pointer for a registered handle. + * + * @param handle integer handle to look up + * @return the registered Pointer, or null if not found + */ + public Pointer getHandle(int handle) { + return handleRegistry.get(handle); + } + + /** + * Closes a registered port and removes it from the registry. + *

+ * Uses ConcurrentHashMap.compute() for atomic removal to prevent + * race conditions (fixes C-04). The port is only closed if it was + * successfully removed from the registry, preventing double-close. + *

+ * + * @param handle integer handle to close + * @return true if port was found and closed, false otherwise + */ + public boolean closeHandle(int handle) { + Pointer pointer = handleRegistry.compute(handle, (key, existing) -> null); + if (pointer != null) { + return portOps.closePort(pointer); + } + return false; + } + + /** + * Checks if a handle is valid and the underlying port is still opened. + * + * @param handle integer handle to check + * @return true if handle exists and port is opened, false otherwise + */ + public boolean isHandleValid(int handle) { + Pointer pointer = handleRegistry.get(handle); + if (pointer == null) { + return false; + } + return portOps.isPortOpened(pointer); + } + + /** + * Closes all registered ports and clears the registry. + *

+ * Iterates through all registered handles, closing each one. + * Called during engine detachment to clean up resources. + *

+ */ + public void closeAll() { + ConcurrentHashMap snapshot = new ConcurrentHashMap<>(handleRegistry); + for (Integer handle : snapshot.keySet()) { + closeHandle(handle); + } + handleRegistry.clear(); + } + + /** + * Returns the underlying registry for testing purposes only. + * + * @return the handle registry map + */ + ConcurrentHashMap getRegistry() { + return handleRegistry; + } +} diff --git a/android/src/main/java/com/xiarui/printer/PrinterPlugin.java b/android/src/main/java/com/xiarui/printer/PrinterPlugin.java new file mode 100644 index 0000000..2b04a5f --- /dev/null +++ b/android/src/main/java/com/xiarui/printer/PrinterPlugin.java @@ -0,0 +1,887 @@ +package com.xiarui.printer; + +import androidx.annotation.NonNull; + +import com.caysn.autoreplyprint.AutoReplyPrint; +import com.sun.jna.Pointer; +import com.sun.jna.WString; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; + +import android.os.Handler; +import android.os.Looper; + +import io.flutter.embedding.engine.plugins.FlutterPlugin; +import io.flutter.plugin.common.MethodCall; +import io.flutter.plugin.common.MethodChannel; +import io.flutter.plugin.common.MethodChannel.MethodCallHandler; +import io.flutter.plugin.common.MethodChannel.Result; + +/** + * Flutter plugin for printer communication via serial/USB ports. + *

+ * Provides MethodChannel handlers for opening, closing, and enumerating + * printer ports using the autoreplyprint AAR SDK. + *

+ */ +public class PrinterPlugin implements FlutterPlugin, MethodCallHandler { + /// The MethodChannel that will the communication between Flutter and native Android + /// + /// This local reference serves to register the plugin with the Flutter Engine and unregister it + /// when the Flutter Engine is detached from the Activity + private MethodChannel channel; + private PortHandleManager portHandleManager; + + /** + * Valid range for baud rate: 1200 to 921600. + */ + private static final int MIN_BAUD_RATE = 1200; + private static final int MAX_BAUD_RATE = 921600; + + /** + * Valid range for data bits: 5 to 8. + */ + private static final int MIN_DATA_BITS = 5; + private static final int MAX_DATA_BITS = 8; + + @Override + public void onAttachedToEngine(@NonNull FlutterPluginBinding flutterPluginBinding) { + channel = new MethodChannel(flutterPluginBinding.getBinaryMessenger(), "printer"); + channel.setMethodCallHandler(this); + portHandleManager = PortHandleManager.getInstance(); + } + + @Override + public void onMethodCall(@NonNull MethodCall call, @NonNull Result result) { + switch (call.method) { + case "getPlatformVersion": + result.success("Android " + android.os.Build.VERSION.RELEASE); + break; + case "openComPort": + handleOpenComPort(call, result); + break; + case "openUsbPort": + handleOpenUsbPort(call, result); + break; + case "closePort": + handleClosePort(call, result); + break; + case "isPortOpened": + handleIsPortOpened(call, result); + break; + case "enumComPorts": + handleEnumComPorts(call, result); + break; + case "enumUsbPorts": + handleEnumUsbPorts(call, result); + break; + case "setMultiByteMode": + handleSetMultiByteMode(call, result); + break; + case "setMultiByteEncoding": + handleSetMultiByteEncoding(call, result); + break; + case "printText": + handlePrintText(call, result); + break; + case "setAlignment": + handleSetAlignment(call, result); + break; + case "setTextScale": + handleSetTextScale(call, result); + break; + case "setTextBold": + handleSetTextBold(call, result); + break; + case "setTextUnderline": + handleSetTextUnderline(call, result); + break; + case "feedLine": + handleFeedLine(call, result); + break; + case "feedDot": + handleFeedDot(call, result); + break; + case "halfCutPaper": + handleHalfCutPaper(call, result); + break; + case "fullCutPaper": + handleFullCutPaper(call, result); + break; + default: + result.notImplemented(); + break; + } + } + + /** + * Handles openComPort method call. + *

+ * Opens a serial port with the specified parameters on a background thread + * and returns the result on the main thread via Handler. + * Validates portName (non-empty), baudRate (1200-921600), and dataBits (5-8). + * Implements PORT-01, PORT-02, T-01-01. + *

+ * + * @param call the method call containing port parameters + * @param result the result callback + */ + private void handleOpenComPort(@NonNull MethodCall call, @NonNull Result result) { + String portName = call.argument("portName"); + Integer baudRate = call.argument("baudRate"); + Integer dataBits = call.argument("dataBits"); + Integer parity = call.argument("parity"); + Integer stopBits = call.argument("stopBits"); + Integer flowControl = call.argument("flowControl"); + Integer autoReplyMode = call.argument("autoReplyMode"); + + // Validate port name (T-01-01) + if (portName == null || portName.isEmpty()) { + result.error("INVALID_ARGUMENT", "portName is required and cannot be empty", null); + return; + } + + // Validate baud rate + if (baudRate == null || baudRate < MIN_BAUD_RATE || baudRate > MAX_BAUD_RATE) { + result.error( + "INVALID_ARGUMENT", + "baudRate must be between " + MIN_BAUD_RATE + " and " + MAX_BAUD_RATE, + null + ); + return; + } + + // Validate data bits (C-02 fix) + int db = dataBits != null ? dataBits : 8; + if (db < MIN_DATA_BITS || db > MAX_DATA_BITS) { + result.error( + "INVALID_ARGUMENT", + "dataBits must be between " + MIN_DATA_BITS + " and " + MAX_DATA_BITS, + null + ); + return; + } + + // Defaults + int p = parity != null ? parity : 0; + int sb = stopBits != null ? stopBits : 0; + int fc = flowControl != null ? flowControl : 0; + int arm = autoReplyMode != null ? autoReplyMode : 1; + + // Execute on background thread (C-01 fix) + final String finalPortName = portName; + final int finalBaudRate = baudRate; + final int finalDataBits = db; + final int finalParity = p; + final int finalStopBits = sb; + final int finalFlowControl = fc; + final int finalAutoReplyMode = arm; + final Handler mainHandler = new Handler(Looper.getMainLooper()); + + new Thread(() -> { + try { + Pointer pointer = AutoReplyPrint.INSTANCE.CP_Port_OpenCom( + finalPortName, + finalBaudRate, + finalDataBits, + finalParity, + finalStopBits, + finalFlowControl, + finalAutoReplyMode + ); + + if (pointer == null || Pointer.nativeValue(pointer) == 0) { + final Pointer finalPointer = pointer; + mainHandler.post(() -> + result.error("PORT_OPEN_FAILED", "Failed to open port: " + finalPortName, null) + ); + return; + } + + int dartHandle = portHandleManager.registerHandle(pointer); + mainHandler.post(() -> result.success(dartHandle)); + } catch (UnsatisfiedLinkError e) { + mainHandler.post(() -> + result.error("NATIVE_LIBRARY_ERROR", e.getMessage(), null) + ); + } catch (Exception e) { + mainHandler.post(() -> + result.error("UNKNOWN_ERROR", e.getMessage(), null) + ); + } + }).start(); + } + + /** + * Handles openUsbPort method call. + *

+ * Opens a USB port on a background thread and returns the result on the main thread. + * Validates portName (non-empty) and autoReplyMode (0 or 1). + * Implements T-01-02. + *

+ * + * @param call the method call containing port parameters + * @param result the result callback + */ + private void handleOpenUsbPort(@NonNull MethodCall call, @NonNull Result result) { + String portName = call.argument("portName"); + Integer autoReplyMode = call.argument("autoReplyMode"); + + // Validate port name (T-01-02) + if (portName == null || portName.isEmpty()) { + result.error("INVALID_ARGUMENT", "portName is required and cannot be empty", null); + return; + } + + int arm = autoReplyMode != null ? autoReplyMode : 1; + + final String finalPortName = portName; + final int finalAutoReplyMode = arm; + final Handler mainHandler = new Handler(Looper.getMainLooper()); + + new Thread(() -> { + try { + Pointer pointer = AutoReplyPrint.INSTANCE.CP_Port_OpenUsb( + finalPortName, + finalAutoReplyMode + ); + + if (pointer == null || Pointer.nativeValue(pointer) == 0) { + mainHandler.post(() -> + result.error("PORT_OPEN_FAILED", "Failed to open USB port: " + finalPortName, null) + ); + return; + } + + int dartHandle = portHandleManager.registerHandle(pointer); + mainHandler.post(() -> result.success(dartHandle)); + } catch (UnsatisfiedLinkError e) { + mainHandler.post(() -> + result.error("NATIVE_LIBRARY_ERROR", e.getMessage(), null) + ); + } catch (Exception e) { + mainHandler.post(() -> + result.error("UNKNOWN_ERROR", e.getMessage(), null) + ); + } + }).start(); + } + + /** + * Handles closePort method call. + *

+ * Closes a port by its integer handle. + * Implements PORT-03, T-01-03. + *

+ * + * @param call the method call containing handle + * @param result the result callback + */ + private void handleClosePort(@NonNull MethodCall call, @NonNull Result result) { + Integer handle = call.argument("handle"); + + if (handle == null) { + result.error("INVALID_ARGUMENT", "handle is required", null); + return; + } + + boolean success = portHandleManager.closeHandle(handle); + result.success(success); + } + + /** + * Handles isPortOpened method call. + *

+ * Checks if a port is currently opened by its integer handle. + *

+ * + * @param call the method call containing handle + * @param result the result callback + */ + private void handleIsPortOpened(@NonNull MethodCall call, @NonNull Result result) { + Integer handle = call.argument("handle"); + + if (handle == null) { + result.error("INVALID_ARGUMENT", "handle is required", null); + return; + } + + boolean isValid = portHandleManager.isHandleValid(handle); + result.success(isValid); + } + + /** + * Handles enumComPorts method call. + *

+ * Enumerates available serial ports and returns a list of port names. + *

+ * + * @param call the method call (no arguments) + * @param result the result callback + */ + private void handleEnumComPorts(@NonNull MethodCall call, @NonNull Result result) { + try { + String[] ports = AutoReplyPrint.CP_Port_EnumCom_Helper.EnumCom(); + List portList = (ports != null) ? Arrays.asList(ports) : new ArrayList<>(); + result.success(portList); + } catch (UnsatisfiedLinkError e) { + result.error("NATIVE_LIBRARY_ERROR", e.getMessage(), null); + } catch (Exception e) { + result.error("UNKNOWN_ERROR", e.getMessage(), null); + } + } + + /** + * Handles enumUsbPorts method call. + *

+ * Enumerates available USB ports and returns a list of port names. + *

+ * + * @param call the method call (no arguments) + * @param result the result callback + */ + private void handleEnumUsbPorts(@NonNull MethodCall call, @NonNull Result result) { + try { + String[] ports = AutoReplyPrint.CP_Port_EnumUsb_Helper.EnumUsb(); + List portList = (ports != null) ? Arrays.asList(ports) : new ArrayList<>(); + result.success(portList); + } catch (UnsatisfiedLinkError e) { + result.error("NATIVE_LIBRARY_ERROR", e.getMessage(), null); + } catch (Exception e) { + result.error("UNKNOWN_ERROR", e.getMessage(), null); + } + } + + /** + * Handles setMultiByteMode method call. + *

+ * Sets the printer to multi-byte encoding mode. + * Must be called before setMultiByteEncoding and printText for Chinese/multibyte text. + * Implements PRINT-01. + *

+ * + * @param call the method call containing handle + * @param result the result callback, returns true on success + */ + private void handleSetMultiByteMode(@NonNull MethodCall call, @NonNull Result result) { + Integer handle = call.argument("handle"); + + if (handle == null) { + result.error("INVALID_ARGUMENT", "handle is required", null); + return; + } + + Pointer pointer = portHandleManager.getHandle(handle); + if (pointer == null) { + result.error("PORT_CLOSED", "Port handle not found: " + handle, null); + return; + } + + try { + boolean success = AutoReplyPrint.INSTANCE.CP_Pos_SetMultiByteMode(pointer); + if (!success) { + result.error("PRINT_FAILED", "setMultiByteMode failed", null); + return; + } + result.success(success); + } catch (UnsatisfiedLinkError e) { + result.error("NATIVE_LIBRARY_ERROR", e.getMessage(), null); + } catch (Exception e) { + result.error("UNKNOWN_ERROR", e.getMessage(), null); + } + } + + /** + * Handles setMultiByteEncoding method call. + *

+ * Sets the multi-byte character encoding for the printer. + * Valid encoding values: 0=GBK, 1=UTF8, 3=BIG5, 4=ShiftJIS, 5=EUC_KR. + * Implements PRINT-01. + *

+ * + * @param call the method call containing handle and encoding (int) + * @param result the result callback, returns true on success + */ + private void handleSetMultiByteEncoding(@NonNull MethodCall call, @NonNull Result result) { + Integer handle = call.argument("handle"); + Integer encoding = call.argument("encoding"); + + if (handle == null) { + result.error("INVALID_ARGUMENT", "handle is required", null); + return; + } + + if (encoding == null) { + result.error("INVALID_ARGUMENT", "encoding is required", null); + return; + } + + if (encoding != 0 && encoding != 1 && encoding != 3 && encoding != 4 && encoding != 5) { + result.error("INVALID_ARGUMENT", + "encoding must be one of {0=GBK, 1=UTF8, 3=BIG5, 4=ShiftJIS, 5=EUC_KR}", null); + return; + } + + Pointer pointer = portHandleManager.getHandle(handle); + if (pointer == null) { + result.error("PORT_CLOSED", "Port handle not found: " + handle, null); + return; + } + + try { + boolean success = AutoReplyPrint.INSTANCE.CP_Pos_SetMultiByteEncoding(pointer, encoding); + if (!success) { + result.error("PRINT_FAILED", "setMultiByteEncoding failed", null); + return; + } + result.success(success); + } catch (UnsatisfiedLinkError e) { + result.error("NATIVE_LIBRARY_ERROR", e.getMessage(), null); + } catch (Exception e) { + result.error("UNKNOWN_ERROR", e.getMessage(), null); + } + } + + /** + * Handles printText method call. + *

+ * Prints text using UTF-8 encoding (WString). + * Requires multi-byte mode and UTF-8 encoding to be set beforehand for correct Chinese character output. + * Implements PRINT-02, T-02-03. + *

+ * + * @param call the method call containing handle and text (String) + * @param result the result callback, returns true on success + */ + private void handlePrintText(@NonNull MethodCall call, @NonNull Result result) { + Integer handle = call.argument("handle"); + String text = call.argument("text"); + + if (handle == null) { + result.error("INVALID_ARGUMENT", "handle is required", null); + return; + } + + if (text == null || text.isEmpty()) { + result.error("INVALID_ARGUMENT", "text is required and cannot be empty", null); + return; + } + + Pointer pointer = portHandleManager.getHandle(handle); + if (pointer == null) { + result.error("PORT_CLOSED", "Port handle not found: " + handle, null); + return; + } + + try { + boolean success = AutoReplyPrint.INSTANCE.CP_Pos_PrintTextInUTF8(pointer, new WString(text)); + if (!success) { + result.error("PRINT_FAILED", "printText failed (length: " + text.length() + ")", null); + return; + } + result.success(success); + } catch (UnsatisfiedLinkError e) { + result.error("NATIVE_LIBRARY_ERROR", e.getMessage(), null); + } catch (Exception e) { + result.error("UNKNOWN_ERROR", e.getMessage(), null); + } + } + + /** + * Handles setAlignment method call. + *

+ * Sets the print alignment. + * Valid values: 0=left, 1=center, 2=right. + * Implements PRINT-04. + *

+ * + * @param call the method call containing handle and alignment (int) + * @param result the result callback, returns true on success + */ + private void handleSetAlignment(@NonNull MethodCall call, @NonNull Result result) { + Integer handle = call.argument("handle"); + Integer alignment = call.argument("alignment"); + + if (handle == null) { + result.error("INVALID_ARGUMENT", "handle is required", null); + return; + } + + if (alignment == null) { + result.error("INVALID_ARGUMENT", "alignment is required", null); + return; + } + + if (alignment != 0 && alignment != 1 && alignment != 2) { + result.error("INVALID_ARGUMENT", "alignment must be one of {0=left, 1=center, 2=right}", null); + return; + } + + Pointer pointer = portHandleManager.getHandle(handle); + if (pointer == null) { + result.error("PORT_CLOSED", "Port handle not found: " + handle, null); + return; + } + + try { + boolean success = AutoReplyPrint.INSTANCE.CP_Pos_SetAlignment(pointer, alignment); + if (!success) { + result.error("PRINT_FAILED", "setAlignment failed", null); + return; + } + result.success(success); + } catch (UnsatisfiedLinkError e) { + result.error("NATIVE_LIBRARY_ERROR", e.getMessage(), null); + } catch (Exception e) { + result.error("UNKNOWN_ERROR", e.getMessage(), null); + } + } + + /** + * Handles setTextScale method call. + *

+ * Sets text width and height scale factors. + * Both widthScale and heightScale must be in range [1, 8]. + * Implements PRINT-04, T-02-02. + *

+ * + * @param call the method call containing handle, widthScale (int), heightScale (int) + * @param result the result callback, returns true on success + */ + private void handleSetTextScale(@NonNull MethodCall call, @NonNull Result result) { + Integer handle = call.argument("handle"); + Integer widthScale = call.argument("widthScale"); + Integer heightScale = call.argument("heightScale"); + + if (handle == null) { + result.error("INVALID_ARGUMENT", "handle is required", null); + return; + } + + if (widthScale == null) { + result.error("INVALID_ARGUMENT", "widthScale is required", null); + return; + } + + if (heightScale == null) { + result.error("INVALID_ARGUMENT", "heightScale is required", null); + return; + } + + if (widthScale < 1 || widthScale > 8) { + result.error("INVALID_ARGUMENT", "widthScale must be in range [1, 8]", null); + return; + } + + if (heightScale < 1 || heightScale > 8) { + result.error("INVALID_ARGUMENT", "heightScale must be in range [1, 8]", null); + return; + } + + Pointer pointer = portHandleManager.getHandle(handle); + if (pointer == null) { + result.error("PORT_CLOSED", "Port handle not found: " + handle, null); + return; + } + + try { + boolean success = AutoReplyPrint.INSTANCE.CP_Pos_SetTextScale(pointer, widthScale, heightScale); + if (!success) { + result.error("PRINT_FAILED", "setTextScale failed", null); + return; + } + result.success(success); + } catch (UnsatisfiedLinkError e) { + result.error("NATIVE_LIBRARY_ERROR", e.getMessage(), null); + } catch (Exception e) { + result.error("UNKNOWN_ERROR", e.getMessage(), null); + } + } + + /** + * Handles setTextBold method call. + *

+ * Sets text bold printing. + * Valid values: 0=off, 1=on. + * Implements PRINT-04. + *

+ * + * @param call the method call containing handle and bold (int, 0 or 1) + * @param result the result callback, returns true on success + */ + private void handleSetTextBold(@NonNull MethodCall call, @NonNull Result result) { + Integer handle = call.argument("handle"); + Integer bold = call.argument("bold"); + + if (handle == null) { + result.error("INVALID_ARGUMENT", "handle is required", null); + return; + } + + if (bold == null) { + result.error("INVALID_ARGUMENT", "bold is required", null); + return; + } + + if (bold != 0 && bold != 1) { + result.error("INVALID_ARGUMENT", "bold must be one of {0=off, 1=on}", null); + return; + } + + Pointer pointer = portHandleManager.getHandle(handle); + if (pointer == null) { + result.error("PORT_CLOSED", "Port handle not found: " + handle, null); + return; + } + + try { + boolean success = AutoReplyPrint.INSTANCE.CP_Pos_SetTextBold(pointer, bold); + if (!success) { + result.error("PRINT_FAILED", "setTextBold failed", null); + return; + } + result.success(success); + } catch (UnsatisfiedLinkError e) { + result.error("NATIVE_LIBRARY_ERROR", e.getMessage(), null); + } catch (Exception e) { + result.error("UNKNOWN_ERROR", e.getMessage(), null); + } + } + + /** + * Handles setTextUnderline method call. + *

+ * Sets text underline style. + * Valid values: 0=none, 1=1-dot, 2=2-dot. + * Implements PRINT-04, T-02-02. + *

+ * + * @param call the method call containing handle and underline (int) + * @param result the result callback, returns true on success + */ + private void handleSetTextUnderline(@NonNull MethodCall call, @NonNull Result result) { + Integer handle = call.argument("handle"); + Integer underline = call.argument("underline"); + + if (handle == null) { + result.error("INVALID_ARGUMENT", "handle is required", null); + return; + } + + if (underline == null) { + result.error("INVALID_ARGUMENT", "underline is required", null); + return; + } + + if (underline != 0 && underline != 1 && underline != 2) { + result.error("INVALID_ARGUMENT", "underline must be one of {0=none, 1=1-dot, 2=2-dot}", null); + return; + } + + Pointer pointer = portHandleManager.getHandle(handle); + if (pointer == null) { + result.error("PORT_CLOSED", "Port handle not found: " + handle, null); + return; + } + + try { + boolean success = AutoReplyPrint.INSTANCE.CP_Pos_SetTextUnderline(pointer, underline); + if (!success) { + result.error("PRINT_FAILED", "setTextUnderline failed", null); + return; + } + result.success(success); + } catch (UnsatisfiedLinkError e) { + result.error("NATIVE_LIBRARY_ERROR", e.getMessage(), null); + } catch (Exception e) { + result.error("UNKNOWN_ERROR", e.getMessage(), null); + } + } + + /** + * Handles feedLine method call. + *

+ * Feeds paper by specified number of lines. + * numLines must be greater than 0. + * Implements PRINT-05, T-02-02. + *

+ * + * @param call the method call containing handle and numLines (int) + * @param result the result callback, returns true on success + */ + private void handleFeedLine(@NonNull MethodCall call, @NonNull Result result) { + Integer handle = call.argument("handle"); + Integer numLines = call.argument("numLines"); + + if (handle == null) { + result.error("INVALID_ARGUMENT", "handle is required", null); + return; + } + + if (numLines == null) { + result.error("INVALID_ARGUMENT", "numLines is required", null); + return; + } + + if (numLines <= 0) { + result.error("INVALID_ARGUMENT", "numLines must be greater than 0", null); + return; + } + + Pointer pointer = portHandleManager.getHandle(handle); + if (pointer == null) { + result.error("PORT_CLOSED", "Port handle not found: " + handle, null); + return; + } + + try { + boolean success = AutoReplyPrint.INSTANCE.CP_Pos_FeedLine(pointer, numLines); + if (!success) { + result.error("PRINT_FAILED", "feedLine failed", null); + return; + } + result.success(success); + } catch (UnsatisfiedLinkError e) { + result.error("NATIVE_LIBRARY_ERROR", e.getMessage(), null); + } catch (Exception e) { + result.error("UNKNOWN_ERROR", e.getMessage(), null); + } + } + + /** + * Handles feedDot method call. + *

+ * Feeds paper by specified number of dots. + * numDots must be greater than 0. + * Implements PRINT-05, T-02-02. + *

+ * + * @param call the method call containing handle and numDots (int) + * @param result the result callback, returns true on success + */ + private void handleFeedDot(@NonNull MethodCall call, @NonNull Result result) { + Integer handle = call.argument("handle"); + Integer numDots = call.argument("numDots"); + + if (handle == null) { + result.error("INVALID_ARGUMENT", "handle is required", null); + return; + } + + if (numDots == null) { + result.error("INVALID_ARGUMENT", "numDots is required", null); + return; + } + + if (numDots <= 0) { + result.error("INVALID_ARGUMENT", "numDots must be greater than 0", null); + return; + } + + Pointer pointer = portHandleManager.getHandle(handle); + if (pointer == null) { + result.error("PORT_CLOSED", "Port handle not found: " + handle, null); + return; + } + + try { + boolean success = AutoReplyPrint.INSTANCE.CP_Pos_FeedDot(pointer, numDots); + if (!success) { + result.error("PRINT_FAILED", "feedDot failed", null); + return; + } + result.success(success); + } catch (UnsatisfiedLinkError e) { + result.error("NATIVE_LIBRARY_ERROR", e.getMessage(), null); + } catch (Exception e) { + result.error("UNKNOWN_ERROR", e.getMessage(), null); + } + } + + /** + * Handles halfCutPaper method call. + *

+ * Triggers half-cut on the paper cutter. + * Implements PRINT-06. + *

+ * + * @param call the method call containing handle + * @param result the result callback, returns true on success + */ + private void handleHalfCutPaper(@NonNull MethodCall call, @NonNull Result result) { + Integer handle = call.argument("handle"); + + if (handle == null) { + result.error("INVALID_ARGUMENT", "handle is required", null); + return; + } + + Pointer pointer = portHandleManager.getHandle(handle); + if (pointer == null) { + result.error("PORT_CLOSED", "Port handle not found: " + handle, null); + return; + } + + try { + boolean success = AutoReplyPrint.INSTANCE.CP_Pos_HalfCutPaper(pointer); + if (!success) { + result.error("PRINT_FAILED", "halfCutPaper failed", null); + return; + } + result.success(success); + } catch (UnsatisfiedLinkError e) { + result.error("NATIVE_LIBRARY_ERROR", e.getMessage(), null); + } catch (Exception e) { + result.error("UNKNOWN_ERROR", e.getMessage(), null); + } + } + + /** + * Handles fullCutPaper method call. + *

+ * Triggers full-cut on the paper cutter. + * Implements PRINT-06. + *

+ * + * @param call the method call containing handle + * @param result the result callback, returns true on success + */ + private void handleFullCutPaper(@NonNull MethodCall call, @NonNull Result result) { + Integer handle = call.argument("handle"); + + if (handle == null) { + result.error("INVALID_ARGUMENT", "handle is required", null); + return; + } + + Pointer pointer = portHandleManager.getHandle(handle); + if (pointer == null) { + result.error("PORT_CLOSED", "Port handle not found: " + handle, null); + return; + } + + try { + boolean success = AutoReplyPrint.INSTANCE.CP_Pos_FullCutPaper(pointer); + if (!success) { + result.error("PRINT_FAILED", "fullCutPaper failed", null); + return; + } + result.success(success); + } catch (UnsatisfiedLinkError e) { + result.error("NATIVE_LIBRARY_ERROR", e.getMessage(), null); + } catch (Exception e) { + result.error("UNKNOWN_ERROR", e.getMessage(), null); + } + } + + @Override + public void onDetachedFromEngine(@NonNull FlutterPluginBinding binding) { + channel.setMethodCallHandler(null); + // Clean up all open ports when engine detaches (T-01-05) + if (portHandleManager != null) { + portHandleManager.closeAll(); + } + } +} diff --git a/android/src/test/java/com/xiarui/printer/PortHandleManagerTest.java b/android/src/test/java/com/xiarui/printer/PortHandleManagerTest.java new file mode 100644 index 0000000..97486b4 --- /dev/null +++ b/android/src/test/java/com/xiarui/printer/PortHandleManagerTest.java @@ -0,0 +1,198 @@ +package com.xiarui.printer; + +import static org.junit.Assert.*; + +import com.sun.jna.Pointer; +import org.junit.Before; +import org.junit.Test; + +/** + * Unit tests for PortHandleManager. + * Uses a mock PortOperations to avoid dependency on native library. + */ +public class PortHandleManagerTest { + + private PortHandleManager manager; + private MockPortOperations mockOps; + + @Before + public void setUp() { + mockOps = new MockPortOperations(); + manager = new PortHandleManager(mockOps); + } + + /** + * Test 1: registerHandle with valid pointer returns positive handle. + */ + @Test + public void testRegisterHandle_ValidPointer_ReturnsPositiveHandle() { + // Create a pointer with non-zero native value + Pointer pointer = new Pointer(0x1234L); + + int handle = manager.registerHandle(pointer); + + assertTrue("Handle should be > 0", handle > 0); + } + + /** + * Test 2: registerHandle with null pointer returns -1. + */ + @Test + public void testRegisterHandle_NullPointer_ReturnsMinusOne() { + int handle = manager.registerHandle(null); + + assertEquals("Null pointer should return -1", -1, handle); + } + + /** + * Test 2b: registerHandle with zero native value pointer returns -1. + */ + @Test + public void testRegisterHandle_ZeroNativeValue_ReturnsMinusOne() { + Pointer pointer = new Pointer(0L); + + int handle = manager.registerHandle(pointer); + + assertEquals("Zero native value should return -1", -1, handle); + } + + /** + * Test 3: getHandle returns the registered pointer. + */ + @Test + public void testGetHandle_RegisteredHandle_ReturnsPointer() { + Pointer pointer = new Pointer(0x5678L); + int handle = manager.registerHandle(pointer); + + Pointer retrieved = manager.getHandle(handle); + + assertEquals("Retrieved pointer should match registered pointer", pointer, retrieved); + } + + /** + * Test 4: getHandle for unregistered handle returns null. + */ + @Test + public void testGetHandle_UnregisteredHandle_ReturnsNull() { + Pointer retrieved = manager.getHandle(999); + + assertNull("Unregistered handle should return null", retrieved); + } + + /** + * Test 5: closeHandle closes the port and removes registration. + */ + @Test + public void testCloseHandle_RegisteredHandle_ReturnsTrue() { + Pointer pointer = new Pointer(0xABCDL); + int handle = manager.registerHandle(pointer); + + boolean result = manager.closeHandle(handle); + + assertTrue("closeHandle should return true for registered handle", result); + assertTrue("Mock close should have been called", mockOps.closeCalled); + assertNull("Handle should be removed after close", manager.getHandle(handle)); + } + + /** + * Test 6: closeHandle for unregistered handle returns false. + */ + @Test + public void testCloseHandle_UnregisteredHandle_ReturnsFalse() { + boolean result = manager.closeHandle(999); + + assertFalse("closeHandle should return false for unregistered handle", result); + assertFalse("Mock close should NOT have been called", mockOps.closeCalled); + } + + /** + * Test 7: closeAll closes all registered ports and clears registry. + */ + @Test + public void testCloseAll_ClosesAllPorts() { + Pointer p1 = new Pointer(0x1111L); + Pointer p2 = new Pointer(0x2222L); + int h1 = manager.registerHandle(p1); + int h2 = manager.registerHandle(p2); + + manager.closeAll(); + + assertNull("Handle 1 should be cleared", manager.getHandle(h1)); + assertNull("Handle 2 should be cleared", manager.getHandle(h2)); + assertTrue("Mock close should have been called", mockOps.closeCalled); + } + + /** + * Test 8: Concurrent registrations return unique handles. + */ + @Test + public void testRegisterHandle_Concurrent_ReturnsUniqueHandles() throws InterruptedException { + final int threadCount = 10; + final int[] handles = new int[threadCount]; + Thread[] threads = new Thread[threadCount]; + + for (int i = 0; i < threadCount; i++) { + final int index = i; + threads[i] = new Thread(() -> { + Pointer pointer = new Pointer(0x1000L + index); + handles[index] = manager.registerHandle(pointer); + }); + } + + for (Thread t : threads) t.start(); + for (Thread t : threads) t.join(); + + // All handles should be unique + for (int i = 0; i < threadCount; i++) { + for (int j = i + 1; j < threadCount; j++) { + assertNotEquals( + "Handles should be unique: " + handles[i] + " vs " + handles[j], + handles[i], handles[j] + ); + } + } + } + + /** + * Test 9: isHandleValid returns true for valid opened handle. + */ + @Test + public void testIsHandleValid_OpenedHandle_ReturnsTrue() { + Pointer pointer = new Pointer(0xEEEEL); + int handle = manager.registerHandle(pointer); + mockOps.isOpenedResult = true; + + boolean valid = manager.isHandleValid(handle); + + assertTrue("Opened handle should be valid", valid); + } + + /** + * Test 10: isHandleValid returns false for unregistered handle. + */ + @Test + public void testIsHandleValid_UnregisteredHandle_ReturnsFalse() { + boolean valid = manager.isHandleValid(999); + + assertFalse("Unregistered handle should not be valid", valid); + } + + /** + * Mock PortOperations for testing without native library. + */ + static class MockPortOperations implements PortHandleManager.PortOperations { + boolean closeCalled = false; + boolean isOpenedResult = true; + + @Override + public boolean closePort(Pointer handle) { + closeCalled = true; + return true; + } + + @Override + public boolean isPortOpened(Pointer handle) { + return isOpenedResult; + } + } +} diff --git a/android/src/test/java/com/xiarui/printer/PrinterPluginTest.java b/android/src/test/java/com/xiarui/printer/PrinterPluginTest.java new file mode 100644 index 0000000..04ff31f --- /dev/null +++ b/android/src/test/java/com/xiarui/printer/PrinterPluginTest.java @@ -0,0 +1,178 @@ +package com.xiarui.printer; + +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.verify; +import static org.junit.Assert.*; + +import io.flutter.plugin.common.MethodCall; +import io.flutter.plugin.common.MethodChannel; +import org.junit.Test; +import org.junit.Before; + +/** + * Unit tests for PrinterPlugin. + * Tests parameter validation and method routing. + */ +public class PrinterPluginTest { + + private PrinterPlugin plugin; + + @Before + public void setUp() { + plugin = new PrinterPlugin(); + } + + @Test + public void onMethodCall_getPlatformVersion_returnsExpectedValue() { + final MethodCall call = new MethodCall("getPlatformVersion", null); + MethodChannel.Result mockResult = mock(MethodChannel.Result.class); + plugin.onMethodCall(call, mockResult); + + verify(mockResult).success("Android " + android.os.Build.VERSION.RELEASE); + } + + @Test + public void onMethodCall_openComPort_emptyPortName_returnsInvalidArgument() { + MethodChannel.Result mockResult = mock(MethodChannel.Result.class); + MethodCall call = new MethodCall("openComPort", new java.util.HashMap() {{ + put("portName", ""); + put("baudRate", 115200); + }}); + + plugin.onMethodCall(call, mockResult); + + verify(mockResult).error("INVALID_ARGUMENT", org.mockito.ArgumentMatchers.contains("portName"), org.mockito.ArgumentMatchers.isNull()); + } + + @Test + public void onMethodCall_openComPort_nullPortName_returnsInvalidArgument() { + MethodChannel.Result mockResult = mock(MethodChannel.Result.class); + MethodCall call = new MethodCall("openComPort", new java.util.HashMap() {{ + put("portName", null); + put("baudRate", 115200); + }}); + + plugin.onMethodCall(call, mockResult); + + verify(mockResult).error("INVALID_ARGUMENT", org.mockito.ArgumentMatchers.contains("portName"), org.mockito.ArgumentMatchers.isNull()); + } + + @Test + public void onMethodCall_openComPort_invalidBaudRate_returnsInvalidArgument() { + MethodChannel.Result mockResult = mock(MethodChannel.Result.class); + MethodCall call = new MethodCall("openComPort", new java.util.HashMap() {{ + put("portName", "/dev/ttyS0"); + put("baudRate", 100); // Below minimum 1200 + }}); + + plugin.onMethodCall(call, mockResult); + + verify(mockResult).error("INVALID_ARGUMENT", org.mockito.ArgumentMatchers.contains("baudRate"), org.mockito.ArgumentMatchers.isNull()); + } + + @Test + public void onMethodCall_openComPort_invalidDataBits_returnsInvalidArgument() { + MethodChannel.Result mockResult = mock(MethodChannel.Result.class); + MethodCall call = new MethodCall("openComPort", new java.util.HashMap() {{ + put("portName", "/dev/ttyS0"); + put("baudRate", 115200); + put("dataBits", 9); // Above maximum 8 + }}); + + plugin.onMethodCall(call, mockResult); + + verify(mockResult).error("INVALID_ARGUMENT", org.mockito.ArgumentMatchers.contains("dataBits"), org.mockito.ArgumentMatchers.isNull()); + } + + @Test + public void onMethodCall_openUsbPort_emptyPortName_returnsInvalidArgument() { + MethodChannel.Result mockResult = mock(MethodChannel.Result.class); + MethodCall call = new MethodCall("openUsbPort", new java.util.HashMap() {{ + put("portName", ""); + }}); + + plugin.onMethodCall(call, mockResult); + + verify(mockResult).error("INVALID_ARGUMENT", org.mockito.ArgumentMatchers.contains("portName"), org.mockito.ArgumentMatchers.isNull()); + } + + @Test + public void onMethodCall_closePort_nullHandle_returnsInvalidArgument() { + MethodChannel.Result mockResult = mock(MethodChannel.Result.class); + MethodCall call = new MethodCall("closePort", new java.util.HashMap() {{ + put("handle", null); + }}); + + plugin.onMethodCall(call, mockResult); + + verify(mockResult).error("INVALID_ARGUMENT", org.mockito.ArgumentMatchers.contains("handle"), org.mockito.ArgumentMatchers.isNull()); + } + + @Test + public void onMethodCall_isPortOpened_nullHandle_returnsInvalidArgument() { + MethodChannel.Result mockResult = mock(MethodChannel.Result.class); + MethodCall call = new MethodCall("isPortOpened", new java.util.HashMap() {{ + put("handle", null); + }}); + + plugin.onMethodCall(call, mockResult); + + verify(mockResult).error("INVALID_ARGUMENT", org.mockito.ArgumentMatchers.contains("handle"), org.mockito.ArgumentMatchers.isNull()); + } + + @Test + public void onMethodCall_unknownMethod_returnsNotImplemented() { + MethodChannel.Result mockResult = mock(MethodChannel.Result.class); + MethodCall call = new MethodCall("unknownMethod", null); + + plugin.onMethodCall(call, mockResult); + + verify(mockResult).notImplemented(); + } + + @Test + public void onMethodCall_closePort_validHandle_callsPortHandleManager() { + // Register a handle first to test close + PortHandleManager manager = PortHandleManager.getInstance(); + com.sun.jna.Pointer pointer = new com.sun.jna.Pointer(0x1234L); + int handle = manager.registerHandle(pointer); + + MethodChannel.Result mockResult = mock(MethodChannel.Result.class); + MethodCall call = new MethodCall("closePort", new java.util.HashMap() {{ + put("handle", handle); + }}); + + plugin.onMethodCall(call, mockResult); + + verify(mockResult).success(true); + } + + @Test + public void onMethodCall_closePort_invalidHandle_returnsFalse() { + MethodChannel.Result mockResult = mock(MethodChannel.Result.class); + MethodCall call = new MethodCall("closePort", new java.util.HashMap() {{ + put("handle", 99999); // Non-existent handle + }}); + + plugin.onMethodCall(call, mockResult); + + verify(mockResult).success(false); + } + + @Test + public void onMethodCall_isPortOpened_validHandle_callsPortHandleManager() { + PortHandleManager manager = PortHandleManager.getInstance(); + com.sun.jna.Pointer pointer = new com.sun.jna.Pointer(0x5678L); + int handle = manager.registerHandle(pointer); + + MethodChannel.Result mockResult = mock(MethodChannel.Result.class); + MethodCall call = new MethodCall("isPortOpened", new java.util.HashMap() {{ + put("handle", handle); + }}); + + plugin.onMethodCall(call, mockResult); + + // Note: Without native library, isPortOpened will fail due to UnsatisfiedLinkError + // but the test verifies the routing logic + } +} diff --git a/docs/apis.md b/docs/apis.md new file mode 100644 index 0000000..5cd87bd --- /dev/null +++ b/docs/apis.md @@ -0,0 +1,5938 @@ +# 接口说明文档 + +> 本文档为打印机SDK接口说明文档,涵盖枚举定义、回调接口、端口函数、票据/标签/页模式/黑标打印函数等完整API参考。 + +## 目录 + +- 开发包简介 (第6页) + +- 常见问题说明 (第7页) + +- 接口说明 (第8页) + +- 枚举和宏定义 (第8页) + +- CP_ComDataBits (第8页) + +- CP_ComParity (第9页) + +- CP_ComStopBits (第10页) + +- CP_ComFlowControl (第11页) + +- CP_CharacterSet (第12页) + +- CP_CharacterCodepage (第13页) + +- CP_MultiByteEncoding (第15页) + +- CP_ImageBinarizationMethod (第16页) + +- CP_ImageCompressionMethod (第17页) + +- CP_ImagePixelsFormat (第18页) + +- CP_QRCodeECC (第19页) + +- CP_Pos_Alignment (第20页) + +- CP_Pos_BarcodeType (第21页) + +- CP_Pos_BarcodeTextPrintPosition (第22页) + +- CP_Page_DrawDirection (第23页) + +- CP_Page_DrawAlignment (第24页) + +- CP_Label_BarcodeType (第25页) + +- CP_Label_BarcodeTextPrintPosition (第26页) + +- CP_Label_Rotation (第27页) + +- CP_Label_Color (第28页) + +- CP_PRINTERSTATUS (第29页) + +- CP_RTSTATUS (第32页) + +- CP_LABEL_TEXT_STYLE (第34页) + +- 回调接口 (第35页) + +- CP_OnNetPrinterDiscovered (第35页) + +- CP_OnBluetoothDeviceDiscovered (第36页) + +- CP_OnWiFiP2PDeviceDiscovered (第37页) + +- CP_OnPortOpenedEvent (第38页) + +- CP_OnPortOpenFailedEvent (第39页) + +- CP_OnPortClosedEvent (第40页) + +- CP_OnPortWrittenEvent (第41页) + +- CP_OnPortReceivedEvent (第42页) + +- CP_OnPrinterStatusEvent (第43页) + +- CP_OnPrinterReceivedEvent (第44页) + +- CP_OnPrinterPrintedEvent (第45页) + +- 添加移除回调 (第46页) + +- CP_Port_AddOnPortOpenedEvent (第46页) + +- CP_Port_AddOnPortOpenFailedEvent (第47页) + +- CP_Port_AddOnPortClosedEvent (第48页) + +- CP_Port_AddOnPortWrittenEvent (第49页) + +- CP_Port_AddOnPortReceivedEvent (第50页) + +- CP_Port_RemoveOnPortOpenedEvent (第51页) + +- CP_Port_RemoveOnPortOpenFailedEvent (第52页) + +- CP_Port_RemoveOnPortClosedEvent (第53页) + +- CP_Port_RemoveOnPortWrittenEvent (第54页) + +- CP_Port_RemoveOnPortReceivedEvent (第55页) + +- CP_Printer_AddOnPrinterStatusEvent (第56页) + +- CP_Printer_AddOnPrinterReceivedEvent (第57页) + +- CP_Printer_AddOnPrinterPrintedEvent (第58页) + +- CP_Printer_RemoveOnPrinterStatusEvent (第59页) + +- CP_Printer_RemoveOnPrinterReceivedEvent (第60页) + +- CP_Printer_RemoveOnPrinterPrintedEvent (第61页) + +- 端口函数 (第62页) + +- CP_Port_EnumCom (第62页) + +- CP_Port_EnumUsb (第63页) + +- CP_Port_EnumNetPrinter (第64页) + +- CP_Port_EnumBtDevice (第65页) + +- CP_Port_EnumBleDevice (第66页) + +- CP_Port_EnumWiFiP2PDevice (第67页) + +- CP_Port_OpenCom (第68页) + +- CP_Port_OpenUsb (第70页) + +- CP_Port_OpenTcp (第71页) + +- CP_Port_OpenBtSpp (第72页) + +- CP_Port_OpenBtBle (第73页) + +- CP_Port_WiFiP2P_Connect (第74页) + +- CP_Port_WiFiP2P_Disconnect (第75页) + +- CP_Port_WiFiP2P_IsConnected (第76页) + +- CP_Port_Write (第77页) + +- CP_Port_Read (第78页) + +- CP_Port_ReadUntilByte (第79页) + +- CP_Port_Available (第80页) + +- CP_Port_SkipAvailable (第81页) + +- CP_Port_IsConnectionValid (第82页) + +- CP_Port_IsOpened (第83页) + +- CP_Port_Close (第84页) + +- 获取打印机信息函数 (第85页) + +- CP_Printer_GetPrinterResolutionInfo (第85页) + +- CP_Printer_GetPrinterFirmwareVersion (第86页) + +- CP_Printer_GetPrinterStatusInfo (第87页) + +- CP_Printer_GetPrinterReceivedInfo (第88页) + +- CP_Printer_GetPrinterPrintedInfo (第89页) + +- CP_Printer_GetPrinterLabelPositionAdjustmentInfo (第90页) + +- CP_Printer_SetPrinterLabelPositionAdjustmentInfo (第91页) + +- CP_Printer_ClearPrinterBuffer (第92页) + +- CP_Printer_ClearPrinterError (第93页) + +- 票据函数 (第94页) + +- CP_Pos_QueryRTStatus (第94页) + +- CP_Pos_QueryPrintResult (第95页) + +- CP_Pos_KickOutDrawer (第96页) + +- CP_Pos_Beep (第97页) + +- CP_Pos_FeedAndHalfCutPaper (第98页) + +- CP_Pos_FullCutPaper (第99页) + +- CP_Pos_HalfCutPaper (第100页) + +- CP_Pos_FeedLine (第101页) + +- CP_Pos_FeedDot (第102页) + +- CP_Pos_PrintSelfTestPage (第103页) + +- CP_Pos_PrintText (第104页) + +- CP_Pos_PrintTextInUTF8 (第105页) + +- CP_Pos_PrintTextInGBK (第106页) + +- CP_Pos_PrintTextInBIG5 (第107页) + +- CP_Pos_PrintTextInShiftJIS (第108页) + +- CP_Pos_PrintTextInEUCKR (第109页) + +- CP_Pos_PrintBarcode (第110页) + +- CP_Pos_PrintBarcode_Code128Auto (第111页) + +- CP_Pos_PrintQRCode (第112页) + +- CP_Pos_PrintQRCodeUseEpsonCmd (第113页) + +- CP_Pos_PrintQRCodeUseImageCmd (第114页) + +- CP_Pos_PrintDoubleQRCode (第115页) + +- CP_Pos_PrintPDF417BarcodeUseEpsonCmd (第116页) + +- CP_Pos_PrintRasterImageFromFile (第117页) + +- CP_Pos_PrintRasterImageFromData (第118页) + +- CP_Pos_PrintRasterImageFromPixels (第119页) + +- CP_Pos_PrintHorizontalLine (第121页) + +- CP_Pos_PrintHorizontalLineSpecifyThickness (第122页) + +- CP_Pos_PrintMultipleHorizontalLinesAtOneRow (第123页) + +- CP_Pos_ResetPrinter (第124页) + +- CP_Pos_SetPrintSpeed (第125页) + +- CP_Pos_SetPrintDensity (第126页) + +- CP_Pos_SetSingleByteMode (第127页) + +- CP_Pos_SetCharacterSet (第128页) + +- CP_Pos_SetCharacterCodepage (第129页) + +- CP_Pos_SetMultiByteMode (第130页) + +- CP_Pos_SetMultiByteEncoding (第131页) + +- CP_Pos_SetMovementUnit (第132页) + +- CP_Pos_SetPrintAreaLeftMargin (第133页) + +- CP_Pos_SetPrintAreaWidth (第134页) + +- CP_Pos_SetHorizontalAbsolutePrintPosition (第135页) + +- CP_Pos_SetHorizontalRelativePrintPosition (第136页) + +- CP_Pos_SetVerticalAbsolutePrintPosition (第137页) + +- CP_Pos_SetVerticalRelativePrintPosition (第138页) + +- CP_Pos_SetAlignment (第139页) + +- CP_Pos_SetTextScale (第140页) + +- CP_Pos_SetAsciiTextFontType (第141页) + +- CP_Pos_SetTextBold (第142页) + +- CP_Pos_SetTextUnderline (第143页) + +- CP_Pos_SetTextUpsideDown (第144页) + +- CP_Pos_SetTextWhiteOnBlack (第145页) + +- CP_Pos_SetTextRotate (第146页) + +- CP_Pos_SetTextLineHeight (第147页) + +- CP_Pos_SetAsciiTextCharRightSpacing (第148页) + +- CP_Pos_SetKanjiTextCharSpacing (第149页) + +- CP_Pos_SetBarcodeUnitWidth (第150页) + +- CP_Pos_SetBarcodeHeight (第151页) + +- CP_Pos_SetBarcodeReadableTextFontType (第152页) + +- CP_Pos_SetBarcodeReadableTextPosition (第153页) + +- 页模式函数 (第154页) + +- CP_Page_SelectPageMode (第154页) + +- CP_Page_SelectPageModeEx (第155页) + +- CP_Page_ExitPageMode (第156页) + +- CP_Page_PrintPage (第157页) + +- CP_Page_ClearPage (第158页) + +- CP_Page_SetPageArea (第159页) + +- CP_Page_SetPageDrawDirection (第160页) + +- CP_Page_DrawRect (第161页) + +- CP_Page_DrawBox (第162页) + +- CP_Page_DrawText (第163页) + +- CP_Page_DrawTextInUTF8 (第164页) + +- CP_Page_DrawTextInGBK (第165页) + +- CP_Page_DrawTextInBIG5 (第166页) + +- CP_Page_DrawTextInShiftJIS (第167页) + +- CP_Page_DrawTextInEUCKR (第168页) + +- CP_Page_DrawBarcode (第169页) + +- CP_Page_DrawQRCode (第170页) + +- CP_Page_DrawRasterImageFromFile (第171页) + +- CP_Page_DrawRasterImageFromData (第172页) + +- CP_Page_DrawRasterImageFromPixels (第173页) + +- 黑标函数 (第175页) + +- CP_BlackMark_EnableBlackMarkMode (第175页) + +- CP_BlackMark_DisableBlackMarkMode (第176页) + +- CP_BlackMark_SetBlackMarkMaxFindLength (第177页) + +- CP_BlackMark_FindNextBlackMark (第178页) + +- CP_BlackMark_SetBlackMarkPaperPrintPosition (第179页) + +- CP_BlackMark_SetBlackMarkPaperCutPosition (第180页) + +- CP_BlackMark_FullCutBlackMarkPaper (第181页) + +- CP_BlackMark_HalfCutBlackMarkPaper (第182页) + +- 标签函数 (第183页) + +- CP_Label_EnableLabelMode (第183页) + +- CP_Label_DisableLabelMode (第184页) + +- CP_Label_CalibrateLabel (第185页) + +- CP_Label_FeedLabel (第186页) + +- CP_Label_BackPaperToPrintPosition (第187页) + +- CP_Label_FeedPaperToTearPosition (第188页) + +- CP_Label_PageBegin (第189页) + +- CP_Label_PagePrint (第190页) + +- CP_Label_DrawText (第191页) + +- CP_Label_DrawTextInUTF8 (第192页) + +- CP_Label_DrawTextInGBK (第193页) + +- CP_Label_DrawBarcode (第194页) + +- CP_Label_DrawQRCode (第196页) + +- CP_Label_DrawPDF417Code (第198页) + +- CP_Label_DrawImageFromFile (第200页) + +- CP_Label_DrawImageFromData (第201页) + +- CP_Label_DrawImageFromPixels (第203页) + +- CP_Label_DrawLine (第205页) + +- CP_Label_DrawRect (第206页) + +- CP_Label_DrawBox (第207页) + +- 其他函数 (第208页) + +- CP_Library_Version (第208页) + +- CP_Proto_QueryBatteryLevel (第209页) + +- CP_Proto_QuerySerialNumber (第210页) + +- CP_Proto_SetSystemNameAndSerialNumber (第211页) + +- CP_Proto_SetBluetoothNameAndPassword (第212页) + +- CP_Proto_SetPTPBasicParameters (第213页) + +- CP_Settings_Hardware_SetPrintSpeed (第216页) + +--- + +## 开发包简介 + +1 开发包带有很多详细的例子,开发之前,请先运行相应的例子测试,测试完全没问题,再考虑开发。 + +2 开发包支持各种打印机,包括但不限于票据打印,标签打印,页模式打印,黑标打印。 + +3 开发包支持自动回传功能,开启该功能需要打印机支持自动回传。 + +4 开发包所有的函数都以CP_作为前缀,避免与别家开发包混淆。 + +5 开发包主要由宏定义,枚举,回调,端口函数,票据打印函数,标签打印函数,页模式打印函数, + +黑标相关函数组成。 + +端口函数以CP_Port_开头,包括打开端口,关闭端口,读写端口等函数。 + +票据打印函数以CP_Pos_开头,主要封装了各种票据指令,可以打印文本,条码,二维码,图片等 + +标签打印函数以CP_Label_开头,主要封装了标签指令,可以打印文本,条码,二维码,图片等 + +页模式打印函数以CP_Page_开头,主要封装了页模式相关指令,可以打印文本,条码,二维码,图片等 + +黑标相关函数以CP_BlackMark_开头,主要封装了黑标定位相关指令 + +6 一个完整的打印流程是,打开端口,各种打印函数,关闭端口。 + +回调接口,可用可不用,不影响正常打印流程,回调只是用于提示信息。 + +7 开发包的所有函数,都在AutoReplyPrint接口里面, + +工程代码里面,导入com.caysn.autoreplyprint包,即可调用开发包的所有函数。 + +8 如需混淆,在proguard-rules.pro文件加入以下内容即可 + +-keep class com.sun.** {*;} + +-keep class com.lvrenyang.** {*;} + +-keep class com.caysn.autoreplyprint.** {*;} + +--- + +## 常见问题说明 + +1 怎么分辨我的打印机是什么机型,该用什么函数? + +要注意看机型,是什么机型,用什么函数,用错了就打印不了,或者会打印乱码 + +放票据纸打印的,就是票据机型,使用CP_Pos_系列函数进行打印 + +放标签纸的,就是标签机型,使用CP_Label_系列函数进行打印 + +有些机器既可以放票据纸打印,也可以放标签纸打印的。可以调用函数,开启或关闭标签模式。 + +最好是咨询卖家,看该用什么例子测试,参考例子来写最省事。 + +2 为什么打印一半关机了? + +看是不是电源不够,额定电压下,一般是要2A的电源就够了。 + +打印机开机时,指示灯的闪烁一般是不一样的,能很明显的看出来。 + +出现问题,要仔细观察指示灯或声音,这样便于定位问题。 + +3 标签打印完,为什么没有定位到缝隙? + +看是不是开启了标签模式,标签是否识别到,测试方法是按一下进纸按键,是不是完整的进一张纸 + +--- + +## 接口说明 + +#### CP_ComDataBits + +枚举和宏定义 + +串口数据位。打开串口时,需要指定数据位,一般为8位数据位。 + +**定义:** + +```java +public static final int CP_ComDataBits_4 = 4; +public static final int CP_ComDataBits_5 = 5; +public static final int CP_ComDataBits_6 = 6; +public static final int CP_ComDataBits_7 = 7; +public static final int CP_ComDataBits_8 = 8; +``` + +--- + +#### CP_ComParity + +串口校验位。打开串口时,需要指定校验位,一般是无校验。 + +**定义:** + +```java +public static final int CP_ComParity_NoParity = 0; +public static final int CP_ComParity_OddParity = 1; +public static final int CP_ComParity_EvenParity = 2; +public static final int CP_ComParity_MarkParity = 3; +public static final int CP_ComParity_SpaceParity = 4; +``` + +--- + +#### CP_ComStopBits + +串口停止位。打开串口时,需要指定停止位,一般是一位停止位。 + +**定义:** + +```java +public static final int CP_ComStopBits_One = 0; +public static final int CP_ComStopBits_OnePointFive = 1; +public static final int CP_ComStopBits_Two = 2; +``` + +--- + +#### CP_ComFlowControl + +串口流控制。打开串口时,需要指定流控制,一般选无流控或者软件流控。硬件流控需要线连对才能用。 + +**定义:** + +```java +public static final int CP_ComFlowControl_None = 0; +public static final int CP_ComFlowControl_XonXoff = 1; +public static final int CP_ComFlowControl_RtsCts = 2; +public static final int CP_ComFlowControl_DtrDsr = 3; +``` + +--- + +#### CP_CharacterSet + +单字节模式下的国际字符集。当打印机处于单字节模式下时,设置打印机国际字符集,会改变0x20-0x7F这个区间 + +的部分文字的打印。比如货币符号人命币或美元。具体细节请看打印机指令集部分。当打印机处于多字节模式下时, + +设置该属性无影响。 + +**定义:** + +```java +public static final int CP_CharacterSet_USA = 0; +public static final int CP_CharacterSet_FRANCE = 1; +public static final int CP_CharacterSet_GERMANY = 2; +public static final int CP_CharacterSet_UK = 3; +public static final int CP_CharacterSet_DENMARK_I = 4; +public static final int CP_CharacterSet_SWEDEN = 5; +public static final int CP_CharacterSet_ITALY = 6; +public static final int CP_CharacterSet_SPAIN_I = 7; +public static final int CP_CharacterSet_JAPAN = 8; +public static final int CP_CharacterSet_NORWAY = 9; +public static final int CP_CharacterSet_DENMARK_II = 10; +public static final int CP_CharacterSet_SPAIN_II = 11; +public static final int CP_CharacterSet_LATIN = 12; +public static final int CP_CharacterSet_KOREA = 13; +public static final int CP_CharacterSet_SLOVENIA = 14; +public static final int CP_CharacterSet_CHINA = 15; +``` + +--- + +#### CP_CharacterCodepage + +单字节模式下的字符代码页。当打印机处于单字节模式下时,设置打印机字符代码页,会改变0x80-0xFF这个区间 + +的部分文字的打印。具体细节请看打印机指令集部分。当打印机处于多字节模式下时,设置该属性无影响。 + +**定义:** + +```java +public static final int CP_CharacterCodepage_CP437 = 0; +public static final int CP_CharacterCodepage_KATAKANA = 1; +public static final int CP_CharacterCodepage_CP850 = 2; +public static final int CP_CharacterCodepage_CP860 = 3; +public static final int CP_CharacterCodepage_CP863 = 4; +public static final int CP_CharacterCodepage_CP865 = 5; +public static final int CP_CharacterCodepage_WCP1251 = 6; +public static final int CP_CharacterCodepage_CP866 = 7; +public static final int CP_CharacterCodepage_MIK = 8; +public static final int CP_CharacterCodepage_CP755 = 9; +public static final int CP_CharacterCodepage_IRAN = 10; +public static final int CP_CharacterCodepage_CP862 = 15; +public static final int CP_CharacterCodepage_WCP1252 = 16; +public static final int CP_CharacterCodepage_WCP1253 = 17; +public static final int CP_CharacterCodepage_CP852 = 18; +public static final int CP_CharacterCodepage_CP858 = 19; +public static final int CP_CharacterCodepage_IRAN_II = 20; +public static final int CP_CharacterCodepage_LATVIAN = 21; +public static final int CP_CharacterCodepage_CP864 = 22; +public static final int CP_CharacterCodepage_ISO_8859_1 = 23; +public static final int CP_CharacterCodepage_CP737 = 24; +public static final int CP_CharacterCodepage_WCP1257 = 25; +public static final int CP_CharacterCodepage_THAI = 26; +public static final int CP_CharacterCodepage_CP720 = 27; +public static final int CP_CharacterCodepage_CP855 = 28; +public static final int CP_CharacterCodepage_CP857 = 29; +public static final int CP_CharacterCodepage_WCP1250 = 30; +public static final int CP_CharacterCodepage_CP775 = 31; +public static final int CP_CharacterCodepage_WCP1254 = 32; +public static final int CP_CharacterCodepage_WCP1255 = 33; +public static final int CP_CharacterCodepage_WCP1256 = 34; +public static final int CP_CharacterCodepage_WCP1258 = 35; +public static final int CP_CharacterCodepage_ISO_8859_2 = 36; +public static final int CP_CharacterCodepage_ISO_8859_3 = 37; +public static final int CP_CharacterCodepage_ISO_8859_4 = 38; +public static final int CP_CharacterCodepage_ISO_8859_5 = 39; +public static final int CP_CharacterCodepage_ISO_8859_6 = 40; +public static final int CP_CharacterCodepage_ISO_8859_7 = 41; +public static final int CP_CharacterCodepage_ISO_8859_8 = 42; +``` + +--- + +#### CP_MultiByteEncoding + +多字节模式下的字符编码。打印机处于多字节模式下时,收到的打印数据,将按照指定的编码进行打印。比如说, + +设置打印机为多字节模式,再指定多字节模式下字符编码为 UTF8 编码,应用程序需按照 UTF8 编码发送字符串给 + +打印机,打印机就会将字符串打印出来。 + +**定义:** + +```java +public static final int CP_MultiByteEncoding_GBK = 0; +public static final int CP_MultiByteEncoding_UTF8 = 1; +public static final int CP_MultiByteEncoding_BIG5 = 3; +public static final int CP_MultiByteEncoding_ShiftJIS = 4; +public static final int CP_MultiByteEncoding_EUCKR = 5; +``` + +--- + +#### CP_ImageBinarizationMethod + +图像二值化算法。由于打印机只能打印黑白单色位图,打印图像的过程中,如果原图是彩图或灰度图,则需要使用 + +二值化算法,将原图转为单色图。不同的算法有不同的效果。 + +阈值算法适用于图片内容都是文字的。 + +误差扩散法适用于所有的图片,但细看会有毛刺。 + +抖动算法效果不如误差扩散法,不建议使用,仅做兼容性保留。 + +**定义:** + +```java +public static final int CP_ImageBinarizationMethod_Dithering = 0; +public static final int CP_ImageBinarizationMethod_Thresholding = 1; +public static final int CP_ImageBinarizationMethod_ErrorDiffusion = 2; +``` + +--- + +#### CP_ImageCompressionMethod + +图片压缩算法。部分打印机支持使用压缩指令打印图片,提高数据传输效率。具体是否支持需要看实际测试结果才 + +知道。 + +**定义:** + +```java +public static final int CP_ImageCompressionMethod_None = 0; +public static final int CP_ImageCompressionMethod_Level1 = 1; +public static final int CP_ImageCompressionMethod_Level2 = 2; +``` + +--- + +#### CP_ImagePixelsFormat + +图片像素格式。打印图片时,如果是直接传的像素数据打印的,那么数据和格式要对应。 + +**定义:** + +```java +public static final int CP_ImagePixelsFormat_MONO = 1; +public static final int CP_ImagePixelsFormat_MONOLSB = 2; +public static final int CP_ImagePixelsFormat_GRAY8 = 3; +public static final int CP_ImagePixelsFormat_BYTEORDERED_RGB24 = 4; +public static final int CP_ImagePixelsFormat_BYTEORDERED_BGR24 = 5; +public static final int CP_ImagePixelsFormat_BYTEORDERED_ARGB32 = 6; +public static final int CP_ImagePixelsFormat_BYTEORDERED_RGBA32 = 7; +public static final int CP_ImagePixelsFormat_BYTEORDERED_ABGR32 = 8; +public static final int CP_ImagePixelsFormat_BYTEORDERED_BGRA32 = 9; +CP_ImagePixelsFormat_MONO = 1, +``` + +单色位图,高位在前 + +```java +CP_ImagePixelsFormat_MONOLSB = 2, +``` + +单色位图,低位在前 + +```java +CP_ImagePixelsFormat_GRAY8 = 3, +``` + +灰度图,每个颜色占一个字节 + +```java +CP_ImagePixelsFormat_BYTEORDERED_RGB24 = 4, +``` + +按照字节顺序,R G B每个颜色占一个字节 + +```java +CP_ImagePixelsFormat_BYTEORDERED_BGR24 = 5, +``` + +按照字节顺序,B G R每个颜色占一个字节 + +```java +CP_ImagePixelsFormat_BYTEORDERED_ARGB32 = 6, +``` + +按照字节顺序,A R G B每个颜色占一个字节 + +```java +CP_ImagePixelsFormat_BYTEORDERED_RGBA32 = 7, +``` + +按照字节顺序,R G B A每个颜色占一个字节 + +```java +CP_ImagePixelsFormat_BYTEORDERED_ABGR32 = 8, +``` + +按照字节顺序,A B G R每个颜色占一个字节 + +```java +CP_ImagePixelsFormat_BYTEORDERED_BGRA32 = 9 +``` + +按照字节顺序,B G R A每个颜色占一个字节 + +--- + +#### CP_QRCodeECC + +二维码纠错等级。 + +**定义:** + +```java +public static final int CP_QRCodeECC_L = 1; +public static final int CP_QRCodeECC_M = 2; +public static final int CP_QRCodeECC_Q = 3; +public static final int CP_QRCodeECC_H = 4; +``` + +--- + +#### CP_Pos_Alignment + +票据模式下打印对齐方式。有左对齐,中对齐,右对齐。 + +**定义:** + +```java +public static final int CP_Pos_Alignment_Left = 0; +public static final int CP_Pos_Alignment_HCenter = 1; +public static final int CP_Pos_Alignment_Right = 2; +``` + +--- + +#### CP_Pos_BarcodeType + +票据指令打印条码时,指定条码类型。 + +**定义:** + +```java +public static final int CP_Pos_BarcodeType_UPCA = 0x41; +public static final int CP_Pos_BarcodeType_UPCE = 0x42; +public static final int CP_Pos_BarcodeType_EAN13 = 0x43; +public static final int CP_Pos_BarcodeType_EAN8 = 0x44; +public static final int CP_Pos_BarcodeType_CODE39 = 0x45; +public static final int CP_Pos_BarcodeType_ITF = 0x46; +public static final int CP_Pos_BarcodeType_CODEBAR = 0x47; +public static final int CP_Pos_BarcodeType_CODE93 = 0x48; +public static final int CP_Pos_BarcodeType_CODE128 = 0x49; +``` + +--- + +#### CP_Pos_BarcodeTextPrintPosition + +票据指令打印条码时,指定条码文字打印位置。 + +**定义:** + +```java +public static final int CP_Pos_BarcodeTextPrintPosition_None = 0; +public static final int CP_Pos_BarcodeTextPrintPosition_AboveBarcode = 1; +public static final int CP_Pos_BarcodeTextPrintPosition_BelowBarcode = 2; +public static final int CP_Pos_BarcodeTextPrintPosition_AboveAndBelowBarcode = 3; +``` + +--- + +#### CP_Page_DrawDirection + +页模式下打印时,指定页面绘制方向。 + +**定义:** + +```java +public static final int CP_Page_DrawDirection_LeftToRight = 0; +public static final int CP_Page_DrawDirection_BottomToTop = 1; +public static final int CP_Page_DrawDirection_RightToLeft = 2; +public static final int CP_Page_DrawDirection_TopToBottom = 3; +``` + +--- + +#### CP_Page_DrawAlignment + +页模式下的相关绘制函数,坐标如果是大于等于零,就是实际坐标。也可以指定为此处的特定值,指定在区域内对 + +齐打印。 + +**定义:** + +```java +public static final int CP_Page_DrawAlignment_Left = -1; +public static final int CP_Page_DrawAlignment_HCenter = -2; +public static final int CP_Page_DrawAlignment_Right = -3; +public static final int CP_Page_DrawAlignment_Top = -1; +public static final int CP_Page_DrawAlignment_VCenter = -2; +public static final int CP_Page_DrawAlignment_Bottom = -3; +``` + +--- + +#### CP_Label_BarcodeType + +标签指令打印条码时,指定条码类型。 + +**定义:** + +```java +public static final int CP_Label_BarcodeType_UPCA = 0; +public static final int CP_Label_BarcodeType_UPCE = 1; +public static final int CP_Label_BarcodeType_EAN13 = 2; +public static final int CP_Label_BarcodeType_EAN8 = 3; +public static final int CP_Label_BarcodeType_CODE39 = 4; +public static final int CP_Label_BarcodeType_ITF = 5; +public static final int CP_Label_BarcodeType_CODEBAR = 6; +public static final int CP_Label_BarcodeType_CODE93 = 7; +public static final int CP_Label_BarcodeType_CODE128 = 8; +public static final int CP_Label_BarcodeType_CODE11 = 9; +public static final int CP_Label_BarcodeType_MSI = 10; +public static final int CP_Label_BarcodeType_128M = 11; +public static final int CP_Label_BarcodeType_EAN128 = 12; +public static final int CP_Label_BarcodeType_25C = 13; +public static final int CP_Label_BarcodeType_39C = 14; +public static final int CP_Label_BarcodeType_39 = 15; +public static final int CP_Label_BarcodeType_EAN13PLUS2 = 16; +public static final int CP_Label_BarcodeType_EAN13PLUS5 = 17; +public static final int CP_Label_BarcodeType_EAN8PLUS2 = 18; +public static final int CP_Label_BarcodeType_EAN8PLUS5 = 19; +public static final int CP_Label_BarcodeType_POST = 20; +public static final int CP_Label_BarcodeType_UPCAPLUS2 = 21; +public static final int CP_Label_BarcodeType_UPCAPLUS5 = 22; +public static final int CP_Label_BarcodeType_UPCEPLUS2 = 23; +public static final int CP_Label_BarcodeType_UPCEPLUS5 = 24; +public static final int CP_Label_BarcodeType_CPOST = 25; +public static final int CP_Label_BarcodeType_MSIC = 26; +public static final int CP_Label_BarcodeType_PLESSEY = 27; +public static final int CP_Label_BarcodeType_ITF14 = 28; +public static final int CP_Label_BarcodeType_EAN14 = 29; +``` + +--- + +#### CP_Label_BarcodeTextPrintPosition + +标签指令打印条码时,指定条码文字打印位置。 + +**定义:** + +```java +public static final int CP_Label_BarcodeTextPrintPosition_None = 0; +public static final int CP_Label_BarcodeTextPrintPosition_AboveBarcode = 1; +public static final int CP_Label_BarcodeTextPrintPosition_BelowBarcode = 2; +public static final int CP_Label_BarcodeTextPrintPosition_AboveAndBelowBarcode = 3; +``` + +--- + +#### CP_Label_Rotation + +标签指令绘制控件时,指定旋转角度。 + +**定义:** + +```java +public static final int CP_Label_Rotation_0 = 0; +public static final int CP_Label_Rotation_90 = 1; +public static final int CP_Label_Rotation_180 = 2; +public static final int CP_Label_Rotation_270 = 3; +``` + +--- + +#### CP_Label_Color + +标签指令绘制控件时,指定绘制颜色。可以是白色或者黑色。 + +**定义:** + +```java +public static final int CP_Label_Color_White = 0; +public static final int CP_Label_Color_Black = 1; +``` + +--- + +#### CP_PRINTERSTATUS + +打印机自动回传的状态定义。一般只需要关注是否有错误,信息部分主要是起到提示功能。 + +**定义:** + +```java +public class CP_PrinterStatus { +private long error_status = 0; +private long info_status = 0; +public CP_PrinterStatus(long error_status, long info_status) { +this.error_status = error_status; +this.info_status = info_status; +} +public long errorStatus() { +return error_status; +} +public long infoStatus() { +return info_status; +} +public boolean ERROR_OCCURED() { +return error_status != 0; +} +public boolean ERROR_CUTTER() { +return (error_status & 0x01) != 0; +} +public boolean ERROR_FLASH() { +return (error_status & 0x02) != 0; +} +public boolean ERROR_NOPAPER() { +return (error_status & 0x04) != 0; +} +public boolean ERROR_VOLTAGE() { +return (error_status & 0x08) != 0; +} +public boolean ERROR_MARKER() { +return (error_status & 0x10) != 0; +``` + +--- + +#### CP_RTSTATUS + +实时状态查询返回的状态定义。此处说明仅供参考,适用于大部分机型,部分机型如不一致,以实际机型指令集为 + +准。 + +**定义:** + +```java +public class CP_RTSTATUS_Helper { +public static boolean CP_RTSTATUS_DRAWER_OPENED(long status) { return (((status >> 0) & 0x04) == 0x00); }; +public static boolean CP_RTSTATUS_OFFLINE(long status) { return (((status >> 0) & 0x08) == 0x08); }; +public static boolean CP_RTSTATUS_COVERUP(long status) { return (((status >> 8) & 0x04) == 0x04); }; +public static boolean CP_RTSTATUS_FEED_PRESSED(long status) { return (((status >> 8) & 0x08) == 0x08); }; +public static boolean CP_RTSTATUS_NOPAPER(long status) { return (((status >> 8) & 0x20) == 0x20); }; +public static boolean CP_RTSTATUS_ERROR_OCCURED(long status) { return (((status >> 8) & 0x40) == 0x40); }; +public static boolean CP_RTSTATUS_CUTTER_ERROR(long status) { return (((status >> 16) & 0x08) == 0x08); }; +public static boolean CP_RTSTATUS_UNRECOVERABLE_ERROR(long status) { return (((status >> 16) & 0x20) == +``` + +0x20); }; + +```java +public static boolean CP_RTSTATUS_DEGREE_OR_VOLTAGE_OVERRANGE(long status) { return (((status >> 16) & +``` + +0x40) == 0x40); }; + +```java +public static boolean CP_RTSTATUS_PAPER_NEAREND(long status) { return (((status >> 24) & 0x08) == 0x08); }; +public static boolean CP_RTSTATUS_PAPER_TAKEOUT(long status) { return (((status >> 24) & 0x04) == 0x04); }; +} +// 这里的实时状态,共占四字节。 +// 从低字节到高字节依次对应指令集中这四个指令: +// 10 04 01 +// 10 04 02 +// 10 04 03 +// 10 04 04 +// 部分机型由于定制或其他原因,状态值定义可能与此处不一致,以实测为准。 +// +// DRAWER_OPENED +// 钱箱打开 +// OFFLINE +// 脱机 +// COVERUP +// 盖子打开 +// FEED_PRESSED +// 走纸键按下 +// NOPAPER +// 缺纸 +// ERROR_OCCURED +// 出错 +// CUTTER_ERROR +// 切刀错误 +// UNRECOVERABLE_ERROR +``` + +--- + +#### CP_LABEL_TEXT_STYLE + +标签指令打印文本时,指定文字打印风格。分别为加粗,下划线,反色,删除线,旋转,宽高加倍。 + +**定义:** + +```java +public class CP_Label_TextStyle { +private int style = 0; +public CP_Label_TextStyle(boolean bold, boolean underline, boolean highlight, boolean strikethrough, int +``` + +rotation, int widthscale, int heightscale) { + +int style = 0; + +if (bold) + +style |= (1 << 0); + +if (underline) + +style |= (1 << 1); + +if (highlight) + +style |= (1 << 2); + +if (strikethrough) + +style |= (1 << 3); + +style |= (rotation << 4); + +style |= (widthscale << 8); + +style |= (heightscale << 12); + +```java +this.style = style; +} +public int getStyle() { +return style; +} +} +``` + +--- + +### 回调接口 + +CP_OnNetPrinterDiscovered + +枚举网络打印机时,传入的回调函数。查到到网络打印机时,会回调该函数。 + +定义 + +public interface CP_OnNetPrinterDiscovered_Callback extends Callback { + +void CP_OnNetPrinterDiscovered(String local_ip, String discovered_mac, String discovered_ip, String + +discovered_name, Pointer private_data); + +} + +#### CP_OnBluetoothDeviceDiscovered + +枚举蓝牙设备时,传入的回调函数。搜索到蓝牙设备时,会回调该函数。 + +**定义:** + +```java +public interface CP_OnBluetoothDeviceDiscovered_Callback extends Callback { +``` + +void CP_OnBluetoothDeviceDiscovered(String device_name, String device_address, Pointer private_data); + +```java +} +``` + +--- + +#### CP_OnWiFiP2PDeviceDiscovered + +枚举WiFiP2P设备时,传入的回调接口。搜索到WiFiP2P设备时,会回调该函数。 + +**定义:** + +```java +public interface CP_OnWiFiP2PDeviceDiscovered_Callback extends Callback { +``` + +void CP_OnWiFiP2PDeviceDiscovered(String device_name, String device_address, String device_type, Pointer + +private_data); + +```java +} +``` + +--- + +#### CP_OnPortOpenedEvent + +端口打开成功时,回调该函数。 + +**定义:** + +```java +public interface CP_OnPortOpenedEvent_Callback extends Callback { +``` + +void CP_OnPortOpenedEvent(Pointer handle, String name, Pointer private_data); + +```java +} +``` + +--- + +#### CP_OnPortOpenFailedEvent + +端口打开失败时,回调该函数。 + +**定义:** + +```java +public interface CP_OnPortOpenFailedEvent_Callback extends Callback { +``` + +void CP_OnPortOpenFailedEvent(Pointer handle, String name, Pointer private_data); + +```java +} +``` + +--- + +#### CP_OnPortClosedEvent + +端口关闭时,会回调该接口。 + +**定义:** + +```java +public interface CP_OnPortClosedEvent_Callback extends Callback { +``` + +void CP_OnPortClosedEvent(Pointer handle, Pointer private_data); + +```java +} +``` + +备注 + +端口异常时,比如USB数据线拔出,会自动关闭端口,并触发回调。 + +--- + +#### CP_OnPortWrittenEvent + +端口写入数据成功时,会回调该函数。 + +**定义:** + +```java +public interface CP_OnPortWrittenEvent_Callback extends Callback { +``` + +void CP_OnPortWrittenEvent(Pointer handle, Pointer buffer, int count, Pointer private_data); + +```java +} +``` + +--- + +#### CP_OnPortReceivedEvent + +端口收到数据时,会回调该函数。 + +**定义:** + +```java +public interface CP_OnPortReceivedEvent_Callback extends Callback { +``` + +void CP_OnPortReceivedEvent(Pointer handle, Pointer buffer, int count, Pointer private_data); + +```java +} +``` + +--- + +#### CP_OnPrinterStatusEvent + +收到打印机自动回传的状态时,会回调该函数。 + +**定义:** + +```java +public interface CP_OnPrinterStatusEvent_Callback extends Callback { +``` + +void CP_OnPrinterStatusEvent(Pointer handle, long printer_error_status, long printer_info_status, Pointer + +private_data); + +```java +} +``` + +--- + +#### CP_OnPrinterReceivedEvent + +收到打印机自动回传的已接收字节数信息时,会回调该函数。 + +**定义:** + +```java +public interface CP_OnPrinterReceivedEvent_Callback extends Callback { +``` + +void CP_OnPrinterReceivedEvent(Pointer handle, int printer_received_byte_count, Pointer private_data); + +```java +} +``` + +--- + +#### CP_OnPrinterPrintedEvent + +收到打印机自动回传的单据打完信息时,会回调该函数。该函数将弃用,不建议使用。 + +**定义:** + +```java +public interface CP_OnPrinterPrintedEvent_Callback extends Callback { +``` + +void CP_OnPrinterPrintedEvent(Pointer handle, int printer_printed_page_id, Pointer private_data); + +```java +} +``` + +--- + +### 添加移除回调 + +CP_Port_AddOnPortOpenedEvent + +添加回调接口,端口打开成功 + +定义 + +public boolean CP_Port_AddOnPortOpenedEvent(CP_OnPortOpenedEvent_Callback event, Pointer private_data); + +// 添加回调接口,端口打开成功 + +// + +// event + +// 回调接口 + +// + +// private_data + +// 传给回调接口的参数 + +// + +// return + +// true on success. + +// false on failed. + +#### CP_Port_AddOnPortOpenFailedEvent + +添加回调接口,端口打开失败 + +**定义:** + +```java +public boolean CP_Port_AddOnPortOpenFailedEvent(CP_OnPortOpenFailedEvent_Callback event, Pointer +``` + +private_data); + +```java +// 添加回调接口,端口打开失败 +// +// event +// 回调接口 +// +// private_data +// 传给回调接口的参数 +// +// return +// true on success. +// false on failed. +``` + +--- + +#### CP_Port_AddOnPortClosedEvent + +添加回调接口,端口关闭 + +**定义:** + +```java +public boolean CP_Port_AddOnPortClosedEvent(CP_OnPortClosedEvent_Callback event, Pointer private_data); +// 添加回调接口,端口关闭 +// +// event +// 回调接口 +// +// private_data +// 传给回调接口的参数 +// +// return +// true on success. +// false on failed. +``` + +--- + +#### CP_Port_AddOnPortWrittenEvent + +添加回调接口,端口写入数据 + +**定义:** + +```java +public boolean CP_Port_AddOnPortWrittenEvent(CP_OnPortWrittenEvent_Callback event, Pointer private_data); +// 添加回调接口,端口写入数据 +// +// event +// 回调接口 +// +// private_data +// 传给回调接口的参数 +// +// return +// true on success. +// false on failed. +``` + +--- + +#### CP_Port_AddOnPortReceivedEvent + +添加回调接口,端口收到数据 + +**定义:** + +```java +public boolean CP_Port_AddOnPortReceivedEvent(CP_OnPortReceivedEvent_Callback event, Pointer private_data); +// 添加回调接口,端口收到数据 +// +// event +// 回调接口 +// +// private_data +// 传给回调接口的参数 +// +// return +// true on success. +// false on failed. +``` + +--- + +#### CP_Port_RemoveOnPortOpenedEvent + +移除回调接口 + +**定义:** + +```java +public boolean CP_Port_RemoveOnPortOpenedEvent(CP_OnPortOpenedEvent_Callback event); +// 移除回调接口 +// +// event +// 回调接口 +// +// return +// true on success. +// false on failed. +``` + +--- + +#### CP_Port_RemoveOnPortOpenFailedEvent + +移除回调接口 + +**定义:** + +```java +public boolean CP_Port_RemoveOnPortOpenFailedEvent(CP_OnPortOpenFailedEvent_Callback event); +// 移除回调接口 +// +// event +// 回调接口 +// +// return +// true on success. +// false on failed. +``` + +--- + +#### CP_Port_RemoveOnPortClosedEvent + +移除回调接口 + +**定义:** + +```java +public boolean CP_Port_RemoveOnPortClosedEvent(CP_OnPortClosedEvent_Callback event); +// 移除回调接口 +// +// event +// 回调接口 +// +// return +// true on success. +// false on failed. +``` + +--- + +#### CP_Port_RemoveOnPortWrittenEvent + +移除回调接口 + +**定义:** + +```java +public boolean CP_Port_RemoveOnPortWrittenEvent(CP_OnPortWrittenEvent_Callback event); +// 移除回调接口 +// +// event +// 回调接口 +// +// return +// true on success. +// false on failed. +``` + +--- + +#### CP_Port_RemoveOnPortReceivedEvent + +移除回调接口 + +**定义:** + +```java +public boolean CP_Port_RemoveOnPortReceivedEvent(CP_OnPortReceivedEvent_Callback event); +// 移除回调接口 +// +// event +// 回调接口 +// +// return +// true on success. +// false on failed. +``` + +--- + +#### CP_Printer_AddOnPrinterStatusEvent + +添加回调接口,打印机状态更新 + +**定义:** + +```java +public boolean CP_Printer_AddOnPrinterStatusEvent(CP_OnPrinterStatusEvent_Callback event, Pointer private_data); +// 添加回调接口,打印机状态更新 +// +// event +// 回调接口 +// +// private_data +// 传给回调接口的参数 +// +// return +// true on success. +// false on failed. +``` + +--- + +#### CP_Printer_AddOnPrinterReceivedEvent + +添加回调接口,打印机已接收字节数更新 + +**定义:** + +```java +public boolean CP_Printer_AddOnPrinterReceivedEvent(CP_OnPrinterReceivedEvent_Callback event, Pointer +``` + +private_data); + +```java +// 添加回调接口,打印机已接收字节数更新 +// +// event +// 回调接口 +// +// private_data +// 传给回调接口的参数 +// +// return +// true on success. +// false on failed. +``` + +--- + +#### CP_Printer_AddOnPrinterPrintedEvent + +添加回调接口,打印机已打印页面ID更新 + +**定义:** + +```java +public boolean CP_Printer_AddOnPrinterPrintedEvent(CP_OnPrinterPrintedEvent_Callback event, Pointer +``` + +private_data); + +```java +// 添加回调接口,打印机已打印页面ID更新 +// +// event +// 回调接口 +// +// private_data +// 传给回调接口的参数 +// +// return +// true on success. +// false on failed. +``` + +--- + +#### CP_Printer_RemoveOnPrinterStatusEvent + +移除回调接口 + +**定义:** + +```java +public boolean CP_Printer_RemoveOnPrinterStatusEvent(CP_OnPrinterStatusEvent_Callback event); +// 移除回调接口 +// +// event +// 回调接口 +// +// return +// true on success. +// false on failed. +``` + +--- + +#### CP_Printer_RemoveOnPrinterReceivedEvent + +移除回调接口 + +**定义:** + +```java +public boolean CP_Printer_RemoveOnPrinterReceivedEvent(CP_OnPrinterReceivedEvent_Callback event); +// 移除回调接口 +// +// event +// 回调接口 +// +// return +// true on success. +// false on failed. +``` + +--- + +#### CP_Printer_RemoveOnPrinterPrintedEvent + +移除回调接口 + +**定义:** + +```java +public boolean CP_Printer_RemoveOnPrinterPrintedEvent(CP_OnPrinterPrintedEvent_Callback event); +// 移除回调接口 +// +// event +// 回调接口 +// +// return +// true on success. +// false on failed. +``` + +--- + +### 端口函数 + +CP_Port_EnumCom + +枚举本地串口 + +定义 + +public class CP_Port_EnumCom_Helper { + +public static String[] EnumCom() + +} + +// return + +// 枚举到的端口 + +#### CP_Port_EnumUsb + +枚举本地USB打印口 + +**定义:** + +```java +public class CP_Port_EnumUsb_Helper { +public static String[] EnumUsb() +} +// return +// 枚举到的端口 +``` + +--- + +#### CP_Port_EnumNetPrinter + +枚举网络打印机 + +**定义:** + +```java +public void CP_Port_EnumNetPrinter(int timeout, IntByReference cancel, CP_OnNetPrinterDiscovered_Callback +``` + +on_discovered, Pointer data); + +```java +// 枚举网络打印机 +// +// timeout +// 超时毫秒时间 +// +// cancel +// 取消标记位,如果设为非零,则枚举提前退出 +// +// on_discovered +// 枚举回调接口 +// +// private_data +// 传给回调接口的参数 +// +// return +// 无 +``` + +--- + +#### CP_Port_EnumBtDevice + +枚举蓝牙打印机 + +**定义:** + +```java +public void CP_Port_EnumBtDevice(int timeout, IntByReference cancel, CP_OnBluetoothDeviceDiscovered_Callback +``` + +on_discovered, Pointer data); + +```java +// 枚举蓝牙打印机 +// +// timeout +// 超时毫秒时间 +// +// cancel +// 取消标记位,如果设为非零,则枚举提前退出 +// +// on_discovered +// 枚举回调接口 +// +// private_data +// 传给回调接口的参数 +// +// return +// 无 +``` + +--- + +#### CP_Port_EnumBleDevice + +枚举BLE蓝牙打印机 + +**定义:** + +```java +public void CP_Port_EnumBleDevice(int timeout, IntByReference cancel, CP_OnBluetoothDeviceDiscovered_Callback +``` + +on_discovered, Pointer data); + +```java +// 枚举BLE蓝牙打印机 +// +// timeout +// 超时毫秒时间 +// +// cancel +// 取消标记位,如果设为非零,则枚举提前退出 +// +// on_discovered +// 枚举回调接口 +// +// private_data +// 传给回调接口的参数 +// +// return +// 无 +``` + +--- + +#### CP_Port_EnumWiFiP2PDevice + +枚举WiFi P2P打印机 + +**定义:** + +```java +public void CP_Port_EnumWiFiP2PDevice(int timeout, IntByReference cancel, +``` + +CP_OnWiFiP2PDeviceDiscovered_Callback on_discovered, Pointer data); + +```java +// 枚举WiFi P2P打印机 +// +// timeout +// 超时毫秒时间 +// +// cancel +// 取消标记位,如果设为非零,则枚举提前退出 +// +// on_discovered +// 枚举回调接口 +// +// private_data +// 传给回调接口的参数 +// +// return +// 无 +``` + +--- + +#### CP_Port_OpenCom + +打开串口 + +**定义:** + +```java +public Pointer CP_Port_OpenCom(String name, int baudrate, int databits, int parity, int stopbits, int flowcontrol, int +``` + +autoreplymode); + +```java +// 打开串口 +// +// name +// 端口名称 +// 例如:COM1,COM2,COM3,...COM11... +// +// baudrate +// 波特率 +// 一般取 9600,19200,38400,57600,115200. +// 需要和打印机波特率保持一致,建议使用高波特率以获得较好的打印速度 +// +// databits +// 数据位,范围[4,8] +// +// parity +// 校验位,各值定义如下: +// 值 定义 +// 0 无校验 +// 1 奇校验 +// 2 偶校验 +// 3 标记校验 +// 4 空白校验 +// +// stopbits +// 停止位,各值定义如下: +// 值 定义 +// 0 1位停止位 +// 1 1.5位停止位 +// 2 2位停止位 +// +// flowcontrol +// 流控制 +// +// autoreplymode +// 0 不开启自动回传模式 +// 1 开启自动回传模式 +// 注意: +``` + +--- + +#### CP_Port_OpenUsb + +打开USB + +**定义:** + +```java +public Pointer CP_Port_OpenUsb(String name, int autoreplymode); +// 打开USB +// +// name +// 端口名称 +// 可由EnumUsb获得 +// 也可以不指定,这时候,如果找到 USB 打印机,会直接打开 +// +// autoreplymode +// 0 不开启自动回传模式 +// 1 开启自动回传模式 +// 注意: +// 仅部分机型支持自动回传模式,是否支持请询问卖家 +// 启动自动回传模式之后,打印机会自动回传状态 +// 不启动则无法自动获取打印机状态 +// +// return +// 返回打开的端口句柄。非零表示打开成功,零表示打开失败。 +// +// remarks +// USB 打印机接到电脑上,如果设备管理器中出现了 USB Printing Support,则可以使用该函数打开。 +``` + +--- + +#### CP_Port_OpenTcp + +打开网口 + +**定义:** + +```java +public Pointer CP_Port_OpenTcp(String local_ip, String dest_ip, short dest_port, int timeout, int autoreplymode); +// 打开网口 +// +// local_ip +// 绑定到本地IP +// 用于多网卡或多个本地IP时,选择指定的IP +// 传入0表示不指定 +// +// dest_ip +// 地址或名称 +// 例如:192.168.1.87 +// +// dest_port +// 端口号 +// 固定值:9100 +// +// timeout +// 连接超时 +// +// autoreplymode +// 0 不开启自动回传模式 +// 1 开启自动回传模式 +// 注意: +// 仅部分机型支持自动回传模式,是否支持请询问卖家 +// 启动自动回传模式之后,打印机会自动回传状态 +// 不启动则无法自动获取打印机状态 +// +// return +// 返回打开的端口句柄。非零表示打开成功,零表示打开失败。 +// +// remarks +// PC和打印机需要同网段的才可以连接 +``` + +--- + +#### CP_Port_OpenBtSpp + +通过SPP连接蓝牙打印机 + +**定义:** + +```java +public Pointer CP_Port_OpenBtSpp(String address, int autoreplymode); +// 通过SPP连接蓝牙打印机 +// +// address +// 打印机地址 +// 例如:"01:02:03:04:05:06" +// +// autoreplymode +// 0 不开启自动回传模式 +// 1 开启自动回传模式 +// 注意: +// 仅部分机型支持自动回传模式,是否支持请询问卖家 +// 启动自动回传模式之后,打印机会自动回传状态 +// 不启动则无法自动获取打印机状态 +// +// return +// 返回打开的端口句柄。非零表示打开成功,零表示打开失败。 +// +// remarks +// only for android +``` + +--- + +#### CP_Port_OpenBtBle + +通过BLE连接蓝牙打印机 + +**定义:** + +```java +public Pointer CP_Port_OpenBtBle(String address, int autoreplymode); +// 通过BLE连接蓝牙打印机 +// +// address +// 打印机地址 +// 例如:"01:02:03:04:05:06" +// +// autoreplymode +// 0 不开启自动回传模式 +// 1 开启自动回传模式 +// 注意: +// 仅部分机型支持自动回传模式,是否支持请询问卖家 +// 启动自动回传模式之后,打印机会自动回传状态 +// 不启动则无法自动获取打印机状态 +// +// return +// 返回打开的端口句柄。非零表示打开成功,零表示打开失败。 +// +// remarks +// only for android,ios,macos +``` + +--- + +#### CP_Port_WiFiP2P_Connect + +通过WiFi P2P连接打印机 + +**定义:** + +```java +public int CP_Port_WiFiP2P_Connect(String device_address, int timeout); +// 通过WiFi P2P连接打印机 +// +// address +// 打印机地址 +// 例如:"01:02:03:04:05:06" +// +// timeout +// 连接超时毫秒时间,建议填10000 +// +// return +// 返回打印机的IP地址(网络字节序)。 +// 非零表示连接成功,零表示连接失败。 +// +// remarks +// only for android +``` + +--- + +#### CP_Port_WiFiP2P_Disconnect + +断开WiFi P2P连接 + +**定义:** + +```java +public void CP_Port_WiFiP2P_Disconnect(); +``` + +--- + +#### CP_Port_WiFiP2P_IsConnected + +WiFi P2P是否已连接 + +**定义:** + +```java +public boolean CP_Port_WiFiP2P_IsConnected(); +// WiFi P2P是否已连接 +// +// return +// 如果已连接,则返回true +// 如果未连接,则返回false +``` + +--- + +#### CP_Port_Write + +向端口写入数据 + +**定义:** + +```java +public int CP_Port_Write(Pointer handle, byte[] buffer, int count, int timeout); +// 向端口写入数据 +// +// handle +// 端口句柄,由OpenXXX返回 +// +// buffer +// 要写入的数据 +// +// count +// 要写入的长度 +// +// timeout +// 写入超时毫秒 +// +// return +// 返回写入的字节数,-1表示写入失败 +``` + +--- + +#### CP_Port_Read + +从端口接收数据 + +**定义:** + +```java +public int CP_Port_Read(Pointer handle, byte[] buffer, int count, int timeout); +// 从端口接收数据 +// +// handle +// 端口句柄,由OpenXXX返回 +// +// buffer +// 接收数据的缓冲区 +// +// count +// 要接收的数据长度 +// +// timeout +// 读取超时毫秒 +// +// return +// 返回读取的字节数,-1表示失败 +``` + +--- + +#### CP_Port_ReadUntilByte + +从端口接收数据 + +**定义:** + +```java +public int CP_Port_ReadUntilByte(Pointer handle, byte[] buffer, int count, int timeout, byte breakByte); +// 从端口接收数据 +// +// handle +// 端口句柄,由OpenXXX返回 +// +// buffer +// 接收数据的缓冲区 +// +// count +// 要接收的数据长度 +// +// timeout +// 读取超时毫秒 +// +// breakByte +// 结束读取字符 +// +// return +// 返回读取的字节数,-1表示失败 +``` + +--- + +#### CP_Port_Available + +返回可读取的字节数 + +**定义:** + +```java +public int CP_Port_Available(Pointer handle); +// 返回可读取的字节数 +// +// handle +// 端口句柄,由OpenXXX返回 +// +// return +// 返回可读取的字节数,-1表示失败 +``` + +--- + +#### CP_Port_SkipAvailable + +忽略接收缓冲区的数据 + +**定义:** + +```java +public boolean CP_Port_SkipAvailable(Pointer handle); +// 忽略接收缓冲区的数据 +// +// handle +// 端口句柄,由OpenXXX返回 +// +// return +// true on success. +// false on failed. +``` + +--- + +#### CP_Port_IsConnectionValid + +连接是否有效 + +**定义:** + +```java +public boolean CP_Port_IsConnectionValid(Pointer handle); +// 连接是否有效 +// +// handle +// 端口句柄,由OpenXXX返回 +// +// return +// 如果端口已打开,且状态持续更新,则返回true +// 如果端口未打开,已关闭,或状态超过6秒未更新,则返回false +``` + +--- + +#### CP_Port_IsOpened + +检查端口是否打开 + +**定义:** + +```java +public boolean CP_Port_IsOpened(Pointer handle); +// 检查端口是否打开 +// +// handle +// 端口句柄,由OpenXXX返回 +// +// return +// 如果端口已打开,且连接未断开未关闭,则返回true +// 如果端口未打开,或连接已断开已关闭,则返回false +``` + +--- + +#### CP_Port_Close + +关闭端口 + +**定义:** + +```java +public boolean CP_Port_Close(Pointer handle); +// 关闭端口 +// +// handle +// 端口句柄,由OpenXXX返回 +// +// return +// true on success. +// false on failed. +``` + +--- + +### 获取打印机信息函数 + +CP_Printer_GetPrinterResolutionInfo + +获取打印机分辨率信息 + +定义 + +public boolean CP_Printer_GetPrinterResolutionInfo(Pointer handle, IntByReference width_mm, IntByReference + +height_mm, IntByReference dots_per_mm); + +// 获取打印机分辨率信息 + +// + +// handle + +// 端口句柄,由OpenXXX返回 + +// + +// width_mm + +// 标签最大宽度 + +// + +// height_mm + +// 标签最大高度 + +// + +// dots_per_mm + +// 每毫米打印点数 + +// + +// return + +// true on success. + +// false on failed. + +#### CP_Printer_GetPrinterFirmwareVersion + +获取打印机固件版本 + +**定义:** + +```java +public class CP_Printer_GetPrinterFirmwareVersion_Helper { +public static String GetPrinterFirmwareVersion(Pointer handle) +} +``` + +--- + +#### CP_Printer_GetPrinterStatusInfo + +获取打印机自动回传的状态 + +**定义:** + +```java +public boolean CP_Printer_GetPrinterStatusInfo(Pointer handle, LongByReference printer_error_status, +``` + +LongByReference printer_info_status, LongByReference timestamp_ms); + +```java +// 获取打印机自动回传的状态 +// +// handle +// 端口句柄,由OpenXXX返回 +// +// printer_error_status +// 打印机错误状态 +// +// printer_info_status +// 打印机信息状态 +// +// timestamp_ms +// 时间戳 +// +// return +// true on success. +// false on failed. +``` + +--- + +#### CP_Printer_GetPrinterReceivedInfo + +获取打印机已接收字节数 + +**定义:** + +```java +public boolean CP_Printer_GetPrinterReceivedInfo(Pointer handle, IntByReference printer_received_byte_count, +``` + +LongByReference timestamp_ms); + +```java +// 获取打印机已接收字节数 +// +// handle +// 端口句柄,由OpenXXX返回 +// +// printer_received_byte_count +// 打印机已接收字节数 +// +// timestamp_ms +// 时间戳 +// +// return +// true on success. +// false on failed. +``` + +--- + +#### CP_Printer_GetPrinterPrintedInfo + +获取打印机已打印的单据号 + +**定义:** + +```java +public boolean CP_Printer_GetPrinterPrintedInfo(Pointer handle, IntByReference printer_printed_page_id, +``` + +LongByReference timestamp_ms); + +```java +// 获取打印机已打印的单据号 +// +// handle +// 端口句柄,由OpenXXX返回 +// +// printer_printed_page_id +// 打印机已打印的单据号 +// +// timestamp_ms +// 时间戳 +// +// return +// true on success. +// false on failed. +``` + +--- + +#### CP_Printer_GetPrinterLabelPositionAdjustmentInfo + +获取打印机标签位置微调信息 + +**定义:** + +```java +public boolean CP_Printer_GetPrinterLabelPositionAdjustmentInfo(Pointer handle, DoubleByReference +``` + +label_print_position_adjustment, DoubleByReference label_tear_position_adjustment, LongByReference + +timestamp_ms); + +```java +// 获取打印机标签位置微调信息 +// +// handle +// 端口句柄,由OpenXXX返回 +// +// label_print_position_adjustment +// 打印机标签打印位置微调 +// +// label_tear_position_adjustment +// 打印机标签撕纸位置微调 +// +// timestamp_ms +// 时间戳 +// +// return +// true on success. +// false on failed. +``` + +--- + +#### CP_Printer_SetPrinterLabelPositionAdjustmentInfo + +设置标签打印位置和撕纸位置微调 + +**定义:** + +```java +public boolean CP_Printer_SetPrinterLabelPositionAdjustmentInfo(Pointer handle, double +``` + +label_print_position_adjustment, double label_tear_position_adjustment); + +```java +// 设置标签打印位置和撕纸位置微调 +// +// handle +// 端口句柄,由OpenXXX返回 +// +// label_print_position_adjustment +// 标签打印位置微调mm(微调不超过±4mm) +// +// label_tear_position_adjustment +// 标签撕纸位置微调mm(微调不超过±4mm) +// +// return +// 返回值仅指示指令是否写入成功。返回true表示写入成功,返回false表示写入失败。 +``` + +--- + +#### CP_Printer_ClearPrinterBuffer + +实时清除打印机缓存 + +**定义:** + +```java +public boolean CP_Printer_ClearPrinterBuffer(Pointer handle); +// 实时清除打印机缓存 +// +// handle +// 端口句柄,由OpenXXX返回 +// +// return +// true on success. +// false on failed. +``` + +--- + +#### CP_Printer_ClearPrinterError + +实时清除打印机错误 + +**定义:** + +```java +public boolean CP_Printer_ClearPrinterError(Pointer handle); +// 实时清除打印机错误 +// +// handle +// 端口句柄,由OpenXXX返回 +// +// return +// true on success. +// false on failed. +``` + +--- + +### 票据函数 + +CP_Pos_QueryRTStatus + +查询打印机实时状态 + +定义 + +public int CP_Pos_QueryRTStatus(Pointer handle, int timeout); + +// 查询打印机实时状态 + +// 如果是支持自动回传的机型,状态会自动回传,不需要使用本指令查询 + +// 由于实时状态指令,无校验,无法保证结果一定正确 + +// + +// handle + +// 端口句柄,由OpenXXX返回 + +// + +// timeout + +// 超时毫秒时间。 + +// 查询等待时间不超过此时间。 + +// + +// return + +// 返回值仅指示指令是否成功。成功返回实时状态,失败返回0。 + +// 详细状态请查看CP_RTSTATUS_XXX,如果状态定义与实际机型不符,以实测为准。 + +#### CP_Pos_QueryPrintResult + +查询前面内容的打印结果 + +**定义:** + +```java +public boolean CP_Pos_QueryPrintResult(Pointer handle, int timeout); +// 查询前面内容的打印结果 +// +// handle +// 端口句柄,由OpenXXX返回 +// +// timeout +// 超时毫秒时间。 +// 查询打印结果等待时间不超过此时间。 +// +// return +// 返回值仅指示指令是否打印成功。返回true表示打印成功,返回false表示打印失败或查询失败。 +``` + +--- + +#### CP_Pos_KickOutDrawer + +开钱箱(产生钱箱脉冲) + +**定义:** + +```java +public boolean CP_Pos_KickOutDrawer(Pointer handle, int nDrawerIndex, int nHighLevelTime, int nLowLevelTime); +// 开钱箱(产生钱箱脉冲) +// +// handle +// 端口句柄,由OpenXXX返回 +// +// nDrawerIndex +// 钱箱编号,各值说明如下 +// 编号 说明 +// 0 钱箱引脚2 +// 1 钱箱引脚5 +// +// nHighLevelTime +// 高电平毫秒时间 +// +// nLowLevelTime +// 低电平毫秒时间 +// +// return +// 返回值仅指示指令是否写入成功。返回true表示写入成功,返回false表示写入失败。 +``` + +--- + +#### CP_Pos_Beep + +蜂鸣器鸣叫 + +**定义:** + +```java +public boolean CP_Pos_Beep(Pointer handle, int nBeepCount, int nBeepMs); +// 蜂鸣器鸣叫 +// +// handle +// 端口句柄,由OpenXXX返回 +// +// nBeepCount +// 鸣叫次数 +// +// nBeepMs +// 蜂鸣毫秒时间,取值范围[100,900]。取整到百毫秒。 +// +// return +// 返回值仅指示指令是否写入成功。返回true表示写入成功,返回false表示写入失败。 +``` + +--- + +#### CP_Pos_FeedAndHalfCutPaper + +走纸到切刀位置并半切纸 + +**定义:** + +```java +public boolean CP_Pos_FeedAndHalfCutPaper(Pointer handle); +// 走纸到切刀位置并半切纸 +// +// handle +// 端口句柄,由OpenXXX返回 +// +// return +// 返回值仅指示指令是否写入成功。返回true表示写入成功,返回false表示写入失败。 +``` + +--- + +#### CP_Pos_FullCutPaper + +切刀全切 + +**定义:** + +```java +public boolean CP_Pos_FullCutPaper(Pointer handle); +// 切刀全切 +// +// handle +// 端口句柄,由OpenXXX返回 +// +// return +// 返回值仅指示指令是否写入成功。返回true表示写入成功,返回false表示写入失败。 +``` + +--- + +#### CP_Pos_HalfCutPaper + +切刀半切 + +**定义:** + +```java +public boolean CP_Pos_HalfCutPaper(Pointer handle); +// 切刀半切 +// +// handle +// 端口句柄,由OpenXXX返回 +// +// return +// 返回值仅指示指令是否写入成功。返回true表示写入成功,返回false表示写入失败。 +``` + +--- + +#### CP_Pos_FeedLine + +打印机进纸指定行数 + +**定义:** + +```java +public boolean CP_Pos_FeedLine(Pointer handle, int numLines); +// 打印机进纸指定行数 +// +// handle +// 端口句柄,由OpenXXX返回 +// +// numLines +// 要进的行数 +// +// return +// 返回值仅指示指令是否写入成功。返回true表示写入成功,返回false表示写入失败。 +``` + +--- + +#### CP_Pos_FeedDot + +打印机进纸指定点数 + +**定义:** + +```java +public boolean CP_Pos_FeedDot(Pointer handle, int numDots); +// 打印机进纸指定点数 +// +// handle +// 端口句柄,由OpenXXX返回 +// +// numDots +// 要进的点数 +// +// return +// 返回值仅指示指令是否写入成功。返回true表示写入成功,返回false表示写入失败。 +``` + +--- + +#### CP_Pos_PrintSelfTestPage + +打印机打印自检页 + +**定义:** + +```java +public boolean CP_Pos_PrintSelfTestPage(Pointer handle); +// 打印机打印自检页 +// +// handle +// 端口句柄,由OpenXXX返回 +// +// return +// 返回值仅指示指令是否写入成功。返回true表示写入成功,返回false表示写入失败。 +``` + +--- + +#### CP_Pos_PrintText + +打印文本 + +**定义:** + +```java +public boolean CP_Pos_PrintText(Pointer handle, String str); +// 打印文本 +// +// handle +// 端口句柄,由OpenXXX返回 +// +// str +// 要打印的字符串 +// +// return +// 返回值仅指示指令是否写入成功。返回true表示写入成功,返回false表示写入失败。 +``` + +--- + +#### CP_Pos_PrintTextInUTF8 + +打印文本 + +**定义:** + +```java +public boolean CP_Pos_PrintTextInUTF8(Pointer handle, WString str); +// 打印文本 +// +// handle +// 端口句柄,由OpenXXX返回 +// +// str +// 要打印的字符串 +// +// return +// 返回值仅指示指令是否写入成功。返回true表示写入成功,返回false表示写入失败。 +// +// remarks +// 该函数会将数据转为UTF8编码发送。 +``` + +--- + +#### CP_Pos_PrintTextInGBK + +打印文本 + +**定义:** + +```java +public boolean CP_Pos_PrintTextInGBK(Pointer handle, WString str); +// 打印文本 +// +// handle +// 端口句柄,由OpenXXX返回 +// +// str +// 要打印的字符串 +// +// return +// 返回值仅指示指令是否写入成功。返回true表示写入成功,返回false表示写入失败。 +// +// remarks +// 该函数会将数据转为GBK编码发送。 +``` + +--- + +#### CP_Pos_PrintTextInBIG5 + +打印文本 + +**定义:** + +```java +public boolean CP_Pos_PrintTextInBIG5(Pointer handle, WString str); +// 打印文本 +// +// handle +// 端口句柄,由OpenXXX返回 +// +// str +// 要打印的字符串 +// +// return +// 返回值仅指示指令是否写入成功。返回true表示写入成功,返回false表示写入失败。 +// +// remarks +// 该函数会将数据转为BIG5编码发送。 +``` + +--- + +#### CP_Pos_PrintTextInShiftJIS + +打印文本 + +**定义:** + +```java +public boolean CP_Pos_PrintTextInShiftJIS(Pointer handle, WString str); +// 打印文本 +// +// handle +// 端口句柄,由OpenXXX返回 +// +// str +// 要打印的字符串 +// +// return +// 返回值仅指示指令是否写入成功。返回true表示写入成功,返回false表示写入失败。 +// +// remarks +// 该函数会将数据转为ShiftJIS编码发送。 +``` + +--- + +#### CP_Pos_PrintTextInEUCKR + +打印文本 + +**定义:** + +```java +public boolean CP_Pos_PrintTextInEUCKR(Pointer handle, WString str); +// 打印文本 +// +// handle +// 端口句柄,由OpenXXX返回 +// +// str +// 要打印的字符串 +// +// return +// 返回值仅指示指令是否写入成功。返回true表示写入成功,返回false表示写入失败。 +// +// remarks +// 该函数会将数据转为EUCKR编码发送。 +``` + +--- + +#### CP_Pos_PrintBarcode + +打印一维条码 + +**定义:** + +```java +public boolean CP_Pos_PrintBarcode(Pointer handle, int nBarcodeType, String str); +// 打印一维条码 +// +// handle +// 端口句柄,由OpenXXX返回 +// +// nBarcodeType +// 标识条码类型 +// 各值定义如下: +// 值 类型 +// 0x41 UPC-A +// 0x42 UPC-E +// 0x43 EAN13 +// 0x44 EAN8 +// 0x45 CODE39 +// 0x46 ITF +// 0x47 CODABAR +// 0x48 CODE93 +// 0x49 CODE128 +// +// str +// 要打印的条码 +// +// return +// 返回值仅指示指令是否写入成功。返回true表示写入成功,返回false表示写入失败。 +``` + +--- + +#### CP_Pos_PrintBarcode_Code128Auto + +打印CODE128条码,该函数自动切换编码,以便节省空间 + +**定义:** + +```java +public boolean CP_Pos_PrintBarcode_Code128Auto(Pointer handle, String str); +// 打印CODE128条码,该函数自动切换编码,以便节省空间 +// 正常情况下,请不要使用这个函数进行打印CODE128码 +// 这个函数主要用于兼容部分老款机型 +// 新款机型默认已经是支持自动切换编码的 +// 新款机型使用这个函数是无法打印条码的 +// +// handle +// 端口句柄,由OpenXXX返回 +// +// str +// 要打印的条码 +// +// return +// 返回值仅指示指令是否写入成功。返回true表示写入成功,返回false表示写入失败。 +``` + +--- + +#### CP_Pos_PrintQRCode + +打印QR码 + +**定义:** + +```java +public boolean CP_Pos_PrintQRCode(Pointer handle, int nVersion, int nECCLevel, String str); +// 打印QR码 +// +// handle +// 端口句柄,由OpenXXX返回 +// +// nVersion +// 指定字符版本。取值范围:[0,16]。 +// 当 version 为 0 时,打印机根据字符串长度自动计算版本号。 +// +// nECCLevel +// 指定纠错等级。取值范围:[1, 4]。 +// 各值定义如下: +// ECC 纠错等级 +// 1 L:7%,低纠错,数据多。 +// 2 M:15%,中纠错 +// 3 Q:优化纠错 +// 4 H:30%,最高纠错,数据少。 +// +// str +// 要打印的QR码 +// +// return +// 返回值仅指示指令是否写入成功。返回true表示写入成功,返回false表示写入失败。 +``` + +--- + +#### CP_Pos_PrintQRCodeUseEpsonCmd + +打印QR码 + +**定义:** + +```java +public boolean CP_Pos_PrintQRCodeUseEpsonCmd(Pointer handle, int nQRCodeUnitWidth, int nECCLevel, String str); +// 打印QR码 +// +// handle +// 端口句柄,由OpenXXX返回 +// +// nQRCodeUnitWidth +// QRCode 码码块宽度,取值范围:[1, 16]。 +// +// nECCLevel +// 指定纠错等级。取值范围:[1, 4]。 +// 各值定义如下: +// ECC 纠错等级 +// 1 L:7%,低纠错,数据多。 +// 2 M:15%,中纠错 +// 3 Q:优化纠错 +// 4 H:30%,最高纠错,数据少。 +// +// str +// 要打印的QR码 +// +// return +// 返回值仅指示指令是否写入成功。返回true表示写入成功,返回false表示写入失败。 +``` + +--- + +#### CP_Pos_PrintQRCodeUseImageCmd + +打印QR码(以图片指令打印二维码,可用于不支持二维码指令的机型) + +**定义:** + +```java +public boolean CP_Pos_PrintQRCodeUseImageCmd(Pointer handle, String str, int nVersion, int nQRCodeUnitWidth, +``` + +int nECCLevel, int compression_method); + +```java +// 打印QR码(以图片指令打印二维码,可用于不支持二维码指令的机型) +// +// handle +// 端口句柄,由OpenXXX返回 +// +// str +// 要打印的QR码 +// +// nVersion +// 指定字符版本。取值范围:[0,16]。 +// 当 version 为 0 时,打印机根据字符串长度自动计算版本号。 +// +// nQRCodeUnitWidth +// QRCode 码码块宽度,取值范围:[1, 16]。 +// +// nECCLevel +// 指定纠错等级。取值范围:[1, 4]。 +// 各值定义如下: +// ECC 纠错等级 +// 1 L:7%,低纠错,数据多。 +// 2 M:15%,中纠错 +// 3 Q:优化纠错 +// 4 H:30%,最高纠错,数据少。 +// +// compression_method +// 最终打印数据的压缩方式,各值定义如下 +// 值 定义 +// 0 不压缩 +// 1 一级压缩 +// 2 二级压缩 +// +// return +// 返回值仅指示指令是否写入成功。返回true表示写入成功,返回false表示写入失败。 +``` + +--- + +#### CP_Pos_PrintDoubleQRCode + +打印两个QR码 + +**定义:** + +```java +public boolean CP_Pos_PrintDoubleQRCode(Pointer handle, int nQRCodeUnitWidth, int nQR1Position, int +``` + +nQR1Version, int nQR1ECCLevel, String strQR1, int nQR2Position, int nQR2Version, int nQR2ECCLevel, String strQR2); + +```java +// 打印两个QR码 +// +// handle +// 端口句柄,由OpenXXX返回 +// +// nQRCodeUnitWidth +// QRCode 码码块宽度,取值范围:[1, 8]。 +// +// nQR1Position +// nQR2Position +// QRCode position +// +// nQR1Version +// nQR2Version +// 指定字符版本。取值范围:[0,16]。 +// 当 version 为 0 时,打印机根据字符串长度自动计算版本号。 +// +// nQR1ECCLevel +// nQR2ECCLevel +// 指定纠错等级。取值范围:[1, 4]。 +// 各值定义如下: +// ECC 纠错等级 +// 1 L:7%,低纠错,数据多。 +// 2 M:15%,中纠错 +// 3 Q:优化纠错 +// 4 H:30%,最高纠错,数据少。 +// +// strQR1 +// strQR2 +// 要打印的QR码 +// +// return +// 返回值仅指示指令是否写入成功。返回true表示写入成功,返回false表示写入失败。 +``` + +--- + +#### CP_Pos_PrintPDF417BarcodeUseEpsonCmd + +打印PDF417条码 + +**定义:** + +```java +public boolean CP_Pos_PrintPDF417BarcodeUseEpsonCmd(Pointer handle, int columnCount, int rowCount, int +``` + +unitWidth, int rowHeight, int nECCLevel, int dataProcessingMode, String str); + +```java +// 打印PDF417条码 +// +// handle +// 端口句柄,由OpenXXX返回 +// +// columnCount +// 列数,取值范围[0,30] +// +// rowCount +// 行数,取值范围0,[3,90] +// +// unitWidth +// 模块单元宽度,取值范围[2,8] +// +// rowHeight +// 行高,取值范围[2,8] +// +// nECCLevel +// 指定纠错等级。取值范围:[0,8]。 +// +// dataProcessingMode +// 数据处理模式。0选择标准PDF417,1选择截断PDF417。 +// +// str +// 要打印的PDF417码 +// +// return +// 返回值仅指示指令是否写入成功。返回true表示写入成功,返回false表示写入失败。 +``` + +--- + +#### CP_Pos_PrintRasterImageFromFile + +打印图片 + +**定义:** + +```java +public boolean CP_Pos_PrintRasterImageFromFile(Pointer handle, int dstw, int dsth, String pszFile, int +``` + +binaryzation_method, int compression_method); + +```java +// 打印图片 +// +// handle +// 端口句柄,由OpenXXX返回 +// +// dstw +// 要打印的宽度 +// +// dsth +// 要打印的高度 +// +// pszFile +// 图片的路径 +// +// binaryzation_method +// 图片二值化算法。0表示抖动算法,1表示阀值算法,2表示误差扩散法。具体效果请测试查看。 +// +// compression_method +// 最终打印数据的压缩方式,各值定义如下 +// 值 定义 +// 0 不压缩 +// 1 一级压缩 +// 2 二级压缩 +// +// return +// 返回值仅指示指令是否写入成功。返回true表示写入成功,返回false表示写入失败。 +``` + +--- + +#### CP_Pos_PrintRasterImageFromData + +打印图片(图片可由文件读取) + +**定义:** + +```java +public boolean CP_Pos_PrintRasterImageFromData(Pointer handle, int dstw, int dsth, byte[] data, int data_size, int +``` + +binaryzation_method, int compression_method); + +```java +// 打印图片(图片可由文件读取) +// +// handle +// 端口句柄,由OpenXXX返回 +// +// dstw +// 要打印的宽度 +// +// dsth +// 要打印的高度 +// +// data +// 图片数据。 +// +// data_size +// 图片数据长度 +// +// binaryzation_method +// 图片二值化算法。0表示抖动算法,1表示阀值算法,2表示误差扩散法。具体效果请测试查看。 +// +// compression_method +// 最终打印数据的压缩方式,各值定义如下 +// 值 定义 +// 0 不压缩 +// 1 一级压缩 +// 2 二级压缩 +// +// return +// 返回值仅指示指令是否写入成功。返回true表示写入成功,返回false表示写入失败。 +``` + +也可以使用下面的函数 + +```java +public class CP_Pos_PrintRasterImageFromData_Helper { +public static boolean PrintRasterImageFromBitmap(Pointer handle, int dstw, int dsth, Bitmap bitmap, int +``` + +binaryzation_method, int compression_method) + +```java +} +``` + +--- + +#### CP_Pos_PrintRasterImageFromPixels + +打印图片像素数据 + +**定义:** + +```java +public boolean CP_Pos_PrintRasterImageFromPixels(Pointer handle, byte[] img_data, int img_datalen, int img_width, +``` + +int img_height, int img_stride, int img_format, int binaryzation_method, int compression_method); + +```java +// 打印图片像素数据 +// +// handle +// 端口句柄,由OpenXXX返回 +// +// img_data +// 图片的像素数据。 +// +// img_datalen +// 图片的像素数据字节数。 +// +// img_width +// 图片的像素宽度。 +// +// img_height +// 图片的像素高度。 +// +// img_stride +// 图片水平跨度。表示每行字节数。 +// +// img_format +// 图片像素数据格式,各值定义如下 +// 值 定义 +// 1 mono +// 2 monolsb +// 3 gray +// 4 r.g.b in byte-ordered +// 5 b.g.r in byte-ordered +// 6 a.r.g.b in byte-ordered +// 7 r.g.b.a in byte-ordered +// 8 a.b.g.r in byte-ordered +// 9 b.g.r.a in byte-ordered +// +// binaryzation_method +// 图片二值化算法。0表示抖动算法,1表示阀值算法,2表示误差扩散法。具体效果请测试查看。 +// +// compression_method +``` + +--- + +#### CP_Pos_PrintHorizontalLine + +打印一条水平线 + +**定义:** + +```java +public boolean CP_Pos_PrintHorizontalLine(Pointer handle, int nLineStartPosition, int nLineEndPosition); +// 打印一条水平线 +// +// handle +// 端口句柄,由OpenXXX返回 +// +// nLineStartPosition +// 线段起点位置 +// +// nLineEndPosition +// 线段终点位置 +// +// return +// 返回值仅指示指令是否写入成功。返回true表示写入成功,返回false表示写入失败。 +``` + +--- + +#### CP_Pos_PrintHorizontalLineSpecifyThickness + +打印一条水平线 + +**定义:** + +```java +public boolean CP_Pos_PrintHorizontalLineSpecifyThickness(Pointer handle, int nLineStartPosition, int +``` + +nLineEndPosition, int nLineThickness); + +```java +// 打印一条水平线 +// +// handle +// 端口句柄,由OpenXXX返回 +// +// nLineStartPosition +// 线段起点位置 +// +// nLineEndPosition +// 线段终点位置 +// +// nLineThickness +// 线段粗细 +// +// return +// 返回值仅指示指令是否写入成功。返回true表示写入成功,返回false表示写入失败。 +``` + +--- + +#### CP_Pos_PrintMultipleHorizontalLinesAtOneRow + +同一行上打印多条水平线,连续调用可打印曲线 + +**定义:** + +```java +public boolean CP_Pos_PrintMultipleHorizontalLinesAtOneRow(Pointer handle, int nLineCount, int[] +``` + +pLineStartPosition, int[] pLineEndPosition); + +```java +// 同一行上打印多条水平线,连续调用可打印曲线 +// +// handle +// 端口句柄,由OpenXXX返回 +// +// nLineCount +// 线段条数 +// +// pLineStartPosition +// 线段起点位置 +// +// pLineEndPosition +// 线段终点位置 +// +// return +// 返回值仅指示指令是否写入成功。返回true表示写入成功,返回false表示写入失败。 +``` + +--- + +#### CP_Pos_ResetPrinter + +复位打印机,清除设置 + +**定义:** + +```java +public boolean CP_Pos_ResetPrinter(Pointer handle); +// 复位打印机,清除设置 +// +// handle +// 端口句柄,由OpenXXX返回 +// +// return +// 返回值仅指示指令是否写入成功。返回true表示写入成功,返回false表示写入失败。 +``` + +--- + +#### CP_Pos_SetPrintSpeed + +设置打印速度(部分机型支持) + +**定义:** + +```java +public boolean CP_Pos_SetPrintSpeed(Pointer handle, int nSpeed); +// 设置打印速度(部分机型支持) +// +// handle +// 端口句柄,由OpenXXX返回 +// +// nSpeed +// 打印速度,单位毫米每秒 +// +// return +// 返回值仅指示指令是否写入成功。返回true表示写入成功,返回false表示写入失败。 +``` + +--- + +#### CP_Pos_SetPrintDensity + +设置打印浓度(部分机型支持) + +**定义:** + +```java +public boolean CP_Pos_SetPrintDensity(Pointer handle, int nDensity); +// 设置打印浓度(部分机型支持) +// +// handle +// 端口句柄,由OpenXXX返回 +// +// nDensity +// 设置打印浓度[0,15] +// +// return +// 返回值仅指示指令是否写入成功。返回true表示写入成功,返回false表示写入失败。 +``` + +--- + +#### CP_Pos_SetSingleByteMode + +设置打印机为单字节编码 + +**定义:** + +```java +public boolean CP_Pos_SetSingleByteMode(Pointer handle); +// 设置打印机为单字节编码 +// +// handle +// 端口句柄,由OpenXXX返回 +// +// return +// 返回值仅指示指令是否写入成功。返回true表示写入成功,返回false表示写入失败。 +``` + +--- + +#### CP_Pos_SetCharacterSet + +设置打印机字符集 + +**定义:** + +```java +public boolean CP_Pos_SetCharacterSet(Pointer handle, int nCharacterSet); +// 设置打印机字符集 +// +// handle +// 端口句柄,由OpenXXX返回 +// +// nCharacterSet +// 打印机字符集,范围[0,15] +// +// return +// 返回值仅指示指令是否写入成功。返回true表示写入成功,返回false表示写入失败。 +``` + +--- + +#### CP_Pos_SetCharacterCodepage + +设置字符代码页 + +**定义:** + +```java +public boolean CP_Pos_SetCharacterCodepage(Pointer handle, int nCharacterCodepage); +// 设置字符代码页 +// +// handle +// 端口句柄,由OpenXXX返回 +// +// nCharacterCodepage +// 字符代码页,范围[0,255] +// +// return +// 返回值仅指示指令是否写入成功。返回true表示写入成功,返回false表示写入失败。 +``` + +--- + +#### CP_Pos_SetMultiByteMode + +设置打印机为多字节编码 + +**定义:** + +```java +public boolean CP_Pos_SetMultiByteMode(Pointer handle); +// 设置打印机为多字节编码 +// +// handle +// 端口句柄,由OpenXXX返回 +// +// return +// 返回值仅指示指令是否写入成功。返回true表示写入成功,返回false表示写入失败。 +``` + +--- + +#### CP_Pos_SetMultiByteEncoding + +设置打印机多字节编码 + +**定义:** + +```java +public boolean CP_Pos_SetMultiByteEncoding(Pointer handle, int nEncoding); +// 设置打印机多字节编码 +// +// handle +// 端口句柄,由OpenXXX返回 +// +// nEncoding +// 多字节编码,各值定义如下: +// 值 定义 +// 0 GBK +// 1 UTF8 +// 3 BIG5 +// 4 SHIFT-JIS +// 5 EUC-KR +// +// return +// 返回值仅指示指令是否写入成功。返回true表示写入成功,返回false表示写入失败。 +``` + +--- + +#### CP_Pos_SetMovementUnit + +设置打印移动单位 + +**定义:** + +```java +public boolean CP_Pos_SetMovementUnit(Pointer handle, int nHorizontalMovementUnit, int nVerticalMovementUnit); +// 设置打印移动单位 +// +// handle +// 端口句柄,由OpenXXX返回 +// +// nHorizontalMovementUnit +// 水平移动单位 +// +// nVerticalMovementUnit +// 垂直移动单位 +// +// return +// 返回值仅指示指令是否写入成功。返回true表示写入成功,返回false表示写入失败。 +// +// remarks +// 移动单位设置为200,则1mm=8点。 +``` + +--- + +#### CP_Pos_SetPrintAreaLeftMargin + +设置打印区域左边空白 + +**定义:** + +```java +public boolean CP_Pos_SetPrintAreaLeftMargin(Pointer handle, int nLeftMargin); +// 设置打印区域左边空白 +// +// handle +// 端口句柄,由OpenXXX返回 +// +// nLeftMargin +// 左边空白 +// +// return +// 返回值仅指示指令是否写入成功。返回true表示写入成功,返回false表示写入失败。 +``` + +--- + +#### CP_Pos_SetPrintAreaWidth + +设置打印区域宽度 + +**定义:** + +```java +public boolean CP_Pos_SetPrintAreaWidth(Pointer handle, int nWidth); +// 设置打印区域宽度 +// +// handle +// 端口句柄,由OpenXXX返回 +// +// nWidth +// 打印区域宽度 +// +// return +// 返回值仅指示指令是否写入成功。返回true表示写入成功,返回false表示写入失败。 +``` + +--- + +#### CP_Pos_SetHorizontalAbsolutePrintPosition + +设置横向绝对打印位置 + +**定义:** + +```java +public boolean CP_Pos_SetHorizontalAbsolutePrintPosition(Pointer handle, int nPosition); +// 设置横向绝对打印位置 +// +// handle +// 端口句柄,由OpenXXX返回 +// +// nPosition +// 打印位置 +// +// return +// 返回值仅指示指令是否写入成功。返回true表示写入成功,返回false表示写入失败。 +``` + +--- + +#### CP_Pos_SetHorizontalRelativePrintPosition + +设置横向相对打印位置 + +**定义:** + +```java +public boolean CP_Pos_SetHorizontalRelativePrintPosition(Pointer handle, int nPosition); +// 设置横向相对打印位置 +// +// handle +// 端口句柄,由OpenXXX返回 +// +// nPosition +// 打印位置 +// +// return +// 返回值仅指示指令是否写入成功。返回true表示写入成功,返回false表示写入失败。 +``` + +--- + +#### CP_Pos_SetVerticalAbsolutePrintPosition + +设置纵向绝对打印位置,仅在页模式下有效。 + +**定义:** + +```java +public boolean CP_Pos_SetVerticalAbsolutePrintPosition(Pointer handle, int nPosition); +// 设置纵向绝对打印位置,仅在页模式下有效。 +// +// handle +// 端口句柄,由OpenXXX返回 +// +// nPosition +// 打印位置 +// +// return +// 返回值仅指示指令是否写入成功。返回true表示写入成功,返回false表示写入失败。 +``` + +--- + +#### CP_Pos_SetVerticalRelativePrintPosition + +设置纵向相对打印位置,仅在页模式下有效。 + +**定义:** + +```java +public boolean CP_Pos_SetVerticalRelativePrintPosition(Pointer handle, int nPosition); +// 设置纵向相对打印位置,仅在页模式下有效。 +// +// handle +// 端口句柄,由OpenXXX返回 +// +// nPosition +// 打印位置 +// +// return +// 返回值仅指示指令是否写入成功。返回true表示写入成功,返回false表示写入失败。 +``` + +--- + +#### CP_Pos_SetAlignment + +设置打印对齐方式 + +**定义:** + +```java +public boolean CP_Pos_SetAlignment(Pointer handle, int nAlignment); +// 设置打印对齐方式 +// +// handle +// 端口句柄,由OpenXXX返回 +// +// nAlignment +// 打印对齐方式,各值定义如下: +// 值 定义 +// 0 左对齐 +// 1 中对齐 +// 2 右对齐 +// +// return +// 返回值仅指示指令是否写入成功。返回true表示写入成功,返回false表示写入失败。 +``` + +--- + +#### CP_Pos_SetTextScale + +设置文本放大倍数 + +**定义:** + +```java +public boolean CP_Pos_SetTextScale(Pointer handle, int nWidthScale, int nHeightScale); +// 设置文本放大倍数 +// +// handle +// 端口句柄,由OpenXXX返回 +// +// nWidthScale +// 宽度放大倍数 +// +// nHeightScale +// 高度放大倍数 +// +// return +// 返回值仅指示指令是否写入成功。返回true表示写入成功,返回false表示写入失败。 +``` + +--- + +#### CP_Pos_SetAsciiTextFontType + +设置英文字符字体类型 + +**定义:** + +```java +public boolean CP_Pos_SetAsciiTextFontType(Pointer handle, int nFontType); +// 设置英文字符字体类型 +// +// handle +// 端口句柄,由OpenXXX返回 +// +// nFontType +// 英文字符字体类型,各值定义如下: +// 值 定义 +// 0 字型A(12x24) +// 1 字型B(9x17) +// +// return +// 返回值仅指示指令是否写入成功。返回true表示写入成功,返回false表示写入失败。 +``` + +--- + +#### CP_Pos_SetTextBold + +设置文本加粗打印 + +**定义:** + +```java +public boolean CP_Pos_SetTextBold(Pointer handle, int nBold); +// 设置文本加粗打印 +// +// handle +// 端口句柄,由OpenXXX返回 +// +// nBold +// 是否加粗,各值定义如下: +// 值 定义 +// 0 不加粗 +// 1 加粗 +// +// return +// 返回值仅指示指令是否写入成功。返回true表示写入成功,返回false表示写入失败。 +``` + +--- + +#### CP_Pos_SetTextUnderline + +设置文本下划线 + +**定义:** + +```java +public boolean CP_Pos_SetTextUnderline(Pointer handle, int nUnderline); +// 设置文本下划线 +// +// handle +// 端口句柄,由OpenXXX返回 +// +// nUnderline +// 文本下划线,各值定义如下: +// 值 定义 +// 0 无下划线 +// 1 1点下划线 +// 2 2点下划线 +// +// return +// 返回值仅指示指令是否写入成功。返回true表示写入成功,返回false表示写入失败。 +``` + +--- + +#### CP_Pos_SetTextUpsideDown + +设置文本倒置打印 + +**定义:** + +```java +public boolean CP_Pos_SetTextUpsideDown(Pointer handle, int nUpsideDown); +// 设置文本倒置打印 +// +// handle +// 端口句柄,由OpenXXX返回 +// +// nUpsideDown +// 倒置打印,各值定义如下: +// 值 定义 +// 0 不倒置打印 +// 1 倒置打印 +// +// return +// 返回值仅指示指令是否写入成功。返回true表示写入成功,返回false表示写入失败。 +``` + +--- + +#### CP_Pos_SetTextWhiteOnBlack + +设置黑白反显 + +**定义:** + +```java +public boolean CP_Pos_SetTextWhiteOnBlack(Pointer handle, int nWhiteOnBlack); +// 设置黑白反显 +// +// handle +// 端口句柄,由OpenXXX返回 +// +// nWhiteOnBlack +// 黑白反显,各值定义如下: +// 值 定义 +// 0 不黑白反显 +// 1 黑白反显 +// +// return +// 返回值仅指示指令是否写入成功。返回true表示写入成功,返回false表示写入失败。 +``` + +--- + +#### CP_Pos_SetTextRotate + +设置文本旋转90度打印 + +**定义:** + +```java +public boolean CP_Pos_SetTextRotate(Pointer handle, int nRotate); +// 设置文本旋转90度打印 +// +// handle +// 端口句柄,由OpenXXX返回 +// +// nRotate +// 旋转打印,各值定义如下: +// 值 定义 +// 0 不旋转打印 +// 1 旋转90度打印 +// +// return +// 返回值仅指示指令是否写入成功。返回true表示写入成功,返回false表示写入失败。 +``` + +--- + +#### CP_Pos_SetTextLineHeight + +设置行高 + +**定义:** + +```java +public boolean CP_Pos_SetTextLineHeight(Pointer handle, int nLineHeight); +// 设置行高 +// +// handle +// 端口句柄,由OpenXXX返回 +// +// nLineHeight +// 行高,范围[1,255] +// +// return +// 返回值仅指示指令是否写入成功。返回true表示写入成功,返回false表示写入失败。 +``` + +--- + +#### CP_Pos_SetAsciiTextCharRightSpacing + +设置ASCII字符右边空白 + +**定义:** + +```java +public boolean CP_Pos_SetAsciiTextCharRightSpacing(Pointer handle, int nSpacing); +// 设置ASCII字符右边空白 +// +// handle +// 端口句柄,由OpenXXX返回 +// +// nSpacing +// 右边空白,范围[1,255] +// +// return +// 返回值仅指示指令是否写入成功。返回true表示写入成功,返回false表示写入失败。 +``` + +--- + +#### CP_Pos_SetKanjiTextCharSpacing + +设置汉字文本字符左边空白和右边空白 + +**定义:** + +```java +public boolean CP_Pos_SetKanjiTextCharSpacing(Pointer handle, int nLeftSpacing, int nRightSpacing); +// 设置汉字文本字符左边空白和右边空白 +// +// handle +// 端口句柄,由OpenXXX返回 +// +// nLeftSpacing +// 右边空白,范围[1,255] +// +// nRightSpacing +// 右边空白,范围[1,255] +// +// return +// 返回值仅指示指令是否写入成功。返回true表示写入成功,返回false表示写入失败。 +``` + +--- + +#### CP_Pos_SetBarcodeUnitWidth + +设置条码和二维码单元宽度 + +**定义:** + +```java +public boolean CP_Pos_SetBarcodeUnitWidth(Pointer handle, int nBarcodeUnitWidth); +// 设置条码和二维码单元宽度 +// +// handle +// 端口句柄,由OpenXXX返回 +// +// nBarcodeUnitWidth +// 条码单元宽度,取值范围:[1,6] +// +// return +// 返回值仅指示指令是否写入成功。返回true表示写入成功,返回false表示写入失败。 +``` + +--- + +#### CP_Pos_SetBarcodeHeight + +设置条码高度 + +**定义:** + +```java +public boolean CP_Pos_SetBarcodeHeight(Pointer handle, int nBarcodeHeight); +// 设置条码高度 +// +// handle +// 端口句柄,由OpenXXX返回 +// +// nBarcodeHeight +// 定义条码高度。取值范围:[1,255] +// +// return +// 返回值仅指示指令是否写入成功。返回true表示写入成功,返回false表示写入失败。 +``` + +--- + +#### CP_Pos_SetBarcodeReadableTextFontType + +设置条码可读字符字体类型 + +**定义:** + +```java +public boolean CP_Pos_SetBarcodeReadableTextFontType(Pointer handle, int nFontType); +// 设置条码可读字符字体类型 +// +// handle +// 端口句柄,由OpenXXX返回 +// +// nFontType +// 指定可读字符的字体类型,各值定义如下: +// 值 类型 +// 0 标准ASCII +// 1 压缩ASCII +// +// return +// 返回值仅指示指令是否写入成功。返回true表示写入成功,返回false表示写入失败。 +``` + +--- + +#### CP_Pos_SetBarcodeReadableTextPosition + +设置条码可读字符打印位置 + +**定义:** + +```java +public boolean CP_Pos_SetBarcodeReadableTextPosition(Pointer handle, int nTextPosition); +// 设置条码可读字符打印位置 +// +// handle +// 端口句柄,由OpenXXX返回 +// +// nTextPosition +// 条码可读字符位置,取值范围:[0, 3]. +// 各值定义如下: +// 值 定义 +// 0 不显示可读字符 +// 1 在条码下方显示可读字符 +// 2 在条码上方显示可读字符 +// 3 在条码上方和条码下方显示可读字符 +// +// return +// 返回值仅指示指令是否写入成功。返回true表示写入成功,返回false表示写入失败。 +``` + +--- + +### 页模式函数 + +CP_Page_SelectPageMode + +选择页模式 + +定义 + +public boolean CP_Page_SelectPageMode(Pointer handle); + +// 选择页模式 + +// + +// handle + +// 端口句柄,由OpenXXX返回 + +// + +// return + +// 返回值仅指示指令是否写入成功。返回true表示写入成功,返回false表示写入失败。 + +#### CP_Page_SelectPageModeEx + +选择页模式,并设置移动单位和页面大小,还会设置其他一系列参数为默认值 + +**定义:** + +```java +public boolean CP_Page_SelectPageModeEx(Pointer handle, int nHorizontalMovementUnit, int +``` + +nVerticalMovementUnit, int x, int y, int width, int height); + +```java +// 选择页模式,并设置移动单位和页面大小,还会设置其他一系列参数为默认值 +// +// handle +// 端口句柄,由OpenXXX返回 +// +// nHorizontalMovementUnit +// 水平移动单位 +// +// nVerticalMovementUnit +// 垂直移动单位 +// +// x +// 横向起始位置 +// +// y +// 纵向起始位置 +// +// width +// 打印区域宽度 +// +// height +// 打印区域高度 +// +// return +// 返回值仅指示指令是否写入成功。返回true表示写入成功,返回false表示写入失败。 +``` + +--- + +#### CP_Page_ExitPageMode + +退出页模式并回到标准模式 + +**定义:** + +```java +public boolean CP_Page_ExitPageMode(Pointer handle); +// 退出页模式并回到标准模式 +// +// handle +// 端口句柄,由OpenXXX返回 +// +// return +// 返回值仅指示指令是否写入成功。返回true表示写入成功,返回false表示写入失败。 +``` + +--- + +#### CP_Page_PrintPage + +页模式下打印内容 + +**定义:** + +```java +public boolean CP_Page_PrintPage(Pointer handle); +// 页模式下打印内容 +// +// handle +// 端口句柄,由OpenXXX返回 +// +// return +// 返回值仅指示指令是否写入成功。返回true表示写入成功,返回false表示写入失败。 +``` + +--- + +#### CP_Page_ClearPage + +页模式下清除页面 + +**定义:** + +```java +public boolean CP_Page_ClearPage(Pointer handle); +// 页模式下清除页面 +// +// handle +// 端口句柄,由OpenXXX返回 +// +// return +// 返回值仅指示指令是否写入成功。返回true表示写入成功,返回false表示写入失败。 +``` + +--- + +#### CP_Page_SetPageArea + +页模式下设置页区域,页面最高2000点(1mm8点) + +**定义:** + +```java +public boolean CP_Page_SetPageArea(Pointer handle, int x, int y, int width, int height); +// 页模式下设置页区域,页面最高2000点(1mm8点) +// +// handle +// 端口句柄,由OpenXXX返回 +// +// x +// 横向起始位置 +// +// y +// 纵向起始位置 +// +// width +// 打印区域宽度 +// +// height +// 打印区域高度 +// +// return +// 返回值仅指示指令是否写入成功。返回true表示写入成功,返回false表示写入失败。 +``` + +--- + +#### CP_Page_SetPageDrawDirection + +页模式下设置打印方向 + +**定义:** + +```java +public boolean CP_Page_SetPageDrawDirection(Pointer handle, int nDirection); +// 页模式下设置打印方向 +// +// handle +// 端口句柄,由OpenXXX返回 +// +// nDirection +// 打印区域方向,各值定义如下: +// 0 从左到右 +// 1 从下到上 +// 2 从右到左 +// 3 从上到下 +// +// return +// 返回值仅指示指令是否写入成功。返回true表示写入成功,返回false表示写入失败。 +``` + +--- + +#### CP_Page_DrawRect + +页模式下画矩形 + +**定义:** + +```java +public boolean CP_Page_DrawRect(Pointer handle, int x, int y, int width, int height, int color); +// 页模式下画矩形 +// +// handle +// 端口句柄,由OpenXXX返回 +// +// x +// 横向坐标 +// +// y +// 纵向坐标 +// +// width +// 矩形宽度 +// +// height +// 矩形高度 +// +// color +// 矩形颜色,0是白色,1是黑色 +// +// return +// 返回值仅指示指令是否写入成功。返回true表示写入成功,返回false表示写入失败。 +``` + +--- + +#### CP_Page_DrawBox + +页模式下画矩形框 + +**定义:** + +```java +public boolean CP_Page_DrawBox(Pointer handle, int x, int y, int width, int height, int borderwidth, int bordercolor); +// 页模式下画矩形框 +// +// handle +// 端口句柄,由OpenXXX返回 +// +// x +// 横向坐标 +// +// y +// 纵向坐标 +// +// width +// 矩形框宽度 +// +// height +// 矩形框高度 +// +// borderwidth +// 矩形框边框宽度 +// +// bordercolor +// 矩形框边框颜色,0是白色,1是黑色 +// +// return +// 返回值仅指示指令是否写入成功。返回true表示写入成功,返回false表示写入失败。 +``` + +--- + +#### CP_Page_DrawText + +页模式下画文本 + +**定义:** + +```java +public boolean CP_Page_DrawText(Pointer handle, int x, int y, String str); +// 页模式下画文本 +// +// handle +// 端口句柄,由OpenXXX返回 +// +// x +// 横向坐标 +// +// y +// 纵向坐标 +// +// str +// 要打印的字符串 +// +// return +// 返回值仅指示指令是否写入成功。返回true表示写入成功,返回false表示写入失败。 +``` + +--- + +#### CP_Page_DrawTextInUTF8 + +页模式下画文本 + +**定义:** + +```java +public boolean CP_Page_DrawTextInUTF8(Pointer handle, int x, int y, WString str); +// 页模式下画文本 +// +// handle +// 端口句柄,由OpenXXX返回 +// +// x +// 横向坐标 +// +// y +// 纵向坐标 +// +// str +// 要打印的字符串 +// +// return +// 返回值仅指示指令是否写入成功。返回true表示写入成功,返回false表示写入失败。 +// +// remarks +// 该函数会将数据转为UTF8编码发送。 +``` + +--- + +#### CP_Page_DrawTextInGBK + +页模式下画文本 + +**定义:** + +```java +public boolean CP_Page_DrawTextInGBK(Pointer handle, int x, int y, WString str); +// 页模式下画文本 +// +// handle +// 端口句柄,由OpenXXX返回 +// +// x +// 横向坐标 +// +// y +// 纵向坐标 +// +// str +// 要打印的字符串 +// +// return +// 返回值仅指示指令是否写入成功。返回true表示写入成功,返回false表示写入失败。 +// +// remarks +// 该函数会将数据转为GBK编码发送。 +``` + +--- + +#### CP_Page_DrawTextInBIG5 + +页模式下画文本 + +**定义:** + +```java +public boolean CP_Page_DrawTextInBIG5(Pointer handle, int x, int y, WString str); +// 页模式下画文本 +// +// handle +// 端口句柄,由OpenXXX返回 +// +// x +// 横向坐标 +// +// y +// 纵向坐标 +// +// str +// 要打印的字符串 +// +// return +// 返回值仅指示指令是否写入成功。返回true表示写入成功,返回false表示写入失败。 +// +// remarks +// 该函数会将数据转为BIG5编码发送。 +``` + +--- + +#### CP_Page_DrawTextInShiftJIS + +页模式下画文本 + +**定义:** + +```java +public boolean CP_Page_DrawTextInShiftJIS(Pointer handle, int x, int y, WString str); +// 页模式下画文本 +// +// handle +// 端口句柄,由OpenXXX返回 +// +// x +// 横向坐标 +// +// y +// 纵向坐标 +// +// str +// 要打印的字符串 +// +// return +// 返回值仅指示指令是否写入成功。返回true表示写入成功,返回false表示写入失败。 +// +// remarks +// 该函数会将数据转为ShiftJIS编码发送。 +``` + +--- + +#### CP_Page_DrawTextInEUCKR + +页模式下画文本 + +**定义:** + +```java +public boolean CP_Page_DrawTextInEUCKR(Pointer handle, int x, int y, WString str); +// 页模式下画文本 +// +// handle +// 端口句柄,由OpenXXX返回 +// +// x +// 横向坐标 +// +// y +// 纵向坐标 +// +// str +// 要打印的字符串 +// +// return +// 返回值仅指示指令是否写入成功。返回true表示写入成功,返回false表示写入失败。 +// +// remarks +// 该函数会将数据转为EUCKR编码发送。 +``` + +--- + +#### CP_Page_DrawBarcode + +页模式下打印一维条码 + +**定义:** + +```java +public boolean CP_Page_DrawBarcode(Pointer handle, int x, int y, int nBarcodeType, String str); +// 页模式下打印一维条码 +// +// handle +// 端口句柄,由OpenXXX返回 +// +// x +// 横向坐标 +// +// y +// 纵向坐标 +// +// nBarcodeType +// 标识条码类型 +// 各值定义如下: +// 值 类型 +// 0x41 UPC-A +// 0x42 UPC-E +// 0x43 EAN13 +// 0x44 EAN8 +// 0x45 CODE39 +// 0x46 ITF +// 0x47 CODABAR +// 0x48 CODE93 +// 0x49 CODE128 +// +// str +// 要打印的条码 +// +// return +// 返回值仅指示指令是否写入成功。返回true表示写入成功,返回false表示写入失败。 +``` + +--- + +#### CP_Page_DrawQRCode + +页模式下打印QR码 + +**定义:** + +```java +public boolean CP_Page_DrawQRCode(Pointer handle, int x, int y, int nVersion, int nECCLevel, String str); +// 页模式下打印QR码 +// +// handle +// 端口句柄,由OpenXXX返回 +// +// x +// 横向坐标 +// +// y +// 纵向坐标 +// +// nVersion +// 指定字符版本。取值范围:[0,16]。 +// 当 version 为 0 时,打印机根据字符串长度自动计算版本号。 +// +// nECCLevel +// 指定纠错等级。取值范围:[1, 4]。 +// 各值定义如下: +// ECC 纠错等级 +// 1 L:7%,低纠错,数据多。 +// 2 M:15%,中纠错 +// 3 Q:优化纠错 +// 4 H:30%,最高纠错,数据少。 +// +// str +// 要打印的QR码 +// +// return +// 返回值仅指示指令是否写入成功。返回true表示写入成功,返回false表示写入失败。 +``` + +--- + +#### CP_Page_DrawRasterImageFromFile + +页模式下打印图片 + +**定义:** + +```java +public boolean CP_Page_DrawRasterImageFromFile(Pointer handle, int x, int y, int dstw, int dsth, String pszFile, int +``` + +binaryzation_method); + +```java +// 页模式下打印图片 +// +// handle +// 端口句柄,由OpenXXX返回 +// +// x +// 横向坐标 +// +// y +// 纵向坐标 +// +// dstw +// 要打印的宽度 +// +// dsth +// 要打印的高度 +// +// pszFile +// 图片的路径 +// +// binaryzation_method +// 图片二值化算法。0表示抖动算法,1表示阀值算法,2表示误差扩散法。具体效果请测试查看。 +// +// return +// 返回值仅指示指令是否写入成功。返回true表示写入成功,返回false表示写入失败。 +``` + +--- + +#### CP_Page_DrawRasterImageFromData + +页模式下打印图片(图片可由文件读取) + +**定义:** + +```java +public boolean CP_Page_DrawRasterImageFromData(Pointer handle, int x, int y, int dstw, int dsth, byte[] data, int +``` + +data_size, int binaryzation_method); + +```java +// 页模式下打印图片(图片可由文件读取) +// +// handle +// 端口句柄,由OpenXXX返回 +// +// x +// 横向坐标 +// +// y +// 纵向坐标 +// +// dstw +// 要打印的宽度 +// +// dsth +// 要打印的高度 +// +// data +// 图片数据。 +// +// data_size +// 图片数据长度 +// +// binaryzation_method +// 图片二值化算法。0表示抖动算法,1表示阀值算法,2表示误差扩散法,2表示误差扩散法。具体效果请 +``` + +测试查看。 + +```java +// +// return +// 返回值仅指示指令是否写入成功。返回true表示写入成功,返回false表示写入失败。 +``` + +也可以使用下面的函数 + +```java +public class CP_Page_DrawRasterImageFromData_Helper { +public static boolean DrawRasterImageFromBitmap(Pointer handle, int x, int y, int dstw, int dsth, Bitmap bitmap, +``` + +int binaryzation_method) + +```java +} +``` + +--- + +#### CP_Page_DrawRasterImageFromPixels + +页模式下打印图片像素数据 + +**定义:** + +```java +public boolean CP_Page_DrawRasterImageFromPixels(Pointer handle, int x, int y, byte[] img_data, int img_datalen, int +``` + +img_width, int img_height, int img_stride, int img_format, int binaryzation_method); + +```java +// 页模式下打印图片像素数据 +// +// handle +// 端口句柄,由OpenXXX返回 +// +// x +// 横向坐标 +// +// y +// 纵向坐标 +// +// img_data +// 图片的像素数据。 +// +// img_datalen +// 图片的像素数据字节数。 +// +// img_width +// 图片的像素宽度。 +// +// img_height +// 图片的像素高度。 +// +// img_stride +// 图片水平跨度。表示每行字节数。 +// +// img_format +// 图片像素数据格式,各值定义如下 +// 值 定义 +// 1 mono +// 2 monolsb +// 3 gray +// 4 r.g.b in byte-ordered +// 5 b.g.r in byte-ordered +// 6 a.r.g.b in byte-ordered +// 7 r.g.b.a in byte-ordered +// 8 a.b.g.r in byte-ordered +``` + +--- + +### 黑标函数 + +CP_BlackMark_EnableBlackMarkMode + +启用黑标模式,重启打印机生效 + +定义 + +public boolean CP_BlackMark_EnableBlackMarkMode(Pointer handle); + +// 启用黑标模式,重启打印机生效 + +// + +// handle + +// 端口句柄,由OpenXXX返回 + +// + +// return + +// 返回值仅指示指令是否写入成功。返回true表示写入成功,返回false表示写入失败。 + +#### CP_BlackMark_DisableBlackMarkMode + +禁用黑标模式 + +**定义:** + +```java +public boolean CP_BlackMark_DisableBlackMarkMode(Pointer handle); +// 禁用黑标模式 +// +// handle +// 端口句柄,由OpenXXX返回 +// +// return +// 返回值仅指示指令是否写入成功。返回true表示写入成功,返回false表示写入失败。 +``` + +--- + +#### CP_BlackMark_SetBlackMarkMaxFindLength + +设置黑标最大查找距离(重启仍有效) + +**定义:** + +```java +public boolean CP_BlackMark_SetBlackMarkMaxFindLength(Pointer handle, int maxFindLength); +// 设置黑标最大查找距离(重启仍有效) +// +// handle +// 端口句柄,由OpenXXX返回 +// +// maxFindLength +// 最大查找距离(maxFindLength x 0.125 毫米) +// +// return +// 返回值仅指示指令是否写入成功。返回true表示写入成功,返回false表示写入失败。 +``` + +--- + +#### CP_BlackMark_FindNextBlackMark + +查找下一个黑标 + +**定义:** + +```java +public boolean CP_BlackMark_FindNextBlackMark(Pointer handle); +// 查找下一个黑标 +// +// handle +// 端口句柄,由OpenXXX返回 +// +// return +// 返回值仅指示指令是否写入成功。返回true表示写入成功,返回false表示写入失败。 +``` + +--- + +#### CP_BlackMark_SetBlackMarkPaperPrintPosition + +黑标模式下,设置起始打印位置的调整值 + +**定义:** + +```java +public boolean CP_BlackMark_SetBlackMarkPaperPrintPosition(Pointer handle, int position); +// 黑标模式下,设置起始打印位置的调整值 +// +// handle +// 端口句柄,由OpenXXX返回 +// +// position +// 大于0则指定为进纸,小于0则指定为退纸。距离为 position x 0.125 毫米。 +// +// return +// 返回值仅指示指令是否写入成功。返回true表示写入成功,返回false表示写入失败。 +``` + +--- + +#### CP_BlackMark_SetBlackMarkPaperCutPosition + +黑标模式下,设置切纸位置 + +**定义:** + +```java +public boolean CP_BlackMark_SetBlackMarkPaperCutPosition(Pointer handle, int position); +// 黑标模式下,设置切纸位置 +// +// handle +// 端口句柄,由OpenXXX返回 +// +// position +// 大于0则指定为进纸,小于0则指定为退纸。距离为 position x 0.125 毫米。 +// +// return +// 返回值仅指示指令是否写入成功。返回true表示写入成功,返回false表示写入失败。 +``` + +--- + +#### CP_BlackMark_FullCutBlackMarkPaper + +切刀全切 + +**定义:** + +```java +public boolean CP_BlackMark_FullCutBlackMarkPaper(Pointer handle); +// 切刀全切 +// +// handle +// 端口句柄,由OpenXXX返回 +// +// return +// 返回值仅指示指令是否写入成功。返回true表示写入成功,返回false表示写入失败。 +``` + +--- + +#### CP_BlackMark_HalfCutBlackMarkPaper + +切刀半切 + +**定义:** + +```java +public boolean CP_BlackMark_HalfCutBlackMarkPaper(Pointer handle); +// 切刀半切 +// +// handle +// 端口句柄,由OpenXXX返回 +// +// return +// 返回值仅指示指令是否写入成功。返回true表示写入成功,返回false表示写入失败。 +``` + +--- + +### 标签函数 + +CP_Label_EnableLabelMode + +启用标签模式 + +定义 + +public boolean CP_Label_EnableLabelMode(Pointer handle); + +// 启用标签模式 + +// + +// handle + +// 端口句柄,由OpenXXX返回 + +// + +// return + +// 返回值仅指示指令是否写入成功。返回true表示写入成功,返回false表示写入失败。 + +#### CP_Label_DisableLabelMode + +关闭标签模式 + +**定义:** + +```java +public boolean CP_Label_DisableLabelMode(Pointer handle); +// 关闭标签模式 +// +// handle +// 端口句柄,由OpenXXX返回 +// +// return +// 返回值仅指示指令是否写入成功。返回true表示写入成功,返回false表示写入失败。 +``` + +--- + +#### CP_Label_CalibrateLabel + +校准标签纸(更换不同规格标签纸,需要校准) + +**定义:** + +```java +public boolean CP_Label_CalibrateLabel(Pointer handle); +// 校准标签纸(更换不同规格标签纸,需要校准) +// +// handle +// 端口句柄,由OpenXXX返回 +// +// return +// 返回值仅指示指令是否写入成功。返回true表示写入成功,返回false表示写入失败。 +``` + +--- + +#### CP_Label_FeedLabel + +走纸到标签缝隙处 + +**定义:** + +```java +public boolean CP_Label_FeedLabel(Pointer handle); +// 走纸到标签缝隙处 +// +// handle +// 端口句柄,由OpenXXX返回 +// +// return +// 返回值仅指示指令是否写入成功。返回true表示写入成功,返回false表示写入失败。 +``` + +--- + +#### CP_Label_BackPaperToPrintPosition + +打印机退纸到打印位置(适用于标签打印开头定位) + +**定义:** + +```java +public boolean CP_Label_BackPaperToPrintPosition(Pointer handle); +// 打印机退纸到打印位置(适用于标签打印开头定位) +// +// handle +// 端口句柄,由OpenXXX返回 +// +// return +// 返回值仅指示指令是否写入成功。返回true表示写入成功,返回false表示写入失败。 +``` + +--- + +#### CP_Label_FeedPaperToTearPosition + +打印机进纸到撕纸位置(适用于标签打印结束定位) + +**定义:** + +```java +public boolean CP_Label_FeedPaperToTearPosition(Pointer handle); +// 打印机进纸到撕纸位置(适用于标签打印结束定位) +// +// handle +// 端口句柄,由OpenXXX返回 +// +// return +// 返回值仅指示指令是否写入成功。返回true表示写入成功,返回false表示写入失败。 +``` + +--- + +#### CP_Label_PageBegin + +指示一个标签页面的开始,并设置标签页的大小,参考点坐标和页面旋转角度 + +**定义:** + +```java +public boolean CP_Label_PageBegin(Pointer handle, int x, int y, int width, int height, int rotation); +// 指示一个标签页面的开始,并设置标签页的大小,参考点坐标和页面旋转角度 +// +// handle +// 端口句柄,由OpenXXX返回 +// +// x +// 页面起始点 x 坐标 +// +// y +// 页面起始点 y 坐标 +// +// width +// 页面页宽 +// +// height +// 页面页高 +// +// rotation +// 页面旋转。 rotate 的取值范围为{0,1}。为 0,页面不旋转打印,为 1,页面旋转 90 度打印。 +// +// return +// 返回值仅指示指令是否写入成功。返回true表示写入成功,返回false表示写入失败。 +``` + +--- + +#### CP_Label_PagePrint + +将标签页上的内容打印到标签纸上 + +**定义:** + +```java +public boolean CP_Label_PagePrint(Pointer handle, int copies); +// 将标签页上的内容打印到标签纸上 +// +// handle +// 端口句柄,由OpenXXX返回 +// +// copies +// 份数 [ 1 - 255 ] +// +// return +// 返回值仅指示指令是否写入成功。返回true表示写入成功,返回false表示写入失败。 +``` + +--- + +#### CP_Label_DrawText + +在标签页面上指定位置绘制文本。只能单行打印。 + +**定义:** + +```java +public boolean CP_Label_DrawText(Pointer handle, int x, int y, int font, int style, String str); +// 在标签页面上指定位置绘制文本。只能单行打印。 +// +// handle +// 端口句柄,由OpenXXX返回 +// +// x +// 定义文本起始位置 x 坐标,取值范围:[0, Page_Width-1] +// +// y +// 定义文本起始位置 y 坐标,取值范围:[0, Page_Height-1] +// +// font +// 选择字体,可以使用24。 +// 带矢量字机型支持16,[20,99]。 +// +// style +// 字符风格。 +// 数据位 定义 +// 0 加粗标志位: 置 1 字体加粗,清零则字体不加粗。 +// 1 下划线标志位: 置 1 文本带下划线,清零则无下划线。 +// 2 反白标志位: 置 1 文本反白(黑底白字),清零不反白。 +// 3 删除线标志位: 置 1 文本带删除线,清零则无删除线。 +// [5,4] 旋转标志位: 00 旋转 0° ; +// 01 旋转 90°; +// 10 旋转 180°; +// 11 旋转 270°; +// [11,8] 字体宽度放大倍数; +// [15,12] 字体高度放大倍数; +// +// str +// 要打印的字符串 +// +// return +// 返回值仅指示指令是否写入成功。返回true表示写入成功,返回false表示写入失败。 +``` + +--- + +#### CP_Label_DrawTextInUTF8 + +在标签页面上指定位置绘制文本。只能单行打印。 + +**定义:** + +```java +public boolean CP_Label_DrawTextInUTF8(Pointer handle, int x, int y, int font, int style, WString str); +// 在标签页面上指定位置绘制文本。只能单行打印。 +// +// handle +// 端口句柄,由OpenXXX返回 +// +// x +// 定义文本起始位置 x 坐标,取值范围:[0, Page_Width-1] +// +// y +// 定义文本起始位置 y 坐标,取值范围:[0, Page_Height-1] +// +// font +// 选择字体,可以使用24。 +// 带矢量字机型支持16,[20,99]。 +// +// style +// 字符风格。 +// 数据位 定义 +// 0 加粗标志位: 置 1 字体加粗,清零则字体不加粗。 +// 1 下划线标志位: 置 1 文本带下划线,清零则无下划线。 +// 2 反白标志位: 置 1 文本反白(黑底白字),清零不反白。 +// 3 删除线标志位: 置 1 文本带删除线,清零则无删除线。 +// [5,4] 旋转标志位: 00 旋转 0° ; +// 01 旋转 90°; +// 10 旋转 180°; +// 11 旋转 270°; +// [11,8] 字体宽度放大倍数; +// [15,12] 字体高度放大倍数; +// +// str +// 要打印的字符串 +// +// return +// 返回值仅指示指令是否写入成功。返回true表示写入成功,返回false表示写入失败。 +// +// remarks +// 该函数会将数据转为UTF8编码发送。 +``` + +--- + +#### CP_Label_DrawTextInGBK + +在标签页面上指定位置绘制文本。只能单行打印。 + +**定义:** + +```java +public boolean CP_Label_DrawTextInGBK(Pointer handle, int x, int y, int font, int style, WString str); +// 在标签页面上指定位置绘制文本。只能单行打印。 +// +// handle +// 端口句柄,由OpenXXX返回 +// +// x +// 定义文本起始位置 x 坐标,取值范围:[0, Page_Width-1] +// +// y +// 定义文本起始位置 y 坐标,取值范围:[0, Page_Height-1] +// +// font +// 选择字体,可以使用24。 +// 带矢量字机型支持16,[20,99]。 +// +// style +// 字符风格。 +// 数据位 定义 +// 0 加粗标志位: 置 1 字体加粗,清零则字体不加粗。 +// 1 下划线标志位: 置 1 文本带下划线,清零则无下划线。 +// 2 反白标志位: 置 1 文本反白(黑底白字),清零不反白。 +// 3 删除线标志位: 置 1 文本带删除线,清零则无删除线。 +// [5,4] 旋转标志位: 00 旋转 0° ; +// 01 旋转 90°; +// 10 旋转 180°; +// 11 旋转 270°; +// [11,8] 字体宽度放大倍数; +// [15,12] 字体高度放大倍数; +// +// str +// 要打印的字符串 +// +// return +// 返回值仅指示指令是否写入成功。返回true表示写入成功,返回false表示写入失败。 +// +// remarks +// 该函数会将数据转为GBK编码发送。 +``` + +--- + +#### CP_Label_DrawBarcode + +在标签页指定位置绘制一维条码。 + +**定义:** + +```java +public boolean CP_Label_DrawBarcode(Pointer handle, int x, int y, int nBarcodeType, int nBarcodeTextPrintPosition, +``` + +int height, int unitwidth, int rotation, String str); + +```java +// 在标签页指定位置绘制一维条码。 +// +// handle +// 端口句柄,由OpenXXX返回 +// +// x +// 条码左上角 x 坐标值,取值范围:[0, Page_Width-1]。 +// +// y +// 条码左上角 y 坐标值,取值范围:[0, Page_Height-1]。 +// +// nBarcodeType +// 标识条码类型 +// 各值定义看宏定义 +// +// nBarcodeTextPrintPosition +// 条码可读字符位置,取值范围:[0, 3]. +// 各值定义如下: +// 值 定义 +// 0 不显示可读字符 +// 1 在条码下方显示可读字符 +// 2 在条码上方显示可读字符 +// 3 在条码上方和条码下方显示可读字符 +// +// height +// 定义条码高度。 +// +// unitwidth +// 定义码块单元宽度。取值范围:[1, 4]。 +// +// rotation +// 表示旋转角度。取值范围:[0, 3]。各值定义如下: +// 值 定义 +// 0 不旋转绘制。 +// 1 旋转 90°绘制。 +// 2 旋转 180°绘制。 +// 3 旋转 270°绘制。 +``` + +--- + +#### CP_Label_DrawQRCode + +在标签页指定位置绘制QR码。 + +**定义:** + +```java +public boolean CP_Label_DrawQRCode(Pointer handle, int x, int y, int nVersion, int nECCLevel, int unitwidth, int +``` + +rotation, String str); + +```java +// 在标签页指定位置绘制QR码。 +// +// handle +// 端口句柄,由OpenXXX返回 +// +// x +// 左上角 x 坐标值,取值范围:[0,Page_Width-1]。 +// +// y +// 左上角 y 坐标值,取值范围:[0, Page_Height-1]。 +// +// nVersion +// 指定字符版本。取值范围:[0,16]。 +// 当 version 为 0 时,打印机根据字符串长度自动计算版本号。 +// +// nECCLevel +// 指定纠错等级。取值范围:[1, 4]。 +// 各值定义如下: +// ECC 纠错等级 +// 1 L:7%,低纠错,数据多。 +// 2 M:15%,中纠错 +// 3 Q:优化纠错 +// 4 H:30%,最高纠错,数据少。 +// +// unitwidth +// 定义码块单元宽度。取值范围:[1, 4]。 +// +// rotation +// 表示旋转角度。取值范围:[0, 3]。各值定义如下: +// 值 定义 +// 0 不旋转绘制。 +// 1 旋转 90°绘制。 +// 2 旋转 180°绘制。 +// 3 旋转 270°绘制。 +// +// str +// 要打印的QR码 +``` + +--- + +#### CP_Label_DrawPDF417Code + +在标签页指定位置绘制 PDF417 条码 + +**定义:** + +```java +public boolean CP_Label_DrawPDF417Code(Pointer handle, int x, int y, int column, int nAspectRatio, int nECCLevel, +``` + +int unitwidth, int rotation, String str); + +```java +// 在标签页指定位置绘制 PDF417 条码 +// +// handle +// 端口句柄,由OpenXXX返回 +// +// x +// 左上角 x 坐标值,取值范围:[0, Page_Width-1]。 +// +// y +// 左上角 y 坐标值,取值范围:[0, Page_Height-1]。 +// +// column +// ColNum 为列数,表述每行容纳多少码字。一个码字为 17*UnitWidth 个点。行数由打印机自动产生,行数 +``` + +范围限定为 3~90。ColNum 的取值范围:[1,30]。 + +```java +// +// nECCLevel +// 指定纠错等级。取值范围:[0, 8]。 +// 纠错等级取值 纠错码数 可存资料量(字节) +// 0 2 1108 +// 1 4 1106 +// 2 8 1101 +// 3 16 1092 +// 4 32 1072 +// 5 64 1024 +// 6 128 957 +// 7 256 804 +// 8 512 496 +// +// unitwidth +// 定义码块单元宽度。取值范围:[1, 3]。 +// +// rotation +// 表示旋转角度。取值范围:[0, 3]。各值定义如下: +// 值 定义 +// 0 不旋转绘制。 +// 1 旋转 90°绘制。 +// 2 旋转 180°绘制。 +``` + +--- + +#### CP_Label_DrawImageFromFile + +在标签页指定位置绘制位图 + +**定义:** + +```java +public boolean CP_Label_DrawImageFromFile(Pointer handle, int x, int y, int dstw, int dsth, String pszFile, int +``` + +binaryzation_method, int compression_method); + +```java +// 在标签页指定位置绘制位图 +// +// handle +// 端口句柄,由OpenXXX返回 +// +// x +// 左上角 x 坐标值,取值范围:[0, Page_Width-1]。 +// +// y +// 左上角 y 坐标值,取值范围:[0, Page_Height-1]。 +// +// dstw +// 要打印的宽度 +// +// dsth +// 要打印的高度 +// +// pszFile +// 图片的路径 +// +// binaryzation_method +// 图片二值化算法。0表示抖动算法,1表示阀值算法,2表示误差扩散法。具体效果请测试查看。 +// +// compression_method +// 最终打印数据的压缩方式,各值定义如下 +// 值 定义 +// 0 不压缩 +// 1 一级压缩 +// 2 二级压缩 +// +// return +// 返回值仅指示指令是否写入成功。返回true表示写入成功,返回false表示写入失败。 +``` + +--- + +#### CP_Label_DrawImageFromData + +在标签页指定位置绘制位图 + +**定义:** + +```java +public boolean CP_Label_DrawImageFromData(Pointer handle, int x, int y, int dstw, int dsth, byte[] data, int data_size, +``` + +int binaryzation_method, int compression_method); + +```java +// 在标签页指定位置绘制位图 +// +// handle +// 端口句柄,由OpenXXX返回 +// +// x +// 左上角 x 坐标值,取值范围:[0, Page_Width-1]。 +// +// y +// 左上角 y 坐标值,取值范围:[0, Page_Height-1]。 +// +// dstw +// 要打印的宽度 +// +// dsth +// 要打印的高度 +// +// data +// 图片数据。 +// +// data_size +// 图片数据长度 +// +// binaryzation_method +// 图片二值化算法。0表示抖动算法,1表示阀值算法,2表示误差扩散法。具体效果请测试查看。 +// +// compression_method +// 最终打印数据的压缩方式,各值定义如下 +// 值 定义 +// 0 不压缩 +// 1 一级压缩 +// 2 二级压缩 +// +// return +// 返回值仅指示指令是否写入成功。返回true表示写入成功,返回false表示写入失败。 +``` + +也可以使用下面的函数 + +--- + +#### CP_Label_DrawImageFromPixels + +在标签页指定位置绘制位图 + +**定义:** + +```java +public boolean CP_Label_DrawImageFromPixels(Pointer handle, int x, int y, byte[] img_data, int img_datalen, int +``` + +img_width, int img_height, int img_stride, int img_format, int binaryzation_method, int compression_method); + +```java +// 在标签页指定位置绘制位图 +// +// handle +// 端口句柄,由OpenXXX返回 +// +// x +// 左上角 x 坐标值,取值范围:[0, Page_Width-1]。 +// +// y +// 左上角 y 坐标值,取值范围:[0, Page_Height-1]。 +// +// img_data +// 图片的像素数据。 +// +// img_datalen +// 图片的像素数据字节数。 +// +// img_width +// 图片的像素宽度。 +// +// img_height +// 图片的像素高度。 +// +// img_stride +// 图片水平跨度。表示每行字节数。 +// +// img_format +// 图片像素数据格式,各值定义如下 +// 值 定义 +// 1 mono +// 2 monolsb +// 3 gray +// 4 r.g.b in byte-ordered +// 5 b.g.r in byte-ordered +// 6 a.r.g.b in byte-ordered +// 7 r.g.b.a in byte-ordered +// 8 a.b.g.r in byte-ordered +``` + +--- + +#### CP_Label_DrawLine + +在标签页指定位置绘制线段 + +**定义:** + +```java +public boolean CP_Label_DrawLine(Pointer handle, int startx, int starty, int endx, int endy, int linewidth, int linecolor); +// 在标签页指定位置绘制线段 +// +// handle +// 端口句柄,由OpenXXX返回 +// +// startx +// 直线段起始点 x 坐标值,取值范围:[0, Page_Width-1]。 +// +// starty +// 直线段起始点 y 坐标值,取值范围:[0,Page_Height-1]。 +// +// endx +// 直线段终止点 x 坐标值,取值范围:[0, Page_Width-1]。 +// +// endy +// 直线段终止点 y 坐标值,取值范围:[0,Page_Height-1]。 +// +// linewidth +// 直线段线宽,取值范围:[1,Page_Height-1]。 +// +// linecolor +// 直线段颜色线条颜色,0是白色,1是黑色 +// +// return +// 返回值仅指示指令是否写入成功。返回true表示写入成功,返回false表示写入失败。 +``` + +--- + +#### CP_Label_DrawRect + +在标签页指定位置绘制矩形 + +**定义:** + +```java +public boolean CP_Label_DrawRect(Pointer handle, int x, int y, int width, int height, int color); +// 在标签页指定位置绘制矩形 +// +// handle +// 端口句柄,由OpenXXX返回 +// +// x +// 横向坐标 +// +// y +// 纵向坐标 +// +// width +// 矩形宽度 +// +// height +// 矩形高度 +// +// color +// 矩形颜色,0是白色,1是黑色 +// +// return +// 返回值仅指示指令是否写入成功。返回true表示写入成功,返回false表示写入失败。 +``` + +--- + +#### CP_Label_DrawBox + +在标签页指定位置绘制矩形框 + +**定义:** + +```java +public boolean CP_Label_DrawBox(Pointer handle, int x, int y, int width, int height, int borderwidth, int bordercolor); +// 在标签页指定位置绘制矩形框 +// +// handle +// 端口句柄,由OpenXXX返回 +// +// x +// 横向坐标 +// +// y +// 纵向坐标 +// +// width +// 矩形框宽度 +// +// height +// 矩形框高度 +// +// borderwidth +// 矩形框边框宽度 +// +// bordercolor +// 矩形框边框颜色,0是白色,1是黑色 +// +// return +// 返回值仅指示指令是否写入成功。返回true表示写入成功,返回false表示写入失败。 +``` + +--- + +### 其他函数 + +CP_Library_Version + +获取开发包版本字符串 + +定义 + +public String CP_Library_Version(); + +// 获取开发包版本字符串 + +// + +// return + +// 返回开发包版本 + +#### CP_Proto_QueryBatteryLevel + +查询电池电量 + +**定义:** + +```java +public int CP_Proto_QueryBatteryLevel(Pointer handle, int timeout); +// 查询电池电量 +// 仅部分带电池的机型支持该指令 +// +// handle +// 端口句柄,由OpenXXX返回 +// +// timeout +// 超时毫秒时间。 +// 查询等待时间不超过此时间。 +// +// return +// 返回电池电量,范围在0-100之间。返回-1表示查询失败。 +``` + +--- + +#### CP_Proto_QuerySerialNumber + +查询序列号 + +Syntax + +```java +public class CP_Proto_QuerySerialNumber_Helper { +public static String QuerySerialNumber(Pointer handle, int timeout); +} +// 查询序列号 +// 仅部分机型支持该命令 +// +// handle +// 端口句柄,由OpenXXX返回 +// +// timeout +// 超时毫秒时间。 +// 查询等待时间不超过此时间。 +// +// return +// 返回序列号 +``` + +--- + +#### CP_Proto_SetSystemNameAndSerialNumber + +设置系统名称和序列号 + +**定义:** + +```java +public boolean CP_Proto_SetSystemNameAndSerialNumber(Pointer handle, String systemName, String +``` + +serialNumber); + +```java +// 设置系统名称和序列号 +// +// handle +// 端口句柄,由OpenXXX返回 +// +// systemName +// 系统名称 +// +// serialNumber +// 序列号 +// +// return +// 返回值仅指示指令是否写入成功。返回true表示写入成功,返回false表示写入失败。 +``` + +--- + +#### CP_Proto_SetBluetoothNameAndPassword + +设置蓝牙名称和密码 + +**定义:** + +```java +public boolean CP_Proto_SetBluetoothNameAndPassword(Pointer handle, String bluetoothName, String +``` + +bluetoothPassword); + +```java +// 设置蓝牙名称和密码 +// +// handle +// 端口句柄,由OpenXXX返回 +// +// bluetoothName +// 蓝牙名称 +// +// bluetoothPassword +// 蓝牙密码 +// +// return +// 返回值仅指示指令是否写入成功。返回true表示写入成功,返回false表示写入失败。 +``` + +--- + +#### CP_Proto_SetPTPBasicParameters + +设置基础信息,包括语言、波特率、浓度等参数,对应设置工具PTP页。 + +**定义:** + +```java +public boolean CP_Proto_SetPTPBasicParameters(Pointer handle, int baudrate, int codepage, int density, int +``` + +asciiFontType, int lineFeed, int idleTime, int powerOffTime, int maxFeedLength, int pageLength); + +```java +// 设置基础信息,包括语言、波特率、浓度等参数,对应设置工具PTP页。 +// +// handle +// 端口句柄,由OpenXXX返回 +// +// baudrate +// 要设置的波特率 +// +// codepage +// 要设置的语言 +// 具体语言对应的数值如下: +// { ("简体中文"), 255 }, +// { ("繁體中文"), 254 }, +// { ("UTF - 8"), 253 }, +// { ("SHIFT - JIS"), 252 }, +// { ("EUC - KR"), 251 }, +// { ("CP437[U.S.A., Standard Europe]"), 0 }, +// { ("Katakana"), 1 }, +// { ("CP850[Multilingual]"), 2 }, +// { ("CP860[Portuguese]"), 3 }, +// { ("CP863[Canadian - French]"), 4 }, +// { ("CP865[Nordic]"), 5 }, +// { ("WCP1251[Cyrillic]"), 6 }, +// { ("CP866 Cyrilliec #2"), 7 }, +// { ("MIK[Cyrillic / Bulgarian]"), 8 }, +// { ("CP755[East Europe, Latvian 2]"), 9 }, +// { ("Iran"), 10 }, +// { ("CP862[Hebrew]"), 15 }, +// { ("WCP1252 Latin I"), 16 }, +// { ("WCP1253[Greek]"), 17 }, +// { ("CP852[Latina 2]"), 18 }, +// { ("CP858 Multilingual Latin I + Euro)"), 19 }, +// { ("Iran II"), 20 }, +// { ("Latvian"), 21 }, +// { ("CP864[Arabic]"), 22 }, +// { ("ISO - 8859 - 1[West Europe]"), 23 }, +// { ("CP737[Greek]"), 24 }, +``` + +--- + +#### CP_Settings_Hardware_SetPrintSpeed + +设置打印速度 + +**定义:** + +```java +public boolean CP_Settings_Hardware_SetPrintSpeed(Pointer handle, int nSpeed); +// 设置打印速度 +// +// handle +// 端口句柄,由OpenXXX返回 +// +// nSpeed +// 打印速度,单位毫米每秒 +// +// return +// 返回值仅指示指令是否写入成功。返回true表示写入成功,返回false表示写入失败。 +``` + +--- diff --git a/docs/autoreplyprint.aar b/docs/autoreplyprint.aar new file mode 100644 index 0000000..8f3e770 Binary files /dev/null and b/docs/autoreplyprint.aar differ diff --git a/docs/superpowers/specs/2026-04-22-printer-plugin-design.md b/docs/superpowers/specs/2026-04-22-printer-plugin-design.md new file mode 100644 index 0000000..e84ffc9 --- /dev/null +++ b/docs/superpowers/specs/2026-04-22-printer-plugin-design.md @@ -0,0 +1,157 @@ +# 打印机插件设计文档 + +**日期:** 2026-04-22 +**状态:** 已确认 +**目标:** 将 printer 插件开发为完整的打印插件,支持通过 CH34 串口 + autoreplyprint.aar 进行打印 + +## 1. 项目概述 + +### 用途 +公司内部多个 Flutter 项目共用的打印插件,用于打印拉曼检测结果(简单文字票据)。 + +### 技术架构 +- CH34 插件提供串口通道 +- 本项目 Android 原生层集成 autoreplyprint.aar +- Dart 层通过 MethodChannel 调用原生层 +- 底层指令式 API:调用方控制打开→打印→关闭全流程 + +## 2. 整体架构 + +``` +┌─────────────────────────────────────────────┐ +│ 调用方 Flutter 项目 │ +│ Printer.openComPort() → PrinterPortHandle │ +│ handle.printText() → handle.close() │ +└──────────────┬───────────────────────────────┘ + │ MethodChannel 调用 +┌──────────────▼───────────────────────────────┐ +│ Flutter 插件 Dart 层 │ +│ Printer → PrinterPlatform │ +│ → MethodChannelPrinterPort │ +│ → PrinterPortHandle │ +└──────────────┬───────────────────────────────┐ + │ MethodChannel + portHandle +┌──────────────▼───────────────────────────────┐ +│ Android 原生层 (Java) │ +│ PrinterPlugin (MethodCallHandler) │ +│ → PortHandleManager │ +│ → autoreplyprint.aar │ +└───────────────────────────────────────────────┘ +``` + +## 3. API 设计 + +### 使用示例 +```dart +// 打开端口 +PrinterPortHandle port = await Printer.openComPort( + portName: 'COM3', + baudRate: 9600, + dataBits: 8, + parity: Parity.none, + stopBits: StopBits.one, + flowControl: FlowControl.none, +); + +// 设置编码 +await port.setMultiByteMode(); +await port.setMultiByteEncoding(MultiByteEncoding.utf8); + +// 打印 +await port.setAlignment(Alignment.center); +await port.setTextScale(width: 2, height: 2); +await port.printText('拉曼检测结果报告'); + +// 走纸切纸 +await port.feedLine(3); +await port.halfCutPaper(); + +// 关闭 +await port.close(); +``` + +### API 分类 + +| 类别 | 方法 | +|------|------| +| 端口管理 | `openComPort`, `close` | +| 打印文本 | `printText`, `printTextInUtf8`, `printTextInGbk` | +| 格式控制 | `setAlignment`, `setTextScale`, `setTextBold`, `setTextUnderline` | +| 走纸切纸 | `feedLine`, `feedDot`, `halfCutPaper`, `fullCutPaper` | +| 编码模式 | `setMultiByteMode`, `setSingleByteMode`, `setMultiByteEncoding` | +| 条码/二维码 | `printBarcode`, `printQRCode` | +| 图片 | `printRasterImageFromData` | +| 其他 | `resetPrinter`, `kickOutDrawer`, `beep` | + +## 4. MethodChannel 协议 + +### 协议格式 +``` +MethodChannel 名称: "printer" + +Method: "openComPort" + Args: { portName, baudRate, dataBits, parity, stopBits, flowControl, autoReplyMode } + Return: { success: bool, portHandle: int?, error: String? } + +Method: "closePort" + Args: { portHandle } + Return: { success: bool, error: String? } + +Method: "printText" + Args: { portHandle, text } + Return: { success: bool, error: String? } + +... // 每个操作对应一个 method call +``` + +### 错误处理 +- 原生层异常通过 `result.error()` 返回 +- Dart 层统一包装为 `PrinterException` + +## 5. 原生层设计 + +### 端口句柄管理 +- `PrinterPlugin.java` 维护 `Map portHandles` +- `openComPort` 调用 `CP_Port_OpenCom` 返回 Pointer 存入 map +- 生成递增 int 作为 Dart 层 portHandle +- 每个 method call 从 map 中取出 Pointer,调用对应 AAR 函数 + +### AAR 集成 +- 将 `docs/autoreplyprint.aar` 移至 `android/libs/` +- `build.gradle.kts` 配置 `implementation files('libs/autoreplyprint.aar')` + +## 6. 项目结构 + +``` +printer/ +├── lib/ +│ ├── printer.dart # 主入口 +│ ├── printer_platform_interface.dart # 平台接口 +│ ├── printer_method_channel.dart # MethodChannel 实现 +│ └── src/ +│ ├── models/ +│ │ ├── parity.dart # 串口校验枚举 +│ │ ├── alignment.dart # 对齐枚举 +│ │ ├── encoding.dart # 字符编码枚举 +│ │ └── printer_exception.dart # 自定义异常 +│ └── port_handle.dart # 端口句柄封装 +├── android/ +│ ├── build.gradle.kts +│ ├── libs/ +│ │ └── autoreplyprint.aar +│ └── src/main/java/com/xiarui/printer/ +│ ├── PrinterPlugin.java +│ └── PortHandleManager.java +└── test/ + └── printer_test.dart +``` + +## 7. 关键决策 + +| 决策 | 选择 | 理由 | +|------|------|------| +| SDK 集成方式 | 原生层调用 AAR | 利用已有封装,可靠 | +| API 风格 | 底层指令式 | 灵活,调用方控制流程 | +| 字符编码 | UTF8 | 中文支持,现代标准 | +| 连接方式 | 串口 (通过 CH34) | 项目实际硬件需求 | +| 状态回调 | 暂不需要 | 最小实现,后续可扩展 | diff --git a/docs/usage.md b/docs/usage.md new file mode 100644 index 0000000..62b3d76 --- /dev/null +++ b/docs/usage.md @@ -0,0 +1,471 @@ +# Printer 插件使用指南 + +> 本文档介绍 Flutter Printer 插件的安装、配置和使用方法。该插件封装了 Android `autoreplyprint` SDK,提供了串口/USB 端口管理和票据打印功能。 + +## 目录 + +- [安装](#安装) +- [快速开始](#快速开始) +- [端口管理](#端口管理) +- [票据打印](#票据打印) +- [异常处理](#异常处理) +- [完整示例](#完整示例) +- [API 参考](#api-参考) + +--- + +## 安装 + +### 1. 添加依赖 + +在 `pubspec.yaml` 中添加本地依赖: + +```yaml +dependencies: + printer: + path: ../printer # 根据实际路径调整 +``` + +### 2. 配置 Android + +将 `docs/autoreplyprint.aar` 文件放置到 Android 项目的 `app/libs/` 目录下: + +``` +your_flutter_app/ +└── android/ + └── app/ + └── libs/ + └── autoreplyprint.aar +``` + +在 `android/app/build.gradle` 中确保 `libs` 目录被引用: + +```gradle +dependencies { + implementation files('libs/autoreplyprint.aar') + // ... 其他依赖 +} +``` + +--- + +## 快速开始 + +### 基本打印流程 + +一个完整的打印流程包含三个步骤:**打开端口 → 打印内容 → 关闭端口**。 + +```dart +import 'package:printer/printer.dart'; + +final printer = Printer(); + +// 1. 打开端口 +final handle = await printer.openComPort( + portName: '/dev/ttyS0', + baudRate: 115200, +); + +// 2. 设置编码并打印文本 +await printer.setMultiByteMode(handle); +await printer.setMultiByteEncoding(handle, MultiByteEncoding.utf8); +await printer.setAlignment(handle, PrinterAlignment.center); +await printer.printText(handle, 'Hello 中文\n'); +await printer.feedLine(handle, 3); + +// 3. 关闭端口 +await printer.closePort(handle); +``` + +--- + +## 端口管理 + +### 枚举端口 + +```dart +// 枚举串口 +final comPorts = await printer.enumComPorts(); +print('可用串口: $comPorts'); + +// 枚举USB端口 +final usbPorts = await printer.enumUsbPorts(); +print('可用USB口: $usbPorts'); +``` + +### 打开串口 (COM Port) + +```dart +final handle = await printer.openComPort( + portName: '/dev/ttyS0', // 端口名称(必填) + baudRate: 115200, // 波特率(必填),需与打印机保持一致 + dataBits: 8, // 数据位,默认8 + parity: SerialParity.none, // 校验位,默认无校验 + stopBits: SerialStopBits.one, // 停止位,默认1位 + flowControl: SerialFlowControl.none, // 流控制,默认无 + autoReplyMode: true, // 是否开启自动回传模式 +); +``` + +**参数说明:** + +| 参数 | 类型 | 必填 | 默认值 | 说明 | +|------|------|------|--------|------| +| `portName` | `String` | 是 | - | 端口名称,如 `/dev/ttyS0` 或 `COM1` | +| `baudRate` | `int` | 是 | - | 波特率,如 `9600`, `19200`, `38400`, `57600`, `115200` | +| `dataBits` | `int` | 否 | `8` | 数据位,范围 `[4, 8]` | +| `parity` | `SerialParity` | 否 | `none` | 校验位:`none`, `odd`, `even`, `mark`, `space` | +| `stopBits` | `SerialStopBits` | 否 | `one` | 停止位:`one`, `onePointFive`, `two` | +| `flowControl` | `SerialFlowControl` | 否 | `none` | 流控制:`none`, `xonXoff`, `rtsCts`, `dtrDsr` | +| `autoReplyMode` | `bool` | 否 | `true` | 是否开启自动回传模式 | + +### 打开 USB 端口 + +```dart +final handle = await printer.openUsbPort( + portName: 'USB001', // 端口名称(必填),可不指定以自动查找 + autoReplyMode: true, // 是否开启自动回传模式 +); +``` + +### 检查端口状态 + +```dart +final isOpen = await printer.isPortOpened(handle); +if (isOpen) { + print('端口已打开'); +} +``` + +### 关闭端口 + +```dart +final success = await printer.closePort(handle); +``` + +### 使用安全句柄 (推荐) + +使用 `PrinterPortHandle` 可以更安全地管理端口资源,避免资源泄漏: + +```dart +// 打开带句柄的串口 +final portHandle = await printer.openComPortWithHandle( + portName: '/dev/ttyS0', + baudRate: 115200, +); + +try { + // 执行打印操作 + await printer.printText(portHandle.handle, 'Hello\n'); +} finally { + // 确保端口被关闭 + await portHandle.close(); +} +``` + +同样支持 USB 端口: + +```dart +final portHandle = await printer.openUsbPortWithHandle( + portName: 'USB001', +); +``` + +--- + +## 票据打印 + +### 打印文本 + +在打印文本之前,需要先设置多字节编码模式: + +```dart +// 设置多字节模式 +await printer.setMultiByteMode(handle); + +// 设置编码为 UTF-8(支持中文等多语言字符) +await printer.setMultiByteEncoding(handle, MultiByteEncoding.utf8); + +// 打印文本 +await printer.printText(handle, 'Hello 中文\n'); +``` + +**支持的编码类型 (`MultiByteEncoding`):** + +| 编码 | 值 | 说明 | +|------|-----|------| +| `gbk` | `0` | GBK 编码(简体中文) | +| `utf8` | `1` | UTF-8 编码(Unicode,推荐) | +| `big5` | `3` | BIG5 编码(繁体中文) | +| `shiftJis` | `4` | Shift-JIS 编码(日文) | +| `eucKr` | `5` | EUC-KR 编码(韩文) | + +### 设置文本对齐 + +```dart +// 左对齐 +await printer.setAlignment(handle, PrinterAlignment.left); + +// 居中对齐 +await printer.setAlignment(handle, PrinterAlignment.center); + +// 右对齐 +await printer.setAlignment(handle, PrinterAlignment.right); +``` + +### 设置文本缩放 + +宽度和高度缩放值范围为 `1` 到 `8`: + +```dart +await printer.setTextScale( + handle, + widthScale: 2, // 宽度放大2倍 + heightScale: 2, // 高度放大2倍 +); + +await printer.printText(handle, 'Large Text\n'); +``` + +### 设置文本加粗 + +```dart +// 开启加粗 +await printer.setTextBold(handle, true); +await printer.printText(handle, 'Bold Text\n'); + +// 关闭加粗 +await printer.setTextBold(handle, false); +``` + +### 设置文本下划线 + +```dart +// 0 = 无下划线 +// 1 = 1点下划线 +// 2 = 2点下划线 +await printer.setTextUnderline(handle, 1); +await printer.printText(handle, 'Underlined Text\n'); +``` + +### 走纸 + +```dart +// 走纸指定行数 +await printer.feedLine(handle, 5); + +// 走纸指定点数 +await printer.feedDot(handle, 100); +``` + +### 切纸 + +```dart +// 半切(虚线切,纸张不完全分离) +await printer.halfCutPaper(handle); + +// 全切(实线切,纸张完全分离) +await printer.fullCutPaper(handle); +``` + +--- + +## 异常处理 + +所有端口打开操作在失败时会抛出 `PrinterException`: + +```dart +try { + final handle = await printer.openComPort( + portName: '/dev/ttyS0', + baudRate: 115200, + ); +} on PrinterException catch (e) { + print('端口打开失败: ${e.code} - ${e.message}'); +} +``` + +`PrinterException` 包含以下信息: + +| 属性 | 类型 | 说明 | +|------|------|------| +| `code` | `String` | 机器可读的错误码,如 `INVALID_ARGUMENT`, `PORT_OPEN_FAILED` | +| `message` | `String` | 人类可读的错误消息 | +| `details` | `dynamic` | 额外的错误详情(可能为 null) | + +--- + +## 完整示例 + +以下是一个完整的票据打印示例: + +```dart +import 'package:printer/printer.dart'; + +/// 打印收据示例 +Future printReceipt() async { + final printer = Printer(); + PrinterPortHandle? portHandle; + + try { + // 1. 枚举可用端口 + final comPorts = await printer.enumComPorts(); + if (comPorts.isEmpty) { + print('没有可用的串口'); + return; + } + + // 2. 打开第一个可用端口 + portHandle = await printer.openComPortWithHandle( + portName: comPorts.first, + baudRate: 115200, + ); + final handle = portHandle.handle; + + // 3. 设置编码 + await printer.setMultiByteMode(handle); + await printer.setMultiByteEncoding(handle, MultiByteEncoding.utf8); + + // 4. 打印标题(居中、加粗、放大) + await printer.setAlignment(handle, PrinterAlignment.center); + await printer.setTextBold(handle, true); + await printer.setTextScale(handle, widthScale: 2, heightScale: 2); + await printer.printText(handle, '收据标题\n'); + + // 5. 重置样式并打印内容 + await printer.setTextBold(handle, false); + await printer.setTextScale(handle, widthScale: 1, heightScale: 1); + await printer.setAlignment(handle, PrinterAlignment.left); + await printer.printText(handle, '商品名称 数量 价格\n'); + await printer.printText(handle, '----------------------------\n'); + await printer.printText(handle, '商品A 2 ¥20.00\n'); + await printer.printText(handle, '商品B 1 ¥15.00\n'); + await printer.printText(handle, '----------------------------\n'); + await printer.printText(handle, '总计: ¥35.00\n'); + + // 6. 走纸并切纸 + await printer.feedLine(handle, 5); + await printer.halfCutPaper(handle); + + print('打印完成'); + } on PrinterException catch (e) { + print('打印失败: ${e.code} - ${e.message}'); + } finally { + // 7. 确保端口关闭 + await portHandle?.close(); + } +} +``` + +--- + +## API 参考 + +### Printer 类 + +| 方法 | 返回值 | 说明 | +|------|--------|------| +| `getPlatformVersion()` | `Future` | 获取平台版本 | +| `enumComPorts()` | `Future>` | 枚举可用串口 | +| `enumUsbPorts()` | `Future>` | 枚举可用USB端口 | +| `openComPort({...})` | `Future` | 打开串口 | +| `openUsbPort({...})` | `Future` | 打开USB端口 | +| `openComPortWithHandle({...})` | `Future` | 打开串口并返回安全句柄 | +| `openUsbPortWithHandle({...})` | `Future` | 打开USB端口并返回安全句柄 | +| `closePort(int handle)` | `Future` | 关闭端口 | +| `isPortOpened(int handle)` | `Future` | 检查端口是否打开 | +| `setMultiByteMode(int handle)` | `Future` | 设置多字节编码模式 | +| `setMultiByteEncoding(int, MultiByteEncoding)` | `Future` | 设置多字节编码类型 | +| `printText(int handle, String text)` | `Future` | 打印文本 | +| `setAlignment(int, PrinterAlignment)` | `Future` | 设置文本对齐 | +| `setTextScale(int, {width, height})` | `Future` | 设置文本缩放 | +| `setTextBold(int handle, bool bold)` | `Future` | 设置文本加粗 | +| `setTextUnderline(int handle, int level)` | `Future` | 设置文本下划线 | +| `feedLine(int handle, int numLines)` | `Future` | 走纸指定行数 | +| `feedDot(int handle, int numDots)` | `Future` | 走纸指定点数 | +| `halfCutPaper(int handle)` | `Future` | 半切纸 | +| `fullCutPaper(int handle)` | `Future` | 全切纸 | + +### 枚举类型 + +#### SerialParity (串口校验位) + +| 值 | 说明 | +|----|------| +| `none` | 无校验 | +| `odd` | 奇校验 | +| `even` | 偶校验 | +| `mark` | 标记校验 | +| `space` | 空白校验 | + +#### SerialStopBits (串口停止位) + +| 值 | 说明 | +|----|------| +| `one` | 1位停止位 | +| `onePointFive` | 1.5位停止位 | +| `two` | 2位停止位 | + +#### SerialFlowControl (串口流控制) + +| 值 | 说明 | +|----|------| +| `none` | 无流控 | +| `xonXoff` | XON/XOFF 软件流控 | +| `rtsCts` | RTS/CTS 硬件流控 | +| `dtrDsr` | DTR/DSR 硬件流控 | + +#### PrinterAlignment (打印对齐) + +| 值 | 说明 | +|----|------| +| `left` | 左对齐 | +| `center` | 居中对齐 | +| `right` | 右对齐 | + +#### MultiByteEncoding (多字节编码) + +| 值 | 说明 | +|----|------| +| `gbk` | GBK 编码(简体中文) | +| `utf8` | UTF-8 编码(推荐) | +| `big5` | BIG5 编码(繁体中文) | +| `shiftJis` | Shift-JIS 编码(日文) | +| `eucKr` | EUC-KR 编码(韩文) | + +### PrinterPortHandle 类 + +| 属性/方法 | 类型 | 说明 | +|-----------|------|------| +| `handle` | `int` | 整数端口句柄 | +| `isValid` | `bool` | 端口是否仍然有效 | +| `close()` | `Future` | 关闭端口,多次调用为 no-op | + +### PrinterException 类 + +| 属性 | 类型 | 说明 | +|------|------|------| +| `code` | `String` | 错误码 | +| `message` | `String` | 错误消息 | +| `details` | `dynamic` | 额外详情 | + +--- + +## 常见问题 + +### 1. 打印乱码怎么办? + +- 确认编码设置与发送的文本编码一致,推荐使用 `MultiByteEncoding.utf8` +- 确认打印机机型是否正确(票据机型使用 `CP_Pos_` 系列函数) +- 检查波特率是否与打印机设置一致 + +### 2. 端口打开失败? + +- 检查端口名称是否正确 +- 确认端口未被其他应用占用 +- 检查 USB 连接或串口线连接是否正常 + +### 3. 打印到一半停止? + +- 检查电源是否充足(一般需要 2A 电源) +- 检查是否缺纸 diff --git a/example/.gitignore b/example/.gitignore new file mode 100644 index 0000000..3820a95 --- /dev/null +++ b/example/.gitignore @@ -0,0 +1,45 @@ +# Miscellaneous +*.class +*.log +*.pyc +*.swp +.DS_Store +.atom/ +.build/ +.buildlog/ +.history +.svn/ +.swiftpm/ +migrate_working_dir/ + +# IntelliJ related +*.iml +*.ipr +*.iws +.idea/ + +# The .vscode folder contains launch configuration and tasks you configure in +# VS Code which you may wish to be included in version control, so this line +# is commented out by default. +#.vscode/ + +# Flutter/Dart/Pub related +**/doc/api/ +**/ios/Flutter/.last_build_id +.dart_tool/ +.flutter-plugins-dependencies +.pub-cache/ +.pub/ +/build/ +/coverage/ + +# Symbolication related +app.*.symbols + +# Obfuscation related +app.*.map.json + +# Android Studio will place build artifacts here +/android/app/debug +/android/app/profile +/android/app/release diff --git a/example/README.md b/example/README.md new file mode 100644 index 0000000..d22f08b --- /dev/null +++ b/example/README.md @@ -0,0 +1,17 @@ +# printer_example + +Demonstrates how to use the printer plugin. + +## Getting Started + +This project is a starting point for a Flutter application. + +A few resources to get you started if this is your first Flutter project: + +- [Learn Flutter](https://docs.flutter.dev/get-started/learn-flutter) +- [Write your first Flutter app](https://docs.flutter.dev/get-started/codelab) +- [Flutter learning resources](https://docs.flutter.dev/reference/learning-resources) + +For help getting started with Flutter development, view the +[online documentation](https://docs.flutter.dev/), which offers tutorials, +samples, guidance on mobile development, and a full API reference. diff --git a/example/analysis_options.yaml b/example/analysis_options.yaml new file mode 100644 index 0000000..0d29021 --- /dev/null +++ b/example/analysis_options.yaml @@ -0,0 +1,28 @@ +# This file configures the analyzer, which statically analyzes Dart code to +# check for errors, warnings, and lints. +# +# The issues identified by the analyzer are surfaced in the UI of Dart-enabled +# IDEs (https://dart.dev/tools#ides-and-editors). The analyzer can also be +# invoked from the command line by running `flutter analyze`. + +# The following line activates a set of recommended lints for Flutter apps, +# packages, and plugins designed to encourage good coding practices. +include: package:flutter_lints/flutter.yaml + +linter: + # The lint rules applied to this project can be customized in the + # section below to disable rules from the `package:flutter_lints/flutter.yaml` + # included above or to enable additional rules. A list of all available lints + # and their documentation is published at https://dart.dev/lints. + # + # Instead of disabling a lint rule for the entire project in the + # section below, it can also be suppressed for a single line of code + # or a specific dart file by using the `// ignore: name_of_lint` and + # `// ignore_for_file: name_of_lint` syntax on the line or in the file + # producing the lint. + rules: + # avoid_print: false # Uncomment to disable the `avoid_print` rule + # prefer_single_quotes: true # Uncomment to enable the `prefer_single_quotes` rule + +# Additional information about this file can be found at +# https://dart.dev/guides/language/analysis-options diff --git a/example/android/.gitignore b/example/android/.gitignore new file mode 100644 index 0000000..be3943c --- /dev/null +++ b/example/android/.gitignore @@ -0,0 +1,14 @@ +gradle-wrapper.jar +/.gradle +/captures/ +/gradlew +/gradlew.bat +/local.properties +GeneratedPluginRegistrant.java +.cxx/ + +# Remember to never publicly share your keystore. +# See https://flutter.dev/to/reference-keystore +key.properties +**/*.keystore +**/*.jks diff --git a/example/android/app/build.gradle.kts b/example/android/app/build.gradle.kts new file mode 100644 index 0000000..a0e1260 --- /dev/null +++ b/example/android/app/build.gradle.kts @@ -0,0 +1,44 @@ +plugins { + id("com.android.application") + id("kotlin-android") + // The Flutter Gradle Plugin must be applied after the Android and Kotlin Gradle plugins. + id("dev.flutter.flutter-gradle-plugin") +} + +android { + namespace = "com.xiarui.printer_example" + compileSdk = flutter.compileSdkVersion + ndkVersion = flutter.ndkVersion + + compileOptions { + sourceCompatibility = JavaVersion.VERSION_17 + targetCompatibility = JavaVersion.VERSION_17 + } + + kotlinOptions { + jvmTarget = JavaVersion.VERSION_17.toString() + } + + defaultConfig { + // TODO: Specify your own unique Application ID (https://developer.android.com/studio/build/application-id.html). + applicationId = "com.xiarui.printer_example" + // You can update the following values to match your application needs. + // For more information, see: https://flutter.dev/to/review-gradle-config. + minSdk = flutter.minSdkVersion + targetSdk = flutter.targetSdkVersion + versionCode = flutter.versionCode + versionName = flutter.versionName + } + + buildTypes { + release { + // TODO: Add your own signing config for the release build. + // Signing with the debug keys for now, so `flutter run --release` works. + signingConfig = signingConfigs.getByName("debug") + } + } +} + +flutter { + source = "../.." +} diff --git a/example/android/app/src/debug/AndroidManifest.xml b/example/android/app/src/debug/AndroidManifest.xml new file mode 100644 index 0000000..399f698 --- /dev/null +++ b/example/android/app/src/debug/AndroidManifest.xml @@ -0,0 +1,7 @@ + + + + diff --git a/example/android/app/src/main/AndroidManifest.xml b/example/android/app/src/main/AndroidManifest.xml new file mode 100644 index 0000000..f79673c --- /dev/null +++ b/example/android/app/src/main/AndroidManifest.xml @@ -0,0 +1,45 @@ + + + + + + + + + + + + + + + + + + + + + diff --git a/example/android/app/src/main/java/com/xiarui/printer_example/MainActivity.java b/example/android/app/src/main/java/com/xiarui/printer_example/MainActivity.java new file mode 100644 index 0000000..8f3045e --- /dev/null +++ b/example/android/app/src/main/java/com/xiarui/printer_example/MainActivity.java @@ -0,0 +1,6 @@ +package com.xiarui.printer_example; + +import io.flutter.embedding.android.FlutterActivity; + +public class MainActivity extends FlutterActivity { +} diff --git a/example/android/app/src/main/res/drawable-v21/launch_background.xml b/example/android/app/src/main/res/drawable-v21/launch_background.xml new file mode 100644 index 0000000..f74085f --- /dev/null +++ b/example/android/app/src/main/res/drawable-v21/launch_background.xml @@ -0,0 +1,12 @@ + + + + + + + + diff --git a/example/android/app/src/main/res/drawable/launch_background.xml b/example/android/app/src/main/res/drawable/launch_background.xml new file mode 100644 index 0000000..304732f --- /dev/null +++ b/example/android/app/src/main/res/drawable/launch_background.xml @@ -0,0 +1,12 @@ + + + + + + + + diff --git a/example/android/app/src/main/res/mipmap-hdpi/ic_launcher.png b/example/android/app/src/main/res/mipmap-hdpi/ic_launcher.png new file mode 100644 index 0000000..db77bb4 Binary files /dev/null and b/example/android/app/src/main/res/mipmap-hdpi/ic_launcher.png differ diff --git a/example/android/app/src/main/res/mipmap-mdpi/ic_launcher.png b/example/android/app/src/main/res/mipmap-mdpi/ic_launcher.png new file mode 100644 index 0000000..17987b7 Binary files /dev/null and b/example/android/app/src/main/res/mipmap-mdpi/ic_launcher.png differ diff --git a/example/android/app/src/main/res/mipmap-xhdpi/ic_launcher.png b/example/android/app/src/main/res/mipmap-xhdpi/ic_launcher.png new file mode 100644 index 0000000..09d4391 Binary files /dev/null and b/example/android/app/src/main/res/mipmap-xhdpi/ic_launcher.png differ diff --git a/example/android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png b/example/android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png new file mode 100644 index 0000000..d5f1c8d Binary files /dev/null and b/example/android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png differ diff --git a/example/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png b/example/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png new file mode 100644 index 0000000..4d6372e Binary files /dev/null and b/example/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png differ diff --git a/example/android/app/src/main/res/values-night/styles.xml b/example/android/app/src/main/res/values-night/styles.xml new file mode 100644 index 0000000..06952be --- /dev/null +++ b/example/android/app/src/main/res/values-night/styles.xml @@ -0,0 +1,18 @@ + + + + + + + diff --git a/example/android/app/src/main/res/values/styles.xml b/example/android/app/src/main/res/values/styles.xml new file mode 100644 index 0000000..cb1ef88 --- /dev/null +++ b/example/android/app/src/main/res/values/styles.xml @@ -0,0 +1,18 @@ + + + + + + + diff --git a/example/android/app/src/profile/AndroidManifest.xml b/example/android/app/src/profile/AndroidManifest.xml new file mode 100644 index 0000000..399f698 --- /dev/null +++ b/example/android/app/src/profile/AndroidManifest.xml @@ -0,0 +1,7 @@ + + + + diff --git a/example/android/build.gradle.kts b/example/android/build.gradle.kts new file mode 100644 index 0000000..dbee657 --- /dev/null +++ b/example/android/build.gradle.kts @@ -0,0 +1,24 @@ +allprojects { + repositories { + google() + mavenCentral() + } +} + +val newBuildDir: Directory = + rootProject.layout.buildDirectory + .dir("../../build") + .get() +rootProject.layout.buildDirectory.value(newBuildDir) + +subprojects { + val newSubprojectBuildDir: Directory = newBuildDir.dir(project.name) + project.layout.buildDirectory.value(newSubprojectBuildDir) +} +subprojects { + project.evaluationDependsOn(":app") +} + +tasks.register("clean") { + delete(rootProject.layout.buildDirectory) +} diff --git a/example/android/build/reports/problems/problems-report.html b/example/android/build/reports/problems/problems-report.html new file mode 100644 index 0000000..f7c9369 --- /dev/null +++ b/example/android/build/reports/problems/problems-report.html @@ -0,0 +1,663 @@ + + + + + + + + + + + + + Gradle Configuration Cache + + + +
+ +
+ Loading... +
+ + + + + + diff --git a/example/android/gradle.properties b/example/android/gradle.properties new file mode 100644 index 0000000..ae9783e --- /dev/null +++ b/example/android/gradle.properties @@ -0,0 +1,3 @@ +org.gradle.jvmargs=-Xmx8G -XX:MaxMetaspaceSize=4G -XX:ReservedCodeCacheSize=512m -XX:+HeapDumpOnOutOfMemoryError +android.useAndroidX=true +android.overridePathCheck=true diff --git a/example/android/gradle/wrapper/gradle-wrapper.properties b/example/android/gradle/wrapper/gradle-wrapper.properties new file mode 100644 index 0000000..e4ef43f --- /dev/null +++ b/example/android/gradle/wrapper/gradle-wrapper.properties @@ -0,0 +1,5 @@ +distributionBase=GRADLE_USER_HOME +distributionPath=wrapper/dists +zipStoreBase=GRADLE_USER_HOME +zipStorePath=wrapper/dists +distributionUrl=https\://services.gradle.org/distributions/gradle-8.14-all.zip diff --git a/example/android/settings.gradle.kts b/example/android/settings.gradle.kts new file mode 100644 index 0000000..ca7fe06 --- /dev/null +++ b/example/android/settings.gradle.kts @@ -0,0 +1,26 @@ +pluginManagement { + val flutterSdkPath = + run { + val properties = java.util.Properties() + file("local.properties").inputStream().use { properties.load(it) } + val flutterSdkPath = properties.getProperty("flutter.sdk") + require(flutterSdkPath != null) { "flutter.sdk not set in local.properties" } + flutterSdkPath + } + + includeBuild("$flutterSdkPath/packages/flutter_tools/gradle") + + repositories { + google() + mavenCentral() + gradlePluginPortal() + } +} + +plugins { + id("dev.flutter.flutter-plugin-loader") version "1.0.0" + id("com.android.application") version "8.11.1" apply false + id("org.jetbrains.kotlin.android") version "2.2.20" apply false +} + +include(":app") diff --git a/example/integration_test/plugin_integration_test.dart b/example/integration_test/plugin_integration_test.dart new file mode 100644 index 0000000..550d552 --- /dev/null +++ b/example/integration_test/plugin_integration_test.dart @@ -0,0 +1,24 @@ +// This is a basic Flutter integration test. +// +// Since integration tests run in a full Flutter application, they can interact +// with the host side of a plugin implementation, unlike Dart unit tests. +// +// For more information about Flutter integration tests, please see +// https://flutter.dev/to/integration-testing + +import 'package:flutter_test/flutter_test.dart'; +import 'package:integration_test/integration_test.dart'; + +import 'package:printer/printer.dart'; + +void main() { + IntegrationTestWidgetsFlutterBinding.ensureInitialized(); + + testWidgets('getPlatformVersion test', (WidgetTester tester) async { + final Printer plugin = Printer(); + final String? version = await plugin.getPlatformVersion(); + // The version string depends on the host platform running the test, so + // just assert that some non-empty string is returned. + expect(version?.isNotEmpty, true); + }); +} diff --git a/example/lib/main.dart b/example/lib/main.dart new file mode 100644 index 0000000..8dd8bb6 --- /dev/null +++ b/example/lib/main.dart @@ -0,0 +1,59 @@ +import 'package:flutter/material.dart'; +import 'dart:async'; + +import 'package:flutter/services.dart'; +import 'package:printer/printer.dart'; + +void main() { + runApp(const MyApp()); +} + +class MyApp extends StatefulWidget { + const MyApp({super.key}); + + @override + State createState() => _MyAppState(); +} + +class _MyAppState extends State { + String _platformVersion = 'Unknown'; + final _printerPlugin = Printer(); + + @override + void initState() { + super.initState(); + initPlatformState(); + } + + // Platform messages are asynchronous, so we initialize in an async method. + Future initPlatformState() async { + String platformVersion; + // Platform messages may fail, so we use a try/catch PlatformException. + // We also handle the message potentially returning null. + try { + platformVersion = + await _printerPlugin.getPlatformVersion() ?? 'Unknown platform version'; + } on PlatformException { + platformVersion = 'Failed to get platform version.'; + } + + // If the widget was removed from the tree while the asynchronous platform + // message was in flight, we want to discard the reply rather than calling + // setState to update our non-existent appearance. + if (!mounted) return; + + setState(() { + _platformVersion = platformVersion; + }); + } + + @override + Widget build(BuildContext context) { + return MaterialApp( + home: Scaffold( + appBar: AppBar(title: const Text('Plugin example app')), + body: Center(child: Text('Running on: $_platformVersion\n')), + ), + ); + } +} diff --git a/example/pubspec.lock b/example/pubspec.lock new file mode 100644 index 0000000..c5d3588 --- /dev/null +++ b/example/pubspec.lock @@ -0,0 +1,283 @@ +# Generated by pub +# See https://dart.dev/tools/pub/glossary#lockfile +packages: + async: + dependency: transitive + description: + name: async + sha256: e2eb0491ba5ddb6177742d2da23904574082139b07c1e33b8503b9f46f3e1a37 + url: "https://pub.flutter-io.cn" + source: hosted + version: "2.13.1" + boolean_selector: + dependency: transitive + description: + name: boolean_selector + sha256: "8aab1771e1243a5063b8b0ff68042d67334e3feab9e95b9490f9a6ebf73b42ea" + url: "https://pub.flutter-io.cn" + source: hosted + version: "2.1.2" + characters: + dependency: transitive + description: + name: characters + sha256: faf38497bda5ead2a8c7615f4f7939df04333478bf32e4173fcb06d428b5716b + url: "https://pub.flutter-io.cn" + source: hosted + version: "1.4.1" + clock: + dependency: transitive + description: + name: clock + sha256: fddb70d9b5277016c77a80201021d40a2247104d9f4aa7bab7157b7e3f05b84b + url: "https://pub.flutter-io.cn" + source: hosted + version: "1.1.2" + collection: + dependency: transitive + description: + name: collection + sha256: "2f5709ae4d3d59dd8f7cd309b4e023046b57d8a6c82130785d2b0e5868084e76" + url: "https://pub.flutter-io.cn" + source: hosted + version: "1.19.1" + cupertino_icons: + dependency: "direct main" + description: + name: cupertino_icons + sha256: "41e005c33bd814be4d3096aff55b1908d419fde52ca656c8c47719ec745873cd" + url: "https://pub.flutter-io.cn" + source: hosted + version: "1.0.9" + fake_async: + dependency: transitive + description: + name: fake_async + sha256: "5368f224a74523e8d2e7399ea1638b37aecfca824a3cc4dfdf77bf1fa905ac44" + url: "https://pub.flutter-io.cn" + source: hosted + version: "1.3.3" + file: + dependency: transitive + description: + name: file + sha256: a3b4f84adafef897088c160faf7dfffb7696046cb13ae90b508c2cbc95d3b8d4 + url: "https://pub.flutter-io.cn" + source: hosted + version: "7.0.1" + flutter: + dependency: "direct main" + description: flutter + source: sdk + version: "0.0.0" + flutter_driver: + dependency: transitive + description: flutter + source: sdk + version: "0.0.0" + flutter_lints: + dependency: "direct dev" + description: + name: flutter_lints + sha256: "3105dc8492f6183fb076ccf1f351ac3d60564bff92e20bfc4af9cc1651f4e7e1" + url: "https://pub.flutter-io.cn" + source: hosted + version: "6.0.0" + flutter_test: + dependency: "direct dev" + description: flutter + source: sdk + version: "0.0.0" + fuchsia_remote_debug_protocol: + dependency: transitive + description: flutter + source: sdk + version: "0.0.0" + integration_test: + dependency: "direct dev" + description: flutter + source: sdk + version: "0.0.0" + leak_tracker: + dependency: transitive + description: + name: leak_tracker + sha256: "33e2e26bdd85a0112ec15400c8cbffea70d0f9c3407491f672a2fad47915e2de" + url: "https://pub.flutter-io.cn" + source: hosted + version: "11.0.2" + leak_tracker_flutter_testing: + dependency: transitive + description: + name: leak_tracker_flutter_testing + sha256: "1dbc140bb5a23c75ea9c4811222756104fbcd1a27173f0c34ca01e16bea473c1" + url: "https://pub.flutter-io.cn" + source: hosted + version: "3.0.10" + leak_tracker_testing: + dependency: transitive + description: + name: leak_tracker_testing + sha256: "8d5a2d49f4a66b49744b23b018848400d23e54caf9463f4eb20df3eb8acb2eb1" + url: "https://pub.flutter-io.cn" + source: hosted + version: "3.0.2" + lints: + dependency: transitive + description: + name: lints + sha256: "12f842a479589fea194fe5c5a3095abc7be0c1f2ddfa9a0e76aed1dbd26a87df" + url: "https://pub.flutter-io.cn" + source: hosted + version: "6.1.0" + matcher: + dependency: transitive + description: + name: matcher + sha256: dc0b7dc7651697ea4ff3e69ef44b0407ea32c487a39fff6a4004fa585e901861 + url: "https://pub.flutter-io.cn" + source: hosted + version: "0.12.19" + material_color_utilities: + dependency: transitive + description: + name: material_color_utilities + sha256: "9c337007e82b1889149c82ed242ed1cb24a66044e30979c44912381e9be4c48b" + url: "https://pub.flutter-io.cn" + source: hosted + version: "0.13.0" + meta: + dependency: transitive + description: + name: meta + sha256: "23f08335362185a5ea2ad3a4e597f1375e78bce8a040df5c600c8d3552ef2394" + url: "https://pub.flutter-io.cn" + source: hosted + version: "1.17.0" + path: + dependency: transitive + description: + name: path + sha256: "75cca69d1490965be98c73ceaea117e8a04dd21217b37b292c9ddbec0d955bc5" + url: "https://pub.flutter-io.cn" + source: hosted + version: "1.9.1" + platform: + dependency: transitive + description: + name: platform + sha256: "5d6b1b0036a5f331ebc77c850ebc8506cbc1e9416c27e59b439f917a902a4984" + url: "https://pub.flutter-io.cn" + source: hosted + version: "3.1.6" + plugin_platform_interface: + dependency: transitive + description: + name: plugin_platform_interface + sha256: "4820fbfdb9478b1ebae27888254d445073732dae3d6ea81f0b7e06d5dedc3f02" + url: "https://pub.flutter-io.cn" + source: hosted + version: "2.1.8" + printer: + dependency: "direct main" + description: + path: ".." + relative: true + source: path + version: "0.0.1" + process: + dependency: transitive + description: + name: process + sha256: c6248e4526673988586e8c00bb22a49210c258dc91df5227d5da9748ecf79744 + url: "https://pub.flutter-io.cn" + source: hosted + version: "5.0.5" + sky_engine: + dependency: transitive + description: flutter + source: sdk + version: "0.0.0" + source_span: + dependency: transitive + description: + name: source_span + sha256: "56a02f1f4cd1a2d96303c0144c93bd6d909eea6bee6bf5a0e0b685edbd4c47ab" + url: "https://pub.flutter-io.cn" + source: hosted + version: "1.10.2" + stack_trace: + dependency: transitive + description: + name: stack_trace + sha256: "8b27215b45d22309b5cddda1aa2b19bdfec9df0e765f2de506401c071d38d1b1" + url: "https://pub.flutter-io.cn" + source: hosted + version: "1.12.1" + stream_channel: + dependency: transitive + description: + name: stream_channel + sha256: "969e04c80b8bcdf826f8f16579c7b14d780458bd97f56d107d3950fdbeef059d" + url: "https://pub.flutter-io.cn" + source: hosted + version: "2.1.4" + string_scanner: + dependency: transitive + description: + name: string_scanner + sha256: "921cd31725b72fe181906c6a94d987c78e3b98c2e205b397ea399d4054872b43" + url: "https://pub.flutter-io.cn" + source: hosted + version: "1.4.1" + sync_http: + dependency: transitive + description: + name: sync_http + sha256: "7f0cd72eca000d2e026bcd6f990b81d0ca06022ef4e32fb257b30d3d1014a961" + url: "https://pub.flutter-io.cn" + source: hosted + version: "0.3.1" + term_glyph: + dependency: transitive + description: + name: term_glyph + sha256: "7f554798625ea768a7518313e58f83891c7f5024f88e46e7182a4558850a4b8e" + url: "https://pub.flutter-io.cn" + source: hosted + version: "1.2.2" + test_api: + dependency: transitive + description: + name: test_api + sha256: "8161c84903fd860b26bfdefb7963b3f0b68fee7adea0f59ef805ecca346f0c7a" + url: "https://pub.flutter-io.cn" + source: hosted + version: "0.7.10" + vector_math: + dependency: transitive + description: + name: vector_math + sha256: d530bd74fea330e6e364cda7a85019c434070188383e1cd8d9777ee586914c5b + url: "https://pub.flutter-io.cn" + source: hosted + version: "2.2.0" + vm_service: + dependency: transitive + description: + name: vm_service + sha256: "046d3928e16fa4dc46e8350415661755ab759d9fc97fc21b5ab295f71e4f0499" + url: "https://pub.flutter-io.cn" + source: hosted + version: "15.1.0" + webdriver: + dependency: transitive + description: + name: webdriver + sha256: "2f3a14ca026957870cfd9c635b83507e0e51d8091568e90129fbf805aba7cade" + url: "https://pub.flutter-io.cn" + source: hosted + version: "3.1.0" +sdks: + dart: ">=3.11.4 <4.0.0" + flutter: ">=3.18.0-18.0.pre.54" diff --git a/example/pubspec.yaml b/example/pubspec.yaml new file mode 100644 index 0000000..a727ad1 --- /dev/null +++ b/example/pubspec.yaml @@ -0,0 +1,85 @@ +name: printer_example +description: "Demonstrates how to use the printer plugin." +# The following line prevents the package from being accidentally published to +# pub.dev using `flutter pub publish`. This is preferred for private packages. +publish_to: 'none' # Remove this line if you wish to publish to pub.dev + +environment: + sdk: ^3.11.4 + +# Dependencies specify other packages that your package needs in order to work. +# To automatically upgrade your package dependencies to the latest versions +# consider running `flutter pub upgrade --major-versions`. Alternatively, +# dependencies can be manually updated by changing the version numbers below to +# the latest version available on pub.dev. To see which dependencies have newer +# versions available, run `flutter pub outdated`. +dependencies: + flutter: + sdk: flutter + + printer: + # When depending on this package from a real application you should use: + # printer: ^x.y.z + # See https://dart.dev/tools/pub/dependencies#version-constraints + # The example app is bundled with the plugin so we use a path dependency on + # the parent directory to use the current plugin's version. + path: ../ + + # The following adds the Cupertino Icons font to your application. + # Use with the CupertinoIcons class for iOS style icons. + cupertino_icons: ^1.0.8 + +dev_dependencies: + integration_test: + sdk: flutter + flutter_test: + sdk: flutter + + # The "flutter_lints" package below contains a set of recommended lints to + # encourage good coding practices. The lint set provided by the package is + # activated in the `analysis_options.yaml` file located at the root of your + # package. See that file for information about deactivating specific lint + # rules and activating additional ones. + flutter_lints: ^6.0.0 + +# For information on the generic Dart part of this file, see the +# following page: https://dart.dev/tools/pub/pubspec + +# The following section is specific to Flutter packages. +flutter: + + # The following line ensures that the Material Icons font is + # included with your application, so that you can use the icons in + # the material Icons class. + uses-material-design: true + + # To add assets to your application, add an assets section, like this: + # assets: + # - images/a_dot_burr.jpeg + # - images/a_dot_ham.jpeg + + # An image asset can refer to one or more resolution-specific "variants", see + # https://flutter.dev/to/resolution-aware-images + + # For details regarding adding assets from package dependencies, see + # https://flutter.dev/to/asset-from-package + + # To add custom fonts to your application, add a fonts section here, + # in this "flutter" section. Each entry in this list should have a + # "family" key with the font family name, and a "fonts" key with a + # list giving the asset and other descriptors for the font. For + # example: + # fonts: + # - family: Schyler + # fonts: + # - asset: fonts/Schyler-Regular.ttf + # - asset: fonts/Schyler-Italic.ttf + # style: italic + # - family: Trajan Pro + # fonts: + # - asset: fonts/TrajanPro.ttf + # - asset: fonts/TrajanPro_Bold.ttf + # weight: 700 + # + # For details regarding fonts from package dependencies, + # see https://flutter.dev/to/font-from-package diff --git a/example/test/widget_test.dart b/example/test/widget_test.dart new file mode 100644 index 0000000..612fc38 --- /dev/null +++ b/example/test/widget_test.dart @@ -0,0 +1,27 @@ +// This is a basic Flutter widget test. +// +// To perform an interaction with a widget in your test, use the WidgetTester +// utility in the flutter_test package. For example, you can send tap and scroll +// gestures. You can also use WidgetTester to find child widgets in the widget +// tree, read text, and verify that the values of widget properties are correct. + +import 'package:flutter/material.dart'; +import 'package:flutter_test/flutter_test.dart'; + +import 'package:printer_example/main.dart'; + +void main() { + testWidgets('Verify Platform version', (WidgetTester tester) async { + // Build our app and trigger a frame. + await tester.pumpWidget(const MyApp()); + + // Verify that platform version is retrieved. + expect( + find.byWidgetPredicate( + (Widget widget) => + widget is Text && widget.data!.startsWith('Running on:'), + ), + findsOneWidget, + ); + }); +} diff --git a/lib/enums/multi_byte_encoding.dart b/lib/enums/multi_byte_encoding.dart new file mode 100644 index 0000000..358482a --- /dev/null +++ b/lib/enums/multi_byte_encoding.dart @@ -0,0 +1,29 @@ +/// Multi-byte character encoding for ticket printing. +/// +/// Maps to the autoreplyprint AAR SDK CP_MultiByteEncoding constants. +/// The printer uses the specified encoding to interpret received data. +/// +/// Note: value 2 is reserved/unused per SDK specification. The sequence +/// jumps from UTF8(1) to BIG5(3). +enum MultiByteEncoding { + /// GBK encoding (Simplified Chinese) + gbk(0), + + /// UTF-8 encoding (Unicode) + utf8(1), + + // Note: value 2 is reserved/unused per SDK specification + + /// BIG5 encoding (Traditional Chinese) + big5(3), + + /// Shift-JIS encoding (Japanese) + shiftJis(4), + + /// EUC-KR encoding (Korean) + eucKr(5); + + /// The integer value matching the SDK constant. + final int value; + const MultiByteEncoding(this.value); +} diff --git a/lib/enums/printer_alignment.dart b/lib/enums/printer_alignment.dart new file mode 100644 index 0000000..c38eb9a --- /dev/null +++ b/lib/enums/printer_alignment.dart @@ -0,0 +1,17 @@ +/// Alignment for ticket printing. +/// +/// Maps to the autoreplyprint AAR SDK CP_Pos_Alignment constants. +enum PrinterAlignment { + /// Left alignment + left(0), + + /// Center alignment + center(1), + + /// Right alignment + right(2); + + /// The integer value matching the SDK constant. + final int value; + const PrinterAlignment(this.value); +} diff --git a/lib/enums/serial_flow_control.dart b/lib/enums/serial_flow_control.dart new file mode 100644 index 0000000..888f5f3 --- /dev/null +++ b/lib/enums/serial_flow_control.dart @@ -0,0 +1,20 @@ +/// Flow control setting for serial port communication. +/// +/// Maps to the autoreplyprint AAR SDK flow control constants. +enum SerialFlowControl { + /// No flow control + none(0), + + /// XON/XOFF software flow control + xonXoff(1), + + /// RTS/CTS hardware flow control + rtsCts(2), + + /// DTR/DSR hardware flow control + dtrDsr(3); + + /// The integer value matching the AAR SDK constant. + final int value; + const SerialFlowControl(this.value); +} diff --git a/lib/enums/serial_parity.dart b/lib/enums/serial_parity.dart new file mode 100644 index 0000000..fc97fa8 --- /dev/null +++ b/lib/enums/serial_parity.dart @@ -0,0 +1,23 @@ +/// Parity setting for serial port communication. +/// +/// Maps to the autoreplyprint AAR SDK parity constants. +enum SerialParity { + /// No parity + none(0), + + /// Odd parity + odd(1), + + /// Even parity + even(2), + + /// Mark parity + mark(3), + + /// Space parity + space(4); + + /// The integer value matching the AAR SDK constant. + final int value; + const SerialParity(this.value); +} diff --git a/lib/enums/serial_stop_bits.dart b/lib/enums/serial_stop_bits.dart new file mode 100644 index 0000000..3548968 --- /dev/null +++ b/lib/enums/serial_stop_bits.dart @@ -0,0 +1,17 @@ +/// Stop bits setting for serial port communication. +/// +/// Maps to the autoreplyprint AAR SDK stop bits constants. +enum SerialStopBits { + /// 1 stop bit + one(0), + + /// 1.5 stop bits + onePointFive(1), + + /// 2 stop bits + two(2); + + /// The integer value matching the AAR SDK constant. + final int value; + const SerialStopBits(this.value); +} diff --git a/lib/models/printer_exception.dart b/lib/models/printer_exception.dart new file mode 100644 index 0000000..bafb252 --- /dev/null +++ b/lib/models/printer_exception.dart @@ -0,0 +1,24 @@ +/// Custom exception for printer operations. +/// +/// Encapsulates structured error information from the native layer, +/// including error code, message, and optional details. +class PrinterException implements Exception { + /// Machine-readable error code (e.g., 'INVALID_ARGUMENT', 'PORT_OPEN_FAILED'). + final String code; + + /// Human-readable error message. + final String message; + + /// Additional error details (may be null). + final dynamic details; + + /// Creates a [PrinterException] with the given [code] and [message]. + const PrinterException({ + required this.code, + required this.message, + this.details, + }); + + @override + String toString() => 'PrinterException($code): $message'; +} diff --git a/lib/models/printer_port_handle.dart b/lib/models/printer_port_handle.dart new file mode 100644 index 0000000..dc40fec --- /dev/null +++ b/lib/models/printer_port_handle.dart @@ -0,0 +1,44 @@ +/// Close callback type for [PrinterPortHandle]. +typedef ClosePortCallback = Future Function(int handle); + +/// Represents an open printer port handle. +/// +/// Wraps an integer handle and provides a [close] method to release +/// the underlying native port resource. Once closed, the handle +/// becomes invalid. +class PrinterPortHandle { + /// The integer handle returned by the native layer. + final int handle; + + /// Callback to close the port. If null, close() is a no-op. + final ClosePortCallback? _closeCallback; + + /// Whether the port is still valid (not closed). + bool _isValid; + + /// Creates a [PrinterPortHandle] with the given [handle] and optional [closeCallback]. + PrinterPortHandle({ + required this.handle, + ClosePortCallback? closeCallback, + }) : _closeCallback = closeCallback, + _isValid = true; + + /// Returns true if the port is still open and not closed. + bool get isValid => _isValid; + + /// Closes the port and marks the handle as invalid. + /// + /// Subsequent calls to [close] are no-ops if the port is already closed. + Future close() async { + if (!_isValid) { + return; + } + _isValid = false; + if (_closeCallback != null) { + await _closeCallback(handle); + } + } + + @override + String toString() => 'PrinterPortHandle(handle: $handle, valid: $_isValid)'; +} diff --git a/lib/printer.dart b/lib/printer.dart new file mode 100644 index 0000000..81d7754 --- /dev/null +++ b/lib/printer.dart @@ -0,0 +1,227 @@ +import 'enums/multi_byte_encoding.dart'; +import 'enums/printer_alignment.dart'; +import 'enums/serial_flow_control.dart'; +import 'enums/serial_parity.dart'; +import 'enums/serial_stop_bits.dart'; +import 'models/printer_port_handle.dart'; +import 'printer_platform_interface.dart'; + +/// Main entry point for the printer plugin. +/// +/// Provides a thin wrapper around [PrinterPlatform] for port management +/// operations. All methods delegate to the platform interface. +class Printer { + /// Returns the platform version string. + Future getPlatformVersion() { + return PrinterPlatform.instance.getPlatformVersion(); + } + + /// Opens a serial (COM) port with the specified parameters. + /// + /// Returns an integer handle on success. + /// Throws [PrinterException] on failure. + /// + /// Example: + /// ```dart + /// final printer = Printer(); + /// final handle = await printer.openComPort( + /// portName: '/dev/ttyS0', + /// baudRate: 115200, + /// ); + /// ``` + Future openComPort({ + required String portName, + required int baudRate, + int dataBits = 8, + SerialParity parity = SerialParity.none, + SerialStopBits stopBits = SerialStopBits.one, + SerialFlowControl flowControl = SerialFlowControl.none, + bool autoReplyMode = true, + }) { + return PrinterPlatform.instance.openComPort( + portName: portName, + baudRate: baudRate, + dataBits: dataBits, + parity: parity, + stopBits: stopBits, + flowControl: flowControl, + autoReplyMode: autoReplyMode, + ); + } + + /// Opens a USB port with the specified parameters. + /// + /// Returns an integer handle on success. + /// Throws [PrinterException] on failure. + Future openUsbPort({ + required String portName, + bool autoReplyMode = true, + }) { + return PrinterPlatform.instance.openUsbPort( + portName: portName, + autoReplyMode: autoReplyMode, + ); + } + + /// Closes a port by its integer handle. + /// + /// Returns true if successfully closed, false otherwise. + Future closePort(int handle) { + return PrinterPlatform.instance.closePort(handle); + } + + /// Checks if a port is currently opened. + /// + /// Returns true if the port is open, false otherwise. + Future isPortOpened(int handle) { + return PrinterPlatform.instance.isPortOpened(handle); + } + + /// Enumerates available serial (COM) ports. + /// + /// Returns a list of port name strings. + Future> enumComPorts() { + return PrinterPlatform.instance.enumComPorts(); + } + + /// Enumerates available USB ports. + /// + /// Returns a list of port name strings. + Future> enumUsbPorts() { + return PrinterPlatform.instance.enumUsbPorts(); + } + + /// Opens a serial port and returns a [PrinterPortHandle] for safe resource management. + /// + /// The returned handle can be closed via [PrinterPortHandle.close]. + Future openComPortWithHandle({ + required String portName, + required int baudRate, + int dataBits = 8, + SerialParity parity = SerialParity.none, + SerialStopBits stopBits = SerialStopBits.one, + SerialFlowControl flowControl = SerialFlowControl.none, + bool autoReplyMode = true, + }) async { + final handle = await openComPort( + portName: portName, + baudRate: baudRate, + dataBits: dataBits, + parity: parity, + stopBits: stopBits, + flowControl: flowControl, + autoReplyMode: autoReplyMode, + ); + return PrinterPortHandle( + handle: handle, + closeCallback: closePort, + ); + } + + /// Opens a USB port and returns a [PrinterPortHandle] for safe resource management. + /// + /// The returned handle can be closed via [PrinterPortHandle.close]. + Future openUsbPortWithHandle({ + required String portName, + bool autoReplyMode = true, + }) async { + final handle = await openUsbPort( + portName: portName, + autoReplyMode: autoReplyMode, + ); + return PrinterPortHandle( + handle: handle, + closeCallback: closePort, + ); + } + + /// Sets the printer to multi-byte encoding mode. + /// + /// Returns true on success. + Future setMultiByteMode(int handle) { + return PrinterPlatform.instance.setMultiByteMode(handle); + } + + /// Sets the multi-byte character encoding. + /// + /// Returns true on success. + Future setMultiByteEncoding(int handle, MultiByteEncoding encoding) { + return PrinterPlatform.instance.setMultiByteEncoding(handle, encoding); + } + + /// Prints text using UTF-8 encoding. + /// + /// The caller should ensure multi-byte mode is set to UTF-8 before calling: + /// ```dart + /// await printer.setMultiByteMode(handle); + /// await printer.setMultiByteEncoding(handle, MultiByteEncoding.utf8); + /// await printer.printText(handle, 'Hello 中文'); + /// ``` + /// + /// Returns true on success. + Future printText(int handle, String text) { + return PrinterPlatform.instance.printText(handle, text); + } + + /// Sets text alignment. + /// + /// Returns true on success. + Future setAlignment(int handle, PrinterAlignment alignment) { + return PrinterPlatform.instance.setAlignment(handle, alignment); + } + + /// Sets text scale (width and height magnification). + /// + /// Both scales must be between 1 and 8. + /// Returns true on success. + Future setTextScale(int handle, {required int widthScale, required int heightScale}) { + return PrinterPlatform.instance.setTextScale( + handle, + widthScale: widthScale, + heightScale: heightScale, + ); + } + + /// Sets text bold on or off. + /// + /// Returns true on success. + Future setTextBold(int handle, bool bold) { + return PrinterPlatform.instance.setTextBold(handle, bold); + } + + /// Sets text underline level. + /// + /// 0 = no underline, 1 = 1-dot underline, 2 = 2-dot underline. + /// Returns true on success. + Future setTextUnderline(int handle, int underline) { + return PrinterPlatform.instance.setTextUnderline(handle, underline); + } + + /// Feeds paper by specified number of lines. + /// + /// Returns true on success. + Future feedLine(int handle, int numLines) { + return PrinterPlatform.instance.feedLine(handle, numLines); + } + + /// Feeds paper by specified number of dots. + /// + /// Returns true on success. + Future feedDot(int handle, int numDots) { + return PrinterPlatform.instance.feedDot(handle, numDots); + } + + /// Performs a half cut of the paper. + /// + /// Returns true on success. + Future halfCutPaper(int handle) { + return PrinterPlatform.instance.halfCutPaper(handle); + } + + /// Performs a full cut of the paper. + /// + /// Returns true on success. + Future fullCutPaper(int handle) { + return PrinterPlatform.instance.fullCutPaper(handle); + } +} diff --git a/lib/printer_method_channel.dart b/lib/printer_method_channel.dart new file mode 100644 index 0000000..35b9282 --- /dev/null +++ b/lib/printer_method_channel.dart @@ -0,0 +1,339 @@ +import 'package:flutter/foundation.dart'; +import 'package:flutter/services.dart'; + +import 'enums/multi_byte_encoding.dart'; +import 'enums/printer_alignment.dart'; +import 'enums/serial_flow_control.dart'; +import 'enums/serial_parity.dart'; +import 'enums/serial_stop_bits.dart'; +import 'models/printer_exception.dart'; +import 'printer_platform_interface.dart'; + +/// An implementation of [PrinterPlatform] that uses method channels. +class MethodChannelPrinter extends PrinterPlatform { + /// The method channel used to interact with the native platform. + @visibleForTesting + final methodChannel = const MethodChannel('printer'); + + @override + Future getPlatformVersion() async { + final version = await methodChannel.invokeMethod( + 'getPlatformVersion', + ); + return version; + } + + @override + Future openComPort({ + required String portName, + required int baudRate, + int dataBits = 8, + SerialParity parity = SerialParity.none, + SerialStopBits stopBits = SerialStopBits.one, + SerialFlowControl flowControl = SerialFlowControl.none, + bool autoReplyMode = true, + }) async { + try { + final result = await methodChannel.invokeMethod('openComPort', { + 'portName': portName, + 'baudRate': baudRate, + 'dataBits': dataBits, + 'parity': parity.value, + 'stopBits': stopBits.value, + 'flowControl': flowControl.value, + 'autoReplyMode': autoReplyMode ? 1 : 0, + }); + return result!; + } on PlatformException catch (e) { + throw PrinterException( + code: e.code, + message: e.message ?? 'Failed to open COM port', + details: e.details, + ); + } + } + + @override + Future openUsbPort({ + required String portName, + bool autoReplyMode = true, + }) async { + try { + final result = await methodChannel.invokeMethod('openUsbPort', { + 'portName': portName, + 'autoReplyMode': autoReplyMode ? 1 : 0, + }); + return result!; + } on PlatformException catch (e) { + throw PrinterException( + code: e.code, + message: e.message ?? 'Failed to open USB port', + details: e.details, + ); + } + } + + @override + Future closePort(int handle) async { + try { + final result = await methodChannel.invokeMethod( + 'closePort', + {'handle': handle}, + ); + return result ?? false; + } on PlatformException catch (e) { + throw PrinterException( + code: e.code, + message: e.message ?? 'Failed to close port', + details: e.details, + ); + } + } + + @override + Future isPortOpened(int handle) async { + try { + final result = await methodChannel.invokeMethod( + 'isPortOpened', + {'handle': handle}, + ); + return result ?? false; + } on PlatformException catch (e) { + throw PrinterException( + code: e.code, + message: e.message ?? 'Failed to check port status', + details: e.details, + ); + } + } + + @override + Future> enumComPorts() async { + try { + final result = await methodChannel.invokeMethod('enumComPorts'); + return (result ?? []).map((e) => e.toString()).toList(); + } on PlatformException catch (e) { + throw PrinterException( + code: e.code, + message: e.message ?? 'Failed to enumerate COM ports', + details: e.details, + ); + } + } + + @override + Future> enumUsbPorts() async { + try { + final result = await methodChannel.invokeMethod('enumUsbPorts'); + return (result ?? []).map((e) => e.toString()).toList(); + } on PlatformException catch (e) { + throw PrinterException( + code: e.code, + message: e.message ?? 'Failed to enumerate USB ports', + details: e.details, + ); + } + } + + /// Sets the printer to multi-byte encoding mode. + @override + Future setMultiByteMode(int handle) async { + try { + final result = await methodChannel.invokeMethod( + 'setMultiByteMode', + {'handle': handle}, + ); + return result ?? false; + } on PlatformException catch (e) { + throw PrinterException( + code: e.code, + message: e.message ?? 'Failed to set multi-byte mode', + details: e.details, + ); + } + } + + /// Sets the multi-byte character encoding. + @override + Future setMultiByteEncoding(int handle, MultiByteEncoding encoding) async { + try { + final result = await methodChannel.invokeMethod( + 'setMultiByteEncoding', + {'handle': handle, 'encoding': encoding.value}, + ); + return result ?? false; + } on PlatformException catch (e) { + throw PrinterException( + code: e.code, + message: e.message ?? 'Failed to set multi-byte encoding', + details: e.details, + ); + } + } + + /// Prints text using UTF-8 encoding. + @override + Future printText(int handle, String text) async { + try { + final result = await methodChannel.invokeMethod( + 'printText', + {'handle': handle, 'text': text}, + ); + return result ?? false; + } on PlatformException catch (e) { + throw PrinterException( + code: e.code, + message: e.message ?? 'Failed to print text', + details: e.details, + ); + } + } + + /// Sets text alignment. + @override + Future setAlignment(int handle, PrinterAlignment alignment) async { + try { + final result = await methodChannel.invokeMethod( + 'setAlignment', + {'handle': handle, 'alignment': alignment.value}, + ); + return result ?? false; + } on PlatformException catch (e) { + throw PrinterException( + code: e.code, + message: e.message ?? 'Failed to set alignment', + details: e.details, + ); + } + } + + /// Sets text scale (width and height magnification). + @override + Future setTextScale(int handle, {required int widthScale, required int heightScale}) async { + try { + final result = await methodChannel.invokeMethod( + 'setTextScale', + { + 'handle': handle, + 'widthScale': widthScale, + 'heightScale': heightScale, + }, + ); + return result ?? false; + } on PlatformException catch (e) { + throw PrinterException( + code: e.code, + message: e.message ?? 'Failed to set text scale', + details: e.details, + ); + } + } + + /// Sets text bold on or off. + @override + Future setTextBold(int handle, bool bold) async { + try { + final result = await methodChannel.invokeMethod( + 'setTextBold', + {'handle': handle, 'bold': bold ? 1 : 0}, + ); + return result ?? false; + } on PlatformException catch (e) { + throw PrinterException( + code: e.code, + message: e.message ?? 'Failed to set text bold', + details: e.details, + ); + } + } + + /// Sets text underline level. + @override + Future setTextUnderline(int handle, int underline) async { + try { + final result = await methodChannel.invokeMethod( + 'setTextUnderline', + {'handle': handle, 'underline': underline}, + ); + return result ?? false; + } on PlatformException catch (e) { + throw PrinterException( + code: e.code, + message: e.message ?? 'Failed to set text underline', + details: e.details, + ); + } + } + + /// Feeds paper by specified number of lines. + @override + Future feedLine(int handle, int numLines) async { + try { + final result = await methodChannel.invokeMethod( + 'feedLine', + {'handle': handle, 'numLines': numLines}, + ); + return result ?? false; + } on PlatformException catch (e) { + throw PrinterException( + code: e.code, + message: e.message ?? 'Failed to feed line', + details: e.details, + ); + } + } + + /// Feeds paper by specified number of dots. + @override + Future feedDot(int handle, int numDots) async { + try { + final result = await methodChannel.invokeMethod( + 'feedDot', + {'handle': handle, 'numDots': numDots}, + ); + return result ?? false; + } on PlatformException catch (e) { + throw PrinterException( + code: e.code, + message: e.message ?? 'Failed to feed dot', + details: e.details, + ); + } + } + + /// Performs a half cut of the paper. + @override + Future halfCutPaper(int handle) async { + try { + final result = await methodChannel.invokeMethod( + 'halfCutPaper', + {'handle': handle}, + ); + return result ?? false; + } on PlatformException catch (e) { + throw PrinterException( + code: e.code, + message: e.message ?? 'Failed to half cut paper', + details: e.details, + ); + } + } + + /// Performs a full cut of the paper. + @override + Future fullCutPaper(int handle) async { + try { + final result = await methodChannel.invokeMethod( + 'fullCutPaper', + {'handle': handle}, + ); + return result ?? false; + } on PlatformException catch (e) { + throw PrinterException( + code: e.code, + message: e.message ?? 'Failed to full cut paper', + details: e.details, + ); + } + } +} diff --git a/lib/printer_platform_interface.dart b/lib/printer_platform_interface.dart new file mode 100644 index 0000000..62b7e54 --- /dev/null +++ b/lib/printer_platform_interface.dart @@ -0,0 +1,173 @@ +import 'package:plugin_platform_interface/plugin_platform_interface.dart'; + +import 'enums/multi_byte_encoding.dart'; +import 'enums/printer_alignment.dart'; +import 'enums/serial_flow_control.dart'; +import 'enums/serial_parity.dart'; +import 'enums/serial_stop_bits.dart'; +import 'printer_method_channel.dart'; + +/// Platform interface for printer operations. +/// +/// Concrete implementations (e.g., [MethodChannelPrinter]) provide +/// the actual platform-specific behavior. +abstract class PrinterPlatform extends PlatformInterface { + /// Constructs a PrinterPlatform. + PrinterPlatform() : super(token: _token); + + static final Object _token = Object(); + + static PrinterPlatform _instance = MethodChannelPrinter(); + + /// The default instance of [PrinterPlatform] to use. + /// + /// Defaults to [MethodChannelPrinter]. + static PrinterPlatform get instance => _instance; + + /// Platform-specific implementations should set this with their own + /// platform-specific class that extends [PrinterPlatform] when + /// they register themselves. + static set instance(PrinterPlatform instance) { + PlatformInterface.verifyToken(instance, _token); + _instance = instance; + } + + /// Returns the platform version string. + Future getPlatformVersion() { + throw UnimplementedError('getPlatformVersion() has not been implemented.'); + } + + /// Opens a serial (COM) port with the specified parameters. + /// + /// Returns an integer handle on success. + /// Throws [PrinterException] on failure. + Future openComPort({ + required String portName, + required int baudRate, + int dataBits = 8, + SerialParity parity = SerialParity.none, + SerialStopBits stopBits = SerialStopBits.one, + SerialFlowControl flowControl = SerialFlowControl.none, + bool autoReplyMode = true, + }) { + throw UnimplementedError('openComPort() has not been implemented.'); + } + + /// Opens a USB port with the specified parameters. + /// + /// Returns an integer handle on success. + /// Throws [PrinterException] on failure. + Future openUsbPort({ + required String portName, + bool autoReplyMode = true, + }) { + throw UnimplementedError('openUsbPort() has not been implemented.'); + } + + /// Closes a port by its integer handle. + /// + /// Returns true if successfully closed, false otherwise. + Future closePort(int handle) { + throw UnimplementedError('closePort() has not been implemented.'); + } + + /// Checks if a port is currently opened. + /// + /// Returns true if the port is open, false otherwise. + Future isPortOpened(int handle) { + throw UnimplementedError('isPortOpened() has not been implemented.'); + } + + /// Enumerates available serial (COM) ports. + /// + /// Returns a list of port name strings. + Future> enumComPorts() { + throw UnimplementedError('enumComPorts() has not been implemented.'); + } + + /// Enumerates available USB ports. + /// + /// Returns a list of port name strings. + Future> enumUsbPorts() { + throw UnimplementedError('enumUsbPorts() has not been implemented.'); + } + + /// Sets the printer to multi-byte encoding mode. + /// + /// Returns true on success. + Future setMultiByteMode(int handle) { + throw UnimplementedError('setMultiByteMode() has not been implemented.'); + } + + /// Sets the multi-byte character encoding. + /// + /// Returns true on success. + Future setMultiByteEncoding(int handle, MultiByteEncoding encoding) { + throw UnimplementedError('setMultiByteEncoding() has not been implemented.'); + } + + /// Prints text using UTF-8 encoding. + /// + /// Returns true on success. + Future printText(int handle, String text) { + throw UnimplementedError('printText() has not been implemented.'); + } + + /// Sets text alignment. + /// + /// Returns true on success. + Future setAlignment(int handle, PrinterAlignment alignment) { + throw UnimplementedError('setAlignment() has not been implemented.'); + } + + /// Sets text scale (width and height magnification). + /// + /// Both scales must be between 1 and 8. + /// Returns true on success. + Future setTextScale(int handle, {required int widthScale, required int heightScale}) { + throw UnimplementedError('setTextScale() has not been implemented.'); + } + + /// Sets text bold on or off. + /// + /// Returns true on success. + Future setTextBold(int handle, bool bold) { + throw UnimplementedError('setTextBold() has not been implemented.'); + } + + /// Sets text underline level. + /// + /// 0 = no underline, 1 = 1-dot underline, 2 = 2-dot underline. + /// Returns true on success. + Future setTextUnderline(int handle, int underline) { + throw UnimplementedError('setTextUnderline() has not been implemented.'); + } + + /// Feeds paper by specified number of lines. + /// + /// Returns true on success. + Future feedLine(int handle, int numLines) { + throw UnimplementedError('feedLine() has not been implemented.'); + } + + /// Feeds paper by specified number of dots. + /// + /// Returns true on success. + Future feedDot(int handle, int numDots) { + throw UnimplementedError('feedDot() has not been implemented.'); + } + + /// Performs a half cut of the paper. + /// + /// Returns true on success. + Future halfCutPaper(int handle) { + throw UnimplementedError('halfCutPaper() has not been implemented.'); + } + + /// Performs a full cut of the paper. + /// + /// Returns true on success. + Future fullCutPaper(int handle) { + throw UnimplementedError('fullCutPaper() has not been implemented.'); + } +} diff --git a/pubspec.yaml b/pubspec.yaml new file mode 100644 index 0000000..34f94ef --- /dev/null +++ b/pubspec.yaml @@ -0,0 +1,70 @@ +name: printer +description: "A new Flutter project." +version: 0.0.1 +homepage: + +environment: + sdk: ^3.11.4 + flutter: '>=3.3.0' + +dependencies: + flutter: + sdk: flutter + plugin_platform_interface: ^2.0.2 + +dev_dependencies: + flutter_test: + sdk: flutter + flutter_lints: ^6.0.0 + +# For information on the generic Dart part of this file, see the +# following page: https://dart.dev/tools/pub/pubspec + +# The following section is specific to Flutter packages. +flutter: + # This section identifies this Flutter project as a plugin project. + # The 'pluginClass' specifies the class (in Java, Kotlin, Swift, Objective-C, etc.) + # which should be registered in the plugin registry. This is required for + # using method channels. + # The Android 'package' specifies package in which the registered class is. + # This is required for using method channels on Android. + # The 'ffiPlugin' specifies that native code should be built and bundled. + # This is required for using `dart:ffi`. + # All these are used by the tooling to maintain consistency when + # adding or updating assets for this project. + plugin: + platforms: + android: + package: com.xiarui.printer + pluginClass: PrinterPlugin + + # To add assets to your plugin package, add an assets section, like this: + # assets: + # - images/a_dot_burr.jpeg + # - images/a_dot_ham.jpeg + # + # For details regarding assets in packages, see + # https://flutter.dev/to/asset-from-package + # + # An image asset can refer to one or more resolution-specific "variants", see + # https://flutter.dev/to/resolution-aware-images + + # To add custom fonts to your plugin package, add a fonts section here, + # in this "flutter" section. Each entry in this list should have a + # "family" key with the font family name, and a "fonts" key with a + # list giving the asset and other descriptors for the font. For + # example: + # fonts: + # - family: Schyler + # fonts: + # - asset: fonts/Schyler-Regular.ttf + # - asset: fonts/Schyler-Italic.ttf + # style: italic + # - family: Trajan Pro + # fonts: + # - asset: fonts/TrajanPro.ttf + # - asset: fonts/TrajanPro_Bold.ttf + # weight: 700 + # + # For details regarding fonts in packages, see + # https://flutter.dev/to/font-from-package diff --git a/test/printer_method_channel_test.dart b/test/printer_method_channel_test.dart new file mode 100644 index 0000000..e6e35e9 --- /dev/null +++ b/test/printer_method_channel_test.dart @@ -0,0 +1,297 @@ +import 'package:flutter/services.dart'; +import 'package:flutter_test/flutter_test.dart'; +import 'package:printer/printer_method_channel.dart'; +import 'package:printer/enums/multi_byte_encoding.dart'; +import 'package:printer/enums/printer_alignment.dart'; +import 'package:printer/enums/serial_flow_control.dart'; +import 'package:printer/enums/serial_parity.dart'; +import 'package:printer/enums/serial_stop_bits.dart'; +import 'package:printer/models/printer_exception.dart'; + +void main() { + TestWidgetsFlutterBinding.ensureInitialized(); + + MethodChannelPrinter platform = MethodChannelPrinter(); + const MethodChannel channel = MethodChannel('printer'); + + setUp(() { + TestDefaultBinaryMessengerBinding.instance.defaultBinaryMessenger + .setMockMethodCallHandler(channel, (MethodCall methodCall) async { + if (methodCall.method == 'getPlatformVersion') { + return '42'; + } + if (methodCall.method == 'openComPort') { + return 1; + } + if (methodCall.method == 'openUsbPort') { + return 2; + } + if (methodCall.method == 'closePort') { + return true; + } + if (methodCall.method == 'isPortOpened') { + return true; + } + if (methodCall.method == 'enumComPorts') { + return ['/dev/ttyS0', '/dev/ttyS1']; + } + if (methodCall.method == 'enumUsbPorts') { + return ['USB_Printer_0']; + } + if (methodCall.method == 'setMultiByteMode') return true; + if (methodCall.method == 'setMultiByteEncoding') return true; + if (methodCall.method == 'printText') return true; + if (methodCall.method == 'setAlignment') return true; + if (methodCall.method == 'setTextScale') return true; + if (methodCall.method == 'setTextBold') return true; + if (methodCall.method == 'setTextUnderline') return true; + if (methodCall.method == 'feedLine') return true; + if (methodCall.method == 'feedDot') return true; + if (methodCall.method == 'halfCutPaper') return true; + if (methodCall.method == 'fullCutPaper') return true; + return null; + }); + }); + + tearDown(() { + TestDefaultBinaryMessengerBinding.instance.defaultBinaryMessenger + .setMockMethodCallHandler(channel, null); + }); + + test('getPlatformVersion', () async { + expect(await platform.getPlatformVersion(), '42'); + }); + + test('openComPort returns handle', () async { + final handle = await platform.openComPort( + portName: '/dev/ttyS0', + baudRate: 115200, + parity: SerialParity.none, + stopBits: SerialStopBits.one, + flowControl: SerialFlowControl.none, + ); + + expect(handle, 1); + }); + + test('openComPort passes correct parameters', () async { + late Map capturedArgs; + + TestDefaultBinaryMessengerBinding.instance.defaultBinaryMessenger + .setMockMethodCallHandler(channel, (MethodCall methodCall) async { + if (methodCall.method == 'openComPort') { + capturedArgs = methodCall.arguments as Map; + return 42; + } + return null; + }); + + await platform.openComPort( + portName: '/dev/ttyS0', + baudRate: 9600, + dataBits: 7, + parity: SerialParity.even, + stopBits: SerialStopBits.two, + flowControl: SerialFlowControl.rtsCts, + autoReplyMode: false, + ); + + expect(capturedArgs['portName'], '/dev/ttyS0'); + expect(capturedArgs['baudRate'], 9600); + expect(capturedArgs['dataBits'], 7); + expect(capturedArgs['parity'], 2); // SerialParity.even + expect(capturedArgs['stopBits'], 2); // SerialStopBits.two + expect(capturedArgs['flowControl'], 2); // SerialFlowControl.rtsCts + expect(capturedArgs['autoReplyMode'], 0); // false + }); + + test('openUsbPort returns handle', () async { + final handle = await platform.openUsbPort(portName: 'USB_Printer_0'); + + expect(handle, 2); + }); + + test('closePort returns true', () async { + final result = await platform.closePort(1); + + expect(result, true); + }); + + test('isPortOpened returns true', () async { + final result = await platform.isPortOpened(1); + + expect(result, true); + }); + + test('enumComPorts returns list of strings', () async { + final ports = await platform.enumComPorts(); + + expect(ports, isA>()); + expect(ports, contains('/dev/ttyS0')); + expect(ports, contains('/dev/ttyS1')); + }); + + test('enumUsbPorts returns list of strings', () async { + final ports = await platform.enumUsbPorts(); + + expect(ports, isA>()); + expect(ports, contains('USB_Printer_0')); + }); + + test('PlatformException converts to PrinterException on openComPort', () async { + TestDefaultBinaryMessengerBinding.instance.defaultBinaryMessenger + .setMockMethodCallHandler(channel, (MethodCall methodCall) async { + if (methodCall.method == 'openComPort') { + throw PlatformException( + code: 'PORT_OPEN_FAILED', + message: 'Failed to open port', + details: {'port': '/dev/ttyS0'}, + ); + } + return null; + }); + + expect( + () async => platform.openComPort( + portName: '/dev/ttyS0', + baudRate: 115200, + ), + throwsA(isA() + .having((e) => e.code, 'code', 'PORT_OPEN_FAILED') + .having((e) => e.message, 'message', 'Failed to open port')), + ); + }); + + test('PlatformException converts to PrinterException on closePort', () async { + TestDefaultBinaryMessengerBinding.instance.defaultBinaryMessenger + .setMockMethodCallHandler(channel, (MethodCall methodCall) async { + if (methodCall.method == 'closePort') { + throw PlatformException( + code: 'PORT_CLOSE_FAILED', + message: 'Failed to close port', + ); + } + return null; + }); + + expect( + () async => platform.closePort(1), + throwsA(isA() + .having((e) => e.code, 'code', 'PORT_CLOSE_FAILED')), + ); + }); + + test('printText passes text parameter', () async { + late Map capturedArgs; + TestDefaultBinaryMessengerBinding.instance.defaultBinaryMessenger + .setMockMethodCallHandler(channel, (MethodCall methodCall) async { + if (methodCall.method == 'printText') { + capturedArgs = methodCall.arguments as Map; + return true; + } + return null; + }); + + await platform.printText(1, 'Hello 中文'); + expect(capturedArgs['handle'], 1); + expect(capturedArgs['text'], 'Hello 中文'); + }); + + test('setAlignment passes alignment.value', () async { + late Map capturedArgs; + TestDefaultBinaryMessengerBinding.instance.defaultBinaryMessenger + .setMockMethodCallHandler(channel, (MethodCall methodCall) async { + if (methodCall.method == 'setAlignment') { + capturedArgs = methodCall.arguments as Map; + return true; + } + return null; + }); + + await platform.setAlignment(1, PrinterAlignment.center); + expect(capturedArgs['alignment'], 1); // PrinterAlignment.center.value + }); + + test('setMultiByteEncoding passes encoding.value', () async { + late Map capturedArgs; + TestDefaultBinaryMessengerBinding.instance.defaultBinaryMessenger + .setMockMethodCallHandler(channel, (MethodCall methodCall) async { + if (methodCall.method == 'setMultiByteEncoding') { + capturedArgs = methodCall.arguments as Map; + return true; + } + return null; + }); + + await platform.setMultiByteEncoding(1, MultiByteEncoding.utf8); + expect(capturedArgs['encoding'], 1); // MultiByteEncoding.utf8.value + }); + + test('setTextBold converts bool to int 0/1', () async { + late Map capturedArgs; + TestDefaultBinaryMessengerBinding.instance.defaultBinaryMessenger + .setMockMethodCallHandler(channel, (MethodCall methodCall) async { + if (methodCall.method == 'setTextBold') { + capturedArgs = methodCall.arguments as Map; + return true; + } + return null; + }); + + await platform.setTextBold(1, true); + expect(capturedArgs['bold'], 1); + + await platform.setTextBold(1, false); + expect(capturedArgs['bold'], 0); + }); + + test('feedDot passes numDots parameter', () async { + late Map capturedArgs; + TestDefaultBinaryMessengerBinding.instance.defaultBinaryMessenger + .setMockMethodCallHandler(channel, (MethodCall methodCall) async { + if (methodCall.method == 'feedDot') { + capturedArgs = methodCall.arguments as Map; + return true; + } + return null; + }); + + await platform.feedDot(1, 100); + expect(capturedArgs['handle'], 1); + expect(capturedArgs['numDots'], 100); + }); + + test('fullCutPaper passes handle parameter', () async { + late Map capturedArgs; + TestDefaultBinaryMessengerBinding.instance.defaultBinaryMessenger + .setMockMethodCallHandler(channel, (MethodCall methodCall) async { + if (methodCall.method == 'fullCutPaper') { + capturedArgs = methodCall.arguments as Map; + return true; + } + return null; + }); + + await platform.fullCutPaper(1); + expect(capturedArgs['handle'], 1); + }); + + test('PlatformException converts to PrinterException on printText', () async { + TestDefaultBinaryMessengerBinding.instance.defaultBinaryMessenger + .setMockMethodCallHandler(channel, (MethodCall methodCall) async { + if (methodCall.method == 'printText') { + throw PlatformException( + code: 'PRINT_FAILED', + message: 'Failed to print text', + ); + } + return null; + }); + + expect( + () async => platform.printText(1, 'test'), + throwsA(isA() + .having((e) => e.code, 'code', 'PRINT_FAILED')), + ); + }); +} diff --git a/test/printer_test.dart b/test/printer_test.dart new file mode 100644 index 0000000..3a6cddc --- /dev/null +++ b/test/printer_test.dart @@ -0,0 +1,264 @@ +import 'package:flutter_test/flutter_test.dart'; +import 'package:printer/printer.dart'; +import 'package:printer/printer_platform_interface.dart'; +import 'package:printer/printer_method_channel.dart'; +import 'package:printer/enums/multi_byte_encoding.dart'; +import 'package:printer/enums/printer_alignment.dart'; +import 'package:printer/enums/serial_flow_control.dart'; +import 'package:printer/enums/serial_parity.dart'; +import 'package:printer/enums/serial_stop_bits.dart'; +import 'package:plugin_platform_interface/plugin_platform_interface.dart'; + +class MockPrinterPlatform + with MockPlatformInterfaceMixin + implements PrinterPlatform { + @override + Future getPlatformVersion() => Future.value('42'); + + @override + Future openComPort({ + required String portName, + required int baudRate, + int dataBits = 8, + SerialParity parity = SerialParity.none, + SerialStopBits stopBits = SerialStopBits.one, + SerialFlowControl flowControl = SerialFlowControl.none, + bool autoReplyMode = true, + }) => Future.value(1); + + @override + Future openUsbPort({ + required String portName, + bool autoReplyMode = true, + }) => Future.value(2); + + @override + Future closePort(int handle) => Future.value(true); + + @override + Future isPortOpened(int handle) => Future.value(true); + + @override + Future> enumComPorts() => Future.value(['/dev/ttyS0']); + + @override + Future> enumUsbPorts() => Future.value(['USB_Printer_0']); + + @override + Future setMultiByteMode(int handle) => Future.value(true); + + @override + Future setMultiByteEncoding(int handle, MultiByteEncoding encoding) => Future.value(true); + + @override + Future printText(int handle, String text) => Future.value(true); + + @override + Future setAlignment(int handle, PrinterAlignment alignment) => Future.value(true); + + @override + Future setTextScale(int handle, {required int widthScale, required int heightScale}) => Future.value(true); + + @override + Future setTextBold(int handle, bool bold) => Future.value(true); + + @override + Future setTextUnderline(int handle, int underline) => Future.value(true); + + @override + Future feedLine(int handle, int numLines) => Future.value(true); + + @override + Future feedDot(int handle, int numDots) => Future.value(true); + + @override + Future halfCutPaper(int handle) => Future.value(true); + + @override + Future fullCutPaper(int handle) => Future.value(true); +} + +void main() { + final PrinterPlatform initialPlatform = PrinterPlatform.instance; + + test('$MethodChannelPrinter is the default instance', () { + expect(initialPlatform, isInstanceOf()); + }); + + test('getPlatformVersion', () async { + Printer printerPlugin = Printer(); + MockPrinterPlatform fakePlatform = MockPrinterPlatform(); + PrinterPlatform.instance = fakePlatform; + + expect(await printerPlugin.getPlatformVersion(), '42'); + }); + + test('openComPort delegates to platform interface', () async { + final printer = Printer(); + final fakePlatform = MockPrinterPlatform(); + PrinterPlatform.instance = fakePlatform; + + final handle = await printer.openComPort( + portName: '/dev/ttyS0', + baudRate: 115200, + ); + + expect(handle, 1); + }); + + test('openUsbPort delegates to platform interface', () async { + final printer = Printer(); + final fakePlatform = MockPrinterPlatform(); + PrinterPlatform.instance = fakePlatform; + + final handle = await printer.openUsbPort(portName: 'USB_Printer_0'); + + expect(handle, 2); + }); + + test('closePort delegates to platform interface', () async { + final printer = Printer(); + final fakePlatform = MockPrinterPlatform(); + PrinterPlatform.instance = fakePlatform; + + final result = await printer.closePort(1); + + expect(result, true); + }); + + test('isPortOpened delegates to platform interface', () async { + final printer = Printer(); + final fakePlatform = MockPrinterPlatform(); + PrinterPlatform.instance = fakePlatform; + + final result = await printer.isPortOpened(1); + + expect(result, true); + }); + + test('enumComPorts delegates to platform interface', () async { + final printer = Printer(); + final fakePlatform = MockPrinterPlatform(); + PrinterPlatform.instance = fakePlatform; + + final ports = await printer.enumComPorts(); + + expect(ports, ['/dev/ttyS0']); + }); + + test('enumUsbPorts delegates to platform interface', () async { + final printer = Printer(); + final fakePlatform = MockPrinterPlatform(); + PrinterPlatform.instance = fakePlatform; + + final ports = await printer.enumUsbPorts(); + + expect(ports, ['USB_Printer_0']); + }); + + test('openComPortWithHandle returns valid PrinterPortHandle', () async { + final printer = Printer(); + final fakePlatform = MockPrinterPlatform(); + PrinterPlatform.instance = fakePlatform; + + final portHandle = await printer.openComPortWithHandle( + portName: '/dev/ttyS0', + baudRate: 115200, + ); + + expect(portHandle.handle, 1); + expect(portHandle.isValid, true); + await portHandle.close(); + expect(portHandle.isValid, false); + }); + + test('openUsbPortWithHandle returns valid PrinterPortHandle', () async { + final printer = Printer(); + final fakePlatform = MockPrinterPlatform(); + PrinterPlatform.instance = fakePlatform; + + final portHandle = await printer.openUsbPortWithHandle( + portName: 'USB_Printer_0', + ); + + expect(portHandle.handle, 2); + expect(portHandle.isValid, true); + }); + + test('setMultiByteMode delegates to platform interface', () async { + final printer = Printer(); + final fakePlatform = MockPrinterPlatform(); + PrinterPlatform.instance = fakePlatform; + expect(await printer.setMultiByteMode(1), true); + }); + + test('setMultiByteEncoding delegates to platform interface', () async { + final printer = Printer(); + final fakePlatform = MockPrinterPlatform(); + PrinterPlatform.instance = fakePlatform; + expect(await printer.setMultiByteEncoding(1, MultiByteEncoding.utf8), true); + }); + + test('printText delegates to platform interface', () async { + final printer = Printer(); + final fakePlatform = MockPrinterPlatform(); + PrinterPlatform.instance = fakePlatform; + expect(await printer.printText(1, 'Hello 中文'), true); + }); + + test('setAlignment delegates to platform interface', () async { + final printer = Printer(); + final fakePlatform = MockPrinterPlatform(); + PrinterPlatform.instance = fakePlatform; + expect(await printer.setAlignment(1, PrinterAlignment.center), true); + }); + + test('setTextScale delegates to platform interface', () async { + final printer = Printer(); + final fakePlatform = MockPrinterPlatform(); + PrinterPlatform.instance = fakePlatform; + expect(await printer.setTextScale(1, widthScale: 2, heightScale: 2), true); + }); + + test('setTextBold delegates to platform interface', () async { + final printer = Printer(); + final fakePlatform = MockPrinterPlatform(); + PrinterPlatform.instance = fakePlatform; + expect(await printer.setTextBold(1, true), true); + }); + + test('setTextUnderline delegates to platform interface', () async { + final printer = Printer(); + final fakePlatform = MockPrinterPlatform(); + PrinterPlatform.instance = fakePlatform; + expect(await printer.setTextUnderline(1, 1), true); + }); + + test('feedLine delegates to platform interface', () async { + final printer = Printer(); + final fakePlatform = MockPrinterPlatform(); + PrinterPlatform.instance = fakePlatform; + expect(await printer.feedLine(1, 5), true); + }); + + test('feedDot delegates to platform interface', () async { + final printer = Printer(); + final fakePlatform = MockPrinterPlatform(); + PrinterPlatform.instance = fakePlatform; + expect(await printer.feedDot(1, 100), true); + }); + + test('halfCutPaper delegates to platform interface', () async { + final printer = Printer(); + final fakePlatform = MockPrinterPlatform(); + PrinterPlatform.instance = fakePlatform; + expect(await printer.halfCutPaper(1), true); + }); + + test('fullCutPaper delegates to platform interface', () async { + final printer = Printer(); + final fakePlatform = MockPrinterPlatform(); + PrinterPlatform.instance = fakePlatform; + expect(await printer.fullCutPaper(1), true); + }); +}