1.delete crc32.js delete png-metadata.js
Signed-off-by: zhoulisheng <635547767@qq.com>
This commit is contained in:
parent
bdc7156b27
commit
ca22bad4c4
6
OAT.xml
6
OAT.xml
|
@ -6,15 +6,13 @@
|
||||||
<filteritem type="filename" name="hvigorfile.js" desc="hvigor构建脚本,DevEco Studio自动生成,不手动修改"/>
|
<filteritem type="filename" name="hvigorfile.js" desc="hvigor构建脚本,DevEco Studio自动生成,不手动修改"/>
|
||||||
<filteritem type="filename" name="*.json5" desc="hvigor配置文件,DevEco Studio自动生成,不手动修改"/>
|
<filteritem type="filename" name="*.json5" desc="hvigor配置文件,DevEco Studio自动生成,不手动修改"/>
|
||||||
<filteritem type="filepath" name="imageknife/src/main/ets/components/imageknife/pngj/UPNG.js" desc="使用开源库UPNG,使用其默认版权头,无需修改"/>
|
<filteritem type="filepath" name="imageknife/src/main/ets/components/imageknife/pngj/UPNG.js" desc="使用开源库UPNG,使用其默认版权头,无需修改"/>
|
||||||
<filteritem type="filepath" name="imageknife/src/main/ets/components/imageknife/pngj/crc32.js" desc="使用开源库crc-32,使用其默认版权头,无需修改"/>
|
|
||||||
<filteritem type="filepath" name="imageknife/src/main/ets/components/imageknife/pngj/png-metadata.js" desc="使用开源https://dev.exiv2.org/projects/exiv2/wiki/The_Metadata_in_PNG_files,使用其默认版权头,无需修改"/>
|
|
||||||
</filefilter>
|
</filefilter>
|
||||||
<filefilter name="defaultPolicyFilter" desc="Filters for compatibility,license header policies">
|
<filefilter name="defaultPolicyFilter" desc="Filters for compatibility,license header policies">
|
||||||
<filteritem type="filename" name="hvigorfile.js" desc="hvigor构建脚本,DevEco Studio自动生成,不手动修改"/>
|
<filteritem type="filename" name="hvigorfile.js" desc="hvigor构建脚本,DevEco Studio自动生成,不手动修改"/>
|
||||||
<filteritem type="filename" name="*.json5" desc="hvigor配置文件,DevEco Studio自动生成,不手动修改"/>
|
<filteritem type="filename" name="*.json5" desc="hvigor配置文件,DevEco Studio自动生成,不手动修改"/>
|
||||||
<filteritem type="filepath" name="imageknife/src/main/ets/components/imageknife/pngj/UPNG.js" desc="使用开源库UPNG,使用其默认版权头,无需修改"/>
|
<filteritem type="filepath" name="imageknife/src/main/ets/components/imageknife/pngj/UPNG.js" desc="使用开源库UPNG,使用其默认版权头,无需修改"/>
|
||||||
<filteritem type="filepath" name="imageknife/src/main/ets/components/imageknife/pngj/crc32.js" desc="使用开源库crc-32,使用其默认版权头,无需修改"/>
|
|
||||||
<filteritem type="filepath" name="imageknife/src/main/ets/components/imageknife/pngj/png-metadata.js" desc="使用开源https://dev.exiv2.org/projects/exiv2/wiki/The_Metadata_in_PNG_files,使用其默认版权头,无需修改"/>
|
|
||||||
</filefilter>
|
</filefilter>
|
||||||
<filefilter name="defaultPolicyFilter" desc="Filters for copyright header policies">
|
<filefilter name="defaultPolicyFilter" desc="Filters for copyright header policies">
|
||||||
</filefilter>
|
</filefilter>
|
||||||
|
|
|
@ -14,16 +14,6 @@
|
||||||
*/
|
*/
|
||||||
import router from '@system.router';
|
import router from '@system.router';
|
||||||
import { Pngj } from '@ohos/imageknife'
|
import { Pngj } from '@ohos/imageknife'
|
||||||
import {
|
|
||||||
RESOLUTION_UNITS,
|
|
||||||
insertMetadata,
|
|
||||||
readMetadata,
|
|
||||||
writeMetadata,
|
|
||||||
textEncode,
|
|
||||||
textDecode,
|
|
||||||
extractChunks,
|
|
||||||
encodeChunks
|
|
||||||
} from '@ohos/imageknife'
|
|
||||||
import resourceManager from '@ohos.resourceManager';
|
import resourceManager from '@ohos.resourceManager';
|
||||||
import { FileUtils } from '@ohos/imageknife'
|
import { FileUtils } from '@ohos/imageknife'
|
||||||
import featureability from '@ohos.ability.featureAbility'
|
import featureability from '@ohos.ability.featureAbility'
|
||||||
|
@ -165,38 +155,6 @@ struct PngjTestCasePage {
|
||||||
}.width('100%')
|
}.width('100%')
|
||||||
.height(60).backgroundColor(Color.Pink)
|
.height(60).backgroundColor(Color.Pink)
|
||||||
|
|
||||||
Flex({ direction: FlexDirection.Row, alignItems: ItemAlign.Center, justifyContent: FlexAlign.Center }) {
|
|
||||||
Button('测试readMetadata')
|
|
||||||
.onClick(() => {
|
|
||||||
globalThis.ImageKnife.getImageKnifeContext()
|
|
||||||
.resourceManagergetMedia($r('app.media.pngSample').id)
|
|
||||||
.then(data => {
|
|
||||||
let arrayBuffer = this.typedArrayToBuffer(data);
|
|
||||||
let metadata = readMetadata(arrayBuffer);
|
|
||||||
console.log("readMetadata metadata=" + JSON.stringify(metadata))
|
|
||||||
this.hint5 = JSON.stringify(metadata);
|
|
||||||
})
|
|
||||||
.catch(err => {
|
|
||||||
console.log('测试readMetadata err=' + err)
|
|
||||||
})
|
|
||||||
}).margin({ top: 5, left: 10 })
|
|
||||||
Button('测试writeMetadata')
|
|
||||||
.onClick(() => {
|
|
||||||
globalThis.ImageKnife.getImageKnifeContext()
|
|
||||||
.resourceManagergetMedia($r('app.media.pngSample').id)
|
|
||||||
.then(data => {
|
|
||||||
let arrayBuffer = this.typedArrayToBuffer(data);
|
|
||||||
// let pngBuffer = writeMetadata(arrayBuffer);
|
|
||||||
// console.log("writeMetadata metadata="+JSON.stringify(metadata))
|
|
||||||
// this.hint5 = JSON.stringify(metadata);
|
|
||||||
})
|
|
||||||
.catch(err => {
|
|
||||||
console.log('测试writeMetadata err=' + err)
|
|
||||||
})
|
|
||||||
}).margin({ top: 5, left: 10 })
|
|
||||||
}.width('100%')
|
|
||||||
.height(60).backgroundColor(Color.Pink)
|
|
||||||
|
|
||||||
Text(this.hint1)
|
Text(this.hint1)
|
||||||
.width('100%')
|
.width('100%')
|
||||||
.height(120)
|
.height(120)
|
||||||
|
|
|
@ -83,9 +83,6 @@ export * from './src/main/ets/components/imageknife/pngj/Pngj'
|
||||||
export {handler} from './src/main/ets/components/imageknife/pngj/PngWork'
|
export {handler} from './src/main/ets/components/imageknife/pngj/PngWork'
|
||||||
export * from './src/main/ets/components/imageknife/pngj/UPNG'
|
export * from './src/main/ets/components/imageknife/pngj/UPNG'
|
||||||
|
|
||||||
export {
|
|
||||||
RESOLUTION_UNITS, insertMetadata, readMetadata, writeMetadata, textEncode, textDecode, extractChunks, encodeChunks
|
|
||||||
} from './src/main/ets/components/imageknife/pngj/png-metadata'
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
@ -1,109 +0,0 @@
|
||||||
/* crc32.js (C) 2014-present SheetJS -- http://sheetjs.com */
|
|
||||||
/* vim: set ts=2: */
|
|
||||||
/*exported CRC32 */
|
|
||||||
export var Crc32;
|
|
||||||
(function(factory){
|
|
||||||
/*jshint ignore:start */
|
|
||||||
/*eslint-disable */
|
|
||||||
|
|
||||||
factory(Crc32 = {});
|
|
||||||
|
|
||||||
/*eslint-enable */
|
|
||||||
/*jshint ignore:end */
|
|
||||||
}(function (CRC32) {
|
|
||||||
CRC32.version = '1.2.0';
|
|
||||||
/* see perf/crc32table.js */
|
|
||||||
/*global Int32Array */
|
|
||||||
function signed_crc_table() {
|
|
||||||
var c = 0, table = new Array(256);
|
|
||||||
|
|
||||||
for (var n = 0; n != 256; ++n) {
|
|
||||||
c = n;
|
|
||||||
c = ((c & 1) ? (-306674912 ^ (c >>> 1)) : (c >>> 1));
|
|
||||||
c = ((c & 1) ? (-306674912 ^ (c >>> 1)) : (c >>> 1));
|
|
||||||
c = ((c & 1) ? (-306674912 ^ (c >>> 1)) : (c >>> 1));
|
|
||||||
c = ((c & 1) ? (-306674912 ^ (c >>> 1)) : (c >>> 1));
|
|
||||||
c = ((c & 1) ? (-306674912 ^ (c >>> 1)) : (c >>> 1));
|
|
||||||
c = ((c & 1) ? (-306674912 ^ (c >>> 1)) : (c >>> 1));
|
|
||||||
c = ((c & 1) ? (-306674912 ^ (c >>> 1)) : (c >>> 1));
|
|
||||||
c = ((c & 1) ? (-306674912 ^ (c >>> 1)) : (c >>> 1));
|
|
||||||
table[n] = c;
|
|
||||||
}
|
|
||||||
|
|
||||||
return typeof Int32Array !== 'undefined' ? new Int32Array(table) : table;
|
|
||||||
}
|
|
||||||
|
|
||||||
var T = signed_crc_table();
|
|
||||||
|
|
||||||
function crc32_bstr(bstr, seed) {
|
|
||||||
var C = seed ^ -1, L = bstr.length - 1;
|
|
||||||
for (var i = 0; i < L; ) {
|
|
||||||
C = (C >>> 8) ^ T[(C ^ bstr.charCodeAt(i++))&0xFF];
|
|
||||||
C = (C >>> 8) ^ T[(C ^ bstr.charCodeAt(i++))&0xFF];
|
|
||||||
}
|
|
||||||
if (i === L) C = (C >>> 8) ^ T[(C ^ bstr.charCodeAt(i))&0xFF];
|
|
||||||
return C ^ -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
function crc32_buf(buf, seed) {
|
|
||||||
if (buf.length > 10000) return crc32_buf_8(buf, seed);
|
|
||||||
var C = seed ^ -1, L = buf.length - 3;
|
|
||||||
for (var i = 0; i < L; ) {
|
|
||||||
C = (C >>> 8) ^ T[(C ^ buf[i++])&0xFF];
|
|
||||||
C = (C >>> 8) ^ T[(C ^ buf[i++])&0xFF];
|
|
||||||
C = (C >>> 8) ^ T[(C ^ buf[i++])&0xFF];
|
|
||||||
C = (C >>> 8) ^ T[(C ^ buf[i++])&0xFF];
|
|
||||||
}
|
|
||||||
while (i < L + 3) C = (C >>> 8) ^ T[(C ^ buf[i++])&0xFF];
|
|
||||||
return C ^ -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
function crc32_buf_8(buf, seed) {
|
|
||||||
var C = seed ^ -1, L = buf.length - 7;
|
|
||||||
for (var i = 0; i < L; ) {
|
|
||||||
C = (C >>> 8) ^ T[(C ^ buf[i++])&0xFF];
|
|
||||||
C = (C >>> 8) ^ T[(C ^ buf[i++])&0xFF];
|
|
||||||
C = (C >>> 8) ^ T[(C ^ buf[i++])&0xFF];
|
|
||||||
C = (C >>> 8) ^ T[(C ^ buf[i++])&0xFF];
|
|
||||||
C = (C >>> 8) ^ T[(C ^ buf[i++])&0xFF];
|
|
||||||
C = (C >>> 8) ^ T[(C ^ buf[i++])&0xFF];
|
|
||||||
C = (C >>> 8) ^ T[(C ^ buf[i++])&0xFF];
|
|
||||||
C = (C >>> 8) ^ T[(C ^ buf[i++])&0xFF];
|
|
||||||
}
|
|
||||||
while (i < L + 7) C = (C >>> 8) ^ T[(C ^ buf[i++])&0xFF];
|
|
||||||
return C ^ -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
function crc32_str(str, seed) {
|
|
||||||
var C = seed ^ -1;
|
|
||||||
for (var i = 0, L = str.length, c, d; i < L; ) {
|
|
||||||
c = str.charCodeAt(i++);
|
|
||||||
if (c < 0x80) {
|
|
||||||
C = (C >>> 8) ^ T[(C ^ c)&0xFF];
|
|
||||||
} else if (c < 0x800) {
|
|
||||||
C = (C >>> 8) ^ T[(C ^ (192 | ((c >> 6) & 31)))&0xFF];
|
|
||||||
C = (C >>> 8) ^ T[(C ^ (128 | (c & 63)))&0xFF];
|
|
||||||
} else if (c >= 0xD800 && c < 0xE000) {
|
|
||||||
c = (c & 1023) + 64;
|
|
||||||
d = str.charCodeAt(i++) & 1023;
|
|
||||||
C = (C >>> 8) ^ T[(C ^ (240 | ((c >> 8) & 7)))&0xFF];
|
|
||||||
C = (C >>> 8) ^ T[(C ^ (128 | ((c >> 2) & 63)))&0xFF];
|
|
||||||
C = (C >>> 8) ^ T[(C ^ (128 | ((d >> 6) & 15) | ((c & 3) << 4)))&0xFF];
|
|
||||||
C = (C >>> 8) ^ T[(C ^ (128 | (d & 63)))&0xFF];
|
|
||||||
} else {
|
|
||||||
C = (C >>> 8) ^ T[(C ^ (224 | ((c >> 12) & 15)))&0xFF];
|
|
||||||
C = (C >>> 8) ^ T[(C ^ (128 | ((c >> 6) & 63)))&0xFF];
|
|
||||||
C = (C >>> 8) ^ T[(C ^ (128 | (c & 63)))&0xFF];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return C ^ -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
CRC32.table = T;
|
|
||||||
// $FlowIgnore
|
|
||||||
CRC32.bstr = crc32_bstr;
|
|
||||||
// $FlowIgnore
|
|
||||||
CRC32.buf = crc32_buf;
|
|
||||||
// $FlowIgnore
|
|
||||||
CRC32.str = crc32_str;
|
|
||||||
}));
|
|
|
@ -1,423 +0,0 @@
|
||||||
/**
|
|
||||||
* library to read and write PNG Metadata
|
|
||||||
*
|
|
||||||
* References:
|
|
||||||
* w3 PNG Chunks specification: https://www.w3.org/TR/PNG-Chunks.html
|
|
||||||
* The Metadata in PNG files: https://dev.exiv2.org/projects/exiv2/wiki/The_Metadata_in_PNG_files
|
|
||||||
*/
|
|
||||||
|
|
||||||
import { Crc32 } from './crc32'
|
|
||||||
|
|
||||||
|
|
||||||
// Used for fast-ish conversion between uint8s and uint32s/int32s.
|
|
||||||
// Also required in order to remain agnostic for both Node Buffers and
|
|
||||||
// Uint8Arrays.
|
|
||||||
let uint8 = new Uint8Array(4)
|
|
||||||
let int32 = new Int32Array(uint8.buffer)
|
|
||||||
let uint32 = new Uint32Array(uint8.buffer)
|
|
||||||
|
|
||||||
export const RESOLUTION_UNITS = { UNDEFINED: 0, METERS: 1, INCHES: 2 };
|
|
||||||
|
|
||||||
/**
|
|
||||||
* https://github.com/aheckmann/sliced
|
|
||||||
* An Array.prototype.slice.call(arguments) alternative
|
|
||||||
* @param {Object} args something with a length
|
|
||||||
* @param {Number} slice
|
|
||||||
* @param {Number} sliceEnd
|
|
||||||
* @api public
|
|
||||||
*/
|
|
||||||
function sliced(args, slice, sliceEnd) {
|
|
||||||
var ret = [];
|
|
||||||
var len = args.length;
|
|
||||||
|
|
||||||
if (0 === len) return ret;
|
|
||||||
|
|
||||||
var start = slice < 0
|
|
||||||
? Math.max(0, slice + len)
|
|
||||||
: slice || 0;
|
|
||||||
|
|
||||||
if (sliceEnd !== undefined) {
|
|
||||||
len = sliceEnd < 0
|
|
||||||
? sliceEnd + len
|
|
||||||
: sliceEnd
|
|
||||||
}
|
|
||||||
|
|
||||||
while (len-- > start) {
|
|
||||||
ret[len - start] = args[len];
|
|
||||||
}
|
|
||||||
|
|
||||||
return ret;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* https://github.com/hughsk/png-chunk-text
|
|
||||||
* Returns a chunk object containing the metadata for a given key and value:
|
|
||||||
* @param keyword
|
|
||||||
* @param content
|
|
||||||
* @param chunkName
|
|
||||||
* @returns {{data: Uint8Array, name: 'tEXt'}}
|
|
||||||
*/
|
|
||||||
export function textEncode(keyword, content, chunkName = 'tEXt') {
|
|
||||||
keyword = String(keyword)
|
|
||||||
content = String(content)
|
|
||||||
|
|
||||||
if (content.length && (!/^[\x00-\xFF]+$/.test(keyword) || !/^[\x00-\xFF]+$/.test(content))) {
|
|
||||||
throw new Error('Only Latin-1 characters are permitted in PNG tEXt chunks. You might want to consider base64 encoding and/or zEXt compression')
|
|
||||||
}
|
|
||||||
|
|
||||||
if (keyword.length >= 80) {
|
|
||||||
throw new Error('Keyword "' + keyword + '" is longer than the 79-character limit imposed by the PNG specification')
|
|
||||||
}
|
|
||||||
|
|
||||||
let totalSize = keyword.length + content.length + 1
|
|
||||||
let output = new Uint8Array(totalSize)
|
|
||||||
let idx = 0
|
|
||||||
let code
|
|
||||||
|
|
||||||
for (let i = 0; i < keyword.length; i++) {
|
|
||||||
if (!(code = keyword.charCodeAt(i))) {
|
|
||||||
throw new Error('0x00 character is not permitted in tEXt keywords')
|
|
||||||
}
|
|
||||||
|
|
||||||
output[idx++] = code
|
|
||||||
}
|
|
||||||
|
|
||||||
output[idx++] = 0
|
|
||||||
|
|
||||||
for (let j = 0; j < content.length; j++) {
|
|
||||||
if (!(code = content.charCodeAt(j))) {
|
|
||||||
throw new Error('0x00 character is not permitted in tEXt content')
|
|
||||||
}
|
|
||||||
|
|
||||||
output[idx++] = code
|
|
||||||
}
|
|
||||||
|
|
||||||
return {
|
|
||||||
name: chunkName,
|
|
||||||
data: output
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* https://github.com/hughsk/png-chunk-text
|
|
||||||
* Reads a Uint8Array or Node.js Buffer instance containing a tEXt PNG chunk's data and returns its keyword/text:
|
|
||||||
* @param data
|
|
||||||
* @returns {{text: string, keyword: string}}
|
|
||||||
*/
|
|
||||||
export function textDecode(data) {
|
|
||||||
if (data.data && data.name) {
|
|
||||||
data = data.data
|
|
||||||
}
|
|
||||||
|
|
||||||
let naming = true
|
|
||||||
let text = ''
|
|
||||||
let name = ''
|
|
||||||
|
|
||||||
for (let i = 0; i < data.length; i++) {
|
|
||||||
let code = data[i]
|
|
||||||
|
|
||||||
if (naming) {
|
|
||||||
if (code) {
|
|
||||||
name += String.fromCharCode(code)
|
|
||||||
} else {
|
|
||||||
naming = false
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
if (code) {
|
|
||||||
text += String.fromCharCode(code)
|
|
||||||
} else {
|
|
||||||
throw new Error('Invalid NULL character found. 0x00 character is not permitted in tEXt content')
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return {
|
|
||||||
keyword: name,
|
|
||||||
text: text
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* https://github.com/hughsk/png-chunks-extract
|
|
||||||
* Extract the data chunks from a PNG file.
|
|
||||||
* Useful for reading the metadata of a PNG image, or as the base of a more complete PNG parser.
|
|
||||||
* Takes the raw image file data as a Uint8Array or Node.js Buffer, and returns an array of chunks. Each chunk has a name and data buffer:
|
|
||||||
* @param data {Uint8Array}
|
|
||||||
* @returns {[{name: String, data: Uint8Array}]}
|
|
||||||
*/
|
|
||||||
export function extractChunks(data) {
|
|
||||||
if (data[0] !== 0x89) throw new Error('Invalid .png file header')
|
|
||||||
if (data[1] !== 0x50) throw new Error('Invalid .png file header')
|
|
||||||
if (data[2] !== 0x4E) throw new Error('Invalid .png file header')
|
|
||||||
if (data[3] !== 0x47) throw new Error('Invalid .png file header')
|
|
||||||
if (data[4] !== 0x0D) throw new Error('Invalid .png file header: possibly caused by DOS-Unix line ending conversion?')
|
|
||||||
if (data[5] !== 0x0A) throw new Error('Invalid .png file header: possibly caused by DOS-Unix line ending conversion?')
|
|
||||||
if (data[6] !== 0x1A) throw new Error('Invalid .png file header')
|
|
||||||
if (data[7] !== 0x0A) throw new Error('Invalid .png file header: possibly caused by DOS-Unix line ending conversion?')
|
|
||||||
|
|
||||||
let ended = false
|
|
||||||
let chunks = []
|
|
||||||
let idx = 8
|
|
||||||
|
|
||||||
while (idx < data.length) {
|
|
||||||
// Read the length of the current chunk,
|
|
||||||
// which is stored as a Uint32.
|
|
||||||
uint8[3] = data[idx++]
|
|
||||||
uint8[2] = data[idx++]
|
|
||||||
uint8[1] = data[idx++]
|
|
||||||
uint8[0] = data[idx++]
|
|
||||||
|
|
||||||
// Chunk includes name/type for CRC check (see below).
|
|
||||||
let length = uint32[0] + 4
|
|
||||||
let chunk = new Uint8Array(length)
|
|
||||||
chunk[0] = data[idx++]
|
|
||||||
chunk[1] = data[idx++]
|
|
||||||
chunk[2] = data[idx++]
|
|
||||||
chunk[3] = data[idx++]
|
|
||||||
|
|
||||||
// Get the name in ASCII for identification.
|
|
||||||
let name = (
|
|
||||||
String.fromCharCode(chunk[0]) +
|
|
||||||
String.fromCharCode(chunk[1]) +
|
|
||||||
String.fromCharCode(chunk[2]) +
|
|
||||||
String.fromCharCode(chunk[3])
|
|
||||||
)
|
|
||||||
|
|
||||||
// The IHDR header MUST come first.
|
|
||||||
if (!chunks.length && name !== 'IHDR') {
|
|
||||||
throw new Error('IHDR header missing')
|
|
||||||
}
|
|
||||||
|
|
||||||
// The IEND header marks the end of the file,
|
|
||||||
// so on discovering it break out of the loop.
|
|
||||||
if (name === 'IEND') {
|
|
||||||
ended = true
|
|
||||||
chunks.push({
|
|
||||||
name: name,
|
|
||||||
data: new Uint8Array(0)
|
|
||||||
})
|
|
||||||
|
|
||||||
break
|
|
||||||
}
|
|
||||||
|
|
||||||
// Read the contents of the chunk out of the main buffer.
|
|
||||||
for (let i = 4; i < length; i++) {
|
|
||||||
chunk[i] = data[idx++]
|
|
||||||
}
|
|
||||||
|
|
||||||
// Read out the CRC value for comparison.
|
|
||||||
// It's stored as an Int32.
|
|
||||||
uint8[3] = data[idx++]
|
|
||||||
uint8[2] = data[idx++]
|
|
||||||
uint8[1] = data[idx++]
|
|
||||||
uint8[0] = data[idx++]
|
|
||||||
|
|
||||||
let crcActual = int32[0]
|
|
||||||
let crcExpect = Crc32.buf(chunk)
|
|
||||||
if (crcExpect !== crcActual) {
|
|
||||||
throw new Error(
|
|
||||||
'CRC values for ' + name + ' header do not match, PNG file is likely corrupted'
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
// The chunk data is now copied to remove the 4 preceding
|
|
||||||
// bytes used for the chunk name/type.
|
|
||||||
let chunkData = new Uint8Array(chunk.buffer.slice(4))
|
|
||||||
|
|
||||||
chunks.push({
|
|
||||||
name: name,
|
|
||||||
data: chunkData
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!ended) {
|
|
||||||
throw new Error('.png file ended prematurely: no IEND header was found')
|
|
||||||
}
|
|
||||||
|
|
||||||
return chunks
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* https://github.com/hughsk/png-chunks-encode
|
|
||||||
* Return a fresh PNG buffer given a set of PNG chunks. Useful in combination with png-chunks-encode to easily modify or add to the data of a PNG file.
|
|
||||||
* Takes an array of chunks, each with a name and data:
|
|
||||||
* @param chunks {[{name: String, data: Uint8Array}]}
|
|
||||||
* @returns {Uint8Array}
|
|
||||||
*/
|
|
||||||
export function encodeChunks(chunks) {
|
|
||||||
let totalSize = 8
|
|
||||||
let idx = totalSize
|
|
||||||
let i
|
|
||||||
|
|
||||||
for (i = 0; i < chunks.length; i++) {
|
|
||||||
totalSize += chunks[i].data.length
|
|
||||||
totalSize += 12
|
|
||||||
}
|
|
||||||
|
|
||||||
let output = new Uint8Array(totalSize)
|
|
||||||
|
|
||||||
output[0] = 0x89
|
|
||||||
output[1] = 0x50
|
|
||||||
output[2] = 0x4E
|
|
||||||
output[3] = 0x47
|
|
||||||
output[4] = 0x0D
|
|
||||||
output[5] = 0x0A
|
|
||||||
output[6] = 0x1A
|
|
||||||
output[7] = 0x0A
|
|
||||||
|
|
||||||
for (i = 0; i < chunks.length; i++) {
|
|
||||||
let chunk = chunks[i]
|
|
||||||
let name = chunk.name
|
|
||||||
let data = chunk.data
|
|
||||||
let size = data.length
|
|
||||||
let nameChars = [
|
|
||||||
name.charCodeAt(0),
|
|
||||||
name.charCodeAt(1),
|
|
||||||
name.charCodeAt(2),
|
|
||||||
name.charCodeAt(3)
|
|
||||||
]
|
|
||||||
|
|
||||||
uint32[0] = size
|
|
||||||
output[idx++] = uint8[3]
|
|
||||||
output[idx++] = uint8[2]
|
|
||||||
output[idx++] = uint8[1]
|
|
||||||
output[idx++] = uint8[0]
|
|
||||||
|
|
||||||
output[idx++] = nameChars[0]
|
|
||||||
output[idx++] = nameChars[1]
|
|
||||||
output[idx++] = nameChars[2]
|
|
||||||
output[idx++] = nameChars[3]
|
|
||||||
|
|
||||||
for (let j = 0; j < size; ) {
|
|
||||||
output[idx++] = data[j++]
|
|
||||||
}
|
|
||||||
|
|
||||||
let crcCheck = nameChars.concat(sliced(data, undefined, undefined));
|
|
||||||
int32[0] = Crc32.buf(crcCheck)
|
|
||||||
output[idx++] = uint8[3]
|
|
||||||
output[idx++] = uint8[2]
|
|
||||||
output[idx++] = uint8[1]
|
|
||||||
output[idx++] = uint8[0]
|
|
||||||
}
|
|
||||||
|
|
||||||
return output
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* read 4 bytes number from UInt8Array.
|
|
||||||
* @param uint8array
|
|
||||||
* @param offset
|
|
||||||
* @returns {number}
|
|
||||||
*/
|
|
||||||
function readUint32(uint8array, offset) {
|
|
||||||
let byte1, byte2, byte3, byte4;
|
|
||||||
byte1 = uint8array[offset++];
|
|
||||||
byte2 = uint8array[offset++];
|
|
||||||
byte3 = uint8array[offset++];
|
|
||||||
byte4 = uint8array[offset];
|
|
||||||
return 0 | (byte1 << 24) | (byte2 << 16) | (byte3 << 8) | byte4;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* write 4 bytes number to UInt8Array.
|
|
||||||
* @param uint8array
|
|
||||||
* @param num
|
|
||||||
* @param offset
|
|
||||||
*/
|
|
||||||
function writeUInt32(uint8array, num, offset) {
|
|
||||||
uint8array[offset] = (num & 0xff000000) >> 24;
|
|
||||||
uint8array[offset + 1] = (num & 0x00ff0000) >> 16;
|
|
||||||
uint8array[offset + 2] = (num & 0x0000ff00) >> 8;
|
|
||||||
uint8array[offset + 3] = (num & 0x000000ff);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Get object with PNG metadata. only tEXt and pHYs chunks are parsed
|
|
||||||
* @param buffer {Buffer}
|
|
||||||
* @returns {{tEXt: {keyword: value}, pHYs: {x: number, y: number, units: RESOLUTION_UNITS}, [string]: true}}
|
|
||||||
*/
|
|
||||||
export function readMetadata(buffer) {
|
|
||||||
let result = {};
|
|
||||||
const chunks = extractChunks(buffer);
|
|
||||||
chunks.forEach(chunk => {
|
|
||||||
switch (chunk.name) {
|
|
||||||
case 'tEXt':
|
|
||||||
if (!result['tEXt']) {
|
|
||||||
result['tEXt'] = {};
|
|
||||||
}
|
|
||||||
let textChunk = textDecode(chunk.data);
|
|
||||||
result['tEXt'][textChunk.keyword] = textChunk.text;
|
|
||||||
break
|
|
||||||
case 'pHYs':
|
|
||||||
result['pHYs'] = {
|
|
||||||
// Pixels per unit, X axis: 4 bytes (unsigned integer)
|
|
||||||
"x": readUint32(chunk.data, 0),
|
|
||||||
// Pixels per unit, Y axis: 4 bytes (unsigned integer)
|
|
||||||
"y": readUint32(chunk.data, 4),
|
|
||||||
"unit": chunk.data[8],
|
|
||||||
}
|
|
||||||
break
|
|
||||||
case 'gAMA':
|
|
||||||
case 'cHRM':
|
|
||||||
case 'sRGB':
|
|
||||||
case 'IHDR':
|
|
||||||
case 'iCCP':
|
|
||||||
default:
|
|
||||||
result[chunk.name] = true;
|
|
||||||
}
|
|
||||||
})
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* create new Buffer with metadata. only tEXt and pHYs chunks are supported.
|
|
||||||
* @param buffer {Buffer}
|
|
||||||
* @param metadata {{tEXt: {keyword: value}, pHYs: {x: number, y: number, units: RESOLUTION_UNITS}}}
|
|
||||||
* @returns {Buffer}
|
|
||||||
*/
|
|
||||||
export function writeMetadata(buffer, metadata) {
|
|
||||||
const chunks = extractChunks(buffer);
|
|
||||||
insertMetadata(chunks, metadata);
|
|
||||||
ArrayBuffer
|
|
||||||
let out = encodeChunks(chunks).buffer;
|
|
||||||
return out;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
export function insertMetadata(chunks, metadata) {
|
|
||||||
if (metadata.clear) {
|
|
||||||
for (let i = chunks.length - 1; i--; ) {
|
|
||||||
switch (chunks[i].name) {
|
|
||||||
case 'IHDR':
|
|
||||||
case 'IDAT':
|
|
||||||
case 'IEND':
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
chunks.splice(i, 1);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (metadata.tEXt) {
|
|
||||||
for (var keyword in metadata.tEXt) {
|
|
||||||
chunks.splice(-1, 0, textEncode(keyword, metadata.tEXt[keyword]))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (metadata.pHYs) {
|
|
||||||
const data = new Uint8Array(9);
|
|
||||||
writeUInt32(data, metadata.pHYs.x, 0)
|
|
||||||
writeUInt32(data, metadata.pHYs.y, 4)
|
|
||||||
data[8] = metadata.pHYs.units; // inches
|
|
||||||
|
|
||||||
let pHYs = chunks.find(chunk => chunk.name === "pHYs");
|
|
||||||
if (pHYs) {
|
|
||||||
pHYs.data = data;
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
chunks.splice(1, 0, { name: "pHYs", data: data })
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue