Coverage Report

Created: 2025-07-07 10:01

/work/workdir/UnpackedTarball/harfbuzz/src/hb-ot-os2-table.hh
Line
Count
Source (jump to first uncovered line)
1
/*
2
 * Copyright © 2011,2012  Google, Inc.
3
 * Copyright © 2018  Ebrahim Byagowi
4
 *
5
 *  This is part of HarfBuzz, a text shaping library.
6
 *
7
 * Permission is hereby granted, without written agreement and without
8
 * license or royalty fees, to use, copy, modify, and distribute this
9
 * software and its documentation for any purpose, provided that the
10
 * above copyright notice and the following two paragraphs appear in
11
 * all copies of this software.
12
 *
13
 * IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE TO ANY PARTY FOR
14
 * DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES
15
 * ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN
16
 * IF THE COPYRIGHT HOLDER HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH
17
 * DAMAGE.
18
 *
19
 * THE COPYRIGHT HOLDER SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING,
20
 * BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
21
 * FITNESS FOR A PARTICULAR PURPOSE.  THE SOFTWARE PROVIDED HEREUNDER IS
22
 * ON AN "AS IS" BASIS, AND THE COPYRIGHT HOLDER HAS NO OBLIGATION TO
23
 * PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS.
24
 *
25
 * Google Author(s): Behdad Esfahbod
26
 */
27
28
#ifndef HB_OT_OS2_TABLE_HH
29
#define HB_OT_OS2_TABLE_HH
30
31
#include "hb-open-type.hh"
32
#include "hb-ot-os2-unicode-ranges.hh"
33
#include "hb-ot-var-mvar-table.hh"
34
35
#include "hb-set.hh"
36
37
/*
38
 * OS/2 and Windows Metrics
39
 * https://docs.microsoft.com/en-us/typography/opentype/spec/os2
40
 */
