js-wmf/js/wmf.js

417 lines
18 KiB
JavaScript

"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
/*! wmf.js (C) 2020-present SheetJS LLC -- https://sheetjs.com */
var util_1 = require("./util");
var Records_1 = require("./Records");
var parse_emf = function (data) {
//try { require("fs").writeFileSync("out.emf", data); } catch(e) {}
};
/* 2.2.2.9 */
var parse_dib = function (data) {
if (data.length == 0)
return null;
util_1.prep_blob(data, 0);
/* DIBHeaderInfo */
var HeaderSize = data.read_shift(4);
var Width = 0, Height = 0, Planes = 0, BitCount = 0;
var Compression = 0, ImageSize = 0, XPelsPerMeter = 0, YPelsPerMeter = 0, ColorUsed = 0, ColorImportant = 0;
if (HeaderSize == 0x0C) {
Width = data.read_shift(2);
Height = data.read_shift(2);
}
else {
Width = data.read_shift(4, 'i');
Height = data.read_shift(4, 'i');
}
Planes = data.read_shift(2);
BitCount = data.read_shift(2);
var out = {
Width: Width,
Height: Height,
BitCount: BitCount,
};
if (HeaderSize != 0x0C) {
Compression = data.read_shift(4);
ImageSize = data.read_shift(4);
XPelsPerMeter = data.read_shift(4, 'i');
YPelsPerMeter = data.read_shift(4, 'i');
ColorUsed = data.read_shift(4);
ColorImportant = data.read_shift(4);
out["Compression"] = Compression;
if (BitCount == 24 && ImageSize > Height * 3 * Width)
Width = out["Width"] = ImageSize / (Height * 3);
}
/* Colors */
/* BitmapBuffer */
if (ImageSize == data.length - data.l) {
out["ImageData"] = data.slice(data.l, data.length);
util_1.prep_blob(out["ImageData"], 0);
}
return out;
};
var add_to_objects = function (objects, obj) {
for (var i = 0; i < objects.length; ++i)
if (!objects[i]) {
objects[i] = obj;
return;
}
objects.push(obj);
};
exports.get_actions_prepped_bytes = function (data) {
var out = [];
/* 2.3.2.2 META_HEADER */
// Type (2 bytes) must be 1 or 2
var h = data.read_shift(2);
if (h != 1 && h != 2)
throw "Header: Type " + h + " must be 1 or 2";
// HeaderSize expected to be 9
if ((h = data.read_shift(2)) != 9)
throw "Header: HeaderSize " + h + " must be 9";
// Version (2 bytes) 1 or 3
h = data.read_shift(2);
if (h != 0x0100 && h != 0x0300)
throw "Header: Version " + h + " must be 0x0100 or 0x0300";
// SizeLow / SizeHigh
data.l += 4;
// #Objects
var NumberOfObjects = data.read_shift(2);
var objects = Array.from({ length: NumberOfObjects }, function () { return null; });
// MaxRecord
data.l += 4;
// NumberOfMembers
data.l += 2;
var rt = 0;
/* used for EMF */
var escapecnt = 0;
var CommentRecordCount = 0;
var RemainingBytes = 0;
var EnhancedMetafileDataSize = 0;
var bufs = [];
var states = [];
var state = {};
var sidx = -1;
while (data.l < data.length) {
h = data.read_shift(4);
var end = data.l + h * 2 - 4;
rt = data.read_shift(2);
var Record = Records_1.WMFRecords[rt];
if (rt == 0x0000)
break; // META_EOF
switch (rt) {
case 0x0626:
{ // META_ESCAPE
var EscapeFunction = data.read_shift(2);
var Escape = Records_1.WMFEscapes[EscapeFunction];
/* 2.3.6 */
switch (EscapeFunction) {
case 0x000F:
{ // META_ESCAPE_ENHANCED_METAFILE
var ByteCount = data.read_shift(2);
var tmp = data.read_shift(4);
if (tmp != 0x43464D57)
throw "Escape: Comment ID 0x" + tmp.toString(16) + " != 0x43464D57";
tmp = data.read_shift(4);
if (tmp != 0x00000001)
throw "Escape: Comment Type 0x" + tmp.toString(16) + " != 0x00000001";
tmp = data.read_shift(4);
if (tmp != 0x00010000)
throw "Escape: Version 0x" + tmp.toString(16) + " != 0x00010000";
var Checksum = data.read_shift(2);
data.l += 4; // Flags
if (escapecnt == 0) {
CommentRecordCount = data.read_shift(4); // total number of records
}
else {
var _CommentRecordCount = data.read_shift(4);
if (_CommentRecordCount != CommentRecordCount)
throw "Escape: CommentRecordCount " + _CommentRecordCount + " != " + CommentRecordCount;
}
var CurrentRecordSize = data.read_shift(4); // size of this record
var _RemainingBytes = data.read_shift(4);
if (escapecnt > 0 && CurrentRecordSize + _RemainingBytes != RemainingBytes)
throw "Escape: " + RemainingBytes + " != " + CurrentRecordSize + " + " + _RemainingBytes;
RemainingBytes = _RemainingBytes;
var _EnhancedMetafileDataSize = data.read_shift(4);
if (escapecnt == 0) {
if (_EnhancedMetafileDataSize != CurrentRecordSize + _RemainingBytes)
throw "Escape: " + _EnhancedMetafileDataSize + " != " + CurrentRecordSize + " + " + _RemainingBytes;
EnhancedMetafileDataSize = _EnhancedMetafileDataSize;
}
else if (EnhancedMetafileDataSize != _EnhancedMetafileDataSize)
throw "Escape: " + EnhancedMetafileDataSize + " != " + _EnhancedMetafileDataSize;
if (ByteCount != (end - data.l) + 34)
throw "Escape: Sizes " + ByteCount + " != " + (end - data.l) + " + 34";
if (end - data.l != CurrentRecordSize)
throw "Escape: CRSize " + CurrentRecordSize + " != " + (end - data.l);
bufs.push(data.slice(data.l, end));
++escapecnt;
if (escapecnt == CommentRecordCount) {
var prepped = util_1.bconcat(bufs);
util_1.prep_blob(prepped, 0);
parse_emf(prepped);
}
}
break;
default: throw "Escape: Unrecognized META_ESCAPE Type 0x" + EscapeFunction.toString(16);
}
}
break;
// #region 2.3.1 Bitmap Record Types
case 0x0940:
{ // 2.3.1.2 META_DIBBITBLT
var has_bitmap = h != (rt >> 8) + 3;
var RasterOperation = data.read_shift(4);
var YSrc = data.read_shift(2, "i");
var XSrc = data.read_shift(2, "i");
if (!has_bitmap)
data.l += 2;
var Height = data.read_shift(2, "i");
var Width = data.read_shift(2, "i");
var YDest = data.read_shift(2, "i");
var XDest = data.read_shift(2, "i");
var res = {
t: "cpy",
src: [[XSrc, Width], [YSrc, Height]],
dst: [XDest, YDest],
rop: RasterOperation,
s: Object.assign({}, state)
};
if (has_bitmap) {
var DIB = parse_dib(data.slice(data.l, end));
res.data = DIB;
}
out.push(res);
}
break;
case 0x0B41:
{ // 2.3.1.3 META_DIBSTRETCHBLT
var has_bitmap = h != (rt >> 8) + 3;
var RasterOperation = data.read_shift(4);
var SrcHeight = data.read_shift(2, "i");
var SrcWidth = data.read_shift(2, "i");
var YSrc = data.read_shift(2, "i");
var XSrc = data.read_shift(2, "i");
if (!has_bitmap)
data.l += 2;
var DestHeight = data.read_shift(2, "i");
var DestWidth = data.read_shift(2, "i");
var YDest = data.read_shift(2, "i");
var XDest = data.read_shift(2, "i");
var res = {
t: "str",
src: [[XSrc, SrcWidth], [YSrc, SrcHeight]],
dst: [[XDest, DestWidth], [YDest, DestHeight]],
rop: RasterOperation,
s: Object.assign({}, state)
};
if (has_bitmap) {
var DIB = parse_dib(data.slice(data.l, end));
res.data = DIB;
}
out.push(res);
}
break;
// #endregion
// #region 2.3.3 Drawing Record Types
case 0x0A32:
{ // 2.3.3.5 META_EXTTEXTOUT
var Y = data.read_shift(2);
var X = data.read_shift(2);
var StringLength = data.read_shift(2);
var fwOpts = data.read_shift(2); // 2.1.2.2
if (fwOpts & 0x06) {
data.l += 8; // Rectangle 2.2.2.18 (for clipping/opaquing)
}
var str = data.read_shift(StringLength, 'cpstr');
if (data.l < end) { /* TODO: Dx */ }
out.push({ t: "text", v: str, p: [X, Y], s: Object.assign({}, state) });
/* TODO!! */
}
break;
case 0x0325: // 2.3.3.14 META_POLYLINE
case 0x0324: // 2.3.3.15 META_POLYGON
{
var nPoints = data.read_shift(2);
var points = [];
for (var i = 0; i < nPoints; ++i)
points.push([data.read_shift(2), data.read_shift(2)]);
out.push({ t: "poly", p: points, g: rt !== 0x0325, s: Object.assign({}, state) });
}
break;
case 0x0538:
{ // 2.3.3.16 META_POLYPOLYGON
var nPolygons = data.read_shift(2);
var polys = [];
var szs = [];
/* 2.2.2.17 PolyPolygon */
for (var i = 0; i < nPolygons; ++i)
szs[i] = data.read_shift(2);
for (var i = 0; i < szs.length; ++i) {
polys[i] = [];
for (var j = 0; j < szs[i]; ++j)
polys[i].push([data.read_shift(2), data.read_shift(2)]);
out.push({ t: "poly", p: polys[i], g: true, s: Object.assign({}, state) });
}
}
break;
// #endregion
// #region 2.3.4 Object Record Types
case 0x02FC:
{ // 2.3.4.1 META_CREATEBRUSHINDIRECT
var obj = {};
obj.Brush = {
Style: data.read_shift(2),
Color: data.read_shift(4),
Hatch: data.read_shift(2)
};
add_to_objects(objects, obj);
}
break;
case 0x02FB:
{ // 2.3.4.2 META_CREATEFONTINDIRECT
var obj = {};
obj.Font = {};
/* 2.2.1.2 Font TODO!! */
var Height = data.read_shift(2, "i");
var Width = data.read_shift(2, "i");
var Escapement = data.read_shift(2, "i");
var Orientation = data.read_shift(2, "i");
var Weight = data.read_shift(2, "i");
var Italic = !!data.read_shift(1);
var Underline = !!data.read_shift(1);
var StrikeOut = !!data.read_shift(1);
var CharSet = data.read_shift(1);
var OutPrecision = data.read_shift(1);
var ClipPrecision = data.read_shift(1);
var Quality = data.read_shift(1);
var PitchAndFamily = data.read_shift(1);
var Facename = data.read_shift(32, "cstr");
obj.Font.Name = Facename;
obj.Font.Height = Height;
obj.Font.Weight = Weight;
obj.Font.Italic = Italic;
obj.Font.Angle = Escapement / 10;
add_to_objects(objects, obj);
}
break;
case 0x02FA:
{ // 2.3.4.5 META_CREATEPENINDIRECT
var obj = {};
obj.Pen = {
Style: data.read_shift(2),
Width: data.read_shift(4) & 0xFF,
Color: data.read_shift(4)
};
add_to_objects(objects, obj);
}
break;
case 0x01F0:
{ // 2.3.4.7 META_DELETEOBJECT
var ObjectIndex = data.read_shift(2);
objects[ObjectIndex] = null;
}
break;
case 0x012C:
{ // 2.3.4.9 META_SELECTCLIPREGION
var Region = data.read_shift(2);
//Object.assign(state, objects[Region]);
}
break;
case 0x012D:
{ // 2.3.4.10 META_SELECTOBJECT
var ObjectIndex = data.read_shift(2);
Object.assign(state, objects[ObjectIndex]);
// TODO!!
}
break;
// #endregion
// #region 2.3.5 State Record Types
case 0x0416: // 2.3.5.3 META_INTERSECTCLIPRECT
state.ClipRect = [[0, 0], [0, 0]];
state.ClipRect[1][1] = data.read_shift(2);
state.ClipRect[1][0] = data.read_shift(2);
state.ClipRect[0][1] = data.read_shift(2);
state.ClipRect[0][0] = data.read_shift(2);
break;
case 0x0127:
{ // 2.3.5.10 META_RESTOREDC
var nSavedDC = data.read_shift(2, 'i');
state = states[sidx = (nSavedDC >= 0 ? nSavedDC : sidx + nSavedDC)];
}
break;
case 0x001E: // 2.3.5.11 META_SAVEDC
states.push(state);
sidx = states.length - 1;
state = JSON.parse(JSON.stringify(state));
break;
case 0x0102: // 2.3.5.15 META_SETBKMODE
state.BkMode = data.read_shift(2);
break;
case 0x0103: // 2.3.5.17 META_SETMAPMODE
state.MapMode = data.read_shift(2);
break;
case 0x0106: // 2.3.5.20 META_SETPOLYFILLMODE
state.PolyFillMode = data.read_shift(2);
break;
case 0x0107: // 2.3.5.23 META_SETSTRETCHBLTMODE
state.StretchMode = data.read_shift(2);
break;
case 0x012E: // 2.3.5.24 META_SETTEXTALIGN
state.TextAlignmentMode = data.read_shift(2);
break;
case 0x0209: // 2.3.5.26 META_SETTEXTCOLOR
state.TextColor = data.read_shift(4);
break;
case 0x020C: // 2.3.5.30 META_SETWINDOWEXT
state.Extent = [0, 0];
state.Extent[1] = data.read_shift(2);
state.Extent[0] = data.read_shift(2);
break;
case 0x020B: // 2.3.5.31 META_SETWINDOWORG
state.Origin = [0, 0];
state.Origin[1] = data.read_shift(2);
state.Origin[0] = data.read_shift(2);
break;
// #endregion
default:
//if(!Record) throw `Record: Unrecognized type 0x${rt.toString(16)}`;
console.log(Record);
}
data.l = end;
}
if (rt !== 0)
throw "Record: Last Record Type " + rt + " is not EOF type";
return out;
};
exports.image_size_prepped_bytes = function (data) {
/* 2.3.22 META_HEADER */
// Type (2 bytes) must be 1 or 2
var h = data.read_shift(2);
if (h != 1 && h != 2)
throw "Header: Type " + h + " must be 1 or 2";
// HeaderSize expected to be 9
if ((h = data.read_shift(2)) != 9)
throw "Header: HeaderSize " + h + " must be 9";
// Version (2 bytes) 1 or 3
h = data.read_shift(2);
if (h != 0x0100 && h != 0x0300)
throw "Header: Version " + h + " must be 0x0100 or 0x0300";
data.l = 18;
var rt = 0;
while (data.l < data.length) {
h = data.read_shift(4);
var end = data.l + h * 2 - 4;
rt = data.read_shift(2);
if (rt == 0x0000)
break; // META_EOF
if (rt == 0x020C) { // 2.3.5.30 META_SETWINDOWEXT
var extents = [NaN, NaN];
extents[1] = data.read_shift(2);
extents[0] = data.read_shift(2);
return extents;
}
data.l = end;
}
return [NaN, NaN];
};
//# sourceMappingURL=wmf.js.map