Cmap.php 11.3 KB
Newer Older
Ketan's avatar
Ketan committed
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336
<?php
/**
 * Zend Framework
 *
 * LICENSE
 *
 * This source file is subject to the new BSD license that is bundled
 * with this package in the file LICENSE.txt.
 * It is also available through the world-wide-web at this URL:
 * http://framework.zend.com/license/new-bsd
 * If you did not receive a copy of the license and are unable to
 * obtain it through the world-wide-web, please send an email
 * to license@zend.com so we can send you a copy immediately.
 *
 * @category   Zend
 * @package    Zend_Pdf
 * @subpackage Fonts
 * @copyright  Copyright (c) 2005-2015 Zend Technologies USA Inc. (http://www.zend.com)
 * @license    http://framework.zend.com/license/new-bsd     New BSD License
 * @version    $Id$
 */


/**
 * Abstract helper class for {@link Zend_Pdf_Resource_Font} which manages font
 * character maps.
 *
 * Defines the public interface for concrete subclasses which are responsible
 * for mapping Unicode characters to the font's glyph numbers. Also provides
 * shared utility methods.
 *
 * Cmap objects should ordinarily be obtained through the factory method
 * {@link cmapWithTypeData()}.
 *
 * The supported character map types are those found in the OpenType spec. For
 * additional detail on the internal binary format of these tables, see:
 * <ul>
 *  <li>{@link http://developer.apple.com/textfonts/TTRefMan/RM06/Chap6cmap.html}
 *  <li>{@link http://www.microsoft.com/OpenType/OTSpec/cmap.htm}
 *  <li>{@link http://partners.adobe.com/public/developer/opentype/index_cmap.html}
 * </ul>
 *
 * @todo Write code for Zend_Pdf_FontCmap_HighByteMapping class.
 * @todo Write code for Zend_Pdf_FontCmap_MixedCoverage class.
 * @todo Write code for Zend_Pdf_FontCmap_TrimmedArray class.
 * @todo Write code for Zend_Pdf_FontCmap_SegmentedCoverage class.
 *
 * @package    Zend_Pdf
 * @subpackage Fonts
 * @copyright  Copyright (c) 2005-2015 Zend Technologies USA Inc. (http://www.zend.com)
 * @license    http://framework.zend.com/license/new-bsd     New BSD License
 */
abstract class Zend_Pdf_Cmap
{
  /**** Class Constants ****/


  /* Cmap Table Types */

    /**
     * Byte Encoding character map table type.
     */
    const TYPE_BYTE_ENCODING = 0x00;

    /**
     * High Byte Mapping character map table type.
     */
    const TYPE_HIGH_BYTE_MAPPING = 0x02;

    /**
     * Segment Value to Delta Mapping character map table type.
     */
    const TYPE_SEGMENT_TO_DELTA = 0x04;

    /**
     * Trimmed Table character map table type.
     */
    const TYPE_TRIMMED_TABLE = 0x06;

    /**
     * Mixed Coverage character map table type.
     */
    const TYPE_MIXED_COVERAGE = 0x08;

    /**
     * Trimmed Array character map table type.
     */
    const TYPE_TRIMMED_ARRAY = 0x0a;

    /**
     * Segmented Coverage character map table type.
     */
    const TYPE_SEGMENTED_COVERAGE = 0x0c;

    /**
     * Static Byte Encoding character map table type. Variant of
     * {@link TYPE_BYTEENCODING}.
     */
    const TYPE_BYTE_ENCODING_STATIC = 0xf1;

    /**
     * Unknown character map table type.
     */
    const TYPE_UNKNOWN = 0xff;


  /* Special Glyph Names */

    /**
     * Glyph representing missing characters.
     */
    const MISSING_CHARACTER_GLYPH = 0x00;



  /**** Public Interface ****/


  /* Factory Methods */

