twitter bootstrap icons are missing in print

FULL CSS SOLUTION

I have written a CSS print stylesheet solution that should solve 80-90% of this problem occurring for sites that require the icons(glyphicons) from bootstrap to show up when printing without requiring the user to turn on “print background images” in their browser and this solution will work in ALL major browsers(Chrome, Safari, Firefox, IE).

This solution is detailed referencing the bootstrap issue specifically but it should be possible leverage it for other similar issues of background images not printing when needed. It also assumes you are using a separate @media print {} stylesheet. I’ll explain 10-20% of situations it doesn’t solve and why at the end(as well as a fix for these occurrences).

The issue of background-image, background-position and width height properties being used exclusively to position and size sprite images is solved by replacing with the properties content: url(), clip: rect(), margin-top and margin-left along with some overrides.

In my case we were using the <i class="icon-globe"></i> to show links for courses available internationally and so users frequently printed this list but the important indicating information was removed by the browser. I had found the solution of copying all the CSS to display the icons into our print stylesheet along with adding the property value
-webkit-print-color-adjust:exact; to the


    [class^="icon-"], [class*=" icon-"] {
    background-image: url("../img/glyphicons-halflings.png");
    background-position: 14px 14px;
    background-repeat: no-repeat...  
    } 

but this only solved the problem in Chrome and Safari with no indication from the community that Firefox or IE would be supporting this webkit property any time soon.

So we need to completely change how <i class="icon-globe"></i> is rendered when the page is sent to the printer driver by the browser.

The standard method with executing sprites is to declare a visible opening space(14px by 14px in this case) and then reposition the background-image behind that space so the appropriate icon can show through.

To effectively replicate this in the foreground we will use the content: url() to call the image and then clip: rect() to cut this image down to the appropriate icon and then use negative values in margin-top and margin-left to position the new foreground image back where the background image icon had originally been positioned.

A difficulty is cutting the image down using clip requires 4 coordinates(top, right, bottom, left) while background-position only requires 2 coordinates(xpos, ypos – the pixel distances from the top left corner). The other difficulty in using the clip property is that unlike padding or margin these coordinates are not calculated from their respective outside border in but from the top and left sides only which actually makes our math conversion from background-position a little easier but may take some time to get used to.

More info on clip property
(ran out of links due to my low reputation so you’ll need to figure out what sneaky thing I’ve done)
www.ibloomstudios[dot]com/articles/misunderstood_css_clip/
css-tricks[dot]com/css-sprites-with-inline-images/
tympanus[dot]net/codrops/2013/01/16/understanding-the-css-clip-property/

THE ACTUAL CODE

Getting back to the <i class="icon-globe"></i> example, we want to convert


    [class^="icon-"], [class*=" icon-"] {
    display: inline-block;
    width: 14px;
    height: 14px;
    *margin-right: .3em;
    line-height: 14px;
    vertical-align: text-top;
    background-image: url("../img/glyphicons-halflings.png");
    background-position: 14px 14px;
    background-repeat: no-repeat;
    }
    //skipping other icons...

    .icon-globe {
    background-position: -336px -144px;
    }

into this in the print stylesheet


    i[class^="icon-"], i[class*=" icon-"] {
    display: inline-block;
    vertical-align: text-top;
    width: 14px; 
    background-image:none!important; 
    background-repeat:no-repeat;
    background-position: 0 0!important;
    }
    //skipping other icons...

    i.icon-globe::after {
    clip: rect(144px 350px 158px 336px)!important;
    margin-left: -336px!important;
    margin-top: -144px!important;
    content: url('../img/glyphicons-halflings.png')!important;
    position:absolute!important;
    width:auto!important;
    height:auto!important;
    }

We can see that taking the background-position(xpos & ypos OR left & top) coordinates and changing to positives are the same as clip: rect(top, left+14px, top+14px, left).

Then we use the original negative background-position: left top; as margin-left and margin-top.

This CSS also includes some !important overrides in case the original bootstrap icon is displayed over top of our new clipped image which is stripped out upon printing.

