diff --git a/core.php b/core.php
index 7a0ef18..807ae72 100644
--- a/core.php
+++ b/core.php
@@ -1,88 +1,138 @@
 <?php
 
 namespace Keruald;
 
 /**
  * Keruald, core libraries for Pluton and Xen engines.
  * 
  * Global functions
  */
 
+///
+/// Strings
+///
+
+/**
+ * Pads a multibytes string to a certain length with another string
+ *
+ * @param string $str the input string
+ * @param int $pad_length the target string size
+ * @param string $pad_string the padding characters (optional, default is space)
+ * @param int $pad_type STR_PAD_RIGHT, STR_PAD_LEFT, or STR_PAD_BOTH (optional, default is STR_PAD_RIGHT)
+ * @param string the character encoding (optional)
+ *
+ * @return string the padded string
+ *
+ */
+function mb_str_pad($input, $pad_length, $pad_string = ' ', $pad_type = STR_PAD_RIGHT, $encoding = null) {
+    // Inspired by Ronald Ulysses Swanson method
+    // http://stackoverflow.com/a/27194169/1930997
+    // who followed the str_pad PHP implementation.
+
+    if ($encoding === null) {
+        $encoding = mb_internal_encoding();
+    }
+
+    $padBefore = $pad_type === STR_PAD_BOTH || $pad_type === STR_PAD_LEFT;
+    $padAfter = $pad_type === STR_PAD_BOTH || $pad_type === STR_PAD_RIGHT;
+
+    $pad_length -= mb_strlen($input, $encoding);
+    if ($padBefore && $padAfter) {
+        $targetLength = $pad_length / 2;
+    } else {
+        $targetLength = $pad_length;
+    }
+    $strToRepeatLength = mb_strlen($pad_string, $encoding);
+    $repeatTimes = ceil($targetLength / $strToRepeatLength);
+    $repeatedString = str_repeat($pad_string, max(0, $repeatTimes)); // safe if used with valid Unicode sequences (any charset)
+
+    $paddedString = '';
+    if ($padBefore) {
+        $paddedString = mb_substr($repeatedString, 0, floor($targetLength), $encoding);
+    }
+    $paddedString .= $input;
+    if ($padAfter) {
+        $paddedString .= mb_substr($repeatedString, 0, ceil($targetLength), $encoding);
+    }
+
+    return $paddedString;
+}
+
 ///
 /// Error and debug
 ///
  
 /**
  * Prints human-readable information about a variable, wrapped in a <pre> block
  * 
  * @param mixed $variable the variable to dump
  */
 function dprint_r ($variable) {
     echo '<pre>';
     print_r($variable);
     echo '</pre>';
 }
 
 /**
  * Prints human-readable information about a variable, wrapped in a <pre> block
  * then dies
  * 
  * @param mixed $variable the variable to dump
  */
 function dieprint_r ($variable) {
     dprint_r($variable);
     die;
 };
 
 ///
 /// Client information
 ///
 
 /**
  * Returns the full header or the IP part of it
  *
  * @param string $value The header value
  * @return string the IP part
  */
 function extract_client_ip_from_header ($value) {
     if (strpos($value, ',') !== false) {
         //Header contains 'clientIP, proxyIP, anotherProxyIP'
         //The first value is so the one to return.
         //See draft-ietf-appsawg-http-forwarded-10.
         $ips = explode(',', $value, 2);
         return trim($ips[0]);
     }
 
     return $value;
 }
 
 /**
  * Gets remote IP address.
  *
  * This is intended as a drop-in replacement for $_SERVER['REMOTE_ADDR'],
  * which takes in consideration proxy values.
  */
 function get_remote_addr () {
     $candidates = [
         //Standard header provided by draft-ietf-appsawg-http-forwarded-10
         'HTTP_X_FORWARDED_FOR',
 
         //Legacy headers
         'HTTP_CLIENT_IP',
         'HTTP_FORWARDED',
         'HTTP_FORWARDED_FOR',
         'HTTP_X_CLUSTER_CLIENT_IP',
         'HTTP_X_FORWARDED',
 
         //Default header if no proxy information could be detected
         'REMOTE_ADDR',
     ];
 
     foreach ($candidates as $candidate) {
         if (array_key_exists($candidate, $_SERVER)) {
             return extract_client_ip_from_header($_SERVER[$candidate]);
         }
     }
 
     return '';
 }
