Rendering problems when saving multiple Dygraphs as a PNG on mobile devices - mobile

I have a web application that uses Dygraphs to create charts.
The application allows a user to create multiple Dygraph charts (each with their own Y-Axis) that will be stacked on top of each other.
Here's an example of what the multiple Dygraphs look like on a PC browser: Notice that the example displays three different Dygraphs each having their own Y-axis, but the X-axis is hidden for the top 2 charts and visible on the bottom chart.
I will allow the user to save the chart to disk as a PNG. - The way I currently save the multiple Dygraphs as one PNG is:
Create a target canvas that will be used to contain all the visible Dygraphs
Extract each canvas out of each Dygraph, then add each canvas to the target canvas **
Create a PNG via the .toDataURL() function on the target canvas
Here's an example of what the above screenshot looks like when saved as one PNG: (This is exactly what I want from the PNG)
The procedure works fine on browsers on a PC. But when I attempt to save the multiple Dygraphs into one PNG on a phone/tablet browser, the resultant PNG doesn't match the graph that is visible on the screen.
Example:
Here's what the multiple Dygraphs look like on an iPad (screenshot)
And here's what the resultant PNG looks like (Notice how the width and height of each chart does not match the actual iPad display).
I don't understand why the PNG is rendered correctly when I use a PC browser, but is not rendered correctly when I use a browser on a mobile device.
I'm not sure if this problem is due to limitations of the Canvas.toDataURL() function or if this is a Dygraphs problem or something else. I'm fishing for advice that may point me in the right direction and/or shed light on this particular problem.
**I should mention that I use Juan Manuel Caicedo Carvajal's Dygraph-Export extension

I'm guessing the Problem occurs, because the generated canvas isn't rendered fully to the responsive screen of an iPad.
You can try to export the original canvas (instead of generating a new one with the said library) yourself with toDataUrl https://developer.mozilla.org/en-US/docs/Web/API/HTMLCanvasElement/toDataURL
Dygraphs generates 2 canvas, one for the legend and one for the actual graph and lays them ontop of each other. So make sure you choose the right one (not the _hidden_canvas). If the examples works you can draw the legend onto the graph canvas with canvas.drawImage(otherCanvas)
How to Copy Contents of One Canvas to Another Canvas Locally
Hope this helps. Keep me updated!

My workaround/hack for the problem stated in my OP was to make a change to the Dygraph source in the Dygraph.getContextPixelRatio function.
Notice in the code below that I set devicePixelRatio = 1
dygraph-combined.js
Dygraph.getContextPixelRatio = function (context) {
try {
//var devicePixelRatio = window.devicePixelRatio;
var devicePixelRatio = 1; // Hack!!!
var backingStoreRatio = context.webkitBackingStorePixelRatio ||
context.mozBackingStorePixelRatio ||
context.msBackingStorePixelRatio ||
context.oBackingStorePixelRatio ||
context.backingStorePixelRatio || 1;
if (devicePixelRatio !== undefined) {
return devicePixelRatio / backingStoreRatio;
} else {
// At least devicePixelRatio must be defined for this ratio to make sense.
// We default backingStoreRatio to 1: this does not exist on some browsers
// (i.e. desktop Chrome).
return 1;
}
} catch (e) {
return 1;
}
};
In my case, this hack fixed my problem (stated in the OP) and didn't negatively affect any other parts of my application that uses Dygraphs. That said, if you find a better/correct way to fix the problem stated in the OP, please share.

Related

Unable to capture multiple screens screenshot at once using Electron desktopCapturer

I am creating a project in Electron in which I need to capture screenshots of the entire system and not just of the browser, so I am using Electron's desktopCapturer feature to do so, but I have noticed that in case of multiple screens attached to a single system, it only captures screenshots of one screen and not of both.
I use this piece of code to capture screen-shots and I have not found any option to target multi-screen issue:
const image = await desktopCapturer.getSources({
types: ['screen'],
thumbnailSize: {
width, // custom width, and height are provided according to the screen resolution here
height,
},
});

Mapbox Icon-Image Dynamic Symbol (icon) Loading with Sprite

