SVG 'getBBox' fails in a jQueryUI tab - jquery-ui

I have a stand-alone SVG chart generator that works with all the major browsers. However, I've just added code to display the charts inside a jQuery UI tab, and the code has broken. Specifically, 'getBBox' now generally fails. It throws an exception in FF, works as expected in Opera, and gives the wrong answer in Chrome and Safari.
The difference between the old and new code is only, I think, in my understanding of what a 'document' is inside a tab. In the old stand-alone code, I could display a rectangle and get it's bbox as follows (in all browsers):
var svgDocument;
var svgNS = "http://www.w3.org/2000/svg";
...
if(window.svgDocument == null)
svgDocument = evt.target.ownerDocument;
...
var lbox = svgDocument.createElementNS(svgNS, "rect");
lbox.setAttributeNS(null, "x", 50);
lbox.setAttributeNS(null, "y", 50);
lbox.setAttributeNS(null, "width", 40);
lbox.setAttributeNS(null, "height", 40);
lbox.setAttributeNS(null, "stroke", "#E810D6");
lbox.setAttributeNS(null, "stroke-width", 2);
lbox.setAttributeNS(null, "fill-opacity", 1);
lbox.setAttributeNS(null, "stroke-opacity", 1);
lbox.setAttributeNS(null, "stroke-dasharray", 0);
svgDocument.documentElement.appendChild(lbox); // displays the box
var bbox = lbox.getBBox(); // gets the box bounds
The problem is that, when I try to display inside a tab, it's not obvious what svgDocument should be. This is my current code:
var svgDocument = document;
var svgNS = "http://www.w3.org/2000/svg";
var svgRoot;
...
// handle jQuery UI tabs as follows:
var top, svg, chart;
top = $(ui.panel).get(0);
svg = svgDocument.createElementNS(svgNS, "svg");
chart = "chart" + "-" + ui.panel.id;
svg.setAttributeNS(null, "id", chart);
top.appendChild(svg);
svgRoot = svgDocument.getElementById(chart);
...
// SVG draw is identical, except that svgDocument.documentElement is now svgRoot:
var lbox = svgDocument.createElementNS(svgNS, "rect");
lbox.setAttributeNS(null, "x", 50);
lbox.setAttributeNS(null, "y", 50);
lbox.setAttributeNS(null, "width", 40);
lbox.setAttributeNS(null, "height", 40);
lbox.setAttributeNS(null, "stroke", "#E810D6");
lbox.setAttributeNS(null, "stroke-width", 2);
lbox.setAttributeNS(null, "fill-opacity", 1);
lbox.setAttributeNS(null, "stroke-opacity", 1);
lbox.setAttributeNS(null, "stroke-dasharray", 0);
svgRoot.appendChild(lbox);
var bbox = lbox.getBBox();
The new code works "correctly" in Opera. FF, Chrome, and Safari display the rectangle correctly in the new tab, but get the bbox calculation wrong.
Any idea what I'm doing wrong here? Thanks.
[this is probably the same issue as Doing Ajax updates in SVG breaks getBBox, is there a workaround?, but there were no answers on that].
EDIT
I failed to mention that I'm rendering into a hidden tab, which is only displayed when the chart completes. Googling the FF exception code (in the comment below) indicates that there's some issue with getBBox when the element is not displayed. However, I don't understand this. I routinely use getBBox with visibility:hidden to size complex elements before displaying them, on all browsers (when I'm not using tabs). Besides, the rectangle in the example does actually render, as I can see it when the tab becomes visible, so shouldn't getBBox should also work?

Fixed - the answer is actually in the tabs documentation. Whoops.
From http://docs.jquery.com/UI/Tabs#...my_slider.2C_Google_Map.2C_sIFR_etc._not_work_when_placed_in_a_hidden_.28inactive.29_tab.3F
Any component that requires some dimensional computation for its initialization won't work in a hidden tab, because the tab panel itself is hidden via display: none so that any elements inside won't report their actual width and height (0 in most browsers).
There's an easy workaround. Use the off-left technique for hiding inactive tab panels. E.g. in your style sheet replace the rule for the class selector ".ui-tabs .ui-tabs-hide" with
.ui-tabs .ui-tabs-hide {
position: absolute;
left: -10000px;
}

Related

jsPDF - practical example for a fit-to-width shopping cart HTML page?

