Word wrap in generated PDF (using jsPDF)? - jspdf

what I'm doing is using jsPDF to create a PDF of the graph I generated. However, I am not sure how to wrap the title (added by using the text() function). The length of the title will vary from graph to graph. Currently, my titles are running off the page. Any help would be appreciated!
This is the code i have so far:
var doc = new jsPDF();
doc.setFontSize(18);
doc.text(15, 15, reportTitle);
doc.addImage(outputURL, 'JPEG', 15, 40, 180, 100);
doc.save(reportTitle);
Nothing to keep the reportTitle from running off the page

Okay I've solved this. I used the jsPDF function, splitTextToSize(text, maxlen, options). This function returns an array of strings. Fortunately, the jsPDF text() function, which is used to write to the document, accepts both strings and arrays of strings.
var splitTitle = doc.splitTextToSize(reportTitle, 180);
doc.text(15, 20, splitTitle);

You can just use the optional argument maxWidth from the text function.
doc.text(15, 15, reportTitle, { maxWidth: 40 });
That will split the text once it reaches the maxWidth and start on the next line.

Auto-paging and text wrap issue in JSPDF can achieve with following code
var splitTitle = doc.splitTextToSize($('#textarea').val(), 270);
var pageHeight = doc.internal.pageSize.height;
doc.setFontType("normal");
doc.setFontSize("11");
var y = 7;
for (var i = 0; i < splitTitle.length; i++) {
if (y > 280) {
y = 10;
doc.addPage();
}
doc.text(15, y, splitTitle[i]);
y = y + 7;
}
doc.save('my.pdf');

To wrap long string of text to page use this code:
var line = 25 // Line height to start text at
var lineHeight = 5
var leftMargin = 20
var wrapWidth = 180
var longString = 'Long text string goes here'
var splitText = doc.splitTextToSize(longString, wrapWidth)
for (var i = 0, length = splitText.length; i < length; i++) {
// loop thru each line and increase
doc.text(splitText[i], leftMargin, line)
line = lineHeight + line
}

If you need to dynamically add new lines you want to access the array returned by doc.splitTextToSize and then add more vertical space as you go through each line:
var y = 0, lengthOfPage = 500, text = [a bunch of text elements];
//looping thru each text item
for(var i = 0, textlength = text.length ; i < textlength ; i++) {
var splitTitle = doc.splitTextToSize(text[i], lengthOfPage);
//loop thru each line and output while increasing the vertical space
for(var c = 0, stlength = splitTitle.length ; c < stlength ; c++){
doc.text(y, 20, splitTitle[c]);
y = y + 10;
}
}

Working Helper function
Here's a complete helper function based on the answers by #KB1788 and #user3749946:
It includes line wrap, page wrap, and some styling control:
(Gist available here)
function addWrappedText({text, textWidth, doc, fontSize = 10, fontType = 'normal', lineSpacing = 7, xPosition = 10, initialYPosition = 10, pageWrapInitialYPosition = 10}) {
var textLines = doc.splitTextToSize(text, textWidth); // Split the text into lines
var pageHeight = doc.internal.pageSize.height; // Get page height, well use this for auto-paging
doc.setFontType(fontType);
doc.setFontSize(fontSize);
var cursorY = initialYPosition;
textLines.forEach(lineText => {
if (cursorY > pageHeight) { // Auto-paging
doc.addPage();
cursorY = pageWrapInitialYPosition;
}
doc.text(xPosition, cursorY, lineText);
cursorY += lineSpacing;
})
}
Usage
// All values are jsPDF global units (default unit type is `px`)
const doc = new jsPDF();
addWrappedText({
text: "'Twas brillig, and the slithy toves...", // Put a really long string here
textWidth: 220,
doc,
// Optional
fontSize: '12',
fontType: 'normal',
lineSpacing: 7, // Space between lines
xPosition: 10, // Text offset from left of document
initialYPosition: 30, // Initial offset from top of document; set based on prior objects in document
pageWrapInitialYPosition: 10 // Initial offset from top of document when page-wrapping
});

When we use linebreak in jsPDF we get an error stating b.match is not defined, to solve this error just unminify the js and replace b.match with String(b).match and u will get this error twice just replace both and then we get c.split is not defined just do the same in this case replace it with String(c).match and we are done. Now you can see line breaks in you pdf. Thank you