That worked for the globe-icon and solved my specific problem but then I wondered how many other indicating icons were not being printed and so I used some clever replace all commands in notepad to create a single line version of the bootstrap icon CSS and tab delimited each element (plus added some px to the zeros so columns would line up)…


    .icon-glass {   background-position:    0   px  0   px  ;   }
    .icon-music {   background-position:    -24 px  0   px  ;   }
    .icon-search    {   background-position:    -48 px  0   px  ;   }
    .icon-envelope  {   background-position:    -72 px  0   px  ;   }
    .icon-heart {   background-position:    -96 px  0   px  ;   }
    .icon-star  {   background-position:    -120    px  0   px  ;   }
    .icon-star-empty    {   background-position:    -144    px  0   px  ;   }
    .icon-user  {   background-position:    -168    px  0   px  ;   }
    .icon-film  {   background-position:    -192    px  0   px  ;   }
    .icon-th-large  {   background-position:    -216    px  0   px  ;   }
    .icon-th    {   background-position:    -240    px  0   px  ;   }
    .icon-th-list   {   background-position:    -264    px  0   px  ;   }
    .icon-ok    {   background-position:    -288    px  0   px  ;   }
    .icon-remove    {   background-position:    -312    px  0   px  ;   }
    .icon-zoom-in   {   background-position:    -336    px  0   px  ;   }
    .icon-zoom-out  {   background-position:    -360    px  0   px  ;   }
    .icon-off   {   background-position:    -384    px  0   px  ;   }
    .icon-signal    {   background-position:    -408    px  0   px  ;   }
    .icon-cog   {   background-position:    -432    px  0   px  ;   }
    .icon-trash {   background-position:    -456    px  0   px  ;   }
    .icon-home  {   background-position:    0   px  -24 px  ;   }
    .icon-file  {   background-position:    -24 px  -24 px  ;   }
    .icon-time  {   background-position:    -48 px  -24 px  ;   }
    .icon-road  {   background-position:    -72 px  -24 px  ;   }
    .icon-download-alt  {   background-position:    -96 px  -24 px  ;   }
    .icon-download  {   background-position:    -120    px  -24 px  ;   }
    .icon-upload    {   background-position:    -144    px  -24 px  ;   }
    .icon-inbox {   background-position:    -168    px  -24 px  ;   }
    .icon-play-circle   {   background-position:    -192    px  -24 px  ;   }
    .icon-repeat    {   background-position:    -216    px  -24 px  ;   }
    .icon-refresh   {   background-position:    -240    px  -24 px  ;   }
    .icon-list-alt  {   background-position:    -264    px  -24 px  ;   }
    .icon-lock  {   background-position:    -287    px  -24 px  ;   }
    .icon-flag  {   background-position:    -312    px  -24 px  ;   }
    .icon-headphones    {   background-position:    -336    px  -24 px  ;   }
    .icon-volume-off    {   background-position:    -360    px  -24 px  ;   }
    .icon-volume-down   {   background-position:    -384    px  -24 px  ;   }
    .icon-volume-up {   background-position:    -408    px  -24 px  ;   }
    .icon-qrcode    {   background-position:    -432    px  -24 px  ;   }
    .icon-barcode   {   background-position:    -456    px  -24 px  ;   }
    .icon-tag   {   background-position:    0   px  -48 px  ;   }
    .icon-tags  {   background-position:    -25 px  -48 px  ;   }
    .icon-book  {   background-position:    -48 px  -48 px  ;   }
    .icon-bookmark  {   background-position:    -72 px  -48 px  ;   }
    .icon-print {   background-position:    -96 px  -48 px  ;   }
    .icon-camera    {   background-position:    -120    px  -48 px  ;   }
    .icon-font  {   background-position:    -144    px  -48 px  ;   }
    .icon-bold  {   background-position:    -167    px  -48 px  ;   }
    .icon-italic    {   background-position:    -192    px  -48 px  ;   }
    .icon-text-height   {   background-position:    -216    px  -48 px  ;   }
    .icon-text-width    {   background-position:    -240    px  -48 px  ;   }
    .icon-align-left    {   background-position:    -264    px  -48 px  ;   }
    .icon-align-center  {   background-position:    -288    px  -48 px  ;   }
    .icon-align-right   {   background-position:    -312    px  -48 px  ;   }
    .icon-align-justify {   background-position:    -336    px  -48 px  ;   }
    .icon-list  {   background-position:    -360    px  -48 px  ;   }
    .icon-indent-left   {   background-position:    -384    px  -48 px  ;   }
    .icon-indent-right  {   background-position:    -408    px  -48 px  ;   }
    .icon-facetime-video    {   background-position:    -432    px  -48 px  ;   }
    .icon-picture   {   background-position:    -456    px  -48 px  ;   }
    .icon-pencil    {   background-position:    0   px  -72 px  ;   }
    .icon-map-marker    {   background-position:    -24 px  -72 px  ;   }
    .icon-adjust    {   background-position:    -48 px  -72 px  ;   }
    .icon-tint  {   background-position:    -72 px  -72 px  ;   }
    .icon-edit  {   background-position:    -96 px  -72 px  ;   }
    .icon-share {   background-position:    -120    px  -72 px  ;   }
    .icon-check {   background-position:    -144    px  -72 px  ;   }
    .icon-move  {   background-position:    -168    px  -72 px  ;   }
    .icon-step-backward {   background-position:    -192    px  -72 px  ;   }
    .icon-fast-backward {   background-position:    -216    px  -72 px  ;   }
    .icon-backward  {   background-position:    -240    px  -72 px  ;   }
    .icon-play  {   background-position:    -264    px  -72 px  ;   }
    .icon-pause {   background-position:    -288    px  -72 px  ;   }
    .icon-stop  {   background-position:    -312    px  -72 px  ;   }
    .icon-forward   {   background-position:    -336    px  -72 px  ;   }
    .icon-fast-forward  {   background-position:    -360    px  -72 px  ;   }
    .icon-step-forward  {   background-position:    -384    px  -72 px  ;   }
    .icon-eject {   background-position:    -408    px  -72 px  ;   }
    .icon-chevron-left  {   background-position:    -432    px  -72 px  ;   }
    .icon-chevron-right {   background-position:    -456    px  -72 px  ;   }
    .icon-plus-sign {   background-position:    0   px  -96 px  ;   }
    .icon-minus-sign    {   background-position:    -24 px  -96 px  ;   }
    .icon-remove-sign   {   background-position:    -48 px  -96 px  ;   }
    .icon-ok-sign   {   background-position:    -72 px  -96 px  ;   }
    .icon-question-sign {   background-position:    -96 px  -96 px  ;   }
    .icon-info-sign {   background-position:    -120    px  -96 px  ;   }
    .icon-screenshot    {   background-position:    -144    px  -96 px  ;   }
    .icon-remove-circle {   background-position:    -168    px  -96 px  ;   }
    .icon-ok-circle {   background-position:    -192    px  -96 px  ;   }
    .icon-ban-circle    {   background-position:    -216    px  -96 px  ;   }
    .icon-arrow-left    {   background-position:    -240    px  -96 px  ;   }
    .icon-arrow-right   {   background-position:    -264    px  -96 px  ;   }
    .icon-arrow-up  {   background-position:    -289    px  -96 px  ;   }
    .icon-arrow-down    {   background-position:    -312    px  -96 px  ;   }
    .icon-share-alt {   background-position:    -336    px  -96 px  ;   }
    .icon-resize-full   {   background-position:    -360    px  -96 px  ;   }
    .icon-resize-small  {   background-position:    -384    px  -96 px  ;   }
    .icon-plus  {   background-position:    -408    px  -96 px  ;   }
    .icon-minus {   background-position:    -433    px  -96 px  ;   }
    .icon-asterisk  {   background-position:    -456    px  -96 px  ;   }
    .icon-exclamation-sign  {   background-position:    0   px  -120    px  ;   }
    .icon-gift  {   background-position:    -24 px  -120    px  ;   }
    .icon-leaf  {   background-position:    -48 px  -120    px  ;   }
    .icon-fire  {   background-position:    -72 px  -120    px  ;   }
    .icon-eye-open  {   background-position:    -96 px  -120    px  ;   }
    .icon-eye-close {   background-position:    -120    px  -120    px  ;   }
    .icon-warning-sign  {   background-position:    -144    px  -120    px  ;   }
    .icon-plane {   background-position:    -168    px  -120    px  ;   }
    .icon-calendar  {   background-position:    -192    px  -120    px  ;   }
    .icon-random    {   background-position:    -216    px  -120    px  ;   }
    .icon-comment   {   background-position:    -240    px  -120    px  ;   }
    .icon-magnet    {   background-position:    -264    px  -120    px  ;   }
    .icon-chevron-up    {   background-position:    -288    px  -120    px  ;   }
    .icon-chevron-down  {   background-position:    -313    px  -119    px  ;   }
    .icon-retweet   {   background-position:    -336    px  -120    px  ;   }
    .icon-shopping-cart {   background-position:    -360    px  -120    px  ;   }
    .icon-folder-close  {   background-position:    -384    px  -120    px  ;   }
    .icon-folder-open   {   background-position:    -408    px  -120    px  ;   }
    .icon-resize-vertical   {   background-position:    -432    px  -119    px  ;   }
    .icon-resize-horizontal {   background-position:    -456    px  -118    px  ;   }
    .icon-hdd   {   background-position:    0   px  -144    px  ;   }
    .icon-bullhorn  {   background-position:    -24 px  -144    px  ;   }
    .icon-bell  {   background-position:    -48 px  -144    px  ;   }
    .icon-certificate   {   background-position:    -72 px  -144    px  ;   }
    .icon-thumbs-up {   background-position:    -96 px  -144    px  ;   }
    .icon-thumbs-down   {   background-position:    -120    px  -144    px  ;   }
    .icon-hand-right    {   background-position:    -144    px  -144    px  ;   }
    .icon-hand-left {   background-position:    -168    px  -144    px  ;   }
    .icon-hand-up   {   background-position:    -192    px  -144    px  ;   }
    .icon-hand-down {   background-position:    -216    px  -144    px  ;   }
    .icon-circle-arrow-right    {   background-position:    -240    px  -144    px  ;   }
    .icon-circle-arrow-left {   background-position:    -264    px  -144    px  ;   }
    .icon-circle-arrow-up   {   background-position:    -288    px  -144    px  ;   }
    .icon-circle-arrow-down {   background-position:    -312    px  -144    px  ;   }
    .icon-globe {   background-position:    -336    px  -144    px  ;   }
    .icon-wrench    {   background-position:    -360    px  -144    px  ;   }
    .icon-tasks {   background-position:    -384    px  -144    px  ;   }
    .icon-filter    {   background-position:    -408    px  -144    px  ;   }
    .icon-briefcase {   background-position:    -432    px  -144    px  ;   }
    .icon-fullscreen    {   background-position:    -456    px  -144    px  ;   }

