Coverage Report

Created: 2026-07-30 07:17

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/poppler/splash/SplashFont.cc
Line
Count
Source
1
//========================================================================
2
//
3
// SplashFont.cc
4
//
5
//========================================================================
6
7
//========================================================================
8
//
9
// Modified under the Poppler project - http://poppler.freedesktop.org
10
//
11
// All changes made under the Poppler project to this file are licensed
12
// under GPL version 2 or later
13
//
14
// Copyright (C) 2007-2008, 2010, 2014, 2019, 2025, 2026 Albert Astals Cid <aacid@kde.org>
15
// Copyright (C) 2018 Oliver Sander <oliver.sander@tu-dresden.de>
16
// Copyright (C) 2026 g10 Code GmbH, Author: Sune Stolborg Vuorela <sune@vuorela.dk>
17
//
18
// To see a description of the changes please see the Changelog file that
19
// came with your tarball or type make ChangeLog if you are building from git
20
//
21
//========================================================================
22
23
#include <config.h>
24
25
#include <climits>
26
#include <cstring>
27
#include "goo/gmem.h"
28
#include "SplashGlyphBitmap.h"
29
#include "SplashFontFile.h"
30
#include "SplashFont.h"
31
32
//------------------------------------------------------------------------
33
34
struct SplashFontCacheTag
35
{
36
    int c;
37
    short xFrac, yFrac; // x and y fractions
38
    int mru; // valid bit (0x80000000) and MRU index
39
    int x, y, w, h; // offset and size of glyph
40
};
41
42
//------------------------------------------------------------------------
43
// SplashFont
44
//------------------------------------------------------------------------
45
46
0
SplashFont::SplashFont(const std::shared_ptr<SplashFontFile> &fontFileA, const std::array<double, 4> &matA, const std::array<double, 4> &textMatA, bool aaA) : mat(matA), textMat(textMatA)
47
0
{
48
0
    fontFile = fontFileA;
49
0
    aa = aaA;
50
51
0
    cache = nullptr;
52
0
    cacheTags = nullptr;
53
54
0
    xMin = yMin = xMax = yMax = 0;
55
0
}
56
57
void SplashFont::initCache()
58
0
{
59
0
    int i;
60
61
    // this should be (max - min + 1), but we add some padding to
62
    // deal with rounding errors
63
0
    glyphW = xMax - xMin + 3;
64
0
    glyphH = yMax - yMin + 3;
65
0
    if (glyphW > INT_MAX / glyphH) {
66
0
        glyphSize = -1;
67
0
    } else {
68
0
        if (aa) {
69
0
            glyphSize = glyphW * glyphH;
70
0
        } else {
71
0
            glyphSize = ((glyphW + 7) >> 3) * glyphH;
72
0
        }
73
0
    }
74
75
    // set up the glyph pixmap cache
76
0
    cacheAssoc = 8;
77
0
    if (glyphSize <= 64) {
78
0
        cacheSets = 32;
79
0
    } else if (glyphSize <= 128) {
80
0
        cacheSets = 16;
81
0
    } else if (glyphSize <= 256) {
82
0
        cacheSets = 8;
83
0
    } else if (glyphSize <= 512) {
84
0
        cacheSets = 4;
85
0
    } else if (glyphSize <= 1024) {
86
0
        cacheSets = 2;
87
0
    } else {
88
0
        cacheSets = 1;
89
0
    }
90
0
    cache = static_cast<unsigned char *>(gmallocn_checkoverflow(cacheSets * cacheAssoc, glyphSize));
91
0
    if (cache != nullptr) {
92
0
        cacheTags = static_cast<SplashFontCacheTag *>(gmallocn(cacheSets * cacheAssoc, sizeof(SplashFontCacheTag)));
93
0
        for (i = 0; i < cacheSets * cacheAssoc; ++i) {
94
0
            cacheTags[i].mru = i & (cacheAssoc - 1);
95
0
        }
96
0
    } else {
97
0
        cacheAssoc = 0;
98
0
    }
99
0
}
100
101
SplashFont::~SplashFont()
102
0
{
103
0
    if (cache) {
104
0
        gfree(cache);
105
0
    }
106
0
    if (cacheTags) {
107
0
        gfree(cacheTags);
108
0
    }
109
0
}
110
111
bool SplashFont::getGlyph(int c, int xFrac, int yFrac, SplashGlyphBitmap *bitmap, int x0, int y0, const SplashClip &clip, SplashClipResult *clipRes)
112
0
{
113
0
    SplashGlyphBitmap bitmap2;
114
0
    int size;
115
0
    unsigned char *p;
116
0
    int i, j, k;
117
118
    // no fractional coordinates for large glyphs or non-anti-aliased
119
    // glyphs
120
0
    if (!aa || glyphH > 50) {
121
0
        xFrac = yFrac = 0;
122
0
    }
123
124
    // check the cache
125
0
    i = (c & (cacheSets - 1)) * cacheAssoc;
126
0
    for (j = 0; j < cacheAssoc; ++j) {
127
0
        if ((cacheTags[i + j].mru & 0x80000000) && cacheTags[i + j].c == c && static_cast<int>(cacheTags[i + j].xFrac) == xFrac && static_cast<int>(cacheTags[i + j].yFrac) == yFrac) {
128
0
            bitmap->x = cacheTags[i + j].x;
129
0
            bitmap->y = cacheTags[i + j].y;
130
0
            bitmap->w = cacheTags[i + j].w;
131
0
            bitmap->h = cacheTags[i + j].h;
132
0
            for (k = 0; k < cacheAssoc; ++k) {
133
0
                if (k != j && (cacheTags[i + k].mru & 0x7fffffff) < (cacheTags[i + j].mru & 0x7fffffff)) {
134
0
                    ++cacheTags[i + k].mru;
135
0
                }
136
0
            }
137
0
            cacheTags[i + j].mru = 0x80000000;
138
0
            bitmap->aa = aa;
139
0
            bitmap->data = cache + (i + j) * glyphSize;
140
0
            bitmap->freeData = false;
141
142
0
            int rectXMin, rectYMin;
143
0
            if (checkedSubtraction(x0, bitmap->x, &rectXMin)) {
144
0
                return false;
145
0
            }
146
0
            if (checkedSubtraction(y0, bitmap->y, &rectYMin)) {
147
0
                return false;
148
0
            }
149
0
            *clipRes = clip.testRect(rectXMin, rectYMin, rectXMin + bitmap->w - 1, rectYMin + bitmap->h - 1);
150
151
0
            return true;
152
0
        }
153
0
    }
154
155
    // generate the glyph bitmap
156
0
    if (!makeGlyph(c, xFrac, yFrac, &bitmap2, x0, y0, clip, clipRes)) {
157
0
        return false;
158
0
    }
159
160
0
    if (*clipRes == splashClipAllOutside) {
161
0
        bitmap->freeData = false;
162
0
        if (bitmap2.freeData) {
163
0
            gfree(bitmap2.data);
164
0
        }
165
0
        return true;
166
0
    }
167
168
    // if the glyph doesn't fit in the bounding box, return a temporary
169
    // uncached bitmap
170
0
    if (bitmap2.w > glyphW || bitmap2.h > glyphH) {
171
0
        *bitmap = bitmap2;
172
0
        return true;
173
0
    }
174
175
    // insert glyph pixmap in cache
176
0
    if (aa) {
177
0
        size = bitmap2.w * bitmap2.h;
178
0
    } else {
179
0
        size = ((bitmap2.w + 7) >> 3) * bitmap2.h;
180
0
    }
181
0
    p = nullptr; // make gcc happy
182
0
    if (cacheAssoc == 0) {
183
        // we had problems on the malloc of the cache, so ignore it
184
0
        *bitmap = bitmap2;
185
0
    } else {
186
0
        for (j = 0; j < cacheAssoc; ++j) {
187
0
            if ((cacheTags[i + j].mru & 0x7fffffff) == cacheAssoc - 1) {
188
0
                cacheTags[i + j].mru = 0x80000000;
189
0
                cacheTags[i + j].c = c;
190
0
                cacheTags[i + j].xFrac = static_cast<short>(xFrac);
191
0
                cacheTags[i + j].yFrac = static_cast<short>(yFrac);
192
0
                cacheTags[i + j].x = bitmap2.x;
193
0
                cacheTags[i + j].y = bitmap2.y;
194
0
                cacheTags[i + j].w = bitmap2.w;
195
0
                cacheTags[i + j].h = bitmap2.h;
196
0
                p = cache + (i + j) * glyphSize;
197
0
                memcpy(p, bitmap2.data, size);
198
0
            } else {
199
0
                ++cacheTags[i + j].mru;
200
0
            }
201
0
        }
202
0
        *bitmap = bitmap2;
203
0
        bitmap->data = p;
204
0
        bitmap->freeData = false;
205
0
        if (bitmap2.freeData) {
206
0
            gfree(bitmap2.data);
207
0
        }
208
0
    }
209
0
    return true;
210
0
}