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 { sanitizeOT::OS2V1Tail48 bool sanitize (hb_sanitize_context_t *c) const 49 { 50 TRACE_SANITIZE (this); 51 return_trace (c->check_struct (this)); 52 } 53 54 public: 55 HBUINT32 ulCodePageRange1; 56 HBUINT32 ulCodePageRange2; 57 public: 58 DEFINE_SIZE_STATIC (8); 59 }; 60 61 struct OS2V2Tail 62 { has_dataOT::OS2V2Tail63 bool has_data () const { return sxHeight || sCapHeight; } 64 operator ->OT::OS2V2Tail65 const OS2V2Tail * operator -> () const { return this; } operator ->OT::OS2V2Tail66 OS2V2Tail * operator -> () { return this; } 67 sanitizeOT::OS2V2Tail68 bool sanitize (hb_sanitize_context_t *c) const 69 { 70 TRACE_SANITIZE (this); 71 return_trace (c->check_struct (this)); 72 } 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 { get_optical_sizeOT::OS2V5Tail86 inline bool get_optical_size (unsigned int *lower, unsigned int *upper) const 87 { 88 unsigned int lower_optical_size = usLowerOpticalPointSize; 89 unsigned int upper_optical_size = usUpperOpticalPointSize; 90 91 /* Per https://docs.microsoft.com/en-us/typography/opentype/spec/os2#lps */ 92 if (lower_optical_size < upper_optical_size && 93 lower_optical_size >= 1 && lower_optical_size <= 0xFFFE && 94 upper_optical_size >= 2 && upper_optical_size <= 0xFFFF) 95 { 96 *lower = lower_optical_size; 97 *upper = upper_optical_size; 98 return true; 99 } 100 return false; 101 } 102 sanitizeOT::OS2V5Tail103 bool sanitize (hb_sanitize_context_t *c) const 104 { 105 TRACE_SANITIZE (this); 106 return_trace (c->check_struct (this)); 107 } 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 has_dataOT::OS2120 bool has_data () const { return usWeightClass || usWidthClass || usFirstCharIndex || usLastCharIndex; } 121 v1OT::OS2122 const OS2V1Tail &v1 () const { return version >= 1 ? v1X : Null (OS2V1Tail); } v2OT::OS2123 const OS2V2Tail &v2 () const { return version >= 2 ? v2X : Null (OS2V2Tail); } v5OT::OS2124 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 is_italicOT::OS2139 bool is_italic () const { return fsSelection & ITALIC; } is_obliqueOT::OS2140 bool is_oblique () const { return fsSelection & OBLIQUE; } use_typo_metricsOT::OS2141 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 get_widthOT::OS2155 float get_width () const 156 { 157 switch (usWidthClass) { 158 case FWIDTH_ULTRA_CONDENSED:return 50.f; 159 case FWIDTH_EXTRA_CONDENSED:return 62.5f; 160 case FWIDTH_CONDENSED: return 75.f; 161 case FWIDTH_SEMI_CONDENSED: return 87.5f; 162 default: 163 case FWIDTH_NORMAL: return 100.f; 164 case FWIDTH_SEMI_EXPANDED: return 112.5f; 165 case FWIDTH_EXPANDED: return 125.f; 166 case FWIDTH_EXTRA_EXPANDED: return 150.f; 167 case FWIDTH_ULTRA_EXPANDED: return 200.f; 168 } 169 } 170 map_wdth_to_widthclassOT::OS2171 float map_wdth_to_widthclass(float width) const 172 { 173 if (width < 50) return 1.0f; 174 if (width > 200) return 9.0f; 175 176 float ratio = (width - 50) / 12.5f; 177 int a = (int) floorf (ratio); 178 int b = (int) ceilf (ratio); 179 180 /* follow this maping: 181 * https://docs.microsoft.com/en-us/typography/opentype/spec/os2#uswidthclass 182 */ 183 if (b <= 6) // 50-125 184 { 185 if (a == b) return a + 1.0f; 186 } 187 else if (b == 7) // no mapping for 137.5 188 { 189 a = 6; 190 b = 8; 191 } 192 else if (b == 8) 193 { 194 if (a == b) return 8.0f; // 150 195 a = 6; 196 } 197 else 198 { 199 if (a == b && a == 12) return 9.0f; //200 200 b = 12; 201 a = 8; 202 } 203 204 float va = 50 + a * 12.5f; 205 float vb = 50 + b * 12.5f; 206 207 float ret = a + (width - va) / (vb - va); 208 if (a <= 6) ret += 1.0f; 209 return ret; 210 } 211 calc_avg_char_widthOT::OS2212 static unsigned calc_avg_char_width (const hb_hashmap_t<hb_codepoint_t, hb_pair_t<unsigned, int>>& hmtx_map) 213 { 214 unsigned num = 0; 215 unsigned total_width = 0; 216 for (const auto& _ : hmtx_map.values_ref ()) 217 { 218 unsigned width = _.first; 219 if (width) 220 { 221 total_width += width; 222 num++; 223 } 224 } 225 226 return num ? (unsigned) roundf ((double) total_width / (double) num) : 0; 227 } 228 subsetOT::OS2229 bool subset (hb_subset_context_t *c) const 230 { 231 TRACE_SUBSET (this); 232 OS2 *os2_prime = c->serializer->embed (this); 233 if (unlikely (!os2_prime)) return_trace (false); 234 235 #ifndef HB_NO_VAR 236 if (c->plan->normalized_coords) 237 { 238 auto &MVAR = *c->plan->source->table.MVAR; 239 auto *table = os2_prime; 240 241 HB_ADD_MVAR_VAR (HB_OT_METRICS_TAG_HORIZONTAL_ASCENDER, sTypoAscender); 242 HB_ADD_MVAR_VAR (HB_OT_METRICS_TAG_HORIZONTAL_DESCENDER, sTypoDescender); 243 HB_ADD_MVAR_VAR (HB_OT_METRICS_TAG_HORIZONTAL_LINE_GAP, sTypoLineGap); 244 HB_ADD_MVAR_VAR (HB_OT_METRICS_TAG_HORIZONTAL_CLIPPING_ASCENT, usWinAscent); 245 HB_ADD_MVAR_VAR (HB_OT_METRICS_TAG_HORIZONTAL_CLIPPING_DESCENT, usWinDescent); 246 HB_ADD_MVAR_VAR (HB_OT_METRICS_TAG_SUBSCRIPT_EM_X_SIZE, ySubscriptXSize); 247 HB_ADD_MVAR_VAR (HB_OT_METRICS_TAG_SUBSCRIPT_EM_Y_SIZE, ySubscriptYSize); 248 HB_ADD_MVAR_VAR (HB_OT_METRICS_TAG_SUBSCRIPT_EM_X_OFFSET, ySubscriptXOffset); 249 HB_ADD_MVAR_VAR (HB_OT_METRICS_TAG_SUBSCRIPT_EM_Y_OFFSET, ySubscriptYOffset); 250 HB_ADD_MVAR_VAR (HB_OT_METRICS_TAG_SUPERSCRIPT_EM_X_SIZE, ySuperscriptXSize); 251 HB_ADD_MVAR_VAR (HB_OT_METRICS_TAG_SUPERSCRIPT_EM_Y_SIZE, ySuperscriptYSize); 252 HB_ADD_MVAR_VAR (HB_OT_METRICS_TAG_SUPERSCRIPT_EM_X_OFFSET, ySuperscriptXOffset); 253 HB_ADD_MVAR_VAR (HB_OT_METRICS_TAG_SUPERSCRIPT_EM_Y_OFFSET, ySuperscriptYOffset); 254 HB_ADD_MVAR_VAR (HB_OT_METRICS_TAG_STRIKEOUT_SIZE, yStrikeoutSize); 255 HB_ADD_MVAR_VAR (HB_OT_METRICS_TAG_STRIKEOUT_OFFSET, yStrikeoutPosition); 256 257 if (os2_prime->version >= 2) 258 { 259 hb_barrier (); 260 auto *table = & const_cast<OS2V2Tail &> (os2_prime->v2 ()); 261 HB_ADD_MVAR_VAR (HB_OT_METRICS_TAG_X_HEIGHT, sxHeight); 262 HB_ADD_MVAR_VAR (HB_OT_METRICS_TAG_CAP_HEIGHT, sCapHeight); 263 } 264 265 unsigned avg_char_width = calc_avg_char_width (c->plan->hmtx_map); 266 if (!c->serializer->check_assign (os2_prime->xAvgCharWidth, avg_char_width, 267 HB_SERIALIZE_ERROR_INT_OVERFLOW)) 268 return_trace (false); 269 } 270 #endif 271 272 Triple *axis_range; 273 if (c->plan->user_axes_location.has (HB_TAG ('w','g','h','t'), &axis_range)) 274 { 275 unsigned weight_class = static_cast<unsigned> (roundf (hb_clamp (axis_range->middle, 1.0, 1000.0))); 276 if (os2_prime->usWeightClass != weight_class) 277 os2_prime->usWeightClass = weight_class; 278 } 279 280 if (c->plan->user_axes_location.has (HB_TAG ('w','d','t','h'), &axis_range)) 281 { 282 unsigned width_class = static_cast<unsigned> (roundf (map_wdth_to_widthclass (axis_range->middle))); 283 if (os2_prime->usWidthClass != width_class) 284 os2_prime->usWidthClass = width_class; 285 } 286 287 os2_prime->usFirstCharIndex = hb_min (0xFFFFu, c->plan->unicodes.get_min ()); 288 os2_prime->usLastCharIndex = hb_min (0xFFFFu, c->plan->unicodes.get_max ()); 289 290 if (c->plan->flags & HB_SUBSET_FLAGS_NO_PRUNE_UNICODE_RANGES) 291 return_trace (true); 292 293 _update_unicode_ranges (&c->plan->unicodes, os2_prime->ulUnicodeRange); 294 295 return_trace (true); 296 } 297 _update_unicode_rangesOT::OS2298 void _update_unicode_ranges (const hb_set_t *codepoints, 299 HBUINT32 ulUnicodeRange[4]) const 300 { 301 HBUINT32 newBits[4]; 302 for (unsigned int i = 0; i < 4; i++) 303 newBits[i] = 0; 304 305 /* This block doesn't show up in profiles. If it ever did, 306 * we can rewrite it to iterate over OS/2 ranges and use 307 * set iteration to check if the range matches. */ 308 for (auto cp : *codepoints) 309 { 310 unsigned int bit = _hb_ot_os2_get_unicode_range_bit (cp); 311 if (bit < 128) 312 { 313 unsigned int block = bit / 32; 314 unsigned int bit_in_block = bit % 32; 315 unsigned int mask = 1 << bit_in_block; 316 newBits[block] = newBits[block] | mask; 317 } 318 if (cp >= 0x10000 && cp <= 0x110000) 319 { 320 /* the spec says that bit 57 ("Non Plane 0") implies that there's 321 at least one codepoint beyond the BMP; so I also include all 322 the non-BMP codepoints here */ 323 newBits[1] = newBits[1] | (1 << 25); 324 } 325 } 326 327 for (unsigned int i = 0; i < 4; i++) 328 ulUnicodeRange[i] = ulUnicodeRange[i] & newBits[i]; // set bits only if set in the original 329 } 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 }; get_font_pageOT::OS2344 font_page_t get_font_page () const 345 { return (font_page_t) (version == 0 ? fsSelection & 0xFF00 : 0); } 346 get_sizeOT::OS2347 unsigned get_size () const 348 { 349 unsigned result = min_size; 350 if (version >= 1) result += v1X.get_size (); 351 if (version >= 2) result += v2X.get_size (); 352 if (version >= 5) result += v5X.get_size (); 353 return result; 354 } 355 sanitizeOT::OS2356 bool sanitize (hb_sanitize_context_t *c) const 357 { 358 TRACE_SANITIZE (this); 359 if (unlikely (!c->check_struct (this))) return_trace (false); 360 hb_barrier (); 361 if (unlikely (version >= 1 && !v1X.sanitize (c))) return_trace (false); 362 if (unlikely (version >= 2 && !v2X.sanitize (c))) return_trace (false); 363 if (unlikely (version >= 5 && !v5X.sanitize (c))) return_trace (false); 364 return_trace (true); 365 } 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 */ 406