Related

i am new in action script. trying to split a sentence into words and show them in sprite.and also need which sprite i clicked

I am trying to split a sentence in to words and show them into sprite()
var sentence:String = "My name is Subhadip.";
var txt:Array = new Array();
var splittedSentence:Array = sentence.split(" ");
var myArraySprites:Array = new Array();
var myTextImage:Array = new Array();
var div = 40;
for (var i:int = 0; i < splittedSentence.length; i++)
{
var v = 300 - (div * i);
//...
txt[i] = new TextField();
txt[i].autoSize = TextFieldAutoSize.CENTER;
txt[i].text = splittedSentence[i];
var format1:TextFormat = new TextFormat();
format1.size = 24;
txt[i].setTextFormat(format1);
trace(txt[i]);
myArraySprites[i] = new Sprite();
myArraySprites[i].graphics.lineStyle(1, 0x000000, 1);
myArraySprites[i].buttonMode = true;
myArraySprites[i].graphics.beginFill( 0xfffffff );
myArraySprites[i].graphics.drawRect(50, v, 150, div);
myTextImage[i] = new BitmapData(100,v,true,0xffffff);
myTextImage[i].draw(txt[i]);
trace(myTextImage[i]);
//myArraySprites[i].graphics.beginBitmapFill(myTextImage[i]);
//myArraySprites[i].graphics.endFill();
myArraySprites[i].addChild(new Bitmap(myTextImage[i]));
addChild(myArraySprites[i]);
myArraySprites[i].addEventListener(MouseEvent.CLICK, removeThis);
}
function removeThis(e:MouseEvent):void
{
var clickTarget:int = myArraySprites.indexOf(e.currentTarget);
trace("Clicked sprite (id): " + clickTarget);
}
[enter image description here][1]
trying to split a sentence into words and show them in sprite.and also need which sprite i clicked
[1]: https://i.stack.imgur.com/RzHUQ.png
The problem is that you're dealing with screen positions in two different places:
myArraySprites[i].graphics.drawRect(50, v, 150, div);
and
myTextImage[i] = new BitmapData(100,v,true,0xffffff);
Think of it in a more object-oriented way. Each Sprite instance you want to be clickable is a single object. As such it also holds the main properties e.g. the on-screen position determined by it's x and y properties.
Each of those sprites have equal width & height, so make this a global property.
var wid:Number = 150;
var hei:Number = 40;
If we look at your .png, we can see that you want to have a single word centered inside such a sprite. To make things easier, we can use the .align property of the TextFormat class, set it to "center" and make each TextField 150 x 40 by using the wid and hei properties.
Finally each sprite's vertical screen position is shifted up by hei == 40 pixels. So let's determine a start position:
var yPos:Number = 300;
assign it to a sprite instance:
myArraySprites[i].x=50;
myArraySprites[i].y=yPos;
and decrement it by hei inside the for-loop.
Everything put together:
var yPos:Number = 300;
var wid:Number = 150;
var hei:Number = 40;
var sentence:String = "My name is Subhadip.";
var txt:TextField = new TextField();
txt.width = wid;
txt.height = hei;
var format1:TextFormat = new TextFormat();
format1.size = 24;
format1.align = "center";
var splittedSentence:Array = sentence.split(" ");
var myTextImage:Array = new Array();
for (var i:int = 0; i<splittedSentence.length; i++)
{
txt.text = splittedSentence[i];
txt.setTextFormat(format1);
myArraySprites[i] = new Sprite();
myArraySprites[i].x = 50;
myArraySprites[i].y = yPos;
myArraySprites[i].graphics.lineStyle(1, 0x000000, 1);
myArraySprites[i].buttonMode = true;
myArraySprites[i].graphics.beginFill(0xfffffff);
myArraySprites[i].graphics.drawRect(0, 0, wid, hei);
myTextImage[i] = new BitmapData(wid, hei, true, 0xffffff);
myTextImage[i].draw(txt);
myArraySprites[i].addChild(new Bitmap(myTextImage[i]));
addChild(myArraySprites[i]);
myArraySprites[i].addEventListener(MouseEvent.CLICK, removeThis);
yPos -= hei;
}

