Code Coverage
 
Lines
Functions and Methods
Classes and Traits
Total
100.00% covered (success)
100.00%
15 / 15
100.00% covered (success)
100.00%
1 / 1
CRAP
100.00% covered (success)
100.00%
1 / 1
Resolver
100.00% covered (success)
100.00%
15 / 15
100.00% covered (success)
100.00%
1 / 1
7
100.00% covered (success)
100.00%
1 / 1
 decodeRun
100.00% covered (success)
100.00%
15 / 15
100.00% covered (success)
100.00%
1 / 1
7
1<?php
2declare(strict_types=1);
3/**
4 * Pop PHP Framework (https://www.popphp.org/)
5 *
6 * @link       https://github.com/popphp/popphp-framework
7 * @author     Nick Sagona, III <nick@popphp.org>
8 * @copyright  Copyright (c) 2009-2026 Nick Sagona, III
9 * @license    https://www.popphp.org/license     New BSD License
10 */
11
12/**
13 * @namespace
14 */
15namespace Pop\Pdf\Extract\Font;
16
17use Pop\Pdf\Extract\Content\TextRun;
18use Pop\Pdf\Extract\Document;
19
20/**
21 * Pdf extract font resolver class
22 *
23 * @category   Pop
24 * @package    Pop\Pdf
25 * @author     Nick Sagona, III <nick@popphp.org>
26 * @copyright  Copyright (c) 2009-2026 Nick Sagona, III
27 * @license    https://www.popphp.org/license     New BSD License
28 * @version    6.0.0
29 */
30class Resolver
31{
32
33    /**
34     * Decode a text run's raw bytes into Unicode text, never throwing
35     *
36     * @param  TextRun  $run
37     * @param  Document $doc
38     * @return string
39     */
40    public static function decodeRun(TextRun $run, Document $doc): string
41    {
42        if ($run->decodedText !== null) {
43            return $run->decodedText;
44        }
45
46        if ($run->rawBytes === null) {
47            return '';
48        }
49
50        if ($run->font === null) {
51            return $run->rawBytes;
52        }
53
54        try {
55            // Interpreter computes this once per Tf (font activation) and
56            // carries it on the run - falling back to computing it here
57            // only protects callers that construct a TextRun directly
58            // without going through Interpreter (e.g. tests), since hashing
59            // the full resolved font dict per RUN rather than per Tf can be
60            // an O(runs x dict-size) cost on a page with many runs sharing
61            // one font.
62            $key  = $run->fontCacheKey ?? md5(serialize($run->font));
63            $info = $doc->getOrResolveFontInfo($key, fn() => FontInfo::resolve($doc, $run->font));
64
65            if ($info === null) {
66                return $run->rawBytes;
67            }
68
69            return $info->isType0
70                ? CidDecoder::decode($run->rawBytes, $info)
71                : SimpleDecoder::decode($run->rawBytes, $info);
72        } catch (\Throwable $e) {
73            return $run->rawBytes;
74        }
75    }
76
77}