blob: d73666844b4779016287c437b19dae34bf0cd927 [file] [edit]
/*
* 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);
}