I am trying to do something that 'should' be fairly simple. My program (Java, using SWT) has a button that when clicked, opens up a dialog with a browser component. This component hits one of our web-services using some specialized input, and the html that comes back contains a inline SVG image and some basic html (a table essentially). Some sample of what this looks like is as follows (truncated for ease of reading)
<!DOCTYPE html>
<head>
<style>
table { border-collapse: collapse; width: 100%;}
table, td, th { border: 1px solid black;}
svg { top:0; left:0; width:612px; height:450px; display:block; margin:auto;}
</style>
</head>
<body><h1><b>Sample Heading</b></h1>
<svg version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
viewBox="0 0 244.8 180" enable-background="new 0 0 244.8 180" xml:space="preserve" preserveAspectRatio="xMinYMin meet">
<!-- A whole lot of SVG commands here. -->
</svg>
<table border="1"><tr><th>Col1</th><th>Col2</th><th>Col3</th><th>Col4</th></tr>
<tr bgcolor="EDFEED"><td align="center">1</td><td>2</td><td>3</td><td>4</td></tr>
</table>
</body>
</html>
NOTE: The CSS line for SVG is there because I was playing with how to auto-adjust the visual display size in the webpage. During the course of my debugging/testing, this has been added/modified/removed multiple times in various forms.
NOTE2: The SVG viewbox size comes from the initial SVG files I'm using. These come from outside of my process.
This works perfectly fine for display in my dialog but where I'm having trouble is I want to allow my user to print the browser contents using a print button at the bottom of the dialog (So what they should get is the Image at the top, ideally scaled to fill a page width while preserve aspect ratio, with the table of supplementary information below it.) If I make a Javascript call from SWT of window.print();, it will print, but two peculiarities tend to occur.
The first is that if I leave that CSS line with the alterations to the SVG, it sometimes ends up writing off the page. I'm fairly sure this has to do with me doing something idiotic with SVG CSS styles that I can't seem to figure out.
The second is that it frequently ends up overwriting the top of the table element. This seems to happen even if I remove the CSS SVG line that is adjusting the image scale/size.
In addition, if I right-click in IE, and choose print-preview, it looks fine at least with the 'not overwriting the top of the table', but when it actually prints, the paper that is produced does not match what preview shows me. This behavior is consistent if I try this from outside my program in a normal IE window.
The HTML is generated dynamically upon the request to the webservice., but if similar HTML is fed into FireFox or Chrome, neither bug occurs (at least in my barebones testing.) Our app will always run on Windows PCs, and SWT uses IE (I'm using version 11 right now) under the hood, which is why I believe I'm running into this problem. I'm not a Web Developer or CSS guru, and I know enough to get myself into trouble, but I'm not sure how to fix this issue at the moment.
Any guidance or others who have dealt with HTML/SVG printing with IE would be appreciated. Thank you.
Related
I have been developing a web page "game" on my PC based in HTML, SVG, and Javascript. It has a large image of the earth loaded into the SVG views through the SVG <image> tag. Testing on my PC this works with no problem, however recently I published it to a public web page (http://rbarryyoung.com/EarthOrbitalSimulator.html) and discovered that only the bottom right quarter of the SVG is rendering on both SVG views on my iPhone and iPad. Like this:
At first, I thought that it was just the image in the SVG viewports, but then I realized that the entire SVG viewport was black except for the lower-right quadrant. The SVG viewport is correctly fully sized, it just appears as if there is some black mask over 3/4s of it (or only 1/4 of it renders).
Here's what I think are the relevant HTML code lines, the containing Div tag for the first SVG view (line 67):
<div id="divSvg1"
style="position:relative; z-index:1; margin:15px;
top:100px;
width:640px; height:640px;
background-color:black;
float:left;"
>
The SVG tag (line 104):
<svg id="svgEa"
style="width:100%; height:100%;"
viewBox="-7500 -7500 15000 15000"
preserveAspectRatio="xMidYMid meet"
clip-path="url(#svgEaClip)"
transform="scale(1.0,1.0)"
version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
<!-- NOTE: All internal units are in KM (or %) -->
And the embedded Image tag (starting at line 160):
<g id="gEaAll" transform="scale(1.0,1.0)" >
<!-- ... -->
<g id="gEaSurfaceFacingBottom" class="eaSurfaceFacing">
<g id=gEarthImage>
<!-- ... -->
<image x="-6413" y="-6413" width="12826" height="12826" href="eosImages/globe-arctic 8bit.png" />
</g>
</g>
The second SVG view is a shadowed (<use..> tag), zoomed view of the first with the same problem.
I have tested this on my PC, on both screens in Chrome, Edge, and IE, where it works correctly on all of them. I have also tested this on my iPhone with both Safari and Edge and my iPad with Safari, Chrome, and Edge with the same failure on all of them. I have tried just a bare <img> tag of the PNG file outside of SVG and that works fine on these platforms.
I do not have any Android platforms to test with, so if anyone wants to try it and let me know, I can add those results here.
I have researched this, and though there's a bunch of stuff about iOS not rendering images, mostly those are a complete failure to render, rather than this very specific partial rendering, and much less specific stuff about SVG differences. Ultimately I didn't find anything that seemed to be the same problem.
To summarize then, my question is: what is causing this problem or what have I done wrong, and how can I fix it? (I do understand that I will need to have a different style/CSS layout for mobile, but I still need to know what needs to be changed to make this render correctly)
Add X and Y coordinates for your <rect />. In your case, your Clip-Path Rectangle is not in an exact coordinate.
Here is the code working for me
<clipPath>
<rect x="-7500px" y="-7500px" width="100%" height="100%" />
<cliPath>
replace this code with your <clipPath> on line 114 and 301.
Here is the Screenshot
Moreover here is a live demo that worked on my Mac Safari as well in windows Chrome, where I took one part of your code.
Update
Check the answer by #fussionweb.
Orignal answer:
You can try the -webkit- prefix before clip-path. It seems to be a safari issue related to clip-path.
Problem:
On page load, for a button, SVG which is being called in CSS background appears. On some event, I apply a class and to it call another SVG in CSS background. However that new SVG does not appear. If in debugger, I try to toggle the background, it then starts appearing.
What am I missing to get this working in IE11?
In Below picture, we are using <button> tag. On some event, the background SVG is changed.
CSS Code
.buttonIcons{
.svgicon-fields-add{ background-image:url('../assets/themes/svg/arrow_active.svg');
width: 16px;
height: 16px;
}
On disabled state, below is the CSS
.buttonIcons.disabled{
.svgicon-fields-add{ background-image:url('../assets/themes/svg/arrow_disabled.svg');
width: 16px;
height: 16px;
}
So initially all buttons are disabled, but if you see the first button needs to show active svg image, but it is not appearing. I can see it in IE11 console, and if in console, I toggle the property, it starts appearing.
The code works fine in Chrome
Thanks in advance for all the suggestions.
SVG file code
<?xml version="1.0" encoding="utf-8"?>
<svg width="16" height="16" version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
viewBox="0 0 16 16" enable-background="new 0 0 16 16" xml:space="preserve">
<g>
<polygon fill="#00a6a0" points="7.6,1.6 6.7,2.4 12.3,8 6.7,13.6 7.6,14.4 14,8 "/>
<polygon fill="#00a6a0" points="2.6,1.6 1.7,2.4 7.3,8 1.7,13.6 2.6,14.4 9,8 "/>
</g>
</svg>
Based on the small number of views, this is an esoteric case, but I'm having the same problem. I've been on it for days. I also had a double problem where IE would not display the background on toggling the style in developer tools.
I thought it was initially a memory issue, where IE was allocating memory for the icon but not actually putting anything there.
So I applied the SVG as an xml-encoded string directly in the class itself. This solved one instance of an SVG background not appearing, but it didn't solve the problem mentioned here. Furthermore, this allowed me to see the toggle event you mentioned.
So I now believe that it is NOT a memory issue, but an issue with IE10/11's SVG rendering engine. The fact that this problem only happens with certain SVG's reinforces the idea that the IE rendering engine is crapping the bed with certain inputs. It also only happens when a CSS SVG background is being overridden by another class's SVG background.
I thus tried setting the object to display:none, then display:block in the hopes of forcing a re-render of the element. This didn't help. I destroyed the element then rebuilt it and appended it back where it was supposed to go. That didn't work.
To make things even more confusing, I was never able to replicate the problem locally. It would only ever manifest in certain environments, leading me to believe that it is a combination of the browser and some server settings. I have no idea what.
Regardless, the point is that IE is remarkably resilient it not re-rendering what it has rendered. And since the SVG engine is apparently separate from the DOM rendering engine, screwing around with the DOM will have no effect on what the SVG renderer has stored. You have to give it quantifiably different data to force the SVG engine to re-render.
The only solution I found was to have my two CSS classes then give IE different image data than what it found on page load.
.Class1 {
background-image: url("data:image/svg+xml,image-data...");
}
.Class2 {
background-image: url("data:image/svg+xml,image-data...");
}
These classes allow default page load states to be covered. Then when JS events change the appearance, instead of changing the class, assign an in-line CSS style with the XML-ified SVG image data with a slight difference. Anything will work. I used an extra space.
onclick="function(){
element.style.backgroundImage = "url(\"data:image/svg+xml,slightly-different-data...\")"
}
To reiterate, it is an SVG rendering problem that occurs on page load. You can force SVG to re-render the image by giving it slightly different data in-line. I would imagine that you could do the same thing with a third class that contains a reference to a slightly different SVG file from the initial one, but I didn't do this.
Make sure the SVG file has the width and height property.
And if there's a 'responsive' option in it, you should remove it.
The option resets the CSS width and height.
I'm trying out the SVG stacking technique to enable multiple icons stacked in a single file, requiring only a single HTTP request from the browser. The technique is described pretty thoroughly here.
Basically the idea is that you put multiple SVG elements into a single SVG file, and use CSS styling to hide all icons, except for the icon you currently want to display. You select the icon you currently want to display using the CSS :target selector.
The technique works for me, except stacking multiple icons causes weird distortions in the displayed icon, even though all other icons are hidden.
In the example I'm working with, I simplified this to stacking only two icons: an US flag icon and an UK flag icon.
The (simplified) SVG file is:
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<svg id="svg153" xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#" xmlns="http://www.w3.org/2000/svg" height="480" width="640" version="1.1" xmlns:cc="http://creativecommons.org/ns#" xmlns:dc="http://purl.org/dc/elements/1.1/">
<svg:style
xmlns:svg="http://www.w3.org/2000/svg" type="text/css">
.i { display: none; }
.i:target { display: block; }
</svg:style>
<svg:svg id="uk" xmlns:svg="http://www.w3.org/2000/svg" class = "i" height="480" width="640" version="1.1">
<!-- SVG elements to draw UK flag -->
</svg:svg>
<svg:svg id="us" xmlns:svg="http://www.w3.org/2000/svg" class = "i" height="480" width="640" version="1.1">
<!-- SVG elements to draw US flag -->
</svg:svg>
</svg>
Note that the CSS is embedded within the SVG file, in a <svg::style> element. The CSS is simply:
.i { display: none; }
.i:target { display: block; }
This way, any svg::svg element with class="i" is automatically invisible, unless we specifically target it in the SVG url. So this way, to display a US flag icon, I would use the following HTML snippet:
<img
src="flags.svg#us"
width="80"
height="60"
alt="SVG Stacked Image"
/>
And of course, to display the UK flag I would change it to src="flags.svg#uk"
Anyway, all of this works wonderfully... except for a strange image distortion which occurs in both Firefox and Chrome when I stack the images.
Here's a screenshot of the US flag when I remove the (hidden) UK flag from the SVG file:
As you can see, it looks fine.
But when I stack it in front of the UK flag, it looks like:
As you can see, the image becomes strangely distorted - it almost looks like what happens to a low-quality JPEG when you get a lot of "artifacts" in the compressed image.
So why exactly is this happening? The other images stacked with the US flag icon are all invisible, so why should they effect the visible icon at all?
I Googled around a lot looking for answers, and although there are definitely many issues and "gotchas" with the SVG stacking technique, they all relate to cross-browser compatibility. However, the technique works fine on most newer browsers up to and including IE9. Also, the distortion happens in both Firefox and Chrome, so this isn't likely to be some cross-browser issue, but rather something I'm doing wrong.
So, what is causing this weird distortion when I apply the SVG stacking technique?
No idea about stacking and target. But I know two simple methods.. may be those can help you out in an easier way.
When you have chosen different svg icons from net or even from computer but each icon is separate.
There is a website 'icomoon.io', where we can choose different icons from online libraries or your custom svg icons from your computer.
Open 'https://icomoon.io/app/'
Choose 'import icons' to upload custom icons from your computer.
At bottom of the page it has 'Add Icons From Library…' to choose icons from online libraries.
From 'select' tool (At Top) select multiple icons as you like.
After selecting multiple icons choose 'Generate SVG,PNG,PDF' button at bottom.
Then to combine all of them in a single file, click on 'settings icon' located just next to the 'Download' option in first button at bottom left.
Choose 'Include Tiles (CSS Sprite) from it.
Put appropriate margins and number of icons in a row as you like and then download the combine sprite with its xml code in demo.html and css definitions in style.css.
When you have already created single SVG file of multiple icons using 'AI' or any other software.
Just upload(import) that file to icomoon.io and click on 'Generate SVG,PNG,PDF' button and download the sprite xml file.
I tried with my own simple svgs and it works fine, no weird distortion happening. So my guess is it's your svgs for uk and us.
http://pastebin.com/dxVtTQKF
I've just read this article from Google Web Fundamentals on using SVG for icons. Two approaches are given. The first is the inline approach. For example:
<svg version="1.1" xmlns="http://www.w3.org/2000/svg"
xmlns:xlink="http://www.w3.org/1999/xlink"
width="32" height="32" viewBox="0 0 32 32">
<path d="M27 4l-15 15-7-7-5 5 12 12 20-20z" fill="#000000"></path>
</svg>
Demo
The second approach is to use an image tag such as: <img src="credit.svg">
I'm also aware SVGs can be defined as:
an object
an embed tag
an iframe
They can also be set as background images in CSS.
My question:
What are the advantages and disadvantages of each approach?
The advantages of using img, object, embed and other with SVG backgound:
<img class="icon icon_foo"> is much shorter and beautiful than inline SVG — <svg class="icon icon_foo"><use xlink:href="#foo"></use></svg>
The disadvantages of using img, object, embed and other with SVG backgound:
you absolutely have no any control of styling options — you cannot set color, outline width and outline color — all you can do is just to put SVG as background and set styles right in SVG file one time
you cannot reuse your icons because of statement above (if you have one icon in two-three-twenty different places on your web page and it should be colored in different colors the only way is to create two-three-twenty equal SVG files with only one difference — fill option ;)
The advantages of using inline SVG:
reusability (eg. you have only one Twitter SVG icon and you can use it everywhere with any styles, so, you do not need 2 or 3 same SVG files just with different fill attribute in each SVG file)
complete control over your icon appearance — styles, colors, sizes
The disadvantages of using inline SVG:
it is longer than IMG and does not look beautiful
Caching:
Using AngularJS or any other framework:
<ng-include src="'icons.html'"></ng-include>
Using VanillaJS:
file icons.js on your server:
var icons = '<svg><symbol id="icon-XXX" viewBox="0 0 256 256"><path d="{here_goes_compounded_path}"></path></symbol></svg>';
in HEAD section:
<script src="icons.js"></script>
<script>
document.getElementById('icon-placeholder').innerHTML(icons);
</script>
right after the BODY tag:
<div id="icon-placeholder" style="display:none;"></div>
Fiddle with the best approach:
Right after body tag place all your SVG icons (or use Angular's or any other framework ng-include):
<body>
<svg style="display:none;">
<symbol id="icon-XXX" viewBox="0 0 256 256"><path d="{here_goes_compounded_path}"></path></symbol>
<symbol id="icon-YYY" viewBox="0 0 256 256"><path d="{here_goes_compounded_path}"></path></symbol>
</svg>
In any place on your web page put an icon:
<svg class="icon icon_red">
<use xlink:href="#icon-XXX"></use>
</svg>
<svg class="icon icon_green">
<use xlink:href="#icon-YYY"></use>
</svg>
<svg class="icon icon_blue">
<use xlink:href="#icon-XXX"></use>
</svg>
In CSS:
.icon {
width: 16px;
height: 16px;
fill: currentColor;
}
.icon_red { fill: #f00; }
.icon_green { fill: #0f0; }
.icon_blue { fill: #00f; }
.icon_foo {
stroke: #000;
stroke-width: 8;
fill: #0f0;
}
Do not forget about magic "currentColor" CSS variable — it will color your SVG icon in current text color:
fill: currentColor;
Further must read:
any front-end RSS feeds: inline SVG is about a year old discussion
http://tympanus.net/codrops/2013/11/27/svg-icons-ftw/
http://tympanus.net/Tutorials/ResponsiveSVGs/index.html
http://ianfeather.co.uk/ten-reasons-we-switched-from-an-icon-font-to-svg/
The answer is, should you be worrying about who's looking at your code? SVG inline will always bloat your HTML depending how complex the drawn SVG is. Inline does in some respect have better performance implications due to the fact it's being loaded directly with the HTML as opposed to loading the SVG externally when using it, for example, as an <img>. However, it's practically unnoticeable and should be the least priority when coming down to performance.
The disadvantages of inline SVG:
Bloats code
Cannot be cached by the browser
No fallback available
IE, without XHTML (and that's if SVG is supported) doesn't support SVG technically, though this is a low priority and we shouldn't care about it in the current world of the web. Still, it's primarily a disadvantage to those ancient warriors.
The advantages of inline SVG:
I can't think of any, seriously. Other nosey people can see it?
Actually, those without CSS enabled can see it.
The disadvantages of using <img> with SVG:
Again, limited fallback support (You can use Modernizr to replace the .svg extension with .png, but then you rely on the user having javascript enabled)
Limited styling options with the SVG
The advantages of using <img> with SVG:
Semantically better than bloating all your code
Readability in your codebase is better for a) yourself and b) other developers who might be working on the project too.
Better maintainability.
Alternative methods:
You can use the following methods you provided in your question (and below) about using <object>, <embed> and <iframe>, although, these also limit the use cases and would need declaring in every HTML document, which can get messy as you progress in a project, whether it be large or small.
The 'better approach':
Disclaimer: By no means is the a 'better for everyone' method. This is simply how I would declare my SVG elements for reusability and gives me full control of identifying my assets when using SVG.
The biggest pitfall for using inline SVG in your HTML is the fallback support. I have always used SVG as a background image (unless it's a webfont I'm using for icons etc.), purely because I can create a .png version and write the fallback in my CSS, like so:
.icon {
display: inline-block;
vertical-align: baseline;
background-repeat: no-repeat;
background-position: center center;
background-size: contain;
}
.icon--16 {
width: 16px;
height: 16px;
}
/* Always declare the PNG before the SVG. */
.icon--foo-blue {
background-image: url('foo-blue.png'); /* Fallback */
background-image: url('foo-blue.svg'), none; /* Modern */
}
.icon--foo-green {
background-image: url('foo-green.png'); /* Fallback */
background-image: url('foo-green.svg'), none; /* Modern */
}
Then, use it:
<span class="icon icon--16 icon--foo-blue"></span>
<span class="icon icon--16 icon--foo-green"></span>
The disadvantage of this:
No support for CSS styling, however, we should be aware of why we are styling SVG in our CSS. If you're using more colours than your website uses then there is generally something not right. Declaring 4 files for the same SVG but in a different colour is not bad practise as it allows us to cache these in the browser for later, throughout any webpage which in the answer constructed below completely removes this purpose. Take advantage of caching on the server/within the browser!
And of course, it depends what kind of image you are trying to render to the user. If you're going to use large SVG files for showcasing something, for example, I would probably think about what kind of users I'm targeting; that being, those on modern browsers because I might want to include fancy inline SVG animations etc.
It really depends on personal preference too. There is no major red flags for using SVG inline to this day, but we still have some heart for those back in the stone ages of less-than IE9. And, not to forget, Android is quirky with SVG too!
EDIT: It seems like there is a large debate over inline SVG's. One word of warning is, there is no reusability doing this. If you're showcasing a fancy website with fancy SVG animations, then by all means, build your entire page in SVG for crying out loud. No one is stopping you. It's personal preference. But for icons, where you will more than likely reuse throughout a project, please, declare them outside your HTML as it gives you greater control in the long run and not because you need a different colour here and there. Think about it. :)
What I tend to do nowadays is wrap it inside a Web Component:
/assets/svg/hex.js
export default `<svg>...</svg>`;
/my-svg.js
class MySvg extends HTMLElement {
connectedCallback() {
const id = this.getAttribute('id');
import(`./assets/svg/${id}.js`).then(svg => {
this.innerHTML = `
${svg.default}
`;
});
}
}
customElements.define("my-svg", MySvg);
index.html
<my-svg id="hex"></my-svg>
This way I get the advantages of inline svg, lazy loading due to dynamic import and without the bloat in the code I'm supposed to work on / maintain, with just one custom element. And the id attribute makes clear which svg I'm loading.
When I use a SVG image as the tiled background for the body of my page, there is a slight gap between the tiles. If I switch to the PNG version of the file, it does not happen.
I am using the latest version of Google Chrome for Mac – version 19.0.1084.56. I haven’t tested Windows because I don’t have that platform. File works as expected in Safari and FF.
After a bunch of Google searches and searching here on SO I haven’t found any similar reports. Maybe someone here has a solution.
Here is my test code:
<!DOCTYPE html>
<html>
<head>
<title>SVG background test</title>
<style type="text/css">
body {
background-color: #FFF;
background-image: url(img/assets/background.svg);
background-repeat: repeat;
}
</style>
</head>
<body>
</body>
</html>
This turned out not just to be happening it Chrome, but it was more noticeable in that browser. As I posted above, here is the solution:
After reading a solution for a slightly different Chrome/svg problem I figured out the answer. Posting back here in case someone else runs across this issue. I created my SVG file in Illustrator. Apparently my artboard/image size was not in precise pixels. I discovered this by opening my SVG file in a text editor and looking at the top of the file I saw this :
width="229.9999px" height="147.4256px"
I opened the svg file in illustrator and made sure that the dimensions were exactly even pixels, then double checked again in the text file. Corrected dimensions were:
width="230px height="148px"
For some reason simply editing the values in the text file did not work for me, but then again it was faster to just fix in Illustrator than figure out how to edit the svg text file correctly. At any rate, now I do not have a gap in my tiles. Hope that helps someone else if they are having this problem.
There are four things to look for (this answer is for other people who end up here looking for a solution):
Making sure the height and width are integers aka "without a fractional component" (as mentioned by Violet, no decimals)
Example: height="326.25" vs height="326"
Making sure your viewbox is also integers
Example viewBox="0 0 306.25 753" vs viewBox="0 0 306 753"
And if you have a viewport, you can also set the preserveAspectRatio attribute
Learn More On MDN
Another thing to pay attention to is have the height and width attribute set in the svg tag. This addresses an IE background-size sizing problem. This is a good article about it.
In my situation, the SVG repeated correctly in Chrome, but had a gap in Firefox until I set all my attributes to integers.
It's not a problem of width or height. It appears even if your svg pattern is 100px x 100px and have no decimal.
You can resolve the problem putting preserveAspectRatio="none" in your svg file as it : <svg preserveAspectRatio="none" ...>
But when you want to repeat a svg pattern you can't choose this option because your pattern will be distorted.
Have you found the answer ?