summaryrefslogtreecommitdiff
path: root/src/haiku_font_support.cc
diff options
context:
space:
mode:
Diffstat (limited to 'src/haiku_font_support.cc')
-rw-r--r--src/haiku_font_support.cc596
1 files changed, 596 insertions, 0 deletions
diff --git a/src/haiku_font_support.cc b/src/haiku_font_support.cc
new file mode 100644
index 00000000000..9ac0400969b
--- /dev/null
+++ b/src/haiku_font_support.cc
@@ -0,0 +1,596 @@
+/* Haiku window system support. Hey, Emacs, this is -*- C++ -*-
+ Copyright (C) 2021 Free Software Foundation, Inc.
+
+This file is part of GNU Emacs.
+
+GNU Emacs is free software: you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation, either version 3 of the License, or (at
+your option) any later version.
+
+GNU Emacs is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with GNU Emacs. If not, see <https://www.gnu.org/licenses/>. */
+
+#include <config.h>
+
+#include <Font.h>
+#include <Rect.h>
+#include <AffineTransform.h>
+
+#include <cstring>
+#include <cmath>
+
+#include "haiku_support.h"
+
+/* Haiku doesn't expose font language data in BFont objects. Thus, we
+ select a few representative characters for each supported `:lang'
+ (currently Chinese, Korean and Japanese,) and test for those
+ instead. */
+
+static uint32_t language_code_points[MAX_LANGUAGE][4] =
+ {{20154, 20754, 22996, 0}, /* Chinese. */
+ {51312, 49440, 44544, 0}, /* Korean. */
+ {26085, 26412, 12371, 0}, /* Japanese. */};
+
+static void
+estimate_font_ascii (BFont *font, int *max_width,
+ int *min_width, int *avg_width)
+{
+ char ch[2];
+ bool tems[1];
+ int total = 0;
+ int count = 0;
+ int min = 0;
+ int max = 0;
+
+ std::memset (ch, 0, sizeof ch);
+ for (ch[0] = 32; ch[0] < 127; ++ch[0])
+ {
+ tems[0] = false;
+ font->GetHasGlyphs (ch, 1, tems);
+ if (tems[0])
+ {
+ int w = font->StringWidth (ch);
+ ++count;
+ total += w;
+
+ if (!min || min > w)
+ min = w;
+ if (max < w)
+ max = w;
+ }
+ }
+
+ *min_width = min;
+ *max_width = max;
+ *avg_width = total / count;
+}
+
+void
+BFont_close (void *font)
+{
+ if (font != (void *) be_fixed_font &&
+ font != (void *) be_plain_font &&
+ font != (void *) be_bold_font)
+ delete (BFont *) font;
+}
+
+void
+BFont_dat (void *font, int *px_size, int *min_width, int *max_width,
+ int *avg_width, int *height, int *space_width, int *ascent,
+ int *descent, int *underline_position, int *underline_thickness)
+{
+ BFont *ft = (BFont *) font;
+ struct font_height fheight;
+ bool have_space_p;
+
+ char atem[1];
+ bool otem[1];
+
+ ft->GetHeight (&fheight);
+ atem[0] = ' ';
+ otem[0] = false;
+ ft->GetHasGlyphs (atem, 1, otem);
+ have_space_p = otem[0];
+
+ estimate_font_ascii (ft, max_width, min_width, avg_width);
+ *ascent = std::lrint (fheight.ascent);
+ *descent = std::lrint (fheight.descent);
+ *height = *ascent + *descent;
+
+ *space_width = have_space_p ? ft->StringWidth (" ") : 0;
+
+ *px_size = std::lrint (ft->Size ());
+ *underline_position = 0;
+ *underline_thickness = 0;
+}
+
+/* Return non-null if FONT contains CHR, a Unicode code-point. */
+int
+BFont_have_char_p (void *font, int32_t chr)
+{
+ BFont *ft = (BFont *) font;
+ return ft->IncludesBlock (chr, chr);
+}
+
+/* Return non-null if font contains a block from BEG to END. */
+int
+BFont_have_char_block (void *font, int32_t beg, int32_t end)
+{
+ BFont *ft = (BFont *) font;
+ return ft->IncludesBlock (beg, end);
+}
+
+/* Compute bounds for MB_STR, a character in multibyte encoding,
+ used with font. The width (in pixels) is returned in ADVANCE,
+ the left bearing in LB, and the right bearing in RB. */
+void
+BFont_char_bounds (void *font, const char *mb_str, int *advance,
+ int *lb, int *rb)
+{
+ BFont *ft = (BFont *) font;
+ edge_info edge_info;
+ float size, escapement;
+ size = ft->Size ();
+
+ ft->GetEdges (mb_str, 1, &edge_info);
+ ft->GetEscapements (mb_str, 1, &escapement);
+ *advance = std::lrint (escapement * size);
+ *lb = std::lrint (edge_info.left * size);
+ *rb = *advance + std::lrint (edge_info.right * size);
+}
+
+/* The same, but for a variable amount of chars. */
+void
+BFont_nchar_bounds (void *font, const char *mb_str, int *advance,
+ int *lb, int *rb, int32_t n)
+{
+ BFont *ft = (BFont *) font;
+ edge_info edge_info[n];
+ float size;
+ float escapement[n];
+
+ size = ft->Size ();
+
+ ft->GetEdges (mb_str, n, edge_info);
+ ft->GetEscapements (mb_str, n, (float *) escapement);
+
+ for (int32_t i = 0; i < n; ++i)
+ {
+ advance[i] = std::lrint (escapement[i] * size);
+ lb[i] = advance[i] - std::lrint (edge_info[i].left * size);
+ rb[i] = advance[i] + std::lrint (edge_info[i].right * size);
+ }
+}
+
+static void
+font_style_to_flags (char *st, struct haiku_font_pattern *pattern)
+{
+ char *style = strdup (st);
+ char *token;
+ pattern->weight = -1;
+ pattern->width = NO_WIDTH;
+ pattern->slant = NO_SLANT;
+ int tok = 0;
+
+ while ((token = std::strtok (!tok ? style : NULL, " ")) && tok < 3)
+ {
+ if (token && !strcmp (token, "Thin"))
+ pattern->weight = HAIKU_THIN;
+ else if (token && !strcmp (token, "UltraLight"))
+ pattern->weight = HAIKU_ULTRALIGHT;
+ else if (token && !strcmp (token, "ExtraLight"))
+ pattern->weight = HAIKU_EXTRALIGHT;
+ else if (token && !strcmp (token, "Light"))
+ pattern->weight = HAIKU_LIGHT;
+ else if (token && !strcmp (token, "SemiLight"))
+ pattern->weight = HAIKU_SEMI_LIGHT;
+ else if (token && !strcmp (token, "Regular"))
+ {
+ if (pattern->slant == NO_SLANT)
+ pattern->slant = SLANT_REGULAR;
+
+ if (pattern->width == NO_WIDTH)
+ pattern->width = NORMAL_WIDTH;
+
+ if (pattern->weight == -1)
+ pattern->weight = HAIKU_REGULAR;
+ }
+ else if (token && !strcmp (token, "SemiBold"))
+ pattern->weight = HAIKU_SEMI_BOLD;
+ else if (token && !strcmp (token, "Bold"))
+ pattern->weight = HAIKU_BOLD;
+ else if (token && (!strcmp (token, "ExtraBold") ||
+ /* This has actually been seen in the wild. */
+ !strcmp (token, "Extrabold")))
+ pattern->weight = HAIKU_EXTRA_BOLD;
+ else if (token && !strcmp (token, "UltraBold"))
+ pattern->weight = HAIKU_ULTRA_BOLD;
+ else if (token && !strcmp (token, "Book"))
+ pattern->weight = HAIKU_BOOK;
+ else if (token && !strcmp (token, "Heavy"))
+ pattern->weight = HAIKU_HEAVY;
+ else if (token && !strcmp (token, "UltraHeavy"))
+ pattern->weight = HAIKU_ULTRA_HEAVY;
+ else if (token && !strcmp (token, "Black"))
+ pattern->weight = HAIKU_BLACK;
+ else if (token && !strcmp (token, "Medium"))
+ pattern->weight = HAIKU_MEDIUM;
+ else if (token && !strcmp (token, "Oblique"))
+ pattern->slant = SLANT_OBLIQUE;
+ else if (token && !strcmp (token, "Italic"))
+ pattern->slant = SLANT_ITALIC;
+ else if (token && !strcmp (token, "UltraCondensed"))
+ pattern->width = ULTRA_CONDENSED;
+ else if (token && !strcmp (token, "ExtraCondensed"))
+ pattern->width = EXTRA_CONDENSED;
+ else if (token && !strcmp (token, "Condensed"))
+ pattern->width = CONDENSED;
+ else if (token && !strcmp (token, "SemiCondensed"))
+ pattern->width = SEMI_CONDENSED;
+ else if (token && !strcmp (token, "SemiExpanded"))
+ pattern->width = SEMI_EXPANDED;
+ else if (token && !strcmp (token, "Expanded"))
+ pattern->width = EXPANDED;
+ else if (token && !strcmp (token, "ExtraExpanded"))
+ pattern->width = EXTRA_EXPANDED;
+ else if (token && !strcmp (token, "UltraExpanded"))
+ pattern->width = ULTRA_EXPANDED;
+ else
+ {
+ tok = 1000;
+ break;
+ }
+ tok++;
+ }
+
+ if (pattern->weight != -1)
+ pattern->specified |= FSPEC_WEIGHT;
+ if (pattern->slant != NO_SLANT)
+ pattern->specified |= FSPEC_SLANT;
+ if (pattern->width != NO_WIDTH)
+ pattern->specified |= FSPEC_WIDTH;
+
+ if (tok > 3)
+ {
+ pattern->specified &= ~FSPEC_SLANT;
+ pattern->specified &= ~FSPEC_WEIGHT;
+ pattern->specified &= ~FSPEC_WIDTH;
+ pattern->specified |= FSPEC_STYLE;
+ std::strncpy ((char *) &pattern->style, st,
+ sizeof pattern->style - 1);
+ }
+
+ free (style);
+}
+
+static bool
+font_check_wanted_chars (struct haiku_font_pattern *pattern, font_family family,
+ char *style)
+{
+ BFont ft;
+
+ if (ft.SetFamilyAndStyle (family, style) != B_OK)
+ return false;
+
+ for (int i = 0; i < pattern->want_chars_len; ++i)
+ if (!ft.IncludesBlock (pattern->wanted_chars[i],
+ pattern->wanted_chars[i]))
+ return false;
+
+ return true;
+}
+
+static bool
+font_check_one_of (struct haiku_font_pattern *pattern, font_family family,
+ char *style)
+{
+ BFont ft;
+
+ if (ft.SetFamilyAndStyle (family, style) != B_OK)
+ return false;
+
+ for (int i = 0; i < pattern->need_one_of_len; ++i)
+ if (ft.IncludesBlock (pattern->need_one_of[i],
+ pattern->need_one_of[i]))
+ return true;
+
+ return false;
+}
+
+static bool
+font_check_language (struct haiku_font_pattern *pattern, font_family family,
+ char *style)
+{
+ BFont ft;
+
+ if (ft.SetFamilyAndStyle (family, style) != B_OK)
+ return false;
+
+ if (pattern->language == MAX_LANGUAGE)
+ return false;
+
+ for (uint32_t *ch = (uint32_t *)
+ &language_code_points[pattern->language]; *ch; ch++)
+ if (!ft.IncludesBlock (*ch, *ch))
+ return false;
+
+ return true;
+}
+
+static bool
+font_family_style_matches_p (font_family family, char *style, uint32_t flags,
+ struct haiku_font_pattern *pattern,
+ int ignore_flags_p = 0)
+{
+ struct haiku_font_pattern m;
+ m.specified = 0;
+
+ if (style)
+ font_style_to_flags (style, &m);
+
+ if ((pattern->specified & FSPEC_FAMILY) &&
+ strcmp ((char *) &pattern->family, family))
+ return false;
+
+ if (!ignore_flags_p && (pattern->specified & FSPEC_SPACING) &&
+ !(pattern->mono_spacing_p) != !(flags & B_IS_FIXED))
+ return false;
+
+ if (pattern->specified & FSPEC_STYLE)
+ return style && !strcmp (style, pattern->style);
+
+ if ((pattern->specified & FSPEC_WEIGHT)
+ && (pattern->weight
+ != ((m.specified & FSPEC_WEIGHT) ? m.weight : HAIKU_REGULAR)))
+ return false;
+
+ if ((pattern->specified & FSPEC_SLANT)
+ && (pattern->slant
+ != ((m.specified & FSPEC_SLANT) ? m.slant : SLANT_REGULAR)))
+ return false;
+
+ if ((pattern->specified & FSPEC_WANTED)
+ && !font_check_wanted_chars (pattern, family, style))
+ return false;
+
+ if ((pattern->specified & FSPEC_WIDTH)
+ && (pattern->width !=
+ ((m.specified & FSPEC_WIDTH) ? m.width : NORMAL_WIDTH)))
+ return false;
+
+ if ((pattern->specified & FSPEC_NEED_ONE_OF)
+ && !font_check_one_of (pattern, family, style))
+ return false;
+
+ if ((pattern->specified & FSPEC_LANGUAGE)
+ && !font_check_language (pattern, family, style))
+ return false;
+
+ return true;
+}
+
+static void
+haiku_font_fill_pattern (struct haiku_font_pattern *pattern,
+ font_family family, char *style,
+ uint32_t flags)
+{
+ if (style)
+ font_style_to_flags (style, pattern);
+
+ pattern->specified |= FSPEC_FAMILY;
+ std::strncpy (pattern->family, family,
+ sizeof pattern->family - 1);
+ pattern->specified |= FSPEC_SPACING;
+ pattern->mono_spacing_p = flags & B_IS_FIXED;
+}
+
+/* Delete every element of the font pattern PT. */
+void
+haiku_font_pattern_free (struct haiku_font_pattern *pt)
+{
+ struct haiku_font_pattern *tem = pt;
+ while (tem)
+ {
+ struct haiku_font_pattern *t = tem;
+ tem = t->next;
+ delete t;
+ }
+}
+
+/* Find all fonts matching the font pattern PT. */
+struct haiku_font_pattern *
+BFont_find (struct haiku_font_pattern *pt)
+{
+ struct haiku_font_pattern *r = NULL;
+ font_family name;
+ font_style sname;
+ uint32 flags;
+ int sty_count;
+ int fam_count = count_font_families ();
+
+ for (int fi = 0; fi < fam_count; ++fi)
+ {
+ if (get_font_family (fi, &name, &flags) == B_OK)
+ {
+ sty_count = count_font_styles (name);
+ if (!sty_count &&
+ font_family_style_matches_p (name, NULL, flags, pt))
+ {
+ struct haiku_font_pattern *p = new struct haiku_font_pattern;
+ p->specified = 0;
+ p->oblique_seen_p = 1;
+ haiku_font_fill_pattern (p, name, NULL, flags);
+ p->next = r;
+ if (p->next)
+ p->next->last = p;
+ p->last = NULL;
+ p->next_family = r;
+ r = p;
+ }
+ else if (sty_count)
+ {
+ for (int si = 0; si < sty_count; ++si)
+ {
+ int oblique_seen_p = 0;
+ struct haiku_font_pattern *head = r;
+ struct haiku_font_pattern *p = NULL;
+
+ if (get_font_style (name, si, &sname, &flags) == B_OK)
+ {
+ if (font_family_style_matches_p (name, (char *) &sname, flags, pt))
+ {
+ p = new struct haiku_font_pattern;
+ p->specified = 0;
+ haiku_font_fill_pattern (p, name, (char *) &sname, flags);
+ if (p->specified & FSPEC_SLANT &&
+ ((p->slant == SLANT_OBLIQUE) || (p->slant == SLANT_ITALIC)))
+ oblique_seen_p = 1;
+
+ p->next = r;
+ if (p->next)
+ p->next->last = p;
+ r = p;
+ p->next_family = head;
+ }
+ }
+
+ if (p)
+ p->last = NULL;
+
+ for (; head; head = head->last)
+ {
+ head->oblique_seen_p = oblique_seen_p;
+ }
+ }
+ }
+ }
+ }
+
+ /* There's a very good chance that this result will get cached if no
+ slant is specified. Thus, we look through each font that hasn't
+ seen an oblique style, and add one. */
+
+ if (!(pt->specified & FSPEC_SLANT))
+ {
+ /* r->last is invalid from here onwards. */
+ for (struct haiku_font_pattern *p = r; p;)
+ {
+ if (!p->oblique_seen_p)
+ {
+ struct haiku_font_pattern *n = new haiku_font_pattern;
+ *n = *p;
+ n->slant = SLANT_OBLIQUE;
+ p->next = n;
+ p = p->next_family;
+ }
+ else
+ p = p->next_family;
+ }
+ }
+
+ return r;
+}
+
+/* Find and open a font matching the pattern PAT, which must have its
+ family set. */
+int
+BFont_open_pattern (struct haiku_font_pattern *pat, void **font, float size)
+{
+ int sty_count;
+ font_family name;
+ font_style sname;
+ uint32 flags = 0;
+ if (!(pat->specified & FSPEC_FAMILY))
+ return 1;
+ strncpy (name, pat->family, sizeof name - 1);
+ sty_count = count_font_styles (name);
+
+ if (!sty_count &&
+ font_family_style_matches_p (name, NULL, flags, pat, 1))
+ {
+ BFont *ft = new BFont;
+ if (ft->SetFamilyAndStyle (name, NULL) != B_OK)
+ {
+ delete ft;
+ return 1;
+ }
+ ft->SetSize (size);
+ ft->SetEncoding (B_UNICODE_UTF8);
+ ft->SetSpacing (B_BITMAP_SPACING);
+ *font = (void *) ft;
+ return 0;
+ }
+ else if (sty_count)
+ {
+ for (int si = 0; si < sty_count; ++si)
+ {
+ if (get_font_style (name, si, &sname, &flags) == B_OK &&
+ font_family_style_matches_p (name, (char *) &sname, flags, pat))
+ {
+ BFont *ft = new BFont;
+ if (ft->SetFamilyAndStyle (name, sname) != B_OK)
+ {
+ delete ft;
+ return 1;
+ }
+ ft->SetSize (size);
+ ft->SetEncoding (B_UNICODE_UTF8);
+ ft->SetSpacing (B_BITMAP_SPACING);
+ *font = (void *) ft;
+ return 0;
+ }
+ }
+ }
+
+ if (pat->specified & FSPEC_SLANT && pat->slant == SLANT_OBLIQUE)
+ {
+ struct haiku_font_pattern copy = *pat;
+ copy.slant = SLANT_REGULAR;
+ int code = BFont_open_pattern (&copy, font, size);
+ if (code)
+ return code;
+ BFont *ft = (BFont *) *font;
+ /* XXX Font measurements don't respect shear. Haiku bug?
+ This apparently worked in BeOS.
+ ft->SetShear (100.0); */
+ ft->SetFace (B_ITALIC_FACE);
+ return 0;
+ }
+
+ return 1;
+}
+
+/* Query the family of the default fixed font. */
+void
+BFont_populate_fixed_family (struct haiku_font_pattern *ptn)
+{
+ font_family f;
+ font_style s;
+ be_fixed_font->GetFamilyAndStyle (&f, &s);
+
+ ptn->specified |= FSPEC_FAMILY;
+ strncpy (ptn->family, f, sizeof ptn->family - 1);
+}
+
+void
+BFont_populate_plain_family (struct haiku_font_pattern *ptn)
+{
+ font_family f;
+ font_style s;
+ be_plain_font->GetFamilyAndStyle (&f, &s);
+
+ ptn->specified |= FSPEC_FAMILY;
+ strncpy (ptn->family, f, sizeof ptn->family - 1);
+}
+
+int
+BFont_string_width (void *font, const char *utf8)
+{
+ return ((BFont *) font)->StringWidth (utf8);
+}