    /**
     * Instantiates the appropriate concrete subclass based on the type of cmap
     * table and returns the instance.
     *
     * The cmap type must be one of the following values:
     * <ul>
     *  <li>{@link Zend_Pdf_Cmap::TYPE_BYTE_ENCODING}
     *  <li>{@link Zend_Pdf_Cmap::TYPE_BYTE_ENCODING_STATIC}
     *  <li>{@link Zend_Pdf_Cmap::TYPE_HIGH_BYTE_MAPPING}
     *  <li>{@link Zend_Pdf_Cmap::TYPE_SEGMENT_TO_DELTA}
     *  <li>{@link Zend_Pdf_Cmap::TYPE_TRIMMED_TABLE}
     *  <li>{@link Zend_Pdf_Cmap::TYPE_MIXED_COVERAGE}
     *  <li>{@link Zend_Pdf_Cmap::TYPE_TRIMMED_ARRAY}
     *  <li>{@link Zend_Pdf_Cmap::TYPE_SEGMENTED_COVERAGE}
     * </ul>
     *
     * Throws an exception if the table type is invalid or the cmap table data
     * cannot be validated.
     *
     * @param integer $cmapType Type of cmap.
     * @param mixed $cmapData Cmap table data. Usually a string or array.
     * @return Zend_Pdf_Cmap
     * @throws Zend_Pdf_Exception
     */
    public static function cmapWithTypeData($cmapType, $cmapData)
    {
        switch ($cmapType) {
            case Zend_Pdf_Cmap::TYPE_BYTE_ENCODING:
                #require_once 'Zend/Pdf/Cmap/ByteEncoding.php';
                return new Zend_Pdf_Cmap_ByteEncoding($cmapData);

            case Zend_Pdf_Cmap::TYPE_BYTE_ENCODING_STATIC:
                #require_once 'Zend/Pdf/Cmap/ByteEncoding/Static.php';
                return new Zend_Pdf_Cmap_ByteEncoding_Static($cmapData);

            case Zend_Pdf_Cmap::TYPE_HIGH_BYTE_MAPPING:
                #require_once 'Zend/Pdf/Exception.php';
                throw new Zend_Pdf_Exception('High byte mapping cmap currently unsupported',
                                             Zend_Pdf_Exception::CMAP_TYPE_UNSUPPORTED);

            case Zend_Pdf_Cmap::TYPE_SEGMENT_TO_DELTA:
                #require_once 'Zend/Pdf/Cmap/SegmentToDelta.php';
                return new Zend_Pdf_Cmap_SegmentToDelta($cmapData);

            case Zend_Pdf_Cmap::TYPE_TRIMMED_TABLE:
                #require_once 'Zend/Pdf/Cmap/TrimmedTable.php';
                return new Zend_Pdf_Cmap_TrimmedTable($cmapData);

            case Zend_Pdf_Cmap::TYPE_MIXED_COVERAGE:
                #require_once 'Zend/Pdf/Exception.php';
                throw new Zend_Pdf_Exception('Mixed coverage cmap currently unsupported',
                                             Zend_Pdf_Exception::CMAP_TYPE_UNSUPPORTED);

            case Zend_Pdf_Cmap::TYPE_TRIMMED_ARRAY:
                #require_once 'Zend/Pdf/Exception.php';
                throw new Zend_Pdf_Exception('Trimmed array cmap currently unsupported',
                                             Zend_Pdf_Exception::CMAP_TYPE_UNSUPPORTED);

            case Zend_Pdf_Cmap::TYPE_SEGMENTED_COVERAGE:
                #require_once 'Zend/Pdf/Exception.php';
                throw new Zend_Pdf_Exception('Segmented coverage cmap currently unsupported',
                                             Zend_Pdf_Exception::CMAP_TYPE_UNSUPPORTED);

            default:
                #require_once 'Zend/Pdf/Exception.php';
                throw new Zend_Pdf_Exception("Unknown cmap type: $cmapType",
                                             Zend_Pdf_Exception::CMAP_UNKNOWN_TYPE);
        }
    }


  /* Abstract Methods */

    /**
     * Object constructor
     *
     * Parses the raw binary table data. Throws an exception if the table is
     * malformed.
     *
     * @param string $cmapData Raw binary cmap table data.
     * @throws Zend_Pdf_Exception
     */
    abstract public function __construct($cmapData);

