pptx-automizer
Version:
A template based pptx generator
298 lines • 12.2 kB
JavaScript
;
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
return new (P || (P = Promise))(function (resolve, reject) {
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
step((generator = generator.apply(thisArg, _arguments || [])).next());
});
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.XmlSlideHelper = exports.mapUriType = exports.nsMain = void 0;
const xml_helper_1 = require("./xml-helper");
const general_helper_1 = require("../../../PitchDepartment/src/helper/general-helper");
exports.nsMain = 'http://schemas.openxmlformats.org/presentationml/2006/main';
exports.mapUriType = {
'http://schemas.openxmlformats.org/drawingml/2006/table': 'table',
'http://schemas.openxmlformats.org/drawingml/2006/chart': 'chart',
'http://schemas.microsoft.com/office/drawing/2014/chartex': 'chartEx',
'http://schemas.openxmlformats.org/officeDocument/2006/relationships/oleObject': 'oleObject',
'http://schemas.openxmlformats.org/officeDocument/2006/relationships/hyperlink': 'hyperlink',
};
/**
* Class that represents an XML slide helper
*/
class XmlSlideHelper {
/**
* Constructor for the XmlSlideHelper class.
* @param {XmlDocument} slideXml - The slide XML document to be used by the helper.
* @param hasShapes
*/
constructor(slideXml, hasShapes) {
/**
* Fetches an XML file from the given path and extracts the dimensions.
*
* @param {string} path - The path of the XML file.
* @returns {Promise<{ width: number; height: number } | null>} - A promise that resolves with an object containing the width and height, or `null` if there was an error.
*/
this.getAndExtractDimensions = (path) => __awaiter(this, void 0, void 0, function* () {
try {
const xml = yield xml_helper_1.XmlHelper.getXmlFromArchive(this.hasShapes.sourceTemplate.archive, path);
if (!xml)
return null;
const sldSz = xml.getElementsByTagName('p:sldSz')[0];
if (sldSz) {
const width = XmlSlideHelper.parseCoordinate(sldSz, 'cx');
const height = XmlSlideHelper.parseCoordinate(sldSz, 'cy');
return { width, height };
}
return null;
}
catch (error) {
console.warn(`Error while fetching XML from path ${path}: ${error}`);
return null;
}
});
if (!slideXml) {
throw Error('Slide XML is not defined');
}
this.slideXml = slideXml;
this.hasShapes = hasShapes;
}
getSlideCreationId() {
const creationIdItem = this.slideXml
.getElementsByTagName('p14:creationId')
.item(0);
if (!creationIdItem) {
return;
}
const creationIdSlide = creationIdItem.getAttribute('val');
if (!creationIdSlide) {
return;
}
return Number(creationIdSlide);
}
/**
* Get an array of ElementInfo objects for all named elements on a slide.
* @param selector
*/
getElement(selector) {
return __awaiter(this, void 0, void 0, function* () {
const shapeNode = xml_helper_1.XmlHelper.isElementCreationId(selector)
? xml_helper_1.XmlHelper.findByCreationId(this.slideXml, selector)
: xml_helper_1.XmlHelper.findByName(this.slideXml, selector);
return XmlSlideHelper.getElementInfo(shapeNode);
});
}
/**
* Get an array of ElementInfo objects for all named elements on a slide.
* @param filterTags Use an array of strings to filter the output array
*/
getAllElements(filterTags) {
const elementInfo = [];
try {
const shapeNodes = this.getNamedElements(filterTags);
shapeNodes.forEach((shapeNode) => {
elementInfo.push(XmlSlideHelper.getElementInfo(shapeNode));
});
}
catch (error) {
console.error(error);
throw new Error(`Failed to retrieve elements: ${error.message}`);
}
return elementInfo;
}
/**
* Get all text element IDs from the slide.
* @return {string[]} An array of text element IDs.
*/
getAllTextElementIds(useCreationIds) {
const elementIds = [];
try {
elementIds.push(...this.getAllElements(['sp'])
.filter((element) => element.hasTextBody)
.map((element) => (useCreationIds ? element.id : element.name)));
}
catch (error) {
console.error(error);
throw new Error(`Failed to retrieve text element IDs: ${error.message}`);
}
return elementIds;
}
static getElementInfo(slideElement) {
return {
name: XmlSlideHelper.getElementName(slideElement),
id: XmlSlideHelper.getElementCreationId(slideElement),
type: XmlSlideHelper.getElementType(slideElement),
position: XmlSlideHelper.parseShapeCoordinates(slideElement),
hasTextBody: !!XmlSlideHelper.getTextBody(slideElement),
getXmlElement: () => slideElement,
getText: () => XmlSlideHelper.parseTextFragments(slideElement),
getTableInfo: () => XmlSlideHelper.readTableInfo(slideElement),
getAltText: () => XmlSlideHelper.getImageAltText(slideElement),
};
}
/**
* Retrieves a list of all named elements on a slide. Automation requires at least a name.
* @param filterTags Use an array of strings to filter the output array
*/
getNamedElements(filterTags) {
const skipTags = ['spTree'];
const nvPrs = this.slideXml.getElementsByTagNameNS(exports.nsMain, 'cNvPr');
const namedElements = [];
xml_helper_1.XmlHelper.modifyCollection(nvPrs, (nvPr) => {
const parentNode = nvPr.parentNode.parentNode;
const parentTag = parentNode.localName;
if (!skipTags.includes(parentTag) &&
(!(filterTags === null || filterTags === void 0 ? void 0 : filterTags.length) || filterTags.includes(parentTag))) {
namedElements.push(parentNode);
}
});
return namedElements;
}
static getTextBody(shapeNode) {
return shapeNode.getElementsByTagNameNS(exports.nsMain, 'txBody').item(0);
}
static parseTextFragments(shapeNode) {
const txBody = XmlSlideHelper.getTextBody(shapeNode);
const textFragments = [];
if (txBody) {
(0, general_helper_1.vd)('test');
const texts = txBody.getElementsByTagName('a:t');
for (let t = 0; t < texts.length; t++) {
textFragments.push(texts.item(t).textContent);
}
}
return textFragments;
}
static getNonVisibleProperties(shapeNode) {
return shapeNode.getElementsByTagNameNS(exports.nsMain, 'cNvPr').item(0);
}
static getImageAltText(slideElement) {
const cNvPr = XmlSlideHelper.getNonVisibleProperties(slideElement);
if (cNvPr) {
return cNvPr.getAttribute('descr');
}
}
static getElementName(slideElement) {
const cNvPr = XmlSlideHelper.getNonVisibleProperties(slideElement);
if (cNvPr) {
return cNvPr.getAttribute('name');
}
}
static getElementCreationId(slideElement) {
const cNvPr = XmlSlideHelper.getNonVisibleProperties(slideElement);
if (cNvPr) {
const creationIdElement = cNvPr
.getElementsByTagName('a16:creationId')
.item(0);
if (creationIdElement) {
return creationIdElement.getAttribute('id');
}
}
}
/**
* Parses local tag name to specify element type in case it is a 'graphicFrame'.
* @param slideElementParent
*/
static getElementType(slideElementParent) {
let type = slideElementParent.localName;
switch (type) {
case 'graphicFrame':
const graphicData = slideElementParent.getElementsByTagName('a:graphicData')[0];
const uri = graphicData.getAttribute('uri');
type = exports.mapUriType[uri] ? exports.mapUriType[uri] : type;
break;
case 'oleObj':
type = 'OLEObject';
break;
}
// Check for hyperlinks
const hasHyperlink = slideElementParent.getElementsByTagName('a:hlinkClick');
if (hasHyperlink.length > 0) {
type = 'Hyperlink';
}
return type;
}
static parseShapeCoordinates(slideElementParent) {
const xFrmsA = slideElementParent.getElementsByTagName('a:xfrm');
const xFrmsP = slideElementParent.getElementsByTagName('p:xfrm');
const xFrms = xFrmsP.item(0) ? xFrmsP : xFrmsA;
const position = {
x: 0,
y: 0,
cx: 0,
cy: 0,
};
if (!xFrms.item(0)) {
return position;
}
const xFrm = xFrms.item(0);
const Off = xFrm.getElementsByTagName('a:off').item(0);
const Ext = xFrm.getElementsByTagName('a:ext').item(0);
position.x = XmlSlideHelper.parseCoordinate(Off, 'x');
position.y = XmlSlideHelper.parseCoordinate(Off, 'y');
position.cx = XmlSlideHelper.parseCoordinate(Ext, 'cx');
position.cy = XmlSlideHelper.parseCoordinate(Ext, 'cy');
return position;
}
/**
* Asynchronously retrieves the dimensions of a slide.
* Tries to find the dimensions from the slide XML, then from the layout, master, and presentation XMLs in order.
*
* @returns {Promise<{ width: number, height: number }>} The dimensions of the slide.
* @throws Error if unable to determine dimensions.
*/
getDimensions() {
return __awaiter(this, void 0, void 0, function* () {
try {
const dimensions = yield this.getAndExtractDimensions('ppt/presentation.xml');
if (dimensions)
return dimensions;
}
catch (error) {
console.error(`Error while fetching slide dimensions: ${error}`);
throw error;
}
});
}
}
exports.XmlSlideHelper = XmlSlideHelper;
XmlSlideHelper.parseCoordinate = (element, attributeName) => {
return parseInt(element.getAttribute(attributeName), 10);
};
XmlSlideHelper.readTableInfo = (element) => {
const info = [];
const rows = element.getElementsByTagName('a:tr');
if (!rows) {
console.error("Can't find a table row.");
return info;
}
for (let r = 0; r < rows.length; r++) {
const row = rows.item(r);
const columns = row.getElementsByTagName('a:tc');
for (let c = 0; c < columns.length; c++) {
const cell = columns.item(c);
const gridSpan = cell.getAttribute('gridSpan');
const hMerge = cell.getAttribute('hMerge');
const texts = cell.getElementsByTagName('a:t');
const text = [];
for (let t = 0; t < texts.length; t++) {
text.push(texts.item(t).textContent);
}
info.push({
row: r,
column: c,
rowXml: row,
columnXml: cell,
text: text,
textContent: text.join(''),
gridSpan: Number(gridSpan),
hMerge: Number(hMerge),
});
}
}
return info;
};
//# sourceMappingURL=xml-slide-helper.js.map