Comparing version
interface XlsxParserOptions { | ||
/** When the option `dense: false` is passed, parsers will skip empty cells. */ | ||
dense?: boolean; | ||
/** When the opction `styles: false` is passed, parsers will skip cell styles. */ | ||
/** When the option `styles: false` is passed, parsers will skip cell styles. */ | ||
styles?: boolean; | ||
/** When the option `drawings: false` is passed, parsers will skip drawings. */ | ||
drawings?: boolean; | ||
/** When `true`, hidden rows will be skipped during parsing. */ | ||
skipHiddenRows?: boolean; | ||
} | ||
interface Position { | ||
/** Column that is used within the from and to elements. */ | ||
col: number; | ||
/** Column offset within a cell. */ | ||
colOff: number; | ||
/** Row that is used within the from and to elements. */ | ||
row: number; | ||
/** Row offset within a cell. */ | ||
rowOff: number; | ||
} | ||
interface DrawingPosition { | ||
/** From position. */ | ||
from: Position; | ||
/** To position. */ | ||
to: Position; | ||
} | ||
type DrawingObjectType = 'image' | 'shape' | 'connector' | 'textbox' | 'group' | 'unknown'; | ||
interface Drawing { | ||
/** Specifies a unique identifier for the current DrawingML object. */ | ||
id: string; | ||
/** Specifies the name of the object. */ | ||
name: string; | ||
/** Specifies the title (caption) of the current DrawingML object */ | ||
title: string; | ||
/** Specifies alternative text for the current DrawingML object. */ | ||
description: string; | ||
/** Drawing type. */ | ||
type: DrawingObjectType; | ||
/** File in base64 */ | ||
base64: string; | ||
/** Position */ | ||
position: DrawingPosition; | ||
/** Specific properties by type */ | ||
properties?: Record<string, any>; | ||
} | ||
interface RowStyle { | ||
/** Row position. */ | ||
r: number; | ||
/** Row height. */ | ||
height: number; | ||
/** Indicates whether the row should be hidden */ | ||
hidden: boolean; | ||
/** Indicates whether the row should be collapsed */ | ||
collapsed: boolean; | ||
} | ||
@@ -98,2 +144,6 @@ interface ColStyle { | ||
defaultRowHeight: number; | ||
/** 'true' if rows are hidden by default */ | ||
zeroHeight: boolean; | ||
/** Drawings */ | ||
drawings: Drawing[]; | ||
} | ||
@@ -100,0 +150,0 @@ |
@@ -187,2 +187,9 @@ "use strict"; | ||
} | ||
function positionFromExt(start, extValue) { | ||
const EMU_PER_PIXEL = 9525; | ||
const extInPx = extValue ? parseInt(extValue, 10) / EMU_PER_PIXEL : 0; | ||
const CELL_WIDTH_PX = 64; | ||
const CELL_HEIGHT_PX = 20; | ||
return start.col + Math.floor(extInPx / CELL_WIDTH_PX); | ||
} | ||
@@ -638,4 +645,6 @@ // src/core/theme/parseThemeXml.ts | ||
mergeCells: [], | ||
drawings: [], | ||
defaultColWidth: 12.5, | ||
defaultRowHeight: 3.25 | ||
defaultRowHeight: 3.25, | ||
zeroHeight: false | ||
}); | ||
@@ -661,4 +670,4 @@ }); | ||
// src/core/worksheet/parseWorksheetXlm.ts | ||
var parseWorksheetXml = (str, styleSheet, sharedStrings, dense) => { | ||
var _a, _b, _c, _d; | ||
var parseWorksheetXml = (str, relStr, styleSheet, sharedStrings, drawingFiles, dense, skipHiddenRows) => { | ||
var _a, _b, _c, _d, _e; | ||
const worksheet = { | ||
@@ -672,6 +681,9 @@ id: 0, | ||
mergeCells: [], | ||
drawings: [], | ||
defaultColWidth: 12.5, | ||
defaultRowHeight: 3.25 | ||
defaultRowHeight: 3.25, | ||
zeroHeight: false | ||
}; | ||
const xmlDoc = new DOMParser().parseFromString(str, "text/xml"); | ||
const xmlRel = relStr ? new DOMParser().parseFromString(relStr, "text/xml") : void 0; | ||
const worksheetElement = getElementByName(xmlDoc, "worksheet"); | ||
@@ -683,2 +695,3 @@ const dimensionElement = getElementByName(worksheetElement, "dimension"); | ||
const sheetFormatPrElement = getElementByName(worksheetElement, "sheetFormatPr"); | ||
const drawingElement = getElementsByName(xmlRel, "Relationship"); | ||
const colsArray = getElementsByName(colsElement, "col"); | ||
@@ -703,2 +716,3 @@ const rowsArray = getElementsByName(sheetDataElement, "row"); | ||
worksheet.defaultRowHeight = +((_d = sheetFormatPrElement.getAttribute("defaultRowHeight")) != null ? _d : 3.25); | ||
worksheet.zeroHeight = sheetFormatPrElement.getAttribute("zeroHeight") === "1"; | ||
} | ||
@@ -708,3 +722,3 @@ if (colsArray) { | ||
colsArray.forEach((x) => { | ||
var _a2, _b2, _c2, _d2, _e; | ||
var _a2, _b2, _c2, _d2, _e2; | ||
columnStyles.push({ | ||
@@ -715,3 +729,3 @@ min: +((_a2 = x.getAttribute("min")) != null ? _a2 : 1), | ||
hidden: ((_d2 = x.getAttribute("hidden")) != null ? _d2 : "false") === "true", | ||
collapsed: ((_e = x.getAttribute("collapsed")) != null ? _e : "false") === "true" | ||
collapsed: ((_e2 = x.getAttribute("collapsed")) != null ? _e2 : "false") === "true" | ||
}); | ||
@@ -723,13 +737,15 @@ }); | ||
rowsArray.forEach((x) => { | ||
var _a2, _b2; | ||
var _a2, _b2, _c2, _d2, _e2, _f, _g; | ||
const index = +((_a2 = x.getAttribute("r")) != null ? _a2 : 0) - 1; | ||
if (index >= 0) { | ||
const hiddenProp = ((_b2 = x.getAttribute("hidden")) != null ? _b2 : "false") === "true"; | ||
const collapsedProp = ((_c2 = x.getAttribute("collapsed")) != null ? _c2 : "false") === "true"; | ||
if (index >= 0 && (!skipHiddenRows || !hiddenProp && !collapsedProp)) { | ||
const cols = getElementsByName(x, "c"); | ||
cols.forEach((y) => { | ||
var _a3, _b3, _c2, _d2, _e, _f, _g, _h, _i, _j, _k, _l, _m, _n, _o; | ||
var _a3, _b3, _c3, _d3, _e3, _f2, _g2, _h, _i, _j, _k, _l, _m, _n, _o; | ||
const r = (_a3 = y.getAttribute("r")) != null ? _a3 : "A"; | ||
const s = +((_b3 = y.getAttribute("s")) != null ? _b3 : -1); | ||
const t = (_c2 = y.getAttribute("t")) != null ? _c2 : ""; | ||
const formula = (_e = (_d2 = getElementByName(y, "f")) == null ? void 0 : _d2.textContent) != null ? _e : ""; | ||
const value = (_g = (_f = getElementByName(y, "v")) == null ? void 0 : _f.textContent) != null ? _g : ""; | ||
const t = (_c3 = y.getAttribute("t")) != null ? _c3 : ""; | ||
const formula = (_e3 = (_d3 = getElementByName(y, "f")) == null ? void 0 : _d3.textContent) != null ? _e3 : ""; | ||
const value = (_g2 = (_f2 = getElementByName(y, "v")) == null ? void 0 : _f2.textContent) != null ? _g2 : ""; | ||
const pos = getPositionInArray(r); | ||
@@ -758,6 +774,11 @@ worksheet.data[pos.row][pos.col] = { | ||
}); | ||
if (!skipHiddenRows || cols.length > 0) { | ||
worksheet.rowStyles.push({ | ||
r: +((_d2 = x.getAttribute("r")) != null ? _d2 : 0), | ||
height: +((_e2 = x.getAttribute("ht")) != null ? _e2 : worksheet.defaultRowHeight), | ||
hidden: ((_f = x.getAttribute("hidden")) != null ? _f : "false") === "true", | ||
collapsed: ((_g = x.getAttribute("collapsed")) != null ? _g : "false") === "true" | ||
}); | ||
} | ||
} | ||
worksheet.rowStyles.push({ | ||
height: +((_b2 = x.getAttribute("ht")) != null ? _b2 : worksheet.defaultRowHeight) | ||
}); | ||
}); | ||
@@ -771,5 +792,150 @@ if (mergeCellArray) { | ||
} | ||
if (skipHiddenRows) { | ||
if (worksheet.defaultRowHeight === 0 && worksheet.zeroHeight) { | ||
worksheet.data = worksheet.data.filter((x) => x.length > 0 && x[0] !== void 0 && x.some((y) => y.ref !== "")); | ||
} | ||
} | ||
const drawingRel = drawingElement.find( | ||
(rel) => { | ||
var _a2; | ||
return (_a2 = rel.getAttribute("Type")) == null ? void 0 : _a2.includes("drawing"); | ||
} | ||
); | ||
if (drawingRel) { | ||
const drawingTarget = drawingRel.getAttribute("Target") || ""; | ||
const drawingFileName = drawingTarget.split("/").pop() || ""; | ||
const drawingFile = drawingFiles.find((df) => df.src === drawingFileName); | ||
worksheet.drawings = (_e = drawingFile == null ? void 0 : drawingFile.drawings) != null ? _e : []; | ||
} | ||
return worksheet; | ||
}; | ||
// src/core/drawing/parseDrawingXml.ts | ||
var parseDrawingXml = (drawingStr, relStr, media) => { | ||
const drawings = []; | ||
const rels = []; | ||
const xmlDoc = new DOMParser().parseFromString(drawingStr, "text/xml"); | ||
const xmlRel = new DOMParser().parseFromString(relStr, "text/xml"); | ||
const relElement = getElementByName(xmlRel, "Relationships"); | ||
const drawingElement = getElementByName(xmlDoc, "xdr:wsDr"); | ||
const relationshipArray = getElementsByName(relElement, "Relationship"); | ||
const drawingsArray = drawingElement ? [ | ||
...getElementsByName(drawingElement, "xdr:twoCellAnchor"), | ||
...getElementsByName(drawingElement, "xdr:oneCellAnchor") | ||
] : []; | ||
if (relationshipArray) { | ||
relationshipArray.forEach((rel) => { | ||
rels.push({ | ||
id: rel.getAttribute("Id") || "", | ||
uri: rel.getAttribute("Target") || "" | ||
}); | ||
}); | ||
} | ||
if (drawingsArray) { | ||
drawingsArray.forEach((anchor) => { | ||
const elementTypeMap = { | ||
"xdr:pic": "image", | ||
"xdr:sp": "shape", | ||
"xdr:cxnSp": "connector", | ||
"xdr:grpSp": "group" | ||
}; | ||
let drawingType = "unknown"; | ||
let containerElement; | ||
for (const [tag, detectedType] of Object.entries(elementTypeMap)) { | ||
const foundElement = getElementByName(anchor, tag); | ||
if (foundElement) { | ||
drawingType = detectedType; | ||
containerElement = foundElement; | ||
if (drawingType === "shape" && getElementByName(containerElement, "xdr:txBody")) { | ||
drawingType = "textbox"; | ||
} | ||
break; | ||
} | ||
} | ||
const cNvPr = getElementByName(anchor, "xdr:cNvPr"); | ||
const id = (cNvPr == null ? void 0 : cNvPr.getAttribute("id")) || ""; | ||
const name = (cNvPr == null ? void 0 : cNvPr.getAttribute("name")) || ""; | ||
const title = (cNvPr == null ? void 0 : cNvPr.getAttribute("title")) || ""; | ||
const description = (cNvPr == null ? void 0 : cNvPr.getAttribute("descr")) || ""; | ||
const blip = getElementByName(anchor, "a:blip"); | ||
const embedId = (blip == null ? void 0 : blip.getAttribute("r:embed")) || ""; | ||
const rel = rels.find((r) => r.id === embedId); | ||
const mediaFileName = rel ? rel.uri.split("/").pop() : ""; | ||
const mediaFile = media.find((m) => m.name === mediaFileName); | ||
const from = getElementByName(anchor, "xdr:from"); | ||
const to = getElementByName(anchor, "xdr:to"); | ||
const ext = getElementByName(anchor, "xdr:ext"); | ||
const getPosition = (posElement) => { | ||
var _a, _b, _c, _d; | ||
return { | ||
col: parseInt(((_a = getElementByName(posElement, "xdr:col")) == null ? void 0 : _a.textContent) || "0", 10), | ||
colOff: parseInt(((_b = getElementByName(posElement, "xdr:colOff")) == null ? void 0 : _b.textContent) || "0", 10), | ||
row: parseInt(((_c = getElementByName(posElement, "xdr:row")) == null ? void 0 : _c.textContent) || "0", 10), | ||
rowOff: parseInt(((_d = getElementByName(posElement, "xdr:rowOff")) == null ? void 0 : _d.textContent) || "0", 10) | ||
}; | ||
}; | ||
const position = { | ||
from: getPosition(from), | ||
to: to ? getPosition(to) : { | ||
col: positionFromExt(getPosition(from), ext == null ? void 0 : ext.getAttribute("cx")), | ||
colOff: 0, | ||
row: positionFromExt(getPosition(from), ext == null ? void 0 : ext.getAttribute("cy")), | ||
rowOff: 0 | ||
} | ||
}; | ||
const properties = extractPropertiesByType(drawingType, containerElement); | ||
drawings.push({ | ||
id, | ||
name, | ||
title, | ||
description, | ||
type: drawingType, | ||
base64: (mediaFile == null ? void 0 : mediaFile.base64) || "", | ||
properties, | ||
position | ||
}); | ||
}); | ||
} | ||
return drawings; | ||
}; | ||
function extractPropertiesByType(type, container) { | ||
var _a, _b; | ||
if (!container) return {}; | ||
switch (type) { | ||
case "image": | ||
const blip = getElementByName(container, "a:blip"); | ||
return { | ||
embedId: (blip == null ? void 0 : blip.getAttribute("r:embed")) || "" | ||
}; | ||
case "textbox": | ||
const textElements = getElementsByName(container, "a:t"); | ||
const text = textElements.map((el) => el.textContent).join("\n"); | ||
return { text }; | ||
case "shape": | ||
const solidFill = getElementByName(container, "a:solidFill"); | ||
const fillColor = argbToHex((_a = extractColor(solidFill)) != null ? _a : ""); | ||
const prstGeom = getElementByName(container, "a:prstGeom"); | ||
const shapeType = (prstGeom == null ? void 0 : prstGeom.getAttribute("prst")) || "custom"; | ||
return { fillColor, shapeType }; | ||
case "connector": | ||
const line = getElementByName(container, "a:ln"); | ||
const lineColor = argbToHex((_b = extractColor(getElementByName(line, "a:solidFill"))) != null ? _b : ""); | ||
const lineWidth = (line == null ? void 0 : line.getAttribute("w")) || ""; | ||
return { lineColor, lineWidth }; | ||
case "group": | ||
return {}; | ||
default: | ||
return {}; | ||
} | ||
} | ||
function extractColor(colorElement) { | ||
var _a; | ||
if (!colorElement) return void 0; | ||
const srgbClr = getElementByName(colorElement, "a:srgbClr"); | ||
if (srgbClr) return `#${srgbClr.getAttribute("val")}`; | ||
const schemeClr = getElementByName(colorElement, "a:schemeClr"); | ||
if (schemeClr) return (_a = schemeClr.getAttribute("val")) != null ? _a : void 0; | ||
return void 0; | ||
} | ||
// src/xlsxParser.ts | ||
@@ -784,4 +950,4 @@ var XlsxParser = class { | ||
readFile(_0) { | ||
return __async(this, arguments, function* (file, options = { dense: false, styles: false }) { | ||
var _a, _b, _c, _d, _e, _f, _g, _h; | ||
return __async(this, arguments, function* (file, options = { dense: false, styles: false, drawings: false, skipHiddenRows: false }) { | ||
var _a, _b, _c, _d, _e, _f, _g, _h, _i, _j, _k, _l, _m, _n, _o; | ||
const nZip = new import_jszip.default(); | ||
@@ -792,4 +958,9 @@ const result = yield nZip.loadAsync(file); | ||
const sheetFiles = files.filter((x) => x.includes("xl/worksheets/sheet")); | ||
const drawingFiles = files.filter((x) => x.includes("xl/drawings/drawing")); | ||
const drawingRelFiles = files.filter((x) => x.includes("xl/drawings/_rels/drawing")); | ||
const mediaFiles = files.filter((x) => x.includes("xl/media/")); | ||
let sharedStrings = []; | ||
let themes = []; | ||
let drawings = []; | ||
const media = []; | ||
let style = { | ||
@@ -834,6 +1005,26 @@ fonts: [], | ||
} | ||
for (let i = 0; i < mediaFiles.length; i++) { | ||
const base64 = (_h = yield (_g = nZip.file(mediaFiles[i])) == null ? void 0 : _g.async("base64")) != null ? _h : ""; | ||
media.push({ | ||
name: mediaFiles[i].split("/").pop() || "", | ||
base64 | ||
}); | ||
} | ||
for (let i = 0; i < drawingFiles.length; i++) { | ||
const xml = yield (_i = nZip.file(drawingFiles[i])) == null ? void 0 : _i.async("string"); | ||
const relName = drawingFiles[i].split("/").pop() || ""; | ||
const relFilePath = (_j = drawingRelFiles.find((x) => x.includes(`${relName}.rels`))) != null ? _j : ""; | ||
const rel = yield (_k = nZip.file(relFilePath)) == null ? void 0 : _k.async("string"); | ||
if (xml && rel) { | ||
const nDrawing = parseDrawingXml(xml, rel, media); | ||
drawings.push({ src: `drawing${i + 1}.xml`, drawings: nDrawing }); | ||
} | ||
} | ||
for (let i = 0; i < sheetFiles.length; i++) { | ||
const xml = yield (_g = nZip.file(sheetFiles[i])) == null ? void 0 : _g.async("string"); | ||
const xml = yield (_l = nZip.file(sheetFiles[i])) == null ? void 0 : _l.async("string"); | ||
const sheetName = sheetFiles[i].split("/").pop() || ""; | ||
const relFilePath = `xl/worksheets/_rels/${sheetName}.rels`; | ||
const rel = yield (_m = nZip.file(relFilePath)) == null ? void 0 : _m.async("string"); | ||
if (xml) { | ||
const nSheet = parseWorksheetXml(xml, style, sharedStrings, (_h = options.dense) != null ? _h : false); | ||
const nSheet = parseWorksheetXml(xml, rel, style, sharedStrings, drawings, (_n = options.dense) != null ? _n : false, (_o = options.skipHiddenRows) != null ? _o : false); | ||
workbook.workSheets[i].dimention = nSheet.dimention; | ||
@@ -844,5 +1035,5 @@ workbook.workSheets[i].columnStyles = nSheet.columnStyles; | ||
workbook.workSheets[i].mergeCells = nSheet.mergeCells; | ||
workbook.workSheets[i].drawings = nSheet.drawings; | ||
} | ||
} | ||
console.log("themes", themes); | ||
return workbook; | ||
@@ -849,0 +1040,0 @@ }); |
{ | ||
"name": "xlsx-to-js", | ||
"version": "1.0.2", | ||
"version": "1.0.3", | ||
"description": "A TypeScript-based library for parsing Excel (XLSX) with browser support", | ||
@@ -5,0 +5,0 @@ "main": "./dist/index.js", |
@@ -24,3 +24,3 @@ # Xlsx-to-js | ||
const xlsxParser = new XlsxParser(); | ||
const workbook = await xlsxParser.readFile(file, { dense: true, styles: true }); | ||
const workbook = await xlsxParser.readFile(file, { dense: true, styles: true, drawings: true, skipHiddenRows: true }); | ||
``` | ||
@@ -30,6 +30,8 @@ The `readFile` method extract data from spreadsheet bytes stored in a `ArrayBuffer`. | ||
The second argument to `options` accepts the properties: | ||
|Option |Default |Description| | ||
|--------|--------|-----------| | ||
|dense |false | When the option `dense: false` is passed, parsers will skip empty cells. | | ||
|styles |false | When the opction `styles: false` is passed, parsers will skip cell styles. | | ||
|Option |Default |Description| | ||
|---------------|-----------|-----------| | ||
|dense |false | When the option `dense: false` is passed, parsers will skip empty cells. | | ||
|styles |false | When the opction `styles: false` is passed, parsers will skip cell styles. | | ||
|drawings |false | When the option `drawings: false` is passed, parsers will skip parsing drawings and graphical objects. | | ||
|skipHiddenRows |false | When the option `skipHiddenRows: true` is passed, hidden rows will be ignored during parsing. | | ||
@@ -36,0 +38,0 @@ |
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
242790
28.54%2213
24.05%39
5.41%