    /**
     * Returns an array of glyph numbers corresponding to the Unicode characters.
     *
     * If a particular character doesn't exist in this font, the special 'missing
     * character glyph' will be substituted.
     *
     * See also {@link glyphNumberForCharacter()}.
     *
     * @param array $characterCodes Array of Unicode character codes (code points).
     * @return array Array of glyph numbers.
     */
    abstract public function glyphNumbersForCharacters($characterCodes);

    /**
     * Returns the glyph number corresponding to the Unicode character.
     *
     * If a particular character doesn't exist in this font, the special 'missing
     * character glyph' will be substituted.
     *
     * See also {@link glyphNumbersForCharacters()} which is optimized for bulk
     * operations.
     *
     * @param integer $characterCode Unicode character code (code point).
     * @return integer Glyph number.
     */
    abstract public function glyphNumberForCharacter($characterCode);

    /**
     * Returns an array containing the Unicode characters that have entries in
     * this character map.
     *
     * @return array Unicode character codes.
     */
    abstract public function getCoveredCharacters();

    /**
     * Returns an array containing the glyphs numbers that have entries in this character map.
     * Keys are Unicode character codes (integers)
     *
     * This functionality is partially covered by glyphNumbersForCharacters(getCoveredCharacters())
     * call, but this method do it in more effective way (prepare complete list instead of searching
     * glyph for each character code).
     *
     * @internal
     * @return array Array representing <Unicode character code> => <glyph number> pairs.
     */
    abstract public function getCoveredCharactersGlyphs();


  /**** Internal Methods ****/


  /* Internal Utility Methods */

    /**
     * Extracts a signed 2-byte integer from a string.
     *
     * Integers are always big-endian. Throws an exception if the index is out
     * of range.
     *
     * @param string &$data
     * @param integer $index Position in string of integer.
     * @return integer
     * @throws Zend_Pdf_Exception
     */
    protected function _extractInt2(&$data, $index)
    {
        if (($index < 0) | (($index + 1) > strlen($data))) {
            #require_once 'Zend/Pdf/Exception.php';
            throw new Zend_Pdf_Exception("Index out of range: $index",
                                         Zend_Pdf_Exception::INDEX_OUT_OF_RANGE);
        }
        $number = ord($data[$index]);
        if (($number & 0x80) == 0x80) {    // negative
            $number = ~((((~ $number) & 0xff) << 8) | ((~ ord($data[++$index])) & 0xff));
        } else {
            $number = ($number << 8) | ord($data[++$index]);
        }
        return $number;
    }

    /**
     * Extracts an unsigned 2-byte integer from a string.
     *
     * Integers are always big-endian. Throws an exception if the index is out
     * of range.
     *
     * @param string &$data
     * @param integer $index Position in string of integer.
     * @return integer
     * @throws Zend_Pdf_Exception
     */
    protected function _extractUInt2(&$data, $index)
    {
        if (($index < 0) | (($index + 1) > strlen($data))) {
            #require_once 'Zend/Pdf/Exception.php';
            throw new Zend_Pdf_Exception("Index out of range: $index",
                                         Zend_Pdf_Exception::INDEX_OUT_OF_RANGE);
        }
        $number = (ord($data[$index]) << 8) | ord($data[++$index]);
        return $number;
    }

    /**
     * Extracts an unsigned 4-byte integer from a string.
     *
     * Integers are always big-endian. Throws an exception if the index is out
     * of range.
     *
     * NOTE: If you ask for a 4-byte unsigned integer on a 32-bit machine, the
     * resulting value WILL BE SIGNED because PHP uses signed integers internally
     * for everything. To guarantee portability, be sure to use bitwise or
     * similar operators on large integers!
     *
     * @param string &$data
     * @param integer $index Position in string of integer.
     * @return integer
     * @throws Zend_Pdf_Exception
     */
    protected function _extractUInt4(&$data, $index)
    {
        if (($index < 0) | (($index + 3) > strlen($data))) {
            #require_once 'Zend/Pdf/Exception.php';
            throw new Zend_Pdf_Exception("Index out of range: $index",
                                         Zend_Pdf_Exception::INDEX_OUT_OF_RANGE);
        }
        $number = (ord($data[$index]) << 24) | (ord($data[++$index]) << 16) |
                  (ord($data[++$index]) << 8) | ord($data[++$index]);
        return $number;
    }

}