ChartDirector 7.0 (ASP/COM/VB Edition)

Zoom/Scroll with PDF Report (Web)




This example is modified from Zooming and Scrolling with Track Line (Web) to demonstrate creating a PDF report with multiple charts covering the entire data range.

This example adds two buttons to the web page to download the visible chart image and the complete PDF report. These buttons use JsChartViewer.partialUpdateAsAttachment to generate the download attachment requests.

The code on the server side needs to handle 4 kinds of requests:

MultiPagePDF works by generating a PDF page for chart added to it. The chart can be a MultiChart, and can contain free form CDML text, tables, shape and images. In this way, MultiPagePDF can create complete PDF reports.

In this example, the PDF report is generated as follows:

Source Code Listing

[Web Version (in ASP)] aspdemo\zoomscrollpdf.asp
<%@ language="vbscript" %> <% Set cd = CreateObject("ChartDirector.API") ' ' Create a random table for demo purpose. ' Function getRanTable() Set r = cd.RanTable(127, 4, 1827) Call r.setDateCol(0, DateSerial(2015, 1, 1), 86400) Call r.setCol(1, 150, -10, 10) Call r.setCol(2, 200, -10, 10) Call r.setCol(3, 250, -8, 8) Set getRanTable = r Exit Function End Function ' ' Initialize the WebChartViewer when the page is first loaded ' Sub initViewer(viewer) ' The full x-axis range is from Jan 1, 2007 to Jan 1, 2012 startDate = DateSerial(2015, 1, 1) endDate = DateSerial(2020, 1, 1) Call viewer.setFullRange("x", startDate, endDate) ' Initialize the view port to show the last 366 days (out of 1827 days) viewer.ViewPortWidth = 366.0 / 1827 viewer.ViewPortLeft = 1 - viewer.ViewPortWidth ' Set the maximum zoom to 10 days (out of 1827 days) viewer.ZoomInWidthLimit = 10.0 / 1827 End Sub ' ' Draw an XYChart using data from startX to endX ' Function drawXYChart(startX, endX) ' Obtain the random data table Set r = getRanTable() ' Select the data for the visible date range startDate to endDate. It is possible there is no ' data point at exactly startDate or endDate. In this case, we also need the data points that ' are just outside the visible date range to "overdraw" the line a little bit (the "overdrawn" ' part will be clipped to the plot area) In this demo, we do this by adding a one day margin to ' the date range when selecting the data. The selected data from the random data table timeStamps = r.getCol(0) dataSeriesA = r.getCol(1) dataSeriesB = r.getCol(2) dataSeriesC = r.getCol(3) ' ' At this stage, we have extracted the visible data. We can use those data to plot the chart. ' '================================================================================ ' Configure overall chart appearance. '================================================================================ ' Create an XYChart object of size 640 x 350 pixels Set c = cd.XYChart(640, 350) ' Set the plotarea at (55, 50) with width 80 pixels less than chart width, and height 85 pixels ' less than chart height. Use a vertical gradient from light blue (f0f6ff) to sky blue (a0c0ff) ' as background. Set border to transparent and grid lines to white (ffffff). Call c.setPlotArea(55, 50, c.getWidth() - 80, c.getHeight() - 85, c.linearGradientColor(0, 50, _ 0, c.getHeight() - 35, &Hf0f6ff, &Ha0c0ff), -1, cd.Transparent, &Hffffff, &Hffffff) ' As the data can lie outside the plotarea in a zoomed chart, we need enable clipping. Call c.setClipping() ' Add a legend box at (55, 25) using horizontal layout. Use 8pts Arial Bold as font. Set the ' background and border color to Transparent and use line style legend key. Set b = c.addLegend(55, 25, False, "Arial Bold", 8) Call b.setBackground(cd.Transparent) Call b.setLineStyleKey() ' Set the axis stem to transparent Call c.xAxis().setColors(cd.Transparent) Call c.yAxis().setColors(cd.Transparent) ' Add axis title using 10pts Arial Bold Italic font Call c.yAxis().setTitle("Ionic Temperature (C)", "Arial Bold Italic", 10) '================================================================================ ' Add data to chart '================================================================================ ' ' In this example, we represent the data by lines. You may modify the code below to use other ' representations (areas, scatter plot, etc). ' ' Add a line layer for the lines, using a line width of 2 pixels Set layer = c.addLineLayer2() Call layer.setLineWidth(2) ' In this demo, we do not have too many data points. In real code, the chart may contain a lot ' of data points when fully zoomed out - much more than the number of horizontal pixels in this ' plot area. So it is a good idea to use fast line mode. Call layer.setFastLineMode() ' Now we add the 3 data series to a line layer, using the color red (ff33333), green (008800) ' and blue (3333cc) Call layer.setXData(timeStamps) Call layer.addDataSet(dataSeriesA, &Hff3333, "Alpha") Call layer.addDataSet(dataSeriesB, &H008800, "Beta") Call layer.addDataSet(dataSeriesC, &H3333cc, "Gamma") '================================================================================ ' Configure axis scale and labelling '================================================================================ ' Set the x-axis scale Call c.xAxis().setDateScale(startX, endX) ' ' In this demo, the time range can be from a few years to a few days. We demonstrate how to set ' up different date/time format based on the time range. ' ' If all ticks are yearly aligned, then we use "yyyy" as the label format. Call c.xAxis().setFormatCondition("align", 360 * 86400) Call c.xAxis().setLabelFormat("{value|yyyy}") ' If all ticks are monthly aligned, then we use "mmm yyyy" in bold font as the first label of a ' year, and "mmm" for other labels. Call c.xAxis().setFormatCondition("align", 30 * 86400) Call c.xAxis().setMultiFormat(cd.StartOfYearFilter(), "<*font=bold*>{value|mmm yyyy}", _ cd.AllPassFilter(), "{value|mmm}") ' If all ticks are daily algined, then we use "mmm dd<*br*>yyyy" in bold font as the first label ' of a year, and "mmm dd" in bold font as the first label of a month, and "dd" for other labels. Call c.xAxis().setFormatCondition("align", 86400) Call c.xAxis().setMultiFormat(cd.StartOfYearFilter(), _ "<*block,halign=left*><*font=bold*>{value|mmm dd<*br*>yyyy}", cd.StartOfMonthFilter(), _ "<*font=bold*>{value|mmm dd}") Call c.xAxis().setMultiFormat2(cd.AllPassFilter(), "{value|dd}") ' For all other cases (sub-daily ticks), use "hh:nn<*br*>mmm dd" for the first label of a day, ' and "hh:nn" for other labels. Call c.xAxis().setFormatCondition("else") Call c.xAxis().setMultiFormat(cd.StartOfDayFilter(), "<*font=bold*>{value|hh:nn<*br*>mmm dd}", _ cd.AllPassFilter(), "{value|hh:nn}") Set drawXYChart = c Exit Function End Function ' ' Draw the chart ' Sub drawChart(viewer) ' Determine the visible x-axis range viewPortStartDate = cd.NTime(viewer.getValueAtViewPort("x", viewer.ViewPortLeft)) viewPortEndDate = cd.NTime(viewer.getValueAtViewPort("x", viewer.ViewPortRight)) ' Draw the XYChart Set c = drawXYChart(viewPortStartDate, viewPortEndDate) ' Add a title to the chart using 18 pts Times New Roman Bold Italic font Call c.addTitle(" PDF Report Demonstration", "Times New Roman Bold Italic", 18) If viewer.IsAttachmentRequest Then ' Output as PNG attachment Call viewer.setChart(c, cd.PNG) Else ' Output the chart Call viewer.setChart(c, cd.SVG) ' Output Javascript chart model to the browser to support tracking cursor viewer.ChartModel = c.getJsChartModel() End If End Sub ' ' Create a multi-page PDF Report ' Function createPdfReport(viewer) ' The MultiPagePDF object can create PDF from multiple pages, each with one chart object. Since ' a chart object can contain text (eg. using BaseChart.addText) and other charts (eg. using ' MultiChart), that means each page can contain text and multiple charts. Set doc = cd.MultiPagePDF() ' Page configuration - A4 = 210 x 297mm. The PDF default is 96 dpi (dot per inch), so the A4 ' size is equal to 794 x 1123 dots. pageConfig = "pagewidth = 794; pageheight = 1123" ' In this example, we include a cover page with only text. This is by creating an empty pie ' chart with text only. Set firstPage = cd.PieChart(720, 960) Call firstPage.addText(360, 320, _ "<*size=50*>ChartDirector<*br*><*size=30*>PDF Report Demonstration<*/*>", "Arial Bold", _ 30, &H000000, cd.Center) Call firstPage.setOutputOptions(pageConfig) Call doc.addPage(firstPage) ' We include 2 charts per page, with each chart showing one year of data. Each page will also ' have a header and page number startYear = Int(cd.getChartYMD(viewer.getValueAtViewPort("x", 0)) / 10000) endYear = Int(cd.getChartYMD(viewer.getValueAtViewPort("x", 1) - 1) / 10000) pageNumber = 0 For yyyy = startYear To endYear Step 2 ' This chart is the page. Set m = cd.MultiChart(760, 920) ' Use addTitle to add a header Call m.addTitle("ChartDirector PDF Report Demonstration", "Arial Bold", 20) ' Create the first chart Set c = drawXYChart(DateSerial(yyyy, 1, 1), DateSerial(yyyy + 1, 1, 1)) Call m.addChart(Int((m.getWidth() - c.getWidth()) / 2), 100, c) Call c.addTitle("Year " & yyyy) Set c2 = Nothing If yyyy < endYear Then ' Create the second chart Set c2 = drawXYChart(DateSerial(yyyy + 1, 1, 1), DateSerial(yyyy + 2, 1, 1)) Call c2.addTitle("Year " & (yyyy + 1)) Call m.addChart(Int((m.getWidth() - c2.getWidth()) / 2), 500, c2) End If ' Add the page number pageNumber = pageNumber + 1 Call m.addTitle2(cd.BottomCenter, CStr(pageNumber), "Arial Bold", 8) Call m.setOutputOptions(pageConfig) Call doc.addPage(m) Next ' Output the PDF report createPdfReport = doc.outPDF2() End Function ' Create the WebChartViewer object Set viewer = cd.WebChartViewer(Request, "chart1") If viewer.IsAttachmentRequest Then If Request("download") = "report" Then Call viewer.streamChart2(Response, createPdfReport(viewer), "chartdirector_report.pdf") Else Call drawChart(viewer) Call viewer.streamChart(Response, "demo_chart.png") End If Call Response.End End If If viewer.IsPartialUpdateRequest Then ' Draw the chart and perform a partial response. Call drawChart(viewer) Call Response.Write(viewer.partialUpdateChart()) Call Response.End End If ' ' If the code reaches here, it is a full page request. ' ' In this exapmle, we just need to initialize the WebChartViewer and draw the chart. Call initViewer(viewer) Call drawChart(viewer) %> <!DOCTYPE html> <html> <head> <title>PDF Report Demonstration</title> <script type="text/javascript" src="cdjcv.js"></script> <style type="text/css"> .chartButton { font:12px Verdana; border-bottom:#000000 1px solid; padding:5px; cursor:pointer;} .chartButtonSpacer { font:12px Verdana; border-bottom:#000000 1px solid; padding:5px;} .chartButton:hover { box-shadow:inset 0px 0px 0px 2px #444488; } .chartButtonPressed { background-color: #CCFFCC; } </style> </head> <body style="margin:0px;"> <script type="text/javascript"> // // Execute the following initialization code after the web page is loaded // JsChartViewer.addEventListener(window, 'load', function() { // Update the chart when the view port has changed (eg. when the user zooms in using the mouse) var viewer = JsChartViewer.get('<%=viewer.Id%>'); viewer.attachHandler("ViewPortChanged", viewer.partialUpdate); // Draw track cursor when mouse is moving over plotarea. Hide it when mouse leaves plot area. viewer.attachHandler(["MouseMovePlotArea", "TouchStartPlotArea", "TouchMovePlotArea", "PostUpdate", "ChartMove"], function(e) { this.preventDefault(e); // Prevent the browser from using touch events for other actions trackLineLabel(viewer, viewer.getPlotAreaMouseX()); viewer.setAutoHide("all", ["MouseOutPlotArea", "TouchEndPlotArea"]); }); }); // // Draw track line with data labels // function trackLineLabel(viewer, mouseX) { // Remove all previously drawn tracking object viewer.hideObj("all"); // The chart and its plot area var c = viewer.getChart(); var plotArea = c.getPlotArea(); // Get the data x-value that is nearest to the mouse, and find its pixel coordinate. var xValue = c.getNearestXValue(mouseX); var xCoor = c.getXCoor(xValue); // Draw a vertical track line at the x-position viewer.drawVLine("trackLine", xCoor, plotArea.getTopY(), plotArea.getBottomY(), "black 1px dotted"); // Draw a label on the x-axis to show the track line position viewer.showTextBox("xAxisLabel", xCoor, plotArea.getBottomY() + 4, JsChartViewer.Top, c.xAxis().getFormattedLabel(xValue, "mmm dd, yyyy"), "font:bold 11px Arial;color:#FFFFFF;background-color:#000000;padding:0px 3px"); // Iterate through all layers to draw the data labels for (var i = 0; i < c.getLayerCount(); ++i) { var layer = c.getLayerByZ(i); // The data array index of the x-value var xIndex = layer.getXIndexOf(xValue); // Iterate through all the data sets in the layer for (var j = 0; j < layer.getDataSetCount(); ++j) { var dataSet = layer.getDataSetByZ(j); // Get the color and position of the data label var color = dataSet.getDataColor(); var yCoor = c.getYCoor(dataSet.getPosition(xIndex), dataSet.getUseYAxis()); // Draw a track dot with a label next to it for visible data points in the plot area if ((yCoor != null) && (yCoor >= plotArea.getTopY()) && (yCoor <= plotArea.getBottomY()) && (color != null)) { viewer.showTextBox("dataPoint" + i + "_" + j, xCoor, yCoor, JsChartViewer.Center, viewer.htmlRect(7, 7, color)); viewer.showTextBox("dataLabel" + i + "_" + j, xCoor + 5, yCoor, JsChartViewer.Left, dataSet.getValue(xIndex).toPrecision(4), "padding:0px 3px;font:bold 11px Arial;" + "background-color:" + color + ";color:#FFFFFF;-webkit-text-size-adjust:100%;"); } } } } // // This method is called when the user clicks on the Pointer, Zoom In or Zoom Out buttons // function setMouseMode(mode) { var viewer = JsChartViewer.get('<%=viewer.Id%>'); if (mode == viewer.getMouseUsage()) mode = JsChartViewer.Default; // Set the button color based on the selected mouse mode document.getElementById("scrollButton").className = "chartButton" + ((mode == JsChartViewer.Scroll) ? " chartButtonPressed" : ""); document.getElementById("zoomInButton").className = "chartButton" + ((mode == JsChartViewer.ZoomIn) ? " chartButtonPressed" : ""); document.getElementById("zoomOutButton").className = "chartButton" + ((mode == JsChartViewer.ZoomOut) ? " chartButtonPressed" : ""); // Set the mouse mode viewer.setMouseUsage(mode); } </script> <form method="post" id="ZoomScrollPDF" runat="server"> <table cellspacing="0" cellpadding="0" style="border:black 1px solid;"> <tr> <td align="right" colspan="2" style="background:#000088; color:#ffff00; padding:0px 4px 2px 0px;"> <a style="color:#FFFF00; font:italic bold 10pt Arial; text-decoration:none" href="http://www.advsofteng.com/"> Advanced Software Engineering </a> </td> </tr> <tr valign="top"> <td style="width:130px; background:#c0c0ff;"> <div style="width:130px"> <!-- The following table is to create 3 cells for 3 buttons to control the mouse usage mode. --> <table style="width:100%; padding:0px; border:0px; border-spacing:0px;"> <tr> <td class="chartButton" id="scrollButton" onclick="setMouseMode(JsChartViewer.Scroll)" ontouchstart="this.onclick(event); event.preventDefault();"> <img src="scrollew.gif" style="vertical-align:middle" alt="Drag" />&nbsp;&nbsp;Drag to Scroll </td> </tr> <tr> <td class="chartButton" id="zoomInButton" onclick="setMouseMode(JsChartViewer.ZoomIn)" ontouchstart="this.onclick(event); event.preventDefault();"> <img src="zoomInIcon.gif" style="vertical-align:middle" alt="Zoom In" />&nbsp;&nbsp;Zoom In </td> </tr> <tr> <td class="chartButton" id="zoomOutButton" onclick="setMouseMode(JsChartViewer.ZoomOut)" ontouchstart="this.onclick(event); event.preventDefault();"> <img src="zoomOutIcon.gif" style="vertical-align:middle" alt="Zoom Out" />&nbsp;&nbsp;Zoom Out </td> </tr> </table> <div style="margin-top:30px; text-align:center"> <input type="button" value="Download Chart" style="width:100%;height:2em" onclick="JsChartViewer.get('<%=viewer.Id%>').partialUpdateAsAttachment('download=chart');" /> <input type="button" value="PDF Report" style="margin-top:5px; width:100%;height:2em" onclick="JsChartViewer.get('<%=viewer.Id%>').partialUpdateAsAttachment('download=report');" /> </div> </div> </td> <td style="border-left:black 1px solid; padding:10px 5px 0px 5px;"> <!-- ****** Here is the chart image ****** --> <%=viewer.renderHTML()%> </td> </tr> </table> </form> </body> </html>