diff --git a/microdata.module b/microdata.module
index f2e2425..151a58a 100644
--- a/microdata.module
+++ b/microdata.module
@@ -275,6 +275,43 @@ function microdata_entity_load($entities, $type) {
     // Process the mapping into an attributes array that is easier for the
     // field formatter to use.
     $entity->microdata = microdata_mapping_to_attributes($microdata_mapping, $type, $entity);
+
+    // There are some properties we want to output as separate meta tags;
+    // generate itemrefs for them here, so we can output the main items (with
+    // itemrefs) and properties anywhere, independently.
+    // Store the properties, so we can easily output their meta tags anywhere.
+
+    // Store itemref & data for the title.
+    if (!empty($entity->microdata['title']['#attributes']['itemprop'])) {
+      $title = check_plain(entity_label($type, $entity));
+      if ($title) {
+        $itemref_id = microdata_get_itemref_id();
+
+        // Complete the entity title's microdata info.
+        $entity->microdata['title']['#attributes']['content'] = $title;
+        $entity->microdata['title']['#attributes']['id'] = $itemref_id;
+
+        // Store this info separately, for output as separate meta tag.
+        microdata_item($type, $id, $entity->microdata['title']['#attributes'], TRUE);
+
+        // Store the itemref in the microdata list for the main entity.
+        $attributes = $entity->microdata['#attributes'];
+        $attributes['itemref'] = array($itemref_id);
+        microdata_item($type, $id, $attributes);
+      }
+    }
+    // Store itemref & data for the Schema.org URL.
+    // @todo Remove once Google recognizes itemids #1784580.
+    if (!empty($entity->microdata['microdata_schema_url']['#attributes']['itemprop'])) {
+      $itemref_id = microdata_get_itemref_id();
+      $entity->microdata['microdata_schema_url']['#attributes']['id'] = $itemref_id;
+
+      microdata_item($type, $id, $entity->microdata['microdata_schema_url']['#attributes'], TRUE);
+
+      $attributes = $entity->microdata['#attributes'];
+      $attributes['itemref'] = array($itemref_id);
+      microdata_item($type, $id, $attributes);
+    }
   }
 }
 
