Coder Social home page Coder Social logo

heavysixer / node-pptx Goto Github PK

View Code? Open in Web Editor NEW
145.0 6.0 43.0 28.06 MB

Generate PPTX files on the server-side with JavaScript.

License: MIT License

JavaScript 100.00%
node-pptx pptx-files pptx powerpoint powerpoint-javascript-library powerpoint-generation

node-pptx's Introduction

node-pptx

A well-tested, and friendly library to create, edit, and update PPTX files on the server-side with JavaScript.

Features

Getting Started

NOTE: Someone else registered the node-pptx npm package out from under us. Use this package instead:

$ npm i nodejs-pptx

Let's create a very simple presentation with one slide.

const PPTX = require('nodejs-pptx');
let pptx = new PPTX.Composer();

await pptx.compose(pres => {
  pres.addSlide(slide => {
    slide.addText(text => {
      text.value('Hello World');
    });
  });
});

await pptx.save(`./hello-world.pptx`);

General Conventions

nodejs-pptx has a friendly declarative DSL to quickly design a pptx file. This makes your JavaScript code very readable because, it allows you to visually segment and compartmentalize your code to the presentation element you are trying to edit. Here is a simple example of adding a text box to a slide:

await pres.addSlide(slide => {
  // declarative way of adding an object
  slide.addText(text => {
    text
      .value('Hello World!')
      .x(100)
      .y(50)
      .fontFace('Alien Encounters')
      .fontSize(20)
      .textColor('CC0000')
      .textWrap('none')
      .textAlign('left')
      .textVerticalAlign('center')
      .line({ color: '0000FF', dashType: 'dash', width: 1.0 })
      .margin(0);
  });
});

You can also achieve the same result using the more terse object-only format by supplying a configuration object instead of a function.

Note: Not all presentation elements support the object-only format.

slide.addText({ value: 'Link to google.com', x: 100, y: 50, href: 'http://www.google.com' });

Presentations

The following sections defines the various ways to read, compose, and write pptx files. node-pptx allows you to either create a brand new file, or modify an existing pptx file.

Creating a Presentation From Scratch

const PPTX = require('nodejs-pptx');
let pptx = new PPTX.Composer();

await pptx.compose(pres => {
  pres.addSlide(slide => {
    slide.addText(text => {
      text.value('Hello World');
    });
  });
});

await pptx.save(`./hello-world.pptx`);

Modifying an existing Presentation

If you would like to use or modify an existing pptx file, simply load it first.

const PPTX = require('nodejs-pptx');
let pptx = new PPTX.Composer();

await pptx.load(`./existing.pptx`);
await pptx.compose(async pres => {
  await pres.getSlide('slide1').addImage(image => {
    image
      .file(`./images/pizza.jpg`)
      .x(500)
      .y(100)
      .cx(166)
      .cy(100);
  });
});
await pptx.save(`./existing.pptx`);

Saving A Presentation

Saving a presentation is easy to do but because it's asynchronous by design we recommend awaiting the result before moving on.

const PPTX = require('nodejs-pptx');
let pptx = new PPTX.Composer();

await pptx.save(`./blank.pptx`);

Setting Properties

You can set the presentation's properties using the DSL only.

const PPTX = require('nodejs-pptx');
let pptx = new PPTX.Composer();

await pptx.compose(async pres => {
  pres
    .title('My Presentation')
    .author('Mark Daggett')
    .company('Humansized Inc.')
    .revision('20')
    .subject('My Presentation');
});

Setting Layouts

To control the layout of a presentation you choose one of the available defaults. To see a list of provided layouts view the layouts file.

const PPTX = require('nodejs-pptx');
let pptx = new PPTX.Composer();

pptx.compose(pres => {
  pres.layout('LAYOUT_4x3');
});

Setting Text Direction

TODO

Slides

Slides are are by far the most complex feature of this library because they are the backbone for all presentations.

Adding Slides

const PPTX = require('nodejs-pptx');
let pptx = new PPTX.Composer();

await pptx.compose(pres => {
  pres.addSlide(slide => {
    slide.addText(text => {
      text.value('Hello World');
    });
  });
});

Removing Slides

Slides are removed by calling removeSlide() on the Presentation object and passing in the object of the slide you want removed. In order to get a slide object, call Presentation.getSlide() passing in the name of the slide you wish to retrieve. Slide names always follow the format of "slideX" where "X" is the slide number. For example, slide #3 will be named "slide3" and slide #10 will be named "slide10."

Presentation.getSlide() also supports integer slide numbers (slide numbers are base-1). For example, to grab the very first slide of a PPTX, you would call "getSlide(1)".

When calling Presentation.addSlide() without a composition function as the first argument, a slide object will be returned in the promise. This slide object can also be used as a reference for slide removal.

Examples of both:

const PPTX = require('nodejs-pptx');
let pptx = new PPTX.Composer();

await pptx.load(`./existing.pptx`); // load a pre-existing PPTX
await pptx.compose(async pres => {
  pres.removeSlide(pres.getSlide('slide1')); // remove the first slide from the PPTX
  // OR ---> pres.removeSlide(pres.getSlide(1)); <--- example of getting a slide by integer

  let newSlide = await pres.addSlide(); // add a new slide

  newSlide.addText(text => {
    // add some text
    text.value('Hello World');
  });

  pres.removeSlide(newSlide); // remove the slide we just added using the object reference
});

