hb-ot-metrics.cc (15524B)
1 /* 2 * Copyright © 2018-2019 Ebrahim Byagowi 3 * 4 * This is part of HarfBuzz, a text shaping library. 5 * 6 * Permission is hereby granted, without written agreement and without 7 * license or royalty fees, to use, copy, modify, and distribute this 8 * software and its documentation for any purpose, provided that the 9 * above copyright notice and the following two paragraphs appear in 10 * all copies of this software. 11 * 12 * IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE TO ANY PARTY FOR 13 * DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES 14 * ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN 15 * IF THE COPYRIGHT HOLDER HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH 16 * DAMAGE. 17 * 18 * THE COPYRIGHT HOLDER SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING, 19 * BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND 20 * FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS 21 * ON AN "AS IS" BASIS, AND THE COPYRIGHT HOLDER HAS NO OBLIGATION TO 22 * PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS. 23 */ 24 25 #include "hb.hh" 26 27 #include "hb-ot-var-mvar-table.hh" 28 #include "hb-ot-gasp-table.hh" // Just so we compile it; unused otherwise. 29 #include "hb-ot-os2-table.hh" 30 #include "hb-ot-post-table.hh" 31 #include "hb-ot-hhea-table.hh" 32 #include "hb-ot-metrics.hh" 33 #include "hb-ot-face.hh" 34 35 36 /** 37 * SECTION:hb-ot-metrics 38 * @title: hb-ot-metrics 39 * @short_description: OpenType Metrics 40 * @include: hb-ot.h 41 * 42 * Functions for fetching metrics from fonts. 43 **/ 44 45 static float 46 _fix_ascender_descender (float value, hb_ot_metrics_tag_t metrics_tag) 47 { 48 if (metrics_tag == HB_OT_METRICS_TAG_HORIZONTAL_ASCENDER || 49 metrics_tag == HB_OT_METRICS_TAG_VERTICAL_ASCENDER) 50 return fabs ((double) value); 51 if (metrics_tag == HB_OT_METRICS_TAG_HORIZONTAL_DESCENDER || 52 metrics_tag == HB_OT_METRICS_TAG_VERTICAL_DESCENDER) 53 return -fabs ((double) value); 54 return value; 55 } 56 57 /* The common part of _get_position logic needed on hb-ot-font and here 58 to be able to have slim builds without the not always needed parts */ 59 bool 60 _hb_ot_metrics_get_position_common (hb_font_t *font, 61 hb_ot_metrics_tag_t metrics_tag, 62 hb_position_t *position /* OUT. May be NULL. */) 63 { 64 hb_face_t *face = font->face; 65 switch ((unsigned) metrics_tag) 66 { 67 #ifndef HB_NO_VAR 68 #define GET_VAR face->table.MVAR->get_var (metrics_tag, font->coords, font->num_coords) 69 #else 70 #define GET_VAR .0f 71 #endif 72 #define GET_METRIC_X(TABLE, ATTR) \ 73 (face->table.TABLE->has_data () && \ 74 ((void) (position && (*position = font->em_scalef_x (_fix_ascender_descender ( \ 75 face->table.TABLE->ATTR + GET_VAR, metrics_tag)))), true)) 76 #define GET_METRIC_Y(TABLE, ATTR) \ 77 (face->table.TABLE->has_data () && \ 78 ((void) (position && (*position = font->em_scalef_y (_fix_ascender_descender ( \ 79 face->table.TABLE->ATTR + GET_VAR, metrics_tag)))), true)) 80 81 case HB_OT_METRICS_TAG_HORIZONTAL_ASCENDER: 82 return (face->table.OS2->use_typo_metrics () && GET_METRIC_Y (OS2, sTypoAscender)) || 83 GET_METRIC_Y (hhea, ascender); 84 case HB_OT_METRICS_TAG_HORIZONTAL_DESCENDER: 85 return (face->table.OS2->use_typo_metrics () && GET_METRIC_Y (OS2, sTypoDescender)) || 86 GET_METRIC_Y (hhea, descender); 87 case HB_OT_METRICS_TAG_HORIZONTAL_LINE_GAP: 88 return (face->table.OS2->use_typo_metrics () && GET_METRIC_Y (OS2, sTypoLineGap)) || 89 GET_METRIC_Y (hhea, lineGap); 90 91 #ifndef HB_NO_VERTICAL 92 case HB_OT_METRICS_TAG_VERTICAL_ASCENDER: return GET_METRIC_X (vhea, ascender); 93 case HB_OT_METRICS_TAG_VERTICAL_DESCENDER: return GET_METRIC_X (vhea, descender); 94 case HB_OT_METRICS_TAG_VERTICAL_LINE_GAP: return GET_METRIC_X (vhea, lineGap); 95 #endif 96 97 #undef GET_METRIC_Y 98 #undef GET_METRIC_X 99 #undef GET_VAR 100 default: assert (0); return false; 101 } 102 } 103 104 #ifndef HB_NO_METRICS 105 106 #if 0 107 static bool 108 _get_gasp (hb_face_t *face, float *result, hb_ot_metrics_tag_t metrics_tag) 109 { 110 const OT::GaspRange& range = face->table.gasp->get_gasp_range (metrics_tag - HB_TAG ('g','s','p','0')); 111 if (&range == &Null (OT::GaspRange)) return false; 112 if (result) *result = range.rangeMaxPPEM + font->face->table.MVAR->get_var (metrics_tag, font->coords, font->num_coords); 113 return true; 114 } 115 #endif 116 117 /* Private tags for https://github.com/harfbuzz/harfbuzz/issues/1866 */ 118 #define _HB_OT_METRICS_TAG_HORIZONTAL_ASCENDER_OS2 HB_TAG ('O','a','s','c') 119 #define _HB_OT_METRICS_TAG_HORIZONTAL_ASCENDER_HHEA HB_TAG ('H','a','s','c') 120 #define _HB_OT_METRICS_TAG_HORIZONTAL_DESCENDER_OS2 HB_TAG ('O','d','s','c') 121 #define _HB_OT_METRICS_TAG_HORIZONTAL_DESCENDER_HHEA HB_TAG ('H','d','s','c') 122 #define _HB_OT_METRICS_TAG_HORIZONTAL_LINE_GAP_OS2 HB_TAG ('O','l','g','p') 123 #define _HB_OT_METRICS_TAG_HORIZONTAL_LINE_GAP_HHEA HB_TAG ('H','l','g','p') 124 125 /** 126 * hb_ot_metrics_get_position: 127 * @font: an #hb_font_t object. 128 * @metrics_tag: tag of metrics value you like to fetch. 129 * @position: (out) (optional): result of metrics value from the font. 130 * 131 * Fetches metrics value corresponding to @metrics_tag from @font. 132 * 133 * Returns: Whether found the requested metrics in the font. 134 * Since: 2.6.0 135 **/ 136 hb_bool_t 137 hb_ot_metrics_get_position (hb_font_t *font, 138 hb_ot_metrics_tag_t metrics_tag, 139 hb_position_t *position /* OUT. May be NULL. */) 140 { 141 hb_face_t *face = font->face; 142 switch ((unsigned) metrics_tag) 143 { 144 case HB_OT_METRICS_TAG_HORIZONTAL_ASCENDER: 145 case HB_OT_METRICS_TAG_HORIZONTAL_DESCENDER: 146 case HB_OT_METRICS_TAG_HORIZONTAL_LINE_GAP: 147 case HB_OT_METRICS_TAG_VERTICAL_ASCENDER: 148 case HB_OT_METRICS_TAG_VERTICAL_DESCENDER: 149 case HB_OT_METRICS_TAG_VERTICAL_LINE_GAP: return _hb_ot_metrics_get_position_common (font, metrics_tag, position); 150 #ifndef HB_NO_VAR 151 #define GET_VAR hb_ot_metrics_get_variation (font, metrics_tag) 152 #else 153 #define GET_VAR 0 154 #endif 155 #define GET_METRIC_X(TABLE, ATTR) \ 156 (face->table.TABLE->has_data () && \ 157 ((void) (position && (*position = font->em_scalef_x (face->table.TABLE->ATTR + GET_VAR))), true)) 158 #define GET_METRIC_Y(TABLE, ATTR) \ 159 (face->table.TABLE->has_data () && \ 160 ((void) (position && (*position = font->em_scalef_y (face->table.TABLE->ATTR + GET_VAR))), true)) 161 case HB_OT_METRICS_TAG_HORIZONTAL_CLIPPING_ASCENT: return GET_METRIC_Y (OS2, usWinAscent); 162 case HB_OT_METRICS_TAG_HORIZONTAL_CLIPPING_DESCENT: return GET_METRIC_Y (OS2, usWinDescent); 163 164 case HB_OT_METRICS_TAG_HORIZONTAL_CARET_RISE: 165 case HB_OT_METRICS_TAG_HORIZONTAL_CARET_RUN: 166 { 167 unsigned mult = 1u; 168 169 if (font->slant) 170 { 171 unsigned rise = face->table.hhea->caretSlopeRise; 172 unsigned upem = face->get_upem (); 173 mult = (rise && rise < upem) ? hb_min (upem / rise, 256u) : 1u; 174 } 175 176 if (metrics_tag == HB_OT_METRICS_TAG_HORIZONTAL_CARET_RISE) 177 { 178 bool ret = GET_METRIC_Y (hhea, caretSlopeRise); 179 180 if (position) 181 *position *= mult; 182 183 return ret; 184 } 185 else 186 { 187 hb_position_t rise = 0; 188 189 if (font->slant && position && GET_METRIC_Y (hhea, caretSlopeRise)) 190 rise = *position; 191 192 bool ret = GET_METRIC_X (hhea, caretSlopeRun); 193 194 if (position) 195 { 196 *position *= mult; 197 198 if (font->slant) 199 *position += roundf (mult * font->slant_xy * rise); 200 } 201 202 return ret; 203 } 204 } 205 case HB_OT_METRICS_TAG_HORIZONTAL_CARET_OFFSET: return GET_METRIC_X (hhea, caretOffset); 206 207 #ifndef HB_NO_VERTICAL 208 case HB_OT_METRICS_TAG_VERTICAL_CARET_RISE: return GET_METRIC_X (vhea, caretSlopeRise); 209 case HB_OT_METRICS_TAG_VERTICAL_CARET_RUN: return GET_METRIC_Y (vhea, caretSlopeRun); 210 case HB_OT_METRICS_TAG_VERTICAL_CARET_OFFSET: return GET_METRIC_Y (vhea, caretOffset); 211 #endif 212 case HB_OT_METRICS_TAG_X_HEIGHT: return GET_METRIC_Y (OS2->v2 (), sxHeight); 213 case HB_OT_METRICS_TAG_CAP_HEIGHT: return GET_METRIC_Y (OS2->v2 (), sCapHeight); 214 case HB_OT_METRICS_TAG_SUBSCRIPT_EM_X_SIZE: return GET_METRIC_X (OS2, ySubscriptXSize); 215 case HB_OT_METRICS_TAG_SUBSCRIPT_EM_Y_SIZE: return GET_METRIC_Y (OS2, ySubscriptYSize); 216 case HB_OT_METRICS_TAG_SUBSCRIPT_EM_X_OFFSET: return GET_METRIC_X (OS2, ySubscriptXOffset); 217 case HB_OT_METRICS_TAG_SUBSCRIPT_EM_Y_OFFSET: return GET_METRIC_Y (OS2, ySubscriptYOffset); 218 case HB_OT_METRICS_TAG_SUPERSCRIPT_EM_X_SIZE: return GET_METRIC_X (OS2, ySuperscriptXSize); 219 case HB_OT_METRICS_TAG_SUPERSCRIPT_EM_Y_SIZE: return GET_METRIC_Y (OS2, ySuperscriptYSize); 220 case HB_OT_METRICS_TAG_SUPERSCRIPT_EM_X_OFFSET: return GET_METRIC_X (OS2, ySuperscriptXOffset); 221 case HB_OT_METRICS_TAG_SUPERSCRIPT_EM_Y_OFFSET: return GET_METRIC_Y (OS2, ySuperscriptYOffset); 222 case HB_OT_METRICS_TAG_STRIKEOUT_SIZE: return GET_METRIC_Y (OS2, yStrikeoutSize); 223 case HB_OT_METRICS_TAG_STRIKEOUT_OFFSET: return GET_METRIC_Y (OS2, yStrikeoutPosition); 224 case HB_OT_METRICS_TAG_UNDERLINE_SIZE: return GET_METRIC_Y (post->table, underlineThickness); 225 case HB_OT_METRICS_TAG_UNDERLINE_OFFSET: return GET_METRIC_Y (post->table, underlinePosition); 226 227 /* Private tags */ 228 case _HB_OT_METRICS_TAG_HORIZONTAL_ASCENDER_OS2: return GET_METRIC_Y (OS2, sTypoAscender); 229 case _HB_OT_METRICS_TAG_HORIZONTAL_ASCENDER_HHEA: return GET_METRIC_Y (hhea, ascender); 230 case _HB_OT_METRICS_TAG_HORIZONTAL_DESCENDER_OS2: return GET_METRIC_Y (OS2, sTypoDescender); 231 case _HB_OT_METRICS_TAG_HORIZONTAL_DESCENDER_HHEA: return GET_METRIC_Y (hhea, descender); 232 case _HB_OT_METRICS_TAG_HORIZONTAL_LINE_GAP_OS2: return GET_METRIC_Y (OS2, sTypoLineGap); 233 case _HB_OT_METRICS_TAG_HORIZONTAL_LINE_GAP_HHEA: return GET_METRIC_Y (hhea, lineGap); 234 #undef GET_METRIC_Y 235 #undef GET_METRIC_X 236 #undef GET_VAR 237 default: return false; 238 } 239 } 240 241 /** 242 * hb_ot_metrics_get_position_with_fallback: 243 * @font: an #hb_font_t object. 244 * @metrics_tag: tag of metrics value you like to fetch. 245 * @position: (out) (optional): result of metrics value from the font. 246 * 247 * Fetches metrics value corresponding to @metrics_tag from @font, 248 * and synthesizes a value if it the value is missing in the font. 249 * 250 * Since: 4.0.0 251 **/ 252 void 253 hb_ot_metrics_get_position_with_fallback (hb_font_t *font, 254 hb_ot_metrics_tag_t metrics_tag, 255 hb_position_t *position /* OUT */) 256 { 257 hb_font_extents_t font_extents; 258 hb_codepoint_t glyph; 259 hb_glyph_extents_t extents; 260 261 if (hb_ot_metrics_get_position (font, metrics_tag, position)) 262 { 263 if ((metrics_tag != HB_OT_METRICS_TAG_STRIKEOUT_SIZE && 264 metrics_tag != HB_OT_METRICS_TAG_UNDERLINE_SIZE) || 265 *position != 0) 266 return; 267 } 268 269 switch (metrics_tag) 270 { 271 case HB_OT_METRICS_TAG_HORIZONTAL_ASCENDER: 272 case HB_OT_METRICS_TAG_HORIZONTAL_CLIPPING_ASCENT: 273 hb_font_get_extents_for_direction (font, HB_DIRECTION_LTR, &font_extents); 274 *position = font_extents.ascender; 275 break; 276 277 case HB_OT_METRICS_TAG_VERTICAL_ASCENDER: 278 hb_font_get_extents_for_direction (font, HB_DIRECTION_TTB, &font_extents); 279 *position = font_extents.ascender; 280 break; 281 282 case HB_OT_METRICS_TAG_HORIZONTAL_DESCENDER: 283 case HB_OT_METRICS_TAG_HORIZONTAL_CLIPPING_DESCENT: 284 hb_font_get_extents_for_direction (font, HB_DIRECTION_LTR, &font_extents); 285 *position = font_extents.descender; 286 break; 287 288 case HB_OT_METRICS_TAG_VERTICAL_DESCENDER: 289 hb_font_get_extents_for_direction (font, HB_DIRECTION_TTB, &font_extents); 290 *position = font_extents.ascender; 291 break; 292 293 case HB_OT_METRICS_TAG_HORIZONTAL_LINE_GAP: 294 hb_font_get_extents_for_direction (font, HB_DIRECTION_LTR, &font_extents); 295 *position = font_extents.line_gap; 296 break; 297 298 case HB_OT_METRICS_TAG_VERTICAL_LINE_GAP: 299 hb_font_get_extents_for_direction (font, HB_DIRECTION_TTB, &font_extents); 300 *position = font_extents.line_gap; 301 break; 302 303 case HB_OT_METRICS_TAG_HORIZONTAL_CARET_RISE: 304 case HB_OT_METRICS_TAG_VERTICAL_CARET_RISE: 305 *position = 1; 306 break; 307 308 case HB_OT_METRICS_TAG_HORIZONTAL_CARET_RUN: 309 case HB_OT_METRICS_TAG_VERTICAL_CARET_RUN: 310 *position = 0; 311 break; 312 313 case HB_OT_METRICS_TAG_HORIZONTAL_CARET_OFFSET: 314 case HB_OT_METRICS_TAG_VERTICAL_CARET_OFFSET: 315 *position = 0; 316 break; 317 318 case HB_OT_METRICS_TAG_X_HEIGHT: 319 if (hb_font_get_nominal_glyph (font, 'x', &glyph) && 320 hb_font_get_glyph_extents (font, glyph, &extents)) 321 *position = extents.y_bearing; 322 else 323 *position = font->y_scale / 2; 324 break; 325 326 case HB_OT_METRICS_TAG_CAP_HEIGHT: 327 if (hb_font_get_nominal_glyph (font, 'O', &glyph) && 328 hb_font_get_glyph_extents (font, glyph, &extents)) 329 *position = extents.height + 2 * extents.y_bearing; 330 else 331 *position = font->y_scale * 2 / 3; 332 break; 333 334 case HB_OT_METRICS_TAG_STRIKEOUT_SIZE: 335 case HB_OT_METRICS_TAG_UNDERLINE_SIZE: 336 *position = font->y_scale / 18; 337 break; 338 339 case HB_OT_METRICS_TAG_STRIKEOUT_OFFSET: 340 { 341 hb_position_t ascender; 342 hb_ot_metrics_get_position_with_fallback (font, 343 HB_OT_METRICS_TAG_HORIZONTAL_ASCENDER, 344 &ascender); 345 *position = ascender / 2; 346 } 347 break; 348 349 case HB_OT_METRICS_TAG_UNDERLINE_OFFSET: 350 *position = - font->y_scale / 18; 351 break; 352 353 case HB_OT_METRICS_TAG_SUBSCRIPT_EM_X_SIZE: 354 case HB_OT_METRICS_TAG_SUPERSCRIPT_EM_X_SIZE: 355 *position = font->x_scale * 10 / 12; 356 break; 357 358 case HB_OT_METRICS_TAG_SUBSCRIPT_EM_Y_SIZE: 359 case HB_OT_METRICS_TAG_SUPERSCRIPT_EM_Y_SIZE: 360 *position = font->y_scale * 10 / 12; 361 break; 362 363 case HB_OT_METRICS_TAG_SUBSCRIPT_EM_X_OFFSET: 364 case HB_OT_METRICS_TAG_SUPERSCRIPT_EM_X_OFFSET: 365 *position = 0; 366 break; 367 368 case HB_OT_METRICS_TAG_SUBSCRIPT_EM_Y_OFFSET: 369 case HB_OT_METRICS_TAG_SUPERSCRIPT_EM_Y_OFFSET: 370 *position = font->y_scale / 5; 371 break; 372 373 case _HB_OT_METRICS_TAG_MAX_VALUE: 374 default: 375 *position = 0; 376 break; 377 } 378 } 379 380 #ifndef HB_NO_VAR 381 /** 382 * hb_ot_metrics_get_variation: 383 * @font: an #hb_font_t object. 384 * @metrics_tag: tag of metrics value you like to fetch. 385 * 386 * Fetches metrics value corresponding to @metrics_tag from @font with the 387 * current font variation settings applied. 388 * 389 * Returns: The requested metric value. 390 * 391 * Since: 2.6.0 392 **/ 393 float 394 hb_ot_metrics_get_variation (hb_font_t *font, hb_ot_metrics_tag_t metrics_tag) 395 { 396 return font->face->table.MVAR->get_var (metrics_tag, font->coords, font->num_coords); 397 } 398 399 /** 400 * hb_ot_metrics_get_x_variation: 401 * @font: an #hb_font_t object. 402 * @metrics_tag: tag of metrics value you like to fetch. 403 * 404 * Fetches horizontal metrics value corresponding to @metrics_tag from @font 405 * with the current font variation settings applied. 406 * 407 * Returns: The requested metric value. 408 * 409 * Since: 2.6.0 410 **/ 411 hb_position_t 412 hb_ot_metrics_get_x_variation (hb_font_t *font, hb_ot_metrics_tag_t metrics_tag) 413 { 414 return font->em_scalef_x (hb_ot_metrics_get_variation (font, metrics_tag)); 415 } 416 417 /** 418 * hb_ot_metrics_get_y_variation: 419 * @font: an #hb_font_t object. 420 * @metrics_tag: tag of metrics value you like to fetch. 421 * 422 * Fetches vertical metrics value corresponding to @metrics_tag from @font with 423 * the current font variation settings applied. 424 * 425 * Returns: The requested metric value. 426 * 427 * Since: 2.6.0 428 **/ 429 hb_position_t 430 hb_ot_metrics_get_y_variation (hb_font_t *font, hb_ot_metrics_tag_t metrics_tag) 431 { 432 return font->em_scalef_y (hb_ot_metrics_get_variation (font, metrics_tag)); 433 } 434 #endif 435 436 #endif