| /* |
| * Copyright (C) 2026 Behdad Esfahbod |
| * |
| * This is part of HarfBuzz, a text shaping library. |
| * |
| * Permission is hereby granted, without written agreement and without |
| * license or royalty fees, to use, copy, modify, and distribute this |
| * software and its documentation for any purpose, provided that the |
| * above copyright notice and the following two paragraphs appear in |
| * all copies of this software. |
| * |
| * IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE TO ANY PARTY FOR |
| * DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES |
| * ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN |
| * IF THE COPYRIGHT HOLDER HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH |
| * DAMAGE. |
| * |
| * THE COPYRIGHT HOLDER SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING, |
| * BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND |
| * FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS |
| * ON AN "AS IS" BASIS, AND THE COPYRIGHT HOLDER HAS NO OBLIGATION TO |
| * PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS. |
| * |
| * Author(s): Behdad Esfahbod |
| */ |
| |
| #include "font-options.hh" |
| #include "main-font-text.hh" |
| #include "shape-consumer.hh" |
| #include "text-options.hh" |
| #include "gpu-output.hh" |
| |
| #include <hb-ot.h> |
| |
| const unsigned DEFAULT_FONT_SIZE = FONT_SIZE_UPEM; |
| const unsigned SUBPIXEL_BITS = 0; |
| |
| #include "gpu/default-text-en.hh" |
| #include "gpu/default-text-fa.hh" |
| |
| struct demo_script_text_t { |
| hb_script_t script; |
| const char *text; |
| }; |
| |
| static const demo_script_text_t demo_texts[] = { |
| { HB_SCRIPT_LATIN, default_text_en }, |
| { HB_SCRIPT_ARABIC, default_text_fa }, |
| }; |
| |
| static void |
| append_lines (GString *s, const char *text, unsigned max_lines) |
| { |
| const char *p = text; |
| unsigned lines = 0; |
| while (*p && lines < max_lines) |
| { |
| const char *end = strchr (p, '\n'); |
| if (!end) |
| end = p + strlen (p); |
| if (s->len) |
| g_string_append_c (s, '\n'); |
| g_string_append_len (s, p, end - p); |
| lines++; |
| p = *end ? end + 1 : end; |
| } |
| } |
| |
| #define MAX_TOTAL_LINES 60 |
| |
| static bool |
| font_has_script (hb_face_t *face, hb_script_t script) |
| { |
| hb_tag_t script_tags[2]; |
| unsigned num_tags = 2; |
| hb_ot_tags_from_script_and_language (script, HB_LANGUAGE_INVALID, |
| &num_tags, script_tags, nullptr, nullptr); |
| for (unsigned t = 0; t < num_tags; t++) |
| { |
| unsigned script_index; |
| if (hb_ot_layout_table_find_script (face, HB_OT_TAG_GSUB, |
| script_tags[t], |
| &script_index)) |
| return true; |
| } |
| return false; |
| } |
| |
| static char * |
| build_default_text (hb_face_t *face) |
| { |
| /* Count how many scripts the font supports. */ |
| unsigned num_supported = 0; |
| for (unsigned i = 0; i < G_N_ELEMENTS (demo_texts); i++) |
| if (font_has_script (face, demo_texts[i].script)) |
| num_supported++; |
| |
| if (!num_supported) |
| num_supported = 1; /* fallback */ |
| |
| unsigned lines_per = MAX_TOTAL_LINES / num_supported; |
| |
| GString *s = g_string_new (nullptr); |
| |
| for (unsigned i = 0; i < G_N_ELEMENTS (demo_texts); i++) |
| { |
| if (!font_has_script (face, demo_texts[i].script)) |
| continue; |
| append_lines (s, demo_texts[i].text, lines_per); |
| } |
| |
| /* Fallback to English if nothing matched. */ |
| if (!s->len) |
| append_lines (s, default_text_en, MAX_TOTAL_LINES); |
| |
| return g_string_free (s, FALSE); |
| } |
| |
| static const char *gpu_default_text_sentinel = ""; |
| static hb_bool_t *gpu_demo_flag = nullptr; |
| |
| struct gpu_text_options_t : shape_text_options_t |
| { |
| const char *default_text () override |
| { |
| return (gpu_demo_flag && *gpu_demo_flag) ? gpu_default_text_sentinel : nullptr; |
| } |
| }; |
| |
| struct gpu_font_options_t : font_options_t |
| { |
| hb_face_t *default_face () override |
| { |
| if (!gpu_demo_flag || !*gpu_demo_flag) |
| return nullptr; |
| #include "gpu/default-font.hh" |
| hb_blob_t *blob = hb_blob_create ((const char *) default_font, |
| sizeof (default_font), |
| HB_MEMORY_MODE_READONLY, |
| nullptr, nullptr); |
| hb_face_t *face_ = hb_face_create (blob, 0); |
| hb_blob_destroy (blob); |
| return face_; |
| } |
| }; |
| |
| using base_t = main_font_text_t<shape_consumer_t<gpu_output_t>, |
| gpu_font_options_t, |
| gpu_text_options_t>; |
| |
| struct gpu_main_t : base_t |
| { |
| int operator () (int argc, char **argv) |
| { |
| gpu_demo_flag = &this->output.demo; |
| |
| add_options (); |
| add_exit_code (RETURN_VALUE_OPERATION_FAILED, "Operation failed."); |
| |
| parse (&argc, &argv); |
| |
| /* If no text was provided, build from font's supported scripts. */ |
| if (!text || !*text) |
| { |
| g_free (text); |
| text = build_default_text (face); |
| } |
| |
| this->init (this); |
| |
| while (this->consume_line (*this)) |
| ; |
| |
| this->finish (this); |
| |
| if (this->failed && return_value == RETURN_VALUE_SUCCESS) |
| return_value = RETURN_VALUE_OPERATION_FAILED; |
| |
| return return_value; |
| } |
| }; |
| |
| int |
| main (int argc, char **argv) |
| { |
| gpu_main_t driver; |
| return driver (argc, argv); |
| } |