1 /* 2 * Copyright 2011,2012 Google, Inc. 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 * Google Author(s): Behdad Esfahbod 25 */ 26 27 #include "options.hh" 28 29 #ifdef HAVE_FREETYPE 30 #include <hb-ft.h> 31 #endif 32 #ifdef HAVE_OT 33 #include <hb-ot.h> 34 #endif 35 36 struct supported_font_funcs_t { 37 char name[4]; 38 void (*func) (hb_font_t *); 39 } supported_font_funcs[] = 40 { 41 #ifdef HAVE_FREETYPE 42 {"ft", hb_ft_font_set_funcs}, 43 #endif 44 #ifdef HAVE_OT 45 {"ot", hb_ot_font_set_funcs}, 46 #endif 47 }; 48 49 50 void 51 fail (hb_bool_t suggest_help, const char *format, ...) 52 { 53 const char *msg; 54 55 va_list vap; 56 va_start (vap, format); 57 msg = g_strdup_vprintf (format, vap); 58 va_end (vap); 59 const char *prgname = g_get_prgname (); 60 g_printerr ("%s: %s\n", prgname, msg); 61 if (suggest_help) 62 g_printerr ("Try `%s --help' for more information.\n", prgname); 63 64 exit (1); 65 } 66 67 68 hb_bool_t debug = false; 69 70 static gchar * 71 shapers_to_string (void) 72 { 73 GString *shapers = g_string_new (nullptr); 74 const char **shaper_list = hb_shape_list_shapers (); 75 76 for (; *shaper_list; shaper_list++) { 77 g_string_append (shapers, *shaper_list); 78 g_string_append_c (shapers, ','); 79 } 80 g_string_truncate (shapers, MAX (0, (gint)shapers->len - 1)); 81 82 return g_string_free (shapers, false); 83 } 84 85 static G_GNUC_NORETURN gboolean 86 show_version (const char *name G_GNUC_UNUSED, 87 const char *arg G_GNUC_UNUSED, 88 gpointer data G_GNUC_UNUSED, 89 GError **error G_GNUC_UNUSED) 90 { 91 g_printf ("%s (%s) %s\n", g_get_prgname (), PACKAGE_NAME, PACKAGE_VERSION); 92 93 char *shapers = shapers_to_string (); 94 g_printf ("Available shapers: %s\n", shapers); 95 g_free (shapers); 96 if (strcmp (HB_VERSION_STRING, hb_version_string ())) 97 g_printf ("Linked HarfBuzz library has a different version: %s\n", hb_version_string ()); 98 99 exit(0); 100 } 101 102 103 void 104 option_parser_t::add_main_options (void) 105 { 106 GOptionEntry entries[] = 107 { 108 {"version", 0, G_OPTION_FLAG_NO_ARG, 109 G_OPTION_ARG_CALLBACK, (gpointer) &show_version, "Show version numbers", nullptr}, 110 {"debug", 0, 0, G_OPTION_ARG_NONE, &debug, "Free all resources before exit", nullptr}, 111 {nullptr} 112 }; 113 g_option_context_add_main_entries (context, entries, nullptr); 114 } 115 116 static gboolean 117 pre_parse (GOptionContext *context G_GNUC_UNUSED, 118 GOptionGroup *group G_GNUC_UNUSED, 119 gpointer data, 120 GError **error) 121 { 122 option_group_t *option_group = (option_group_t *) data; 123 option_group->pre_parse (error); 124 return *error == nullptr; 125 } 126 127 static gboolean 128 post_parse (GOptionContext *context G_GNUC_UNUSED, 129 GOptionGroup *group G_GNUC_UNUSED, 130 gpointer data, 131 GError **error) 132 { 133 option_group_t *option_group = static_cast<option_group_t *>(data); 134 option_group->post_parse (error); 135 return *error == nullptr; 136 } 137 138 void 139 option_parser_t::add_group (GOptionEntry *entries, 140 const gchar *name, 141 const gchar *description, 142 const gchar *help_description, 143 option_group_t *option_group) 144 { 145 GOptionGroup *group = g_option_group_new (name, description, help_description, 146 static_cast<gpointer>(option_group), nullptr); 147 g_option_group_add_entries (group, entries); 148 g_option_group_set_parse_hooks (group, pre_parse, post_parse); 149 g_option_context_add_group (context, group); 150 } 151 152 void 153 option_parser_t::parse (int *argc, char ***argv) 154 { 155 setlocale (LC_ALL, ""); 156 157 GError *parse_error = nullptr; 158 if (!g_option_context_parse (context, argc, argv, &parse_error)) 159 { 160 if (parse_error != nullptr) { 161 fail (true, "%s", parse_error->message); 162 //g_error_free (parse_error); 163 } else 164 fail (true, "Option parse error"); 165 } 166 } 167 168 169 static gboolean 170 parse_margin (const char *name G_GNUC_UNUSED, 171 const char *arg, 172 gpointer data, 173 GError **error G_GNUC_UNUSED) 174 { 175 view_options_t *view_opts = (view_options_t *) data; 176 view_options_t::margin_t &m = view_opts->margin; 177 switch (sscanf (arg, "%lf%*[ ,]%lf%*[ ,]%lf%*[ ,]%lf", &m.t, &m.r, &m.b, &m.l)) { 178 case 1: m.r = m.t; 179 case 2: m.b = m.t; 180 case 3: m.l = m.r; 181 case 4: return true; 182 default: 183 g_set_error (error, G_OPTION_ERROR, G_OPTION_ERROR_BAD_VALUE, 184 "%s argument should be one to four space-separated numbers", 185 name); 186 return false; 187 } 188 } 189 190 191 static gboolean 192 parse_shapers (const char *name G_GNUC_UNUSED, 193 const char *arg, 194 gpointer data, 195 GError **error G_GNUC_UNUSED) 196 { 197 shape_options_t *shape_opts = (shape_options_t *) data; 198 g_strfreev (shape_opts->shapers); 199 shape_opts->shapers = g_strsplit (arg, ",", 0); 200 return true; 201 } 202 203 static G_GNUC_NORETURN gboolean 204 list_shapers (const char *name G_GNUC_UNUSED, 205 const char *arg G_GNUC_UNUSED, 206 gpointer data G_GNUC_UNUSED, 207 GError **error G_GNUC_UNUSED) 208 { 209 for (const char **shaper = hb_shape_list_shapers (); *shaper; shaper++) 210 g_printf ("%s\n", *shaper); 211 212 exit(0); 213 } 214 215 216 static gboolean 217 parse_features (const char *name G_GNUC_UNUSED, 218 const char *arg, 219 gpointer data, 220 GError **error G_GNUC_UNUSED) 221 { 222 shape_options_t *shape_opts = (shape_options_t *) data; 223 char *s = (char *) arg; 224 char *p; 225 226 shape_opts->num_features = 0; 227 g_free (shape_opts->features); 228 shape_opts->features = nullptr; 229 230 if (!*s) 231 return true; 232 233 /* count the features first, so we can allocate memory */ 234 p = s; 235 do { 236 shape_opts->num_features++; 237 p = strchr (p, ','); 238 if (p) 239 p++; 240 } while (p); 241 242 shape_opts->features = (hb_feature_t *) calloc (shape_opts->num_features, sizeof (*shape_opts->features)); 243 if (!shape_opts->features) 244 return false; 245 246 /* now do the actual parsing */ 247 p = s; 248 shape_opts->num_features = 0; 249 while (p && *p) { 250 char *end = strchr (p, ','); 251 if (hb_feature_from_string (p, end ? end - p : -1, &shape_opts->features[shape_opts->num_features])) 252 shape_opts->num_features++; 253 p = end ? end + 1 : nullptr; 254 } 255 256 return true; 257 } 258 259 static gboolean 260 parse_variations (const char *name G_GNUC_UNUSED, 261 const char *arg, 262 gpointer data, 263 GError **error G_GNUC_UNUSED) 264 { 265 font_options_t *font_opts = (font_options_t *) data; 266 char *s = (char *) arg; 267 char *p; 268 269 font_opts->num_variations = 0; 270 g_free (font_opts->variations); 271 font_opts->variations = nullptr; 272 273 if (!*s) 274 return true; 275 276 /* count the variations first, so we can allocate memory */ 277 p = s; 278 do { 279 font_opts->num_variations++; 280 p = strchr (p, ','); 281 if (p) 282 p++; 283 } while (p); 284 285 font_opts->variations = (hb_variation_t *) calloc (font_opts->num_variations, sizeof (*font_opts->variations)); 286 if (!font_opts->variations) 287 return false; 288 289 /* now do the actual parsing */ 290 p = s; 291 font_opts->num_variations = 0; 292 while (p && *p) { 293 char *end = strchr (p, ','); 294 if (hb_variation_from_string (p, end ? end - p : -1, &font_opts->variations[font_opts->num_variations])) 295 font_opts->num_variations++; 296 p = end ? end + 1 : nullptr; 297 } 298 299 return true; 300 } 301 302 static gboolean 303 parse_text (const char *name G_GNUC_UNUSED, 304 const char *arg, 305 gpointer data, 306 GError **error G_GNUC_UNUSED) 307 { 308 text_options_t *text_opts = (text_options_t *) data; 309 310 if (text_opts->text) 311 { 312 g_set_error (error, G_OPTION_ERROR, G_OPTION_ERROR_BAD_VALUE, 313 "Either --text or --unicodes can be provided but not both"); 314 return false; 315 } 316 317 text_opts->text = g_strdup (arg); 318 return true; 319 } 320 321 322 static gboolean 323 parse_unicodes (const char *name G_GNUC_UNUSED, 324 const char *arg, 325 gpointer data, 326 GError **error G_GNUC_UNUSED) 327 { 328 text_options_t *text_opts = (text_options_t *) data; 329 330 if (text_opts->text) 331 { 332 g_set_error (error, G_OPTION_ERROR, G_OPTION_ERROR_BAD_VALUE, 333 "Either --text or --unicodes can be provided but not both"); 334 return false; 335 } 336 337 GString *gs = g_string_new (nullptr); 338 char *s = (char *) arg; 339 char *p; 340 341 while (s && *s) 342 { 343 while (*s && strchr ("<+>{},;&#\\xXuUnNiI\n\t", *s)) 344 s++; 345 346 errno = 0; 347 hb_codepoint_t u = strtoul (s, &p, 16); 348 if (errno || s == p) 349 { 350 g_set_error (error, G_OPTION_ERROR, G_OPTION_ERROR_BAD_VALUE, 351 "Failed parsing Unicode values at: '%s'", s); 352 return false; 353 } 354 355 g_string_append_unichar (gs, u); 356 357 s = p; 358 } 359 360 text_opts->text = g_string_free (gs, FALSE); 361 return true; 362 } 363 364 365 void 366 view_options_t::add_options (option_parser_t *parser) 367 { 368 GOptionEntry entries[] = 369 { 370 {"annotate", 0, 0, G_OPTION_ARG_NONE, &this->annotate, "Annotate output rendering", nullptr}, 371 {"background", 0, 0, G_OPTION_ARG_STRING, &this->back, "Set background color (default: " DEFAULT_BACK ")", "rrggbb/rrggbbaa"}, 372 {"foreground", 0, 0, G_OPTION_ARG_STRING, &this->fore, "Set foreground color (default: " DEFAULT_FORE ")", "rrggbb/rrggbbaa"}, 373 {"line-space", 0, 0, G_OPTION_ARG_DOUBLE, &this->line_space, "Set space between lines (default: 0)", "units"}, 374 {"margin", 0, 0, G_OPTION_ARG_CALLBACK, (gpointer) &parse_margin, "Margin around output (default: " G_STRINGIFY(DEFAULT_MARGIN) ")","one to four numbers"}, 375 {nullptr} 376 }; 377 parser->add_group (entries, 378 "view", 379 "View options:", 380 "Options for output rendering", 381 this); 382 } 383 384 void 385 shape_options_t::add_options (option_parser_t *parser) 386 { 387 GOptionEntry entries[] = 388 { 389 {"list-shapers", 0, G_OPTION_FLAG_NO_ARG, 390 G_OPTION_ARG_CALLBACK, (gpointer) &list_shapers, "List available shapers and quit", nullptr}, 391 {"shaper", 0, G_OPTION_FLAG_HIDDEN, 392 G_OPTION_ARG_CALLBACK, (gpointer) &parse_shapers, "Hidden duplicate of --shapers", nullptr}, 393 {"shapers", 0, 0, G_OPTION_ARG_CALLBACK, (gpointer) &parse_shapers, "Set comma-separated list of shapers to try","list"}, 394 {"direction", 0, 0, G_OPTION_ARG_STRING, &this->direction, "Set text direction (default: auto)", "ltr/rtl/ttb/btt"}, 395 {"language", 0, 0, G_OPTION_ARG_STRING, &this->language, "Set text language (default: $LANG)", "langstr"}, 396 {"script", 0, 0, G_OPTION_ARG_STRING, &this->script, "Set text script (default: auto)", "ISO-15924 tag"}, 397 {"bot", 0, 0, G_OPTION_ARG_NONE, &this->bot, "Treat text as beginning-of-paragraph", nullptr}, 398 {"eot", 0, 0, G_OPTION_ARG_NONE, &this->eot, "Treat text as end-of-paragraph", nullptr}, 399 {"preserve-default-ignorables",0, 0, G_OPTION_ARG_NONE, &this->preserve_default_ignorables, "Preserve Default-Ignorable characters", nullptr}, 400 {"utf8-clusters", 0, 0, G_OPTION_ARG_NONE, &this->utf8_clusters, "Use UTF8 byte indices, not char indices", nullptr}, 401 {"cluster-level", 0, 0, G_OPTION_ARG_INT, &this->cluster_level, "Cluster merging level (default: 0)", "0/1/2"}, 402 {"normalize-glyphs",0, 0, G_OPTION_ARG_NONE, &this->normalize_glyphs, "Rearrange glyph clusters in nominal order", nullptr}, 403 {"verify", 0, 0, G_OPTION_ARG_NONE, &this->verify, "Perform sanity checks on shaping results", nullptr}, 404 {"num-iterations", 0, 0, G_OPTION_ARG_INT, &this->num_iterations, "Run shaper N times (default: 1)", "N"}, 405 {nullptr} 406 }; 407 parser->add_group (entries, 408 "shape", 409 "Shape options:", 410 "Options for the shaping process", 411 this); 412 413 const gchar *features_help = "Comma-separated list of font features\n" 414 "\n" 415 " Features can be enabled or disabled, either globally or limited to\n" 416 " specific character ranges. The format for specifying feature settings\n" 417 " follows. All valid CSS font-feature-settings values other than 'normal'\n" 418 " and 'inherited' are also accepted, though, not documented below.\n" 419 "\n" 420 " The range indices refer to the positions between Unicode characters,\n" 421 " unless the --utf8-clusters is provided, in which case range indices\n" 422 " refer to UTF-8 byte indices. The position before the first character\n" 423 " is always 0.\n" 424 "\n" 425 " The format is Python-esque. Here is how it all works:\n" 426 "\n" 427 " Syntax: Value: Start: End:\n" 428 "\n" 429 " Setting value:\n" 430 " \"kern\" 1 0 # Turn feature on\n" 431 " \"+kern\" 1 0 # Turn feature on\n" 432 " \"-kern\" 0 0 # Turn feature off\n" 433 " \"kern=0\" 0 0 # Turn feature off\n" 434 " \"kern=1\" 1 0 # Turn feature on\n" 435 " \"aalt=2\" 2 0 # Choose 2nd alternate\n" 436 "\n" 437 " Setting index:\n" 438 " \"kern[]\" 1 0 # Turn feature on\n" 439 " \"kern[:]\" 1 0 # Turn feature on\n" 440 " \"kern[5:]\" 1 5 # Turn feature on, partial\n" 441 " \"kern[:5]\" 1 0 5 # Turn feature on, partial\n" 442 " \"kern[3:5]\" 1 3 5 # Turn feature on, range\n" 443 " \"kern[3]\" 1 3 3+1 # Turn feature on, single char\n" 444 "\n" 445 " Mixing it all:\n" 446 "\n" 447 " \"aalt[3:5]=2\" 2 3 5 # Turn 2nd alternate on for range"; 448 449 GOptionEntry entries2[] = 450 { 451 {"features", 0, 0, G_OPTION_ARG_CALLBACK, (gpointer) &parse_features, features_help, "list"}, 452 {nullptr} 453 }; 454 parser->add_group (entries2, 455 "features", 456 "Features options:", 457 "Options for font features used", 458 this); 459 } 460 461 static gboolean 462 parse_font_size (const char *name G_GNUC_UNUSED, 463 const char *arg, 464 gpointer data, 465 GError **error G_GNUC_UNUSED) 466 { 467 font_options_t *font_opts = (font_options_t *) data; 468 if (0 == strcmp (arg, "upem")) 469 { 470 font_opts->font_size_y = font_opts->font_size_x = FONT_SIZE_UPEM; 471 return true; 472 } 473 switch (sscanf (arg, "%lf%*[ ,]%lf", &font_opts->font_size_x, &font_opts->font_size_y)) { 474 case 1: font_opts->font_size_y = font_opts->font_size_x; 475 case 2: return true; 476 default: 477 g_set_error (error, G_OPTION_ERROR, G_OPTION_ERROR_BAD_VALUE, 478 "%s argument should be one or two space-separated numbers", 479 name); 480 return false; 481 } 482 } 483 void 484 font_options_t::add_options (option_parser_t *parser) 485 { 486 char *text = nullptr; 487 488 { 489 static_assert ((ARRAY_LENGTH_CONST (supported_font_funcs) > 0), 490 "No supported font-funcs found."); 491 GString *s = g_string_new (nullptr); 492 g_string_printf (s, "Set font functions implementation to use (default: %s)\n\n Supported font function implementations are: %s", 493 supported_font_funcs[0].name, 494 supported_font_funcs[0].name); 495 for (unsigned int i = 1; i < ARRAY_LENGTH (supported_font_funcs); i++) 496 { 497 g_string_append_c (s, '/'); 498 g_string_append (s, supported_font_funcs[i].name); 499 } 500 text = g_string_free (s, FALSE); 501 parser->free_later (text); 502 } 503 504 char *font_size_text; 505 if (default_font_size == FONT_SIZE_UPEM) 506 font_size_text = (char *) "Font size (default: upem)"; 507 else 508 { 509 font_size_text = g_strdup_printf ("Font size (default: %d)", default_font_size); 510 parser->free_later (font_size_text); 511 } 512 513 GOptionEntry entries[] = 514 { 515 {"font-file", 0, 0, G_OPTION_ARG_STRING, &this->font_file, "Set font file-name", "filename"}, 516 {"face-index", 0, 0, G_OPTION_ARG_INT, &this->face_index, "Set face index (default: 0)", "index"}, 517 {"font-size", 0, default_font_size ? 0 : G_OPTION_FLAG_HIDDEN, 518 G_OPTION_ARG_CALLBACK, (gpointer) &parse_font_size, font_size_text, "1/2 numbers or 'upem'"}, 519 {"font-funcs", 0, 0, G_OPTION_ARG_STRING, &this->font_funcs, text, "impl"}, 520 {nullptr} 521 }; 522 parser->add_group (entries, 523 "font", 524 "Font options:", 525 "Options for the font", 526 this); 527 528 const gchar *variations_help = "Comma-separated list of font variations\n" 529 "\n" 530 " Variations are set globally. The format for specifying variation settings\n" 531 " follows. All valid CSS font-variation-settings values other than 'normal'\n" 532 " and 'inherited' are also accepted, though, not documented below.\n" 533 "\n" 534 " The format is a tag, optionally followed by an equals sign, followed by a\n" 535 " number. For example:\n" 536 "\n" 537 " \"wght=500\"\n" 538 " \"slnt=-7.5\"\n"; 539 540 GOptionEntry entries2[] = 541 { 542 {"variations", 0, 0, G_OPTION_ARG_CALLBACK, (gpointer) &parse_variations, variations_help, "list"}, 543 {nullptr} 544 }; 545 parser->add_group (entries2, 546 "variations", 547 "Varitions options:", 548 "Options for font variations used", 549 this); 550 } 551 552 void 553 text_options_t::add_options (option_parser_t *parser) 554 { 555 GOptionEntry entries[] = 556 { 557 {"text", 0, 0, G_OPTION_ARG_CALLBACK, (gpointer) &parse_text, "Set input text", "string"}, 558 {"text-file", 0, 0, G_OPTION_ARG_STRING, &this->text_file, "Set input text file-name\n\n If no text is provided, standard input is used for input.\n", "filename"}, 559 {"unicodes", 'u', 0, G_OPTION_ARG_CALLBACK, (gpointer) &parse_unicodes, "Set input Unicode codepoints", "list of hex numbers"}, 560 {"text-before", 0, 0, G_OPTION_ARG_STRING, &this->text_before, "Set text context before each line", "string"}, 561 {"text-after", 0, 0, G_OPTION_ARG_STRING, &this->text_after, "Set text context after each line", "string"}, 562 {nullptr} 563 }; 564 parser->add_group (entries, 565 "text", 566 "Text options:", 567 "Options for the input text", 568 this); 569 } 570 571 void 572 output_options_t::add_options (option_parser_t *parser) 573 { 574 const char *text; 575 576 if (nullptr == supported_formats) 577 text = "Set output serialization format"; 578 else 579 { 580 char *items = g_strjoinv ("/", const_cast<char **> (supported_formats)); 581 text = g_strdup_printf ("Set output format\n\n Supported output formats are: %s", items); 582 g_free (items); 583 parser->free_later ((char *) text); 584 } 585 586 GOptionEntry entries[] = 587 { 588 {"output-file", 'o', 0, G_OPTION_ARG_STRING, &this->output_file, "Set output file-name (default: stdout)","filename"}, 589 {"output-format", 'O', 0, G_OPTION_ARG_STRING, &this->output_format, text, "format"}, 590 {nullptr} 591 }; 592 parser->add_group (entries, 593 "output", 594 "Output destination & format options:", 595 "Options for the destination & form of the output", 596 this); 597 } 598 599 600 601 hb_font_t * 602 font_options_t::get_font (void) const 603 { 604 if (font) 605 return font; 606 607 hb_blob_t *blob = nullptr; 608 609 /* Create the blob */ 610 { 611 char *font_data; 612 unsigned int len = 0; 613 hb_destroy_func_t destroy; 614 void *user_data; 615 hb_memory_mode_t mm; 616 617 /* This is a hell of a lot of code for just reading a file! */ 618 if (!font_file) 619 fail (true, "No font file set"); 620 621 if (0 == strcmp (font_file, "-")) { 622 /* read it */ 623 GString *gs = g_string_new (nullptr); 624 char buf[BUFSIZ]; 625 #if defined(_WIN32) || defined(__CYGWIN__) 626 setmode (fileno (stdin), O_BINARY); 627 #endif 628 while (!feof (stdin)) { 629 size_t ret = fread (buf, 1, sizeof (buf), stdin); 630 if (ferror (stdin)) 631 fail (false, "Failed reading font from standard input: %s", 632 strerror (errno)); 633 g_string_append_len (gs, buf, ret); 634 } 635 len = gs->len; 636 font_data = g_string_free (gs, false); 637 user_data = font_data; 638 destroy = (hb_destroy_func_t) g_free; 639 mm = HB_MEMORY_MODE_WRITABLE; 640 } else { 641 GError *error = nullptr; 642 GMappedFile *mf = g_mapped_file_new (font_file, false, &error); 643 if (mf) { 644 font_data = g_mapped_file_get_contents (mf); 645 len = g_mapped_file_get_length (mf); 646 if (len) { 647 destroy = (hb_destroy_func_t) g_mapped_file_unref; 648 user_data = (void *) mf; 649 mm = HB_MEMORY_MODE_READONLY_MAY_MAKE_WRITABLE; 650 } else 651 g_mapped_file_unref (mf); 652 } else { 653 fail (false, "%s", error->message); 654 //g_error_free (error); 655 } 656 if (!len) { 657 /* GMappedFile is buggy, it doesn't fail if file isn't regular. 658 * Try reading. 659 * https://bugzilla.gnome.org/show_bug.cgi?id=659212 */ 660 GError *error = nullptr; 661 gsize l; 662 if (g_file_get_contents (font_file, &font_data, &l, &error)) { 663 len = l; 664 destroy = (hb_destroy_func_t) g_free; 665 user_data = (void *) font_data; 666 mm = HB_MEMORY_MODE_WRITABLE; 667 } else { 668 fail (false, "%s", error->message); 669 //g_error_free (error); 670 } 671 } 672 } 673 674 if (debug) 675 mm = HB_MEMORY_MODE_DUPLICATE; 676 677 blob = hb_blob_create (font_data, len, mm, user_data, destroy); 678 } 679 680 /* Create the face */ 681 hb_face_t *face = hb_face_create (blob, face_index); 682 hb_blob_destroy (blob); 683 684 685 font = hb_font_create (face); 686 687 if (font_size_x == FONT_SIZE_UPEM) 688 font_size_x = hb_face_get_upem (face); 689 if (font_size_y == FONT_SIZE_UPEM) 690 font_size_y = hb_face_get_upem (face); 691 692 int scale_x = (int) scalbnf (font_size_x, subpixel_bits); 693 int scale_y = (int) scalbnf (font_size_y, subpixel_bits); 694 hb_font_set_scale (font, scale_x, scale_y); 695 hb_face_destroy (face); 696 697 hb_font_set_variations (font, variations, num_variations); 698 699 void (*set_font_funcs) (hb_font_t *) = nullptr; 700 if (!font_funcs) 701 { 702 set_font_funcs = supported_font_funcs[0].func; 703 } 704 else 705 { 706 for (unsigned int i = 0; i < ARRAY_LENGTH (supported_font_funcs); i++) 707 if (0 == g_ascii_strcasecmp (font_funcs, supported_font_funcs[i].name)) 708 { 709 set_font_funcs = supported_font_funcs[i].func; 710 break; 711 } 712 if (!set_font_funcs) 713 { 714 GString *s = g_string_new (nullptr); 715 for (unsigned int i = 0; i < ARRAY_LENGTH (supported_font_funcs); i++) 716 { 717 if (i) 718 g_string_append_c (s, '/'); 719 g_string_append (s, supported_font_funcs[i].name); 720 } 721 char *p = g_string_free (s, FALSE); 722 fail (false, "Unknown font function implementation `%s'; supported values are: %s; default is %s", 723 font_funcs, 724 p, 725 supported_font_funcs[0].name); 726 //free (p); 727 } 728 } 729 set_font_funcs (font); 730 731 return font; 732 } 733 734 735 const char * 736 text_options_t::get_line (unsigned int *len) 737 { 738 if (text) { 739 if (!line) line = text; 740 if (line_len == (unsigned int) -1) 741 line_len = strlen (line); 742 743 if (!line_len) { 744 *len = 0; 745 return nullptr; 746 } 747 748 const char *ret = line; 749 const char *p = (const char *) memchr (line, '\n', line_len); 750 unsigned int ret_len; 751 if (!p) { 752 ret_len = line_len; 753 line += ret_len; 754 line_len = 0; 755 } else { 756 ret_len = p - ret; 757 line += ret_len + 1; 758 line_len -= ret_len + 1; 759 } 760 761 *len = ret_len; 762 return ret; 763 } 764 765 if (!fp) { 766 if (!text_file) 767 fail (true, "At least one of text or text-file must be set"); 768 769 if (0 != strcmp (text_file, "-")) 770 fp = fopen (text_file, "r"); 771 else 772 fp = stdin; 773 774 if (!fp) 775 fail (false, "Failed opening text file `%s': %s", 776 text_file, strerror (errno)); 777 778 gs = g_string_new (nullptr); 779 } 780 781 g_string_set_size (gs, 0); 782 char buf[BUFSIZ]; 783 while (fgets (buf, sizeof (buf), fp)) { 784 unsigned int bytes = strlen (buf); 785 if (bytes && buf[bytes - 1] == '\n') { 786 bytes--; 787 g_string_append_len (gs, buf, bytes); 788 break; 789 } 790 g_string_append_len (gs, buf, bytes); 791 } 792 if (ferror (fp)) 793 fail (false, "Failed reading text: %s", 794 strerror (errno)); 795 *len = gs->len; 796 return !*len && feof (fp) ? nullptr : gs->str; 797 } 798 799 800 FILE * 801 output_options_t::get_file_handle (void) 802 { 803 if (fp) 804 return fp; 805 806 if (output_file) 807 fp = fopen (output_file, "wb"); 808 else { 809 #if defined(_WIN32) || defined(__CYGWIN__) 810 setmode (fileno (stdout), O_BINARY); 811 #endif 812 fp = stdout; 813 } 814 if (!fp) 815 fail (false, "Cannot open output file `%s': %s", 816 g_filename_display_name (output_file), strerror (errno)); 817 818 return fp; 819 } 820 821 static gboolean 822 parse_verbose (const char *name G_GNUC_UNUSED, 823 const char *arg G_GNUC_UNUSED, 824 gpointer data G_GNUC_UNUSED, 825 GError **error G_GNUC_UNUSED) 826 { 827 format_options_t *format_opts = (format_options_t *) data; 828 format_opts->show_text = format_opts->show_unicode = format_opts->show_line_num = true; 829 return true; 830 } 831 832 void 833 format_options_t::add_options (option_parser_t *parser) 834 { 835 GOptionEntry entries[] = 836 { 837 {"show-text", 0, 0, G_OPTION_ARG_NONE, &this->show_text, "Prefix each line of output with its corresponding input text", nullptr}, 838 {"show-unicode", 0, 0, G_OPTION_ARG_NONE, &this->show_unicode, "Prefix each line of output with its corresponding input codepoint(s)", nullptr}, 839 {"show-line-num", 0, 0, G_OPTION_ARG_NONE, &this->show_line_num, "Prefix each line of output with its corresponding input line number", nullptr}, 840 {"verbose", 'v', G_OPTION_FLAG_NO_ARG, 841 G_OPTION_ARG_CALLBACK, (gpointer) &parse_verbose, "Prefix each line of output with all of the above", nullptr}, 842 {"no-glyph-names", 0, G_OPTION_FLAG_REVERSE, 843 G_OPTION_ARG_NONE, &this->show_glyph_names, "Output glyph indices instead of names", nullptr}, 844 {"no-positions", 0, G_OPTION_FLAG_REVERSE, 845 G_OPTION_ARG_NONE, &this->show_positions, "Do not output glyph positions", nullptr}, 846 {"no-clusters", 0, G_OPTION_FLAG_REVERSE, 847 G_OPTION_ARG_NONE, &this->show_clusters, "Do not output cluster indices", nullptr}, 848 {"show-extents", 0, 0, G_OPTION_ARG_NONE, &this->show_extents, "Output glyph extents", nullptr}, 849 {"show-flags", 0, 0, G_OPTION_ARG_NONE, &this->show_flags, "Output glyph flags", nullptr}, 850 {"trace", 'V', 0, G_OPTION_ARG_NONE, &this->trace, "Output interim shaping results", nullptr}, 851 {nullptr} 852 }; 853 parser->add_group (entries, 854 "output-syntax", 855 "Output syntax:\n" 856 " text: [<glyph name or index>=<glyph cluster index within input>@<horizontal displacement>,<vertical displacement>+<horizontal advance>,<vertical advance>|...]\n" 857 " json: [{\"g\": <glyph name or index>, \"ax\": <horizontal advance>, \"ay\": <vertical advance>, \"dx\": <horizontal displacement>, \"dy\": <vertical displacement>, \"cl\": <glyph cluster index within input>}, ...]\n" 858 "\nOutput syntax options:", 859 "Options for the syntax of the output", 860 this); 861 } 862 863 void 864 format_options_t::serialize_unicode (hb_buffer_t *buffer, 865 GString *gs) 866 { 867 unsigned int num_glyphs = hb_buffer_get_length (buffer); 868 hb_glyph_info_t *info = hb_buffer_get_glyph_infos (buffer, nullptr); 869 870 g_string_append_c (gs, '<'); 871 for (unsigned int i = 0; i < num_glyphs; i++) 872 { 873 if (i) 874 g_string_append_c (gs, ','); 875 g_string_append_printf (gs, "U+%04X", info->codepoint); 876 info++; 877 } 878 g_string_append_c (gs, '>'); 879 } 880 881 void 882 format_options_t::serialize_glyphs (hb_buffer_t *buffer, 883 hb_font_t *font, 884 hb_buffer_serialize_format_t output_format, 885 hb_buffer_serialize_flags_t flags, 886 GString *gs) 887 { 888 g_string_append_c (gs, '['); 889 unsigned int num_glyphs = hb_buffer_get_length (buffer); 890 unsigned int start = 0; 891 892 while (start < num_glyphs) 893 { 894 char buf[1024]; 895 unsigned int consumed; 896 start += hb_buffer_serialize_glyphs (buffer, start, num_glyphs, 897 buf, sizeof (buf), &consumed, 898 font, output_format, flags); 899 if (!consumed) 900 break; 901 g_string_append (gs, buf); 902 } 903 g_string_append_c (gs, ']'); 904 } 905 void 906 format_options_t::serialize_line_no (unsigned int line_no, 907 GString *gs) 908 { 909 if (show_line_num) 910 g_string_append_printf (gs, "%d: ", line_no); 911 } 912 void 913 format_options_t::serialize_buffer_of_text (hb_buffer_t *buffer, 914 unsigned int line_no, 915 const char *text, 916 unsigned int text_len, 917 hb_font_t *font, 918 GString *gs) 919 { 920 if (show_text) 921 { 922 serialize_line_no (line_no, gs); 923 g_string_append_c (gs, '('); 924 g_string_append_len (gs, text, text_len); 925 g_string_append_c (gs, ')'); 926 g_string_append_c (gs, '\n'); 927 } 928 929 if (show_unicode) 930 { 931 serialize_line_no (line_no, gs); 932 serialize_unicode (buffer, gs); 933 g_string_append_c (gs, '\n'); 934 } 935 } 936 void 937 format_options_t::serialize_message (unsigned int line_no, 938 const char *type, 939 const char *msg, 940 GString *gs) 941 { 942 serialize_line_no (line_no, gs); 943 g_string_append_printf (gs, "%s: %s", type, msg); 944 g_string_append_c (gs, '\n'); 945 } 946 void 947 format_options_t::serialize_buffer_of_glyphs (hb_buffer_t *buffer, 948 unsigned int line_no, 949 const char *text, 950 unsigned int text_len, 951 hb_font_t *font, 952 hb_buffer_serialize_format_t output_format, 953 hb_buffer_serialize_flags_t format_flags, 954 GString *gs) 955 { 956 serialize_line_no (line_no, gs); 957 serialize_glyphs (buffer, font, output_format, format_flags, gs); 958 g_string_append_c (gs, '\n'); 959 } 960