diff --git a/l10n/en-US/viewer.properties b/l10n/en-US/viewer.properties
index de6fd95db..1c4dd1cde 100644
--- a/l10n/en-US/viewer.properties
+++ b/l10n/en-US/viewer.properties
@@ -87,3 +87,5 @@ loading_error=An error occurred while loading the PDF.
 # Some common types are e.g.: "Check", "Text", "Comment", "Note"
 text_annotation_type=[{{type}} Annotation]
 request_password=PDF is protected by a password:
+
+printing_not_supported=Warning: Printing is not fully supported by this browser.
diff --git a/web/viewer.css b/web/viewer.css
index b8e86f2c8..80a8c5a40 100644
--- a/web/viewer.css
+++ b/web/viewer.css
@@ -1113,7 +1113,16 @@ canvas {
   font-size: 10px;
 }
 
+@page {
+  margin: 0;
+} 
+
+#printContainer {
+  display: none;
+}
+
 @media print {
+  /* Rules for browsers that don't support mozPrintCallback. */
   #sidebarContainer, .toolbar, #loadingBox, #errorWrapper, .textLayer {
     display: none;
   }
@@ -1135,6 +1144,19 @@ canvas {
   .page[data-loaded] {
     display: block;
   }
+
+  /* Rules for browsers that support mozPrintCallback */
+  body[data-mozPrintCallback] #outerContainer {
+    display: none;
+  }
+  body[data-mozPrintCallback] #printContainer {
+    display: block;
+  }
+  #printContainer canvas {
+    position: relative;
+    top: 0;
+    left: 0;
+  }
 }
 
 @media all and (max-width: 950px) {
diff --git a/web/viewer.html b/web/viewer.html
index c59d9fcf3..28e57f700 100644
--- a/web/viewer.html
+++ b/web/viewer.html
@@ -102,11 +102,9 @@
                    <span data-l10n-id="open_file_label">Open</span>
                 </button>
 
-                <!--
                 <button id="print" class="toolbarButton print" title="Print" tabindex="11" data-l10n-id="print" onclick="window.print()">
                   <span data-l10n-id="print_label">Print</span>
                 </button>
-                -->
 
                 <button id="download" class="toolbarButton download" title="Download" onclick="PDFView.download();" tabindex="12" data-l10n-id="download">
                   <span data-l10n-id="download_label">Download</span>
@@ -176,5 +174,6 @@
       </div> <!-- mainContainer -->
 
     </div> <!-- outerContainer -->
+    <div id="printContainer"></div>
   </body>
 </html>
diff --git a/web/viewer.js b/web/viewer.js
index 516081f6f..cbe20bb66 100644
--- a/web/viewer.js
+++ b/web/viewer.js
@@ -379,6 +379,17 @@ var PDFView = {
     return currentPageNumber;
   },
 
+  get supportsPrinting() {
+    var canvas = document.createElement('canvas');
+    var value = 'mozPrintCallback' in canvas;
+    // shadow
+    Object.defineProperty(this, 'supportsPrinting', { value: value,
+                                                      enumerable: true,
+                                                      configurable: true,
+                                                      writable: false });
+    return value;
+  },
+
   open: function pdfViewOpen(url, scale, password) {
     var parameters = {password: password};
     if (typeof url === 'string') { // URL
@@ -1041,6 +1052,26 @@ var PDFView = {
       params[unescape(key)] = unescape(value);
     }
     return params;
+  },
+
+  beforePrint: function pdfViewSetupBeforePrint() {
+    if (!this.supportsPrinting) {
+      var printMessage = mozL10n.get('printing_not_supported', null,
+          'Warning: Printing is not fully supported by this browser.');
+      this.error(printMessage);
+      return;
+    }
+    var body = document.querySelector('body');
+    body.setAttribute('data-mozPrintCallback', true);
+    for (var i = 0, ii = this.pages.length; i < ii; ++i) {
+      this.pages[i].beforePrint();
+    }
+  },
+
+  afterPrint: function pdfViewSetupAfterPrint() {
+    var div = document.getElementById('printContainer');
+    while (div.hasChildNodes())
+      div.removeChild(div.lastChild);
   }
 };
 
@@ -1360,6 +1391,44 @@ var PageView = function pageView(container, pdfPage, id, scale,
     div.setAttribute('data-loaded', true);
   };
 
+  this.beforePrint = function pageViewBeforePrint() {
+    var pdfPage = this.pdfPage;
+    var viewport = pdfPage.getViewport(1);
+
+    var canvas = this.canvas = document.createElement('canvas');
+    canvas.width = viewport.width;
+    canvas.height = viewport.height;
+    canvas.style.width = viewport.width + 'pt';
+    canvas.style.height = viewport.height + 'pt';
+
+    var printContainer = document.getElementById('printContainer');
+    printContainer.appendChild(canvas);
+
+    var self = this;
+    canvas.mozPrintCallback = function(obj) {
+      var ctx = obj.context;
+      var renderContext = {
+        canvasContext: ctx,
+        viewport: viewport
+      };
+
+      pdfPage.render(renderContext).then(function() {
+        // Tell the printEngine that rendering this canvas/page has finished.
+        obj.done();
+        self.pdfPage.destroy();
+      }, function(error) {
+        console.error(error);
+        // Tell the printEngine that rendering this canvas/page has failed.
+        // This will make the print proces stop.
+        if ('abort' in object)
+          obj.abort();
+        else
+          obj.done();
+        self.pdfPage.destroy();
+      });
+    };
+  };
+
   this.updateStats = function pageViewUpdateStats() {
     if (PDFJS.pdfBug && Stats.enabled) {
       var stats = this.stats;
@@ -1706,6 +1775,10 @@ window.addEventListener('load', function webViewerLoad(evt) {
     document.querySelector('#viewSearch').classList.remove('hidden');
   }
 
+  if (!PDFView.supportsPrinting) {
+    document.getElementById('print').classList.add('hidden');
+  }
+
   // Listen for warnings to trigger the fallback UI.  Errors should be caught
   // and call PDFView.error() so we don't need to listen for those.
   PDFJS.LogManager.addLogger({
@@ -1960,3 +2033,11 @@ window.addEventListener('keydown', function keydown(evt) {
     evt.preventDefault();
   }
 });
+
+window.addEventListener('beforeprint', function beforePrint(evt) {
+  PDFView.beforePrint();
+});
+
+window.addEventListener('afterprint', function afterPrint(evt) {
+  PDFView.afterPrint();
+});