asList(
67 | new MainReactPackage(),
68 | new SunmiV2PrinterPackage()
69 | );
70 | }
71 | ```
72 |
73 | **Step 3:**
74 |
75 | Import in React-Native:
76 |
77 | ```javascript
78 | import SunmiV2Printer from 'react-native-sunmi-v2-printer';
79 | ```
80 |
81 | ## API
82 |
83 | ### Constants
84 |
85 | | Name | Type| Description |
86 | |:-----:|:-----:|:-----------:|
87 | | Constants | string | Printer's status |
88 | | hasPrinter | boolean | Is printer available |
89 | | printerVersion | string | Printer's version |
90 | | printerSerialNo | string | Printer's serial number |
91 | | printerModal | string | Printer's model |
92 |
93 | ### Printer Status
94 |
95 | | Name | Description |
96 | |:-----:|:-----------:|
97 | | OUT_OF_PAPER_ACTION | Printer of paper |
98 | | ERROR_ACTION | Printing error |
99 | | NORMAL_ACTION | Printing normal |
100 | | COVER_OPEN_ACTION | Printer's cover has open |
101 | | COVER_ERROR_ACTION | Printer's cover is unusal |
102 | | KNIFE_ERROR_1_ACTION | 切刀异常1-卡切刀 |
103 | | KNIFE_ERROR_2_ACTION | 切刀异常2-切刀修复 |
104 | | OVER_HEATING_ACITON | Printer is overheat |
105 | | FIRMWARE_UPDATING_ACITON | Upgrade printer's firmware |
106 |
107 | ### Example
108 |
109 | Please check on `example/` floder
110 |
--------------------------------------------------------------------------------
/example/android/gradlew.bat:
--------------------------------------------------------------------------------
1 | @rem
2 | @rem Copyright 2015 the original author or authors.
3 | @rem
4 | @rem Licensed under the Apache License, Version 2.0 (the "License");
5 | @rem you may not use this file except in compliance with the License.
6 | @rem You may obtain a copy of the License at
7 | @rem
8 | @rem https://www.apache.org/licenses/LICENSE-2.0
9 | @rem
10 | @rem Unless required by applicable law or agreed to in writing, software
11 | @rem distributed under the License is distributed on an "AS IS" BASIS,
12 | @rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | @rem See the License for the specific language governing permissions and
14 | @rem limitations under the License.
15 | @rem
16 |
17 | @if "%DEBUG%" == "" @echo off
18 | @rem ##########################################################################
19 | @rem
20 | @rem Gradle startup script for Windows
21 | @rem
22 | @rem ##########################################################################
23 |
24 | @rem Set local scope for the variables with windows NT shell
25 | if "%OS%"=="Windows_NT" setlocal
26 |
27 | set DIRNAME=%~dp0
28 | if "%DIRNAME%" == "" set DIRNAME=.
29 | set APP_BASE_NAME=%~n0
30 | set APP_HOME=%DIRNAME%
31 |
32 | @rem Resolve any "." and ".." in APP_HOME to make it shorter.
33 | for %%i in ("%APP_HOME%") do set APP_HOME=%%~fi
34 |
35 | @rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
36 | set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m"
37 |
38 | @rem Find java.exe
39 | if defined JAVA_HOME goto findJavaFromJavaHome
40 |
41 | set JAVA_EXE=java.exe
42 | %JAVA_EXE% -version >NUL 2>&1
43 | if "%ERRORLEVEL%" == "0" goto init
44 |
45 | echo.
46 | echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
47 | echo.
48 | echo Please set the JAVA_HOME variable in your environment to match the
49 | echo location of your Java installation.
50 |
51 | goto fail
52 |
53 | :findJavaFromJavaHome
54 | set JAVA_HOME=%JAVA_HOME:"=%
55 | set JAVA_EXE=%JAVA_HOME%/bin/java.exe
56 |
57 | if exist "%JAVA_EXE%" goto init
58 |
59 | echo.
60 | echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME%
61 | echo.
62 | echo Please set the JAVA_HOME variable in your environment to match the
63 | echo location of your Java installation.
64 |
65 | goto fail
66 |
67 | :init
68 | @rem Get command-line arguments, handling Windows variants
69 |
70 | if not "%OS%" == "Windows_NT" goto win9xME_args
71 |
72 | :win9xME_args
73 | @rem Slurp the command line arguments.
74 | set CMD_LINE_ARGS=
75 | set _SKIP=2
76 |
77 | :win9xME_args_slurp
78 | if "x%~1" == "x" goto execute
79 |
80 | set CMD_LINE_ARGS=%*
81 |
82 | :execute
83 | @rem Setup the command line
84 |
85 | set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar
86 |
87 | @rem Execute Gradle
88 | "%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS%
89 |
90 | :end
91 | @rem End local scope for the variables with windows NT shell
92 | if "%ERRORLEVEL%"=="0" goto mainEnd
93 |
94 | :fail
95 | rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of
96 | rem the _cmd.exe /c_ return code!
97 | if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1
98 | exit /b 1
99 |
100 | :mainEnd
101 | if "%OS%"=="Windows_NT" endlocal
102 |
103 | :omega
104 |
--------------------------------------------------------------------------------
/example/android/app/src/debug/java/com/example/ReactNativeFlipper.java:
--------------------------------------------------------------------------------
1 | /**
2 | * Copyright (c) Facebook, Inc. and its affiliates.
3 | *
4 | * This source code is licensed under the MIT license found in the LICENSE file in the root
5 | * directory of this source tree.
6 | */
7 | package com.example;
8 |
9 | import android.content.Context;
10 | import com.facebook.flipper.android.AndroidFlipperClient;
11 | import com.facebook.flipper.android.utils.FlipperUtils;
12 | import com.facebook.flipper.core.FlipperClient;
13 | import com.facebook.flipper.plugins.crashreporter.CrashReporterPlugin;
14 | import com.facebook.flipper.plugins.databases.DatabasesFlipperPlugin;
15 | import com.facebook.flipper.plugins.fresco.FrescoFlipperPlugin;
16 | import com.facebook.flipper.plugins.inspector.DescriptorMapping;
17 | import com.facebook.flipper.plugins.inspector.InspectorFlipperPlugin;
18 | import com.facebook.flipper.plugins.network.FlipperOkhttpInterceptor;
19 | import com.facebook.flipper.plugins.network.NetworkFlipperPlugin;
20 | import com.facebook.flipper.plugins.react.ReactFlipperPlugin;
21 | import com.facebook.flipper.plugins.sharedpreferences.SharedPreferencesFlipperPlugin;
22 | import com.facebook.react.ReactInstanceManager;
23 | import com.facebook.react.bridge.ReactContext;
24 | import com.facebook.react.modules.network.NetworkingModule;
25 | import okhttp3.OkHttpClient;
26 |
27 | public class ReactNativeFlipper {
28 | public static void initializeFlipper(Context context, ReactInstanceManager reactInstanceManager) {
29 | if (FlipperUtils.shouldEnableFlipper(context)) {
30 | final FlipperClient client = AndroidFlipperClient.getInstance(context);
31 |
32 | client.addPlugin(new InspectorFlipperPlugin(context, DescriptorMapping.withDefaults()));
33 | client.addPlugin(new ReactFlipperPlugin());
34 | client.addPlugin(new DatabasesFlipperPlugin(context));
35 | client.addPlugin(new SharedPreferencesFlipperPlugin(context));
36 | client.addPlugin(CrashReporterPlugin.getInstance());
37 |
38 | NetworkFlipperPlugin networkFlipperPlugin = new NetworkFlipperPlugin();
39 | NetworkingModule.setCustomClientBuilder(
40 | new NetworkingModule.CustomClientBuilder() {
41 | @Override
42 | public void apply(OkHttpClient.Builder builder) {
43 | builder.addNetworkInterceptor(new FlipperOkhttpInterceptor(networkFlipperPlugin));
44 | }
45 | });
46 | client.addPlugin(networkFlipperPlugin);
47 | client.start();
48 |
49 | // Fresco Plugin needs to ensure that ImagePipelineFactory is initialized
50 | // Hence we run if after all native modules have been initialized
51 | ReactContext reactContext = reactInstanceManager.getCurrentReactContext();
52 | if (reactContext == null) {
53 | reactInstanceManager.addReactInstanceEventListener(
54 | new ReactInstanceManager.ReactInstanceEventListener() {
55 | @Override
56 | public void onReactContextInitialized(ReactContext reactContext) {
57 | reactInstanceManager.removeReactInstanceEventListener(this);
58 | reactContext.runOnNativeModulesQueueThread(
59 | new Runnable() {
60 | @Override
61 | public void run() {
62 | client.addPlugin(new FrescoFlipperPlugin());
63 | }
64 | });
65 | }
66 | });
67 | } else {
68 | client.addPlugin(new FrescoFlipperPlugin());
69 | }
70 | }
71 | }
72 | }
73 |
--------------------------------------------------------------------------------
/example/ios/example.xcodeproj/xcshareddata/xcschemes/example.xcscheme:
--------------------------------------------------------------------------------
1 |
2 |
5 |
8 |
9 |
15 |
21 |
22 |
23 |
24 |
25 |
30 |
31 |
33 |
39 |
40 |
41 |
42 |
43 |
53 |
55 |
61 |
62 |
63 |
64 |
70 |
72 |
78 |
79 |
80 |
81 |
83 |
84 |
87 |
88 |
89 |
--------------------------------------------------------------------------------
/example/ios/example.xcodeproj/xcshareddata/xcschemes/example-tvOS.xcscheme:
--------------------------------------------------------------------------------
1 |
2 |
5 |
8 |
9 |
15 |
21 |
22 |
23 |
24 |
25 |
30 |
31 |
33 |
39 |
40 |
41 |
42 |
43 |
53 |
55 |
61 |
62 |
63 |
64 |
70 |
72 |
78 |
79 |
80 |
81 |
83 |
84 |
87 |
88 |
89 |
--------------------------------------------------------------------------------
/example/ios/example/LaunchScreen.storyboard:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
25 |
31 |
37 |
38 |
39 |
40 |
41 |
42 |
43 |
44 |
45 |
46 |
47 |
48 |
49 |
50 |
51 |
52 |
53 |
54 |
55 |
56 |
57 |
58 |
59 |
--------------------------------------------------------------------------------
/example/android/gradlew:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env sh
2 |
3 | #
4 | # Copyright 2015 the original author or authors.
5 | #
6 | # Licensed under the Apache License, Version 2.0 (the "License");
7 | # you may not use this file except in compliance with the License.
8 | # You may obtain a copy of the License at
9 | #
10 | # https://www.apache.org/licenses/LICENSE-2.0
11 | #
12 | # Unless required by applicable law or agreed to in writing, software
13 | # distributed under the License is distributed on an "AS IS" BASIS,
14 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15 | # See the License for the specific language governing permissions and
16 | # limitations under the License.
17 | #
18 |
19 | ##############################################################################
20 | ##
21 | ## Gradle start up script for UN*X
22 | ##
23 | ##############################################################################
24 |
25 | # Attempt to set APP_HOME
26 | # Resolve links: $0 may be a link
27 | PRG="$0"
28 | # Need this for relative symlinks.
29 | while [ -h "$PRG" ] ; do
30 | ls=`ls -ld "$PRG"`
31 | link=`expr "$ls" : '.*-> \(.*\)$'`
32 | if expr "$link" : '/.*' > /dev/null; then
33 | PRG="$link"
34 | else
35 | PRG=`dirname "$PRG"`"/$link"
36 | fi
37 | done
38 | SAVED="`pwd`"
39 | cd "`dirname \"$PRG\"`/" >/dev/null
40 | APP_HOME="`pwd -P`"
41 | cd "$SAVED" >/dev/null
42 |
43 | APP_NAME="Gradle"
44 | APP_BASE_NAME=`basename "$0"`
45 |
46 | # Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
47 | DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"'
48 |
49 | # Use the maximum available, or set MAX_FD != -1 to use that value.
50 | MAX_FD="maximum"
51 |
52 | warn () {
53 | echo "$*"
54 | }
55 |
56 | die () {
57 | echo
58 | echo "$*"
59 | echo
60 | exit 1
61 | }
62 |
63 | # OS specific support (must be 'true' or 'false').
64 | cygwin=false
65 | msys=false
66 | darwin=false
67 | nonstop=false
68 | case "`uname`" in
69 | CYGWIN* )
70 | cygwin=true
71 | ;;
72 | Darwin* )
73 | darwin=true
74 | ;;
75 | MINGW* )
76 | msys=true
77 | ;;
78 | NONSTOP* )
79 | nonstop=true
80 | ;;
81 | esac
82 |
83 | CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar
84 |
85 | # Determine the Java command to use to start the JVM.
86 | if [ -n "$JAVA_HOME" ] ; then
87 | if [ -x "$JAVA_HOME/jre/sh/java" ] ; then
88 | # IBM's JDK on AIX uses strange locations for the executables
89 | JAVACMD="$JAVA_HOME/jre/sh/java"
90 | else
91 | JAVACMD="$JAVA_HOME/bin/java"
92 | fi
93 | if [ ! -x "$JAVACMD" ] ; then
94 | die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME
95 |
96 | Please set the JAVA_HOME variable in your environment to match the
97 | location of your Java installation."
98 | fi
99 | else
100 | JAVACMD="java"
101 | which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
102 |
103 | Please set the JAVA_HOME variable in your environment to match the
104 | location of your Java installation."
105 | fi
106 |
107 | # Increase the maximum file descriptors if we can.
108 | if [ "$cygwin" = "false" -a "$darwin" = "false" -a "$nonstop" = "false" ] ; then
109 | MAX_FD_LIMIT=`ulimit -H -n`
110 | if [ $? -eq 0 ] ; then
111 | if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then
112 | MAX_FD="$MAX_FD_LIMIT"
113 | fi
114 | ulimit -n $MAX_FD
115 | if [ $? -ne 0 ] ; then
116 | warn "Could not set maximum file descriptor limit: $MAX_FD"
117 | fi
118 | else
119 | warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT"
120 | fi
121 | fi
122 |
123 | # For Darwin, add options to specify how the application appears in the dock
124 | if $darwin; then
125 | GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\""
126 | fi
127 |
128 | # For Cygwin or MSYS, switch paths to Windows format before running java
129 | if [ "$cygwin" = "true" -o "$msys" = "true" ] ; then
130 | APP_HOME=`cygpath --path --mixed "$APP_HOME"`
131 | CLASSPATH=`cygpath --path --mixed "$CLASSPATH"`
132 | JAVACMD=`cygpath --unix "$JAVACMD"`
133 |
134 | # We build the pattern for arguments to be converted via cygpath
135 | ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null`
136 | SEP=""
137 | for dir in $ROOTDIRSRAW ; do
138 | ROOTDIRS="$ROOTDIRS$SEP$dir"
139 | SEP="|"
140 | done
141 | OURCYGPATTERN="(^($ROOTDIRS))"
142 | # Add a user-defined pattern to the cygpath arguments
143 | if [ "$GRADLE_CYGPATTERN" != "" ] ; then
144 | OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)"
145 | fi
146 | # Now convert the arguments - kludge to limit ourselves to /bin/sh
147 | i=0
148 | for arg in "$@" ; do
149 | CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -`
150 | CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option
151 |
152 | if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition
153 | eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"`
154 | else
155 | eval `echo args$i`="\"$arg\""
156 | fi
157 | i=`expr $i + 1`
158 | done
159 | case $i in
160 | 0) set -- ;;
161 | 1) set -- "$args0" ;;
162 | 2) set -- "$args0" "$args1" ;;
163 | 3) set -- "$args0" "$args1" "$args2" ;;
164 | 4) set -- "$args0" "$args1" "$args2" "$args3" ;;
165 | 5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;;
166 | 6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;;
167 | 7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;;
168 | 8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;;
169 | 9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;;
170 | esac
171 | fi
172 |
173 | # Escape application args
174 | save () {
175 | for i do printf %s\\n "$i" | sed "s/'/'\\\\''/g;1s/^/'/;\$s/\$/' \\\\/" ; done
176 | echo " "
177 | }
178 | APP_ARGS=`save "$@"`
179 |
180 | # Collect all arguments for the java command, following the shell quoting and substitution rules
181 | eval set -- $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS "\"-Dorg.gradle.appname=$APP_BASE_NAME\"" -classpath "\"$CLASSPATH\"" org.gradle.wrapper.GradleWrapperMain "$APP_ARGS"
182 |
183 | exec "$JAVACMD" "$@"
184 |
--------------------------------------------------------------------------------
/android/src/main/java/woyou/aidlservice/jiuiv5/IWoyouService.aidl:
--------------------------------------------------------------------------------
1 | //P、V系列
2 |
3 | package woyou.aidlservice.jiuiv5;
4 |
5 | import woyou.aidlservice.jiuiv5.ICallback;
6 | import woyou.aidlservice.jiuiv5.ITax;
7 | import android.graphics.Bitmap;
8 | import com.sunmi.trans.TransBean;
9 |
10 | interface IWoyouService
11 | {
12 | /**
13 | * 替换原打印机升级固件接口(void updateFirmware())
14 | * 现更改为负载包名的数据接口,仅系统调用
15 | * 支持版本:4.0.0以上
16 | */
17 | boolean postPrintData(String packageName, in byte[] data, int offset, int length);
18 |
19 | /**
20 | * 打印机固件状态
21 | * 返回: 0--未知, A5--bootloader, C3--print
22 | */
23 | int getFirmwareStatus();
24 |
25 | /**
26 | * 获取打印服务版本
27 | * 返回: WoyouService服务版本
28 | */
29 | String getServiceVersion();
30 |
31 | /**
32 | * 初始化打印机,重置打印机的逻辑程序,但不清空缓存区数据,因此
33 | * 未完成的打印作业将在重置后继续
34 | */
35 | void printerInit(in ICallback callback);
36 |
37 | /**
38 | * 打印机自检,打印机会打印自检页
39 | */
40 | void printerSelfChecking(in ICallback callback);
41 |
42 | /**
43 | * 获取打印机板序列号
44 | * 返回:打印机板的序列号
45 | */
46 | String getPrinterSerialNo();
47 |
48 | /**
49 | * 获取打印机固件版本号
50 | * 返回:打印机固件版本号
51 | */
52 | String getPrinterVersion();
53 |
54 | /**
55 | * 获取打印机型号
56 | * 返回:打印机型号
57 | */
58 | String getPrinterModal();
59 |
60 | /**
61 | * 获取打印机上电后的打印长度
62 | * callback onReturnString 中返回
63 | */
64 | void getPrintedLength(in ICallback callback);
65 |
66 | /**
67 | * 打印机走纸(强制换行,结束之前的打印内容后走纸n行)
68 | * n: 走纸行数
69 | */
70 | void lineWrap(int n, in ICallback callback);
71 |
72 | /**
73 | * epson指令打印
74 | */
75 | void sendRAWData(in byte[] data, in ICallback callback);
76 |
77 | /**
78 | * 设置对齐模式,对之后打印有影响,除非初始化
79 | * alignment: 对齐方式 0--居左 , 1--居中, 2--居右
80 | */
81 | void setAlignment(int alignment, in ICallback callback);
82 |
83 | /**
84 | * 设置打印字体, 暂时仅能系统调用,外部调用无效
85 | */
86 | void setFontName(String typeface, in ICallback callback);
87 |
88 | /**
89 | * 设置字体大小, 对之后打印有影响,除非初始化
90 | * 注意:字体大小是超出标准国际指令的打印方式,
91 | * 调整字体大小会影响字符宽度,每行字符数量也会随之改变,
92 | * 因此按等宽字体形成的排版可能会错乱
93 | * fontsize: 字体大小
94 | */
95 | void setFontSize(float fontsize, in ICallback callback);
96 |
97 | /**
98 | * 打印文字,文字宽度满一行自动换行排版,不满一整行不打印除非强制换行
99 | * text: 要打印的文字字符串
100 | */
101 | void printText(String text, in ICallback callback);
102 |
103 | /**
104 | * 打印指定字体的文本,字体设置只对本次有效
105 | * text: 要打印文字
106 | * typeface: 字体名称(暂时仅能系统调用,外部调用无效)
107 | * fontsize: 字体大小
108 | */
109 | void printTextWithFont(String text, String typeface, float fontsize, in ICallback callback);
110 |
111 | /**
112 | * 打印表格的一行,可以指定列宽、对齐方式
113 | * colsTextArr: 各列文本字符串数组
114 | * colsWidthArr: 各列宽度数组(以英文字符计算, 每个中文字符占两个英文字符, 每个宽度大于0)
115 | * colsAlign: 各列对齐方式(0居左, 1居中, 2居右)
116 | * 备注: 三个参数的数组长度应该一致, 如果colsText[i]的宽度大于colsWidth[i], 则文本换行
117 | */
118 | void printColumnsText(in String[] colsTextArr, in int[] colsWidthArr, in int[] colsAlign, in ICallback callback);
119 |
120 | /**
121 | * 打印图片
122 | * bitmap: 最大宽度384像素,超出宽度将显示不全;图片大小长*宽<8M;
123 | */
124 | void printBitmap(in Bitmap bitmap, in ICallback callback);
125 |
126 | /**
127 | * 打印一维条码
128 | * data: 条码数据
129 | * symbology: 条码类型
130 | * 0 -- UPC-A, 要求12位数字(最后一位校验位必须正确),但受限于打印机的宽度及条码宽度
131 | * 1 -- UPC-E, 要求8位数字(最后一位校验位必须正确),但受限于打印机的宽度及条码宽度
132 | * 2 -- JAN13(EAN13), 要求13位数字(最后一位校验位必须正确),但受限于打印机的宽度及条码宽度
133 | * 3 -- JAN8(EAN8), 要求8位数字(最后一位校验位必须正确),但受限于打印机的宽度及条码宽度
134 | * 4 -- CODE39, 数字英文及8个特殊符号且首尾为*号,但受限于打印机的宽度及条码宽度
135 | * 5 -- ITF, 字符为数字且小于14位,但受限于打印机的宽度及条码宽度
136 | * 6 -- CODABAR, 起始和终止必须为A-D,数据为0-9及6个特殊字符,长度任意但受限于打印机的宽度及条码宽度
137 | * 7 -- CODE93, 字符任意,长度任意但受限于打印机的宽度及条码宽度
138 | * 8 -- CODE128 字符任意,长度任意但受限于打印机的宽度及条码宽度
139 | * height: 条码高度, 取值1到255, 默认162
140 | * width: 条码宽度, 取值2至6, 默认2
141 | * textposition: 文字位置 0--不打印文字, 1--文字在条码上方, 2--文字在条码下方, 3--条码上下方均打印
142 | */
143 | void printBarCode(String data, int symbology, int height, int width, int textposition, in ICallback callback);
144 |
145 | /**
146 | * 打印二维条码
147 | * data: 二维码数据
148 | * modulesize: 二维码块大小(单位:点, 取值 1 至 16 )
149 | * errorlevel: 二维码纠错等级(0 至 3),
150 | * 0 -- 纠错级别L ( 7%),
151 | * 1 -- 纠错级别M (15%),
152 | * 2 -- 纠错级别Q (25%),
153 | * 3 -- 纠错级别H (30%)
154 | */
155 | void printQRCode(String data, int modulesize, int errorlevel, in ICallback callback);
156 |
157 | /**
158 | * 打印文字,文字宽度满一行自动换行排版,不满一整行不打印除非强制换行
159 | * 文字按矢量文字宽度原样输出,即每个字符不等宽
160 | * text: 要打印的文字字符串
161 | */
162 | void printOriginalText(String text, in ICallback callback);
163 |
164 | /**
165 | * lib包打印专用接口
166 | * transbean:打印任务列表
167 | */
168 | void commitPrint(in TransBean[] transbean, in ICallback callback);
169 |
170 | /**
171 | * 打印缓冲区内容
172 | */
173 | void commitPrinterBuffer();
174 |
175 | /**
176 | * 进入事务模式,所有打印调用将缓存;
177 | * 调用commitPrinterBuffe()、exitPrinterBuffer(true)、commitPrinterBufferWithCallback()、
178 | * exitPrinterBufferWithCallback(true)后才进行打印;
179 | * clean: 如果之前没退出事务模式,是否清除已缓存的缓冲区内容
180 | */
181 | void enterPrinterBuffer(in boolean clean);
182 |
183 | /**
184 | * 退出缓冲模式
185 | * commit: 是否打印出缓冲区内容
186 | */
187 | void exitPrinterBuffer(in boolean commit);
188 |
189 | /**
190 | * 发送数控指令
191 | * data:税控命令
192 | */
193 | void tax(in byte [] data,in ITax callback);
194 |
195 | /**
196 | * 获取打印机头的型号
197 | * callback onReturnString 中返回
198 | */
199 | void getPrinterFactory(in ICallback callback);
200 |
201 | /**
202 | * 清除打印机缓存数据(仅系统调用,外部调用无效)
203 | */
204 | void clearBuffer();
205 |
206 | /**
207 | * 带反馈打印缓冲区内容
208 | */
209 | void commitPrinterBufferWithCallback(in ICallback callback);
210 |
211 | /**
212 | * 带反馈退出缓冲打印模式
213 | * commit: 是否提交缓冲区内容
214 | */
215 | void exitPrinterBufferWithCallback(in boolean commit, in ICallback callback);
216 |
217 | /**
218 | * 打印表格的一行,可以指定列宽、对齐方式
219 | * colsTextArr: 各列文本字符串数组
220 | * colsWidthArr: 各列宽度权重即各列所占比例
221 | * colsAlign: 各列对齐方式(0居左, 1居中, 2居右)
222 | * 备注: 三个参数的数组长度应该一致, 如果colsText[i]的宽度大于colsWidth[i], 则文本换行
223 | */
224 | void printColumnsString(in String[] colsTextArr, in int[] colsWidthArr, in int[] colsAlign, in ICallback callback);
225 |
226 | /**
227 | * 获取打印机的最新状态
228 | * 返回:打印机状态反馈 1正常 2准备中 3通信异常 4缺纸 5过热 505:无打印机 507:更新失败
229 | */
230 | int updatePrinterState();
231 |
232 | /**
233 | * 自定义打印图片
234 | * bitmap: 图片bitmap对象(最大宽度384像素,图片超过1M无法打印)
235 | * type: 目前有两种打印方式:0、同printBitmap 1、阈值200的黑白化图片 2、灰度图片
236 | * 支持版本: P1-v3.2.0以上
237 | * P14g-v1.1.6以上
238 | * V1s-v3.1.6以上
239 | * V2-v1.0.0以上
240 | */
241 | void printBitmapCustom(in Bitmap bitmap, in int type, in ICallback callback);
242 |
243 | /**
244 | * 获取强制启用字体加倍状态
245 | * 返回 0:未启用 1:倍宽 2:倍高 3:倍高倍宽
246 | * 支持版本: P1-v3.2.0以上
247 | * P14g-v1.2.0以上
248 | * V1s-v3.2.0以上
249 | * V2-v1.0.0以上
250 | */
251 | int getForcedDouble();
252 |
253 | /**
254 | * 是否强制启用反白样式
255 | * 返回 true:启用 false:未启用
256 | * 支持版本: P1-v3.2.0以上
257 | * P14g-v1.2.0以上
258 | * V1s-v3.2.0以上
259 | * V2-v1.0.0以上
260 | */
261 | boolean isForcedAntiWhite();
262 |
263 | /**
264 | * 是否强制启用加粗样式
265 | * 返回 true:启用 false:未启用
266 | * 支持版本: P1-v3.2.0以上
267 | * P14g-v1.2.0以上
268 | * V1s-v3.2.0以上
269 | * V2-v1.0.0以上
270 | */
271 | boolean isForcedBold();
272 |
273 | /**
274 | * 是否强制启用下划线样式
275 | * 返回 true:启用 false:未启用
276 | * 支持版本: P1-v3.2.0以上
277 | * P14g-v1.2.0以上
278 | * V1s-v3.2.0以上
279 | * V2-v1.0.0以上
280 | */
281 | boolean isForcedUnderline();
282 |
283 | /**
284 | * 获取强制启用行高状态
285 | * 返回 -1:未启用 0~255:强制行高像素高度
286 | * 支持版本: P1-v3.2.0以上
287 | * P14g-v1.2.0以上
288 | * V1s-v3.2.0以上
289 | * V2-v1.0.0以上
290 | */
291 | int getForcedRowHeight();
292 |
293 | /**
294 | * 获取当前字体
295 | * 返回 0:商米字体1.0 1:商米字体2.0
296 | * 支持版本: P1-v3.2.0以上
297 | * P14g-v1.2.0以上
298 | * V1s-v3.2.0以上
299 | * V2-v1.0.0以上
300 | */
301 | int getFontName();
302 | }
--------------------------------------------------------------------------------
/example/android/app/build.gradle:
--------------------------------------------------------------------------------
1 | apply plugin: "com.android.application"
2 |
3 | import com.android.build.OutputFile
4 |
5 | /**
6 | * The react.gradle file registers a task for each build variant (e.g. bundleDebugJsAndAssets
7 | * and bundleReleaseJsAndAssets).
8 | * These basically call `react-native bundle` with the correct arguments during the Android build
9 | * cycle. By default, bundleDebugJsAndAssets is skipped, as in debug/dev mode we prefer to load the
10 | * bundle directly from the development server. Below you can see all the possible configurations
11 | * and their defaults. If you decide to add a configuration block, make sure to add it before the
12 | * `apply from: "../../node_modules/react-native/react.gradle"` line.
13 | *
14 | * project.ext.react = [
15 | * // the name of the generated asset file containing your JS bundle
16 | * bundleAssetName: "index.android.bundle",
17 | *
18 | * // the entry file for bundle generation. If none specified and
19 | * // "index.android.js" exists, it will be used. Otherwise "index.js" is
20 | * // default. Can be overridden with ENTRY_FILE environment variable.
21 | * entryFile: "index.android.js",
22 | *
23 | * // https://reactnative.dev/docs/performance#enable-the-ram-format
24 | * bundleCommand: "ram-bundle",
25 | *
26 | * // whether to bundle JS and assets in debug mode
27 | * bundleInDebug: false,
28 | *
29 | * // whether to bundle JS and assets in release mode
30 | * bundleInRelease: true,
31 | *
32 | * // whether to bundle JS and assets in another build variant (if configured).
33 | * // See http://tools.android.com/tech-docs/new-build-system/user-guide#TOC-Build-Variants
34 | * // The configuration property can be in the following formats
35 | * // 'bundleIn${productFlavor}${buildType}'
36 | * // 'bundleIn${buildType}'
37 | * // bundleInFreeDebug: true,
38 | * // bundleInPaidRelease: true,
39 | * // bundleInBeta: true,
40 | *
41 | * // whether to disable dev mode in custom build variants (by default only disabled in release)
42 | * // for example: to disable dev mode in the staging build type (if configured)
43 | * devDisabledInStaging: true,
44 | * // The configuration property can be in the following formats
45 | * // 'devDisabledIn${productFlavor}${buildType}'
46 | * // 'devDisabledIn${buildType}'
47 | *
48 | * // the root of your project, i.e. where "package.json" lives
49 | * root: "../../",
50 | *
51 | * // where to put the JS bundle asset in debug mode
52 | * jsBundleDirDebug: "$buildDir/intermediates/assets/debug",
53 | *
54 | * // where to put the JS bundle asset in release mode
55 | * jsBundleDirRelease: "$buildDir/intermediates/assets/release",
56 | *
57 | * // where to put drawable resources / React Native assets, e.g. the ones you use via
58 | * // require('./image.png')), in debug mode
59 | * resourcesDirDebug: "$buildDir/intermediates/res/merged/debug",
60 | *
61 | * // where to put drawable resources / React Native assets, e.g. the ones you use via
62 | * // require('./image.png')), in release mode
63 | * resourcesDirRelease: "$buildDir/intermediates/res/merged/release",
64 | *
65 | * // by default the gradle tasks are skipped if none of the JS files or assets change; this means
66 | * // that we don't look at files in android/ or ios/ to determine whether the tasks are up to
67 | * // date; if you have any other folders that you want to ignore for performance reasons (gradle
68 | * // indexes the entire tree), add them here. Alternatively, if you have JS files in android/
69 | * // for example, you might want to remove it from here.
70 | * inputExcludes: ["android/**", "ios/**"],
71 | *
72 | * // override which node gets called and with what additional arguments
73 | * nodeExecutableAndArgs: ["node"],
74 | *
75 | * // supply additional arguments to the packager
76 | * extraPackagerArgs: []
77 | * ]
78 | */
79 |
80 | project.ext.react = [
81 | enableHermes: false, // clean and rebuild if changing
82 | ]
83 |
84 | apply from: "../../node_modules/react-native/react.gradle"
85 |
86 | /**
87 | * Set this to true to create two separate APKs instead of one:
88 | * - An APK that only works on ARM devices
89 | * - An APK that only works on x86 devices
90 | * The advantage is the size of the APK is reduced by about 4MB.
91 | * Upload all the APKs to the Play Store and people will download
92 | * the correct one based on the CPU architecture of their device.
93 | */
94 | def enableSeparateBuildPerCPUArchitecture = false
95 |
96 | /**
97 | * Run Proguard to shrink the Java bytecode in release builds.
98 | */
99 | def enableProguardInReleaseBuilds = false
100 |
101 | /**
102 | * The preferred build flavor of JavaScriptCore.
103 | *
104 | * For example, to use the international variant, you can use:
105 | * `def jscFlavor = 'org.webkit:android-jsc-intl:+'`
106 | *
107 | * The international variant includes ICU i18n library and necessary data
108 | * allowing to use e.g. `Date.toLocaleString` and `String.localeCompare` that
109 | * give correct results when using with locales other than en-US. Note that
110 | * this variant is about 6MiB larger per architecture than default.
111 | */
112 | def jscFlavor = 'org.webkit:android-jsc:+'
113 |
114 | /**
115 | * Whether to enable the Hermes VM.
116 | *
117 | * This should be set on project.ext.react and mirrored here. If it is not set
118 | * on project.ext.react, JavaScript will not be compiled to Hermes Bytecode
119 | * and the benefits of using Hermes will therefore be sharply reduced.
120 | */
121 | def enableHermes = project.ext.react.get("enableHermes", false);
122 |
123 | android {
124 | compileSdkVersion rootProject.ext.compileSdkVersion
125 |
126 | compileOptions {
127 | sourceCompatibility JavaVersion.VERSION_1_8
128 | targetCompatibility JavaVersion.VERSION_1_8
129 | }
130 |
131 | defaultConfig {
132 | applicationId "com.example"
133 | minSdkVersion rootProject.ext.minSdkVersion
134 | targetSdkVersion rootProject.ext.targetSdkVersion
135 | versionCode 1
136 | versionName "1.0"
137 | }
138 | splits {
139 | abi {
140 | reset()
141 | enable enableSeparateBuildPerCPUArchitecture
142 | universalApk false // If true, also generate a universal APK
143 | include "armeabi-v7a", "x86", "arm64-v8a", "x86_64"
144 | }
145 | }
146 | signingConfigs {
147 | debug {
148 | storeFile file('debug.keystore')
149 | storePassword 'android'
150 | keyAlias 'androiddebugkey'
151 | keyPassword 'android'
152 | }
153 | }
154 | buildTypes {
155 | debug {
156 | signingConfig signingConfigs.debug
157 | }
158 | release {
159 | // Caution! In production, you need to generate your own keystore file.
160 | // see https://reactnative.dev/docs/signed-apk-android.
161 | signingConfig signingConfigs.debug
162 | minifyEnabled enableProguardInReleaseBuilds
163 | proguardFiles getDefaultProguardFile("proguard-android.txt"), "proguard-rules.pro"
164 | }
165 | }
166 |
167 | // applicationVariants are e.g. debug, release
168 | applicationVariants.all { variant ->
169 | variant.outputs.each { output ->
170 | // For each separate APK per architecture, set a unique version code as described here:
171 | // https://developer.android.com/studio/build/configure-apk-splits.html
172 | def versionCodes = ["armeabi-v7a": 1, "x86": 2, "arm64-v8a": 3, "x86_64": 4]
173 | def abi = output.getFilter(OutputFile.ABI)
174 | if (abi != null) { // null for the universal-debug, universal-release variants
175 | output.versionCodeOverride =
176 | versionCodes.get(abi) * 1048576 + defaultConfig.versionCode
177 | }
178 |
179 | }
180 | }
181 | }
182 |
183 | dependencies {
184 | compile project(':react-native-sunmi-v2-printer')
185 |
186 | implementation fileTree(dir: "libs", include: ["*.jar"])
187 | //noinspection GradleDynamicVersion
188 | implementation "com.facebook.react:react-native:+" // From node_modules
189 |
190 | implementation "androidx.swiperefreshlayout:swiperefreshlayout:1.0.0"
191 |
192 | debugImplementation("com.facebook.flipper:flipper:${FLIPPER_VERSION}") {
193 | exclude group:'com.facebook.fbjni'
194 | }
195 |
196 | debugImplementation("com.facebook.flipper:flipper-network-plugin:${FLIPPER_VERSION}") {
197 | exclude group:'com.facebook.flipper'
198 | exclude group:'com.squareup.okhttp3', module:'okhttp'
199 | }
200 |
201 | debugImplementation("com.facebook.flipper:flipper-fresco-plugin:${FLIPPER_VERSION}") {
202 | exclude group:'com.facebook.flipper'
203 | }
204 |
205 | if (enableHermes) {
206 | def hermesPath = "../../node_modules/hermes-engine/android/";
207 | debugImplementation files(hermesPath + "hermes-debug.aar")
208 | releaseImplementation files(hermesPath + "hermes-release.aar")
209 | } else {
210 | implementation jscFlavor
211 | }
212 | }
213 |
214 | // Run this once to be able to run the application with BUCK
215 | // puts all compile dependencies into folder libs for BUCK to use
216 | task copyDownloadableDepsToLibs(type: Copy) {
217 | from configurations.compile
218 | into 'libs'
219 | }
220 |
221 | apply from: file("../../node_modules/@react-native-community/cli-platform-android/native_modules.gradle"); applyNativeModulesAppBuildGradle(project)
222 |
--------------------------------------------------------------------------------
/android/src/main/java/com/sunmi/v2/printer/ESCUtil.java:
--------------------------------------------------------------------------------
1 | package com.sunmi.v2.printer;
2 |
3 | import java.io.UnsupportedEncodingException;
4 | import java.util.List;
5 | import java.util.ArrayList;
6 | import java.util.Map;
7 |
8 | import android.graphics.Bitmap;
9 | import android.graphics.BitmapFactory;
10 |
11 | public class ESCUtil {
12 |
13 | public static final byte ESC = 27;// 换码
14 | public static final byte FS = 28;// 文本分隔符
15 | public static final byte GS = 29;// 组分隔符
16 | public static final byte DLE = 16;// 数据连接换码
17 | public static final byte EOT = 4;// 传输结束
18 | public static final byte ENQ = 5;// 询问字符
19 | public static final byte SP = 32;// 空格
20 | public static final byte HT = 9;// 横向列表
21 | public static final byte LF = 10;// 打印并换行(水平定位)
22 | public static final byte CR = 13;// 归位键
23 | public static final byte FF = 12;// 走纸控制(打印并回到标准模式(在页模式下) )
24 | public static final byte CAN = 24;// 作废(页模式下取消打印数据 )
25 |
26 | // ------------------------打印机初始化-----------------------------
27 |
28 | private static String hexStr = "0123456789ABCDEF";
29 | private static String[] binaryArray = { "0000", "0001", "0010", "0011",
30 | "0100", "0101", "0110", "0111", "1000", "1001", "1010", "1011",
31 | "1100", "1101", "1110", "1111" };
32 |
33 | private static byte uniteBytes(byte src0, byte src1) {
34 | byte _b0 = Byte.decode(new String(new byte[] { src0 })).byteValue();
35 | _b0 = (byte) (_b0 << 4);
36 | byte _b1 = Byte.decode(new String(new byte[] { src1 })).byteValue();
37 | byte ret = (byte) (_b0 | _b1);
38 | byte aret = Byte.decode("0x" + ret).byteValue();
39 |
40 | return aret;
41 | }
42 | public static String binaryStrToHexString(String binaryStr) {
43 | String hex = "";
44 | String f4 = binaryStr.substring(0, 4);
45 | String b4 = binaryStr.substring(4, 8);
46 | for (int i = 0; i < binaryArray.length; i++) {
47 | if (f4.equals(binaryArray[i]))
48 | hex += hexStr.substring(i, i + 1);
49 | }
50 | for (int i = 0; i < binaryArray.length; i++) {
51 | if (b4.equals(binaryArray[i]))
52 | hex += hexStr.substring(i, i + 1);
53 | }
54 |
55 | return hex;
56 | }
57 |
58 | public static byte[] HexStringToBinary(String hexString) {
59 | int len = hexString.length() / 2;
60 | byte[] bytes = new byte[len];
61 | byte high = 0;
62 | byte low = 0;
63 | for (int i = 0; i < len; i++) {
64 | high = (byte) ((hexStr.indexOf(hexString.charAt(2 * i))) << 4);
65 | low = (byte) hexStr.indexOf(hexString.charAt(2 * i + 1));
66 | bytes[i] = (byte) (high & 0xF0 | low & 0x0F);
67 | }
68 | return bytes;
69 | }
70 |
71 | public static List binaryListToHexStringList(List list) {
72 | List hexList = new ArrayList();
73 | for (String binaryStr : list) {
74 | StringBuffer sb = new StringBuffer();
75 | for (int i = 0; i < binaryStr.length(); i += 8) {
76 | String str = binaryStr.substring(i, i + 8);
77 | String hexString = binaryStrToHexString(str);
78 | sb.append(hexString);
79 | }
80 | hexList.add(sb.toString());
81 | }
82 | return hexList;
83 |
84 | }
85 | public static byte[] sysCopy(List srcArrays) {
86 | int len = 0;
87 | for (byte[] srcArray : srcArrays) {
88 | len += srcArray.length;
89 | }
90 | byte[] destArray = new byte[len];
91 | int destLen = 0;
92 | for (byte[] srcArray : srcArrays) {
93 | System.arraycopy(srcArray, 0, destArray, destLen, srcArray.length);
94 | destLen += srcArray.length;
95 | }
96 | return destArray;
97 | }
98 |
99 | public static byte[] hexList2Byte(List list) {
100 | List commandList = new ArrayList();
101 | for (String hexStr : list) {
102 | commandList.add(HexStringToBinary(hexStr));
103 | }
104 | byte[] bytes = sysCopy(commandList);
105 | return bytes;
106 | }
107 |
108 | /**
109 | * 打印机初始化
110 | *
111 | * @return
112 | */
113 | public static byte[] init_printer() {
114 | byte[] result = new byte[2];
115 | result[0] = ESC;
116 | result[1] = 64;
117 | return result;
118 | }
119 |
120 | // ------------------------换行-----------------------------
121 |
122 | /**
123 | * 换行
124 | *
125 | * @param lineNum要换几行
126 | * @return
127 | */
128 | public static byte[] nextLine(int lineNum) {
129 | byte[] result = new byte[lineNum];
130 | for (int i = 0; i < lineNum; i++) {
131 | result[i] = LF;
132 | }
133 |
134 | return result;
135 | }
136 |
137 | // ------------------------下划线-----------------------------
138 |
139 | /**
140 | * 绘制下划线(1点宽)
141 | *
142 | * @return
143 | */
144 | public static byte[] underlineWithOneDotWidthOn() {
145 | byte[] result = new byte[3];
146 | result[0] = ESC;
147 | result[1] = 45;
148 | result[2] = 1;
149 | return result;
150 | }
151 |
152 | /**
153 | * 绘制下划线(2点宽)
154 | *
155 | * @return
156 | */
157 | public static byte[] underlineWithTwoDotWidthOn() {
158 | byte[] result = new byte[3];
159 | result[0] = ESC;
160 | result[1] = 45;
161 | result[2] = 2;
162 | return result;
163 | }
164 |
165 | /**
166 | * 取消绘制下划线
167 | *
168 | * @return
169 | */
170 | public static byte[] underlineOff() {
171 | byte[] result = new byte[3];
172 | result[0] = ESC;
173 | result[1] = 45;
174 | result[2] = 0;
175 | return result;
176 | }
177 |
178 | // ------------------------加粗-----------------------------
179 |
180 | /**
181 | * 选择加粗模式
182 | *
183 | * @return
184 | */
185 | public static byte[] boldOn() {
186 | byte[] result = new byte[3];
187 | result[0] = ESC;
188 | result[1] = 69;
189 | result[2] = 0xF;
190 | return result;
191 | }
192 |
193 | /**
194 | * 取消加粗模式
195 | *
196 | * @return
197 | */
198 | public static byte[] boldOff() {
199 | byte[] result = new byte[3];
200 | result[0] = ESC;
201 | result[1] = 69;
202 | result[2] = 0;
203 | return result;
204 | }
205 |
206 | // ------------------------对齐-----------------------------
207 |
208 | /**
209 | * 左对齐
210 | *
211 | * @return
212 | */
213 | public static byte[] alignLeft() {
214 | byte[] result = new byte[3];
215 | result[0] = ESC;
216 | result[1] = 97;
217 | result[2] = 0;
218 | return result;
219 | }
220 |
221 | /**
222 | * 居中对齐
223 | *
224 | * @return
225 | */
226 | public static byte[] alignCenter() {
227 | byte[] result = new byte[3];
228 | result[0] = ESC;
229 | result[1] = 97;
230 | result[2] = 1;
231 | return result;
232 | }
233 |
234 | /**
235 | * 右对齐
236 | *
237 | * @return
238 | */
239 | public static byte[] alignRight() {
240 | byte[] result = new byte[3];
241 | result[0] = ESC;
242 | result[1] = 97;
243 | result[2] = 2;
244 | return result;
245 | }
246 |
247 | /**
248 | * 水平方向向右移动col列
249 | *
250 | * @param col
251 | * @return
252 | */
253 | public static byte[] set_HT_position(byte col) {
254 | byte[] result = new byte[4];
255 | result[0] = ESC;
256 | result[1] = 68;
257 | result[2] = col;
258 | result[3] = 0;
259 | return result;
260 | }
261 | // ------------------------字体变大-----------------------------
262 |
263 | /**
264 | * 字体变大为标准的n倍
265 | *
266 | * @param num
267 | * @return
268 | */
269 | public static byte[] fontSizeSetBig(int num) {
270 | byte realSize = 0;
271 | switch (num) {
272 | case 1:
273 | realSize = 0;
274 | break;
275 | case 2:
276 | realSize = 17;
277 | break;
278 | case 3:
279 | realSize = 34;
280 | break;
281 | case 4:
282 | realSize = 51;
283 | break;
284 | case 5:
285 | realSize = 68;
286 | break;
287 | case 6:
288 | realSize = 85;
289 | break;
290 | case 7:
291 | realSize = 102;
292 | break;
293 | case 8:
294 | realSize = 119;
295 | break;
296 | }
297 | byte[] result = new byte[3];
298 | result[0] = 29;
299 | result[1] = 33;
300 | result[2] = realSize;
301 | return result;
302 | }
303 |
304 | // ------------------------字体变小-----------------------------
305 |
306 | /**
307 | * 字体取消倍宽倍高
308 | *
309 | * @param num
310 | * @return
311 | */
312 | public static byte[] fontSizeSetSmall(int num) {
313 | byte[] result = new byte[3];
314 | result[0] = ESC;
315 | result[1] = 33;
316 |
317 | return result;
318 | }
319 |
320 | // ------------------------切纸-----------------------------
321 |
322 | /**
323 | * 进纸并全部切割
324 | *
325 | * @return
326 | */
327 | public static byte[] feedPaperCutAll() {
328 | byte[] result = new byte[4];
329 | result[0] = GS;
330 | result[1] = 86;
331 | result[2] = 65;
332 | result[3] = 0;
333 | return result;
334 | }
335 |
336 | /**
337 | * 进纸并切割(左边留一点不切)
338 | *
339 | * @return
340 | */
341 | public static byte[] feedPaperCutPartial() {
342 | byte[] result = new byte[4];
343 | result[0] = GS;
344 | result[1] = 86;
345 | result[2] = 66;
346 | result[3] = 0;
347 | return result;
348 | }
349 |
350 | // ------------------------切纸-----------------------------
351 | public static byte[] byteMerger(byte[] byte_1, byte[] byte_2) {
352 | byte[] byte_3 = new byte[byte_1.length + byte_2.length];
353 | System.arraycopy(byte_1, 0, byte_3, 0, byte_1.length);
354 | System.arraycopy(byte_2, 0, byte_3, byte_1.length, byte_2.length);
355 | return byte_3;
356 | }
357 |
358 | public static byte[] byteMerger(byte[][] byteList) {
359 |
360 | int length = 0;
361 | for (int i = 0; i < byteList.length; i++) {
362 | length += byteList[i].length;
363 | }
364 | byte[] result = new byte[length];
365 |
366 | int index = 0;
367 | for (int i = 0; i < byteList.length; i++) {
368 | byte[] nowByte = byteList[i];
369 | for (int k = 0; k < byteList[i].length; k++) {
370 | result[index] = nowByte[k];
371 | index++;
372 | }
373 | }
374 | for (int i = 0; i < index; i++) {
375 | // CommonUtils.LogWuwei("", "result[" + i + "] is " + result[i]);
376 | }
377 | return result;
378 | }
379 |
380 | // --------------------
381 | public static byte[] generateMockData() {
382 | try {
383 | byte[] next2Line = ESCUtil.nextLine(2);
384 | byte[] title = "出餐单(午餐)**万通中心店".getBytes("gb2312");
385 |
386 | byte[] boldOn = ESCUtil.boldOn();
387 | byte[] fontSize2Big = ESCUtil.fontSizeSetBig(3);
388 | byte[] center = ESCUtil.alignCenter();
389 | byte[] Focus = "网 507".getBytes("gb2312");
390 | byte[] boldOff = ESCUtil.boldOff();
391 | byte[] fontSize2Small = ESCUtil.fontSizeSetSmall(3);
392 |
393 | byte[] left = ESCUtil.alignLeft();
394 | byte[] orderSerinum = "订单编号:11234".getBytes("gb2312");
395 | boldOn = ESCUtil.boldOn();
396 | byte[] fontSize1Big = ESCUtil.fontSizeSetBig(2);
397 | byte[] FocusOrderContent = "韭菜鸡蛋饺子-小份(单)".getBytes("gb2312");
398 | boldOff = ESCUtil.boldOff();
399 | byte[] fontSize1Small = ESCUtil.fontSizeSetSmall(2);
400 |
401 | next2Line = ESCUtil.nextLine(2);
402 |
403 | byte[] priceInfo = "应收:22元 优惠:2.5元 ".getBytes("gb2312");
404 | byte[] nextLine = ESCUtil.nextLine(1);
405 |
406 | byte[] priceShouldPay = "实收:19.5元".getBytes("gb2312");
407 | nextLine = ESCUtil.nextLine(1);
408 |
409 | byte[] takeTime = "取餐时间:2015-02-13 12:51:59".getBytes("gb2312");
410 | nextLine = ESCUtil.nextLine(1);
411 | byte[] setOrderTime = "下单时间:2015-02-13 12:35:15".getBytes("gb2312");
412 |
413 | byte[] tips_1 = "微信关注\"**\"自助下单每天免1元".getBytes("gb2312");
414 | nextLine = ESCUtil.nextLine(1);
415 | byte[] tips_2 = "饭后点评再奖5毛".getBytes("gb2312");
416 | byte[] next4Line = ESCUtil.nextLine(4);
417 |
418 | byte[] breakPartial = ESCUtil.feedPaperCutPartial();
419 |
420 | byte[][] cmdBytes = { title, nextLine, center, boldOn, fontSize2Big, Focus, boldOff, fontSize2Small,
421 | next2Line, left, orderSerinum, nextLine, center, boldOn, fontSize1Big, FocusOrderContent, boldOff,
422 | fontSize1Small, nextLine, left, next2Line, priceInfo, nextLine, priceShouldPay, next2Line, takeTime,
423 | nextLine, setOrderTime, next2Line, center, tips_1, nextLine, center, tips_2, next4Line,
424 | breakPartial };
425 |
426 | return ESCUtil.byteMerger(cmdBytes);
427 |
428 | } catch (UnsupportedEncodingException e) {
429 | e.printStackTrace();
430 | }
431 | return null;
432 | }
433 |
434 | public static byte[] decodeBitmap(byte[] bitmapBytes) {
435 |
436 | Bitmap bmp = BitmapFactory.decodeByteArray(bitmapBytes, 0, bitmapBytes.length);
437 |
438 | int zeroCount = bmp.getWidth() % 8;
439 | String zeroStr = "";
440 | if (zeroCount > 0) {
441 | for (int i = 0; i < (8 - zeroCount); i++) {
442 | zeroStr = zeroStr + "0";
443 | }
444 | }
445 |
446 | List list = new ArrayList<>();
447 | for (int i = 0; i < bmp.getHeight(); i++) {
448 | StringBuilder sb = new StringBuilder();
449 | for (int j = 0; j < bmp.getWidth(); j++) {
450 | int color = bmp.getPixel(j, i);
451 |
452 | int r = (color >> 16) & 0xff;
453 | int g = (color >> 8) & 0xff;
454 | int b = color & 0xff;
455 |
456 | // if color close to white,bit='0', else bit='1'
457 | if (r > 160 && g > 160 && b > 160)
458 | sb.append("0");
459 | else
460 | sb.append("1");
461 | }
462 | if (zeroCount > 0) {
463 | sb.append(zeroStr);
464 | }
465 |
466 | list.add(sb.toString());
467 | }
468 |
469 | List bmpHexList = binaryListToHexStringList(list);
470 | List commandList = new ArrayList<>();
471 | commandList.addAll(bmpHexList);
472 |
473 | return hexList2Byte(commandList);
474 | }
475 | public static byte[] decodeBitmap2(byte[] bitmapBytes) {
476 | Bitmap bmp = BitmapFactory.decodeByteArray(bitmapBytes, 0, bitmapBytes.length);
477 | return draw2PxPoint(bmp);
478 | }
479 |
480 | public static byte[] draw2PxPoint(Bitmap bmp) {
481 | //用来存储转换后的 bitmap 数据。为什么要再加1000,这是为了应对当图片高度无法
482 | //整除24时的情况。比如bitmap 分辨率为 240 * 250,占用 7500 byte,
483 | //但是实际上要存储11行数据,每一行需要 24 * 240 / 8 =720byte 的空间。再加上一些指令存储的开销,
484 | //所以多申请 1000byte 的空间是稳妥的,不然运行时会抛出数组访问越界的异常。
485 | int size = bmp.getWidth() * bmp.getHeight() / 8 + 1000;
486 | byte[] data = new byte[size];
487 | int k = 0;
488 | //设置行距为0的指令
489 | data[k++] = 0x1B;
490 | data[k++] = 0x33;
491 | data[k++] = 0x00;
492 | // 逐行打印
493 | for (int j = 0; j < bmp.getHeight() / 24f; j++) {
494 | //打印图片的指令
495 | data[k++] = 0x1B;
496 | data[k++] = 0x2A;
497 | data[k++] = 33;
498 | data[k++] = (byte) (bmp.getWidth() % 256); //nL
499 | data[k++] = (byte) (bmp.getWidth() / 256); //nH
500 | //对于每一行,逐列打印
501 | for (int i = 0; i < bmp.getWidth(); i++) {
502 | //每一列24个像素点,分为3个字节存储
503 | for (int m = 0; m < 3; m++) {
504 | //每个字节表示8个像素点,0表示白色,1表示黑色
505 | for (int n = 0; n < 8; n++) {
506 | byte b = px2Byte(i, j * 24 + m * 8 + n, bmp);
507 | data[k] += data[k] + b;
508 | }
509 | k++;
510 | }
511 | }
512 | data[k++] = 10;//换行
513 | }
514 | return data;
515 | }
516 | /**
517 | * 灰度图片黑白化,黑色是1,白色是0
518 | *
519 | * @param x 横坐标
520 | * @param y 纵坐标
521 | * @param bit 位图
522 | * @return
523 | */
524 | public static byte px2Byte(int x, int y, Bitmap bit) {
525 | if (x < bit.getWidth() && y < bit.getHeight()) {
526 | byte b;
527 | int pixel = bit.getPixel(x, y);
528 | int red = (pixel & 0x00ff0000) >> 16; // 取高两位
529 | int green = (pixel & 0x0000ff00) >> 8; // 取中两位
530 | int blue = pixel & 0x000000ff; // 取低两位
531 | int gray = RGB2Gray(red, green, blue);
532 | if (gray < 128) {
533 | b = 1;
534 | } else {
535 | b = 0;
536 | }
537 | return b;
538 | }
539 | return 0;
540 | }
541 | /**
542 | * 图片灰度的转化
543 | */
544 | private static int RGB2Gray(int r, int g, int b) {
545 | int gray = (int) (0.29900 * r + 0.58700 * g + 0.11400 * b); //灰度转化公式
546 | return gray;
547 | }
548 | }
549 |
550 |
551 |
--------------------------------------------------------------------------------
/example/ios/Podfile.lock:
--------------------------------------------------------------------------------
1 | PODS:
2 | - boost-for-react-native (1.63.0)
3 | - CocoaAsyncSocket (7.6.4)
4 | - CocoaLibEvent (1.0.0)
5 | - DoubleConversion (1.1.6)
6 | - FBLazyVector (0.63.3)
7 | - FBReactNativeSpec (0.63.3):
8 | - Folly (= 2020.01.13.00)
9 | - RCTRequired (= 0.63.3)
10 | - RCTTypeSafety (= 0.63.3)
11 | - React-Core (= 0.63.3)
12 | - React-jsi (= 0.63.3)
13 | - ReactCommon/turbomodule/core (= 0.63.3)
14 | - Flipper (0.54.0):
15 | - Flipper-Folly (~> 2.2)
16 | - Flipper-RSocket (~> 1.1)
17 | - Flipper-DoubleConversion (1.1.7)
18 | - Flipper-Folly (2.3.0):
19 | - boost-for-react-native
20 | - CocoaLibEvent (~> 1.0)
21 | - Flipper-DoubleConversion
22 | - Flipper-Glog
23 | - OpenSSL-Universal (= 1.0.2.20)
24 | - Flipper-Glog (0.3.6)
25 | - Flipper-PeerTalk (0.0.4)
26 | - Flipper-RSocket (1.1.0):
27 | - Flipper-Folly (~> 2.2)
28 | - FlipperKit (0.54.0):
29 | - FlipperKit/Core (= 0.54.0)
30 | - FlipperKit/Core (0.54.0):
31 | - Flipper (~> 0.54.0)
32 | - FlipperKit/CppBridge
33 | - FlipperKit/FBCxxFollyDynamicConvert
34 | - FlipperKit/FBDefines
35 | - FlipperKit/FKPortForwarding
36 | - FlipperKit/CppBridge (0.54.0):
37 | - Flipper (~> 0.54.0)
38 | - FlipperKit/FBCxxFollyDynamicConvert (0.54.0):
39 | - Flipper-Folly (~> 2.2)
40 | - FlipperKit/FBDefines (0.54.0)
41 | - FlipperKit/FKPortForwarding (0.54.0):
42 | - CocoaAsyncSocket (~> 7.6)
43 | - Flipper-PeerTalk (~> 0.0.4)
44 | - FlipperKit/FlipperKitHighlightOverlay (0.54.0)
45 | - FlipperKit/FlipperKitLayoutPlugin (0.54.0):
46 | - FlipperKit/Core
47 | - FlipperKit/FlipperKitHighlightOverlay
48 | - FlipperKit/FlipperKitLayoutTextSearchable
49 | - YogaKit (~> 1.18)
50 | - FlipperKit/FlipperKitLayoutTextSearchable (0.54.0)
51 | - FlipperKit/FlipperKitNetworkPlugin (0.54.0):
52 | - FlipperKit/Core
53 | - FlipperKit/FlipperKitReactPlugin (0.54.0):
54 | - FlipperKit/Core
55 | - FlipperKit/FlipperKitUserDefaultsPlugin (0.54.0):
56 | - FlipperKit/Core
57 | - FlipperKit/SKIOSNetworkPlugin (0.54.0):
58 | - FlipperKit/Core
59 | - FlipperKit/FlipperKitNetworkPlugin
60 | - Folly (2020.01.13.00):
61 | - boost-for-react-native
62 | - DoubleConversion
63 | - Folly/Default (= 2020.01.13.00)
64 | - glog
65 | - Folly/Default (2020.01.13.00):
66 | - boost-for-react-native
67 | - DoubleConversion
68 | - glog
69 | - glog (0.3.5)
70 | - OpenSSL-Universal (1.0.2.20):
71 | - OpenSSL-Universal/Static (= 1.0.2.20)
72 | - OpenSSL-Universal/Static (1.0.2.20)
73 | - RCTRequired (0.63.3)
74 | - RCTTypeSafety (0.63.3):
75 | - FBLazyVector (= 0.63.3)
76 | - Folly (= 2020.01.13.00)
77 | - RCTRequired (= 0.63.3)
78 | - React-Core (= 0.63.3)
79 | - React (0.63.3):
80 | - React-Core (= 0.63.3)
81 | - React-Core/DevSupport (= 0.63.3)
82 | - React-Core/RCTWebSocket (= 0.63.3)
83 | - React-RCTActionSheet (= 0.63.3)
84 | - React-RCTAnimation (= 0.63.3)
85 | - React-RCTBlob (= 0.63.3)
86 | - React-RCTImage (= 0.63.3)
87 | - React-RCTLinking (= 0.63.3)
88 | - React-RCTNetwork (= 0.63.3)
89 | - React-RCTSettings (= 0.63.3)
90 | - React-RCTText (= 0.63.3)
91 | - React-RCTVibration (= 0.63.3)
92 | - React-callinvoker (0.63.3)
93 | - React-Core (0.63.3):
94 | - Folly (= 2020.01.13.00)
95 | - glog
96 | - React-Core/Default (= 0.63.3)
97 | - React-cxxreact (= 0.63.3)
98 | - React-jsi (= 0.63.3)
99 | - React-jsiexecutor (= 0.63.3)
100 | - Yoga
101 | - React-Core/CoreModulesHeaders (0.63.3):
102 | - Folly (= 2020.01.13.00)
103 | - glog
104 | - React-Core/Default
105 | - React-cxxreact (= 0.63.3)
106 | - React-jsi (= 0.63.3)
107 | - React-jsiexecutor (= 0.63.3)
108 | - Yoga
109 | - React-Core/Default (0.63.3):
110 | - Folly (= 2020.01.13.00)
111 | - glog
112 | - React-cxxreact (= 0.63.3)
113 | - React-jsi (= 0.63.3)
114 | - React-jsiexecutor (= 0.63.3)
115 | - Yoga
116 | - React-Core/DevSupport (0.63.3):
117 | - Folly (= 2020.01.13.00)
118 | - glog
119 | - React-Core/Default (= 0.63.3)
120 | - React-Core/RCTWebSocket (= 0.63.3)
121 | - React-cxxreact (= 0.63.3)
122 | - React-jsi (= 0.63.3)
123 | - React-jsiexecutor (= 0.63.3)
124 | - React-jsinspector (= 0.63.3)
125 | - Yoga
126 | - React-Core/RCTActionSheetHeaders (0.63.3):
127 | - Folly (= 2020.01.13.00)
128 | - glog
129 | - React-Core/Default
130 | - React-cxxreact (= 0.63.3)
131 | - React-jsi (= 0.63.3)
132 | - React-jsiexecutor (= 0.63.3)
133 | - Yoga
134 | - React-Core/RCTAnimationHeaders (0.63.3):
135 | - Folly (= 2020.01.13.00)
136 | - glog
137 | - React-Core/Default
138 | - React-cxxreact (= 0.63.3)
139 | - React-jsi (= 0.63.3)
140 | - React-jsiexecutor (= 0.63.3)
141 | - Yoga
142 | - React-Core/RCTBlobHeaders (0.63.3):
143 | - Folly (= 2020.01.13.00)
144 | - glog
145 | - React-Core/Default
146 | - React-cxxreact (= 0.63.3)
147 | - React-jsi (= 0.63.3)
148 | - React-jsiexecutor (= 0.63.3)
149 | - Yoga
150 | - React-Core/RCTImageHeaders (0.63.3):
151 | - Folly (= 2020.01.13.00)
152 | - glog
153 | - React-Core/Default
154 | - React-cxxreact (= 0.63.3)
155 | - React-jsi (= 0.63.3)
156 | - React-jsiexecutor (= 0.63.3)
157 | - Yoga
158 | - React-Core/RCTLinkingHeaders (0.63.3):
159 | - Folly (= 2020.01.13.00)
160 | - glog
161 | - React-Core/Default
162 | - React-cxxreact (= 0.63.3)
163 | - React-jsi (= 0.63.3)
164 | - React-jsiexecutor (= 0.63.3)
165 | - Yoga
166 | - React-Core/RCTNetworkHeaders (0.63.3):
167 | - Folly (= 2020.01.13.00)
168 | - glog
169 | - React-Core/Default
170 | - React-cxxreact (= 0.63.3)
171 | - React-jsi (= 0.63.3)
172 | - React-jsiexecutor (= 0.63.3)
173 | - Yoga
174 | - React-Core/RCTSettingsHeaders (0.63.3):
175 | - Folly (= 2020.01.13.00)
176 | - glog
177 | - React-Core/Default
178 | - React-cxxreact (= 0.63.3)
179 | - React-jsi (= 0.63.3)
180 | - React-jsiexecutor (= 0.63.3)
181 | - Yoga
182 | - React-Core/RCTTextHeaders (0.63.3):
183 | - Folly (= 2020.01.13.00)
184 | - glog
185 | - React-Core/Default
186 | - React-cxxreact (= 0.63.3)
187 | - React-jsi (= 0.63.3)
188 | - React-jsiexecutor (= 0.63.3)
189 | - Yoga
190 | - React-Core/RCTVibrationHeaders (0.63.3):
191 | - Folly (= 2020.01.13.00)
192 | - glog
193 | - React-Core/Default
194 | - React-cxxreact (= 0.63.3)
195 | - React-jsi (= 0.63.3)
196 | - React-jsiexecutor (= 0.63.3)
197 | - Yoga
198 | - React-Core/RCTWebSocket (0.63.3):
199 | - Folly (= 2020.01.13.00)
200 | - glog
201 | - React-Core/Default (= 0.63.3)
202 | - React-cxxreact (= 0.63.3)
203 | - React-jsi (= 0.63.3)
204 | - React-jsiexecutor (= 0.63.3)
205 | - Yoga
206 | - React-CoreModules (0.63.3):
207 | - FBReactNativeSpec (= 0.63.3)
208 | - Folly (= 2020.01.13.00)
209 | - RCTTypeSafety (= 0.63.3)
210 | - React-Core/CoreModulesHeaders (= 0.63.3)
211 | - React-jsi (= 0.63.3)
212 | - React-RCTImage (= 0.63.3)
213 | - ReactCommon/turbomodule/core (= 0.63.3)
214 | - React-cxxreact (0.63.3):
215 | - boost-for-react-native (= 1.63.0)
216 | - DoubleConversion
217 | - Folly (= 2020.01.13.00)
218 | - glog
219 | - React-callinvoker (= 0.63.3)
220 | - React-jsinspector (= 0.63.3)
221 | - React-jsi (0.63.3):
222 | - boost-for-react-native (= 1.63.0)
223 | - DoubleConversion
224 | - Folly (= 2020.01.13.00)
225 | - glog
226 | - React-jsi/Default (= 0.63.3)
227 | - React-jsi/Default (0.63.3):
228 | - boost-for-react-native (= 1.63.0)
229 | - DoubleConversion
230 | - Folly (= 2020.01.13.00)
231 | - glog
232 | - React-jsiexecutor (0.63.3):
233 | - DoubleConversion
234 | - Folly (= 2020.01.13.00)
235 | - glog
236 | - React-cxxreact (= 0.63.3)
237 | - React-jsi (= 0.63.3)
238 | - React-jsinspector (0.63.3)
239 | - React-RCTActionSheet (0.63.3):
240 | - React-Core/RCTActionSheetHeaders (= 0.63.3)
241 | - React-RCTAnimation (0.63.3):
242 | - FBReactNativeSpec (= 0.63.3)
243 | - Folly (= 2020.01.13.00)
244 | - RCTTypeSafety (= 0.63.3)
245 | - React-Core/RCTAnimationHeaders (= 0.63.3)
246 | - React-jsi (= 0.63.3)
247 | - ReactCommon/turbomodule/core (= 0.63.3)
248 | - React-RCTBlob (0.63.3):
249 | - FBReactNativeSpec (= 0.63.3)
250 | - Folly (= 2020.01.13.00)
251 | - React-Core/RCTBlobHeaders (= 0.63.3)
252 | - React-Core/RCTWebSocket (= 0.63.3)
253 | - React-jsi (= 0.63.3)
254 | - React-RCTNetwork (= 0.63.3)
255 | - ReactCommon/turbomodule/core (= 0.63.3)
256 | - React-RCTImage (0.63.3):
257 | - FBReactNativeSpec (= 0.63.3)
258 | - Folly (= 2020.01.13.00)
259 | - RCTTypeSafety (= 0.63.3)
260 | - React-Core/RCTImageHeaders (= 0.63.3)
261 | - React-jsi (= 0.63.3)
262 | - React-RCTNetwork (= 0.63.3)
263 | - ReactCommon/turbomodule/core (= 0.63.3)
264 | - React-RCTLinking (0.63.3):
265 | - FBReactNativeSpec (= 0.63.3)
266 | - React-Core/RCTLinkingHeaders (= 0.63.3)
267 | - React-jsi (= 0.63.3)
268 | - ReactCommon/turbomodule/core (= 0.63.3)
269 | - React-RCTNetwork (0.63.3):
270 | - FBReactNativeSpec (= 0.63.3)
271 | - Folly (= 2020.01.13.00)
272 | - RCTTypeSafety (= 0.63.3)
273 | - React-Core/RCTNetworkHeaders (= 0.63.3)
274 | - React-jsi (= 0.63.3)
275 | - ReactCommon/turbomodule/core (= 0.63.3)
276 | - React-RCTSettings (0.63.3):
277 | - FBReactNativeSpec (= 0.63.3)
278 | - Folly (= 2020.01.13.00)
279 | - RCTTypeSafety (= 0.63.3)
280 | - React-Core/RCTSettingsHeaders (= 0.63.3)
281 | - React-jsi (= 0.63.3)
282 | - ReactCommon/turbomodule/core (= 0.63.3)
283 | - React-RCTText (0.63.3):
284 | - React-Core/RCTTextHeaders (= 0.63.3)
285 | - React-RCTVibration (0.63.3):
286 | - FBReactNativeSpec (= 0.63.3)
287 | - Folly (= 2020.01.13.00)
288 | - React-Core/RCTVibrationHeaders (= 0.63.3)
289 | - React-jsi (= 0.63.3)
290 | - ReactCommon/turbomodule/core (= 0.63.3)
291 | - ReactCommon/turbomodule/core (0.63.3):
292 | - DoubleConversion
293 | - Folly (= 2020.01.13.00)
294 | - glog
295 | - React-callinvoker (= 0.63.3)
296 | - React-Core (= 0.63.3)
297 | - React-cxxreact (= 0.63.3)
298 | - React-jsi (= 0.63.3)
299 | - Yoga (1.14.0)
300 | - YogaKit (1.18.1):
301 | - Yoga (~> 1.14)
302 |
303 | DEPENDENCIES:
304 | - DoubleConversion (from `../node_modules/react-native/third-party-podspecs/DoubleConversion.podspec`)
305 | - FBLazyVector (from `../node_modules/react-native/Libraries/FBLazyVector`)
306 | - FBReactNativeSpec (from `../node_modules/react-native/Libraries/FBReactNativeSpec`)
307 | - Flipper (~> 0.54.0)
308 | - Flipper-DoubleConversion (= 1.1.7)
309 | - Flipper-Folly (~> 2.2)
310 | - Flipper-Glog (= 0.3.6)
311 | - Flipper-PeerTalk (~> 0.0.4)
312 | - Flipper-RSocket (~> 1.1)
313 | - FlipperKit (~> 0.54.0)
314 | - FlipperKit/Core (~> 0.54.0)
315 | - FlipperKit/CppBridge (~> 0.54.0)
316 | - FlipperKit/FBCxxFollyDynamicConvert (~> 0.54.0)
317 | - FlipperKit/FBDefines (~> 0.54.0)
318 | - FlipperKit/FKPortForwarding (~> 0.54.0)
319 | - FlipperKit/FlipperKitHighlightOverlay (~> 0.54.0)
320 | - FlipperKit/FlipperKitLayoutPlugin (~> 0.54.0)
321 | - FlipperKit/FlipperKitLayoutTextSearchable (~> 0.54.0)
322 | - FlipperKit/FlipperKitNetworkPlugin (~> 0.54.0)
323 | - FlipperKit/FlipperKitReactPlugin (~> 0.54.0)
324 | - FlipperKit/FlipperKitUserDefaultsPlugin (~> 0.54.0)
325 | - FlipperKit/SKIOSNetworkPlugin (~> 0.54.0)
326 | - Folly (from `../node_modules/react-native/third-party-podspecs/Folly.podspec`)
327 | - glog (from `../node_modules/react-native/third-party-podspecs/glog.podspec`)
328 | - RCTRequired (from `../node_modules/react-native/Libraries/RCTRequired`)
329 | - RCTTypeSafety (from `../node_modules/react-native/Libraries/TypeSafety`)
330 | - React (from `../node_modules/react-native/`)
331 | - React-callinvoker (from `../node_modules/react-native/ReactCommon/callinvoker`)
332 | - React-Core (from `../node_modules/react-native/`)
333 | - React-Core/DevSupport (from `../node_modules/react-native/`)
334 | - React-Core/RCTWebSocket (from `../node_modules/react-native/`)
335 | - React-CoreModules (from `../node_modules/react-native/React/CoreModules`)
336 | - React-cxxreact (from `../node_modules/react-native/ReactCommon/cxxreact`)
337 | - React-jsi (from `../node_modules/react-native/ReactCommon/jsi`)
338 | - React-jsiexecutor (from `../node_modules/react-native/ReactCommon/jsiexecutor`)
339 | - React-jsinspector (from `../node_modules/react-native/ReactCommon/jsinspector`)
340 | - React-RCTActionSheet (from `../node_modules/react-native/Libraries/ActionSheetIOS`)
341 | - React-RCTAnimation (from `../node_modules/react-native/Libraries/NativeAnimation`)
342 | - React-RCTBlob (from `../node_modules/react-native/Libraries/Blob`)
343 | - React-RCTImage (from `../node_modules/react-native/Libraries/Image`)
344 | - React-RCTLinking (from `../node_modules/react-native/Libraries/LinkingIOS`)
345 | - React-RCTNetwork (from `../node_modules/react-native/Libraries/Network`)
346 | - React-RCTSettings (from `../node_modules/react-native/Libraries/Settings`)
347 | - React-RCTText (from `../node_modules/react-native/Libraries/Text`)
348 | - React-RCTVibration (from `../node_modules/react-native/Libraries/Vibration`)
349 | - ReactCommon/turbomodule/core (from `../node_modules/react-native/ReactCommon`)
350 | - Yoga (from `../node_modules/react-native/ReactCommon/yoga`)
351 |
352 | SPEC REPOS:
353 | trunk:
354 | - boost-for-react-native
355 | - CocoaAsyncSocket
356 | - CocoaLibEvent
357 | - Flipper
358 | - Flipper-DoubleConversion
359 | - Flipper-Folly
360 | - Flipper-Glog
361 | - Flipper-PeerTalk
362 | - Flipper-RSocket
363 | - FlipperKit
364 | - OpenSSL-Universal
365 | - YogaKit
366 |
367 | EXTERNAL SOURCES:
368 | DoubleConversion:
369 | :podspec: "../node_modules/react-native/third-party-podspecs/DoubleConversion.podspec"
370 | FBLazyVector:
371 | :path: "../node_modules/react-native/Libraries/FBLazyVector"
372 | FBReactNativeSpec:
373 | :path: "../node_modules/react-native/Libraries/FBReactNativeSpec"
374 | Folly:
375 | :podspec: "../node_modules/react-native/third-party-podspecs/Folly.podspec"
376 | glog:
377 | :podspec: "../node_modules/react-native/third-party-podspecs/glog.podspec"
378 | RCTRequired:
379 | :path: "../node_modules/react-native/Libraries/RCTRequired"
380 | RCTTypeSafety:
381 | :path: "../node_modules/react-native/Libraries/TypeSafety"
382 | React:
383 | :path: "../node_modules/react-native/"
384 | React-callinvoker:
385 | :path: "../node_modules/react-native/ReactCommon/callinvoker"
386 | React-Core:
387 | :path: "../node_modules/react-native/"
388 | React-CoreModules:
389 | :path: "../node_modules/react-native/React/CoreModules"
390 | React-cxxreact:
391 | :path: "../node_modules/react-native/ReactCommon/cxxreact"
392 | React-jsi:
393 | :path: "../node_modules/react-native/ReactCommon/jsi"
394 | React-jsiexecutor:
395 | :path: "../node_modules/react-native/ReactCommon/jsiexecutor"
396 | React-jsinspector:
397 | :path: "../node_modules/react-native/ReactCommon/jsinspector"
398 | React-RCTActionSheet:
399 | :path: "../node_modules/react-native/Libraries/ActionSheetIOS"
400 | React-RCTAnimation:
401 | :path: "../node_modules/react-native/Libraries/NativeAnimation"
402 | React-RCTBlob:
403 | :path: "../node_modules/react-native/Libraries/Blob"
404 | React-RCTImage:
405 | :path: "../node_modules/react-native/Libraries/Image"
406 | React-RCTLinking:
407 | :path: "../node_modules/react-native/Libraries/LinkingIOS"
408 | React-RCTNetwork:
409 | :path: "../node_modules/react-native/Libraries/Network"
410 | React-RCTSettings:
411 | :path: "../node_modules/react-native/Libraries/Settings"
412 | React-RCTText:
413 | :path: "../node_modules/react-native/Libraries/Text"
414 | React-RCTVibration:
415 | :path: "../node_modules/react-native/Libraries/Vibration"
416 | ReactCommon:
417 | :path: "../node_modules/react-native/ReactCommon"
418 | Yoga:
419 | :path: "../node_modules/react-native/ReactCommon/yoga"
420 |
421 | SPEC CHECKSUMS:
422 | boost-for-react-native: 39c7adb57c4e60d6c5479dd8623128eb5b3f0f2c
423 | CocoaAsyncSocket: 694058e7c0ed05a9e217d1b3c7ded962f4180845
424 | CocoaLibEvent: 2fab71b8bd46dd33ddb959f7928ec5909f838e3f
425 | DoubleConversion: cde416483dac037923206447da6e1454df403714
426 | FBLazyVector: 878b59e31113e289e275165efbe4b54fa614d43d
427 | FBReactNativeSpec: 7da9338acfb98d4ef9e5536805a0704572d33c2f
428 | Flipper: be611d4b742d8c87fbae2ca5f44603a02539e365
429 | Flipper-DoubleConversion: 38631e41ef4f9b12861c67d17cb5518d06badc41
430 | Flipper-Folly: e4493b013c02d9347d5e0cb4d128680239f6c78a
431 | Flipper-Glog: 1dfd6abf1e922806c52ceb8701a3599a79a200a6
432 | Flipper-PeerTalk: 116d8f857dc6ef55c7a5a75ea3ceaafe878aadc9
433 | Flipper-RSocket: 64e7431a55835eb953b0bf984ef3b90ae9fdddd7
434 | FlipperKit: ab353d41aea8aae2ea6daaf813e67496642f3d7d
435 | Folly: b73c3869541e86821df3c387eb0af5f65addfab4
436 | glog: 40a13f7840415b9a77023fbcae0f1e6f43192af3
437 | OpenSSL-Universal: ff34003318d5e1163e9529b08470708e389ffcdd
438 | RCTRequired: 48884c74035a0b5b76dbb7a998bd93bcfc5f2047
439 | RCTTypeSafety: edf4b618033c2f1c5b7bc3d90d8e085ed95ba2ab
440 | React: f36e90f3ceb976546e97df3403e37d226f79d0e3
441 | React-callinvoker: 18874f621eb96625df7a24a7dc8d6e07391affcd
442 | React-Core: ac3d816b8e3493970153f4aaf0cff18af0bb95e6
443 | React-CoreModules: 4016d3a4e518bcfc4f5a51252b5a05692ca6f0e1
444 | React-cxxreact: ffc9129013b87cb36cf3f30a86695a3c397b0f99
445 | React-jsi: df07aa95b39c5be3e41199921509bfa929ed2b9d
446 | React-jsiexecutor: b56c03e61c0dd5f5801255f2160a815f4a53d451
447 | React-jsinspector: 8e68ffbfe23880d3ee9bafa8be2777f60b25cbe2
448 | React-RCTActionSheet: 53ea72699698b0b47a6421cb1c8b4ab215a774aa
449 | React-RCTAnimation: 1befece0b5183c22ae01b966f5583f42e69a83c2
450 | React-RCTBlob: 0b284339cbe4b15705a05e2313a51c6d8b51fa40
451 | React-RCTImage: d1756599ebd4dc2cb19d1682fe67c6b976658387
452 | React-RCTLinking: 9af0a51c6d6a4dd1674daadafffc6d03033a6d18
453 | React-RCTNetwork: 332c83929cc5eae0b3bbca4add1d668e1fc18bda
454 | React-RCTSettings: d6953772cfd55f2c68ad72b7ef29efc7ec49f773
455 | React-RCTText: 65a6de06a7389098ce24340d1d3556015c38f746
456 | React-RCTVibration: 8e9fb25724a0805107fc1acc9075e26f814df454
457 | ReactCommon: 4167844018c9ed375cc01a843e9ee564399e53c3
458 | Yoga: 7d13633d129fd179e01b8953d38d47be90db185a
459 | YogaKit: f782866e155069a2cca2517aafea43200b01fd5a
460 |
461 | PODFILE CHECKSUM: 311cf87a4a33d759b7ec994ec3735e03d4ededbf
462 |
463 | COCOAPODS: 1.10.0
464 |
--------------------------------------------------------------------------------
/android/src/main/java/com/sunmi/v2/printer/BitmapUtils.java:
--------------------------------------------------------------------------------
1 | package com.sunmi.v2.printer;
2 |
3 | import java.io.Closeable;
4 | import java.io.File;
5 | import java.io.FileNotFoundException;
6 | import java.io.FileOutputStream;
7 | import java.io.InputStream;
8 | import java.io.OutputStream;
9 |
10 | import android.content.Context;
11 | import android.content.res.Resources;
12 | import android.database.Cursor;
13 | import android.graphics.Bitmap;
14 | import android.graphics.Bitmap.CompressFormat;
15 | import android.graphics.BitmapFactory;
16 | import android.graphics.Canvas;
17 | import android.graphics.Color;
18 | import android.graphics.Matrix;
19 | import android.graphics.Paint;
20 | import android.graphics.Rect;
21 | import android.graphics.RectF;
22 | import android.net.Uri;
23 | import android.provider.MediaStore.Images.ImageColumns;
24 | import android.util.Log;
25 |
26 | /**
27 | * bitmap操作的工具类
28 | *
29 | * @author longtao.li
30 | * 2012-10-18
31 | *
32 | */
33 | public class BitmapUtils {
34 |
35 | private static final String TAG = "BitmapUtils";
36 | private static final int DEFAULT_COMPRESS_QUALITY = 50;
37 | private static final int INDEX_ORIENTATION = 0;
38 |
39 | private static final String[] IMAGE_PROJECTION = new String[] {
40 | ImageColumns.ORIENTATION
41 | };
42 |
43 | private final Context context;
44 |
45 | public BitmapUtils(Context context) {
46 | this.context = context;
47 | }
48 |
49 | /**
50 | * Creates a mutable bitmap from subset of source bitmap, transformed by the optional matrix.
51 | */
52 | private static Bitmap createBitmap(
53 | Bitmap source, int x, int y, int width, int height, Matrix m) {
54 | // Re-implement Bitmap createBitmap() to always return a mutable bitmap.
55 | Canvas canvas = new Canvas();
56 |
57 | Bitmap bitmap;
58 | Paint paint;
59 | if ((m == null) || m.isIdentity()) {
60 | bitmap = Bitmap.createBitmap(width, height, source.getConfig());
61 | paint = null;
62 | } else {
63 | RectF rect = new RectF(0, 0, width, height);
64 | m.mapRect(rect);
65 | bitmap = Bitmap.createBitmap(
66 | Math.round(rect.width()), Math.round(rect.height()), source.getConfig());
67 |
68 | canvas.translate(-rect.left, -rect.top);
69 | canvas.concat(m);
70 |
71 | paint = new Paint(Paint.FILTER_BITMAP_FLAG);
72 | if (!m.rectStaysRect()) {
73 | paint.setAntiAlias(true);
74 | }
75 | }
76 | bitmap.setDensity(source.getDensity());
77 | canvas.setBitmap(bitmap);
78 |
79 | Rect srcBounds = new Rect(x, y, x + width, y + height);
80 | RectF dstBounds = new RectF(0, 0, width, height);
81 | canvas.drawBitmap(source, srcBounds, dstBounds, paint);
82 | return bitmap;
83 | }
84 |
85 | private void closeStream(Closeable stream) {
86 | if (stream != null) {
87 | try {
88 | stream.close();
89 | } catch (Exception e) {
90 | e.printStackTrace();
91 | }
92 | }
93 | }
94 |
95 | public Rect getBitmapBounds(byte[] data){
96 | Rect bounds = new Rect();
97 | try {
98 | BitmapFactory.Options options = new BitmapFactory.Options();
99 | options.inJustDecodeBounds = true;
100 | BitmapFactory.decodeByteArray(data, 0, data.length, options);
101 | bounds.right = options.outWidth;
102 | bounds.bottom = options.outHeight;
103 | Log.i(TAG, "options.outWidth="+options.outWidth+" , "+"options.outHeight="+options.outHeight);
104 | } catch (Exception e) {
105 | }finally {
106 | }
107 | return bounds;
108 | }
109 |
110 | public Rect getBitmapBounds(Uri uri) {
111 | Rect bounds = new Rect();
112 | InputStream is = null;
113 |
114 | try {
115 | is = context.getContentResolver().openInputStream(uri);
116 | BitmapFactory.Options options = new BitmapFactory.Options();
117 | options.inJustDecodeBounds = true;
118 | BitmapFactory.decodeStream(is, null, options);
119 |
120 | bounds.right = options.outWidth;
121 | bounds.bottom = options.outHeight;
122 | Log.i(TAG, "options.outWidth="+options.outWidth+" , "+"options.outHeight="+options.outHeight);
123 | } catch (Exception e) {
124 | e.printStackTrace();
125 | } finally {
126 | closeStream(is);
127 | }
128 |
129 | return bounds;
130 | }
131 |
132 | public Rect getBitmapBounds(InputStream is, boolean isClose) {
133 | Rect bounds = new Rect();
134 |
135 | try {
136 | BitmapFactory.Options options = new BitmapFactory.Options();
137 | options.inJustDecodeBounds = true;
138 | BitmapFactory.decodeStream(is, null, options);
139 |
140 | bounds.right = options.outWidth;
141 | bounds.bottom = options.outHeight;
142 | Log.i(TAG, "options.outWidth="+options.outWidth+" , "+"options.outHeight="+options.outHeight);
143 | } catch (Exception e) {
144 | e.printStackTrace();
145 | } finally {
146 | if( isClose )
147 | closeStream(is);
148 | }
149 |
150 | return bounds;
151 | }
152 |
153 | private int getOrientation(Uri uri) {
154 | int orientation = 0;
155 | Cursor cursor = null;
156 | try {
157 | cursor = context.getContentResolver().query(uri, IMAGE_PROJECTION, null, null, null);
158 | if ((cursor != null) && cursor.moveToNext()) {
159 | orientation = cursor.getInt(INDEX_ORIENTATION);
160 | }
161 | } catch (Exception e) {
162 | // Ignore error for no orientation column; just use the default orientation value 0.
163 | } finally {
164 | if (cursor != null) {
165 | cursor.close();
166 | }
167 | }
168 | return orientation;
169 | }
170 |
171 | /**
172 | * Decodes bitmap (maybe immutable) that keeps aspect-ratio and spans most within the bounds.
173 | */
174 | public Bitmap decodeBitmapByStream(InputStream is, Rect bounds, int width, int height) {
175 | Log.i(TAG, "width = " + width + " , " + "height = " + height);
176 | Bitmap bitmap = null;
177 | try {
178 | // TODO: Take max pixels allowed into account for calculation to avoid possible OOM.
179 | // Rect bounds = getBitmapBounds(is, false);
180 | int sampleSize = Math.max(bounds.width() / width, bounds.height() / height);
181 | sampleSize = Math.min(sampleSize,
182 | Math.max(bounds.width() / height, bounds.height() / width));
183 |
184 | BitmapFactory.Options options = new BitmapFactory.Options();
185 | options.inSampleSize = Math.max(sampleSize, 1);
186 | options.inPreferredConfig = Bitmap.Config.ARGB_8888;
187 |
188 | Log.i(TAG, "sampleSize = " + sampleSize + " , " + "options.inSampleSize = " + options.inSampleSize);
189 | bitmap = BitmapFactory.decodeStream(is, null, options);//!!!!溢出
190 | } catch (Exception e) {
191 | Log.e(TAG, e.getMessage());
192 | } finally {
193 | closeStream(is);
194 | }
195 |
196 | // Ensure bitmap in 8888 format, good for editing as well as GL compatible.
197 | if ((bitmap != null) && (bitmap.getConfig() != Bitmap.Config.ARGB_8888)) {
198 | Bitmap copy = bitmap.copy(Bitmap.Config.ARGB_8888, true);
199 | bitmap.recycle();
200 | bitmap = copy;
201 | }
202 |
203 | if (bitmap != null) {
204 | // Scale down the sampled bitmap if it's still larger than the desired dimension.
205 | float scale = Math.min((float) width / bitmap.getWidth(),
206 | (float) height / bitmap.getHeight());
207 | scale = Math.max(scale, Math.min((float) height / bitmap.getWidth(),
208 | (float) width / bitmap.getHeight()));
209 | if (scale < 1) {
210 | Matrix m = new Matrix();
211 | m.setScale(scale, scale);
212 | Bitmap transformed = createBitmap(
213 | bitmap, 0, 0, bitmap.getWidth(), bitmap.getHeight(), m);
214 | bitmap.recycle();
215 | return transformed;
216 | }
217 | }
218 | return bitmap;
219 | }
220 |
221 | /**
222 | * Decodes bitmap (maybe immutable) that keeps aspect-ratio and spans most within the bounds.
223 | */
224 | public Bitmap decodeBitmap(byte[] data, int width, int height){
225 | Log.i(TAG, "width = " + width + " , " + "height = " + height);
226 | Bitmap bitmap = null;
227 | try {
228 | // TODO: Take max pixels allowed into account for calculation to avoid possible OOM.
229 | Rect bounds = getBitmapBounds(data);
230 | int sampleSize = Math.max(bounds.width() / width, bounds.height() / height);
231 | sampleSize = Math.min(sampleSize,
232 | Math.max(bounds.width() / height, bounds.height() / width));
233 |
234 | BitmapFactory.Options options = new BitmapFactory.Options();
235 | options.inSampleSize = Math.max(sampleSize, 1);
236 | options.inPreferredConfig = Bitmap.Config.ARGB_8888;
237 |
238 | Log.i(TAG, "sampleSize = " + sampleSize + " , " + "options.inSampleSize = " + options.inSampleSize);
239 | bitmap = BitmapFactory.decodeByteArray(data, 0, data.length, options);//!!!!溢出
240 | } catch (Exception e) {
241 | Log.e(TAG, e.getMessage());
242 | } finally {
243 | data = null;
244 | }
245 |
246 | // Ensure bitmap in 8888 format, good for editing as well as GL compatible.
247 | if ((bitmap != null) && (bitmap.getConfig() != Bitmap.Config.ARGB_8888)) {
248 | Bitmap copy = bitmap.copy(Bitmap.Config.ARGB_8888, true);
249 | bitmap.recycle();
250 | bitmap = copy;
251 | }
252 |
253 | if (bitmap != null) {
254 | // Scale down the sampled bitmap if it's still larger than the desired dimension.
255 | float scale = Math.min((float) width / bitmap.getWidth(),
256 | (float) height / bitmap.getHeight());
257 | scale = Math.max(scale, Math.min((float) height / bitmap.getWidth(),
258 | (float) width / bitmap.getHeight()));
259 | if (scale < 1) {
260 | Matrix m = new Matrix();
261 | m.setScale(scale, scale);
262 | Bitmap transformed = createBitmap(
263 | bitmap, 0, 0, bitmap.getWidth(), bitmap.getHeight(), m);
264 | bitmap.recycle();
265 | return transformed;
266 | }
267 | }
268 | return bitmap;
269 | }
270 |
271 | /**
272 | * Decodes bitmap (maybe immutable) that keeps aspect-ratio and spans most within the bounds.
273 | */
274 | private Bitmap decodeBitmap(Uri uri, int width, int height) {
275 | Log.i(TAG, "width = " + width + " , " + "height = " + height);
276 | InputStream is = null;
277 | Bitmap bitmap = null;
278 |
279 | try {
280 | // TODO: Take max pixels allowed into account for calculation to avoid possible OOM.
281 | Rect bounds = getBitmapBounds(uri);
282 | int sampleSize = Math.max(bounds.width() / width, bounds.height() / height);
283 | sampleSize = Math.min(sampleSize,
284 | Math.max(bounds.width() / height, bounds.height() / width));
285 |
286 | BitmapFactory.Options options = new BitmapFactory.Options();
287 | options.inSampleSize = Math.max(sampleSize, 1);
288 | options.inPreferredConfig = Bitmap.Config.ARGB_8888;
289 |
290 | is = context.getContentResolver().openInputStream(uri);
291 | Log.i(TAG, "sampleSize = " + sampleSize + " , " + "options.inSampleSize = " + options.inSampleSize);
292 | bitmap = BitmapFactory.decodeStream(is, null, options);//!!!!溢出
293 | } catch (Exception e) {
294 | } finally {
295 | closeStream(is);
296 | }
297 |
298 | // Ensure bitmap in 8888 format, good for editing as well as GL compatible.
299 | if ((bitmap != null) && (bitmap.getConfig() != Bitmap.Config.ARGB_8888)) {
300 | Bitmap copy = bitmap.copy(Bitmap.Config.ARGB_8888, true);
301 | bitmap.recycle();
302 | bitmap = copy;
303 | }
304 |
305 | if (bitmap != null) {
306 | // Scale down the sampled bitmap if it's still larger than the desired dimension.
307 | float scale = Math.min((float) width / bitmap.getWidth(),
308 | (float) height / bitmap.getHeight());
309 | scale = Math.max(scale, Math.min((float) height / bitmap.getWidth(),
310 | (float) width / bitmap.getHeight()));
311 | if (scale < 1) {
312 | Matrix m = new Matrix();
313 | m.setScale(scale, scale);
314 | Bitmap transformed = createBitmap(
315 | bitmap, 0, 0, bitmap.getWidth(), bitmap.getHeight(), m);
316 | bitmap.recycle();
317 | return transformed;
318 | }
319 | }
320 | return bitmap;
321 | }
322 |
323 | public Bitmap transform(Bitmap bitmap, int width, int height){
324 | // Scale down the sampled bitmap if it's still larger than the desired dimension.
325 | float scale = Math.min((float) width / bitmap.getWidth(),
326 | (float) height / bitmap.getHeight());
327 | scale = Math.max(scale, Math.min((float) height / bitmap.getWidth(),
328 | (float) width / bitmap.getHeight()));
329 | if (scale < 1) {
330 | Matrix m = new Matrix();
331 | m.setScale(scale, scale);
332 | Bitmap transformed = createBitmap(
333 | bitmap, 0, 0, bitmap.getWidth(), bitmap.getHeight(), m);
334 | bitmap.recycle();
335 | return transformed;
336 | }
337 | return bitmap;
338 | }
339 |
340 | /**
341 | * Gets decoded bitmap that keeps orientation as well.
342 | */
343 | public Bitmap getBitmap(Uri uri, int width, int height) {
344 | Bitmap bitmap = decodeBitmap(uri, width, height);
345 |
346 | // Rotate the decoded bitmap according to its orientation if it's necessary.
347 | if (bitmap != null) {
348 | int orientation = getOrientation(uri);
349 | if (orientation != 0) {
350 | Matrix m = new Matrix();
351 | m.setRotate(orientation);
352 | Bitmap transformed = createBitmap(
353 | bitmap, 0, 0, bitmap.getWidth(), bitmap.getHeight(), m);
354 | bitmap.recycle();
355 | return transformed;
356 | }
357 | }
358 | return bitmap;
359 | }
360 |
361 | /**
362 | * Saves the bitmap by given directory, filename, and format; if the directory is given null,
363 | * then saves it under the cache directory.
364 | */
365 | public File saveBitmap(
366 | Bitmap bitmap, String directory, String filename, CompressFormat format) {
367 |
368 | if (directory == null) {
369 | directory = context.getCacheDir().getAbsolutePath();
370 | } else {
371 | // Check if the given directory exists or try to create it.
372 | File file = new File(directory);
373 | if (!file.isDirectory() && !file.mkdirs()) {
374 | return null;
375 | }
376 | }
377 |
378 | File file = null;
379 | OutputStream os = null;
380 |
381 | try {
382 | filename = (format == CompressFormat.PNG) ? filename + ".png" : filename + ".jpg";
383 | file = new File(directory, filename);
384 | os = new FileOutputStream(file);
385 | bitmap.compress(format, DEFAULT_COMPRESS_QUALITY, os);
386 | } catch (FileNotFoundException e) {
387 | e.printStackTrace();
388 | } finally {
389 | closeStream(os);
390 | }
391 | return file;
392 | }
393 |
394 | /**
395 | * 缩放bitmap
396 | * @param bitmap
397 | * @param w
398 | * @param h
399 | * @return
400 | */
401 | public static Bitmap zoomBitmap(Bitmap bitmap, float w, float h){
402 | int width = bitmap.getWidth();
403 | int height = bitmap.getHeight();
404 | Matrix matrix = new Matrix();
405 | float scaleW = ((float)w / width);
406 | float scaleH = ((float)h / height);
407 | matrix.postScale(scaleW, scaleH);
408 | Bitmap newBmp = Bitmap.createBitmap(bitmap, 0, 0, width, height, matrix, true);
409 |
410 | return newBmp;
411 | }
412 |
413 | public static Bitmap rotateBitmap(Bitmap bitmap, float degrees){
414 | if (degrees != 0 && bitmap != null) {
415 | Matrix m = new Matrix();
416 | m.postRotate(degrees);
417 | // m.setRotate(degrees,
418 | // (float) bitmap.getWidth() / 2, (float) bitmap.getHeight() / 2);
419 | try {
420 | bitmap = Bitmap.createBitmap(
421 | bitmap, 0, 0, bitmap.getWidth(), bitmap.getHeight(), m, true);
422 | // if (bitmap != b2) {
423 | // bitmap.recycle(); //Android开发网再次提示Bitmap操作完应该显示的释放
424 | // bitmap = b2;
425 | // }
426 | } catch (OutOfMemoryError ex) {
427 | // Android建议大家如果出现了内存不足异常,最好return 原始的bitmap对象。.
428 | }
429 | }
430 | return bitmap;
431 | }
432 |
433 | public static Bitmap drawTextToBitmap(Context gContext, int gResId, String gText) {
434 | Log.i(TAG, "drawTextToBitmap = " + gText);
435 | Resources resources = gContext.getResources();
436 | float scale = resources.getDisplayMetrics().density;
437 | Bitmap bitmap =
438 | BitmapFactory.decodeResource(resources, gResId);
439 |
440 | Bitmap.Config bitmapConfig =
441 | bitmap.getConfig();
442 | // set default bitmap config if none
443 | if(bitmapConfig == null) {
444 | bitmapConfig = Bitmap.Config.ARGB_8888;
445 | }
446 | // resource bitmaps are imutable,
447 | // so we need to convert it to mutable one
448 | bitmap = bitmap.copy(bitmapConfig, true);
449 |
450 | Canvas canvas = new Canvas(bitmap);
451 | // new antialised Paint
452 | Paint paint = new Paint(Paint.ANTI_ALIAS_FLAG);
453 | // text color - #3D3D3D
454 | paint.setColor(Color.WHITE);
455 | // text size in pixels
456 | paint.setTextSize((int) (12 * scale));
457 | // text shadow
458 | // paint.setShadowLayer(1f, 0f, 1f, Color.WHITE);
459 |
460 | // draw text to the Canvas center
461 | Rect bounds = new Rect();
462 | paint.getTextBounds(gText, 0, gText.length(), bounds);
463 | int x = (bitmap.getWidth() - bounds.width())/2;
464 | int y = (bitmap.getHeight())/2 + (int)scale*2;
465 |
466 | canvas.drawText(gText, x, y, paint);
467 |
468 | canvas.save();
469 | canvas.restore();
470 |
471 | return bitmap;
472 | }
473 |
474 | }
475 |
--------------------------------------------------------------------------------
/android/src/main/java/com/sunmi/v2/printer/BytesUtil.java:
--------------------------------------------------------------------------------
1 | package com.sunmi.v2.printer;
2 |
3 | import java.io.ByteArrayOutputStream;
4 | import java.util.Hashtable;
5 |
6 | import com.google.zxing.BarcodeFormat;
7 | import com.google.zxing.EncodeHintType;
8 | import com.google.zxing.WriterException;
9 | import com.google.zxing.common.BitMatrix;
10 | import com.google.zxing.qrcode.QRCodeWriter;
11 |
12 | import android.annotation.SuppressLint;
13 | import android.graphics.Bitmap;
14 |
15 | public class BytesUtil {
16 | /**
17 | * 生成间断性黑块数据
18 | * @param w : 打印纸宽度, 单位点
19 | * @return
20 | */
21 | public static byte[] initBlackBlock(int w){
22 | int ww = (w + 7)/8 ;
23 | int n = (ww + 11)/12;
24 | int hh = n * 24;
25 | byte[] data = new byte[ hh * ww + 10];
26 |
27 | data[0] = 0x0A;
28 | data[1] = 0x1D;
29 | data[2] = 0x76;
30 | data[3] = 0x30;
31 | data[4] = 0x00;
32 |
33 | data[5] = (byte)ww;//xL
34 | data[6] = (byte)(ww >> 8);//xH
35 | data[7] = (byte)hh;
36 | data[8] = (byte)(hh >> 8);
37 |
38 | int k = 9;
39 | for(int i=0; i < n; i++){
40 | for(int j=0; j<24; j++){
41 | for(int m =0; m> 8);//xH
71 | data[6] = (byte)hh;
72 | data[7] = (byte)(hh >> 8);
73 |
74 | int k = 8;
75 | for(int i=0; i> 8);//xH
101 | data[6] = (byte)hh;
102 | data[7] = (byte)(hh >> 8);
103 |
104 | int k = 8;
105 | byte m =(byte)0xAA;
106 | for(int i=0; i> 8);//xH
137 | data[7] = (byte)hh;
138 | data[8] = (byte)(hh >> 8);
139 |
140 | int k = 9;
141 | int m = 31;
142 | for(int i=0; i> 8);
264 | return returnText;
265 | }
266 |
267 | public static byte[] PrintBarcode(String stBarcode) {
268 | int iLength = stBarcode.length() + 4;
269 | byte[] returnText = new byte[iLength];
270 |
271 | returnText[0] = 0x1D;
272 | returnText[1] = 'k';
273 | returnText[2] = 0x45;
274 | returnText[3] = (byte) stBarcode.length(); // 条码长度;
275 |
276 | System.arraycopy(stBarcode.getBytes(), 0, returnText, 4,
277 | stBarcode.getBytes().length);
278 |
279 | return returnText;
280 | }
281 |
282 | public static byte[] CutPaper() {
283 | byte[] returnText = {0x20,0x0A, 0x1D, 0x56, 0x42, 0x00 }; // 切纸; GS V
284 | // 66D 0D
285 | return returnText;
286 | }
287 |
288 | public static byte[] selfCheck(){
289 | byte[] returnText = {0x1F, 0x1B, 0x1F, 0x53};
290 | return returnText;
291 | }
292 |
293 | public static byte[] getPrinterStatus(){
294 | byte[] data = {0x0A,0x10,0x04,0x01};
295 | return data;
296 | }
297 |
298 |
299 | public static String getHexStringFromBytes(byte[] data){
300 | if(data == null || data.length <= 0){
301 | return null;
302 | }
303 | String hexString = "0123456789ABCDEF";
304 | int size = data.length * 2;
305 | StringBuilder sb = new StringBuilder(size);
306 | for (int i = 0; i < data.length; i++) {
307 | sb.append(hexString.charAt((data[i] & 0xF0) >> 4));
308 | sb.append(hexString.charAt((data[i] & 0x0F) >> 0));
309 | }
310 | return sb.toString();
311 | }
312 |
313 | //一个二维码
314 | /**
315 | * 打印二维码
316 | * @param code: 二维码数据
317 | * @param modulesize: 二维码块大小(单位:点, 取值 1 至 16 )
318 | * @param errorlevel: 二维码纠错等级(0 至 3)
319 | * 0 -- 纠错级别L ( 7%)
320 | * 1 -- 纠错级别M (15%)
321 | * 2 -- 纠错级别Q (25%)
322 | * 3 -- 纠错级别H (30%)
323 | */
324 |
325 | public static byte[] getPrintQRCode(String code, int modulesize, int errorlevel){
326 | ByteArrayOutputStream buffer = new ByteArrayOutputStream();
327 | try{
328 | buffer.write(setQRCodeSize(modulesize));
329 | buffer.write(setQRCodeErrorLevel(errorlevel));
330 | buffer.write(getQCodeBytes(code));
331 | buffer.write(getBytesForPrintQRCode(true));
332 | }catch(Exception e){
333 | e.printStackTrace();
334 | }
335 | return buffer.toByteArray();
336 | }
337 |
338 | //两个二维码
339 | /**
340 | * 打印二维码
341 | * @param code1: 二维码数据
342 | * @param code2: 二维码数据
343 | * @param modulesize: 二维码块大小(单位:点, 取值 1 至 16 )
344 | * @param errorlevel: 二维码纠错等级(0 至 3)
345 | * 0 -- 纠错级别L ( 7%)
346 | * 1 -- 纠错级别M (15%)
347 | * 2 -- 纠错级别Q (25%)
348 | * 3 -- 纠错级别H (30%)
349 | */
350 |
351 | public static byte[] getPrintDoubleQRCode(String code1, String code2, int modulesize, int errorlevel){
352 | ByteArrayOutputStream buffer = new ByteArrayOutputStream();
353 | try{
354 | buffer.write(setQRCodeSize(modulesize));
355 | buffer.write(setQRCodeErrorLevel(errorlevel));
356 | buffer.write(getQCodeBytes(code1));
357 | buffer.write(getBytesForPrintQRCode(false));
358 | buffer.write(getQCodeBytes(code2));
359 |
360 | //加入横向间隔
361 | buffer.write(new byte[]{0x1B, 0x5C, 0x30, 0x00});
362 |
363 | buffer.write(getBytesForPrintQRCode(true));
364 | }catch(Exception e){
365 | e.printStackTrace();
366 | }
367 | return buffer.toByteArray();
368 | }
369 |
370 | //一维码
371 | public static byte[] getPrintBarCode(String data, int symbology,
372 | int height, int width, int textposition){
373 |
374 | if(symbology < 0 || symbology > 8){
375 | return new byte[]{0x0A};
376 | }
377 | if(width < 2 || width > 6){
378 | width = 2;
379 | }
380 | if(textposition <0 || textposition > 3){
381 | textposition = 0;
382 | }
383 | if(height < 1 || height>255){
384 | height = 162;
385 | }
386 |
387 | ByteArrayOutputStream buffer = new ByteArrayOutputStream();
388 | try{
389 | buffer.write(new byte[]{0x1D,0x66,0x01,0x1D,0x48,(byte)textposition,
390 | 0x1D,0x77,(byte)width,0x1D,0x68,(byte)height,0x0A});
391 |
392 | byte[] barcode = data.getBytes();
393 |
394 | if(symbology == 8){
395 | buffer.write(new byte[]{0x1D,0x6B,0x49,(byte)(barcode.length+2),0x7B,0x42});
396 | }else{
397 | buffer.write(new byte[]{0x1D,0x6B,(byte)(symbology + 0x41),(byte)barcode.length});
398 | }
399 |
400 | buffer.write(barcode);
401 |
402 | }catch(Exception e){
403 | e.printStackTrace();
404 | }
405 | return buffer.toByteArray();
406 | }
407 |
408 | ////////////////////////////////////////////////////////////////////////////////////
409 | ////////////////////////// private /////////////////////////
410 | ////////////////////////////////////////////////////////////////////////////////////
411 |
412 |
413 | private static byte[] setQRCodeSize(int modulesize){
414 | //二维码块大小设置指令
415 | byte[] dtmp = new byte[8];
416 | dtmp[0] = 0x1D;
417 | dtmp[1] = 0x28;
418 | dtmp[2] = 0x6B;
419 | dtmp[3] = 0x03;
420 | dtmp[4] = 0x00;
421 | dtmp[5] = 0x31;
422 | dtmp[6] = 0x43;
423 | dtmp[7] = (byte)modulesize;
424 | return dtmp;
425 | }
426 | private static byte[] setQRCodeErrorLevel(int errorlevel){
427 | //二维码纠错等级设置指令
428 | byte[] dtmp = new byte[8];
429 | dtmp[0] = 0x1D;
430 | dtmp[1] = 0x28;
431 | dtmp[2] = 0x6B;
432 | dtmp[3] = 0x03;
433 | dtmp[4] = 0x00;
434 | dtmp[5] = 0x31;
435 | dtmp[6] = 0x45;
436 | dtmp[7] = (byte)(48+errorlevel);
437 | return dtmp;
438 | }
439 | private static byte[] getBytesForPrintQRCode(boolean single){
440 | //打印已存入数据的二维码
441 | byte[] dtmp;
442 | if(single){ //同一行只打印一个QRCode, 后面加换行
443 | dtmp = new byte[9];
444 | dtmp[8] = 0x0A;
445 | }else{
446 | dtmp = new byte[8];
447 | }
448 | dtmp[0] = 0x1D;
449 | dtmp[1] = 0x28;
450 | dtmp[2] = 0x6B;
451 | dtmp[3] = 0x03;
452 | dtmp[4] = 0x00;
453 | dtmp[5] = 0x31;
454 | dtmp[6] = 0x51;
455 | dtmp[7] = 0x30;
456 | return dtmp;
457 | }
458 | private static byte[] getQCodeBytes(String code){
459 | //二维码存入指令
460 | ByteArrayOutputStream buffer = new ByteArrayOutputStream();
461 | try{
462 | byte[] d = getGbk(code);
463 | int len = d.length + 3;
464 | if(len > 7092)len = 7092;
465 | buffer.write((byte)0x1D);
466 | buffer.write((byte)0x28);
467 | buffer.write((byte)0x6B);
468 | buffer.write((byte)len);
469 | buffer.write((byte)(len >> 8));
470 | buffer.write((byte)0x31);
471 | buffer.write((byte)0x50);
472 | buffer.write((byte)0x30);
473 | for(int i=0; i> 8);//xH
596 | data[6] = 13; //高度13
597 | data[7] = 0;
598 |
599 | int k = 8;
600 | for(int i=0; i < 3 * ww; i++){
601 | data[k++] = 0;
602 | }
603 | for(int i=0; i < ww; i++){
604 | data[k++] = kk[type][0];
605 | }
606 | for(int i=0; i < ww; i++){
607 | data[k++] = kk[type][1];
608 | }
609 | for(int i=0; i < ww; i++){
610 | data[k++] = kk[type][2];
611 | }
612 | for(int i=0; i < ww; i++){
613 | data[k++] = kk[type][3];
614 | }
615 | for(int i=0; i < ww; i++){
616 | data[k++] = kk[type][4];
617 | }
618 | for(int i=0; i < ww; i++){
619 | data[k++] = kk[type][5];
620 | }
621 | for(int i=0; i < ww; i++){
622 | data[k++] = kk[type][6];
623 | }
624 | for(int i=0; i < 3 * ww; i++){
625 | data[k++] = 0;
626 | }
627 | return data;
628 | }
629 | public static byte[] initLine2(int w){
630 | int ww = (w + 7)/8;
631 |
632 | byte[] data = new byte[ 12 * ww + 8];
633 |
634 | data[0] = 0x1D;
635 | data[1] = 0x76;
636 | data[2] = 0x30;
637 | data[3] = 0x00;
638 |
639 | data[4] = (byte)ww;//xL
640 | data[5] = (byte)(ww >> 8);//xH
641 | data[6] = 12; //高度13
642 | data[7] = 0;
643 |
644 | int k = 8;
645 | for(int i=0; i < 5 * ww; i++){
646 | data[k++] = 0;
647 | }
648 | for(int i=0; i < ww; i++){
649 | data[k++] = 0x7f;
650 | }
651 | for(int i=0; i < ww; i++){
652 | data[k++] = 0x7f;
653 | }
654 | for(int i=0; i < 5 * ww; i++){
655 | data[k++] = 0;
656 | }
657 | return data;
658 | }
659 |
660 | public static Bitmap getLineBitmapFromData(int size, int width){
661 | int[] pixels = createLineData(size, width);
662 | return getBitmapFromData(pixels, width, size + 6);
663 | }
664 | private static byte getBitMatrixColor(BitMatrix bits, int x, int y){
665 | int width = bits.getWidth();
666 | int height = bits.getHeight();
667 | if( x >= width || y >= height || x < 0 || y < 0)return 0;
668 | if(bits.get(x, y)){
669 | return 1;
670 | }else{
671 | return 0;
672 | }
673 | }
674 |
675 | public static byte[] getBytesFromBitMatrix(BitMatrix bits){
676 | if(bits == null)return null;
677 |
678 | int h = bits.getHeight();
679 | int w = (bits.getWidth()+7) / 8;
680 | byte[] rv = new byte[h * w + 8];
681 |
682 | rv[0] = 0x1D;
683 | rv[1] = 0x76;
684 | rv[2] = 0x30;
685 | rv[3] = 0x00;
686 |
687 | rv[4] = (byte)w;//xL
688 | rv[5] = (byte)(w >> 8);//xH
689 | rv[6] = (byte)h;
690 | rv[7] = (byte)(h >> 8);
691 |
692 | int k = 8;
693 | for(int i=0; i hints = new Hashtable();
708 | hints.put(EncodeHintType.CHARACTER_SET, "utf-8");
709 | //图像数据转换,使用了矩阵转换
710 | BitMatrix bitMatrix = new QRCodeWriter().encode(data, BarcodeFormat.QR_CODE, size, size, hints);
711 | System.out.println("bitmatrix height:" + bitMatrix.getHeight() + " width:" + bitMatrix.getWidth());
712 | return getBytesFromBitMatrix(bitMatrix);
713 | } catch (WriterException e) {
714 | // TODO Auto-generated catch block
715 | e.printStackTrace();
716 | }
717 | return null;
718 | }
719 |
720 | public static byte[] getHorizontalLine(int w, int size, int type){
721 | int ww = (w + 7)/8;
722 | int hh = size + 6;
723 |
724 | byte kk = (byte)0xff; //实线
725 |
726 | if(type == 0){
727 | kk = 0x3f; //虚线
728 | }
729 |
730 | byte[] data = new byte[ hh * ww + 8];
731 |
732 | data[0] = 0x1D;
733 | data[1] = 0x76;
734 | data[2] = 0x30;
735 | data[3] = 0x00;
736 |
737 | data[4] = (byte)ww;//xL
738 | data[5] = (byte)(ww >> 8);//xH
739 | data[6] = (byte)hh; //高度
740 | data[7] = (byte)(hh >> 8);
741 |
742 | int k = 8;
743 | for(int i=0; i < 3 * ww; i++){
744 | data[k++] = 0;
745 | }
746 | for(int j=0; j < size; j++){
747 | for(int i=0; i < ww; i++){
748 | data[k++] = kk;
749 | }
750 | }
751 | for(int i=0; i < 3 * ww; i++){
752 | data[k++] = 0;
753 | }
754 | return data;
755 | }
756 | }
757 |
--------------------------------------------------------------------------------
/example/App.js:
--------------------------------------------------------------------------------
1 | import React, {useEffect, useState} from 'react';
2 | import {
3 | Text,
4 | View,
5 | StyleSheet,
6 | TouchableOpacity,
7 | DeviceEventEmitter,
8 | } from 'react-native';
9 |
10 | import SunmiV2Printer from 'react-native-sunmi-v2-printer';
11 |
12 | const App = () => {
13 | const [status, setStatus] = useState('');
14 |
15 | useEffect(() => {
16 | listener = null;
17 |
18 | try {
19 | listener = DeviceEventEmitter.addListener('PrinterStatus', (action) => {
20 | switch (action) {
21 | case SunmiV2Printer.Constants.NORMAL_ACTION:
22 | setStatus(() => 'printer normal');
23 | break;
24 | case SunmiV2Printer.Constants.OUT_OF_PAPER_ACTION:
25 | setStatus(() => 'printer out out page');
26 | break;
27 | case SunmiV2Printer.Constants.COVER_OPEN_ACTION:
28 | setStatus(() => 'printer cover open');
29 | break;
30 | default:
31 | setStatus(() => 'printer status:' + action);
32 | }
33 | });
34 | } catch (e) {
35 | console.log(e);
36 | }
37 |
38 | return () => {
39 | if (listener) {
40 | listener.remove();
41 | }
42 | };
43 | }, []);
44 |
45 | const print = async () => {
46 | let logo =
47 | 'data:image/jpeg;base64,/9j/4AAQSkZJRgABAQEAYABgAAD/2wBDAAQCAwMDAgQDAwMEBAQEBQkGBQUFBQsICAYJDQsNDQ0LDAwOEBQRDg8TDwwMEhgSExUWFxcXDhEZGxkWGhQWFxb/2wBDAQQEBAUFBQoGBgoWDwwPFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhb/wAARCALRBAADASIAAhEBAxEB/8QAHwAAAQUBAQEBAQEAAAAAAAAAAAECAwQFBgcICQoL/8QAtRAAAgEDAwIEAwUFBAQAAAF9AQIDAAQRBRIhMUEGE1FhByJxFDKBkaEII0KxwRVS0fAkM2JyggkKFhcYGRolJicoKSo0NTY3ODk6Q0RFRkdISUpTVFVWV1hZWmNkZWZnaGlqc3R1dnd4eXqDhIWGh4iJipKTlJWWl5iZmqKjpKWmp6ipqrKztLW2t7i5usLDxMXGx8jJytLT1NXW19jZ2uHi4+Tl5ufo6erx8vP09fb3+Pn6/8QAHwEAAwEBAQEBAQEBAQAAAAAAAAECAwQFBgcICQoL/8QAtREAAgECBAQDBAcFBAQAAQJ3AAECAxEEBSExBhJBUQdhcRMiMoEIFEKRobHBCSMzUvAVYnLRChYkNOEl8RcYGRomJygpKjU2Nzg5OkNERUZHSElKU1RVVldYWVpjZGVmZ2hpanN0dXZ3eHl6goOEhYaHiImKkpOUlZaXmJmaoqOkpaanqKmqsrO0tba3uLm6wsPExcbHyMnK0tPU1dbX2Nna4uPk5ebn6Onq8vP09fb3+Pn6/9oADAMBAAIRAxEAPwD7+oooqQCiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKMiis7xTrOneHvD15rer3KW1lYwtNPK54VR/nH40AaNAOa8j+G37Rfw38WaidJuNQl0DVlIH2HWI/s8jZ+6VLcNkc8V61G6SIHRgysMgg5BFADqKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigArwr4t69YeNvGl5pt9dmHwL4D/07xJOeI7+6QbktA3cLwzDuSBXa/Hnxbf6LpFr4c8NIZvE/iSQ2mmIv/LuCPnuH9FjXJz64r41/bR8bafoWi2XwM8F3TXFjpji4169V90mo3rH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UAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUmMjB5paKAOI+JPwk+HnjyFl8TeFrC7lwQtyIgsqe4cc5r59+JH7DXh66LXHgjxTd6awB22t8vnIT2zJ94D8K+uaKAPzQ8f/sqfGXwwWePQodat1zmbTZQwA9drYb9K8l1/Qtb0KZo9b0a/05kbawu7Z4wD9WAzX7E1Q1nRtI1eExarpdneoRgrc26SDH/AgaAPx2UgjIII9qK/T/xl+zX8GPEkjTXfgq0t7hhjz7Rmib8gcfpXlnir9hrwRcmR/D/ijV9PZslVuNsyKfwC8UAfCdFfUviL9hvx1ahzovivStRIztE0bQZ9M/exXBa/+yl8btKjZj4Zhvtg+7Y3SyFvpnGaAPF6K6zXPhb8SdFDf2r4G1y1Cfe3WpOMdema5mWyv4jiXT72P2e2kX+YoAhopshER/eZjz03Db/Ok82LGfNTH+8KAH0UzzY/+eif99ClR0d9qMHP91Tk/pQA6ipYrS9l/wBVYXkuf+eds7fyFdX4c+E/xO14qdH8C63dBzwVtio+vzYoA4+ivoLwZ+xz8XtYmRtUi03RrduWee43yL/wAD+te1fDr9h/wdpzxXHi/wAQ32sypy0NuBBC59COSR+VAHwvYW11f3q2djbTXVw/3IYIy7t9FHNe5/B/9k34o+M2iutVtY/DWmsQTNff610PdIxzn/exX3p8Pfhn4C8DWkdv4W8K6dpwjACyJCGk/wC+2y3611tMDxb4G/sx/Db4cCK9+wnXNXjwft+ooG2sO6R8qn617OqqihVAAHAAGAKdRSAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKo6ho+k35JvdMs7gnkmaBXP6ir1FFwOW1D4b/AA9vipvfBHh+4KZ2+bpsTYz1xlaypfgr8JpJWkb4faBuY5O2xQD8gOK76igDgD8EPhIf+afaF/4CLWjYfC74bWUqyWngPw7DIq7Q6aZEGx9dua66igDO0zQtE04g6fpFjalehht0TH5CtGiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKGAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQB//Z';
48 | //ensure the base64 string without URI Scheme
49 | let logobase64 = logo.replace('data:image/jpeg;base64,', '');
50 | let orderList = [
51 | ['2020-11-25 15:00:00', '', ''],
52 | ['Some item x 1', '', ''],
53 | ['', '', '$100'],
54 | ['2020-11-25 15:00:00', '', ''],
55 | ['Some item x 1', '', ''],
56 | ['', '', '$100'],
57 | ];
58 | let columnAliment = [0, 1, 2];
59 | let columnWidth = [25, 1, 5];
60 | try {
61 | //set aligment: 0-left,1-center,2-right
62 | await SunmiV2Printer.setAlignment(1);
63 |
64 | //图片bitmap对象(最大宽度384像素,超过无法打印并且回调callback异常函数)
65 | await SunmiV2Printer.printBitmap(
66 | logobase64,
67 | 384 /*width*/,
68 | 380 /*height*/,
69 | );
70 | //SunmiV2Printer.commitPrinterBuffer();
71 |
72 | await SunmiV2Printer.setFontSize(40);
73 | await SunmiV2Printer.printOriginalText('Title name\n');
74 | await SunmiV2Printer.setFontSize(50);
75 | await SunmiV2Printer.printOriginalText('Subtitle name\n');
76 | await SunmiV2Printer.setFontSize(20);
77 | await SunmiV2Printer.setAlignment(0);
78 | await SunmiV2Printer.printOriginalText('Receipt ID: 1234567890\n');
79 | await SunmiV2Printer.printOriginalText(`Date: 2020-11-25 15:00:00\n`);
80 | await SunmiV2Printer.printOriginalText(
81 | '===============================\n',
82 | );
83 | await SunmiV2Printer.setFontSize(22);
84 | for (var i in orderList) {
85 | console.log(orderList[i]);
86 | console.log(columnWidth);
87 | console.log(columnAliment);
88 | await SunmiV2Printer.printColumnsText(
89 | orderList[i],
90 | columnWidth,
91 | columnAliment,
92 | );
93 | }
94 | await SunmiV2Printer.setFontSize(20);
95 | await SunmiV2Printer.printOriginalText(
96 | '===============================\n',
97 | );
98 | await SunmiV2Printer.setAlignment(2);
99 | await SunmiV2Printer.setFontSize(30);
100 | await SunmiV2Printer.printOriginalText('Total: $200\n');
101 | await SunmiV2Printer.setFontSize(20);
102 | await SunmiV2Printer.printOriginalText(
103 | '===============================\n',
104 | );
105 | await SunmiV2Printer.printOriginalText('\n\n');
106 | } catch (e) {
107 | console.log(e);
108 | }
109 | };
110 |
111 | return (
112 |
113 | {`Printer Status: ${status}`}
114 | print()}>
115 | Print
116 |
117 |
118 | );
119 | };
120 |
121 | const styles = StyleSheet.create({
122 | container: {
123 | flex: 1,
124 | justifyContent: 'center',
125 | alignItems: 'center',
126 | },
127 | button: {
128 | marginTop: 50,
129 | paddingHorizontal: 50,
130 | paddingVertical: 10,
131 | borderRadius: 10,
132 | backgroundColor: 'steelblue',
133 | },
134 | buttonText: {
135 | color: 'white',
136 | },
137 | });
138 |
139 | export default App;
140 |
--------------------------------------------------------------------------------