diff --git a/core/includes/pager.inc b/core/includes/pager.inc
index 91532e3..52913ad 100644
--- a/core/includes/pager.inc
+++ b/core/includes/pager.inc
@@ -141,31 +141,39 @@ function pager_get_query_parameters() {
   return $query;
 }
 
-/**
- * Returns HTML for a query pager.
- *
- * Menu callbacks that display paged query results should call theme('pager') to
- * retrieve a pager control so that users can view other results. Format a list
- * of nearby pages with additional query results.
- *
- * @param $variables
- *   An associative array containing:
- *   - tags: An array of labels for the controls in the pager.
- *   - element: An optional integer to distinguish between multiple pagers on
- *     one page.
- *   - parameters: An associative array of query string parameters to append to
- *     the pager links.
- *   - quantity: The number of pages in the list.
- *
- * @ingroup themeable
- */
-function theme_pager($variables) {
-  $tags = $variables['tags'];
-  $element = $variables['element'];
-  $parameters = $variables['parameters'];
-  $quantity = $variables['quantity'];
+function template_preprocess_pager(&$variables) {
   global $pager_page_array, $pager_total;
 
+  // Declare the actual/effective theme variables.
+  $variables['first'] = NULL;
+  $variables['previous'] = NULL;
+  $variables['next'] = NULL;
+  $variables['last'] = NULL;
+
+  $variables['less'] = FALSE;
+  $variables['more'] = FALSE;
+  $variables['items'] = array();
+  $variables['current_item'] = NULL;
+  $variables['current_index'] = NULL;
+
+  // Nothing to do if there is only one page.
+  $element = &$variables['element'];
+  if ($pager_total[$element] <= 1) {
+    return;
+  }
+
+  // Fill in default link labels.
+  $tags = &$variables['tags'];
+  $tags += array(
+    t('« first'),
+    t('‹ previous'),
+    t('next ›'),
+    t('last »'),
+  );
+
+  $parameters = &$variables['parameters'];
+  $quantity = &$variables['quantity'];
+
   // Calculate various markers within this pager piece:
   // Middle is used to "center" pages around the current page.
   $pager_middle = ceil($quantity / 2);
@@ -177,7 +185,6 @@ function theme_pager($variables) {
   $pager_last = $pager_current + $quantity - $pager_middle;
   // max is the maximum page number
   $pager_max = $pager_total[$element];
-  // End of marker calculations.
 
   // Prepare for generation loop.
   $i = $pager_first;
@@ -191,199 +198,286 @@ function theme_pager($variables) {
     $pager_last = $pager_last + (1 - $i);
     $i = 1;
   }
-  // End of generation loop preparation.
 
-  $li_first = '';
-  $li_previous = '';
-  $li_next = '';
-  $li_last = '';
+  // Second sanity check: If there is only one page, nothing to do.
+  if ($i == $pager_max) {
+    return;
+  }
+  $current_path = current_path();
 
   // Create the "first" and "previous" links if we are not on the first page.
   if ($pager_page_array[$element] > 0) {
-    $li_first = theme('pager_link__first', array(
-      'text' => (isset($tags[0]) ? $tags[0] : t('« first')),
-      'page_new' => pager_load_array(0, $element, $pager_page_array),
-      'element' => $element,
-      'parameters' => $parameters,
-      'attributes' => array('rel' => 'first'),
-    ));
-    $li_previous = theme('pager_link__previous', array(
-      'text' => isset($tags[1]) ? $tags[1] : t('‹ previous'),
-      'page_new' => pager_load_array($pager_page_array[$element] - 1, $element, $pager_page_array),
-      'element' => $element,
-      'parameters' => $parameters,
-      'attributes' => array('rel' => 'prev'),
-    ));
+    $variables['first'] = array(
+      '#theme' => 'pager_link__first',
+      '#text' => $tags[0],
+      '#path' => $current_path,
+      '#element' => $element,
+      '#options' => array(
+        'query' => pager_query_add_page($parameters, $element, 0),
+        'attributes' => array(
+          'title' => t('Go to first page'),
+          'rel' => 'first',
+        ),
+      ),
+    );
+    $variables['previous'] = array(
+      '#theme' => 'pager_link__previous',
+      '#text' => $tags[1],
+      '#path' => $current_path,
+      '#element' => $element,
+      '#options' => array(
+        'query' => pager_query_add_page($parameters, $element, $pager_page_array[$element] - 1),
+        'attributes' => array(
+          'title' => t('Go to previous page'),
+          'rel' => 'prev',
+        ),
+      ),
+    );
   }
 
   // Create the "last" and "next" links if we are not on the last page.
   if ($pager_page_array[$element] < ($pager_total[$element] - 1)) {
-    $li_next = theme('pager_link__next', array(
-      'text' => isset($tags[3]) ? $tags[3] : t('next ›'),
-      'page_new' => pager_load_array($pager_page_array[$element] + 1, $element, $pager_page_array),
-      'element' => $element,
-      'parameters' => $parameters,
-      'attributes' => array('rel' => 'next'),
-    ));
-    $li_last = theme('pager_link__last', array(
-      'text' => (isset($tags[4]) ? $tags[4] : t('last »')),
-      'page_new' => pager_load_array($pager_total[$element] - 1, $element, $pager_page_array),
-      'element' => $element,
-      'parameters' => $parameters,
-      'attributes' => array('rel' => 'last'),
-    ));
+    $variables['next'] = array(
+      '#theme' => 'pager_link__next',
+      '#text' => $tags[2],
+      '#path' => $current_path,
+      '#element' => $element,
+      '#options' => array(
+        'query' => pager_query_add_page($parameters, $element, $pager_page_array[$element] + 1),
+        'attributes' => array(
+          'title' => t('Go to next page'),
+          'rel' => 'next',
+        ),
+      ),
+    );
+    $variables['last'] = array(
+      '#theme' => 'pager_link__last',
+      '#text' => $tags[3],
+      '#path' => $current_path,
+      '#element' => $element,
+      '#options' => array(
+        'query' => pager_query_add_page($parameters, $element, $pager_total[$element] - 1),
+        'attributes' => array(
+          'title' => t('Go to last page'),
+          'rel' => 'last',
+        ),
+      ),
+    );
   }
 
-  if ($pager_total[$element] > 1) {
-    if ($li_first) {
-      $items[] = array(
-        'class' => array('pager-first'),
-        'data' => $li_first,
-      );
-    }
-    if ($li_previous) {
-      $items[] = array(
-        'class' => array('pager-previous'),
-        'data' => $li_previous,
+  // Check whether there are further previous pages.
+  if ($i > 1) {
+    $variables['less'] = '…';
+  }
+  // Generate the actual pager items.
+  for (; $i <= $pager_last && $i <= $pager_max; $i++) {
+    if ($i < $pager_current) {
+      $variables['items'][$i] = array(
+        '#theme' => 'pager_link',
+        '#text' => $i,
+        '#path' => $current_path,
+        '#options' => array(
+          'query' => pager_query_add_page($parameters, $element, $i - 1),
+          'attributes' => array(
+            'title' => t('Go to page @number', array('@number' => $i)),
+          ),
+        ),
+        '#element' => $element,
+        // Specify how many pages are between this item and the current page.
+        // Ignored by default, supplied for alternative implementations.
+        '#interval' => ($pager_current - $i),
       );
     }
-
-    // When there is more than one page, create the pager list.
-    if ($i != $pager_max) {
-      if ($i > 1) {
-        $items[] = array(
-          'class' => array('pager-ellipsis'),
-          'data' => '…',
-        );
-      }
-      // Now generate the actual pager piece.
-      for (; $i <= $pager_last && $i <= $pager_max; $i++) {
-        if ($i < $pager_current) {
-          $items[] = array(
-            'class' => array('pager-item'),
-            'data' => theme('pager_link', array(
-              'text' => $i,
-              'page_new' => pager_load_array($i - 1, $element, $pager_page_array),
-              'element' => $element,
-              'interval' => ($pager_current - $i),
-              'parameters' => $parameters,
-            )),
-          );
-        }
-        if ($i == $pager_current) {
-          $items[] = array(
-            'class' => array('pager-current'),
-            'data' => $i,
-          );
-        }
-        if ($i > $pager_current) {
-          $items[] = array(
-            'class' => array('pager-item'),
-            'data' => theme('pager_link', array(
-              'text' => $i,
-              'page_new' => pager_load_array($i - 1, $element, $pager_page_array),
-              'element' => $element,
-              'interval' => ($i - $pager_current),
-              'parameters' => $parameters,
-            )),
-          );
-        }
-      }
-      if ($i < $pager_max) {
-        $items[] = array(
-          'class' => array('pager-ellipsis'),
-          'data' => '…',
-        );
-      }
-    }
-    // End generation.
-    if ($li_next) {
-      $items[] = array(
-        'class' => array('pager-next'),
-        'data' => $li_next,
-      );
+    if ($i == $pager_current) {
+      $variables['items'][$i] = $i;
+      $variables['current_item'] = &$variables['items'][$i];
+      $variables['current_index'] = $i;
     }
-    if ($li_last) {
-      $items[] = array(
-        'class' => array('pager-last'),
-        'data' => $li_last,
+    if ($i > $pager_current) {
+      $variables['items'][$i] = array(
+        '#theme' => 'pager_link',
+        '#text' => $i,
+        '#path' => $current_path,
+        '#options' => array(
+          'query' => pager_query_add_page($parameters, $element, $i - 1),
+          'attributes' => array(
+            'title' => t('Go to page @number', array('@number' => $i)),
+          ),
+        ),
+        '#element' => $element,
+        // Specify how many pages are between this item and the current page.
+        // Ignored by default, supplied for alternative implementations.
+        '#interval' => ($i - $pager_current),
       );
     }
-    return '<h2 class="element-invisible">' . t('Pages') . '</h2>' . theme('item_list', array(
-      'items' => $items,
-      'attributes' => array('class' => array('pager')),
-    ));
+  }
+  // Check whether there are further next pages.
+  if ($i < $pager_max) {
+    $variables['more'] = '…';
   }
 }
 
 /**
- * Returns HTML for a link to a specific query result page.
+ * Returns HTML for a query pager.
+ *
+ * Menu callbacks that display paged query results should call theme('pager') to
+ * retrieve a pager control so that users can view other results. Format a list
+ * of nearby pages with additional query results.
  *
  * @param $variables
  *   An associative array containing:
- *   - text: The link text. Also used to figure out the title attribute of the
- *     link, if it is not provided in $variables['attributes']['title']; in
- *     this case, $variables['text'] must be one of the standard pager link
- *     text strings that would be generated by the pager theme functions, such
- *     as a number or t('« first').
- *   - page_new: The first result to display on the linked page.
+ *   - first: A render array for the "first" link. NULL if there is none.
+ *   - previous: A render array for the "previous" link. NULL if there is none.
+ *   - next: A render array for the "next" link. NULL if there is none.
+ *   - last: A render array for the "last" link. NULL if there is none.
+ *   - less: An ellipsis if there are further previous pages not contained in
+ *     the pager items, or FALSE if there are no further pages.
+ *   - more: An ellipsis if there are further next pages not contained in the
+ *     pager items, or FALSE if there are no further pages.
+ *   - items: An indexed array that contains render arrays for the actual page
+ *     links.
+ *   - current_item: A reference to the render array value in 'items' for the
+ *     current page. Contains the bare page number by default. Change this to
+ *     adjust the output for the item pointing to the current page.
+ *   - current_index: The index of the current page in 'items'. I.e., use
+ *     $variables['items'][$variables['current_index']] to access the item
+ *     pointing to the current page (which yields the same as 'current_item').
+ *   Additionally, the variables that were originally passed to theme('pager')
+ *   by a module:
+ *   - tags: An array of labels for the controls in the pager.
  *   - element: An optional integer to distinguish between multiple pagers on
  *     one page.
  *   - parameters: An associative array of query string parameters to append to
- *     the pager link.
- *   - attributes: An associative array of HTML attributes to apply to the
- *     pager link.
- *
- * @see theme_pager()
+ *     all pager links.
+ *   - quantity: The number of pages in the list.
  *
  * @ingroup themeable
  */
-function theme_pager_link($variables) {
-  $text = $variables['text'];
-  $page_new = $variables['page_new'];
-  $element = $variables['element'];
-  $parameters = $variables['parameters'];
-  $attributes = $variables['attributes'];
-
-  $page = isset($_GET['page']) ? $_GET['page'] : '';
-  if ($new_page = implode(',', pager_load_array($page_new[$element], $element, explode(',', $page)))) {
-    $parameters['page'] = $new_page;
+function theme_pager($variables) {
+  // If there are not pages, ensure nothing is output.
+  if (!$variables['items']) {
+    return '';
   }
+  $items = array();
 
-  $query = array();
-  if (count($parameters)) {
-    $query = drupal_get_query_parameters($parameters, array());
+  // First and previous links.
+  if ($variables['first']) {
+    $items[] = array(
+      'class' => array('pager-first'),
+      'data' => $variables['first'],
+    );
   }
-  if ($query_pager = pager_get_query_parameters()) {
-    $query = array_merge($query, $query_pager);
+  if ($variables['previous']) {
+    $items[] = array(
+      'class' => array('pager-previous'),
+      'data' => $variables['previous'],
+    );
   }
 
-  // Set each pager link title
-  if (!isset($attributes['title'])) {
-    static $titles = NULL;
-    if (!isset($titles)) {
-      $titles = array(
-        t('« first') => t('Go to first page'),
-        t('‹ previous') => t('Go to previous page'),
-        t('next ›') => t('Go to next page'),
-        t('last »') => t('Go to last page'),
+  // Page links.
+  if ($variables['less']) {
+    $items[] = array(
+      'class' => array('pager-ellipsis'),
+      'data' => $variables['less'],
+    );
+  }
+  foreach ($variables['items'] as $index => $item) {
+    if ($index != $variables['current_index']) {
+      $items[] = array(
+        'class' => array('pager-item'),
+        'data' => $item,
       );
     }
-    if (isset($titles[$text])) {
-      $attributes['title'] = $titles[$text];
-    }
-    elseif (is_numeric($text)) {
-      $attributes['title'] = t('Go to page @number', array('@number' => $text));
+    else {
+      $items[] = array(
+        'class' => array('pager-current'),
+        'data' => $item,
+      );
     }
   }
+  if ($variables['more']) {
+    $items[] = array(
+      'class' => array('pager-ellipsis'),
+      'data' => $variables['more'],
+    );
+  }
+
+  // Next and last links.
+  if ($variables['next']) {
+    $items[] = array(
+      'class' => array('pager-next'),
+      'data' => $variables['next'],
+    );
+  }
+  if ($variables['last']) {
+    $items[] = array(
+      'class' => array('pager-last'),
+      'data' => $variables['last'],
+    );
+  }
 
+  $output = '<h2 class="element-invisible">' . t('Pages') . '</h2>';
+  $output .= theme('item_list__pager', array(
+    'items' => $items,
+    'attributes' => array('class' => array('pager')),
+  ));
+  return $output;
+}
+
+/**
+ * Returns HTML for a link to a specific query result page.
+ *
+ * @param $variables
+ *   An associative array. See theme_link() for details.
+ *
+ * @see theme_pager()
+ *
+ * @ingroup themeable
+ */
+function theme_pager_link($variables) {
+  $text = $variables['text'];
+  $attributes = $variables['options']['attributes'];
+  $html = !empty($variables['options']['html']);
   // @todo l() cannot be used here, since it adds an 'active' class based on the
   //   path only (which is always the current path for pager links). Apparently,
   //   none of the pager links is active at any time - but it should still be
   //   possible to use l() here.
   // @see http://drupal.org/node/1410574
-  $attributes['href'] = url(current_path(), array('query' => $query));
-  return '<a' . new Attribute($attributes) . '>' . check_plain($text) . '</a>';
+  $attributes['href'] = url($variables['path'], $variables['options']);
+  return '<a' . new Attribute($attributes) . '>' . ($html ? $text : check_plain($text)) . '</a>';
+}
+
+/**
+ * Adds the 'page' parameter to the query parameter array of a pager link.
+ *
+ * @param array $query
+ *   An associative array of query parameters to add to.
+ * @param integer $element
+ *   An integer to distinguish between multiple pagers on one page.
+ * @param integer $index
+ *   The index of the target page in the pager array.
+ *
+ * @return array
+ *   The altered $query parameter array.
+ *
+ * @todo Document the pager/element/index architecture and logic. It is not
+ *   clear what is happening in this function as well as pager_load_array(),
+ *   and whether this can be simplified in any way.
+ */
+function pager_query_add_page(array $query, $element, $index) {
+  global $pager_page_array;
+
+  // Determine the first result to display on the linked page.
+  $page_new = pager_load_array($index, $element, $pager_page_array);
+
+  $page = isset($_GET['page']) ? $_GET['page'] : '';
+  if ($new_page = implode(',', pager_load_array($page_new[$element], $element, explode(',', $page)))) {
+    $query['page'] = $new_page;
+  }
+  if ($query_pager = pager_get_query_parameters()) {
+    $query = array_merge($query, $query_pager);
+  }
+  return $query;
 }
 
 /**
diff --git a/core/includes/theme.inc b/core/includes/theme.inc
index cc29a98..b740fd0 100644
--- a/core/includes/theme.inc
+++ b/core/includes/theme.inc
@@ -2107,7 +2107,17 @@ function theme_item_list($variables) {
       $i++;
       $attributes = array();
 
-      if (is_array($item)) {
+      // Temporary hack to enable render arrays.
+      // @see http://drupal.org/node/891112
+      if (is_array($item) && !isset($item['data'])) {
+        $attributes = isset($item['#wrapper_attributes']) ? $item['#wrapper_attributes'] : array();
+        $value = drupal_render($item);
+      }
+      elseif (is_array($item) && isset($item['data']) && is_array($item['data'])) {
+        $attributes = array_diff_key($item, array('data' => 0, 'children' => 0, 'type' => 0));
+        $value = drupal_render($item['data']);
+      }
+      elseif (is_array($item)) {
         $value = '';
         if (isset($item['data'])) {
           $value .= $item['data'];
@@ -3004,20 +3014,8 @@ function drupal_common_theme() {
     'pager' => array(
       'variables' => array('tags' => array(), 'element' => 0, 'parameters' => array(), 'quantity' => 9),
     ),
-    'pager_first' => array(
-      'variables' => array('text' => NULL, 'element' => 0, 'parameters' => array()),
-    ),
-    'pager_previous' => array(
-      'variables' => array('text' => NULL, 'element' => 0, 'interval' => 1, 'parameters' => array()),
-    ),
-    'pager_next' => array(
-      'variables' => array('text' => NULL, 'element' => 0, 'interval' => 1, 'parameters' => array()),
-    ),
-    'pager_last' => array(
-      'variables' => array('text' => NULL, 'element' => 0, 'parameters' => array()),
-    ),
     'pager_link' => array(
-      'variables' => array('text' => NULL, 'page_new' => NULL, 'element' => NULL, 'parameters' => array(), 'attributes' => array()),
+      'variables' => array('text' => NULL, 'path' => NULL, 'options' => array('query' => array(), 'attributes' => array(), 'html' => FALSE)),
     ),
     // From menu.inc.
     'menu_link' => array(