Reordering Slides

You can move a slide's position by calling moveTo() on a Slide object. See the section above ("Removing Slides") for how to grab a Slide object. The moveTo() function takes one parameter: the destination slide number in which you want the slide to move. Slide numbers are always base-1. For example, to move a slide from its original position to the second slide in the presentation, you would call "moveTo(2)".

Example #1 (to move slide #5 to slide #2 on an existing PPTX):

const PPTX = require('nodejs-pptx');
let pptx = new PPTX.Composer();

await pptx.load(`./existing.pptx`); // load a pre-existing PPTX
await pptx.compose(async pres => {
  let slide = pres.getSlide(5);
  slide.moveTo(2);
});

Example #2 (to move slide #2 to slide #6 on a PPTX created from scratch):

const PPTX = require('nodejs-pptx');
let pptx = new PPTX.Composer();

await pptx.compose(async pres => {
  let slide1 = await pres.addSlide();
  let slide2 = await pres.addSlide();
  let slide3 = await pres.addSlide();
  let slide4 = await pres.addSlide();
  let slide5 = await pres.addSlide();
  let slide6 = await pres.addSlide();

  slide1.addText({ value: 'Slide 1', x: 200, y: 100 });
  slide2.addText({ value: 'Slide 2', x: 200, y: 100 });
  slide3.addText({ value: 'Slide 3', x: 200, y: 100 });
  slide4.addText({ value: 'Slide 4', x: 200, y: 100 });
  slide5.addText({ value: 'Slide 5', x: 200, y: 100 });
  slide6.addText({ value: 'Slide 6', x: 200, y: 100 });

  slide2.moveTo(6);
});

Formatting Options

Applying background and text colors:

const PPTX = require('nodejs-pptx');
let pptx = new PPTX.Composer();

await pptx.compose(async pres => {
  await pres.addSlide(slide => {
    slide.textColor('00AAFF');
    slide.backgroundColor('FFD777');
    slide.addText(text => {
      text.value('Hello World!');
    });
  });
});
await pptx.save('./colors.pptx');

Applying Master Slides

TODO

Adding Slide Numbers

TODO

Adding Content to Slides

This library supports charts, images, text boxes, and shapes. The following section describes the ways in which you can add these elements. to a slide.

Objects are layered on top of one another in the order in which they are added. Therefore you'll want to add background items first and gradually work your way towards the top of the composition.

Charts

Charts have very minimal support right now, think of it mostly as a proof of concept at this point.

Bar Charts

const PPTX = require('nodejs-pptx');
let pptx = new PPTX.Composer();

let barChartData1 = [
  {
    name: 'Series 1',
    labels: ['Category 1', 'Category 2', 'Category 3', 'Category 4'],
    values: [4.3, 2.5, 3.5, 4.5],
  },
  {
    name: 'Series 2',
    labels: ['Category 1', 'Category 2', 'Category 3', 'Category 4'],
    values: [2.4, 4.4, 1.8, 2.8],
  },
  {
    name: 'Series 3',
    labels: ['Category 1', 'Category 2', 'Category 3', 'Category 4'],
    values: [2.0, 2.0, 3.0, 5.0],
  },
];

await pptx
  .compose(async pres => {
    await pres.layout('LAYOUT_4x3').addSlide(async slide => {
      await slide.addChart(chart => {
        chart
          .type('bar')
          .data(barChartData1)
          .x(100)
          .y(100)
          .cx(400)
          .cy(300);
      });
    });
  })
  .save('./chart.pptx');

Images

const PPTX = require('nodejs-pptx');
let pptx = new PPTX.Composer();

await pptx.compose(async pres => {
  await pres.addSlide(async slide => {
    // Images can be added locally
    slide.addImage(image => {
      image
        .file('./images/pizza.jpg')
        .x(100)
        .cx(200);
    });

    // Images can be downloaded from the internet.
    await slide.addImage({
      src: 'https://www.google.com/images/branding/googlelogo/2x/googlelogo_color_120x44dp.png',
      href: 'https://www.google.com',
      x: 10,
      y: 400,
      cx: 50,
    });

    // Images can be added inline as a base64 encoded string.
    slide.addImage(image => {
      image
        .data('iVBORw0KGgoA[...]Jggg')
        .x(350)
        .y(200);
    });
  });
});

Media Objects

The pptx spec calls for support of media objects (video & audio) however presently node-pptx doesn't support these objects.

Shapes

For a full list of the supported shapes check the shape-types file.

const PPTX = require('nodejs-pptx');
let pptx = new PPTX.Composer();

await pptx.compose(async pres => {
  await pres.addSlide(slide => {
    //Creating a shape using the DSL.
    slide.addShape(shape => {
      shape
        .type(PPTX.ShapeTypes.TRIANGLE)
        .x(50)
        .y(50)
        .cx(50)
        .cy(50);
    });

    //Creating a shape using the object-only syntax.
    slide.addShape({ type: PPTX.ShapeTypes.TRIANGLE, x: 150, y: 50, cx: 50, cy: 50, color: '00FF00' });

    // Adding a hyperlink to the shape.
    slide.addShape({ type: PPTX.ShapeTypes.UP_ARROW, x: 500, y: 140, cx: 100, cy: 50, color: '0000FF', href: 'www.google.com' });
  });
});

