Skip to main content
Glama
xml-slide-helper.js•9.05 kB
"use strict"; 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"); 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', }; /** * 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 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, }; } /** * Retreives 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 getNonVisibleProperties(shapeNode) { return shapeNode.getElementsByTagNameNS(exports.nsMain, 'cNvPr').item(0); } 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; } 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); }; //# sourceMappingURL=xml-slide-helper.js.map

Latest Blog Posts

MCP directory API

We provide all the information about MCP servers via our MCP API.

curl -X GET 'https://glama.ai/api/mcp/v1/servers/guangxiangdebizi/PPT-MCP'

If you have feedback or need assistance with the MCP directory API, please join our Discord server