I am trying to do something I feel is very simple, yet seems that I am clearly misunderstanding a crucial piece of the mapbox addlayer feature.
The Goal
Create dynamically identified icons, based on a features data value (e.g. geojson feature data vale title: "walmart"). Essentially just adding dynamic store icons from the sprite image when those locations are queried via tilequery. picture representation here
The problem
I keep getting an error when trying to use the sprite values from the style. Error: util.js:349 Image "airport-11" could not be loaded. Please make sure you have added the image with map.addImage() or a "sprite" property in your style. You can provide missing images by listening for the "styleimagemissing" map event.
I see tons of resources talking about sprites, but none discuss how to exactly implement them in this fashion. I have even tried querying the sprite and then adding the values using dot notation to access sprite values. This gives an error of "undefined" and invalid value.
Example code:
map.addLayer({
id: "tilequery-points",
type: "symbol",
source: "tilequery", // Set the layer source
layout: {
"icon-image": [
"match",
["get", "title"],
["HEB"],
"H-E-B_logo",
["Pilot Flying j"],
sprite.Pilot_Travel_Centers_logo,
// "Pilot_Travel_Centers_logo",
["Dollar General"],
"Dollar_General_logo",
["Cumberland Farms Corp"],
"Cumberland_Farms_logo (1)",
["CEFCO"],
"CEFCO-convenience-stores-Logo_510px",
["BJs Wholesale Inc"],
The Question
How do I access the sprite values and not get an error?!!!
Thanks for the help! I Wouldn't ask if I didn't need it!
UPDATE
I have figured out that to use sprite images inside of any layer, the images will automatically be available if you have them in your Mapbox studio sprite image collection. The confusion was that previously, I was not able to use them from link. However, it should work automatically.
Hope it helps!
It's true the documentation about sprites is not super clear. I'll try to summarise (simplifying a bit).
A Mapbox GL style has one sprite. That's a PNG containing all the icons, plus a JSON file specifying what each icon is called (its icon ID), and where it located within the PNG. The sprite is specified by giving a URL as the sprite property: https://docs.mapbox.com/mapbox-gl-js/style-spec/sprite/
You can also add images to the sprite dynamically after the map loads, with map.loadImage and map.addImage, specifying the icon ID.
To display an icon, you use that same ID in a symbol layer: "icon-image": "myicon".
You can run into trouble when you try to combine your own icons with those in a Mapbox basemap (which are Maki icons with names like `airport-11').
To combine them, you can do one of these three things:
upload your icons to a style in Mapbox Studio
load your icons dynamically
generate a new sprite sheet offline, using something like mbsprite
I don't know what you meant about "dot notation", but no, that's not the right path.

URLImages in list renderer disappearing on iOS

I have a custom list renderer that has a logo for a business in it. It has a label Logo_URLImage that has the placeholder image. In my code I get the data and assign HashMaps to the list model, h.put("Logo_URLImage",imageURL); where imageURL is a string that has the absolute URL to the image.
On Android it seems to always work, but most of the testing has been on iOS devices. What happens is that images are updated as you'd expect the first time or two that you run it. A run later on will show blank images (flash the place holder image and then blank) and once that happens images will never come back.
Any thoughts on what might be causing this?
Check that you defined the LogoName map entry. Check that it is unique per image?
Check that it doesn't include special characters that might cause an issue.

How to download images from array to tableviewcontroller in swift?

I have a table view controller with a cell that contains an UIImageView. I also have a NSMutableArray that contains the url's. I want the url's to download the images and place them in the correct order. The NSMutableArray also contains some empty Strings and the cell that it corresponds too I want to have my placeholder image from my image assets.
How can I get it to work? I have also populated each cell with a title and summary but cannot workout how images work.
UPDATE
The code used for the image download. Note the photoLabels contains the array of images. Some of the photos are in the incorrect place once the first placeholder image occurs (It is one index late). Why is it doing that. Does anyone know why. I have println(photoLabels) and all the 50 strings are correct (with some just being "")
If anyone can help that would be great.
let imageURL: String = photoLabels.objectAtIndex(indexPath.row) as String
println(imageURL)
if imageURL == "" {
cell.imageContainer.image = UIImage(named: "placeholder")
} else {
cell.imageContainer.setImageWithURL(NSURL(string: imageURL))
}
return cell
Thanks
This seemingly innocent question actually entails a rat's nest of interesting details. These include:
Use lazy loading of the image, loading them just-in-time, rather than trying to download them up front;
Download the images asynchronously;
While downloading the images as needed, cache them (using NSCache, not NSMutableArray) so that if you scroll back to see some images recently downloaded that you don't have to download them again;
But, in response to memory pressure, make sure to empty the RAM-based cache (but still avail yourself of the persistent storage cache);
If user scrolls quickly down to the 100th row in the table example, make sure that the images for the visible cells don't get backlogged behind the requests for the previous 99 images (nb: you should test your app in suboptimal conditions, e.g. a poor 2G or 3G cellular environment, which can be simulated with the network link conditioner); and
You might want a placeholder image to show until the asynchronously retrieved image is downloaded (or retrieved from the cache).
The bottom line is that this takes a non-trivial amount of effort to do properly. As a result, I'd encourage you to use an existing solution, for example the UIImageView categories that are available from SDWebImage or AFNetworking. Both of these two frameworks offer a nice category for UIImageView that allows you to request the image to be downloaded asynchronously (it's sd_setImageWithURL in SDWebImage; it's setImageWithURL in AFNetworking).

Dynamic Hyperlink in Livecycle Form

I am trying to figure out how to make a hyperlink in a Livecycle Form which points to a URL which will change on different days that the form is rendered. For example on one day I might want the hyperlink to point to:
mywebsite/mypage?option=XXX
and on another day I want it to point to:
mywebsite/mypage?option=YYY
The XXX and YYY can be passed into the form's data pretty easily as XML, but I just don't know how to make it so that the hyperlink is changed to correspond to this.
Any suggestions?
This can be accomplished with JavaScript in LiveCycle Designer. The following script, placed on the Form's docReady event will let you dynamically change the URL of a text object.
form1::docReady - (JavaScript, client)
// If this code is running on the server, you don't want it to run any code
// that might force a relayout, or you could get stuck in an infinite loop
if (xfa.host.name != "XFAPresentationAgent") {
// You would load the URL that you want into this variable, based on
// whatever XML data is being passed into your form
var sURL = "www.stackoverflow.com"; // mywebsite/mypage?option=xxx
// URLs are encoded in XHTML. In order to change the URL, you need
// to create the right XHTML string and push it into the Text object's
// <value> node. This is a super simple XHTML shell for this purpose.
// You could add all sorts of markup to make your hyperlink look pretty
var sRichText = "<body><p>Foo</p></body>";
// Assuming you have a text object called "Text1" on the form, this
// call will push the rich text into the node. Note that this call
// will force a re-layout of the form
this.resolveNode("Text1").value.exData.loadXML(sRichText, false, true);
}
There are a couple of caveats: URLs in Acrobat are only supported in Acrobat 9.0 and later. So if someone using an older version of Acrobat opens your form, the URLs won't work.
Also, as you can see from the "if (xfa.host.name !=...)" line, this code won't run properly if the form is being generated on the server, because forcing a re-layout of a form during docReady can cause problems on certain older versions of the LiveCycle server. If you do need to run this script on the server, you should probably pick a different event then form::docReady.
I a number of complaints from users in WorkSpace that clicking links opened them in the same tab so they lost their WorkSpace form, and there's no option to change that in Designer 11. I think the solution I came up with for that would work for you too.
I made buttons with no border and no background, and in their click event have this line (in Javascript, run at client)
app.launchURL("http:/stackoverflow.com/", true);
It would be easy to add some logic to choose the right URL based on the day and it doesn't cause any form re-rendering.
In some spots where the hyperlink is in line with other text, I leave the text of the link blue and underlined but with no hyperlink, and just place the button (no background, no border, no caption) over it. Does require positioned and not flowed subforms for that to work, so depending on your layout it could get a little clunky.
Wow, just realized I am super late to the party. Well, for anyone using ES4 facing a similar problem . . .
Ended up using a 3rd party component to manipulate the PDF's hyperlinks...wish there was a better solution as this one costs about $1000.

Resources