await pptx.save(`./shapes-test.pptx`);

Text Boxes

As the name suggests text can be added to the slide using addText. The text box element also supports the creation of external links (which open a web browser) and internal linking (which link to another slide in the same presentation).

const PPTX = require('nodejs-pptx');
let pptx = new PPTX.Composer();

await pptx.compose(async pres => {
  await pres.addSlide(slide => {
    // declarative way of adding an object
    slide.addText(text => {
      text
        .value('Hello World!')
        .x(100)
        .y(50)
        .fontFace('Alien Encounters')
        .fontSize(20)
        .textColor('CC0000')
        .textWrap('none')
        .textAlign('left')
        .textVerticalAlign('center')
        .line({ color: '0000FF', dashType: 'dash', width: 1.0 })
        .margin(0);
    });

    // plain "config" method of adding an object
    slide.addText({ value: 'Link to google.com', x: 200, y: 300, href: 'http://www.google.com' });
  });
});

await pptx.save(`./text-box-new-simple.pptx`);

To create an external link specify the full URI path as the value for the url key.

defaultSlide.addText({ value: 'This is a hyperlink!', x: 0, y: 25, cx: 400, href: 'http://www.google.com' });

To link to another slide specify the slide number preceded with a hash like so:

defaultSlide.addText({ value: 'This go to slide 3', x: 0, y: 50, href: '#3' });

Understanding Async and Await

nodejs-pptx is synchronous by design, which means commands execute one step at a time and do not move on until the previous step completes. However, there are certain cases where synchronous flow is undesirable. For example when adding an image from the internet which must first be downloaded. In these cases you can use Javascript's native async and await functions to turn the previous synchronous flow into one that allows for asynchronous operations. Consider these two examples:

// Synchronous example:
pptx.compose(pres => {
  pres.addSlide(slide => {
    slide.addImage(image => {
      image
        .file(`${__dirname}/images/pizza.jpg`)
        .x(100)
        .cx(200);
    });
  });
});

// Asynchronous example

// We must now specify that the `pres` object is async.
await pptx.compose(async pres => {
  // we must specify that the slide object is also async.
  await pres.addSlide(async slide => {
    // because our image is located on a server we must await it's download.
    await slide.addImage({
      src: 'https://www.mcdonalds.com/content/dam/usa/logo/m_logo.png',
      href: 'https://www.mcdonalds.com',
      x: 10,
      y: 400,
      cx: 50,
    });
  });
});

Testing

To run the unit tests for nodejs-pptx simply type this into your command line:

yarn test

You can also see the code coverage metrics as well which will be written to the coverage/ directory.

Contributing

Like many other open source projects nodejs-pptx was created to service the kinds of use cases we needed to support for our own work. This means that the project itself is far from feature-complete. Send issues and pull requests with your ideas!

Good First Issue is a great starting point for PRs.

Special Thanks

This project builds off the hard work of several other PPTX libraries namely:

License

MIT © Mark Daggett

node-pptx's People

Contributors

cea2k avatar dependabot[bot] avatar eliteuser avatar gregdolley avatar heavysixer avatar jboler avatar kobusan avatar luntzel avatar tadeubdev avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar

node-pptx's Issues

Find/Replace text, Get list of slides containing some text

I have a presentation with a lot of variable placeholders that look like this: {some_variable}

I'd like to find those and replace them with the proper values (from a JSON file). I'm doing this now with docxtemplater, but I'd rather use node-pptx.

In addition, I'd like to search the presentation for the remaining instances of a variable that were not filled in - this is an indication that there's no data for that slide and it should be removed. I'd like to search for, say, "{remove_slide}" and remove that slide. I could do it with a list of slides containing that variable, then I'd sort it in descending order, then use node-pptx to delete the slides. Sorting the list descending seems like the proper path so you don't screw up the slide ordering while you're deleting slides. Maybe there's a simpler way.

issue

Hi! 👋

Firstly, thanks for your work on this project! 🙂

Today I used patch-package to patch [email protected] for the project I'm working on.

Here is the diff that solved my problem:

