Overview

The JavaScript Scheduler supports export of complex event/task objects to image (PNG, JPEG, SVG) using active areas:

  • Use active areas to insert rectangle objects at a specified position (pixel position or date/time position)
  • Active areas can display images supported by the browser (PNG, JPEG, SVG format)
  • The Scheduler can display and export font icons (e.g Font Awesome)
  • Active areas support vertical and horizontal text alignment, custom background color and font color
  • Export the Scheduler view to PNG, JPEG or SVG using exportAs() method
  • Includes a trial version of DayPilot Pro for JavaScript (see also License below)

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.

Live Demo

Scheduler Image Export

The JavaScript/HTML5 Scheduler component includes built-in support for client-side image export. It exports the Scheduler view to common formats:

  • JPEG - bitmap format with lossy compression (can be used for PDF export)
  • PNG - bitmap format with lossless compression
  • SVG - vector format (ideal for printing)

However, the export feature does't simply copy the screen - each element has to be rendered individually using basic elements like lines, rectangles, and text strings. That means the image export has limited support for HTML and CSS:

  • The Scheduler tries to read the styles of individual elements (e.g. event background) and use them for export.
  • The inner HTML is interpreted as text. The <br> elements are converted to line breaks.
  • The CSS detection works on element level and it doesn't read styles that you use inside the element.

To overcome these limitations the Scheduler offers two tools:

  • onBeforeEventExport event handler lets you provide plain text instead of custom HTML and override the detected styles (similar event handlers are also available for other Scheduler elements, like grid cells and row headers)
  • event active areas let you insert custom objects at specified positions inside an event

This tutorial shows how to use the active areas to build the event content and make it exportable.

Rich Content with Active Areas

The active areas can be used to create complex event layouts. If you specify the active area content and appearance using the built-in properties it will be automatically used during export.

SVG Image

javascript-scheduler-how-to-export-html-to-image-svg.png

dp.events.list = [
  {
    "id": 1,
    "text": "SVG Image",
    "start": "2019-11-08T00:00:00",
    "end": "2019-11-12T00:00:00",
    "resource": "R2",
    "barColor": "#f1c232",
    "areas": [
      {
        "right": 3,
        "top": 8,
        "width": 26,
        "height": 26,
        "image": "icon.svg"
      }
    ]
  }
];

PNG Image

javascript-scheduler-how-to-export-html-to-image-png.png

dp.events.list = [
  {
    "id": 2,
    "text": "PNG Image",
    "start": "2019-11-08T00:00:00",
    "end": "2019-11-12T00:00:00",
    "resource": "R2",
    "barColor": "#f1c232",
    "areas": [
      {
        "right": 3,
        "top": 8,
        "width": 26,
        "height": 26,
        "image": "icon.png"
      }
    ]
  }
];

Font Icon

javascript-scheduler-how-to-export-html-to-image-font-icon.png

dp.events.list = [
  {
    "id": 3,
    "text": "Font icon",
    "start": "2019-11-08T00:00:00",
    "end": "2019-11-12T00:00:00",
    "resource": "R2",
    "barColor": "#f1c232",
    "areas": [
      {
        "right": 8,
        "top": 8,
        "width": 16,
        "height": 14,
        "icon": "icon-edit"
      }
    ]
  }
];

Centered Text

javascript-scheduler-how-to-export-html-to-image-centered-text.png

dp.events.list = [
  {
    "id": 21,
    "text": "",
    "start": "2019-11-08T00:00:00",
    "end": "2019-11-12T00:00:00",
    "resource": "R2",
    "barHidden": true,
    "areas": [
      {
        "right": 10,
        "top": 5,
        "left": 10,
        "bottom": 5,
        "text": "center/center",
        "backColor": "#6aa84f",
        "fontColor": "#ffffff",
        "horizontalAlignment": "center",
        "verticalAlignment": "center"
      }
    ]
  }
];

Left-Aligned Text

javascript-scheduler-how-to-export-html-to-image-left-aligned-text.png

dp.events.list = [
  {
    "id": 22,
    "text": "",
    "start": "2019-11-08T00:00:00",
    "end": "2019-11-12T00:00:00",
    "resource": "R2",
    "barHidden": true,
    "areas": [
      {
        "right": 10,
        "top": 5,
        "left": 10,
        "bottom": 5,
        "text": "left/top",
        "backColor": "#6aa84f",
        "fontColor": "#ffffff",
        "horizontalAlignment": "left",
        "verticalAlignment": "top"
      }
    ]
  }
];

Right-Aligned Text

javascript-scheduler-how-to-export-html-to-image-right-aligned-text.png