…and then I could use an excel spreadsheet to do all the calculations in one go,
I setup an excel sheet to do any sprite modifications as long as the formatting above is used and we only need 3 variables to replicate this process -img path, width and height, I will update with exact formula in those cells if people request those details but for now here is the result(after a bit more clever replace all commands in notepad++ to remove spaces between integers and px and adding some carriage returns)…


    i.icon-glass::after{
    clip: rect( 0px  14px  14px  0px)!important;
    margin-top:  0px!important;
    margin-left: 0px!important;
    content: url('../img/glyphicons-halflings.png')!important; position:absolute!important;
    width:auto!important;
    height:auto!important;
    }

    i.icon-music::after{
    clip: rect( 0px  38px  14px  24px)!important;
    margin-top:  0px!important;
    margin-left: -24px!important;
    content: url('../img/glyphicons-halflings.png')!important;
    position:absolute!important;
    width:auto!important;
    height:auto!important;
    }

    i.icon-search::after{
    clip: rect( 0px  62px  14px  48px)!important;
    margin-top:  0px!important;
    margin-left: -48px!important;
    content: url('../img/glyphicons-halflings.png')!important;
    position:absolute!important;
    width:auto!important;
    height:auto!important;
    }

Arg I ran out of character space and hyperlinks since my reputation is too low which you can help me with)
I posted the entire CSS result in the Bootstrap Issue Report referenced in an earlier answer
https://github.com/twitter/bootstrap/issues/4412

