1/* 2 * Copyright (c) 2022-2023 Huawei Device Co., Ltd. 3 * Licensed under the Apache License, Version 2.0 (the "License"); 4 * you may not use this file except in compliance with the License. 5 * You may obtain a copy of the License at 6 * 7 * http://www.apache.org/licenses/LICENSE-2.0 8 * 9 * Unless required by applicable law or agreed to in writing, software 10 * distributed under the License is distributed on an "AS IS" BASIS, 11 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 * See the License for the specific language governing permissions and 13 * limitations under the License. 14 */ 15 16import fileio from '@ohos.fileio' 17import image from '@ohos.multimedia.image' 18import photoAccessHelper from '@ohos.file.photoAccessHelper' 19import common from '@ohos.app.ability.common' 20import prompt from '@ohos.promptAction'; 21import { 22 BarcodeFormat, 23 MultiFormatWriter, 24 BitMatrix, 25 ZXingStringEncoding, 26 EncodeHintType, 27 MultiFormatReader, 28 DecodeHintType, 29 RGBLuminanceSource, 30 BinaryBitmap, 31 HybridBinarizer 32} from '@ohos/zxing'; 33import DateTimeUtil from '../utils/DateTimeUtil' 34import { CameraService } from './CameraService' 35import { QRCodeScanConst, ImageAttribute, DecodeResultAttribute } from './QRCodeScanConst'; 36import Logger from '../utils/Logger' 37import Want from '@ohos.app.ability.Want' 38import dataSharePredicates from '@ohos.data.dataSharePredicates' 39 40const TAG: string = 'QRCodeParser' 41let predicates: dataSharePredicates.DataSharePredicates = new dataSharePredicates.DataSharePredicates(); 42 43/** 44 * 二维码解析器 45 */ 46class QRCodeParser { 47 48 /** 49 * 解析从相机获取的二维码图片 50 * 51 * @param cameraService 52 * @param canvasContext 53 */ 54 parseQRCodeImageFromCamera(cameraService: CameraService, 55 imageComponentType?: image.ComponentType): void { 56 Logger.info("parseQRCodeImageFromCamera start") 57 this.parseQRCodeImageWithNameFromCamera(cameraService, imageComponentType); 58 Logger.info("parseQRCodeImageFromCamera end") 59 } 60 61 /** 62 * 解析从相机获取的二维码图片,指定文件名称 63 * 64 * @param cameraService 65 * @param canvasContext 66 */ 67 parseQRCodeImageWithNameFromCamera(cameraService: CameraService, 68 imageComponentType?: image.ComponentType): void { 69 Logger.info("parseQRCodeImageWithNameFromCamera...") 70 cameraService.imageReceiver.on('imageArrival', async () => { 71 Logger.info("parseQRCodeImageWithNameFromCamera imageArrival start") 72 // 从接收器获取下一个图像,并返回结果 73 let targetImage: image.Image = await cameraService.imageReceiver.readNextImage() 74 // 默认按JPEG格式处理 75 let imgComponentType = imageComponentType === undefined ? image.ComponentType.JPEG : imageComponentType 76 let imageComponent = await targetImage.getComponent(imgComponentType) 77 // 给每次接收的image资源赋予随机文件名 78 let fileName = this.getRandomFileName(QRCodeScanConst.IMG_FILE_PREFIX, QRCodeScanConst.IMG_SUFFIX_JPG) 79 // 将image的ArrayBuffer写入指定文件中,返回文件uri 80 let imageUri = await this.createPublicDirFileAsset(fileName, imageComponent.byteBuffer); 81 // 释放已读取的image资源,以便处理下一个资源 82 await targetImage.release() 83 84 // 解析二维码 85 let qrCodeParseRlt = await this.parseImageQRCode(imageUri); 86 if (!qrCodeParseRlt.isSucess) { 87 Logger.error("parseQRCodeImageWithNameFromCamera qrCodeParseRlt is null") 88 prompt.showToast({ 89 message: $r('app.string.qrCodeNotRecognized') 90 }) 91 return; 92 } 93 // 拼接解析结果 94 AppStorage.SetOrCreate(QRCodeScanConst.QR_CODE_PARSE_RESULT, qrCodeParseRlt.decodeResult); 95 Logger.info("parseQRCodeImageWithNameFromCamera imageArrival end") 96 }) 97 } 98 99 /** 100 * 解析图片二维码信息 101 * @param canvasContext 102 * @param imageSrc 103 */ 104 async parseImageQRCode(imageSrc: string): Promise<DecodeResultAttribute> { 105 Logger.info(`parseImageQRCode start`); 106 let media = photoAccessHelper.getPhotoAccessHelper(AppStorage.get<common.UIAbilityContext>('context')); 107 let imagesIdFetchOp: photoAccessHelper.FetchOptions = { 108 fetchColumns: [], 109 predicates: predicates.equalTo('uri', imageSrc) 110 } 111 // 获取图片文件资源 112 let fetchIdFileResult = await media.getAssets(imagesIdFetchOp); 113 let fileIdAsset = await fetchIdFileResult.getFirstObject(); 114 // 获取文件描述符 115 let fd = await fileIdAsset.open('RW'); 116 // 获取PixelMap图片数据 117 let imageSource = image.createImageSource(fd, { sourceDensity: 120, sourceSize: { width: 120, height: 120 } }) 118 let decodingOptions: image.DecodingOptions = { 119 sampleSize: 1, 120 editable: true, 121 desiredSize: { width: 120, height: 120 }, 122 rotate: 0, 123 desiredPixelFormat: 3, 124 desiredRegion: { size: { height: 120, width: 120 }, x: 0, y: 0 }, 125 index: 0 126 }; 127 let pixMapData = await imageSource.createPixelMap(decodingOptions); 128 let pixelBytesNumber = pixMapData.getPixelBytesNumber(); 129 let arrayBuffer: ArrayBuffer = new ArrayBuffer(pixelBytesNumber); 130 // 读取图像像素数据,结果写入ArrayBuffer里 131 await pixMapData.readPixelsToBuffer(arrayBuffer); 132 let int32Array = new Int32Array(arrayBuffer); 133 let luminanceSource = new RGBLuminanceSource(int32Array, 120, 120); 134 let binaryBitmap = new BinaryBitmap(new HybridBinarizer(luminanceSource)); 135 let mltiFormatReader = new MultiFormatReader(); 136 let hints: Map<number, number[]> = new Map(); 137 hints.set(DecodeHintType.POSSIBLE_FORMATS, [BarcodeFormat.QR_CODE]); 138 mltiFormatReader.setHints(hints); 139 try { 140 // 解析二维码 141 let decodeResult = mltiFormatReader.decode(binaryBitmap); 142 let decodeText = decodeResult.getText(); 143 Logger.info(`parseImageQRCode end ${decodeText}`); 144 return { isSucess: true, decodeResult: decodeText }; 145 } catch (err) { 146 let error = `The error is ${err}`; 147 Logger.info(`parseImageQRCode end`); 148 return { isSucess: false, decodeResult: error }; 149 } 150 } 151 /** 152 * 获取图片的属性 153 * @param context 154 * @param imageSrc 155 */ 156 async getImageSource(imageSrc: string): Promise<ImageAttribute> { 157 Logger.info(`getImageSource start`); 158 let media = photoAccessHelper.getPhotoAccessHelper(AppStorage.get<common.UIAbilityContext>('context')); 159 let imagesIdFetchOp: photoAccessHelper.FetchOptions = { 160 fetchColumns: ['width', 'height'], 161 predicates: predicates.equalTo('uri', imageSrc) 162 } 163 // 获取图片文件资源 164 let fetchIdFileResult = await media.getAssets(imagesIdFetchOp); 165 let fileIdAsset = await fetchIdFileResult.getFirstObject(); 166 // 将字符串分割,下标为1的数据即为图片类型 167 let imgType = fileIdAsset.displayName.split('.')[1]; 168 // 获取文件描述符 169 let fd = await fileIdAsset.open('RW'); 170 let context = AppStorage.get<common.UIAbilityContext>('context'); 171 // 获取当前时间 172 let time = new Date().getTime(); 173 // 拼接路径 174 let imagePath = `${context?.cacheDir}/${time.toString()}_note.${imgType}`; 175 // 将图片copy到此路径当中 176 await fileio.copyFile(fd, imagePath); 177 // 创建一个图片源类 178 let imageSource = image.createImageSource(imagePath); 179 // 创建PixelMap数组 180 let pixelMap = await imageSource.createPixelMap(); 181 // 4、关闭安fd,Asset 182 await fileIdAsset.close(fd); 183 return { 184 width: fileIdAsset.get('width') as number, 185 height: fileIdAsset.get('height') as number, 186 pixelMap: pixelMap 187 }; 188 } 189 /** 190 * 在媒体公共资源目录下的创建指定类型的资源对象 191 */ 192 async createPublicDirFileAsset(fileDisplayName: string, 193 fileByteBuffer: ArrayBuffer): Promise<string> { 194 Logger.info("createPublicDirFileAsset start") 195 // 获取mediaLibrary对象 196 let mediaLibraryObj = photoAccessHelper.getPhotoAccessHelper(AppStorage.get('context')) 197 let fileAsset = await mediaLibraryObj.createAsset(fileDisplayName) 198 // 拿到fileAccess资源对应的uri 199 let fileUri = fileAsset.uri 200 // 调用open方法打开这个资源对象 // TODO 为啥要打开? 201 let fd = await fileAsset.open('Rw') 202 // 将图片资源写入对应的fileAccess 203 await fileio.write(fd, fileByteBuffer) 204 // 关闭这个资源对象 205 await fileAsset.close(fd) 206 Logger.info("createPublicDirFileAsset end") 207 return fileUri 208 } 209 210 /** 211 * 根据时间生成随机文件名 212 * 213 * @param filePrefix 文件名前缀 214 * @param fileSuffix 文件名后缀 215 */ 216 getRandomFileName(filePrefix: string, fileSuffix: string) { 217 let dateTimeUtil = new DateTimeUtil() 218 return filePrefix + `${dateTimeUtil.getDate()}_${dateTimeUtil.getTime()}` + fileSuffix 219 } 220} 221 222export { QRCodeParser }