dp.events.list = [
  {
    "id": 23,
    "text": "",
    "start": "2019-11-08T00:00:00",
    "end": "2019-11-12T00:00:00",
    "resource": "R2",
    "barHidden": true,
    "areas": [
      {
        "right": 10,
        "top": 5,
        "left": 10,
        "bottom": 5,
        "text": "right/bottom",
        "backColor": "#6aa84f",
        "fontColor": "#ffffff",
        "horizontalAlignment": "right",
        "verticalAlignment": "bottom"
      }
    ]
  }
];

Text Padding

javascript-scheduler-how-to-export-html-to-image-text-padding.png

dp.events.list = [
  {
    "id": 24,
    "text": "",
    "start": "2019-11-08T00:00:00",
    "end": "2019-11-12T00:00:00",
    "resource": "R2",
    "barHidden": true,
    "areas": [
      {
        "right": 10,
        "top": 5,
        "left": 10,
        "bottom": 5,
        "text": "2px padding",
        "backColor": "#6aa84f",
        "fontColor": "#ffffff",
        "horizontalAlignment": "left",
        "verticalAlignment": "top",
        "padding": 2
      }
    ]
  }
];

Background Color

javascript-scheduler-how-to-export-html-to-image-background-color.png

dp.events.list = [
  {
    "id": 33,
    "text": "",
    "start": "2019-11-08T00:00:00",
    "end": "2019-11-12T00:00:00",
    "resource": "R2",
    "barHidden": true,
    "areas": [
      {
        "right": 10,
        "top": 5,
        "left": 10,
        "bottom": 5,
        "text": "#1155cc",
        "backColor": "#1155cc",
        "fontColor": "#ffffff",
        "horizontalAlignment": "center",
        "verticalAlignment": "center"
      }
    ]
  }
];

Exported Image

Scheduler with a sample event set:

javascript-scheduler-how-to-export-html-to-image-source.png

Exported image:

javascript-scheduler-how-to-export-html-to-image-output.png

Source Code

<!DOCTYPE html>
<html>
<head>
  <meta charset="utf-8"/>
  <title>JavaScript Scheduler: How to Export HTML to Image</title>

  <style type="text/css">
    <!-- ... -->
  </style>

  <link rel="stylesheet" href="icons/style.css">

  <!-- DayPilot library -->
  <script src="js/daypilot/daypilot-all.min.js"></script>
</head>
<body>
<div class="header">
  <h1><a href='https://code.daypilot.org/61152/javascript-scheduler-how-to-export-html-to-image'>JavaScript Scheduler: How to Export HTML to Image</a></h1>
  <div><a href="https://javascript.daypilot.org/">DayPilot for JavaScript</a> - HTML5 Calendar/Scheduling Components for JavaScript/Angular/React/Vue</div>
</div>
<div class="main">
  <div id="dp"></div>
  <div class="generated">Generated using <a href="https://builder.daypilot.org/">DayPilot UI Builder</a>.</div>

  <div class="space">
    <button id="export">Export</button>
  </div>

  <div class="space">
    <img id="target" >
  </div>
</div>