@@ -331,13 +368,18 @@ function microdata_field_attach_view_alter(&$output, $context) {
         foreach ($object_entities as $delta => $object_entity) {
           if (isset($object_entity->microdata['#attributes']['itemscope'])) {
             $field_id = microdata_get_itemref_id();
-            $object_id = microdata_get_itemref_id();
             // Add the referenced entity to the microdata item list.
             $object_attributes = $object_entity->microdata['#attributes'];
             if (isset($field_microdata['#attributes']['itemprop'])) {
               $object_attributes['itemprop'] = $field_microdata['#attributes']['itemprop'];
             }
-            $object_attributes['id'][] = $object_id;
+            if (!empty($object_attributes['id'])) {
+              $object_id = reset($object_attributes['id']);
+            }
+            else {
+              $object_id = microdata_get_itemref_id();
+              $object_attributes['id'][] = $object_id;
+            }
             $object_attributes['itemref'][] = $field_id;
             list($entity_id,,) = entity_extract_ids($object_entity_type, $object_entity);
             microdata_item($object_entity_type, $entity_id, $object_attributes);
@@ -498,51 +540,12 @@ function theme_microdata_image_formatter(&$variables) {
  * microdata_field_attach_view_alter or by any custom layout systems like Views.
  */
 function microdata_process_page(&$variables) {
-  $page = &$variables['page'];
   $items = microdata_item();
-  $titles = array();
-  $schema_urls = array();
-
-  foreach ($items as $entity_type => &$ids) {
-    foreach ($ids as $id => &$attributes) {
-      $entity = entity_load_single($entity_type, $id);
-      // Add the titles' itemprops. Because we can't be sure that the title
-      // will be nested within the entity's HTML element, we use <meta> to
-      // place a copy of the value.
-      if (!empty($entity->microdata['title']) && !empty($entity->microdata['title']['#attributes'])) {
-        // Sometimes the title is actually a 'name' property.
-        $title = isset($entity->title) ? $entity->title : $entity->name;
-        $field_id = microdata_get_itemref_id();
-        $entity->microdata['title']['#attributes']['content'] = $title;
-        $entity->microdata['title']['#attributes']['id'] = $field_id;
-        $titles[$entity_type][] = array('#attributes' => $entity->microdata['title']['#attributes']);
-        $attributes['#attributes']['itemref'][] = $field_id;
-      }
-      // Add the Schema.org URLs.
-      // @todo Remove once Google recognizes itemids #1784580.
-      if (!empty($entity->microdata['microdata_schema_url'])) {
-        $field_id = microdata_get_itemref_id();
-        $entity->microdata['microdata_schema_url']['#attributes']['id'] = $field_id;
-        $schema_urls[$entity_type][] = array('#attributes' => $entity->microdata['microdata_schema_url']['#attributes']);
-        $attributes['#attributes']['itemref'][] = $field_id;
-      }
-    }
-  }
 
-  // Add Schema.org URLs.
-  // @todo Remove this when Google accepts itemid #1784580.
-  $page['content']['microdata_items']['items'] = array(
+  $variables['page']['content']['microdata_items'] = array(
     '#type' => 'markup',
     '#markup' => theme('microdata_item_meta_tags', array('items' => $items, 'html_tag' => 'meta')),
   );
-  $page['content']['microdata_items']['titles'] = array(
-    '#type' => 'markup',
-    '#markup' => theme('microdata_item_meta_tags', array('items' => $titles, 'html_tag' => 'meta')),
-  );
-  $page['content']['microdata_items']['schema_urls'] = array(
-    '#type' => 'markup',
-    '#markup' => theme('microdata_item_meta_tags', array('items' => $schema_urls, 'html_tag' => 'link')),
-  );
 }
 
 /**
@@ -551,15 +554,38 @@ function microdata_process_page(&$variables) {
  * As this runs once, it has less overhead than theme_html_tag when there is a
  * large number of items on a page. However, it still allows developers to
  * switch to a different html element.
+ *
+ * @todo reassess whether all this needs to be in a theming function.
+ * Note: $variables['html_tag'] is now ignored by default.
  */
 function theme_microdata_item_meta_tags($variables) {
   $output = '';
   $item_array = $variables['items'];
-  $tag = $variables['html_tag'];
 
   foreach ($item_array as $entity_type => $items) {
     foreach ($items as $id => $item) {
-      $output .= "<$tag" . drupal_attributes($item['#attributes']) . " />";
+
+      if (isset($item['#attributes']['itemscope'])) {
+        // itemscope is not allowed in a meta tag, so we make it a span.
+        // Also, itemscope is no 'real attribute', in that it does not have a
+        // value. So print 'itemscope', not 'itemscope="".
+        unset($item['#attributes']['itemscope']);
+        $output .= '<span itemscope ' . drupal_attributes($item['#attributes']) . "></span>";
+      }
+      else {
+        $output .= '<meta ' . drupal_attributes($item['#attributes']) . ' />';
+      }
+
+      // Output any extra property tags.
+      foreach ($item as $prop_id => $attributes) {
+        if ($prop_id !== '#attributes') {
+          $tag = 'meta';
+          if ($prop_id == 'url') {
+            $tag = 'link';
+          }
+          $output .= "<$tag" . drupal_attributes($attributes['#attributes']) . " />";
+        }
+      }
     }
   }
   return $output;
@@ -645,6 +671,9 @@ function microdata_save_mapping($entity_type, $bundle, $mapping) {
  * In order to support Views and Panels, we place items as meta tags at the
  * bottom of the page and use the itemref feature of microdata to set the
  * relationships.
+ * This separate storage of microdata (which is mostly a copy of all individual
+ * $entity->microdata structures, with 'itemref' attributes added to them)
+ * ensures we have all data available for output at e.g. hook_process_page().
  *
  * @param $item_type
  *   Usually the entity type when working with an entity or a reference field.
@@ -654,8 +683,11 @@ function microdata_save_mapping($entity_type, $bundle, $mapping) {
  *   Usually the entity id, this id will be used to join attributes from
  *   multiple points in the system and should be an ID that can be used in
  *   conjunction with the item_type to identify the property.
- * @param $attributes
- *   The HTML attributes that should be added to this meta tag. These should
+ * @param $input_attributes
+ *   An empty value, for retrieving values. The return value will be all
+ *   stored microdata items, or only the items for a specific type/id,
+ *   depending on the value of $item_type and $item_id.
+ *   An array, for storing HTML attributes for an item. These should
  *   usually be taken directly from the variable provided in
  *   $entity->microdata[$field_name]['#attributes']. Can include:
  *   - itemscope: Required to declare this as an item.
@@ -665,45 +697,89 @@ function microdata_save_mapping($entity_type, $bundle, $mapping) {
  *   - itemid: The globally unique ID for the item.
  *   - itemref: The HTML ids of any fields related to this item. This is
  *     usually set within the microdata_preprocess_field implementation.
+ *   All attributes previously set for this item type/id will be overwritten by
+ *   the values in $input_attributes, except previous 'itemref' attributes will
+ *   be kept / merged into $input_attributes (if $extra_property is FALSE).
+ * @param $extra_property
+ *   If TRUE, the $input_attributes are not added for the item itself, but are
+ *   a separate microdata property that should be output along with the item.
+ *   $input_attributes must contain a value for the 'itemprop' attribute in this
+ *   case, and the caller is responsible for creating an 'itemref' pointing to
+ *   this extra property, in the main item's attributes.
  *
  * @return Array or NULL
  *   If no parameters were provided, return the array of all items. If
  *   parameters were provided, the function is simply adding a new item, so
  *   return NULL.
  */
-function microdata_item($item_type = NULL, $item_id = NULL, $input_attributes = array()) {
-  static $microdata_item;
+function microdata_item($item_type = NULL, $item_id = NULL, $input_attributes = array(), $extra_property = FALSE) {
+  static $microdata_items;
 
   // If this is the first item to be added, initalize the array.
-  if (empty($microdata_item)) {
-    $microdata_item = array();
+  if (empty($microdata_items)) {
+    $microdata_items = array();
   }
 
-  // If no arguments have been passed in, return the complete array. Rather
-  // than performing an array_unique check whenever an item is added, remove
-  // duplicates before returning.
-  if (empty($item_type)) {
-    foreach ($microdata_item as $entity_type => &$items) {
-      foreach ($items as &$item) {
-        foreach ($item as &$attributes) {
-          foreach ($attributes as &$attribute) {
-            if (is_array($attribute)) {
-              $attribute = array_unique($attribute);
-            }
-          }
-        }
+  if (empty($input_attributes) || !is_array($input_attributes)) {
+
+    // Return currently stored items.
+    if (isset($item_type) && isset($microdata_items[$item_type])) {
+      if (isset($item_id)) {
+        // Return ID-specific items
+        return isset($microdata_items[$item_type][$item_id]) ?
+          $microdata_items[$item_type][$item_id] : NULL;
       }
+      // Return type-specific items, indexed by ID
+      return isset($microdata_items[$item_type]) ?
+        $microdata_items[$item_type] : NULL;
     }
+    return $microdata_items;
+  }
 
-    return $microdata_item;
+  if (empty($item_type) || empty($item_id)) {
+    // Invalid function arguments.
+    return;
   }
 
-  // If the attributes array doesn't exist yet, initalize it.
-  if (!isset($microdata_item[$item_type][$item_id]['#attributes'])) {
-    $microdata_item[$item_type][$item_id]['#attributes'] = array();
+  // If the attributes array doesn't exist yet, initalize it; also if
+  // $extra_property == TRUE.
+  if (!isset($microdata_items[$item_type][$item_id]['#attributes'])) {
+    $microdata_items[$item_type][$item_id]['#attributes'] = array();
+  }
+
+  $item = &$microdata_items[$item_type][$item_id];
+  if ($extra_property) {
+
+    // Set extra separate meta tag for this item; the 'itemprop' contains
+    // the unique key (which can overwrite tags set earlier).
+    // This is sometimes a scalar, sometimes an array.
+    if (!empty($input_attributes['itemprop'])
+        && (!is_array($input_attributes['itemprop']) || !empty($input_attributes['itemprop'][0]))
+    ) {
+      if (is_array($input_attributes['itemprop'])) {
+        $prop = $input_attributes['itemprop'][0];
+      }
+      else {
+        $prop = $input_attributes['itemprop'];
+      }
+      $item[$prop]['#attributes'] = $input_attributes;
+    }
+  }
+  else {
+
+    // Set attributes. Keep/merge earlier set 'itemref' values;
+    // overwrite others.
+    if (!empty($item['#attributes']['itemref'])) {
+      if (empty($input_attributes['itemref'])) {
+        $input_attributes['itemref'] = $item['#attributes']['itemref'];
+      }
+      else {
+        $input_attributes['itemref'] = array_merge($item['#attributes']['itemref'], $input_attributes['itemref']);
+      }
+    }
+    // Set attributes for this item
+    $item['#attributes'] = $input_attributes;
   }
-  $attributes = &$microdata_item[$item_type][$item_id]['#attributes'];
-  $attributes = array_merge_recursive($attributes, $input_attributes);
 }
 
 /**
