我有一个紧凑的画布到png下载保护程序功能(见下面的代码)。 这段代码运行良好,我对它的输出感到满意......主要。 第二个替换就足够了吗? 那个替换会是什么样子? 我唯一的其他选择是使用imagemagick对文件进行后期处理。 有什么想法吗?
更完整:我想从javascript添加元数据。 我发现这个链接 http://dev.exiv2.org/projects/exiv2/wiki/The_Metadata_in_PNG_files 其中详细介绍了结构,我也许有足够的时间弄清楚。 如果有人有经验并且可以为我缩短时间,我将不胜感激。
//------------------------------------------------------------------
function save () // has to be function not var for onclick to work.
//------------------------------------------------------------------
{
var element = document.getElementById("saver");
element.download = savename;
element.href = document.
getElementById(id.figure1a.canvas).
toDataURL("image/png").
replace(/^data:image/[^;]/,'data:application/octet-stream');
}
Base-64 表示形式与内部块关系不大。它只是编码为字符串的[任何]二进制数据,因此可以通过仅字符串协议传输(或在文本上下文中显示)。
创建一个示例可能有点宽泛,但希望显示主要步骤将有助于实现您正在寻找的内容:
- 要将块添加到PNG,您首先必须使用XHR/fetch(对于Data-URI)将其数据转换为ArrayBuffer,或者在将PNG作为Blob的情况下使用FileReader(我推荐)。见
toBlob()
)。 - 将 DataView 添加到 ArrayBuffer
- 转到表示 IHDR 块开始的数组中的位置0x08,读取块的长度 (Uint32)(它很可能对几乎任何 PNG 都具有相同的静态大小,但由于可能会有更改,并且您不需要记住块大小,我们将从这里读取它)。将长度添加到位置(+4 表示块末尾的 CRC-32,如果您在读取长度时没有移动指针,则为 +4),通常这应该使您处于位置 0x21。
- 您现在有了下一个块的位置,我们可以使用它来插入我们自己的文本块
- 使用具有原始 ArrayBuffer 的子数组将第一部分拆分为部分数组(常规数组),例如
new Uint8Array(arraybuffer, 0, position);
- 您也可以使用子数组方法。 - 将新的块*生成为类型化数组并添加到部分数组中
- 将原始 PNG 数组的剩余部分(不含第一部分)添加到部分数组中,例如
new Uint8Array(arraybuffer, position, length - position);
- 使用部分数组直接作为参数将部分数组转换为 Blob (
var newPng = new Blob(partArray, {type: "image/png"})
;)。现在,这将包含自定义区块。从那里,您可以使用对象 URL 将其作为图像读回(或使其可供下载)。
*) 块:
tEXt
请注意,它仅限于拉丁语-1 字符集,这意味着您必须粉饰要使用的字符串 - 对 unicode (UTF-8) 内容使用iTXt
- 为了简单起见,我们将在此处使用tEXt
。- 关键字和值在
tEXt
块中由 NUL 字节 (0x00) 分隔,并且关键字必须完全按照规范中的定义进行键入。 - 以这种方式构建块:
- 从字符串中获取字节大小
- 添加 12 个字节(用于长度、4-cc 和 CRC-32)
- 以这种方式格式化数组(您也可以在此处使用 DataView):
Uint32 - length of chunk (data only in number of bytes) Uint32 - "tEXt" as four-cc [...] - The data itself (copy byte-wise) Uint32 - CRC32* which includes the FourCC but not length and itself.
PNG 中的所有数据都是大端序。
要计算CRC-32,请随意使用我的pngtoy解决方案的这一部分(LUT就是这样构建的)。以下是格式化四抄送的一种方法:
function makeFourCC(n) { // n = "tEXt" etc., big-endian
var c = n.charCodeAt.bind(n);
return (c(0) & 0x7f) << 24 | (c(1) & 0x7f) << 16 | (c(2) & 0x7f) << 8 | c(3) & 0x7f
}