Highchart export filename with special characters - highcharts

I am using Highcharts to render plots within ShinyServer. Everything works well on Firefox but in Chrome the export chocks on duplicate headers; this is because the filename contains commas.
Is there a way to properly escape the filename within highcharts or Shiny? The recommendation is to double quote the filename but I only pass the first part of the name to highcharts and not the extension so I cannot double quote the whole string in Shiny. I have access to the highcharts server so I could modify that if I knew where to look.
I realise the simple solutions are
Not to put commas in the filename or
Not to use chrome,
but
Sooner or later I will have to use a special character of some sort and
Chrome is just adhering more strictly to safe practices and other browsers will follow suite one day.

You can wrap exporting function by API and then in this part, escape / modify name.
Highcharts.post(options.url, {
filename: options.filename || 'chart',
type: options.type,
width: options.width || 0, // IE8 fails to post undefined correctly, so use 0
scale: options.scale || 2,
svg: svg
}, options.formAttributes);

Related

How to prevent automatic hyperlink detection in the console of Firefox/Chrome developer tools?

Something that drives me nuts in the developper tools of Chrome (106) and Firefox (105) is the fact that whenever some text logged to the console via console.log(text) happens to contain a hyperlink, this link is not only turned clickable (I can live with it even when I usually prefer to have just plain text) but is abbreviated, if it is a long link. So when I want to control what precise link is in some variable, I cannot just write e.g. console.log(img.src), because some of the interesting information of the link is hidden.
You can try yourself with
var href = 'https://stackoverflow.com/search?q=%5Bgoogle-chrome-devtools%5D+%5Bconsole.log%5D+%5Bfirefox-developer-tools%5D+%5Bhyperlink%5D+automatic+detection&someMoreStuffTomakeTheLinkLonger';
console.log(href);
In both, Firefox and Chrome, the output for me contains some '...', e.g. in Firefox I obtain as output:
https://stackoverflow.com/search?q=%5Bgoogle-chrome-devtools…link%5D+automatic+detection&someMoreStuffTomakeTheLinkLonger
thus hiding the part after "-devtools". (Chrome hides a slightly different part). The console is mostly a debugging tool. I log things because I want to see them, not hide them. I always need to either hover with the mouse and wait for the tooltip (doesn't allow me to copy fractions of the link) or to right click copy the link and paste it somewhere where I can see it completely. Or take a substring to remove the "https://" in the front. But note that the variable isn't necessarily a single hyperlink, but can be any text containing several such hyperlinks. I didn't find a way to force console.log to just print plain text all content. Did anybody meet this problem as well and find a workaround?
I made this a community wiki answer, because the main insight is not from myself but from the comments. Feel free to improve.
The console.log() function allows several arguments, which allows also a formatted output similar to printf in some languages. The possibilities of formatting can be found in the documentation of console.log() on MDN. In any case, this formatted output provides a solution at least for Chrome, as #wOxxOm pointed out in the comments:
console.log('%O', href) // works in Chrome
This is rather surprising, because %O is described at MDN as
"Outputs a JavaScript object. Clicking the object name opens more information about it in the inspector".
It seems there is no 'clicking' in Chrome when the object is a string.
There is also %s for string output, but this just gives the standard behavior of replacing links in both browsers. And for Firefox none of the above two formatting options works. There one really has to replace the protocol "https://" by something that is not recognized as link. A space behind ':' seems enough, so "https: //". It turns out, that one can also insert a formatting string "https:%c//", which can even be empty, and thus yield an output which is the complete link and can be copied as well:
console.log(href.replace(/(https?:)/, "$1%c"), ""); // works in Firefox
In particular the FF solution is cumbersome, and there might also be several links within one console-output. So it is useful to define one's own log-function (or if one prefers, redefine console.log, but note the remark at the end)
function isChrome() {...} // use your favorite Chrome detection here
function isFirefox() {...} // use your favorite Firefox detection here
function plainLog() {
const msg = arguments[0];
if (isChrome() && arguments.length == 1 && typeof msg == "string") {
return console.log("%O", msg);
}
if (isFirefox() && arguments.length == 1 && typeof msg == "string") {
const emptyStyle = ""; // serves only as a separator, such that FF doesn't recognize the link anymore
const reg = /(https?:)\/\//g;
const emptyStyles = []; // we need to insert one empty Style for every found link
const matches = msg.matchAll(reg);
for (let match of matches) {
emptyStyles.push(emptyStyle);
}
return console.log(msg.replace(reg, '$1%c//'), ...emptyStyles);
}
return console.log(...arguments);
}
For browser detection isChrome() and isFirefox() see e.g. here on SO.
One can of course extend the redefinition also to the other console functions (console.info, console.warn, etc.)
The downside of the redefinition of console.log is that usually every output of the console shows also the last entry of the call stack as a practical link to the source of the logging. But due to the redefintion, this link is now always to the same place, namely the file and line number where plainLog() is defined and calls console.log(), instead of the place where the new log command plainLog() was called. This new problem is described on SO here, but the solution (see comment) is again a bit involved and also not completely satisfying to serve as a replacement for the built-in console.log . So if links appear only rarely in the logging, it's probably better to switch to the redefined plainLog() only for these links.

TCPDF Embedded Base64 Encoded Images in HTML String

I have read several posts on this subject but didn't want to piggy-back on any of them with additional questions.
Specifically this post: TCPDF and insert an image base64 encoded
I am generating a PDF from within a custom theme in Wordpress. I'm using TCPDF 6.2.3 (latest stable release, I believe).
I am building this PDF from the same HTML I am using to display on the page. If I embed the full base64 encoded string, it works correctly in the browser, but the image is missing from the PDF.
If I use the "#" method described in the linked post, I get a broken image in the browser (expectedly) but still nothing in the PDF.
All the rest of my HTML markup is rendering in the PDF, images are just not showing.
Is there some other setting or option I need to set in order to get the images to appear in the PDF, and/or can you spot anything I'm doing wrong here? No errors, the images are just not visible in the PDF.
This is how I set the image up:
$imageLocation = $img_root.$imgsrc;
$ext = end(explode(".", $imageLocation));
$image = base64_encode(file_get_contents($imageLocation));
//$response .= "<img src='data:image/$ext;base64,$image'>"; //works in browser but not in PDF
$response .= "<img src='#$image' class='socf_image'>"; //does not work in browser or PDF
And here is the method to create the PDF:
function createPDF($response)
{
// Include the main TCPDF library (search for installation path).
require_once('tcpdf_6_3_2/tcpdf/tcpdf.php');
// create new PDF document
$pdf = new TCPDF(PDF_PAGE_ORIENTATION, PDF_UNIT, PDF_PAGE_FORMAT, true, 'UTF-8', false);
// set document information
$pdf->SetCreator(PDF_CREATOR);
$pdf->SetAuthor('test');
$pdf->SetTitle('test');
$pdf->SetSubject('test');
$pdf->SetKeywords('test');
// set default header data
$pdf->SetHeaderData(PDF_HEADER_LOGO, PDF_HEADER_LOGO_WIDTH, PDF_HEADER_TITLE.' 001', PDF_HEADER_STRING, array(0,64,255), array(0,64,128));
$pdf->setFooterData(array(0,64,0), array(0,64,128));
// set header and footer fonts
$pdf->setHeaderFont(Array(PDF_FONT_NAME_MAIN, '', PDF_FONT_SIZE_MAIN));
$pdf->setFooterFont(Array(PDF_FONT_NAME_DATA, '', PDF_FONT_SIZE_DATA));
// set default monospaced font
$pdf->SetDefaultMonospacedFont(PDF_FONT_MONOSPACED);
// set margins
$pdf->SetMargins(PDF_MARGIN_LEFT, PDF_MARGIN_TOP, PDF_MARGIN_RIGHT);
$pdf->SetHeaderMargin(PDF_MARGIN_HEADER);
$pdf->SetFooterMargin(PDF_MARGIN_FOOTER);
// set auto page breaks
$pdf->SetAutoPageBreak(TRUE, PDF_MARGIN_BOTTOM);
// set image scale factor
$pdf->setImageScale(PDF_IMAGE_SCALE_RATIO);
// set default font subsetting mode
$pdf->setFontSubsetting(true);
// Set font
$pdf->SetFont('helvetica', '', 14, '', true);
// Add a page
$pdf->AddPage();
$html = $response;
$pdf->writeHTML($response, true, false, true, false, '');
return $pdf;
}
Well, fortunately, I was able to figure it out on my own. Perhaps this isn't the best forum for seeking help with this library? If anyone can suggest a better place to get help, I'd appreciate the direction.
Ultimately, the issue was two-fold:
The "#" notation is required for the PDf while the approach is what works for displaying the HTML in browser. So a string replace before creating the PDF solves that.
This is the tricky part. The HTML needs to use double-quotes around the properties, not single quotes. My code was using double quotes for the PHP strings, so the HTML properties were surrounded with single quotes and that was the issue. Swapping the two quote types was the last piece of the puzzle to get the images to appear in the PDF.
Hopefully this will help someone else who is pulling their hair out trying to blindly find their way through this library like me.

regex to extract URLs from text - Ruby

I am trying to detect the urls from a text and replace them by wrapping in quotes like below:
original text: Hey, it is a url here www.example.com
required text: Hey, it is a url here "www.example.com"
original text show my input value and required text represents the required output. I searched a lot on web but could not find any possible solution. I already have tried URL.extract feature but that doesn't seem to detect URLs without http or https. Below are the examples of some of urls I want to deal with. Kindly let me know if you know the solution.
ANQUETIL-DUPERRON Abraham-Hyacinthe, KIEFFER Jean-Luc, www.hominides.net/html/actualites/outils-preuve-presence-hominides-asie-0422.php,Les Belles lettres, 2001.
https://www.ancient-code.com/indian-archeologists-stumbleacross-ruins-great-forgotten-civilization-mizoram/
www.jstor.org/stable/24084454
www.biorespire.com/2016/03/22/une-nouvelle-villeantique-d%C3%A9couverte-en-inde/
insu.cnrs.fr/terre-solide/terre-et-vie/de-nouvellesdatations-repoussent-l-age-de-l-apparition-d-outils-surle-so
www.cerege.fr/spip.php?page=pageperso&id_user=94
Find words who look like urls:
str = "ANQUETIL-DUPERRON Abraham-Hyacinthe, KIEFFER Jean-Luc, www.hominides.net/html/actualites/outils-preuve-presence-hominides-asie-0422.php,Les Belles lettres, 2001.\n\nhttps://www.ancient-code.com/indian-archeologists-stumbleacross-ruins-great-forgotten-civilization-mizoram/\n\nwww.jstor.org/stable/24084454\n\nwww.biorespire.com/2016/03/22/une-nouvelle-villeantique-d%C3%A9couverte-en-inde/\n\ninsu.cnrs.fr/terre-solide/terre-et-vie/de-nouvellesdatations-repoussent-l-age-de-l-apparition-d-outils-surle-so\n\nwww.cerege.fr/spip.php?page=pageperso&id_user=94"
str.split.select{|w| w[/(\b+\.\w+)/]}
This will give you an array of words which have no spaces and include a one or more . characters which MIGHT work for your use case.
puts str.split.select{|w| w[/(\b+\.\w+)/]}
www.hominides.net/html/actualites/outils-preuve-presence-hominides-asie-0422.php,
https://www.ancient-code.com/indian-archeologists-stumbleacross-ruins-great-forgotten-civilization-mizoram/
www.jstor.org/stable/24084454
www.biorespire.com/2016/03/22/une-nouvelle-villeantique-d%C3%A9couverte-en-inde/
insu.cnrs.fr/terre-solide/terre-et-vie/de-nouvellesdatations-repoussent-l-age-de-l-apparition-d-outils-surle-so
www.cerege.fr/spip.php?page=pageperso&id_user=94
Updated
Complete solution to modify your string:
str_with_quote = str.clone # make a clone for the `gsub!`
str.split.select{|w| w[/(\b+\.\w+)/]}
.each{|url| str_with_quote.gsub!(url, '"' + url + '"')}
Now your cloned object wraps urls inside double quotes
puts str_with_quote
Will give you this output
ANQUETIL-DUPERRON Abraham-Hyacinthe, KIEFFER Jean-Luc, "www.hominides.net/html/actualites/outils-preuve-presence-hominides-asie-0422.php,Les" Belles lettres, 2001.
"https://www.ancient-code.com/indian-archeologists-stumbleacross-ruins-great-forgotten-civilization-mizoram/"
"www.jstor.org/stable/24084454"
"www.biorespire.com/2016/03/22/une-nouvelle-villeantique-d%C3%A9couverte-en-inde/"
"insu.cnrs.fr/terre-solide/terre-et-vie/de-nouvellesdatations-repoussent-l-age-de-l-apparition-d-outils-surle-so"
"www.cerege.fr/spip.php?page=pageperso&id_user=94"

Custom font faces in jsPDF?

Is it possible to include custom fonts in jsPDF ?
With the basic library, if I console log 'doc.getFontList()' I get:
Courier, Helvetica, Times, courier, helvetica, times
But, say I want to use 'Comic Sans' ( not that I would ;o) ) can it be done ?
Even better, could I use a font is locally stored and has been declared in the site with #font-face ?
I found this was possible by modifying jsPDF.js to expose the existing addFont method in the public API.
In jsPDF.js, look for:
//---------------------------------------
// Public API
Add the following:
API.addFont = function(postScriptName, fontName, fontStyle) {
addFont(postScriptName, fontName, fontStyle, 'StandardEncoding');
};
I put this method near other font methods for clarity - API.setFont, API.setFontSize, API.setFontType, etc.
Now in your code, use:
doc.addFont('ComicSansMS', 'Comic Sans', 'normal');
doc.setFont('Comic Sans');
doc.text(50,50,'Hello World');
This works for me with #font-face fonts included with css before loading jsPDF, as well as system fonts. There's probably a better way to do this using jsPDF's plugin framework, but this quick and dirty solution should at least get you going.
Note that doc.getFontList() will not show added fonts:
// TODO: iterate over fonts array or return copy of fontmap instead in case more are ever added.
It seems to be a lot easier with the latest version of jsPDF (1.5.3):
If you look in the folder jsPDF-master > fontconverter, there's a file fontconverter.html. Open in your browser and use the Browse... button to navigate to, and select your .ttf font file.
Click 'Create'.
The page will offer a "download" to be saved. This will produce a .js file called [something like] RopaSans-Regular-normal.js. This needs to be included in your page producing the PDF's. Personally, I've done it in the main page's header (and please note the order of the scripts):
<!-- pdf creation -->
<script src="FileSaver.js-master/src/FileSaver.js"></script>
<script src="jsPDF-master/dist/jspdf.debug.js"></script>
<!-- custom font definition -->
<script src="path-to-the-file-just-saved/RopaSans-Regular-normal.js" type="module"></script>
Now in your PDF generation method in js:
doc.setFont('RopaSans-Regular');
doc.setFontType('normal');
Here is the solution I'm using...
First, as others have mentioned - you need these two libraries:
jsPDF: https://github.com/MrRio/jsPDF
jsPDF-CustomFonts-support: https://github.com/sphilee/jsPDF-CustomFonts-support
Next - the second library requires that you provide it with at least one custom font in a file named default_vfs.js. I'm using two custom fonts - Arimo-Regular.ttf and Arimo-Bold.ttf - both from Google Fonts. So, my default_vfs.js file looks like this:
(
(function (jsPDFAPI) {
"use strict";
jsPDFAPI.addFileToVFS('Arimo-Regular.ttf','[Base64-encoded string of your font]');
jsPDFAPI.addFileToVFS('Arimo-Bold.ttf','[Base64-encoded string of your font]');
})(jsPDF.API);
Obviously, you version would look different, depending on the font(s) you're using.
There's a bunch of ways to get the Base64-encoded string for your font, but I used this: https://www.giftofspeed.com/base64-encoder/.
It lets you upload a font .ttf file, and it'll give you the Base64 string that you can paste into default_vfs.js.
You can see what the actual file looks like, with my fonts, here: https://cdn.rawgit.com/stuehler/jsPDF-CustomFonts-support/master/dist/default_vfs.js
So, once your fonts are stored in that file, your HTML should look like this:
<script src="js/jspdf.min.js"></script>
<script src="js/jspdf.customfonts.min.js"></script>
<script src="js/default_vfs.js"></script>
Finally, your JavaScript code looks something like this:
const doc = new jsPDF({
unit: 'pt',
orientation: 'p',
lineHeight: 1.2
});
doc.addFont("Arimo-Regular.ttf", "Arimo", "normal");
doc.addFont("Arimo-Bold.ttf", "Arimo", "bold");
doc.setFont("Arimo");
doc.setFontType("normal");
doc.setFontSize(28);
doc.text("Hello, World!", 100, 100);
doc.setFontType("bold");
doc.text("Hello, BOLD World!", 100, 150);
doc.save("customFonts.pdf");
This is probably obvious to most, but in that addFont() method, the three parameters are:
The font's name you used in the addFileToVFS() function in the default_vfs.js file
The font's name you use in the setFont() function in your JavaScript
The font's style you use in the setFontType() function in your JavaScript
You can see this working here: https://codepen.io/stuehler/pen/pZMdKo
Hope this works as well for you as it did for me.
I'm using Angular 8 and Todd's answer worked for me.
Once you get the .js file from fontconverter.html, you can import it in typescript like so:
import fontref = require('path/to/font/CustomFont-normal.js')
Then all you have to do to load the font is 'call' fontref:
makePdf() {
let doc = new jsPDF();
fontref; // 'call' .js to load font
doc.getFontList(); // contains a key-value pair for CustomFont
doc.setFont("CustomFont"); // set font
doc.setFontType("normal");
doc.setFontSize(28);
doc.text("Hello", 20, 20);
window.open(doc.output('bloburl')); // open pdf in new tab
}
After looking at the fontconverter.html, and seeing that it does nothing more than package the TTF files into a base64 string inside a JS file, I came up with the following method that I call before creating my document. It basically does what the individual files resulting from fontconverter.html do, just on-demand:
async function loadFont(src, name, style, weight) {
const fontBytes = await fetch(src).then(res => res.arrayBuffer());
var filename = src.split('\\').pop().split('/').pop();
var base64String = btoa(String.fromCharCode.apply(null, new Uint8Array(fontBytes)));
var callAddFont = function () {
this.addFileToVFS(filename, base64String);
this.addFont(filename, name, style, weight );
};
jsPDF.API.events.push(['addFonts', callAddFont]);
}
Call it like this:
await loadFont("/css/fonts/exo-2-v9-latin-ext_latin-italic.ttf", "Exo-2", "italic", 400);
await loadFont("/css/fonts/exo-2-v9-latin-ext_latin-regular.ttf", "Exo-2", "normal", 400);
await loadFont("/css/fonts/exo-2-v9-latin-ext_latin-500.ttf", "Exo-2", "normal", 500);
await loadFont("/css/fonts/exo-2-v9-latin-ext_latin-500italic.ttf", "Exo-2", "italic", 500);
It loads the font from the URL, and adds it to the VFS and font manager. Important: the font name cannot include spaces. You won't get any warnings, but the resulting PDF will either not open or the text will look funny.
Some of these answers are outdated, so I am linking the readme file from Mr. Rio himself regarding the latest release as of this post. Below is a copy of the paragraph from that readme file followed by a link to the readme file itself. Hope this additional resource is helpful:
Use of UTF-8 / TTF:
The 14 standard fonts in PDF are limited to the
ASCII-codepage. If you want to use UTF-8 you have to to integrate a
custom font, which provides the needed glyphs. jsPDF supports
.ttf-files. So if you want to have for example chinese text in your
pdf, your font has to have the necessary chinese glyphs. So check if
your font supports the wanted glyphs or else it will show a blank
space instead of the text.
To add the font to jsPDF use our fontconverter in
/fontconverter/fontconverter.html . The fontconverter will create a
js-file with the content of the provided ttf-file as base64 encoded
string and additional code for jsPDF. You just have to add this
generated js-File to your project. You are then ready to go to use
setFont-method in your code and write your UTF-8 encoded text.
https://github.com/MrRio/jsPDF/blob/master/README.md#use-of-utf-8--ttf
//use necessary config, read the docs http://raw.githack.com/MrRio/jsPDF/master/docs/jsPDF.html
import MuliSemiB64 from "../functions/MuliSemiB64";
let doc = new jsPDF({
orientation: "p",
unit: "px",
format: "a5",
});
doc.addFileToVFS("MULI-SEMIBOLD.TTF", MuliSemiB64());
//MuliSemiB64() is a function that returns the Muli ttf file in its base64 string format, convert your font ttf file and copy the string, save to a variable and use the function to return the string. Use a site like https://www.giftofspeed.com/base64-encoder/ for the conversion
doc.addFont("MULI-SEMIBOLD.TTF", "Muli-Semi-Bold", "Semi-Bold");
doc.setFont("Muli-Semi-Bold", "Semi-Bold");
doc.text("Have Fun :*", 35, 25);
The easiest way that I have found by far is using the jspdf-customfonts package.
Simply install the package by
npm i jspdf-customfonts
then add the following files in the head tag of your index.html for default configurations
script src="https://unpkg.com/jspdf#latest/dist/jspdf.min.js"></script>
<script src="dist/jspdf.customfonts.min.js"></script>
<script src="dist/default_vfs.js"></script>
Now you can download the ttf file of whichever font you want. Then go to this site, select your font and copy the code, and you are done!

How to display Chinese characters with Three.js

I am using the OBJLoader to load a large 3D model (described in a .obj file) and I want to display the models name on its surfaces. Though it seems that Three.js can only display English characters. My question is how can I display Chinese characters in Three.js?
there are a couple of ways to display text (https://github.com/mrdoob/three.js/wiki/Text-in-Three.js), but since exporting a chinese font might be more difficult, it might be easier to draw the chinese characters to a canvas texture and use the texture as a material in the scene.
It seem that you can use Facetype.js to get a Chinese font library, for example"YaHei_Regular.typeface.json" , then you can show Chinese characters.
var fontLoader = new THREE.FontLoader();
fontLoader.load("YaHei_Regular.typeface.json", (font)=> {
this.font = font;
});
Another option is using msdf-bmfont-xml. This example uses Microsoft YaHei.
charset.txt —
你好,世界
You may need to install dependencies first. Then:
npm install msdf-bmfont-xml -g
msdf-bmfont -f json yahei.ttf -i charset.txt --pot --square
and finally, use three-bmfont-text to render the text.
Three.js doesn't display Chinese regularly because it doesn't support the charset. You have to load it dynamically.
It seems there're two methods to load: 1, new THREE.TTFLoader().load('*.ttf') , it loads a ttf file that support Chinese charset. But I failed.
2, new THREE.FontLoader().load('*.json') it loads a json file transformed by
the ttf on http://gero3.github.io/facetype.js/ website.
But firstly you have to find a complete ttf file. I tried 方正兰亭超细黑简体 and 方正赵佶瘦金书 which both work, you can google and download ttf file. I found some ttf can't be identified by three.js completely. You perhaps see some Chinese char display normally but others still display '?'.
The final code snippets as following:
const three_font = new THREE.FontLoader();
three_font.load('*.json', function (font_font) {
font=font_font
})
// finally add text with font
const geometry = new THREE.TextGeometry(
{
font: font,
size, height: h, curveSegments: 4, bevelThickness: 2, bevelSize: 2, bevelEnabled: true
});
geometry.computeBoundingSphere();
geometry.computeVertexNormals();
const mesh = new THREE.Mesh(geometry, pool.textMaterial);
mesh.position.set(x * deviation, y, z * deviation);
mesh.rotation.set(rx, ry, rz);
scene.add(mesh);

Resources