In Dart, in a sequential list of integers, how do I find a number between the items?

In a list of sequential integers, is there a simple way to locate where another integer would be placed (between two of the list members)?
main() {
var myList = new List();
myList.addAll([0, 4, 10, 20, 33, 45, 55, 64]);
int setStart;
int currentPosition;
currentPosition = 12;
// if currentPosition is greater than or equal to myList[fooPosition]
// but less than myList[barPosition]
// setStart = myList[foo]
}
So since the currentPosition is 12, the correct answer for setStart would be 10.
Try checking out package:collection's binarySearch.
Ok, figured it out myself. Pretty simple really. I just needed to add another variable (x) to indicate the list position of the upper number:
for (var i = 0; i < myList.length; i++) {
var x = i + 1;
if (currentPosition >= myList[i] && currentPosition < myList [x]) {
setStart = myList[i];
};
};

Ipad not showing canvas lines properly

I have created small script for vision testing. I posted part of it on https://jsfiddle.net/jaka_87/bpadyanh/
The theory behind it: Canvas is filled with pattern.In each pattern there are 3 lines drawn. Two black ones with the white one in the middle. The white line has 2*with of one black line. When width of this lines is so small that our eye cant distinguish between these three we see only gray background (even thou the lines are still there).
I tested it on few computers. On most of them it works well (thou i saw some strange patterns on some of the older ones - one column of vertical white lines then 2-3 dark column then white one....) I was assuming that it has do do with the display/graphic card or something similar. I tested it on some some mobile devices. It works fine on my Nexus 7 and Moto G, but not on my Transformer Prime pad (strange pattern like described before - for which again I blame the tablet).
It looks absolutely horrible (by far the worst from all tested) on my Ipad and my friends Iphone. I was expecting the best result there since they are known for very good screens but the results are horrible. When the lines are wide enough its OK but when they get narrower they are merged together to one either black or white line - not shown separately.
Is there any way to fix that so it would work on iOS ??
var povecava2 = "0.04226744186046511";
var izmerjeno =1;
var razmak =3.5;
var contrast = 1;
var canvas = document.getElementById('canvas1');
var ctx = canvas.getContext('2d');
// širina canvasa
ctx.canvas.width = window.innerWidth-23;
ctx.canvas.height = window.innerHeight-70;
var sirinaopto=Math.round(200*izmerjeno*povecava2*razmak);
if(sirinaopto & 1){sirinaopto=sirinaopto;}else{ sirinaopto=sirinaopto+1;} // če je širina optotipa soda ali liha
var enota4 =((0.19892970392*130*izmerjeno*povecava2)/4).toFixed(2); // 1 kotna minuta
var center= Math.round((ctx.canvas.width-(sirinaopto))/2);
// kolkrat gre v višino
var kolkratgre = Math.floor(ctx.canvas.height/(sirinaopto));
var visina2= sirinaopto*kolkratgre;
// kolkrat gre v širino
var kolkratgrehor = Math.ceil(ctx.canvas.width/sirinaopto); if(kolkratgrehor % 2 == 0) { var kolkratgrehor=kolkratgrehor-1; }
var zacetek = (ctx.canvas.width-(kolkratgrehor*sirinaopto))/2;
ctx.rect(0,0,ctx.canvas.width,ctx.canvas.height);
ctx.fillStyle="rgb(140,140,140)";
ctx.fill();
// 90 stopinj
var canvasPattern0 = document.createElement("canvas");
canvasPattern0.width = sirinaopto;
canvasPattern0.height = sirinaopto;
var contextPattern0 = canvasPattern0.getContext("2d");
contextPattern0.mozImageSmoothingEnabled = false;
contextPattern0.imageSmoothingEnabled = false;
contextPattern0.translate((canvasPattern0.width/2)-(enota4*2),(canvasPattern0.width/2)-(10*enota4));
contextPattern0.beginPath();
contextPattern0.globalAlpha = contrast;
contextPattern0.moveTo(enota4/2,0);
contextPattern0.lineTo(enota4/2,20*enota4);
contextPattern0.lineWidth=enota4;
contextPattern0.strokeStyle = 'black';
contextPattern0.stroke();
contextPattern0.closePath();
contextPattern0.beginPath();
contextPattern0.globalAlpha = contrast;
contextPattern0.moveTo(enota4*2,0);
contextPattern0.lineTo(enota4*2,20*enota4);
contextPattern0.lineWidth=enota4*2;
contextPattern0.strokeStyle = 'white';
contextPattern0.stroke();
contextPattern0.closePath();
contextPattern0.beginPath();
contextPattern0.globalAlpha = contrast;
contextPattern0.moveTo(enota4*3.5,0);
contextPattern0.lineTo(enota4*3.5,20*enota4);
contextPattern0.lineWidth=enota4;
contextPattern0.strokeStyle = 'black';
contextPattern0.stroke();
contextPattern0.closePath();
// 0 stopinj
var canvasPattern1 = document.createElement("canvas");
canvasPattern1.width = sirinaopto;canvasPattern1.height = sirinaopto;
var contextPattern1 = canvasPattern1.getContext("2d");
contextPattern1.translate(sirinaopto/2, sirinaopto/2);
contextPattern1.rotate(90*Math.PI/180);
contextPattern1.drawImage(canvasPattern0, sirinaopto*(-0.5), sirinaopto*(-0.5));
contextPattern1.save();
var imagesLoaded = [];
imagesLoaded.push(canvasPattern0);
imagesLoaded.push(canvasPattern1);
var randomPattern = function(imgWidth, imgHeight, areaWidth, areaHeight) {
// either set a defined width/height for our images, or use the first one's
imgWidth = sirinaopto;
imgHeight = sirinaopto;
// restrict the randmoness size by using an areaWidth/Height
areaWidth = ctx.canvas.width;
areaHeight = visina2;
// create a buffer canvas
var patternCanvas = canvas.cloneNode(true);
var patternCtx = patternCanvas.getContext('2d');
patternCanvas.width = areaWidth;
patternCanvas.height = areaHeight;
// var xloops = Math.ceil(areaWidth / imgWidth);
var xloops = Math.ceil(areaWidth / imgWidth); if(xloops % 2 == 0) { var xloops=xloops-1; }
var yloops = Math.ceil(areaHeight / imgHeight);
//alert(xloops);
for (var xpos = 0; xpos < xloops; xpos++) {
for (var ypos = 0; ypos < yloops; ypos++) {
var img = imagesLoaded[Math.floor(Math.random() * imagesLoaded.length)];
patternCtx.drawImage(img, (xpos * imgWidth)+zacetek, (ypos * imgHeight), imgWidth, imgHeight);
}
}
// create a pattern from this randomly created image
return patternCtx.createPattern(patternCanvas, 'repeat');
}
var draw = function() {
//create the random pattern (should be moved out of the draw)
var patt = randomPattern(sirinaopto,sirinaopto);
ctx.fillStyle = patt;
ctx.fillRect(0,0,ctx.canvas.width, visina2)
};
draw();