WHEN IT WONT WORK

Now anyone that has tested this by viewing in their browser window using their print stylesheet instead of screen will see that it works perfectly and as I said earlier this solution as far as I can tell will work except in 10%-20% percent of cases. The exceptions to this solution will only show up when actually printing the pages(or printing to a file for paperless debugging).

What happens is the new foreground images sprites can overflow outside printable areas because of the position: absolute; property which is required to use clip property. When it comes to the W3C standard the rendering of these images is undefined as stated in the CSS Paged Media Module Level 3 in section 4.2-Content outside the page box;

This specification does not define how boxes positioned outside the page box are handled.

(Also check this for an older but better explanation HTML print with absolute postitions )

So what do the browser giants do when no standard has been agreed upon, they all do something different. What happens is the entire sprite image(non-visible portion) that overflows along the top, bottom and sides of the printable page area forces the browsers to decide how to handle and reconcile the CSS and printable page areas. This browser correction is not visible when viewing the in the browser because it is all one page and images can overflow side limits without issue. I’ll explain what each one does as of May 28 2014 and most likely why it is happening this way.

First lets go with the browser that handles it the best,

Internet Explorer!

(I bet you though I was going to say anything else)
The image is clipped properly but is pushed away from limiting printable area edges and so will appear in the wrong place on print out.

Safari and Chrome behave similarly, the image is moved by limiting printable area edges but the clip remains in the spot is was designated so wrong or no icon is shown.

Firefox appears to handle this the worst by only printing icons on the first page and if overflow occurs then forces all remaining icons into the top page on top of one another within the div or section it exists. (one might argue that this precludes Firefox from the overall solution but the fact that the first page works makes me hopeful that Mozilla will resolve in the future if we ask nicely)

Why do I say this will work for 80-90%? because the size of the sprite and the distance from printable area are 2 determining factors that will vary widely from page to page and should only effect in most cases up to 20% of the printable area.

SOLUTION FOR THESE 10-20% OF OCCURRENCES

In my case the icon is being used in a large list across many pages and so the globe-icon at the top of almost every page is misaligned or the wrong icon depending on which browser.
Since I know this page will be printed often and needs to be accurate then I need to make sure this works at least 99% of the time. I’ll do this by cutting out the globe-icon from the sprite and and insert it without all the extra positioning and clipping CSS (which is the original best answer for this issue).


    i.icon-globe::after{
     content: url('../img/globe-glyphicon.png')!important;
    }

and what about that 1% of users that still cant print this off properly, I print to a PDF file from a browser that does work and I make that available to download and print.

Thx for reading (@_@)

Leave a Comment