Features

  • Uses JavaScript Scheduler control from DayPilot Pro for JavaScript
  • Export the Scheduler to a multi-page PDF file (client-side).
  • Splits the Scheduler vertically (by rows), printing row and time headers on every page.
  • Prints a specified number of rows per page.
  • Includes a trial version of DayPilot Pro for JavaScript (see License below)
  • The PDF export uses jsPDF library (MIT license)

See also a related tutorial that shows how to split a long timeline into multiple pages horizontally:

License

Licensed for testing and evaluation purposes. Please see the license agreement included in the sample project. You can use the source code of the tutorial if you are a licensed user of DayPilot Pro for JavaScript. Buy a license.

Scheduler Configuration

html5-javascript-scheduler-paged-pdf-export-configuration.png

We will use a simple Scheduler configuration:

  • The vertical (Y) axis displays 20 resources.
  • The horizontal (X) axis displays 31 days.
  • Two row header columns (default column with the resource name + custom column with event count)
  • A sample "Reservation #1" event (from January 4 to January 8)
<div id="dp"></div>

<script>
    var dp = new DayPilot.Scheduler("dp");
    dp.scale = "Day";
    dp.timeHeaders = [
        { groupBy: "Month"},
        { groupBy: "Day", format: "d"}
    ];

    dp.startDate = new DayPilot.Date("2017-01-01");
    dp.days = 31;
    dp.resources = [
        {name: "Resource 1", id: 1},
        {name: "Resource 2", id: 2},
        {name: "Resource 3", id: 3},
        {name: "Resource 4", id: 4},
        {name: "Resource 5", id: 5},
        {name: "Resource 6", id: 6},
        {name: "Resource 7", id: 7},
        {name: "Resource 8", id: 8},
        {name: "Resource 9", id: 9},
        {name: "Resource 10", id: 10},
        {name: "Resource 11", id: 11},
        {name: "Resource 12", id: 12},
        {name: "Resource 13", id: 13},
        {name: "Resource 14", id: 14},
        {name: "Resource 15", id: 15},
        {name: "Resource 16", id: 16},
        {name: "Resource 17", id: 17},
        {name: "Resource 18", id: 18},
        {name: "Resource 19", id: 19},
        {name: "Resource 20", id: 20},
    ];
    dp.events.list = [
        {
            start: "2017-01-04T00:00:00",
            end: "2017-01-09T00:00:00",
            id: 1,
            resource: 2,
            text: "Reservation #1"
        }

    ];
    dp.rowHeaderColumns = [
        { title: "Resource"},
        { title: "Event count"},
    ];
    dp.onBeforeRowHeaderRender = function(args) {
        args.row.columns[0].html = args.row.events.all().length;
    };
    dp.init();

</script>

PDF Export (Single Page)

html5-javascript-scheduler-paged-pdf-export-single-page.png

In order to export the Scheduler as a single page we will use DayPilot.Scheduler.exportAs() method with "area" option set to "full".

The export function creates a new PDF file using jsPDF library:

var doc = new jsPDF("portrait", "in", "letter");

It prints a page header ("Scheduler"):

doc.setFontSize(40);
doc.text(0.5, 1, "Scheduler");

It exports the Scheduler as JPEG image using exportAs() method. It uses the following options:

  • It prints the complete Scheduler (area: "full").
  • It uses scale: 2 to make the exported file bigger. The default 1:1 scale might result in a blurred image when inserted into a PDF file.
  • It uses JPEG image format. This isn't optimal because JPEG uses a lossy compression and the exported image is better suited for lossless formats (straigh lines, uniform backgrounds). However, jsPDF can only insert a PNG image uncompressed (equivalent of BMP) and that would result in an extremely big PDF file.
  • It tries to compensates the lossy JPEG format by increasing the image quality to 0.95.
var image = dp.exportAs("jpeg", {
    area: "full",
    scale: 2,
    quality: 0.95
});

The dimensions of the output image are scaled down using shrink() method so the image fits the page.

var dimensions = image.dimensions();  // pixels
var maxDimensions = { width:7, height: 9};   // inches
var shrinked = shrink(dimensions, maxDimensions);

The resulting JPEG image is inserted into the PDF page:

doc.addImage(image.toDataUri(), 'JPEG', 0.5, 1.5, shrinked.width, shrinked.height);

Full sample:

<script>
  $("#download").click(function() {
      var blob = createPdfAsBlobOnePage();
      DayPilot.Util.downloadBlob(blob, "scheduler.pdf");
  });

  function createPdfAsBlobOnePage() {
      var doc = new jsPDF("portrait", "in", "letter");
      doc.setFontSize(40);
      doc.text(0.5, 1, "Scheduler");

      var image = dp.exportAs("jpeg", {
          area: "full",
          scale: 2,
          quality: 0.95
      });

      var dimensions = image.dimensions();  // pixels
      var maxDimensions = { width:7, height: 9};   // inches
      var shrinked = shrink(dimensions, maxDimensions);

      doc.addImage(image.toDataUri(), 'JPEG', 0.5, 1.5, shrinked.width, shrinked.height);

      return doc.output("blob");
  }

  function shrink(dimensions, max) {
      var widthRatio = dimensions.width / max.width;
      var heightRatio = dimensions.height / max.height;

      var ratio = Math.max(widthRatio, heightRatio);
      ratio = Math.max(ratio, 1);

      var width = dimensions.width / ratio;
      var height = dimensions.height / ratio;
      return { width: width, height: height};
  }
</script>

PDF Export (Multiple Pages)

html5-javascript-scheduler-paged-pdf-export-multiple-pages.png

It's also possible to export a specifies segment of the Scheduler using "area": "range" option.

We will use this mode to split the Scheduler vertically during export, displaying a selected number of rows per page.

The start and end row of every segment is specified using "resourceFrom" and "resourceTo" options, respectively.

<script>
  $("#download").click(function() {
      var blob = createPdfAsBlob(5);
      DayPilot.Util.downloadBlob(blob, "scheduler.pdf");
  });

  function createPdfAsBlob(rowsPerPage) {
      var doc = new jsPDF("landscape", "in", "letter")
      doc.setFontSize(40);
      doc.text(0.5, 1, "Scheduler");

      var rows = dp.rows.all();
      var pages = Math.ceil(rows.length / rowsPerPage);

      for (var i = 0; i < pages; i++) {
          var startId = i*rowsPerPage;
          var endId = i*rowsPerPage + rowsPerPage - 1;
          endId = Math.min(endId, rows.length - 1);

          var image = dp.exportAs("jpeg", {
              area: "range",
              scale: 2,
              resourceFrom: rows[startId].id,
              resourceTo: rows[endId].id,
              quality: 0.95
          });

          var dimensions = image.dimensions();  // pixels
          var maxDimensions = { width: 10, height: 6}; // inches
          var shrinked = shrink(dimensions, maxDimensions);  // inches

          doc.addImage(image.toDataUri(), 'JPEG', 0.5, 1.5, shrinked.width, shrinked.height);

          var last = (i === pages - 1);
          if (!last) {
              doc.addPage();
          }
      }

      return doc.output("blob");
  }

  function shrink(dimensions, max) {
      var widthRatio = dimensions.width / max.width;
      var heightRatio = dimensions.height / max.height;

      var ratio = Math.max(widthRatio, heightRatio);
      ratio = Math.max(ratio, 1);

      var width = dimensions.width / ratio;
      var height = dimensions.height / ratio;
      return { width: width, height: height};
  }
</script>