I'm trying to implement jsPDF pdf saving in a Vue app and I'm finding it overwhelming and nearly impossible to affect any visual change in the output.
The current results are: gigantic text and huge images.
Is there a way to get it to respect any of my CSS, #mediap print or otherwise, particularly for div borders?
The docs are very deep so I'd love an example of how to take a div-based table-like layout—e.g. a shopping cart—and fit it to width. I'd also love tips on avoid content bleeding across page breaks.
I've tried doc.setFont, doc.setFontSize and other methods and nothing changes the output.
this.doc.html(document.getElementById("pdfList"), {
callback: function (doc) {
doc.save("Cycad Inventory");
},
x: 40,
y: 40,
});
exportPDF = () => {
const unit = "pt";
const size = "A4"; // Use A1, A2, A3 or A4
const orientation = "portrait"; // portrait or landscape
const marginLeft = 30;
const doc = new jsPDF(orientation, unit, size);
doc.setFontSize(14);
const title = "Report";
const headers = [["Time", "Source", "Destination", "Protocol", "Length"]];
const data = this.state.ipData.map(elt => [elt.time, elt.source, elt.destination, elt.protocol, elt.length]);
let content = {
startY: 50,
head: headers,
body: data,
}
doc.text(title, marginLeft, 40);
doc.autoTable(content);
doc.save(this.state.ipData.fileName);
}
This is a code I used in reactJS. I hope this will help you to do changes to your code. Here I have declared the height, width, font sizes margins.

jsPDF fromHTML image (to large) and page margin-bottom (not working)

I just implemented jspdf in a page, where users work with an editor and then want to be able to print the result. When I tested some example content, I run in to that problem the images are not displayed in there orignial size. They are scaled/displayed so large, that they are lager then the width of the page.
Also I had a problem with the page height/margin-bottom. If the content is to large for a page, then there will be no margin at the bottom of the page. So if for what ever reason an elemtent seems to be too large for a page, this element goes until the end of the page.
I googled a lod, but I couldn't find a solution to avoid that too large elements go until the border of the page.
And I also could not find something, for how to set an image to it's original or max-size when I don't know the content of the page.
Here is js code and an example page: jsfiddle-example
$(".printPdf").click(function () {
var pdfdoc = new jsPDF("portrait");
var getPageTitle = $("#page-title");
var pageTitle = (getPageTitle != "")? getPageTitle : "Document";
var contentClass = ".page-content";
var $pageContent = $(contentClass);
var margins = {
top: 10,
bottom: 20,
left: 15,
width: 180
};
pdfdoc.setDisplayMode('original');
pdfdoc.fromHTML($pageContent.get(0), margins.left, margins.top, {
"width": margins.width,
"text-align": "unset"
}, function () { pdfdoc.save(pdfdoc.pageTitle + ".pdf") }, margins);
});

How to align text in center using jspdf

How to align text center using jsPDF.
var doc = new jsPDF();
doc.text(40, 250, 'Hi How are you');
If you are using the latest version (1.1.135) the api has changed some for the text function. It now reads as:
API.text = function(text, x, y, flags, angle, align);
If you don't need to use the flags or angle though, you can simply use:
var doc = new jsPDF();
doc.text('Hi How are you', 40, 250, 'center');
Keep in mind that the center call uses the x parameter now as the center of the text string, and not the left most border as it does when rendering left aligned.
Link to source
Edit:
Alternately you can calculate the proper x offset to just use the text function normally like so:
var text = "Hi How are you",
xOffset = (doc.internal.pageSize.width / 2) - (doc.getStringUnitWidth(text) * doc.internal.getFontSize() / 2);
doc.text(text, xOffset, 250);
Angular 6.
Footer align to horizontally center
var doc = new jsPDF();
var pageHeight = doc.internal.pageSize.height || doc.internal.pageSize.getHeight();
var pageWidth = doc.internal.pageSize.width || doc.internal.pageSize.getWidth();
// FOOTER
let str = "Your footer text";
doc.setTextColor(100);
doc.setFontSize(10);
doc.text(str, pageWidth / 2, pageHeight - 10, {align: 'center'});
doc.save("example.pdf");
Above answers didn't work for me, I ended up doing the following to center the text
let textX = (doc.internal.pageSize.getWidth() - doc.getTextWidth(text))/2
doc.text(text, textX, textY);
this worked:
var xOffset = doc.internal.pageSize.width / 2
doc.text('hello world', xOffset, 8, {align: 'center'});
2022: this works assuming your page width is 210 (default A4).
doc.text("This is centred text.", 105, 80, null, null, "center");
Here's a link to their live demo per the README:
http://raw.githack.com/MrRio/jsPDF/master/index.html
2022: I'm finding that JSPDF is buggy. It took a while to figure out how to install the advertised 'runs in a browser' implementation for a PHP app instead of a JS front end framework. There's a line that's required window.jsPDF = window.jspdf.jsPDF; that isn't mentioned anywhere in the documentation, I had to go through a downloaded example piece by piece to find it. Now I'm finding that the center text function doesn't work. In 2 different local environments and a JSFiddle, on multiple browsers, it sends the text off the left side of the page when the align:center option is implemented. While the above solution works, it breaks down if text is longer than one line, which, incidentally, is another out of the box bug - the text runs out of the document instead of wrapping, and there is no wrap option. So, it seems after all these hours I'm out of luck and I'll have to go a different route. Plugin is not maintained and this should be noted in documentation. Recommend to not waste your time.
This works somewhat, but isn't precise, if you know please tell me why.
I calculate the width of my text in order to center it myself.
For this, I used the getTextDimensions() method on my jsPDF object
var pdf = new jsPDF({
orientation : 'p',
unit: 'px',
format: [500, 750],
putOnlyUsedFonts:true
});
var textDimensions = pdf.getTextDimensions('MyText');
You can now use textDimensions.w to get text-width and textDimensions.h for height
Then use this to center your text.
var textWidth = textDimensions.w;
pdf.text('MyText', (pdfWidth / 2) - (textWidth / 2), 100);
BUT: You need to know your PDF's width to do this.
I 'solved' this by defining height and width myself, but you can easily find height and width of common formats online.
Like A4: 210mm*297mm.
Just remember to set unit: 'mm' when creating your jsPDF.
var doc = new jsPDF();
// set midPage for variable use
var midPage = doc.internal.pageSize.getWidth()/2
// Default is 210 mm so default midway by value is 105
doc.setFontSize(40);
doc.text("Octonyan loves jsPDF", 105, 15, null, null, "center");
// Better to use a variable "midPage" (from above)
doc.setFontSize(30);
doc.text("Centered (USA), Centred (UK)", midPage , 30, null, null, "center");