<script>
  var dp = new DayPilot.Scheduler("dp", {
    timeHeaders: [{"groupBy":"Month"},{"groupBy":"Day","format":"d"}],
    scale: "Day",
    days: 30,
    startDate: "2019-11-01",
    timeRangeSelectedHandling: "Enabled",
    onTimeRangeSelected: function (args) {
      var dp = this;
      DayPilot.Modal.prompt("Create a new event:", "Event 1").then(function(modal) {
        dp.clearSelection();
        if (!modal.result) { return; }
        dp.events.add(new DayPilot.Event({
          start: args.start,
          end: args.end,
          id: DayPilot.guid(),
          resource: args.resource,
          text: modal.result
        }));
      });
    },
    treeEnabled: true,
  });
  dp.resources = [
    {name: "Icons", id: "R1"},
    {name: "Text", id: "R2"},
    {name: "Background color", id: "R3"},
    {name: "Resource 4", id: "R4"},
    {name: "Resource 5", id: "R5"},
    {name: "Resource 6", id: "R6"},
    {name: "Resource 7", id: "R7"},
    {name: "Resource 8", id: "R8"},
    {name: "Resource 9", id: "R9"},
  ];
  dp.onBeforeEventRender = function(args) {
    /*args.data.areas = [
      {
        right: 3,
        top: 5,
        width: 26,
        height: 26,
        image: "diamond.svg"
      },

    ];*/
  };
  dp.eventVersionsEnabled = true;
  dp.events.list = [
    {
      id: 1,
      text: "SVG Image",
      start: "2019-11-02T00:00:00",
      end: "2019-11-06T00:00:00",
      resource: "R1",
      barColor: "#f1c232",
      areas: [
        {
          right: 3,
          top: 8,
          width: 26,
          height: 26,
          image: "icon.svg"
        },
      ]
    },
    {
      id: 2,
      text: "PNG Image",
      start: "2019-11-07T00:00:00",
      end: "2019-11-11T00:00:00",
      resource: "R1",
      barColor: "#f1c232",
      areas: [
        {
          right: 3,
          top: 8,
          width: 26,
          height: 26,
          image: "icon.png"
        },
      ]
    },
    {
      id: 3,
      text: "Font icon",
      start: "2019-11-12T00:00:00",
      end: "2019-11-16T00:00:00",
      resource: "R1",
      barColor: "#f1c232",
      areas: [
        {
          right: 8,
          top: 8,
          width: 16,
          height: 14,
          icon: "icon-edit"
        },
      ]
    },
    {
      id: 21,
      text: "",
      start: "2019-11-02T00:00:00",
      end: "2019-11-06T00:00:00",
      resource: "R2",
      barHidden: true,
      areas: [
        {
          right: 10,
          top: 5,
          left: 10,
          bottom: 5,
          text: "center/center",
          backColor: "#6aa84f",
          fontColor: "#ffffff",
          horizontalAlignment: "center",
          verticalAlignment: "center"
        },
      ]
    },
    {
      id: 22,
      text: "",
      start: "2019-11-07T00:00:00",
      end: "2019-11-11T00:00:00",
      resource: "R2",
      barHidden: true,
      areas: [
        {
          right: 10,
          top: 5,
          left: 10,
          bottom: 5,
          text: "left/top",
          backColor: "#6aa84f",
          fontColor: "#ffffff",
          horizontalAlignment: "left",
          verticalAlignment: "top"
        },
      ]
    },
    {
      id: 23,
      text: "",
      start: "2019-11-12T00:00:00",
      end: "2019-11-16T00:00:00",
      resource: "R2",
      barHidden: true,
      areas: [
        {
          right: 10,
          top: 5,
          left: 10,
          bottom: 5,
          text: "right/bottom",
          backColor: "#6aa84f",
          fontColor: "#ffffff",
          horizontalAlignment: "right",
          verticalAlignment: "bottom"
        },
      ]
    },
    {
      id: 24,
      text: "",
      start: "2019-11-17T00:00:00",
      end: "2019-11-21T00:00:00",
      resource: "R2",
      barHidden: true,
      areas: [
        {
          right: 10,
          top: 5,
          left: 10,
          bottom: 5,
          text: "2px padding",
          backColor: "#6aa84f",
          fontColor: "#ffffff",
          horizontalAlignment: "left",
          verticalAlignment: "top",
          padding: 2
        },
      ]
    },
    {
      id: 31,
      text: "",
      start: "2019-11-02T00:00:00",
      end: "2019-11-06T00:00:00",
      resource: "R3",
      barHidden: true,
      areas: [
        {
          right: 10,
          top: 5,
          left: 10,
          bottom: 5,
          text: "#6d9eeb",
          backColor: "#6d9eeb",
          fontColor: "#ffffff",
          horizontalAlignment: "center",
          verticalAlignment: "center"
        },
      ]
    },
    {
      id: 32,
      text: "",
      start: "2019-11-07T00:00:00",
      end: "2019-11-11T00:00:00",
      resource: "R3",
      barHidden: true,
      areas: [
        {
          right: 10,
          top: 5,
          left: 10,
          bottom: 5,
          text: "#3c78d8",
          backColor: "#3c78d8",
          fontColor: "#ffffff",
          horizontalAlignment: "center",
          verticalAlignment: "center"
        },
      ]
    },
    {
      id: 33,
      text: "",
      start: "2019-11-12T00:00:00",
      end: "2019-11-16T00:00:00",
      resource: "R3",
      barHidden: true,
      areas: [
        {
          right: 10,
          top: 5,
          left: 10,
          bottom: 5,
          text: "#1155cc",
          backColor: "#1155cc",
          fontColor: "#ffffff",
          horizontalAlignment: "center",
          verticalAlignment: "center"
        },
      ]
    },
    {
      id: 34,
      text: "",
      start: "2019-11-17T00:00:00",
      end: "2019-11-21T00:00:00",
      resource: "R3",
      barHidden: true,
      areas: [
        {
          right: 10,
          top: 5,
          left: 10,
          bottom: 5,
          text: "transparent",
          backColor: "transparent",
          horizontalAlignment: "center",
          verticalAlignment: "center"
        },
      ]
    },
  ];
  dp.init();
</script>


<script>
  var elements = {
    export: document.getElementById("export"),
    target: document.getElementById("target")
  };

  elements.export.addEventListener("click", function(ev) {
    elements.target.src = dp.exportAs("png").toDataUri();
  });

</script>

</body>
</html>