41
#define HB_OT_TAG_OS2 HB_TAG('O','S','/','2')
42
43
44
namespace OT {
45
46
struct OS2V1Tail
47
{
48
  bool sanitize (hb_sanitize_context_t *c) const
49
60
  {
50
60
    TRACE_SANITIZE (this);
51
60
    return_trace (c->check_struct (this));
52
60
  }
53
54
  public:
55
  HBUINT32  ulCodePageRange1;
56
  HBUINT32  ulCodePageRange2;
57
  public:
58
  DEFINE_SIZE_STATIC (8);
59
};
60
61
struct OS2V2Tail
62
{
63
0
  bool has_data () const { return sxHeight || sCapHeight; }
64
65
0
  const OS2V2Tail * operator -> () const { return this; }
66
0
  OS2V2Tail * operator -> () { return this; }
67
68
  bool sanitize (hb_sanitize_context_t *c) const
69
60
  {
70
60
    TRACE_SANITIZE (this);
71
60
    return_trace (c->check_struct (this));
72
60
  }
73
74
  public:
75
  HBINT16 sxHeight;
76
  HBINT16 sCapHeight;
77
  HBUINT16  usDefaultChar;
78
  HBUINT16  usBreakChar;
79
  HBUINT16  usMaxContext;
80
  public:
81
  DEFINE_SIZE_STATIC (10);
82
};
83
84
struct OS2V5Tail
85
{
86
  inline bool get_optical_size (unsigned int *lower, unsigned int *upper) const
87
0
  {
88
0
    unsigned int lower_optical_size = usLowerOpticalPointSize;
89
0
    unsigned int upper_optical_size = usUpperOpticalPointSize;
90
0
91
0
    /* Per https://docs.microsoft.com/en-us/typography/opentype/spec/os2#lps */
92
0
    if (lower_optical_size < upper_optical_size &&
93
0
  lower_optical_size >= 1 && lower_optical_size <= 0xFFFE &&
94
0
  upper_optical_size >= 2 && upper_optical_size <= 0xFFFF)
95
0
    {
96
0
      *lower = lower_optical_size;
97
0
      *upper = upper_optical_size;
98
0
      return true;
99
0
    }
100
0
    return false;
101
0
  }
102
103
  bool sanitize (hb_sanitize_context_t *c) const
104
0
  {
105
0
    TRACE_SANITIZE (this);
106
0
    return_trace (c->check_struct (this));
107
0
  }
108
109
  public:
110
  HBUINT16  usLowerOpticalPointSize;
111
  HBUINT16  usUpperOpticalPointSize;
112
  public:
113
  DEFINE_SIZE_STATIC (4);
114
};
115
116
struct OS2
117
{
118
  static constexpr hb_tag_t tableTag = HB_OT_TAG_OS2;
119
120
2.11M
  bool has_data () const { return usWeightClass || usWidthClass || usFirstCharIndex || usLastCharIndex; }
121
122
0
  const OS2V1Tail &v1 () const { return version >= 1 ? v1X : Null (OS2V1Tail); }
123
0
  const OS2V2Tail &v2 () const { return version >= 2 ? v2X : Null (OS2V2Tail); }
124
0
  const OS2V5Tail &v5 () const { return version >= 5 ? v5X : Null (OS2V5Tail); }
125
126
  enum selection_flag_t {
127
    ITALIC    = 1u<<0,
128
    UNDERSCORE    = 1u<<1,
129
    NEGATIVE    = 1u<<2,
130
    OUTLINED    = 1u<<3,
131
    STRIKEOUT   = 1u<<4,
132
    BOLD    = 1u<<5,
133
    REGULAR   = 1u<<6,
134
    USE_TYPO_METRICS  = 1u<<7,
135
    WWS     = 1u<<8,
136
    OBLIQUE   = 1u<<9
137
  };
138
139
0
  bool        is_italic () const { return fsSelection & ITALIC; }
140
0
  bool       is_oblique () const { return fsSelection & OBLIQUE; }
141
13.7M
  bool use_typo_metrics () const { return fsSelection & USE_TYPO_METRICS; }
142
143
  enum width_class_t {
144
    FWIDTH_ULTRA_CONDENSED  = 1, /* 50% */
145
    FWIDTH_EXTRA_CONDENSED  = 2, /* 62.5% */
146
    FWIDTH_CONDENSED    = 3, /* 75% */
147
    FWIDTH_SEMI_CONDENSED = 4, /* 87.5% */
148
    FWIDTH_NORMAL   = 5, /* 100% */
149
    FWIDTH_SEMI_EXPANDED  = 6, /* 112.5% */
150
    FWIDTH_EXPANDED   = 7, /* 125% */
151
    FWIDTH_EXTRA_EXPANDED = 8, /* 150% */
152
    FWIDTH_ULTRA_EXPANDED = 9  /* 200% */
153
  };
154
155
  float get_width () const
156
0
  {
157
0
    switch (usWidthClass) {
158
0
    case FWIDTH_ULTRA_CONDENSED:return 50.f;
159
0
    case FWIDTH_EXTRA_CONDENSED:return 62.5f;
160
0
    case FWIDTH_CONDENSED:  return 75.f;
161
0
    case FWIDTH_SEMI_CONDENSED: return 87.5f;
162
0
    default:
163
0
    case FWIDTH_NORMAL:   return 100.f;
164
0
    case FWIDTH_SEMI_EXPANDED:  return 112.5f;
165
0
    case FWIDTH_EXPANDED: return 125.f;
166
0
    case FWIDTH_EXTRA_EXPANDED: return 150.f;
167
0
    case FWIDTH_ULTRA_EXPANDED: return 200.f;
168
0
    }
169
0
  }
170
171
  float map_wdth_to_widthclass(float width) const
172
0
  {
173
0
    if (width < 50) return 1.0f;
174
0
    if (width > 200) return 9.0f;
175
0
176
0
    float ratio = (width - 50) / 12.5f;
177
0
    int a = (int) floorf (ratio);
178
0
    int b = (int) ceilf (ratio);
179
0
180
0
    /* follow this maping:
181
0
     * https://docs.microsoft.com/en-us/typography/opentype/spec/os2#uswidthclass
182
0
     */
183
0
    if (b <= 6) // 50-125
184
0
    {
185
0
      if (a == b) return a + 1.0f;
186
0
    }
187
0
    else if (b == 7) // no mapping for 137.5
188
0
    {
189
0
      a = 6;
190
0
      b = 8;
191
0
    }
192
0
    else if (b == 8)
193
0
    {
194
0
      if (a == b) return 8.0f; // 150
195
0
      a = 6;
196
0
    }
197
0
    else
198
0
    {
199
0
      if (a == b && a == 12) return 9.0f; //200
200
0
      b = 12;
201
0
      a = 8;
202
0
    }
203
0
204
0
    float va = 50 + a * 12.5f;
205
0
    float vb = 50 + b * 12.5f;
206
0
207
0
    float ret =  a + (width - va) / (vb - va);
208
0
    if (a <= 6) ret += 1.0f;
209
0
    return ret;
210
0
  }
211
212
  static unsigned calc_avg_char_width (const hb_hashmap_t<hb_codepoint_t, hb_pair_t<unsigned, int>>& hmtx_map)
213
0
  {
214
0
    unsigned num = 0;
215
0
    unsigned total_width = 0;
216
0
    for (const auto& _ : hmtx_map.values_ref ())
217
0
    {
218
0
      unsigned width = _.first;
219
0
      if (width)
220
0
      {
221
0
        total_width += width;
222
0
        num++;
223
0
      }
224
0
    }
225
0
226
0
    return num ? (unsigned) roundf ((double) total_width / (double) num) : 0;
227
0
  }
228
229
  bool subset (hb_subset_context_t *c) const
230
0
  {
231
0
    TRACE_SUBSET (this);
232
0
    OS2 *os2_prime = c->serializer->embed (this);
233
0
    if (unlikely (!os2_prime)) return_trace (false);
234
0
235
0
#ifndef HB_NO_VAR
236
0
    if (c->plan->normalized_coords)
237
0
    {
238
0
      auto &MVAR = *c->plan->source->table.MVAR;
239
0
      auto *table = os2_prime;
240
0
241
0
      HB_ADD_MVAR_VAR (HB_OT_METRICS_TAG_HORIZONTAL_ASCENDER,         sTypoAscender);
242
0
      HB_ADD_MVAR_VAR (HB_OT_METRICS_TAG_HORIZONTAL_DESCENDER,        sTypoDescender);
243
0
      HB_ADD_MVAR_VAR (HB_OT_METRICS_TAG_HORIZONTAL_LINE_GAP,         sTypoLineGap);
244
0
      HB_ADD_MVAR_VAR (HB_OT_METRICS_TAG_HORIZONTAL_CLIPPING_ASCENT,  usWinAscent);
245
0
      HB_ADD_MVAR_VAR (HB_OT_METRICS_TAG_HORIZONTAL_CLIPPING_DESCENT, usWinDescent);
246
0
      HB_ADD_MVAR_VAR (HB_OT_METRICS_TAG_SUBSCRIPT_EM_X_SIZE,         ySubscriptXSize);
247
0
      HB_ADD_MVAR_VAR (HB_OT_METRICS_TAG_SUBSCRIPT_EM_Y_SIZE,         ySubscriptYSize);
248
0
      HB_ADD_MVAR_VAR (HB_OT_METRICS_TAG_SUBSCRIPT_EM_X_OFFSET,       ySubscriptXOffset);
249
0
      HB_ADD_MVAR_VAR (HB_OT_METRICS_TAG_SUBSCRIPT_EM_Y_OFFSET,       ySubscriptYOffset);
250
0
      HB_ADD_MVAR_VAR (HB_OT_METRICS_TAG_SUPERSCRIPT_EM_X_SIZE,       ySuperscriptXSize);
251
0
      HB_ADD_MVAR_VAR (HB_OT_METRICS_TAG_SUPERSCRIPT_EM_Y_SIZE,       ySuperscriptYSize);
252
0
      HB_ADD_MVAR_VAR (HB_OT_METRICS_TAG_SUPERSCRIPT_EM_X_OFFSET,     ySuperscriptXOffset);
253
0
      HB_ADD_MVAR_VAR (HB_OT_METRICS_TAG_SUPERSCRIPT_EM_Y_OFFSET,     ySuperscriptYOffset);
254
0
      HB_ADD_MVAR_VAR (HB_OT_METRICS_TAG_STRIKEOUT_SIZE,              yStrikeoutSize);
255
0
      HB_ADD_MVAR_VAR (HB_OT_METRICS_TAG_STRIKEOUT_OFFSET,            yStrikeoutPosition);
256
0
257
0
      if (os2_prime->version >= 2)
258
0
      {
259
0
        hb_barrier ();
260
0
        auto *table = & const_cast<OS2V2Tail &> (os2_prime->v2 ());
261
0
        HB_ADD_MVAR_VAR (HB_OT_METRICS_TAG_X_HEIGHT,                   sxHeight);
262
0
        HB_ADD_MVAR_VAR (HB_OT_METRICS_TAG_CAP_HEIGHT,                 sCapHeight);
263
0
      }
264
0
265
0
      unsigned avg_char_width = calc_avg_char_width (c->plan->hmtx_map);
266
0
      if (!c->serializer->check_assign (os2_prime->xAvgCharWidth, avg_char_width,
267
0
                                        HB_SERIALIZE_ERROR_INT_OVERFLOW))
268
0
        return_trace (false);
269
0
    }
270
0
#endif
271
0
272
0
    Triple *axis_range;
273
0
    if (c->plan->user_axes_location.has (HB_TAG ('w','g','h','t'), &axis_range))
274
0
    {
275
0
      unsigned weight_class = static_cast<unsigned> (roundf (hb_clamp (axis_range->middle, 1.0, 1000.0)));
276
0
      if (os2_prime->usWeightClass != weight_class)
277
0
        os2_prime->usWeightClass = weight_class;
278
0
    }
279
0
280
0
    if (c->plan->user_axes_location.has (HB_TAG ('w','d','t','h'), &axis_range))
281
0
    {
282
0
      unsigned width_class = static_cast<unsigned> (roundf (map_wdth_to_widthclass (axis_range->middle)));
283
0
      if (os2_prime->usWidthClass != width_class)
284
0
        os2_prime->usWidthClass = width_class;
285
0
    }
286
0
287
0
    os2_prime->usFirstCharIndex = hb_min (0xFFFFu, c->plan->os2_info.min_cmap_codepoint);
288
0
    os2_prime->usLastCharIndex  = hb_min (0xFFFFu, c->plan->os2_info.max_cmap_codepoint);
289
0
290
0
    if (c->plan->flags & HB_SUBSET_FLAGS_NO_PRUNE_UNICODE_RANGES)
291
0
      return_trace (true);
292
0
293
0
    _update_unicode_ranges (&c->plan->unicodes, os2_prime->ulUnicodeRange);
294
0
295
0
    return_trace (true);
296
0
  }
297
298
  void _update_unicode_ranges (const hb_set_t *codepoints,
299
             HBUINT32 ulUnicodeRange[4]) const
300
0
  {
301
0
    HBUINT32 newBits[4];
302
0
    for (unsigned int i = 0; i < 4; i++)
303
0
      newBits[i] = 0;
304
0
305
0
    /* This block doesn't show up in profiles. If it ever did,
306
0
     * we can rewrite it to iterate over OS/2 ranges and use
307
0
     * set iteration to check if the range matches. */
308
0
    for (auto cp : *codepoints)
309
0
    {
310
0
      unsigned int bit = _hb_ot_os2_get_unicode_range_bit (cp);
311
0
      if (bit < 128)
312
0
      {
313
0
  unsigned int block = bit / 32;
314
0
  unsigned int bit_in_block = bit % 32;
315
0
  unsigned int mask = 1 << bit_in_block;
316
0
  newBits[block] = newBits[block] | mask;
317
0
      }
318
0
      if (cp >= 0x10000 && cp <= 0x110000)
319
0
      {
320
0
  /* the spec says that bit 57 ("Non Plane 0") implies that there's
321
0
     at least one codepoint beyond the BMP; so I also include all
322
0
     the non-BMP codepoints here */
323
0
  newBits[1] = newBits[1] | (1 << 25);
324
0
      }
325
0
    }
326
0
327
0
    for (unsigned int i = 0; i < 4; i++)
328
0
      ulUnicodeRange[i] = ulUnicodeRange[i] & newBits[i]; // set bits only if set in the original
329
0
  }
330
331
  /* https://github.com/Microsoft/Font-Validator/blob/520aaae/OTFontFileVal/val_OS2.cs#L644-L681
332
   * https://docs.microsoft.com/en-us/typography/legacy/legacy_arabic_fonts */
333
  enum font_page_t
334
  {
335
    FONT_PAGE_NONE    = 0,
336
    FONT_PAGE_HEBREW    = 0xB100, /* Hebrew Windows 3.1 font page */
337
    FONT_PAGE_SIMP_ARABIC = 0xB200, /* Simplified Arabic Windows 3.1 font page */
338
    FONT_PAGE_TRAD_ARABIC = 0xB300, /* Traditional Arabic Windows 3.1 font page */
339
    FONT_PAGE_OEM_ARABIC  = 0xB400, /* OEM Arabic Windows 3.1 font page */
340
    FONT_PAGE_SIMP_FARSI  = 0xBA00, /* Simplified Farsi Windows 3.1 font page */
341
    FONT_PAGE_TRAD_FARSI  = 0xBB00, /* Traditional Farsi Windows 3.1 font page */
342
    FONT_PAGE_THAI    = 0xDE00  /* Thai Windows 3.1 font page */
343
  };
344
  font_page_t get_font_page () const
345
0
  { return (font_page_t) (version == 0 ? fsSelection & 0xFF00 : 0); }
346
347
  unsigned get_size () const
348
0
  {
349
0
    unsigned result = min_size;
350
0
    if (version >= 1) result += v1X.get_size ();
351
0
    if (version >= 2) result += v2X.get_size ();
352
0
    if (version >= 5) result += v5X.get_size ();
353
0
    return result;
354
0
  }
355
356
  bool sanitize (hb_sanitize_context_t *c) const
357
60
  {
358
60
    TRACE_SANITIZE (this);
359
60
    if (unlikely (!c->check_struct (this))) return_trace (false);
360
60
    hb_barrier ();
361
60
    if (unlikely (version >= 1 && !v1X.sanitize (c))) return_trace (false);
362
60
    if (unlikely (version >= 2 && !v2X.sanitize (c))) return_trace (false);
363
60
    if (unlikely (version >= 5 && !v5X.sanitize (c))) return_trace (false);
364
60
    return_trace (true);
365
60
  }
366
367
  public:
368
  HBUINT16  version;
369
  HBINT16 xAvgCharWidth;
370
  HBUINT16  usWeightClass;
371
  HBUINT16  usWidthClass;
372
  HBUINT16  fsType;
373
  HBINT16 ySubscriptXSize;
374
  HBINT16 ySubscriptYSize;
375
  HBINT16 ySubscriptXOffset;
376
  HBINT16 ySubscriptYOffset;
377
  HBINT16 ySuperscriptXSize;
378
  HBINT16 ySuperscriptYSize;
379
  HBINT16 ySuperscriptXOffset;
380
  HBINT16 ySuperscriptYOffset;
381
  HBINT16 yStrikeoutSize;
382
  HBINT16 yStrikeoutPosition;
383
  HBINT16 sFamilyClass;
384
  HBUINT8 panose[10];
385
  HBUINT32  ulUnicodeRange[4];
386
  Tag   achVendID;
387
  HBUINT16  fsSelection;
388
  HBUINT16  usFirstCharIndex;
389
  HBUINT16  usLastCharIndex;
390
  HBINT16 sTypoAscender;
391
  HBINT16 sTypoDescender;
392
  HBINT16 sTypoLineGap;
393
  HBUINT16  usWinAscent;
394
  HBUINT16  usWinDescent;
395
  OS2V1Tail v1X;
396
  OS2V2Tail v2X;
397
  OS2V5Tail v5X;
398
  public:
399
  DEFINE_SIZE_MIN (78);
400
};
401
402
} /* namespace OT */
403
404
405
#endif /* HB_OT_OS2_TABLE_HH */