diff --git a/examples/acroforms/forms.js b/examples/acroforms/forms.js
new file mode 100644
index 000000000..6ec92766d
--- /dev/null
+++ b/examples/acroforms/forms.js
@@ -0,0 +1,141 @@
+/* -*- Mode: Java; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set shiftwidth=2 tabstop=2 autoindent cindent expandtab: */
+
+//
+// Basic AcroForms input controls rendering
+//
+
+'use strict';
+
+var formFields = {};
+
+function setupForm(div, content, scale) {
+  function bindInputItem(input, item) {
+    if (input.name in formFields) {
+      var value = formFields[input.name];
+      if (input.type == 'checkbox')
+        input.checked = value;
+      else if (!input.type || input.type == 'text')
+        input.value = value;
+    }
+    input.onchange = function pageViewSetupInputOnBlur() {
+      if (input.type == 'checkbox')
+        formFields[input.name] = input.checked;
+      else if (!input.type || input.type == 'text')
+        formFields[input.name] = input.value;
+    };
+  }
+  function createElementWithStyle(tagName, item) {
+    var element = document.createElement(tagName);
+    element.style.left = (item.x * scale) + 'px';
+    element.style.top = (item.y * scale) + 'px';
+    element.style.width = Math.ceil(item.width * scale) + 'px';
+    element.style.height = Math.ceil(item.height * scale) + 'px';
+    return element;
+  }
+  function assignFontStyle(element, item) {
+    var fontStyles = '';
+    if ('fontSize' in item)
+      fontStyles += 'font-size: ' + Math.round(item.fontSize * scale) + 'px;';
+    switch (item.textAlignment) {
+      case 0:
+        fontStyles += 'text-align: left;';
+        break;
+      case 1:
+        fontStyles += 'text-align: center;';
+        break;
+      case 2:
+        fontStyles += 'text-align: right;';
+        break;
+    }
+    element.setAttribute('style', element.getAttribute('style') + fontStyles);
+  }
+
+  var items = content.getAnnotations();
+  for (var i = 0; i < items.length; i++) {
+    var item = items[i];
+    switch (item.type) {
+      case 'Widget':
+        if (item.fieldType != 'Tx' && item.fieldType != 'Btn' &&
+            item.fieldType != 'Ch')
+          break;
+        var inputDiv = createElementWithStyle('div', item);
+        inputDiv.className = 'inputHint';
+        div.appendChild(inputDiv);
+        var input;
+        if (item.fieldType == 'Tx') {
+          input = createElementWithStyle('input', item);
+        }
+        if (item.fieldType == 'Btn') {
+          input = createElementWithStyle('input', item);
+          if (item.flags & 32768) {
+            input.type = 'radio';
+             // radio button is not supported
+          } else if (item.flags & 65536) {
+            input.type = 'button';
+            // pushbutton is not supported
+          } else {
+            input.type = 'checkbox';
+          }
+        }
+        if (item.fieldType == 'Ch') {
+          input = createElementWithStyle('select', item);
+          // select box is not supported
+        }
+        input.className = 'inputControl';
+        input.name = item.fullName;
+        input.title = item.alternativeText;
+        assignFontStyle(input, item);
+        bindInputItem(input, item);
+        div.appendChild(input);
+        break;
+    }
+  }
+}
+
+function renderPage(div, pdf, pageNumber, callback) {
+  var page = pdf.getPage(pageNumber);
+  var scale = 1.5;
+
+  var pageDisplayWidth = page.width * scale;
+  var pageDisplayHeight = page.height * scale;
+
+  var pageDivHolder = document.createElement('div');
+  pageDivHolder.className = 'pdfpage';
+  pageDivHolder.style.width = pageDisplayWidth + 'px';
+  pageDivHolder.style.height = pageDisplayHeight + 'px';
+  div.appendChild(pageDivHolder);
+
+  // Prepare canvas using PDF page dimensions
+  var canvas = document.createElement('canvas');
+  var context = canvas.getContext('2d');
+  canvas.width = pageDisplayWidth;
+  canvas.height = pageDisplayHeight;
+  pageDivHolder.appendChild(canvas);
+
+
+  // Render PDF page into canvas context
+  page.startRendering(context, callback);
+
+  // Prepare and populate form elements layer
+  var formDiv = document.createElement('div');
+  pageDivHolder.appendChild(formDiv);
+
+  setupForm(formDiv, page, scale);
+}
+
+PDFJS.getPdf(pdfWithFormsPath, function getPdfForm(data) {
+  // Instantiate PDFDoc with PDF data
+  var pdf = new PDFJS.PDFDoc(data);
+  
+  // Rendering all pages starting from first
+  var viewer = document.getElementById('viewer');
+  var pageNumber = 1;
+  renderPage(viewer, pdf, pageNumber++, function pageRenderingComplete() {
+    if (pageNumber > pdf.numPages)
+      return; // All pages rendered
+    // Continue rendering of the next page
+    renderPage(viewer, pdf, pageNumber++, pageRenderingComplete);
+  });
+});
+
diff --git a/examples/acroforms/index.html b/examples/acroforms/index.html
new file mode 100644
index 000000000..5fad4648a
--- /dev/null
+++ b/examples/acroforms/index.html
@@ -0,0 +1,52 @@
+<!doctype html>
+<html>
+
+<head>
+  <!-- In production, only one script (pdf.js) is necessary -->
+  <!-- In production, change the content of PDFJS.workerSrc below -->
+  <script type="text/javascript" src="../../src/core.js"></script>
+  <script type="text/javascript" src="../../src/util.js"></script>
+  <script type="text/javascript" src="../../src/canvas.js"></script>
+  <script type="text/javascript" src="../../src/obj.js"></script>
+  <script type="text/javascript" src="../../src/function.js"></script>
+  <script type="text/javascript" src="../../src/charsets.js"></script>
+  <script type="text/javascript" src="../../src/cidmaps.js"></script>
+  <script type="text/javascript" src="../../src/colorspace.js"></script>
+  <script type="text/javascript" src="../../src/crypto.js"></script>
+  <script type="text/javascript" src="../../src/evaluator.js"></script>
+  <script type="text/javascript" src="../../src/fonts.js"></script>
+  <script type="text/javascript" src="../../src/glyphlist.js"></script>
+  <script type="text/javascript" src="../../src/image.js"></script>
+  <script type="text/javascript" src="../../src/metrics.js"></script>
+  <script type="text/javascript" src="../../src/parser.js"></script>
+  <script type="text/javascript" src="../../src/pattern.js"></script>
+  <script type="text/javascript" src="../../src/stream.js"></script>
+  <script type="text/javascript" src="../../src/worker.js"></script>
+  <script type="text/javascript" src="../../external/jpgjs/jpg.js"></script>
+
+  <script type="text/javascript">
+    // Specify the main script used to create a new PDF.JS web worker.
+    // In production, change this to point to the combined `pdf.js` file.
+    PDFJS.workerSrc = '../../src/worker_loader.js';
+
+    // Specify the PDF with AcroForm here
+    var pdfWithFormsPath = '../../test/pdfs/f1040.pdf';
+  </script>
+  
+  <style>
+  .pdfpage { position:relative; top: 0; left: 0; border: solid 1px black; margin: 10px; }
+  .pdfpage > canvas { position: absolute; top: 0; left: 0; }
+  .pdfpage > div { position: absolute; top: 0; left: 0; }
+  .inputControl { background: transparent; border: 0px none; position: absolute; margin: auto; }
+  .inputControl[type='checkbox'] { margin: 0px; }
+  .inputHint { opacity: 0.2; background: #ccc; position: absolute; }
+  </style>
+
+  <script type="text/javascript" src="forms.js"></script>
+</head>
+
+<body>
+  <div id="viewer"></div>
+</body>
+
+</html>
diff --git a/src/core.js b/src/core.js
index a6effd2bd..71c18f178 100644
--- a/src/core.js
+++ b/src/core.js
@@ -274,46 +274,115 @@ var Page = (function PageClosure() {
       }
     },
     getLinks: function pageGetLinks() {
+      var links = [];
+      var annotations = pageGetAnnotations();
+      var i, n = annotations.length;
+      for (i = 0; i < n; ++i) {
+        if (annotations[i].type != 'Link')
+          continue;
+        links.push(annotations[i]);
+      }
+      return links;
+    },
+    getAnnotations: function pageGetAnnotations() {
       var xref = this.xref;
+      function getInheritableProperty(annotation, name) {
+        var item = annotation;
+        while (item && !item.has(name)) {
+          item = xref.fetchIfRef(item.get('Parent'));
+        }
+        if (!item)
+          return null;
+        return item.get(name);
+      }
+
       var annotations = xref.fetchIfRef(this.annotations) || [];
       var i, n = annotations.length;
-      var links = [];
+      var items = [];
       for (i = 0; i < n; ++i) {
-        var annotation = xref.fetch(annotations[i]);
+        var annotationRef = annotations[i];
+        var annotation = xref.fetch(annotationRef);
         if (!isDict(annotation))
           continue;
         var subtype = annotation.get('Subtype');
-        if (!isName(subtype) || subtype.name != 'Link')
+        if (!isName(subtype))
           continue;
         var rect = annotation.get('Rect');
         var topLeftCorner = this.rotatePoint(rect[0], rect[1]);
         var bottomRightCorner = this.rotatePoint(rect[2], rect[3]);
 
-        var link = {};
-        link.x = Math.min(topLeftCorner.x, bottomRightCorner.x);
-        link.y = Math.min(topLeftCorner.y, bottomRightCorner.y);
-        link.width = Math.abs(topLeftCorner.x - bottomRightCorner.x);
-        link.height = Math.abs(topLeftCorner.y - bottomRightCorner.y);
-        var a = this.xref.fetchIfRef(annotation.get('A'));
-        if (a) {
-          switch (a.get('S').name) {
-            case 'URI':
-              link.url = a.get('URI');
-              break;
-            case 'GoTo':
-              link.dest = a.get('D');
+        var item = {};
+        item.type = subtype.name;
+        item.x = Math.min(topLeftCorner.x, bottomRightCorner.x);
+        item.y = Math.min(topLeftCorner.y, bottomRightCorner.y);
+        item.width = Math.abs(topLeftCorner.x - bottomRightCorner.x);
+        item.height = Math.abs(topLeftCorner.y - bottomRightCorner.y);
+        switch (subtype.name) {
+          case 'Link':
+            var a = this.xref.fetchIfRef(annotation.get('A'));
+            if (a) {
+              switch (a.get('S').name) {
+                case 'URI':
+                  link.url = a.get('URI');
+                  break;
+                case 'GoTo':
+                  link.dest = a.get('D');
+                  break;
+                default:
+                  TODO('other link types');
+              }
+            } else if (annotation.has('Dest')) {
+              // simple destination link
+              var dest = annotation.get('Dest');
+              link.dest = isName(dest) ? dest.name : dest;
+            }
+            break;
+          case 'Widget':
+            var fieldType = getInheritableProperty(annotation, 'FT');
+            if (!isName(fieldType))
               break;
-            default:
-              TODO('other link types');
-          }
-        } else if (annotation.has('Dest')) {
-          // simple destination link
-          var dest = annotation.get('Dest');
-          link.dest = isName(dest) ? dest.name : dest;
+            item.fieldType = fieldType.name;
+            // Building the full field name by collecting the field and
+            // its ancestors 'T' properties and joining them using '.'.
+            var fieldName = [];
+            var namedItem = annotation, ref = annotationRef;
+            while (namedItem) {
+              var parentRef = namedItem.get('Parent');
+              var parent = xref.fetchIfRef(parentRef);
+              var name = namedItem.get('T');
+              if (name)
+                fieldName.unshift(stringToPDFString(name));
+              else {
+                // The field name is absent, that means more than one field
+                // with the same name may exist. Replacing the empty name
+                // with the '`' plus index in the parent's 'Kids' array.
+                // This is not in the PDF spec but necessary to id the
+                // the input controls.
+                var kids = xref.fetchIfRef(parent.get('Kids'));
+                var j, jj;
+                for (j = 0, jj = kids.length; j < jj; j++) {
+                  if (kids[j].num == ref.num && kids[j].gen == ref.gen)
+                    break;
+                }
+                fieldName.unshift('`' + j);
+              }
+              namedItem = parent;
+              ref = parentRef;
+            }
+            item.fullName = fieldName.join('.');
+            var alternativeText = stringToPDFString(annotation.get('TU') || '');
+            item.alternativeText = alternativeText;
+            var da = getInheritableProperty(annotation, 'DA') || '';
+            var m = /([\d\.]+)\sTf/.exec(da);
+            if (m)
+              item.fontSize = parseFloat(m[1]);
+            item.textAlignment = getInheritableProperty(annotation, 'Q');
+            item.flags = getInheritableProperty(annotation, 'Ff') || 0;
+            break;
         }
-        links.push(link);
+        items.push(item);
       }
-      return links;
+      return items;
     },
     startRendering: function pageStartRendering(ctx, callback, textLayer)  {
       this.ctx = ctx;
@@ -352,6 +421,7 @@ var PDFDocModel = (function PDFDocModelClosure() {
     assertWellFormed(stream.length > 0, 'stream must have data');
     this.stream = stream;
     this.setup();
+    this.acroForm = this.xref.fetchIfRef(this.catalog.catDict.get('AcroForm'));
   }
 
   function find(stream, needle, limit, backwards) {
diff --git a/web/viewer.js b/web/viewer.js
index 153de99aa..32b3101c2 100644
--- a/web/viewer.js
+++ b/web/viewer.js
@@ -458,7 +458,7 @@ var PageView = function pageView(container, content, id, pageWidth, pageHeight,
     delete this.canvas;
   };
 
-  function setupLinks(content, scale) {
+  function setupAnnotations(content, scale) {
     function bindLink(link, dest) {
       link.href = PDFView.getDestinationHash(dest);
       link.onclick = function pageViewSetupLinksOnclick() {
@@ -467,18 +467,27 @@ var PageView = function pageView(container, content, id, pageWidth, pageHeight,
         return false;
       };
     }
+    function createElementWithStyle(tagName, item) {
+      var element = document.createElement(tagName);
+      element.style.left = (Math.floor(item.x - view.x) * scale) + 'px';
+      element.style.top = (Math.floor(item.y - view.y) * scale) + 'px';
+      element.style.width = Math.ceil(item.width * scale) + 'px';
+      element.style.height = Math.ceil(item.height * scale) + 'px';
+      return element;
+    }
 
-    var links = content.getLinks();
-    for (var i = 0; i < links.length; i++) {
-      var link = document.createElement('a');
-      link.style.left = (Math.floor(links[i].x - view.x) * scale) + 'px';
-      link.style.top = (Math.floor(links[i].y - view.y) * scale) + 'px';
-      link.style.width = Math.ceil(links[i].width * scale) + 'px';
-      link.style.height = Math.ceil(links[i].height * scale) + 'px';
-      link.href = links[i].url || '';
-      if (!links[i].url)
-        bindLink(link, ('dest' in links[i]) ? links[i].dest : null);
-      div.appendChild(link);
+    var items = content.getAnnotations();
+    for (var i = 0; i < items.length; i++) {
+      var item = items[i];
+      switch (item.type) {
+        case 'Link':
+          var link = createElementWithStyle('a', item);
+          link.href = item.url || '';
+          if (!item.url)
+            bindLink(link, ('dest' in item) ? item.dest : null);
+          div.appendChild(link);
+          break;
+      }
     }
   }
 
@@ -598,7 +607,7 @@ var PageView = function pageView(container, content, id, pageWidth, pageHeight,
       }).bind(this), textLayer
     );
 
-    setupLinks(this.content, this.scale);
+    setupAnnotations(this.content, this.scale);
     div.setAttribute('data-loaded', true);
 
     return true;
@@ -889,6 +898,8 @@ window.addEventListener('pagechange', function pagechange(evt) {
 
 window.addEventListener('keydown', function keydown(evt) {
   var curElement = document.activeElement;
+  if (curElement && curElement.tagName == 'INPUT')
+    return;
   var controlsElement = document.getElementById('controls');
   while (curElement) {
     if (curElement === controlsElement)