html2canvas svg textPath Custom font family not rendering in firefox [duplicate] - svg

This question already has answers here:
Fonts missing from SVG file when placed in <img> data URI or <canvas>
(2 answers)
How to use Google fonts in Canvas when Drawing DOM objects in SVG?
(2 answers)
Custom font not displaying in SVG pattern used as background-image
(1 answer)
Closed 4 months ago.
HTML:
<svg>
<path id="text2" d="M156.56,461.68c-38.49-66.82-41.3-151.76,.03-223.35,41.33-71.58,116.3-111.63,193.41-111.7"></path>
<text><textPath href="#text2" startOffset="50%" >Here is example text</textPath></text>
</svg>
CSS:
#font-face {
font-family: 'CustomFont';
src: url('fonts/customfont.woff') format('woff')
}
svg * {
font-family: 'CustomFont';
}
JS:
html2canvas(targetElement, {
backgroundColor:null
})
.then((canvas) => {
const base64image = canvas.toDataURL('image/png');
let anchor = document.createElement('a');
anchor.setAttribute('href', base64image);
anchor.setAttribute('download', 'testdownload.png');
anchor.click();
anchor.remove();
})
I try to text on svg path and download it as png with html2canvas.
I have customfont. Chrome and safari render the fonts correctly. So that i can get the result png how i want.
But it's not working in fireFox. Firefox doesn't render any customfont.
Firefox renders the font,
if i use font-family: Arial; which is intrinsic.
if the text not in svg. (it means, it renders if text is in others: div, span, p etc.)
What am i missing here now for firefox?

Related

Download svg including it's text tag value

I'm working on a way to download svg from a webpage.
I have pretty much everything working apart from the last part:
Downloading the svg including the text tag included in it.
Now, to give a better background:
I will need to download images out of an svg using fontawesome icon as text.
The svg is properly downloaded, hower the text tag is not (it's downloaded as a broken image "square")
Here's a simplified version of my code:
function triggerDownload(imgURI, name, format) {
let evt = new MouseEvent('click', {
view: window,
bubbles: false,
cancelable: true
});
let a = document.createElement('a');
a.setAttribute('download', name + '.' + format);
a.setAttribute('href', imgURI);
a.setAttribute('target', '_blank');
a.dispatchEvent(evt);
}
function clickSVG(event) {
const dd = 300;
const format = "png"
let canvas = document.getElementById('canvas'),
target = event.currentTarget;
canvas.width = dd;
canvas.height = dd;
debugger
let newImage = target.cloneNode(true),
circle = newImage.getElementsByClassName('svgCircle-test') ? newImage.getElementsByClassName('svgCircle-test') : null,
image = newImage.getElementsByClassName('svgImage-test');
newImage.height.baseVal.value = dd;
newImage.width.baseVal.value = dd;
if (circle.length > 0) {
circle[0].cx.baseVal.value = dd / 2;
circle[0].cy.baseVal.value = dd / 2;
circle[0].r.baseVal.value = dd / 2;
}
let ctx = canvas.getContext('2d'),
data = (new XMLSerializer()).serializeToString(newImage),
DOMURL = window.URL || window.webkitURL || window,
name = newImage.getAttribute('data-name'),
img = new Image(),
svgBlob = new Blob([data], {type: 'image/svg+xml;charset=utf-8'}),
url = DOMURL.createObjectURL(svgBlob);
img.onload = function () {
ctx.drawImage(img, 0, 0);
DOMURL.revokeObjectURL(url);
let imgURI = canvas
.toDataURL(`image/${format}`)
.replace(`image/${format}`, 'image/octet-stream');
triggerDownload(imgURI, name, format);
};
img.src = url;
}
document.getElementById("svg").addEventListener("click", clickSVG)
The html looks like this:
<svg id="svg" height="200" width="200" data-name="test">
<circle cx="100" cy="100" r="100" fill="#faa" class="svgCircle-test" />
<text x="0" y="120" width="200" height="200" class="svgImage-test"></text>
</svg>
<canvas id="canvas" />
And here's the CSS:
svg {
margin-top:10px;
cursor: pointer;
display: inline-block;
padding: 5px;
}
svg text{
font-family:'FontAwesome';
font-size: 100px;
}
#canvas {
display: none;
}
A codepen to help you understand the issue and help me out a bit better can be found here: https://codepen.io/NickHG/pen/QMmJvd
To see the issue, click on the circle (this will download the svg as a png image).
NB: If the download doesn't start, it's probably your browser blocking popups. Just allow it to see the downloaded image.
Thanks
There are a couple of things going on here.
Once you "convert" the SVG file to an HTMLImageElement (<image>), as you are doing here onto the canvas, things change:
the styling you have applied to the <text> no longer applies. That's because it is in the HTML file, not the SVG "file". You need to add the styling it to the SVG itself.
SVGs rendered as an <image> need to be self contained. They can't reference external files such as the Font Awesome font.
To make it self contained, you need to embed the font file in the SVG itself using a Data URL.
You'll need to add a <style> element to the SVG, and include a #font-face rule that specifies a Base64 encoded font file (or files).
See this question for an example