How do I adjust where the calendar appears for the datePicker in the Grails UI plugin without the position changing when the page is resized?

I wanted to change where the calendar appeared for the datePicker portion of the Grails UI plugin. After a bit of digging, I found it in the InputTagLib.groovy file:
out << """
YAHOO.util.Event.on("${showButtonId}", "click", function() {
var buttonRegion = YAHOO.util.Dom.getRegion('${showButtonId}');
var buttonHeight = buttonRegion.bottom - buttonRegion.top;
var buttonWidth = buttonRegion.right - buttonRegion.left;
var xy = YAHOO.util.Dom.getXY('${showButtonId}');
var newXY = [xy[0] + buttonWidth, xy[1] + buttonHeight];
YAHOO.util.Dom.setXY('${id}_calContainer_c', newXY);
GRAILSUI.${jsid}Panel.show();
if (YAHOO.env.ua.opera && document.documentElement) {
// Opera needs to force a repaint
document.documentElement.className += "";
}
});"""
The position is set on this line:
YAHOO.util.Dom.setXY('${id}_calContainer_c', newXY);
The YAHOO.util.Dom.setXY() method takes two arguments.
The first argument of the setXY method is either the ID of an HTMLElement, or an actual HTMLElement object. The second argument is an array containing two values: [x, y] where x is the distance from the left edge of the document, and y is the distance from the top edge of the document.
Source: http://developer.yahoo.com/yui/examples/dom/setxy.html
So I can easily adjust where the calendar appears by changing the values in the newXY variable that is passed to the setXY method and it works fine.
However, if I resize the page, the calendar positions its top left corner directly under the showButton's bottom left corner. What is it that makes the repositioning happen when the page is resized and how can I resolve it?
EDIT : A little more info:
This happens in the most recent versions of IE, Firefox, and Chrome
If I resize the page and then click the button to show the calendar, the calendar shows in the expected place but it will reposition if I resize the page again once the calendar is open

jQuery horizontal ui scroll not calculating outerWidth in Chrome

I'm having a problems to make a jQuery horizontal ui scrollbar work in Chrome. It works just fine in FireFox and even IE, but in Chrome I simply can't make it calculate the correct width of my "content" area. The "container" has a 920px fixed width, so no problem with that, but my "content" is, "on this page", exactly 4983px wide but when calculating with outerWidth() and even outerWidth(true), it will return a nonsense value that's a lot smaller than it should be!
Here's the link to the page I'm working on.
And here's the code I have until now. It's a mess because I'm still working and doing some tests...
var container = $('.gallery');
var content = $('.content', container);
var itemsWidth = content.outerWidth(true) - container.width();
var width = 0;
$('figure').each(function() {
width += $(this).width();
});
console.log('I dont know if I chose width: ' + width);
console.log('Or if I chose itemsWidth: ' + itemsWidth);
console.log('Actually, none of them is working on Chrome/webkit browsers');
$('.slider', container).slider({
min: 0,
max: itemsWidth - 20,
stop: function(event, ui){
content.animate({ 'margin-left' : ui.value * -1}, 500);
},
slide: function(event, ui){
console.log(ui.value);
content.css('margin-left',ui.value * -1);
}
});
Notice that I'm trying to calculate the width value in two different ways: itemsWidth (var) and width (var). None of them work. Strange thing is... if you keep refreshing the browser (Chrome), it will eventually grab the correct width of the "content", but it's like once in every 10–15 tries =\
It seems to be a Chrome/Webkit bug, but I have no idea about how to solve that!
Thanks for your time and help!

Resources