Skip to main content
Glama
has-shapes.js•25.4 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 }); const xml_relationship_helper_1 = require("../helper/xml-relationship-helper"); const xml_helper_1 = require("../helper/xml-helper"); const file_helper_1 = require("../helper/file-helper"); const chart_1 = require("../shapes/chart"); const image_1 = require("../shapes/image"); const element_type_1 = require("../enums/element-type"); const generic_1 = require("../shapes/generic"); const xml_slide_helper_1 = require("../helper/xml-slide-helper"); class HasShapes { constructor(params) { /** * List of unsupported tags in slide xml * @internal */ this.unsupportedTags = [ 'p:custDataLst', 'p:oleObj', // 'mc:AlternateContent', //'a14:imgProps', ]; /** * List of unsupported tags in slide xml * @internal */ this.unsupportedRelationTypes = [ 'http://schemas.openxmlformats.org/officeDocument/2006/relationships/oleObject', 'http://schemas.openxmlformats.org/officeDocument/2006/relationships/vmlDrawing', 'http://schemas.openxmlformats.org/officeDocument/2006/relationships/tags', ]; this.sourceTemplate = params.template; this.modifications = []; this.relModifications = []; this.importElements = []; this.status = params.presentation.status; this.content = params.presentation.content; } /** * Asynchronously retrieves all text element IDs from the slide. * @returns {Promise<string[]>} A promise that resolves to an array of text element IDs. */ getAllTextElementIds() { return __awaiter(this, void 0, void 0, function* () { const xmlSlideHelper = yield this.getSlideHelper(); // Get all text element IDs return xmlSlideHelper.getAllTextElementIds(this.sourceTemplate.useCreationIds || false); }); } /** * Asynchronously retrieves all elements from the slide. * @params filterTags Use an array of strings to filter parent tags (e.g. 'sp') * @returns {Promise<ElementInfo[]>} A promise that resolves to an array of ElementInfo objects. */ getAllElements(filterTags) { return __awaiter(this, void 0, void 0, function* () { const xmlSlideHelper = yield this.getSlideHelper(); // Get all ElementInfo objects return xmlSlideHelper.getAllElements(filterTags); }); } /** * Asynchronously retrieves the dimensions of the slide. * This function utilizes the XmlSlideHelper to get the slide dimensions. * * @returns {Promise<{width: number, height: number}>} A promise that resolves to an object containing the width and height of the slide. */ getDimensions() { return __awaiter(this, void 0, void 0, function* () { const xmlSlideHelper = yield this.getSlideHelper(); return xmlSlideHelper.getDimensions(); }); } /** * Asynchronously retrieves an instance of XmlSlideHelper for slide. * @returns {Promise<XmlSlideHelper>} An instance of XmlSlideHelper. */ getSlideHelper() { return __awaiter(this, void 0, void 0, function* () { try { // Retrieve the slide XML data const slideXml = yield xml_helper_1.XmlHelper.getXmlFromArchive(this.sourceTemplate.archive, this.sourcePath); // Initialize the XmlSlideHelper return new xml_slide_helper_1.XmlSlideHelper(slideXml, this); } catch (error) { // Log the error message throw new Error(error.message); } }); } /** * Push modifications list * @internal * @param callback */ modify(callback) { this.modifications.push(callback); } /** * Push relations modifications list * @internal * @param callback */ modifyRelations(callback) { this.relModifications.push(callback); } /** * Select and modify a single element on an added slide. * @param {string} selector - Element's name on the slide. * Should be a unique string defined on the "Selection"-pane within ppt. * @param {ShapeModificationCallback | ShapeModificationCallback[]} callback - One or more callback functions to apply. * Depending on the shape type (e.g. chart or table), different arguments will be passed to the callback. */ modifyElement(selector, callback) { const presName = this.sourceTemplate.name; const slideNumber = this.sourceNumber; this.addElementToModificationsList(presName, slideNumber, selector, 'modify', callback); return this; } /** * Select, insert and (optionally) modify a single element to a slide. * @param {string} presName - Filename or alias name of the template presentation. * Must have been importet with Automizer.load(). * @param {number} slideNumber - Slide number within the specified template to search for the required element. * @param {ShapeModificationCallback | ShapeModificationCallback[]} callback - One or more callback functions to apply. * Depending on the shape type (e.g. chart or table), different arguments will be passed to the callback. */ addElement(presName, slideNumber, selector, callback) { this.addElementToModificationsList(presName, slideNumber, selector, 'append', callback); return this; } /** * Remove a single element from slide. * @param {string} selector - Element's name on the slide. */ removeElement(selector) { const presName = this.sourceTemplate.name; const slideNumber = this.sourceNumber; this.addElementToModificationsList(presName, slideNumber, selector, 'remove', undefined); return this; } /** * Adds element to modifications list * @internal * @param presName * @param slideNumber * @param selector * @param mode * @param [callback] * @returns element to modifications list */ addElementToModificationsList(presName, slideNumber, selector, mode, callback) { this.importElements.push({ presName, slideNumber, selector, mode, callback, }); } /** * ToDo: Implement creationIds as well for slideMasters * * Try to convert a given slide's creationId to corresponding slide number. * Used if automizer is run with useCreationIds: true * @internal * @param PresTemplate * @slideNumber SourceSlideIdentifier * @returns number */ getSlideNumber(template, slideIdentifier) { if (template.useCreationIds === true && template.creationIds !== undefined) { const matchCreationId = template.creationIds.find((slideInfo) => slideInfo.id === Number(slideIdentifier)); if (matchCreationId) { return matchCreationId.number; } throw ('Could not find slide number for creationId: ' + slideIdentifier + '@' + template.name); } return slideIdentifier; } /** * Imported selected elements * @internal * @returns selected elements */ importedSelectedElements() { return __awaiter(this, void 0, void 0, function* () { for (const element of this.importElements) { const info = yield this.getElementInfo(element); switch (info === null || info === void 0 ? void 0 : info.type) { case element_type_1.ElementType.Chart: yield new chart_1.Chart(info, this.targetType)[info.mode](this.targetTemplate, this.targetNumber, this.targetType); break; case element_type_1.ElementType.Image: yield new image_1.Image(info, this.targetType)[info.mode](this.targetTemplate, this.targetNumber, this.targetType); break; case element_type_1.ElementType.Shape: yield new generic_1.GenericShape(info, this.targetType)[info.mode](this.targetTemplate, this.targetNumber, this.targetType); break; default: break; } } }); } /** * Gets element info * @internal * @param importElement * @returns element info */ getElementInfo(importElement) { return __awaiter(this, void 0, void 0, function* () { const template = this.root.getTemplate(importElement.presName); const slideNumber = importElement.mode === 'append' ? this.getSlideNumber(template, importElement.slideNumber) : importElement.slideNumber; let currentMode = 'slideToSlide'; if (this.targetType === 'slideMaster') { if (importElement.mode === 'append') { currentMode = 'slideToMaster'; } else { currentMode = 'onMaster'; } } // It is possible to import shapes from loaded slides to slideMaster, // as well as to modify an existing shape on current slideMaster const sourcePath = currentMode === 'onMaster' ? `ppt/slideMasters/slideMaster${slideNumber}.xml` : `ppt/slides/slide${slideNumber}.xml`; const sourceRelPath = currentMode === 'onMaster' ? `ppt/slideMasters/_rels/slideMaster${slideNumber}.xml.rels` : `ppt/slides/_rels/slide${slideNumber}.xml.rels`; const sourceArchive = yield template.archive; const useCreationIds = template.useCreationIds === true && template.creationIds !== undefined; const { sourceElement, selector, mode } = yield this.findElementOnSlide(importElement.selector, sourceArchive, sourcePath, useCreationIds); if (!sourceElement) { console.error(`Can't find element on slide ${slideNumber} in ${importElement.presName}: `); console.log(importElement); return; } const appendElementParams = yield this.analyzeElement(sourceElement, sourceArchive, sourceRelPath); return { mode: importElement.mode, name: selector, hasCreationId: mode === 'findByElementCreationId', sourceArchive, sourceSlideNumber: slideNumber, sourceElement, callback: importElement.callback, target: appendElementParams.target, type: appendElementParams.type, }; }); } /** * @param selector * @param sourceArchive * @param sourcePath * @param useCreationIds */ findElementOnSlide(selector, sourceArchive, sourcePath, useCreationIds) { return __awaiter(this, void 0, void 0, function* () { const strategies = []; if (typeof selector === 'string') { if (useCreationIds) { strategies.push({ mode: 'findByElementCreationId', selector: selector, }); } strategies.push({ mode: 'findByElementName', selector: selector, }); } else if (selector.name) { strategies.push({ mode: 'findByElementCreationId', selector: selector.creationId, }); strategies.push({ mode: 'findByElementName', selector: selector.name, }); } for (const findElement of strategies) { const mode = findElement.mode; const sourceElement = yield xml_helper_1.XmlHelper[mode](sourceArchive, sourcePath, findElement.selector); if (sourceElement) { return { sourceElement, selector: findElement.selector, mode }; } } return { sourceElement: undefined, selector: JSON.stringify(selector) }; }); } checkIntegrity(info, assert) { return __awaiter(this, void 0, void 0, function* () { if (info || assert) { const masterRels = (yield new xml_relationship_helper_1.XmlRelationshipHelper().initialize(this.targetArchive, `${this.targetType}${this.targetNumber}.xml.rels`, `ppt/${this.targetType}s/_rels`)); yield masterRels.assertRelatedContent(this.sourceArchive, info, assert); } }); } /** * Adds slide to presentation * @internal * @returns slide to presentation */ addToPresentation() { return __awaiter(this, void 0, void 0, function* () { const relId = yield xml_helper_1.XmlHelper.getNextRelId(this.targetArchive, 'ppt/_rels/presentation.xml.rels'); yield this.appendToSlideRel(this.targetArchive, relId, this.targetNumber); if (this.targetType === 'slide') { yield this.appendToSlideList(this.targetArchive, relId); } else if (this.targetType === 'slideMaster') { yield this.appendToSlideMasterList(this.targetArchive, relId); } else if (this.targetType === 'slideLayout') { // No changes to ppt/presentation.xml required for slideLayouts } yield this.appendToContentType(this.targetArchive, this.targetNumber); }); } /** * Appends to slide rel * @internal * @param rootArchive * @param relId * @param slideCount * @returns to slide rel */ appendToSlideRel(rootArchive, relId, slideCount) { return xml_helper_1.XmlHelper.append({ archive: rootArchive, file: `ppt/_rels/presentation.xml.rels`, parent: (xml) => xml.getElementsByTagName('Relationships')[0], tag: 'Relationship', attributes: { Id: relId, Type: `http://schemas.openxmlformats.org/officeDocument/2006/relationships/${this.targetType}`, Target: `${this.targetType}s/${this.targetType}${slideCount}.xml`, }, }); } /** * Appends a new slide to slide list in presentation.xml. * If rootArchive has no slides, a new node will be created. * "id"-attribute of 'p:sldId'-element must be greater than 255. * @internal * @param rootArchive * @param relId * @returns to slide list */ appendToSlideList(rootArchive, relId) { return xml_helper_1.XmlHelper.append({ archive: rootArchive, file: `ppt/presentation.xml`, assert: (xml) => __awaiter(this, void 0, void 0, function* () { if (xml.getElementsByTagName('p:sldIdLst').length === 0) { xml_helper_1.XmlHelper.insertAfter(xml.createElement('p:sldIdLst'), xml.getElementsByTagName('p:sldMasterIdLst')[0]); } }), parent: (xml) => xml.getElementsByTagName('p:sldIdLst')[0], tag: 'p:sldId', attributes: { 'r:id': relId, }, }); } /** * Appends a new slide to slide list in presentation.xml. * If rootArchive has no slides, a new node will be created. * "id"-attribute of 'p:sldId'-element must be greater than 255. * @internal * @param rootArchive * @param relId * @returns to slide list */ appendToSlideMasterList(rootArchive, relId) { return xml_helper_1.XmlHelper.append({ archive: rootArchive, file: `ppt/presentation.xml`, parent: (xml) => xml.getElementsByTagName('p:sldMasterIdLst')[0], tag: 'p:sldMasterId', attributes: { 'r:id': relId, }, }); } /** * Appends slide to content type * @internal * @param rootArchive * @param slideCount * @returns slide to content type */ appendToContentType(rootArchive, count) { return xml_helper_1.XmlHelper.append(xml_helper_1.XmlHelper.createContentTypeChild(rootArchive, { PartName: `/ppt/${this.targetType}s/${this.targetType}${count}.xml`, ContentType: `application/vnd.openxmlformats-officedocument.presentationml.${this.targetType}+xml`, })); } /** * slideNote numbers differ from slide numbers if presentation * contains slides without notes. We need to find out * the proper enumeration of slideNote xml files. * @internal * @returns slide note file number */ getSlideNoteSourceNumber() { return __awaiter(this, void 0, void 0, function* () { const targets = yield xml_helper_1.XmlHelper.getTargetsByRelationshipType(this.sourceArchive, `ppt/slides/_rels/slide${this.sourceNumber}.xml.rels`, 'http://schemas.openxmlformats.org/officeDocument/2006/relationships/notesSlide'); if (targets.length) { const targetNumber = targets[0].file .replace('../notesSlides/notesSlide', '') .replace('.xml', ''); return Number(targetNumber); } }); } /** * Copys slide note files * @internal * @returns slide note files */ copySlideNoteFiles(sourceNotesNumber) { return __awaiter(this, void 0, void 0, function* () { yield file_helper_1.FileHelper.zipCopy(this.sourceArchive, `ppt/notesSlides/notesSlide${sourceNotesNumber}.xml`, this.targetArchive, `ppt/notesSlides/notesSlide${this.targetNumber}.xml`); yield file_helper_1.FileHelper.zipCopy(this.sourceArchive, `ppt/notesSlides/_rels/notesSlide${sourceNotesNumber}.xml.rels`, this.targetArchive, `ppt/notesSlides/_rels/notesSlide${this.targetNumber}.xml.rels`); }); } /** * Updates slide note file * @internal * @returns slide note file */ updateSlideNoteFile(sourceNotesNumber) { return __awaiter(this, void 0, void 0, function* () { yield xml_helper_1.XmlHelper.replaceAttribute(this.targetArchive, `ppt/notesSlides/_rels/notesSlide${this.targetNumber}.xml.rels`, 'Relationship', 'Target', `../slides/slide${this.sourceNumber}.xml`, `../slides/slide${this.targetNumber}.xml`); yield xml_helper_1.XmlHelper.replaceAttribute(this.targetArchive, `ppt/slides/_rels/slide${this.targetNumber}.xml.rels`, 'Relationship', 'Target', `../notesSlides/notesSlide${sourceNotesNumber}.xml`, `../notesSlides/notesSlide${this.targetNumber}.xml`); }); } /** * Appends notes to content type * @internal * @param rootArchive * @param slideCount * @returns notes to content type */ appendNotesToContentType(rootArchive, slideCount) { return xml_helper_1.XmlHelper.append(xml_helper_1.XmlHelper.createContentTypeChild(rootArchive, { PartName: `/ppt/notesSlides/notesSlide${slideCount}.xml`, ContentType: `application/vnd.openxmlformats-officedocument.presentationml.notesSlide+xml`, })); } /** * Copys related content * @internal * @returns related content */ copyRelatedContent() { return __awaiter(this, void 0, void 0, function* () { const charts = yield chart_1.Chart.getAllOnSlide(this.sourceArchive, this.relsPath); for (const chart of charts) { yield new chart_1.Chart({ mode: 'append', target: chart, sourceArchive: this.sourceArchive, sourceSlideNumber: this.sourceNumber, }, this.targetType).modifyOnAddedSlide(this.targetTemplate, this.targetNumber); } const images = yield image_1.Image.getAllOnSlide(this.sourceArchive, this.relsPath); for (const image of images) { yield new image_1.Image({ mode: 'append', target: image, sourceArchive: this.sourceArchive, sourceSlideNumber: this.sourceNumber, }, this.targetType).modifyOnAddedSlide(this.targetTemplate, this.targetNumber); } }); } /** * Analyzes element * @internal * @param sourceElement * @param sourceArchive * @param slideNumber * @returns element */ analyzeElement(sourceElement, sourceArchive, relsPath) { return __awaiter(this, void 0, void 0, function* () { const isChart = sourceElement.getElementsByTagName('c:chart'); if (isChart.length) { const target = yield xml_helper_1.XmlHelper.getTargetByRelId(sourceArchive, relsPath, sourceElement, 'chart'); return { type: element_type_1.ElementType.Chart, target: target, }; } const isChartEx = sourceElement.getElementsByTagName('cx:chart'); if (isChartEx.length) { const target = yield xml_helper_1.XmlHelper.getTargetByRelId(sourceArchive, relsPath, sourceElement, 'chartEx'); return { type: element_type_1.ElementType.Chart, target: target, }; } const isImage = sourceElement.getElementsByTagName('p:nvPicPr'); if (isImage.length) { return { type: element_type_1.ElementType.Image, target: yield xml_helper_1.XmlHelper.getTargetByRelId(sourceArchive, relsPath, sourceElement, 'image'), }; } return { type: element_type_1.ElementType.Shape, }; }); } /** * Applys modifications * @internal * @returns modifications */ applyModifications() { return __awaiter(this, void 0, void 0, function* () { for (const modification of this.modifications) { const xml = yield xml_helper_1.XmlHelper.getXmlFromArchive(this.targetArchive, this.targetPath); modification(xml); xml_helper_1.XmlHelper.writeXmlToArchive(this.targetArchive, this.targetPath, xml); } }); } /** * Apply modifications to slide relations * @internal * @returns modifications */ applyRelModifications() { return __awaiter(this, void 0, void 0, function* () { yield xml_helper_1.XmlHelper.modifyXmlInArchive(this.targetArchive, `ppt/${this.targetType}s/_rels/${this.targetType}${this.targetNumber}.xml.rels`, this.relModifications); }); } /** * Removes all unsupported tags from slide xml. * E.g. added relations & tags by Thinkcell cannot * be processed by pptx-automizer at the moment. * @internal */ cleanSlide(targetPath) { return __awaiter(this, void 0, void 0, function* () { const xml = yield xml_helper_1.XmlHelper.getXmlFromArchive(this.targetArchive, targetPath); this.unsupportedTags.forEach((tag) => { const drop = xml.getElementsByTagName(tag); const length = drop.length; if (length && length > 0) { xml_helper_1.XmlHelper.sliceCollection(drop, 0); } }); xml_helper_1.XmlHelper.writeXmlToArchive(this.targetArchive, targetPath, xml); }); } /** * Removes all unsupported relations from _rels xml. * @internal */ cleanRelations(targetRelsPath) { return __awaiter(this, void 0, void 0, function* () { yield xml_helper_1.XmlHelper.removeIf({ archive: this.targetArchive, file: targetRelsPath, tag: 'Relationship', clause: (xml, item) => { return this.unsupportedRelationTypes.includes(item.getAttribute('Type')); }, }); }); } } exports.default = HasShapes; //# sourceMappingURL=has-shapes.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