Printing in Openlayers 3 (pdf)

I have made a printing tools for openlayers 3 which prints in PDF format. Here is my code to print in pdf.
var dims = {
a0: [1189, 841],
a1: [841, 594],
a2: [594, 420],
a3: [420, 297],
a4: [297, 210],
a5: [210, 148]
};
var exportElement = document.getElementById('export-pdf');
exportElement.addEventListener('click', function(e) {
if (exportElement.className.indexOf('disabled') > -1) {
return;
}
exportElement.className += ' disabled';
var format = document.getElementById('format').value;
var resolution = document.getElementById('resolution').value;
var buttonLabelElement = document.getElementById('button-label');
var label = buttonLabelElement.innerText;
var dim = dims[format];
var width = Math.round(dim[0] * resolution / 25.4);
var height = Math.round(dim[1] * resolution / 25.4);
var size = /** #type {ol.Size} */ (map.getSize());
var extent = map.getView().calculateExtent(size);
map.once('postcompose', function(event) {
//var tileQueue = map.getTileQueue();
// To prevent potential unexpected division-by-zero
// behaviour, tileTotalCount must be larger than 0.
//var tileTotalCount = tileQueue.getCount() || 1;
var interval;
interval = setInterval(function() {
//var tileCount = tileQueue.getCount();
//var ratio = 1 - tileCount / tileTotalCount;
//buttonLabelElement.innerText = ' ' + (100 * ratio).toFixed(1) + '%';
//if (ratio == 1 && !tileQueue.getTilesLoading()) {
clearInterval(interval);
buttonLabelElement.innerText = label;
var canvas = event.context.canvas;
var data = canvas.toDataURL('image/jpeg');
var pdf = new jsPDF('landscape', undefined, format);
pdf.addImage(data, 'JPEG', 0, 0, dim[0], dim[1]);
pdf.save('map.pdf');
map.setSize(size);
map.getView().fitExtent(extent, size);
map.renderSync();
exportElement.className =
exportElement.className.replace(' disabled', '');
// }
}, 100);
});
map.setSize([width, height]);
map.getView().fitExtent(extent, /** #type {ol.Size} */ (map.getSize()));
map.renderSync();
}, false);
I can print in PDF when I have only OSM Layer but when I add local layers from my geoserver I can't print anything and the whole application is freezed.
Can anyone tell me what am I doing wrong here?
I am using jspdf to print pdf.
AJ
Your problem is that you load imagery from other domains, and haven't configured them for CORS. See https://developer.mozilla.org/en-US/docs/Web/HTML/CORS_enabled_image for a description on cross origin image use.
In order to get data out of the canvas, all images put into it must be from the same domain or transmitted with the appropriate Access-Control-Allow-Origin header.
I would investigate how to set up your server to serve the map imagery with those headers. You should also take a look at the crossOrigin option on your ol3 sources.
There is few solutions for CORS.
Very simple solution is to proxy OSM requests through your backend server (user <-> backend <-> OSM), but then we have little more server load.

