Code Coverage
 
Classes and Traits
Functions and Methods
Lines
Total
0.00% covered (danger)
0.00%
0 / 1
80.00% covered (warning)
80.00%
4 / 5
CRAP
95.83% covered (success)
95.83%
46 / 48
Encoding
0.00% covered (danger)
0.00%
0 / 1
80.00% covered (warning)
80.00%
4 / 5
32
95.83% covered (success)
95.83%
46 / 48
 decodeSymbol
100.00% covered (success)
100.00%
1 / 1
7
100.00% covered (success)
100.00%
7 / 7
 getCodePoint
0.00% covered (danger)
0.00%
0 / 1
5.27
77.78% covered (warning)
77.78%
7 / 9
 decodeUTF16
100.00% covered (success)
100.00%
1 / 1
9
100.00% covered (success)
100.00%
15 / 15
 decodeANSI
100.00% covered (success)
100.00%
1 / 1
5
100.00% covered (success)
100.00%
8 / 8
 encodeUTF8
100.00% covered (success)
100.00%
1 / 1
6
100.00% covered (success)
100.00%
9 / 9
<?php
declare(strict_types=1);
namespace PhpOffice\WMF\Renderer;
/**
 * Conversion of the strings of metafiles to UTF-8
 */
class Encoding
{
    /**
     * Characters of Windows-1252 from 0x80 to 0x9F
     */
    protected const WINDOWS_1252 = [
        0x20AC, 0x81, 0x201A, 0x0192, 0x201E, 0x2026, 0x2020, 0x2021, 0x02C6, 0x2030, 0x0160, 0x2039, 0x0152, 0x8D, 0x017D, 0x8F,
        0x90, 0x2018, 0x2019, 0x201C, 0x201D, 0x2022, 0x2013, 0x2014, 0x02DC, 0x2122, 0x0161, 0x203A, 0x0153, 0x9D, 0x017E, 0x0178,
    ];
    /**
     * Characters of the Symbol font (from 0x20 to 0xFF) which are not ASCII characters
     */
    protected const SYMBOL = [
        0x22 => 0x2200, 0x24 => 0x2203, 0x27 => 0x220B, 0x2A => 0x2217, 0x2D => 0x2212, 0x40 => 0x2245, 0x41 => 0x0391, 0x42 => 0x0392,
        0x43 => 0x03A7, 0x44 => 0x0394, 0x45 => 0x0395, 0x46 => 0x03A6, 0x47 => 0x0393, 0x48 => 0x0397, 0x49 => 0x0399, 0x4A => 0x03D1,
        0x4B => 0x039A, 0x4C => 0x039B, 0x4D => 0x039C, 0x4E => 0x039D, 0x4F => 0x039F, 0x50 => 0x03A0, 0x51 => 0x0398, 0x52 => 0x03A1,
        0x53 => 0x03A3, 0x54 => 0x03A4, 0x55 => 0x03A5, 0x56 => 0x03C2, 0x57 => 0x03A9, 0x58 => 0x039E, 0x59 => 0x03A8, 0x5A => 0x0396,
        0x5C => 0x2234, 0x5E => 0x22A5, 0x60 => 0x203E, 0x61 => 0x03B1, 0x62 => 0x03B2, 0x63 => 0x03C7, 0x64 => 0x03B4, 0x65 => 0x03B5,
        0x66 => 0x03C6, 0x67 => 0x03B3, 0x68 => 0x03B7, 0x69 => 0x03B9, 0x6A => 0x03D5, 0x6B => 0x03BA, 0x6C => 0x03BB, 0x6D => 0x03BC,
        0x6E => 0x03BD, 0x6F => 0x03BF, 0x70 => 0x03C0, 0x71 => 0x03B8, 0x72 => 0x03C1, 0x73 => 0x03C3, 0x74 => 0x03C4, 0x75 => 0x03C5,
        0x76 => 0x03D6, 0x77 => 0x03C9, 0x78 => 0x03BE, 0x79 => 0x03C8, 0x7A => 0x03B6, 0x7E => 0x223C, 0xA0 => 0x20AC, 0xA1 => 0x03D2,
        0xA2 => 0x2032, 0xA3 => 0x2264, 0xA4 => 0x2044, 0xA5 => 0x221E, 0xA6 => 0x0192, 0xA7 => 0x2663, 0xA8 => 0x2666, 0xA9 => 0x2665,
        0xAA => 0x2660, 0xAB => 0x2194, 0xAC => 0x2190, 0xAD => 0x2191, 0xAE => 0x2192, 0xAF => 0x2193, 0xB0 => 0x00B0, 0xB1 => 0x00B1,
        0xB2 => 0x2033, 0xB3 => 0x2265, 0xB4 => 0x00D7, 0xB5 => 0x221D, 0xB6 => 0x2202, 0xB7 => 0x2022, 0xB8 => 0x00F7, 0xB9 => 0x2260,
        0xBA => 0x2261, 0xBB => 0x2248, 0xBC => 0x2026, 0xBD => 0x23D0, 0xBE => 0x23AF, 0xBF => 0x21B5, 0xC0 => 0x2135, 0xC1 => 0x2111,
        0xC2 => 0x211C, 0xC3 => 0x2118, 0xC4 => 0x2297, 0xC5 => 0x2295, 0xC6 => 0x2205, 0xC7 => 0x2229, 0xC8 => 0x222A, 0xC9 => 0x2283,
        0xCA => 0x2287, 0xCB => 0x2284, 0xCC => 0x2282, 0xCD => 0x2286, 0xCE => 0x2208, 0xCF => 0x2209, 0xD0 => 0x2220, 0xD1 => 0x2207,
        0xD2 => 0x00AE, 0xD3 => 0x00A9, 0xD4 => 0x2122, 0xD5 => 0x220F, 0xD6 => 0x221A, 0xD7 => 0x22C5, 0xD8 => 0x00AC, 0xD9 => 0x2227,
        0xDA => 0x2228, 0xDB => 0x21D4, 0xDC => 0x21D0, 0xDD => 0x21D1, 0xDE => 0x21D2, 0xDF => 0x21D3, 0xE0 => 0x25CA, 0xE1 => 0x2329,
        0xE2 => 0x00AE, 0xE3 => 0x00A9, 0xE4 => 0x2122, 0xE5 => 0x2211, 0xE6 => 0x239B, 0xE7 => 0x239C, 0xE8 => 0x239D, 0xE9 => 0x23A1,
        0xEA => 0x23A2, 0xEB => 0x23A3, 0xEC => 0x23A7, 0xED => 0x23A8, 0xEE => 0x23A9, 0xEF => 0x23AA, 0xF1 => 0x232A, 0xF2 => 0x222B,
        0xF3 => 0x2320, 0xF4 => 0x23AE, 0xF5 => 0x2321, 0xF6 => 0x239E, 0xF7 => 0x239F, 0xF8 => 0x23A0, 0xF9 => 0x23A4, 0xFA => 0x23A5,
        0xFB => 0x23A6, 0xFC => 0x23AB, 0xFD => 0x23AC, 0xFE => 0x23AD,
    ];
    /**
     * Converts a text drawn with the Symbol font to Unicode (Greek letters, mathematical symbols...)
     *
     * The characters of the Symbol font are ASCII characters (0x20 to 0xFF) or private characters (U+F020 to U+F0FF)
     */
    public static function decodeSymbol(string $text): string
    {
        $result = '';
        foreach (preg_split('//u', $text, -1, PREG_SPLIT_NO_EMPTY) ?: [] as $character) {
            $codePoint = self::getCodePoint($character);
            if ($codePoint >= 0xF020 && $codePoint <= 0xF0FF) {
                $codePoint -= 0xF000;
            }
            $result .= isset(self::SYMBOL[$codePoint]) ? self::encodeUTF8(self::SYMBOL[$codePoint]) : ($codePoint < 0x100 ? self::encodeUTF8($codePoint) : $character);
        }
        return $result;
    }
    /**
     * Returns the code point of a UTF-8 character
     */
    protected static function getCodePoint(string $character): int
    {
        $bytes = array_values(unpack('C*', $character) ?: [0]);
        switch (count($bytes)) {
            case 1:
                return $bytes[0];
            case 2:
                return (($bytes[0] & 0x1F) << 6) | ($bytes[1] & 0x3F);
            case 3:
                return (($bytes[0] & 0x0F) << 12) | (($bytes[1] & 0x3F) << 6) | ($bytes[2] & 0x3F);
            default:
                return (($bytes[0] & 0x07) << 18) | (($bytes[1] & 0x3F) << 12) | (($bytes[2] & 0x3F) << 6) | ($bytes[3] & 0x3F);
        }
    }
    /**
     * Decodes a UTF-16LE string (stopping at the first NUL character) to UTF-8
     */
    public static function decodeUTF16(string $data): string
    {
        $data = (string) substr($data, 0, strlen($data) - strlen($data) % 2);
        if ($data === '') {
            return '';
        }
        $units = array_values(unpack('v*', $data));
        $count = count($units);
        $result = '';
        for ($i = 0; $i < $count; ++$i) {
            $codePoint = $units[$i];
            if ($codePoint == 0) {
                break;
            }
            if ($codePoint >= 0xD800 && $codePoint < 0xDC00 && $i + 1 < $count && $units[$i + 1] >= 0xDC00 && $units[$i + 1] < 0xE000) {
                $codePoint = 0x10000 + (($codePoint - 0xD800) << 10) + ($units[$i + 1] - 0xDC00);
                ++$i;
            }
            $result .= self::encodeUTF8($codePoint);
        }
        return $result;
    }
    /**
     * Decodes a Windows-1252 string (stopping at the first NUL character) to UTF-8
     */
    public static function decodeANSI(string $data): string
    {
        $result = '';
        $length = strlen($data);
        for ($i = 0; $i < $length; ++$i) {
            $byte = ord($data[$i]);
            if ($byte == 0) {
                break;
            }
            $result .= self::encodeUTF8($byte >= 0x80 && $byte < 0xA0 ? self::WINDOWS_1252[$byte - 0x80] : $byte);
        }
        return $result;
    }
    public static function encodeUTF8(int $codePoint): string
    {
        if ($codePoint >= 0xD800 && $codePoint < 0xE000) {
            // Lone surrogate
            $codePoint = 0xFFFD;
        }
        if ($codePoint < 0x80) {
            return chr($codePoint);
        }
        if ($codePoint < 0x800) {
            return chr(0xC0 | ($codePoint >> 6)) . chr(0x80 | ($codePoint & 0x3F));
        }
        if ($codePoint < 0x10000) {
            return chr(0xE0 | ($codePoint >> 12)) . chr(0x80 | (($codePoint >> 6) & 0x3F)) . chr(0x80 | ($codePoint & 0x3F));
        }
        return chr(0xF0 | ($codePoint >> 18)) . chr(0x80 | (($codePoint >> 12) & 0x3F)) . chr(0x80 | (($codePoint >> 6) & 0x3F)) . chr(0x80 | ($codePoint & 0x3F));
    }
}