chrome extension shadow DOM import boostrap fonts

so I'd like to display bootstrap 3 icons in a shadowroot added from a chrome extension content script. So far its not working, help?
manifest.js does include the woff file in web_accessible_resources
shadow root has style tag with:
#import url(chrome-extension://__MSG_##extension_id__/fonts/glyphicons-halflings-regular.woff2);
What am I missing?
To import a font, you should not use #import url which is used to import a CSS stylesheet.
Instead, you should use the #font-face directive.
Also, this directive should be placed in the <head> element of the HTML page, not inside the Shadow DOM.
host.attachShadow( { mode: 'open' } )
.innerHTML = `
<style>.icon { font-family: Icons; color: green ; font-size:30pt }</style>
<span class="icon">&#xe084</span>`
#font-face {
font-family: "Icons" ;
src: url("https://ajax.aspnetcdn.com/ajax/bootstrap/3.3.7/fonts/glyphicons-halflings-regular.woff2") format("woff2")
}
<div id=host></div>
You can read this SO answer for further details.

How can I use an SVG image as a map marker in OpenLayers-3?

I am trying to create map "pin-drops" (ie. map markers) in OpenLayers-3 (OL3) using SVG images.
Currently, I am using PNG images as the pindrops that reference the ol.style.Icon source (“src”) property attribute just fine. However, this fails using an SVG image. Is there some other way to use an SVG in the same manner? Maybe by using a reference besides ol.style.Icon even? There is already a lot of built-in SVG in Open Layers so this should be possible, but I haven't found a way to get this working in OL3. Is there some other way to do this in OL3 that I should consider?
Please note: we already tried using an ol.Vector layer, however when the user zooms in/out, the size of the SVG image grows/shrinks which is an inadequate workaround.
OL3 (fails):
var createMapMarkerImage = function() {
return function(feature, resolution) {
var iconStyle = new ol.style.Style({
image: new ol.style.Icon( ({
src: 'img/map_pindrop.svg' // OL3 doesn’t like this, but accepts a .PNG just fine
}))
});
return [iconStyle];
};
};
Very similar functionality, is the below example I found online, is almost perfect if it weren’t for the fact that the example uses OpenLayers-2 (OL2) functionality which calls openlayers.js library (instead of OL3’s ol.js library). Sadly, swapping these javascript files out fails.
OL2 (works -but is the old OL library):
http://dev.openlayers.org/sandbox/camptocamp/tipi/examples/vector-symbols.html
Searching online for a solution to this seems to produce only other confused people searching for a solution.
Please help,
FreeBeer
Based on #ahocevar answer, you can use data URIs for SVG:
new ol.style.Style({
image: new ol.style.Icon({
anchor: [0, 0],
src: 'data:image/svg+xml;utf8,<svg>/* SVG DATA */</svg>'
})
});
Convert the SVG to Base 64 . This (Link) helped me.
copied the base 64 and used it as a string in javascript .
Eg : var svg = "convertedBase64";
Then
var icon = new ol.style.Icon({
src:'data:image/svg+xml;base64,'+svg ,
other props
});
And you are done, may be a few Kbs more than SVG but this did work perfect for me .
SVG icons work fine as long as the content-type of your SVG image file is image/svg+xml. Also note that no external references are supported inside the SVG. OpenLayers 3 simply uses the drawImage function of the 2d context. You can find more details on the requirements of SVG content here: https://developer.mozilla.org/en-US/docs/Web/API/Canvas_API/Drawing_DOM_objects_into_a_canvas.
I had the same issue, but not even serving the image with the proper mime type helped.
It boiled down to the SVG not defining width and height properly.
I added the width and height attributes to the <svg> tag, like:
<svg width="100px" height="100px" version="1.1" xmlns="http://www.w3.org/2000/svg" viewBox="0.75 0 30 45" xml:space="preserve">
After that I was able to use my svg just like any other image.
I also had issues to show the icon image, ahocevar answer helped me to
solve my problem but I had also to search for the php header, for SVG
In case you are or others who see this answer are using php to generate the SVG you have to use header function to identify the content-type
header('Content-type: image/svg+xml'); /* this line will do the magic */
echo '<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<svg width="100%" height="100%" version="1.1" xmlns="http://www.w3.org/2000/svg">
<circle cx="100" cy="50" r="40" stroke="black" stroke-width="2" fill="red"/>
</svg>';
Building upon SoonDead's answer, I had to come up with a way to add the width and height to the svg without touching the source. Using angular, this is sort of what I did:
$http.get('path/to/image.svg').then(function (response) {
// create element
var svgEl = angular.element(response.data);
// set width and height
svgEl.attr('width', '50px');
svgEl.attr('height', '50px');
// base64 encode
var base64Svg = btoa(unescape(encodeURIComponent(svgEl[0].outerHTML)));
// create the style
var style = new ol.style.Style({
image: new ol.style.Icon({
src: 'data:image/svg+xml;base64,'+base64Svg,
imgSize: [50, 50],
size: [50, 50],
})
});
// apply the style
feature.setStyle(style);
});
It's a little verbose, but it seems to do the job.