achartengine x-axis labels shift compared to values

I noticed that the Xlabels of my Timechart are out of sync with the X-
values. The points should be right above the labels. On the left side
it OK, but it shifts towards the right. I don't know how to solve this
What i get:
http://tinypic.com/r/2uqj905/7
How it should be like:
http://www.achartengine.org/dimages/average_temperature.png
I tried a line chart or converted the date to a double but that had no
effect.
Any help would be be nice.
regards, Christian
This is some of my code:
XYMultipleSeriesRenderer renderer = buildRenderer(colors, styles);
renderer.setPointSize(5.5f);
renderer.setZoomEnabled(false, false);
renderer.setMarginsColor(Color.parseColor("#00FF0000"));
renderer.setAxisTitleTextSize(16);
renderer.setYLabelsAlign(Align.RIGHT);
renderer.setLabelsTextSize(15);
renderer.setLegendTextSize(15);
renderer.setMargins(new int[] { 10, 65, 18, 10 });
int length = renderer.getSeriesRendererCount();
for (int i = 0; i < length; i++) {
XYSeriesRenderer seriesRenderer = (XYSeriesRenderer) renderer
.getSeriesRendererAt(i);
seriesRenderer.setFillPoints(true);
seriesRenderer.setLineWidth(4.0f); // dikte van lijn
}
MinMax minMax = determineMinMax(targetSteps, samples);
setChartSettings(renderer, context.getString(R.string.graph_title),
dateLabelOnScreenType(type),
context.getString(R.string.graph_y_axis), minMax.minX,
minMax.maxX, minY, maxY, Color.WHITE,
Color.BLACK);
renderer.setXLabels(7);
renderer.setYLabels(0);
renderer.setDisplayChartValues(false);
renderer.setShowGrid(true);
renderer.setPanEnabled(false, false);
// set the data
String[] titles = new String[] {
context.getString(R.string.graph_legend_target),
context.getString(R.string.graph_legend_actual) };
XYMultipleSeriesDataset dataSet = buildDateDataset(titles,
xSeriesList,
ySeriesList);
TimeChart chart = new TimeChart(dataSet, renderer);
chart.setDateFormat(dateFormatOnSampleType(type));
GraphicalView gview = new GraphicalView(context, chart);
renderer.setYLabelsAlign(Align.RIGHT);
Change to:
renderer.setYLabelsAlign(Align.CENTER);
The solution to this was added recently. Just call:
renderer.setXRoundedLabels(false)

Resources