heavysixer / node-pptx

Generate PPTX files on the server-side with JavaScript.
MIT License
164 stars 46 forks source link
node-pptx powerpoint powerpoint-generation powerpoint-javascript-library pptx pptx-files

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