I am trying to make a PDF of a reasonable amount of text and graphs in my html using html2pdf. So far so good, the PDF gets made and it looks fine. It is about 20 pages.
However, multiple graphs are missing. Some relevant information:
Most of the missing graphs are at the end of the document. However, the final graph is rendered, so it is not an explicit cut off at the end
The text accompanying the graphs is there, but the graph itself is not
The missing graphs are all of the same type. Other graphs of this type are rendered and look fine. It does not seem to be a problem with the type
If I reduce the scale on the html2canvas configuration to about 0.8, every graph gets rendered (but of course quality is reduced). I'd like the scale to be 2.
The fact that scale influences whether they are rendered or not, gives me the idea that something like timing / timeouts are a problem here. Larger scale means obviously longer rendering time, but it does not seem to wait for it to be done. Or something like that.
Below the majority of the code that makes the PDF.
The onClone is necessary for the graphs to be rendered correctly. If it is removed, the problem described above still occurs (but the graphs that áre rendered are ugly).
const options = {
filename: 'test.pdf',
margin: [15, 0, 15, 0],
image: { type: 'jpeg', quality: 1 }
html2canvas: {
scale: 2,
scrollY: 0,
onclone: (element) => {
const svgElements = element.body.querySelectorAll('svg');
Array.from(svgElements).forEach((item: any) => {
item.setAttribute('width', item.getBoundingClientRect().width.toString());
item.setAttribute('height', item.getBoundingClientRect().height.toString());
item.style.width = null;
item.style.height = null;
});
}
},
jsPDF: { orientation: 'portrait', format: 'a4' }
};
setTimeout(() => {
const pdfElement = document.getElementById('contentToConvert');
html2pdf().from(pdfElement).set(options).save()
.catch((err) => this.errorHandlerService.handleError(err))
}, 100);
It sounds like you may be exceeding the maximum canvas size on your browser. This varies by browser (and browser version). Try the demo from here to check out your browser's limit. If you can find 2 browsers with different limits (on my desktop, Safari and Chrome have the same, but the max area in FireFox is a bit lower - iPhone area much lower in Safari), try pushing your scale down on the one with the larger limit until it succeeds, and then see if that fails on the one with the lower limits. There are other limits in your browser (eg. max heap size) which may come into play. If this is the case, I don't have a good solution for you - its usually impractical to get clients to all reconfigure their browsers (and most of these limits are hard anyway). For obvious reasons, browsers don't allow the website to make arbitrary changes to memory limits. If you are using Node.js, you may have more success in dealing with memory limits. Either way (Node or otherwise), it's sometimes better to send things back to the server when you are pushing the limits of the client.
Related
I am starting to work with Spark AR studio and I looking for to get the screen size in pixel to compare the coordinate obtained by the gesture.location on Tap.
TouchGestures.onTap().subscribe((gesture) => {
// ! The location is always specified in the screen coordinates
Diagnostics.log(`Screen touch in pixel = { x:${gesture.location.x}, y: ${gesture.location.y} }`);
// ????
});
The gesture.location is in pixel (screen coordinate) and would like to compare it with the screen size to determine which side of the screen is touched.
Maybe using the Camera.focalPlane could be a good idea...
Update
I tried two new things to have the screen size:
const CameraInfo = require('CameraInfo');
Diagnostics.log(CameraInfo.previewSize.height.pinLastValue());
const focalPlane = Scene.root.find('Camera').focalPlane;
Diagnostics.log(focalPlane.height.pinLastValue());
But both return 0
This answer might be a bit late but it might be a nice addition for people looking for a solution where the values can easily be used in script, I came across this code(not mine, forgot to save a link):
var screen_height = 0;
Scene.root.find('screenCanvas').bounds.height.monitor({fireOnInitialValue: true}).subscribe(function (height) {
screen_height = height.newValue;
});
var screen_width = 0;
Scene.root.find('screenCanvas').bounds.width.monitor({fireOnInitialValue: true}).subscribe(function (width) {
screen_width = width.newValue;
});
This worked well for me since I couldn't figure out how to use Diagnostics.log with the data instead of Diagnostics.watch.
Finally,
Using the Device Info in the Patch Editor and passing these to the script works!
First, add a variable "to script" in the editor:
Then, create that in patch editor:
And you can grab that with this script:
const Patches = require('Patches');
const screenSize = Patches.getPoint2DValue('screenSize');
My mistake was to use Diagnostic.log() to check if my variable worked well.
Instead use Diagnostic.watch():
Diagnostic.watch('screenSize.x', screenSize.x);
Diagnostic.watch('screenSize.y', screenSize.y);
Screen size is available via the Device Info patch output, after dragging it to patch editor from the Scene section.
Now in the open beta (as of this post) you can drag Device from the scene sidebar into the patch editor to get a patch that outputs screen size, screen scale, and safe area inserts as well as the self Object.
The Device patch
The device size can be used in scripts using CameraInfo.previewSize.width and CameraInfo.previewSize.height respectively. For instance, if you wanted to get 2d points representing the min/max points on the screen, this'd do the trick.
const CameraInfo = require('CameraInfo')
const Reactive = require('Reactive')
const min = Reactive.point2d(
Reactive.val(0),
Reactive.val(0)
)
const max = Reactive.point2d(
CameraInfo.previewSize.width,
CameraInfo.previewSize.height
)
(The point I want to emphasize being that CameraInfo.previewSize.width and CameraInfo.previewSize.height are ScalarSignals, not number literals.)
Edit: Here's a link to the documentation: https://sparkar.facebook.com/ar-studio/learn/documentation/reference/classes/camerainfomodule
I'm trying to implement JS testing by loading pages and taking screenshots of the elements with puppeteer. So far so good, everything works perfectly in my local (after I fixed a snag between a normal screen an a retina display) but when I ran the same testing on TravisCI I got small text differences that I can't get around, anyone has any clue what is going on?
This is how I configure my browser instance:
browser = await puppeteer.launch(({
headless: true,
args :[
'--hide-scrollbars',
'--enable-font-antialiasing',
'--force-device-scale-factor=1', '--high-dpi-support=1',
'--no-sandbox', '--disable-setuid-sandbox', // Props for TravisCI
]
}));
And here is how I compare the screenshots:
const compareScreenshots = (fileName) => {
return new Promise((resolve) => {
const base = fs.createReadStream(`${BASE_IMAGES_PATH}/${fileName}.png`).pipe(new PNG()).on('parsed', doneReading);
const live = fs.createReadStream(`${WORKING_IMAGES_PATH}/${fileName}.png`).pipe(new PNG()).on('parsed', doneReading);
let filesRead = 0;
function doneReading() {
// Wait until both files are read.
if (++filesRead < 2) {
return;
}
// Do the visual diff.
const diff = new PNG({width: base.width, height: base.height});
const mismatchedPixels = pixelmatch(
base.data, live.data, diff.data, base.width, base.height,
{threshold: 0.1});
resolve({
mismatchedPixels,
diff,
});
}
});
};
Here is an example of the diff that this is generating:
I had a similar problem. I put in a delay of 400ms before snapping the screenshot and it seems to have fixed the problem. If you come up with something better I'd love to know it.
Fonts can be rendered slightly differently on different OSes. This can cause the artifacts along the edges of the text that you are experiencing. You have a few options:
apply a slight Gaussian blur to the images before comparison (or use css blur). This will smooth away the differences between hard edges in the images.
increase the 'threshold' property to make the anti-aliasing filtering less sensitive.
allow a certain number of pixel differences in your comparison by using a percentage of the total image ( width * height * percentage_threshold ). This number will be influenced by how much text is on the screen at any given point.
use standardized Webfonts for all text - this could help get the fonts close to identical given that you are using the same browser on both systems.
I had a similar issue and I ended up with running my snapshot tests "locally" inside a docker container. I also mounted the project folder so whenever snapshots had to be updated they were updated inside the container but also in the host os.
I am regularly updating several Dygraphs graphs. After some period of time, normally a few minutes, some or all of them get corrupted as shown in the figure below. I haven't been able to tie this a particular event or browser. This happens even with a simple graph where I am just reloading the data stored in a CSV file. I call updateOptions({ file: URL }) on the graph object, where URL points to the CSV file, followed by calling resetZoom() on the graph object to update the axes. Googling hasn't revealed anyone suffering similar behaviour, so I'm lost as to what is causing this.
Update 1: It is linked to minimizing and maximizing the browser.
Update 2: The problem doesn't occur in Firefox. It does happen in Google Chrome and Internet Explorer, although IE has the additional problem of freezing after a while (a problem for another day).
Update 3: Minimum working examples added at http://jsfiddle.net/williamshipman/tvxekq56/ and http://jsfiddle.net/williamshipman/af66qstt/. Repeatedly minimize and maximize the browser window, after a while the distortion occurs. The first example uses AngularJS (like my own work), while the second demonstrates the same bug in pure JavaScript. You may have to minimize and maximize more than a dozen times to see the bug, it seems pretty random.
For me similar problem appears when I show and hide Y2 axis.
This one line helped me: ctx.clearRect(0, 0, this.width, this.height);
File: dygraph-canvas.js
var DygraphCanvasRenderer = function(dygraph, element, elementContext, layout) {
...
ctx = this.dygraph_.hidden_ctx_;
ctx.clearRect(0, 0, this.width, this.height); // <== clear whole canvas before cliping
ctx.beginPath();
ctx.rect(this.area.x, this.area.y, this.area.w, this.area.h);
ctx.clip();
};
The root of the problem
Canvas context is not fully restored after all draw is done.
Solution 1. (workaround)
Injecting canvas_ctx_.restore() after draw is done and context.save() before. save() is needed because library is restoring the context before every draw(except the initial one).
let g = new Dygraph('graph', {
underlayCallback: (context) => {
context.save();
},
drawCallback: (dygraph) => {
dygraph.canvas_ctx_.restore();
},
});
Solution 2. (library fix)
Here is my commit you can apply to the lib's src/dygraph.js
https://github.com/pawelzwronek/dygraphs/commit/c66ca37b82f14e096652a338cae8abf568b9c764
While it is easy enough to get firstPaint times from dev tools, is there a way to get the timing from JS?
Yes, this is part of the paint timing API.
You probably want the timing for first-contentful-paint, which you can get using:
const paintTimings = performance.getEntriesByType('paint');
const fmp = paintTimings.find(({ name }) => name === "first-contentful-paint");
enter code here
console.log(`First contentful paint at ${fmp.startTime}ms`);
Recently new browser APIs like PerformanceObserver and PerformancePaintTiming have made it easier to retrieve First Contentful Paint (FCP) by Javascript.
I made a JavaScript library called Perfume.js which works with few lines of code
const perfume = new Perfume({
firstContentfulPaint: true
});
// ⚡️ Perfume.js: First Contentful Paint 2029.00 ms
I realize you asked about First Paint (FP) but would suggest using First Contentful Paint (FCP) instead.
The primary difference between the two metrics is FP marks the point
when the browser renders anything that is visually different from what
was on the screen prior to navigation. By contrast, FCP is the point
when the browser renders the first bit of content from the DOM, which
may be text, an image, SVG, or even a canvas element.
if(typeof(PerformanceObserver)!=='undefined'){ //if browser is supporting
const observer = new PerformanceObserver((list) => {
for (const entry of list.getEntries()) {
console.log(entry.entryType);
console.log(entry.startTime);
console.log(entry.duration);
}
});
observer.observe({entryTypes: ['paint']});
}
this will help you just paste this code in starting of your js app before everything else.
I'm working on an implementation of a live-updating line graph using gRaphael which is a graphic extension of the Raphael SVG library.
I can't seem to find any examples of somebody doing this as a near-realtime updating project, which is fine. I'm assuming there's a way to call refresh on the graph with a new data set (without the need to re-initialize a whole new Raphael object each time!), but therein lies the problem:
There doesn't seem to be accurate documentation anywhere. I discovered this StackOverflow question: Graphael line chart which in turn led to this documentation project: https://github.com/kennyshen/g.raphael/tree/master/docs , but the results were cold. Using the examples provided, I ran into some errors:
the syntax r.g.linechart() used in the examples was no longer valid (where r is the Raphael object and I assume g is a gRaphael property within). Somewhere along the way somebody must have switched to properly extending the Raphael object so that r.linechart() worked.
The parameters passed into linechart() were incorrect, resulting in an undefined error again. If I passed in only the #x, #y, width, height, arrayX, arrayY parameters and dropped the chart labels, etc., I could render a plain line. But of course I need to be able to label my axes and provide a legend, etc.
Needless to say, a library without an API document isn't going to do anybody much good, but there are stalwarts out there who are willing to learn based strictly on reading the code itself. I'm not one of those. I would probably do OK with a well-commented example, preferably using live updates.
So I guess the questions are:
Does anybody know better documentation than the one I linked to?
Can someone point me to examples, documentation failing?
Can someone provide a proper itemization of the parameters that linechart() will accept?
Thanks!
For the record, here's how far I am so far:
var r = Raphael('line-chart');
// did NOT work -->
var linechart = r.g.linechart(
10,10,300,220,[1,2,3,4,5],[10,20,15,35,30],
{"colors":["#444"], "symbol":"s", axis:"0 0 1 1"}
);
// worked in a limited way, rendering a plain line with no visible labels/graph -->
var linechart = r.linechart(
10,10,300,220,[1,2,3,4,5],[10,20,15,35,30]
);
I am still trying to learn Raphael myself, but here are the primary resources I have been using: http://g.raphaeljs.com/reference.html and the same sans the "g."
here is a fiddle that pretty much pulls off an updating linechart with knockout/gRaphael, prob not the best solution, but its an idea: http://jsfiddle.net/kcar/mHG2q/
Just a note, I didn't start learning it until I combined reading with trial/error (with a lot of error), so play with the fiddle and see how things change.
but the basic code for it is like:
//constructor
var lines = r.linechart(10, 10, width, height, xVals, yVals, { nostroke: false, axis: "0 0 1 1", symbol: "circle", smooth: true })
.hoverColumn(function () { //this function sets the hover tag effect
this.tags = r.set();
for (var i = 0, ii = this.y.length; i < ii; i++) {
this.tags.push(r.tag(this.x, this.y[i], this.values[i], 160, 10).insertBefore(this).attr([{ fill: "#fff" }, { fill: this.symbols[i].attr("fill") }]));
}
}, function () {
this.tags && this.tags.remove();
});
lines.symbols.attr({ r: 3 }); //this adjusts size of the point symbols
There is a fork in GitHub that is working on the documentation and examples.
You will need to download the code and view it from you computer. It is a work in progress but it's more than you can find in the official g.Raphael page.
I also found this small post with some examples.