Is it possible, in Javascript, to run a function in background ?
I am generating a pdf with pdfmake tool in an angularJS app, but the pdf generation is quite long (3-4 seconds) and during this time, the ui freeze completely.
I would like to run a background task and force the pdf download without freezing the user ui, is it possible ?
Here how I am running pdfmake (pdfmake and _ are custom factories):
'use strict';
angular.module('App')
.service('CatalogPdfService', ['pdfmake', '_', '$q', '$filter',
function (pdfmake, _, $q, $filter) {
var $translate = $filter('translate');
var listDate = new Date();
return {
download: download
};
function download(data) {
listDate = _.first(data).publishedOn;
console.log('start download');
var deferred = $q.defer();
var filename = $translate('APP.EXPORT.pdf.catalog.title', {date: $filter('amDateFormat')(listDate, 'DDMMYYYY')}) + '.pdf';
create(data).download(filename, function () {
console.log('end download');
deferred.resolve();
});
return deferred.promise;
}
function create(data) {
// group data by category
var dataByCategory = _.groupBy(data, function (d) {
return d.category;
});
// group categories data by subcategory
_.forEach(dataByCategory, function (d, i) {
dataByCategory[i] = _.groupBy(d, function (d) {
return d.subcategory;
});
});
var content = {
table: {
headerRows: 1,
widths: ['*', 20, 10, 20, 20, 20, 20, 40, 20, 30],
body: [
[
{text: $translate('APP.EXPORT.pdf.catalog.header.article') , style: 'headings', alignment: 'left'},
{text: $translate('APP.EXPORT.pdf.catalog.header.mine') , style: 'headings'},
{text: $translate('APP.EXPORT.pdf.catalog.header.rank') , style: 'headings'},
{text: $translate('APP.EXPORT.pdf.catalog.header.origin') , style: 'headings'},
{text: $translate('APP.EXPORT.pdf.catalog.header.transporter') , style: 'headings'},
{text: $translate('APP.EXPORT.pdf.catalog.header.culture') , style: 'headings'},
{text: $translate('APP.EXPORT.pdf.catalog.header.label') , style: 'headings'},
{text: $translate('APP.EXPORT.pdf.catalog.header.unit') , style: 'headings'},
{text: $translate('APP.EXPORT.pdf.catalog.header.packing') , style: 'headings'},
{text: $translate('APP.EXPORT.pdf.catalog.header.price') , style: 'headings'}
]
]
},
layout: {
hLineWidth: function (i) {
return (i == 0) ? 0 : 1;
},
vLineWidth: function (i) {
return 0;
},
hLineColor: function (i, node) {
return '#ccc';
}
}
};
_.forEach(dataByCategory, function (data, category) {
content.table.body = content.table.body.concat(renderCategory(category, data));
});
var dd = {};
dd.content = renderHeader().concat(content);
dd.header = function (currentPage, pageCount) {
return {
text: $translate('APP.EXPORT.pdf.catalog.pagecount', {start: currentPage.toString(), end: pageCount.toString()}),
alignment: 'right',
color: '#666',
margin: [0, 20, 40, 0]
};
};
dd.styles = {
title: {
fontSize: 15,
bold: true
},
headings: {
italics: true,
alignment: 'center'
},
flag: {
alignment: 'center',
italics: true,
color: '#666'
},
category: {
bold: true,
fontSize: 12,
margin: [0, 10, 0, 0] // Left, Top, Right, Bottom
},
subcategory: {
bold: true,
fontSize: 10,
margin: [0, 7, 0, 5] // Left, Top, Right, Bottom
}
};
dd.defaultStyle = {
fontSize: 8
};
return pdfmake.createPdf(dd);
}
function renderHeader() {
return [
{image: logo(), height:40, width: 86},
{
margin: [0, 10, 0, 20],
table: {
widths: [100, 100, 100, '*'],
body: [
[
{text: $translate('APP.COMMON.address', {char: '\n'})},
{text: '\n' + $translate('APP.COMMON.phone')},
{text: '\n' + $translate('APP.COMMON.fax')},
{text: '\n' + $translate('APP.EXPORT.pdf.catalog.listno', {date: $filter('amDateFormat')(listDate, 'DD/MM/YYYY')}) , alignment: 'right'}
]
]
},
layout: {
hLineWidth: function (i) {
return (i == 0) ? 0 : 1;
},
vLineWidth: function (i) {
return 0;
}
}
}];
}
function renderCategory(name, data) {
var category = [
[
{text: name, style: 'category', colspan: 10},
'', '', '', '', '', '', '', '', ''
]
];
_.forEach(data, function (data, name) {
category = category.concat(renderSubcategory(name, data));
});
return category;
}
function renderSubcategory(name, data) {
var subcategory = [
[
{text: name, style: 'subcategory', colspan: 10},
'', '', '', '', '', '', '', '', ''
]
];
_.forEach(data, function (product) {
subcategory.push(renderProduct(product));
});
return subcategory;
}
function renderProduct(product) {
return [
product.name,
{
text: (product.isInPrivateList ? 'Oui' : ''),
style: 'flag'
},
{
text: (null === product.rank ? '' : String(product.rank)),
style: 'flag'
},
{
text: (product.origin || ''),
style: 'flag'
},
{
text: (product.transporter || ''),
style: 'flag'
},
{
text: (product.label || ''),
style: 'flag'
},
{
text: (product.culture || ''),
style: 'flag'
},
{
text: product.unit,
margin: [0, 0, 5, 0],
italics: true,
alignment: 'right'
},
{
text: (product.quantity || '1'),
italics: true,
fillColor: '#eee',
alignment: 'center'
},
{
text: product.unitPrice,
margin: [0, 0, 5, 0],
italics: true,
fillColor: '#eee',
alignment: 'right'
}
];
}
function logo() {
return 'data:image/jpeg;base64,blabla bigbase64 string'
}
}]);
You could use a Web Worker to generate the PDF. But you should be aware of some restrictions when using them. Here is a good reference.
I created a factory in Angular for doing work on a worker thread. Something like this:
/*
Here's an example on how to get this sack of moldering spuds to do something:
var myWorker = new MyWorker({ fn: function() {
this.onmessage = function(args) {
setTimeout(function() {
this.postMessage('Got args: ' + args.data);
}, 20000);
};
} });
myWorker.do('Test').then(function(message) {
alert(message);
});
*/
'use strict';
angular.module('myApp')
.factory('MyWorker', function($q) {
var _worker;
var MyWorker = function(settings) {
_init(settings);
};
MyWorker.prototype.do = function(args) {
var deferred = $q.defer();
_worker.onmessage = function(message) {
deferred.resolve(message.data);
};
//Fire up the blades.
if (args)
_worker.postMessage(args);
else
_worker.postMessage();
return deferred.promise;
};
MyWorker.prototype.destroy = function() {
_worker.terminate();
};
function _init(settings) {
if (settings.script)
_worker = new Worker(settings.script);
//Need to make this IE (10+) friendly.
else if (settings.fn) {
var blobUrl = window.URL.createObjectURL(new Blob(
['(', settings.fn.toString(), ')()'],
{ type: 'application/javascript' }
));
_worker = new Worker(blobUrl);
}
};
return MyWorker;
});
This will give you a rough idea about how it can be implemented in AngularJS, but seriously take it with a grain of salt.
Related
Currently, I have this amchart below.
And I want to create another amchart with the same values. (I just want to replace the graph).
The new graphic is here -> https://www.amcharts.com/demos/line-graph/?theme=material#code
my code Angular:
prepareDataForTemplate(res) {
this.showLineChart = false;
if (res.RETURNCODE == 'OKK00') {
this.lines = [];
this.dataForChart.data.splice(0, this.dataForChart.data.length);
this.dataForChart.date.splice(0, this.dataForChart.date.length);
this.dataForChart.data.push(...res.HISTDEV.VALUE.map(x => x.COURS));
this.dataForChart.date.push(...res.HISTDEV.VALUE.map(x => x.DATE));
this.showLineChart = true;
if (res.HISTDEV.VALUE.length > 0) {
this.yMin = res.HISTDEV.VALUE[0].COURS;
this.yMax = res.HISTDEV.VALUE[0].COURS;
res.HISTDEV.VALUE.map(value => {
if (value.COURS < this.yMin) {
this.yMin = value.COURS;
}
if (value.COURS > this.yMax) {
this.yMax = value.COURS;
}
})
}
this.loadChart();
} else {
}
}
Then, the method loadChart(), it is the graph that I want to change...
loadChart() {
let datePipe = this.datePipe;
let decimalPipe = this.decimalPipe;
let leleuxNumPipe = this.leleuxNumPipe;
this.lineChartReturn = {
tooltip: {
trigger: 'axis',
position: function(pt) {
return [pt[0], '10%'];
},
formatter: function(params) {
return datePipe.transform(params[0].name) + "<br/>" +
params[0].marker + " " +
params[0].seriesName + " <b>" +
leleuxNumPipe.transform(decimalPipe.transform(params[0].value, '1.2-2')) + "</b";
}
},
title: {
left: 'center',
text: '',
},
xAxis: {
type: 'category',
boundaryGap: false,
//show: true,
data: this.dataForChart.date,
axisLabel: {
formatter: function(value, index) {
return datePipe.transform(value);
}
}
},
yAxis: {
type: 'value',
min: this.yMin,
max: this.yMax,
//show: true
axisLabel: {
formatter: function(value, index) {
return leleuxNumPipe.transform(decimalPipe.transform(value, '1.2-2'))
}
}
},
dataZoom: [{
type: 'inside',
start: 0,
end: 100
}, {
start: 0,
end: 10,
handleIcon: 'M10.7,11.9v-1.3H9.3v1.3c-4.9,0.3-8.8,4.4-8.8,9.4c0,5,3.9,9.1,8.8,9.4v1.3h1.3v-1.3c4.9-0.3,8.8-4.4,8.8-9.4C19.5,16.3,15.6,12.2,10.7,11.9z M13.3,24.4H6.7V23h6.6V24.4z M13.3,19.6H6.7v-1.4h6.6V19.6z',
handleSize: '80%',
handleStyle: {
color: '#fff',
shadowBlur: 3,
shadowColor: 'rgba(0, 0, 0, 0.6)',
shadowOffsetX: 2,
shadowOffsetY: 2
}
}],
series: [{
name: 'Amount',
type: 'line',
smooth: true,
symbol: 'none',
sampling: 'average',
itemStyle: {
color: 'rgba(255, 152, 0, .6)', // 'rgb(255, 70, 131)'
},
areaStyle: {
color: 'rgba(255, 152, 0, 0.15)',
origin: 'start'
},
lineStyle: {
// width: 1,
color: 'rgba(255, 152, 0, .6)',
},
data: this.dataForChart.data,
}, ]
};
}
In the doc amchart -> https://www.amcharts.com/demos/line-graph/?theme=material#code
I don't understand how to I adapt the code from amcharts with my method loadchart() ?
Sorry, if I ask you a lot
I'm pretty new to amcharts myself, but I'll give it a try.
First, I'm assuming that if you have a chart that you want to replace, you can save a reference to it, that would look something like this:
private chart: am4charts.XYChart;
And when you create your chart for the first time, wherever it is, you can do somthing like this:
this.chart = am4core.createFromConfig(this.lineChartReturn, 'htmlIdReference', am4charts.XYChart);
If your setup looks something like that, then you should be able to just use the dispose function, and then create it again with the new lineChartReturn you generate in loadChart
loadChart() {
if (this.chart) {
this.chart.dispose();
}
// your current code to generate new this.lineChartReturn
// Then just create it again using the createFromConfig
this.chart = am4core.createFromConfig(this.lineChartReturn, 'htmlIdReference', am4charts.XYChart);
}
I am using organization Highcharts in which the parents on Level 2 are linking to same childs.
As you can see in the picture Nurrettin & Mohammad Sabir nodes linked to all same child nodes. However Nurretin is a parent of Cemil Ceylan & Mustafa while M.Sabir is a parent of remaining 7 child nodes.
But issue is why Nurretin node is linked to all?
Can somebody answer it ?
Organization Highcharts
The Code is Here
renderSubordinates=(data)=>{
console.log('data',data)
var array2D =[];
var arraySeries2D =[];
var arr = [];
var levelObj;
var arrSeries = [];
const rec = (val) => {
let name = val.from;
for(let j=0;j<val.to.length;j++){
let nameChild = val.to[j].from;
array2D.push([name,nameChild])
}
return [...array2D]
}
const createHC = (data) => {
arr.push(rec(data));
if(data.to.length){
data.to.map(x=>{
arr.push(rec(x));
createHC(x);
})
}
}
var data1 = data[0];
createHC(data1);
console.log('arr',arr.pop())
var dataArr = arr.pop()
var levelInd = arr;
var levelArr =levelInd.map((x,index)=>{
// console.log('index',index);
levelObj={
level: index,
color: {
linearGradient: { x1: 0, x2: 1, y1: 0, y2: 1 },
stops: [
[0, "#0376b0"],
[1, "#b4e5fe"],
],
},
dataLabels: {
color: 'white',
style:{
fontSize:'10px'
}
}}
return levelObj
})
const seriesRec = (val) => {
let obj = {id:val.name,title:val.title,name:val.name,image:val.image,
dataLabels: {
enabled: true,
style: {
fontSize: '10px'
}
}};
arraySeries2D.push(obj)
return [...arraySeries2D]
}
const seriesHC = (data) => {
arrSeries.push(seriesRec(data));
if(data.to.length){
data.to.map(x=>{
arrSeries.push(seriesRec(x));
seriesHC(x);
})
}
}
var data1 = data[0];
seriesHC(data1);
var dataSeriesArr = arrSeries.pop()
let obj: Highcharts.Options = {
chart: {
height: 1200,
inverted: true
},
title: {
text: ''
},
series: [{
type: 'organization',
name: '',
keys: ['from', 'to'],
data: dataArr,
levels: levelArr,
nodes:dataSeriesArr,
colorByPoint: false,
color: '#007ad0',
dataLabels: {
color: 'white'
},
borderColor: '',
borderRadius:20,
nodeWidth: 120,
className:'node-class'
}],
tooltip: {
outside: true
},
plotOptions: {
organization: {
linkColor: 'gray',
linkLineWidth:2,
}
},
exporting: {
allowHTML: true,
sourceWidth: 800,
sourceHeight: 600
}
};
return obj
}
I am using the pdfmake library in a project, but its execution takes about 2-3 min.
Is the library normally slow or do I need to improve the performance of my code ?
It is also possible that performances are impacted by the code being executed in chunks, but I am not sure I understand the role of chunks and why they are used.
var docDefinition = {
footer: function (currentPage, pageCount) {
return {
margin: [40, 0, 0, 0],
columns: [{
fontSize: 8,
text: [
{
text: 'Page ' + currentPage.toString() + ' / ' + pageCount,
}
],
}]
};
},
content: contentAry,
styles: {
clsHeader: {
fontSize: 12,
bold: true
},
clsSubHeader: {
fontSize: 10
},
clsTblHeader: {
fillColor: '#9e9e9e',
color: '#FFFFFF'
},
clsImage: {
margin: [0, 40, 0, 0]
},
clsTable: {
fontSize: 8
}
},
defaultStyle: {
alignment: 'justify'
}
}
var doc = printer.createPdfKitDocument(docDefinition);
var chunks = [];
doc.on('readable', function () {
var chunk;
while ((chunk = doc.read(9007199254740991)) !== null) {
chunks.push(chunk);
}
});
Is it possible to modify the chunk size?
I'm looking to simplify this code. Any way to it so? Spring MVC + Apex Charts
var d = /*[[${s0}]]*/ null`; <-- It is sent via the Spring Framework. Basically represents datetime(in millis) at `d[0]`, `d[3]`,... Temperature at `d[1]`, `d[4]`,... and Humidity at `d[2]`, `d[5]`,...
<script type="text/javascript" th:inline="javascript">
var d = /*[[${s0}]]*/ null;
var options = {
chart: {
type: 'area',
height: 300
},
series: [
{
name: 'Temperature',
data: [
[d[0], d[1]],
[d[3], d[4]],
[d[6], d[7]],
[d[9], d[10]],
[d[12], d[13]],
[d[15], d[16]],
[d[18], d[19]],
[d[21], d[22]],
[d[24], d[25]],
[d[27], d[28]],
[d[30], d[31]],
[d[33], d[34]],
[d[36], d[37]],
[d[39], d[40]],
[d[42], d[43]],
[d[45], d[46]],
[d[48], d[49]],
[d[51], d[52]],
[d[54], d[55]],
[d[57], d[58]],
[d[60], d[61]],
[d[63], d[64]],
[d[66], d[67]],
[d[69], d[70]]
]
},
{
name: "Humidity",
data: [
[d[0], d[2]],
[d[3], d[5]],
[d[6], d[8]],
[d[9], d[11]],
[d[12], d[14]],
[d[15], d[17]],
[d[18], d[20]],
[d[21], d[23]],
[d[24], d[26]],
[d[27], d[29]],
[d[30], d[32]],
[d[33], d[35]],
[d[36], d[38]],
[d[39], d[41]],
[d[42], d[44]],
[d[45], d[47]],
[d[48], d[50]],
[d[51], d[53]],
[d[54], d[56]],
[d[57], d[59]],
[d[60], d[62]],
[d[63], d[65]],
[d[66], d[68]],
[d[69], d[71]]
]
}
],
xaxis: {
type: 'datetime'
},
yaxis: [
{
axisTicks: {
show: true
},
axisBorder: {
show: true,
},
title: {
text: "Temperature"
}
}, {
min: 0,
max: 100,
opposite: true,
axisTicks: {
show: true
},
axisBorder: {
show: true,
},
title: {
text: "Humidity"
}
}
],
legend: {
position: 'top',
horizontalAlign: 'center'
},
tooltip: {
x: {
format: 'HH:mm dd/MM/yy'
},
}
}
var chart = new ApexCharts(document.querySelector("#chart0"), options);
chart.render();
</script>
I just need to simplify sending data via d[0], d[1] etc. Is there any kind of loop or anything else I can use?
You could take a function which takes the data and a pattern for the wanted elements and an offset for increment for the next row.
function mapByPattern(data, pattern, offset) {
var result = [], i = 0;
while (i < data.length) {
result.push(pattern.map(j => data[i + j]));
i += offset;
}
return result;
}
var data = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11],
result = { series: [
{ name: 'Temperature', data: mapByPattern(data, [0, 1], 3) },
{ name: "Humidity", data: mapByPattern(data, [0, 2], 3) }
]};
console.log(result);
Thank You, Nina. Code code didn't work exactly as i wanted but was so helpful to fix my own. Thanks alot! Here's some fixed code :)
var data = /*[[${s0}]]*/ null;
function mapByPattern(data, pattern, offset) {
var result = [], i = 0;
while (i < data.length) {
result.push(pattern.map(j => data[i + j]));
i += offset;
}
return result;
}
var options = {
chart: {
type: 'area',
height: 300
},
series: [
{
name: 'Temperature',
data: mapByPattern(data, [0, 1], 3)
},
{
name: "Humidity",
data: mapByPattern(data, [0, 2], 3)
}
],
xaxis: {
type: 'datetime'
},
yaxis: [
{
axisTicks: {
show: true
},
axisBorder: {
show: true,
},
title: {
text: "Temperature"
}
}, {
min: 0,
max: 100,
opposite: true,
axisTicks: {
show: true
},
axisBorder: {
show: true,
},
title: {
text: "Humidity"
}
}
],
legend: {
position: 'top',
horizontalAlign: 'center'
},
tooltip: {
x: {
format: 'HH:mm dd/MM/yy'
},
}
}
var chart = new ApexCharts(document.querySelector("#chart0"), options);
chart.render();
I'm working with pdfmake to generate pdf with javascript. I'm trying to build a table dynamically but not works ,this my attempt
$.ajax({
type: "POST",
url: myURL,
success:function(data){
/* data has a format like :
*[{"peaje":"Peaje 1","ruta":"Ruta 1","fechaCruce":"2014-10-18","hora":"15:42","valor":"5000"},{"peaje":"Peaje 1","ruta":"Ruta 1","fechaCruce":"2014-10-18","hora":"14:21","valor":"7000"},{"peaje":"Peaje 1","ruta":"Ruta 1","fechaCruce":"2014-09-19","hora":"11:58","valor":"17000"}]
*/
var peajes = JSON.parse( data );
var body = [];
var titulos = new Array( 'PEAJE', 'RUTA', 'FECHA CRUCE', 'HORA', 'VALOR' );
body.push( titulos );
for (key in peajes)
{
if (peajes.hasOwnProperty(key))
{
var peaje = peajes[key];
var fila = new Array();
fila.push( peaje.peaje.toString() );
fila.push( peaje.ruta.toString() );
fila.push( peaje.fechaCruce.toString() );
fila.push( peaje.hora.toString() );
fila.push( peaje.valor.toString() );
body.push(fila);
}
}
console.log( body );
var docDefinition = {
content: [
{
table: {
headerRows: 1,
widths: [ '*', 'auto', 100, '*' ],
body: body
}
}]
};//end docDefinition
pdfMake.createPdf(docDefinition).open();
}//end success
});
This is the example of the library http://pdfmake.org/#/gettingstarted
I don't know what am I doing wrong?
For multiple rows, here is an example
var externalDataRetrievedFromServer = [
{ name: 'Bartek', age: 34 },
{ name: 'John', age: 27 },
{ name: 'Elizabeth', age: 30 },
];
function buildTableBody(data, columns) {
var body = [];
body.push(columns);
data.forEach(function(row) {
var dataRow = [];
columns.forEach(function(column) {
dataRow.push(row[column].toString());
})
body.push(dataRow);
});
return body;
}
function table(data, columns) {
return {
table: {
headerRows: 1,
body: buildTableBody(data, columns)
}
};
}
var dd = {
content: [
{ text: 'Dynamic parts', style: 'header' },
table(externalDataRetrievedFromServer, ['name', 'age'])
]
}
You should make array of column names & values:
var column = [];
column.push({ text: 'A', style: 'tableHeader'});
column.push({ text: 'B', style: 'tableHeader'});
var value = [];
value.push({ text: 'Asda', style: 'tableHeader'});
value.push({ text: 'Bsa', style: 'tableHeader'});
When you make a table, you should do like this.
table: {
headerRows: 1,
body: [
column, value
]
}
For headers and rows are dynamic , we can define headers in an array and also rows and then join them into one following this example ( copy and paste into http://pdfmake.org/playground.html to see it in action ) :
// playground requires you to assign document definition to a variable called dd
var headers = {
fila_0:{
col_1:{ text: 'Faltas', style: 'tableHeader',rowSpan: 2, alignment: 'center',margin: [0, 8, 0, 0] },
col_2:{ text: 'Fecha', style: 'tableHeader',rowSpan: 2, alignment: 'center',margin: [0, 8, 0, 0] },
col_3:{ text: 'Descripción', style: 'tableHeader',rowSpan: 2, alignment: 'center',margin: [0, 8, 0, 0] },
col_4:{ text: 'Cita con acudientes', style: 'tableHeader',colSpan: 2, alignment: 'center' }
},
fila_1:{
col_1:{ text: 'Header 1', style: 'tableHeader', alignment: 'center' },
col_2:{ text: 'Header 2', style: 'tableHeader', alignment: 'center' },
col_3:{ text: 'Header 3', style: 'tableHeader', alignment: 'center' },
col_4:{ text: 'Citación', style: 'tableHeader', alignment: 'center' },
col_5:{ text: 'Cumplimiento', style: 'tableHeader', alignment: 'center'}
}
}
var rows = {
a: {
peaje: '1',
ruta: '2',
fechaCruce: '3',
hora: '4',
valor: '5'
},
b: {
peaje: '1',
ruta: '2',
fechaCruce: '3',
hora: '4',
valor: '5'
}
}
var body = [];
for (var key in headers){
if (headers.hasOwnProperty(key)){
var header = headers[key];
var row = new Array();
row.push( header.col_1 );
row.push( header.col_2 );
row.push( header.col_3 );
row.push( header.col_4 );
row.push( header.col_5 );
body.push(row);
}
}
for (var key in rows)
{
if (rows.hasOwnProperty(key))
{
var data = rows[key];
var row = new Array();
row.push( data.peaje.toString() );
row.push( data.ruta.toString() );
row.push( data.fechaCruce.toString() );
row.push( data.hora.toString() );
row.push( data.valor.toString() );
body.push(row);
}
}
var dd = {
pageMargins: [40,155,40,55],
pageOrientation: 'landscape',
header: function() {
return {
margin: 40,
columns: [
{
},
{ text:['Resumen disciplinario'],
alignment: 'left',bold:true,margin:[-405,80,0,0],fontSize: 24}
]
}
},
footer: function(currentPage, pageCount) {
return { text:'Pagina '+ currentPage.toString() + ' de ' + pageCount, alignment: 'center',margin:[0,30,0,0] };
},
content: [
//{ text: 'Tables', style: 'header' },
'\nEl estudiante AGRESOTH NEGRETE JORYETH TATIANA - 901 - TARDE tiene 1 actas, con 1 faltas acomuladas y a manera de resumen descritas a continuación:\n\n',
//{ text: 'A simple table (no headers, no width specified, no spans, no styling)', style: 'sta' },
//'The following table has nothing more than a body array',
{
style: 'tableExample',
table: {
widths: [ '*', '*', '*', '*', '*' ],
headerRows: 2,
// keepWithHeaderRows: 1,
body: body
}
}],
styles: {
header: {
fontSize: 28,
bold: true
},
subheader: {
fontSize: 15,
bold: true
},
quote: {
italics: true
},
small: {
fontSize: 8
},
sta: {
fontSize: 11,
bold: false,
alignment: 'justify'
}
}
}