diff --git a/node_modules/nodejs-pptx/lib/factories/index.js b/node_modules/nodejs-pptx/lib/factories/index.js
index 9d96aa5..3932925 100644
--- a/node_modules/nodejs-pptx/lib/factories/index.js
+++ b/node_modules/nodejs-pptx/lib/factories/index.js
@@ -39,7 +39,7 @@ class PowerPointFactory {
         await zip.loadAsync(data);
 
         for (let key in zip.files) {
-            if (zip.files.hasOwnProperty(key)) {
+            if (Object.prototype.hasOwnProperty.call(zip.files, key)) {
                 let ext = key.substr(key.lastIndexOf('.'));
 
                 if (ext === '.xml' || ext === '.rels') {

This issue body was partially generated by patch-package.

Refactor this name

Image is already a defined class within JavaScript (at least in the browser environment.) To prevent confusion I think we should refactor this name like we did the Text class.

Additionally, the addImage and addSlide methods are confusing in terms of their responsibilities. I'd assume that if you created an Image you would not call addImage on an image...

class Image extends ElementProperties {

Extract presenter notes?

How can one extract just the presenter notes?
The solution which I'm using is this:

// const fs = require("fs");
const PPTX2Json = require("pptx2json");
const pptx2json = new PPTX2Json();

pptx2json.toJson("./test.pptx").then((json) => {
  json = Object.keys(json)
    .filter((key) => key.includes("ppt/notesSlides/notesSlide"))
    .sort((a, b) => a - b)
    .map((key) =>
      json[key]["p:notes"]["p:cSld"][0]["p:spTree"][0]["p:sp"]
        .filter((item) => item["p:txBody"])
        .map((item) => item["p:txBody"][0]["a:p"])
        .map((item) => item.map((item) => item["a:r"]))[0]
        .filter((x) => x)
        .map((item) => [
          ...item.reduce((prev, curr) => [...prev, curr["a:t"]], []),
        ])
        .join(" ")
    );
  console.log(json);
  // fs.writeFileSync("./data.txt", JSON.stringify(json));
});

Is it possible to edit an existing Text Box?

I have a template pptx that I need to generate copies of by swapping data in and out of it. Is it possible to reference a specific text box? Even a plain getElements() would be great! Any insight on this?

Thank you, the package is awesome!

Copying slides

Is there a way to copy an existing slide into a new slide?

Something like:

pres.copySlide(pres.getSlide(3))

Problems calling the library front next.js

Hello - I'm running into this error when the node function is being called from a next.js runtime. My file/function works fine when calling directly from node, but it appears to fail to load based on a file path:

  • error unhandledRejection: Error: ENOENT: no such file or directory, open '/Users/username/dev/app/.next/server/app/api/endpoint/../fragments/[Content_Types].xml'

It seems like the next.js runtime is a different path than the node_modules or the Content_Types.xml. I have tried putting the fragments directory in public and fetching the file, i have tried absolute paths, I'm not sure why it's not working when being called by the API. I found the reference in this file which could be causing the issue:

node-pptx/fragments/[Content_Types].xml

    build() {
        xml2js.parseString(fs.readFileSync(`${__dirname}/../fragments/[Content_Types].xml`), (err, js) => {
            this.content['[Content_Types].xml'] = js;
        });

        this.addDefaultMediaContentTypes();
    }

Would you accept a PR to allow loading pptx from buffer OR file path?

Issue with loading PowerPoint file from buffer

The save() method allows you to specify either a callback (passing buffer) or file path, but when loading a PowerPoint file, you must pass a file path. This can be pretty inconvenient in cases where you already have the PowerPoint file contents in a buffer.

Proposed solution

To address this issue, can we please add support for loading from a buffer as well as a file path.

I've quickly scanned your code, and at first glance it looks like it's as simple as changing loadExistingPPTX() :

async loadExistingPPTX(done) {
if (this.templateFilePath !== null) {
await this.powerPointFactory.loadFromRawFileData(fs.readFileSync(this.templateFilePath));
}
}

To something like:

async loadExistingPPTX(done) {
    // Throw an error if templateFilePath is null or undefined
    if (!this.templateFilePath) {
        throw new Error('Input is null or undefined. Input must be a file path or buffer.');
    }

    try {
        // Check if templateFilePath is a string (file path)
        if (typeof this.templateFilePath === 'string') {
            // Load PowerPoint file from the specified file path using fs.readFileSync()
            await this.powerPointFactory.loadFromRawFileData(fs.readFileSync(this.templateFilePath));
        }
        // Check if templateFilePath is a buffer
        else if (Buffer.isBuffer(this.templateFilePath)) {
            // Load PowerPoint file from the buffer directly using powerPointFactory.loadFromRawFileData()
            await this.powerPointFactory.loadFromRawFileData(this.templateFilePath);
        }
        // If templateFilePath is neither a string nor a buffer, throw an error
        else {
            throw new Error('Invalid input type. Input must be a file path or buffer.');
        }
    } catch (error) {
        // Catch any errors that occur during loading and rethrow with a more descriptive message
        throw new Error(`Error loading PPTX file: ${error.message}`);
    }
}

Obviously we'd also need to change the variable name to templateFile or similar and any other housekeeping as needed.

Would you accept a PR from me along these lines to add this feature?

Update data of existing chart

I've got an existing set of slides which have a couple of charts and a couple of tables on them. I'd like to be able to update the Excel data which sits behind the charts, is that possible? I couldn't see a getChart function.

I'd like to update the data in the tables too. The tables aren't Excel as far as I know, they're just PowerPoint's 'Insert Table' tables. I don't mind tidying the slide up manually if there gets to be too many rows, but I'd like to be able to start with what I've created within PowerPoint, rather than having to recreate it all from scratch.

Textbox height/width options?

I notice textboxes all come in the same size, can I set the height and width as needed? I see the constructor for Textbox has cx and cy but no props to override it?

Bug when trying to load existing présentation

I'm trying to load exisiting powerpoint and add some slide to him.
But when I save it, I get this stacktrace:

(node:10568) UnhandledPromiseRejectionWarning: TypeError: Cannot read property 'Relationships' of undefined at PptRelsFactory.addPresentationToSlideRelationship (D:\development\sprint-reviewer\node_modules\nodejs-pptx\lib\factories\ppt\rels.js:24:56) at PptFactory.addSlide (D:\development\sprint-reviewer\node_modules\nodejs-pptx\lib\factories\ppt\index.js:64:80) at PowerPointFactory.addSlide (D:\development\sprint-reviewer\node_modules\nodejs-pptx\lib\factories\index.js:127:52) at Presentation.addSlide (D:\development\sprint-reviewer\node_modules\nodejs-pptx\lib\presentation.js:112:45) at D:\development\sprint-reviewer\index.js:71:18 at Composer.compose (D:\development\sprint-reviewer\node_modules\nodejs-pptx\lib\composer.js:20:15) at createPptx (D:\development\sprint-reviewer\index.js:65:10) at D:\development\sprint-reviewer\index.js:42:5 at processTicksAndRejections (internal/process/task_queues.js:85:5) (node:10568) UnhandledPromiseRejectionWarning: Unhandled promise rejection. This error originated either by throwing inside of an async function without a catch block, or by rejecting a promise which was not handled with .catch(). (rejection id: 1) (node:10568) [DEP0018] DeprecationWarning: Unhandled promise rejections are deprecated. In the future, promise rejections that are not handled will terminate the Node.js process with a non-zero exit code. (node:10568) UnhandledPromiseRejectionWarning: TypeError: Cannot read property 'Relationships' of undefined at PptRelsFactory.addPresentationToSlideRelationship (D:\development\sprint-reviewer\node_modules\nodejs-pptx\lib\factories\ppt\rels.js:24:56) at PptFactory.addSlide (D:\development\sprint-reviewer\node_modules\nodejs-pptx\lib\factories\ppt\index.js:64:80) at PowerPointFactory.addSlide (D:\development\sprint-reviewer\node_modules\nodejs-pptx\lib\factories\index.js:127:52) at Presentation.addSlide (D:\development\sprint-reviewer\node_modules\nodejs-pptx\lib\presentation.js:112:45) at D:\development\sprint-reviewer\index.js:83:14 at Composer.compose (D:\development\sprint-reviewer\node_modules\nodejs-pptx\lib\composer.js:20:15) at createPptx (D:\development\sprint-reviewer\index.js:65:10) at D:\development\sprint-reviewer\index.js:42:5 at processTicksAndRejections (internal/process/task_queues.js:85:5) (node:10568) UnhandledPromiseRejectionWarning: Unhandled promise rejection. This error originated either by throwing inside of an async function without a catch block, or by rejecting a promise which was not handled with .catch(). (rejection id: 2) (node:10568) UnhandledPromiseRejectionWarning: TypeError: Cannot read property 'Types' of undefined at ContentTypeFactory.addDefaultContentType (D:\development\sprint-reviewer\node_modules\nodejs-pptx\lib\factories\content-types.js:29:44) at ContentTypeFactory.addDefaultMediaContentTypes (D:\development\sprint-reviewer\node_modules\nodejs-pptx\lib\factories\content-types.js:21:14) at PowerPointFactory.addDefaultMediaContentTypes (D:\development\sprint-reviewer\node_modules\nodejs-pptx\lib\factories\index.js:241:33) at Presentation.save (D:\development\sprint-reviewer\node_modules\nodejs-pptx\lib\presentation.js:163:32) at Composer.save (D:\development\sprint-reviewer\node_modules\nodejs-pptx\lib\composer.js:25:33) at createPptx (D:\development\sprint-reviewer\index.js:91:10) at D:\development\sprint-reviewer\index.js:42:5 at processTicksAndRejections (internal/process/task_queues.js:85:5) (node:10568) UnhandledPromiseRejectionWarning: Unhandled promise rejection. This error originated either by throwing inside of an async function without a catch block, or by rejecting a promise which was not handled with .catch(). (rejection id: 4)

How to position text?

I'm trying to figure out how to position the text using the x and y values

await pres.getSlide('slide3')
            .addText(text => {
                text.value('val')
                    .x(10)
                    .y(50)
            })

Is there a way by which I can know exactly what values to pass for x and y? For example, by opening an existing ppt and getting the position of a similar element and then passing those values in the function.
Or do I have to do this by trial and error?

Installation Errors

I got this error while installing it.

Error: Can't find Python executable "python", you can set the PYTHON env variable.

.. I tried few things like i tried to set python env variables but no use.

Please suggest a fix.
error

Can't remove slide from presentation

I am trying to remove slide from my presentation using code

`const PPTX = require('nodejs-pptx');
let pptx = new PPTX.Composer();

async function load() {
await pptx.load('/app/go/src/ppt2svgconverter/Node-PPTX/hello-world.pptx'); // load a pre-existing PPTX
}

async function removeSlide() {
await pptx.compose(pres => {
pres.removeSlide(pres.getSlide("slide1")); // remove the first slide from the PPTX
// OR ---> pres.removeSlide(pres.getSlide(1)); <--- example of getting a slide by intege
});
}
load();
removeSlide();`

But getting error like node:10115) UnhandledPromiseRejectionWarning: Error: Invalid slide name in Presentation.getSlide(): slide1

PPT I used is attached here
hello-world.pptx

npm package not installing correctly

npm i nodejs-pptx doesn't seem to install all the correct files. Right now I see index.js, the license file, package.json, and readme.md.
Can you please check/fix this? Thank you!

Allow passing an image name when adding to Slide

Hi! 👋

Firstly, thanks for your work on this project! 🙂

Today I used patch-package to patch [email protected] for the project I'm working on.

Here is the diff that solved my problem:

diff --git a/node_modules/nodejs-pptx/lib/factories/index.js b/node_modules/nodejs-pptx/lib/factories/index.js
index 7809e88..15b7b82 100644
--- a/node_modules/nodejs-pptx/lib/factories/index.js
+++ b/node_modules/nodejs-pptx/lib/factories/index.js
@@ -199,11 +199,11 @@ class PowerPointFactory {
         }
     }
 
-    addImage(slide, image) {
-        image.setContent(this.pptFactory.addImage(slide, image));
+    addImage(slide, image, name) {
+        image.setContent(this.pptFactory.addImage(slide, image, name));
     }
 
-    async addImageFromRemoteUrl(slide, image) {
+    async addImageFromRemoteUrl(slide, image, name) {
         image.source = await new Promise(function(resolve, reject) {
             request.get(image.downloadUrl, { timeout: 30000 }, function(err, res, buffer) {
                 if (err) reject(err);
@@ -211,7 +211,7 @@ class PowerPointFactory {
             });
         });
 
-        return this.addImage(slide, image);
+        return this.addImage(slide, image, name);
     }
 
     addText(slide, textBox) {
diff --git a/node_modules/nodejs-pptx/lib/factories/ppt/index.js b/node_modules/nodejs-pptx/lib/factories/ppt/index.js
index 9652f45..4796637 100644
--- a/node_modules/nodejs-pptx/lib/factories/ppt/index.js
+++ b/node_modules/nodejs-pptx/lib/factories/ppt/index.js
@@ -74,21 +74,21 @@ class PptFactory {
         this.slideFactory.moveSlide(sourceSlideNum, destinationSlideNum);
     }
 
-    addImage(slide, image) {
+    addImage(slide, image, name = uuidv4()) {
         let mediaName = '';
         let source = image.source;
 
         if (image.sourceType === 'file') {
-            mediaName = `image-${uuidv4()}${path.extname(source)}`;
+            mediaName = `${name}${path.extname(source)}`;
         } else if (image.sourceType === 'base64') {
             let imageExt = 'png'; // assume png unless otherwise specified
 
             if (source && /image\/(\w+);/.exec(source) && /image\/(\w+);/.exec(source).length > 0) imageExt = /image\/(\w+);/.exec(source)[1];
             if (source.indexOf(';') > -1) source = source.split(';').pop();
 
-            mediaName = `image-${uuidv4()}.${imageExt}`;
+            mediaName = `${name}.${imageExt}`;
         } else if (image.sourceType === 'url') {
-            mediaName = `image-${uuidv4()}${path.extname(image.downloadUrl)}`;
+            mediaName = `${name}${path.extname(image.downloadUrl)}`;
         } else {
             throw new Error('Invalid "sourceType" specified in PptFactory.addImage(). Possible values: "base64," "file," or "binary."');
         }
diff --git a/node_modules/nodejs-pptx/lib/slide.js b/node_modules/nodejs-pptx/lib/slide.js
index b5c93f0..98870e5 100644
--- a/node_modules/nodejs-pptx/lib/slide.js
+++ b/node_modules/nodejs-pptx/lib/slide.js
@@ -56,7 +56,7 @@ class Slide {
         }
     }
 
-    async addImage(config) {
+    async addImage(config, name) {
         let image = new Image();
 
         try {
@@ -67,9 +67,9 @@ class Slide {
 
         try {
             if (image.sourceType === 'file' || image.sourceType === 'base64') {
-                this.powerPointFactory.addImage(this, image);
+                this.powerPointFactory.addImage(this, image, name);
             } else if (image.sourceType === 'url') {
-                await this.powerPointFactory.addImageFromRemoteUrl(this, image);
+                await this.powerPointFactory.addImageFromRemoteUrl(this, image, name);
             }
 
             this.elements.push(image);

This issue body was partially generated by patch-package.

deep require is no longer supported for uuid library

First of all, thank you for your very helpful library !

I ran into the following problem:
In the file node-pptx/lib/factories/ppt/index.js line 5 a deep requires is used for the uuid library :
const uuidv4 = require('uuid/v4');
According to their page this is deprecated and no longer supported: https://github.com/uuidjs/uuid#deep-requires-no-longer-supported

Therefore the following error is thrown in my project: Error [ERR_PACKAGE_PATH_NOT_EXPORTED]: Package subpath './v4' is not defined by "exports" in .../node_modules/uuid/package.json

Size not decreasing when removing the slides from a presentation

When I remove the slides from a presentation of size 10MB with 5 slides, the resulting presentation is also 10MB. Even after removing 4 of the 5 slides, its still 10MB.

const PPTX = require('node-pptx');
let pptx = new PPTX.Composer();

await pptx.load('./sample.pptx')
await pptx.compose(async pres => {
  pres.removeSlide(pres.getSlide('slide1')); 
  

});
await pptx.save('./test.pptx');

It would be nice if the size decreased with reducing slides. Please correct me if I'm making a mistake.
And is there a way to ADD slides from existing presentation to another presentation.
Something like this...

slide1=pres1.getSlide('slide1')
pres2.addSlide(slide1)

Load PPTX through Buffer

Hi!
i would like to load a pptx file from http request. I have thought that the fastest way for doing it is to load it througth buffer.
right now i'm ussing the version 1.0.1

Text over image

Hey,
I've been using the library for a week now, so far I haven't had any problems and a really good library!
I encountered the first problem when trying to add an image that will serve as a background to the text and I can't do it.
I manage to add text over text and add image over image.
But I can't add text over a picture.
I tried to create the image first and then the text and vice versa, but always the image is before the text. Am I missing something? Or is there really a problem with that?
Maybe there is another way to do it?
Many thanks in advance,

Example to my code:

slide.addText(text => {
        text
            .value("text")
            .textColor("ffffff")
            .backgroundColor("000000")
            .x(0)
            .cx(SlideSettings.headerSettings.width)
            .y(0)
            .cy(SlideSettings.headerSettings.height)
            .textVerticalAlign("center")
            .textAlign("center");
    });

  // Add background image
   slide.addImage(image => {
        image
            .data(base64_encode(`D:/projects/PowerPoint generator/Public/Images/Gradiant3.png`))
            .x(0)
            .cx(SlideSettings.headerSettings.width)
            .y(-2)
            .cy(SlideSettings.headerSettings.height + 5);
    });

    // Add tag image
    slide.addImage(image => {
        image
            .data(base64_encode('D:/projects/PowerPoint generator/Public/Images/a_tag.png'))
            .x(0)
            .cx(SlideSettings.tagSettings.width)
            .y(-2)
            .cy(SlideSettings.tagSettings.height);
    });

    // Add tag image
    slide.addImage(image => {
        image
            .data(base64_encode('D:/projects/PowerPoint generator/Public/Images/b_tag.png'))
            .x(SlideSettings.headerSettings.width - SlideSettings.tagSettings.width - 10)
            .cx(SlideSettings.tagSettings.width)
            .y(5)
            .cy(SlideSettings.tagSettings.height);
    });

Ron

How to use Bullet points?

Hi - I am new to Node JS and wanted to create a PPT with bullet points using node-pptx.
Could you please add examples for bullet points example or let me know how to do it?

TypeError: zip.files.hasOwnProperty is not a function

TypeError: zip.files.hasOwnProperty is not a function
at PowerPointFactory.loadFromRawFileData (node_modules/nodejs-pptx/lib/factories/index.js:42:27)
at runMicrotasks ()
at processTicksAndRejections (internal/process/task_queues.js:95:5)
at async Presentation.loadExistingPPTX (node_modules/nodejs-pptx/lib/presentation.js:99:13)
at async Composer.load (node_modules/nodejs-pptx/lib/composer.js:15:9)

multi lines was saved as 1 line

test code

  let pptx = new PPTX.Composer()
  await pptx.load(__dirname + '/../../templates/test.pptx')
  const tmpPPTX = '/tmp/worship' + Math.random() + '.pptx'  
  await pptx.save(tmpPPTX)

In the test.pptx, I have a slide which has two lines as its tile.

first line
second line

but the saved ppt always rendered to 1 line

first line second line

I looked into the codes, we are using
xml2js.parseString to parse xml string to js obj.
and use xml2js.Builder to save js obj back to xml string

with below code, we can easily reproduce the issue:

let xml = '<?xml version="1.0" encoding="UTF-8" standalone="yes"?><a:p><a:pPr marL="0" lvl="0" indent="0" algn="ctr" rtl="0"><a:spcBef><a:spcPts val="0"/></a:spcBef><a:spcAft><a:spcPts val="0"/></a:spcAft><a:buClr><a:schemeClr val="dk1"/></a:buClr><a:buSzPts val="1100"/><a:buFont typeface="Arial"/><a:buNone/></a:pPr><a:r><a:rPr lang="ja-JP" altLang="en-US"/><a:t>first line</a:t></a:r><a:br><a:rPr lang="en-CA" altLang="ja-JP" dirty="0"/></a:br><a:r><a:rPr lang="ja-JP" altLang="en-US"/><a:t>second line</a:t></a:r><a:endParaRPr lang="ja-JP" altLang="en-US" dirty="0"/></a:p>'

  xml2js.parseString(xml, function(err, js) {
    let builder = new xml2js.Builder({ renderOpts: { pretty: false } });
    let savexml = builder.buildObject(js);
    console.log(xml)
    console.log(savexml)
    console.log(xml === savexml)
  });

The output is:

<?xml version="1.0" encoding="UTF-8" standalone="yes"?><a:p><a:pPr marL="0" lvl="0" indent="0" algn="ctr" rtl="0"><a:spcBef><a:spcPts val="0"/></a:spcBef><a:spcAft><a:spcPts val="0"/></a:spcAft><a:buClr><a:schemeClr val="dk1"/></a:buClr><a:buSzPts val="1100"/><a:buFont typeface="Arial"/><a:buNone/></a:pPr><a:r><a:rPr lang="ja-JP" altLang="en-US"/><a:t>first line</a:t></a:r><a:br><a:rPr lang="en-CA" altLang="ja-JP" dirty="0"/></a:br><a:r><a:rPr lang="ja-JP" altLang="en-US"/><a:t>second line</a:t></a:r><a:endParaRPr lang="ja-JP" altLang="en-US" dirty="0"/></a:p>
<?xml version="1.0" encoding="UTF-8" standalone="yes"?><a:p><a:pPr marL="0" lvl="0" indent="0" algn="ctr" rtl="0"><a:spcBef><a:spcPts val="0"/></a:spcBef><a:spcAft><a:spcPts val="0"/></a:spcAft><a:buClr><a:schemeClr val="dk1"/></a:buClr><a:buSzPts val="1100"/><a:buFont typeface="Arial"/><a:buNone/></a:pPr><a:r><a:rPr lang="ja-JP" altLang="en-US"/><a:t>first line</a:t></a:r><a:r><a:rPr lang="ja-JP" altLang="en-US"/><a:t>second line</a:t></a:r><a:br><a:rPr lang="en-CA" altLang="ja-JP" dirty="0"/></a:br><a:endParaRPr lang="ja-JP" altLang="en-US" dirty="0"/></a:p>
false

As you can see in the output, the break line node <a:br> was saved after the two lines <a:r> rather than between them.
That is the reason why multi line was saved into 1 line.

Animations

What about animations? looking at powerpoint slide XML, it doesn't seem that compilcated.

Convert to Typescript

Hello! I would like to see this project support typescript. I would also love to start doing it myself. Let me know if it's ok to start a PR for this

modify slide

how can I edit text, images and other elements of a slide?

npm package not updated?

looking at the NPM packages I can see the following :

image

but github shows a latest version of 2021.

image

Can I suggest pushing out the updates to NPM, or updating the installation instructions to load the release from Github?

Trying to integrate with this from my electron app with vue

Hi all, thanks all for creating and maintaining this package.

I am trying to create an electron app that creates a pptx file via nodejs. I have added a dependency to this package and trying to run it, but whenever I try to import an existing pptx file, it throws below error [1], which seems like coming from this line [2]. I have listed below how to reproduce this error. Please let me know if there's anything I am missing or you need anything else to debug. Thank you so much!

[1]

(node:34950) UnhandledPromiseRejectionWarning: Error: ENOENT: no such file or directory, open '/Users/dongkyukim/workplace/hymn-lyrics-ppt-app/packages/electron-app/dist-electron/../fragments/[Content_Types].xml'
    at Object.openSync (node:fs:601:3)
    at Object.func [as openSync] (node:electron/js2c/asar_bundle:2:1869)
    at Object.readFileSync (node:fs:469:35)
    at Object.readFileSync (node:electron/js2c/asar_bundle:2:9212)
    at ContentTypeFactory.build (/Users/dongkyukim/workplace/hymn-lyrics-ppt-app/packages/electron-app/dist-electron/main.js:197426:31)
    at PowerPointFactory.build (/Users/dongkyukim/workplace/hymn-lyrics-ppt-app/packages/electron-app/dist-electron/main.js:204005:29)
    at new PowerPointFactory (/Users/dongkyukim/workplace/hymn-lyrics-ppt-app/packages/electron-app/dist-electron/main.js:203972:10)
    at new Presentation (/Users/dongkyukim/workplace/hymn-lyrics-ppt-app/packages/electron-app/dist-electron/main.js:204163:30)
    at new Composer (/Users/dongkyukim/workplace/hymn-lyrics-ppt-app/packages/electron-app/dist-electron/main.js:204292:25)
    at generatePPTX (/Users/dongkyukim/workplace/hymn-lyrics-ppt-app/packages/electron-app/dist-electron/main.js:205722:15)
    at /Users/dongkyukim/workplace/hymn-lyrics-ppt-app/packages/electron-app/dist-electron/main.js:205781:3
    at WebContents.<anonymous> (node:electron/js2c/browser_init:2:89468)
    at WebContents.emit (node:events:513:28)

[2]

xml2js.parseString(fs.readFileSync(`${__dirname}/../fragments/[Content_Types].xml`), (err, js) => {

How to reproduce

  1. git clone https://github.com/dongkyu9315/hymn-lyrics-ppt-app.git
  2. cd hymn-lyrics-ppt-app
  3. yarn run dev:all (this will start an electron app)
  4. Click the Trigger convert button and you will see an error

await is only valid in async function

when i folow examples , i have some issues that ' i cant use await becuz it can be used only in async function' but i saw that node-pptx is sync funtion,,,
so how can i use await like you used in examples??

Support for tables?

Guys,

Does this library support adding tables? If not, is there a way to use
https://github.com/gitbrent/PptxGenJS/
to create a slide and add it using node-pptx?

I tried something like

const PPTX = require('nodejs-pptx');
let pptx = new PPTX.Composer();
const fs = require('fs');
const pptxgenjs  =  require("pptxgenjs");
let pres = new pptxgenjs();

let slide = pres.addSlide();
slide.bkgd = '#39006A';
slide.addText('rajat');
async function init() {
    await pptx.load(`./existing.pptx`);
    await pptx.compose(async pres => {
        pres.addSlide(slide);
        fs.writeFile('helloworlds.xml',await pres.getSlide('slide1'), function (err) {
            if (err) return console.log(err);
        });
    });
    await pptx.save(`./existing1.pptx`);
}
init();

but have been unsuccessful so far.
I get UnhandledPromiseRejectionWarning: TypeError: config is not a function

can we edit existing slide element?

Screen Shot 2021-04-30 at 4 48 58 PM

i want to replace all *cross with actual value coming from REST API, can we do this?
from documentation i could see to add new element
await pres.getSlide('slide1').addImage(image => {
image
.file(./images/pizza.jpg)
.x(500)
.y(100)
.cx(166)
.cy(100);
});

Apply Master

Hey,

is there a possibility to apply a slide master?
In my case, I have an existing .pptx-file and I want to change the slide master of an added slide (considering there is more than one slide master).

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.