diff --git a/tests/CoreTest.php b/tests/CoreTest.php
index 30bc018..dcef8b6 100644
--- a/tests/CoreTest.php
+++ b/tests/CoreTest.php
@@ -1,45 +1,81 @@
 <?php
 
 namespace Keruald;
 
 class CoreTest extends \PHPUnit_Framework_Testcase {
 
+    ///
+    /// Strings
+    ///
+
+    function test_mb_str_pad () {
+        // Tests from http://3v4l.org/UnXTF
+        // http://web.archive.org/web/20150711100913/http://3v4l.org/UnXTF
+
+        $this->assertEquals('àèòàFOOàèòà', mb_str_pad("FOO", 11, "àèò", STR_PAD_BOTH, "UTF-8"));
+        $this->assertEquals('àèòFOOàèòà', mb_str_pad("FOO", 10, "àèò", STR_PAD_BOTH, "UTF-8"));
+        $this->assertEquals('àèòBAAZàèòà', mb_str_pad("BAAZ", 11, "àèò", STR_PAD_BOTH, "UTF-8"));
+        $this->assertEquals('àèòBAAZàèò', mb_str_pad("BAAZ", 10, "àèò", STR_PAD_BOTH, "UTF-8"));
+        $this->assertEquals('FOOBAR', mb_str_pad("FOOBAR", 6, "àèò", STR_PAD_BOTH, "UTF-8"));
+        $this->assertEquals('FOOBAR', mb_str_pad("FOOBAR", 1, "àèò", STR_PAD_BOTH, "UTF-8"));
+        $this->assertEquals('FOOBAR', mb_str_pad("FOOBAR", 0, "àèò", STR_PAD_BOTH, "UTF-8"));
+        $this->assertEquals('FOOBAR', mb_str_pad("FOOBAR", -10, "àèò", STR_PAD_BOTH, "UTF-8"));
+
+        $this->assertEquals('àèòàèòàèFOO', mb_str_pad("FOO", 11, "àèò", STR_PAD_LEFT, "UTF-8"));
+        $this->assertEquals('àèòàèòàFOO', mb_str_pad("FOO", 10, "àèò", STR_PAD_LEFT, "UTF-8"));
+        $this->assertEquals('àèòàèòàBAAZ', mb_str_pad("BAAZ", 11, "àèò", STR_PAD_LEFT, "UTF-8"));
+        $this->assertEquals('àèòàèòBAAZ', mb_str_pad("BAAZ", 10, "àèò", STR_PAD_LEFT, "UTF-8"));
+        $this->assertEquals('FOOBAR', mb_str_pad("FOOBAR", 6, "àèò", STR_PAD_LEFT, "UTF-8"));
+        $this->assertEquals('FOOBAR', mb_str_pad("FOOBAR", 1, "àèò", STR_PAD_LEFT, "UTF-8"));
+        $this->assertEquals('FOOBAR', mb_str_pad("FOOBAR", 0, "àèò", STR_PAD_LEFT, "UTF-8"));
+        $this->assertEquals('FOOBAR', mb_str_pad("FOOBAR", -10, "àèò", STR_PAD_LEFT, "UTF-8"));
+
+        $this->assertEquals('FOOàèòàèòàè', mb_str_pad("FOO", 11, "àèò", STR_PAD_RIGHT, "UTF-8"));
+        $this->assertEquals('FOOàèòàèòà', mb_str_pad("FOO", 10, "àèò", STR_PAD_RIGHT, "UTF-8"));
+        $this->assertEquals('BAAZàèòàèòà', mb_str_pad("BAAZ", 11, "àèò", STR_PAD_RIGHT, "UTF-8"));
+        $this->assertEquals('BAAZàèòàèò', mb_str_pad("BAAZ", 10, "àèò", STR_PAD_RIGHT, "UTF-8"));
+        $this->assertEquals('FOOBAR', mb_str_pad("FOOBAR", 6, "àèò", STR_PAD_RIGHT, "UTF-8"));
+        $this->assertEquals('FOOBAR', mb_str_pad("FOOBAR", 1, "àèò", STR_PAD_RIGHT, "UTF-8"));
+        $this->assertEquals('FOOBAR', mb_str_pad("FOOBAR", 0, "àèò", STR_PAD_RIGHT, "UTF-8"));
+        $this->assertEquals('FOOBAR', mb_str_pad("FOOBAR", -10, "àèò", STR_PAD_RIGHT, "UTF-8"));
+    }
+
     ///
     /// Client information
     ///
 
     function test_extract_client_ip_from_header () {
         $values = [
             //Each value should return 10.0.0.3
             '10.0.0.3',
             '10.0.0.3,10.0.0.4',
             '10.0.0.3, 10.0.0.4',
             '10.0.0.3, 10.0.0.4, lorem ipsum dolor',
         ];
         foreach ($values as $value) {
             $this->assertEquals(
                 '10.0.0.3',
                 extract_client_ip_from_header($value)
             );
         }
 
         $this->assertEmpty(
             extract_client_ip_from_header('')
         );
     }
 
     function test_get_remote_addr () {
         $this->assertEmpty(get_remote_addr());
 
         $_SERVER = [
             'REMOTE_ADDR' => '10.0.0.2'
         ];
         $this->assertEquals('10.0.0.2', get_remote_addr());
 
         $_SERVER += [
             'HTTP_X_FORWARDED_FOR' => '10.0.0.3',
             'HTTP_CLIENT_IP' => '10.0.0.4',
         ];
         $this->assertEquals('10.0.0.3', get_remote_addr(), "HTTP_X_FORWARDED_FOR must be prioritized.");
     }
 }