fabric.js to update to rotate div instead of canvas

How to make div rotatable instead of canvas using fabric.js? What's needed to update in fabric.js to accomplish this requirement?
It would be fine if canvas placed inside div to render image but instead of canvas whole div need to be rotated.
It's not the job of Fabric.js to rotate (or really in any way manipulate) regular HTML elements.
You can use CSS3 styles to rotate the div which contains the canvas:
#myCanvasWrapperDiv
{
transform: rotate(30deg);
-ms-transform: rotate(30deg); /* IE 9 */
-webkit-transform: rotate(30deg); /* Safari and Chrome */
-o-transform: rotate(30deg); /* Opera */
-moz-transform: rotate(30deg); /* Firefox */
}
If you want the user to be able to rotate this div, you'll have to give them a way to do it. That could be as simple as a text input with a button or an onchange event handler, or complex, like a rotating handle. There may be a jQuery plugin to make elements "rotatable", similar to drag-and-drop plugins.

One SVG file, many SVG gradients inside

I’m making a set of buttons which use dynamic gradients. I’ve taken care of Firefox 3.6+ and WebKit by using their proprietary CSS extensions and all I need to do is support Opera, iOS and IE9 by using background-image: url("gradient.svg").
This is relatively easy, I made an SVG file, linked it and got it working. However, I’m making a set so I need at least 6 gradients. When I normally do it in images, I create a sprite for fast HTTP access. I’m not sure how to achieve this in SVG – can I use one file and access different parts of its XML by using #identifiers, like XBL does?
My current SVG:
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN"
"http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
<svg version="1.1" xmlns="http://www.w3.org/2000/svg">
<defs>
<linearGradient id="select-gradient" x1="0" x2="0" y1="0" y2="1">
<stop offset="0%" stop-color="rgb(231,244,248)"/>
<stop offset="100%" stop-color="rgb(207,233,241)"/>
</linearGradient>
<style type="text/css">
rect {
fill: url(#select-gradient);
}
</style>
</defs>
<rect x="0" y="0" rx="6" ry="6" height="100%" width="100%"/>
</svg>
And then I have CSS:
.button-1 {
background-image: url("gradient-1.svg");
}
.button-2 {
background-image: url("gradient-2.svg");
}
I want to do something like this:
.button-1 {
background-image: url("gradient.svg#gradient1");
}
.button-2 {
background-image: url("gradient.svg#gradient2");
}
Is it even possible? Can you help me out? I really don’t wanna push 6 XML files when I can do it with one.
If you just want gradients for button backgrounds, most of this can be acheived in css. For the remaining browsers, ie6 + can user ms filters:
http://msdn.microsoft.com/en-us/library/ms532847.aspx
iOS uses webkit to render, so you can use -webkit vendor prefix. Unfortunately you will still need svg for opera, but this may make it easier (or just use a normal image sprite for opera's 1% of users)
in theory - according to SVG documentation #Params it is possible. You could use 2 params for setting up both colors, you could create multiple rects with different gradients, height set to 0 and then make only one 100% (like ?gradient2=100%)
What you could do is load your SVG file that contains all of the definitions first, and then load your other SVG files.
Using Firefox, jQuery SVG , and a minor shot of framework...
in your XHTML:
<div id="common_svg_defs"><!--ieb--></div>
<div id="first_thing"><!--ieb--></div>
<div id="second_thing"><!--ieb--></div>
in your JavaScript:
var do_stuff = function()
{
// load your common svg file with this goo.
$('#common_svg_defs').svg({
loadURL: 'path/filename.svg',
onLoad: function(svg, error) { run_test(svg, error);} });
}
var run_test = function(svg, error)
{
if (typeof(error) !== "undefined")
{
if (typeof(console.log) !== "undefined")
{
console.log(error);
}
}
else
{
// load your other svg files here, or just
// set a flag letting you know it's ready.
$('#first_thing').svg({
loadURL: 'path/anotherfilename.svg',
onLoad: function(svg, error) { somecallback(svg, error);} });
$('#second_thing').svg({
loadURL: 'path/anotherfilename.svg',
onLoad: function(svg, error) { somecallback(svg, error);} });
}
}
Because the id can be found in the documents scope, the SVG are capable of finding the IRI reference.
This allows you to define things once (that would not otherwise be defined in a css) and avoid id collisions.
Cheers,
Christopher Smithson

Resources