+/*
+ * Copyright 2022 Olaf Wintermann
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+ * DEALINGS IN THE SOFTWARE.
+ */
+
+#include "nfont.h"
+
+
+/* font list functions */
+
+NFont *FontCreate(Display *dp, FcPattern *pattern)
+{
+ if(!pattern) {
+ return NULL;
+ }
+ FcResult result;
+ pattern = FcPatternDuplicate(pattern);
+ FcPattern *match = XftFontMatch(dp, DefaultScreen(dp), pattern, &result);
+
+ double sz = 0;
+ result = FcPatternGetDouble (pattern, FC_SIZE, 0, &sz);
+ if(result != FcResultMatch) {
+ FcPatternGetDouble (match, FC_SIZE, 0, &sz);
+ }
+
+ XftFont *defaultFont = XftFontOpenPattern(dp, match);
+ if(!defaultFont) {
+ FcPatternDestroy(match);
+ return NULL;
+ }
+
+ NFont *font = malloc(sizeof(NFont));
+ font->display = dp;
+ font->pattern = pattern;
+ font->fail = NULL;
+ font->size = sz;
+ font->ref = 1;
+
+ NFontList *list = malloc(sizeof(NFontList));
+ list->font = defaultFont;
+ list->next = NULL;
+ font->fonts = list;
+
+ return font;
+}
+
+NFont *FontFromName(Display *dp, const char *name)
+{
+ FcPattern *pattern = FcNameParse((FcChar8*)name);
+ if(!pattern) {
+ return NULL;
+ }
+ NFont *font = FontCreate(dp, pattern);
+ FcPatternDestroy(pattern);
+ return font;
+}
+
+XftFont *FontListAddFontForChar(NFont *f, FcChar32 c)
+{
+ /* charset for char c */
+ FcCharSet *charset = FcCharSetCreate();
+ FcValue value;
+ value.type = FcTypeCharSet;
+ value.u.c = charset;
+ FcCharSetAddChar(charset, c);
+ if(!FcCharSetHasChar(charset, c)) {
+ FcCharSetDestroy(charset);
+ return f->fonts->font;
+ }
+
+ /* font lookup based on the NFont pattern */
+ FcPattern *pattern = FcPatternDuplicate(f->pattern);
+ FcPatternAdd(pattern, FC_CHARSET, value, 0);
+ FcResult result;
+ FcPattern *match = XftFontMatch (
+ f->display, DefaultScreen(f->display), pattern, &result);
+ if(!match) {
+ FcPatternDestroy(pattern);
+ FontAddFail(f, charset);
+ return f->fonts->font;
+ }
+
+ XftFont *newFont = XftFontOpenPattern(f->display, match);
+ if(!newFont || !FcCharSetHasChar(newFont->charset, c)) {
+ FcPatternDestroy(pattern);
+ FcPatternDestroy(match);
+ if(newFont) {
+ XftFontClose(f->display, newFont);
+ }
+ FontAddFail(f, charset);
+ return f->fonts->font;
+ }
+
+ FcCharSetDestroy(charset);
+
+ NFontList *newElm = malloc(sizeof(NFontList));
+ newElm->font = newFont;
+ newElm->next = NULL;
+
+ NFontList *elm = f->fonts;
+ NFontList *last = NULL;
+ while(elm) {
+ last = elm;
+ elm = elm->next;
+ }
+ last->next = newElm;
+
+
+ return newFont;
+}
+
+XftFont *FindFont(NFont *f, FcChar32 c)
+{
+ if(c < 128) {
+ return f->fonts->font;
+ }
+
+ /* make sure the char is not in the fail list, because we don't
+ * want to retry font lookups */
+ NCharSetList *fail = f->fail;
+ while(fail) {
+ if(FcCharSetHasChar(fail->charset, c)) {
+ return f->fonts->font;
+ }
+ fail = fail->next;
+
+ }
+
+ /* find a font that has this char */
+ NFontList *elm = f->fonts;
+ while(elm) {
+ if(FcCharSetHasChar(elm->font->charset, c)) {
+ return elm->font;
+ }
+ elm = elm->next;
+ }
+
+ /* open a new font for this char */
+ return FontListAddFontForChar(f, c);
+}
+
+XftFont *FontDefault(NFont *f) {
+ return f->fonts->font;
+}
+
+void FontAddFail(NFont *f, FcCharSet *c)
+{
+ NCharSetList *elm = f->fail;
+ NCharSetList *last = elm;
+ while(elm) {
+ last = elm;
+ elm = elm->next;
+ }
+
+ NCharSetList *newElm = malloc(sizeof(NCharSetList));
+ newElm->charset = c;
+ newElm->next = NULL;
+ if(last) {
+ last->next = newElm;
+ } else {
+ f->fail = newElm;
+ }
+}
+
+void FontDestroy(NFont *f)
+{
+ NCharSetList *c = f->fail;
+ NCharSetList *nc;
+ while(c) {
+ FcCharSetDestroy(c->charset);
+ nc = c->next;
+ free(c);
+ c = nc;
+ }
+
+ NFontList *l = f->fonts;
+ NFontList *nl;
+ while(l) {
+ XftFontClose(f->display, l->font);
+ nl = l->next;
+ free(nl);
+ l = nl;
+ }
+
+ FcPatternDestroy(f->pattern);
+ free(f);
+}
+
+NFont *FontRef(NFont *font) {
+ font->ref++;
+ return font;
+}
+
+void FontUnref(NFont *font) {
+ if(--font->ref == 0) {
+ FontDestroy(font);
+ }
+}
\ No newline at end of file