This sample shows how to create an expense report and save it to a PDF file using the FlexGridPdfConverter and PdfDocument API. The sample creates two FlexGrid instances (data & footer) internally and exports them to a document using the FlexGridPdfConverter.draw method in order to display an expense table. drawText and other vector graphics methods of PdfDocument are used to draw captions and expected handwritten entries.
Learn about FlexGrid | FlexGrid API Reference
This example uses Angular.
import 'bootstrap.css';
import '@mescius/wijmo.styles/wijmo.css';
import './styles.css';
//
import * as wijmo from '@mescius/wijmo';
import '@mescius/wijmo.chart.render';
import * as grid from '@mescius/wijmo.grid';
import * as pdf from '@mescius/wijmo.pdf';
import * as gridPdf from '@mescius/wijmo.grid.pdf';
//
import '@angular/compiler';
import { Component, Inject, enableProdMode, ViewChild, ɵresolveComponentResources } from '@angular/core';
import { BrowserModule, bootstrapApplication } from '@angular/platform-browser';
import { DataService, IEmployee, IExpenseDetails } from './app.data';
import { WjGridModule } from '@mescius/wijmo.angular2.grid';
//
@Component({
standalone: true,
providers: [DataService],
imports: [WjGridModule, BrowserModule],
selector: 'app-component',
templateUrl: 'src/app.component.html'
})
export class AppComponent {
constructor(@Inject(DataService) private dataService: DataService) {
}
//
export() {
let doc = new pdf.PdfDocument({
header: {
declarative: {
text: 'Expense Report\t&[Page]\\&[Pages]',
font: new pdf.PdfFont('times', 12),
brush: '#bfc1c2'
}
},
lineGap: 2,
pageSettings: {
margins: {
left: 36,
right: 36,
top: 36,
bottom: 36
}
},
ended: (sender: pdf.PdfDocument, args: pdf.PdfDocumentEndedEventArgs) => pdf.saveBlob(args.blob, 'FlexGrid.pdf')
});
//
this.dataService.getEmployees().forEach((employee, i, arr) => {
this.drawEmployee(doc, employee);
//
if (i < arr.length - 1) {
doc.addPage();
}
});
//
doc.end();
}
//
private drawEmployee(doc: pdf.PdfDocument, employee: IEmployee) {
let tot = employee.expenses.totals;
let expenses = employee.expenses.items.sort((a, b) => a.date.getTime() - b.date.getTime());
//
let minDate = expenses[0].date,
maxDate = expenses[expenses.length - 1].date,
columns = [
{ header: 'Date', binding: 'date', width: 105 },
{ header: 'Description', binding: 'description', format: 'c', width: 105 },
{ header: 'Hotel', binding: 'hotel', format: 'c', width: 105 },
{ header: 'Transport', binding: 'transport', format: 'c', width: 105 },
{ header: 'Meal', binding: 'meal', format: 'c', width: 105 },
{ header: 'Fuel', binding: 'fuel', format: 'c', width: 105 },
{ header: 'Misc', binding: 'misc', format: 'c', width: 105 },
{ header: 'Total', binding: 'total', format: 'c', width: 105 }
],
bold = new pdf.PdfFont('times', 10, 'normal', 'bold'),
thinPen = new pdf.PdfPen('#000000', 0.5);
//
let flexGrid: grid.FlexGrid,
footer: grid.FlexGrid;
//
try {
// * setup FlexGrid *
flexGrid = new grid.FlexGrid('#flexGrid');
footer = new grid.FlexGrid('#flexGridFooter');
//
flexGrid.initialize({
autoGenerateColumns: false,
allowMerging: grid.AllowMerging.All,
columns: columns,
headersVisibility: grid.HeadersVisibility.Column,
itemsSource: expenses
});
//
footer.initialize({
allowMerging: grid.AllowMerging.All,
autoGenerateColumns: false,
headersVisibility: grid.HeadersVisibility.None,
columns: columns,
itemsSource: <IExpenseDetails[]>[{
date: null,
description: null,
hotel: tot.hotel,
transport: tot.transport,
meal: tot.meal,
fuel: tot.fuel,
misc: tot.misc,
total: tot.total
}]
});
//
footer.cells.setCellData(0, 'date', 'Total', false);
footer.cells.setCellData(0, 'description', 'Total', false);
footer.cells.rows[0].allowMerging = true;
//
// * draw captions *
doc.drawText('Purpose: ', null, null, { font: bold, continued: true });
doc.drawText(employee.purpose);
//
doc.drawText('From: ', 380, 0, { font: bold, continued: true });
doc.drawText(wijmo.changeType(minDate, wijmo.DataType.String, 'd'));
//
doc.drawText('To: ', 470, 0, { font: bold, continued: true });
doc.drawText(wijmo.changeType(maxDate, wijmo.DataType.String, 'd'));
//
doc.moveDown(2);
//
let y = doc.y;
doc.drawText('Name: ', 20, y, { font: bold, continued: true });
doc.drawText(employee.name);
//
doc.drawText('Position: ', 190, y, { font: bold, continued: true });
doc.drawText(employee.position);
//
doc.drawText('SSN: ', 360, y, { font: bold, continued: true });
doc.drawText(employee.ssn);
//
y = doc.y;
doc.drawText('Department: ', 20, y, { font: bold, continued: true });
doc.drawText(employee.department);
//
doc.drawText('Manager: ', 190, y, { font: bold, continued: true });
doc.drawText(employee.manager);
//
doc.drawText('Employee ID: ', 360, y, { font: bold, continued: true });
doc.drawText(employee.id);
//
doc.moveDown(2);
//
// draw FlexGrid
gridPdf.FlexGridPdfConverter.draw(flexGrid, doc, doc.width, null, {
styles: {
cellStyle: {
backgroundColor: '#ffffff',
borderColor: '#c6c6c6'
},
altCellStyle: {
backgroundColor: '#f9f9f9'
},
groupCellStyle: {
font: { weight: 'bold' },
backgroundColor: '#dddddd'
},
headerCellStyle: {
backgroundColor: '#eaeaea'
}
}
});
//
// draw footer
gridPdf.FlexGridPdfConverter.draw(footer, doc, doc.width, null, {
styles: {
cellStyle: {
font: { weight: 'bold' },
backgroundColor: '#dddddd',
borderColor: '#c6c6c6'
}
}
});
//
doc.moveDown(2);
//
// * draw captions *
doc.drawText('Subtotal: ', 400, doc.y, { font: bold, continued: true });
doc.drawText(wijmo.changeType(tot.total - employee.advance, wijmo.DataType.String, 'c'));
//
doc.drawText('Cash Advance: ', 400, doc.y, { font: bold, continued: true });
doc.drawText(wijmo.changeType(employee.advance, wijmo.DataType.String, 'c'));
//
doc.drawText('Total: ', 400, doc.y, { font: bold, continued: true });
doc.drawText(wijmo.changeType(tot.total, wijmo.DataType.String, 'c'));
doc.moveDown(2);
//
this.checkLineAvailable(doc);
//
y = doc.y;
let sz = doc.drawText('Employee signature: ', 0, y);
doc.paths.moveTo(sz.size.width, doc.y).lineTo(sz.size.width + 150, doc.y).stroke(thinPen);
sz = doc.drawText('Date: ', 300, y);
doc.paths.moveTo(300 + sz.size.width + 5, doc.y).lineTo(300 + sz.size.width + 100, doc.y).stroke(thinPen);
doc.moveDown();
//
this.checkLineAvailable(doc);
//
y = doc.y;
sz = doc.drawText('Approved by: ', 0, y);
doc.paths.moveTo(sz.size.width, doc.y).lineTo(sz.size.width + 150, doc.y).stroke(thinPen);
sz = doc.drawText('Date: ', 300, y);
doc.paths.moveTo(300 + sz.size.width, doc.y).lineTo(300 + sz.size.width + 100, doc.y).stroke(thinPen);
}
finally {
if (flexGrid) {
flexGrid.dispose();
}
//
if (footer) {
footer.dispose();
}
}
}
//
private checkLineAvailable(doc: pdf.PdfDocument) {
if (doc.height - doc.y < doc.lineHeight() + doc.lineGap) {
doc.addPage();
}
}
}
//
//
enableProdMode();
// Resolve resources (templateUrl, styleUrls etc), After resolution all URLs have been converted into `template` strings.
ɵresolveComponentResources(fetch).then(() => {
// Bootstrap application
bootstrapApplication(AppComponent).catch(err => console.error(err));
});
Submit and view feedback for