aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--.gitignore8
-rw-r--r--LICENSE15
-rw-r--r--Makefile60
-rw-r--r--common.h32
-rw-r--r--config.mk10
-rw-r--r--demo.c73
-rw-r--r--libskrift.h183
-rw-r--r--libskrift_calculate_dpi.c4
-rw-r--r--libskrift_close_font.c14
-rw-r--r--libskrift_create_context.c82
-rw-r--r--libskrift_free_context.c11
-rw-r--r--libskrift_get_cluster_glyph.c69
-rw-r--r--libskrift_get_grapheme_glyph.c133
-rw-r--r--libskrift_get_rendering_settings.c8
-rw-r--r--libskrift_inches_to_pixels.c4
-rw-r--r--libskrift_merge_glyphs.c62
-rw-r--r--libskrift_millimeters_to_pixels.c4
-rw-r--r--libskrift_open_font.c50
-rw-r--r--libskrift_open_font_fd.c59
-rw-r--r--libskrift_open_font_file.c18
-rw-r--r--libskrift_open_font_mem.c18
-rw-r--r--libskrift_points_to_pixels.c4
22 files changed, 921 insertions, 0 deletions
diff --git a/.gitignore b/.gitignore
new file mode 100644
index 0000000..28edc93
--- /dev/null
+++ b/.gitignore
@@ -0,0 +1,8 @@
+\#*\#
+*~
+*.o
+*.a
+*.so
+*.su
+*.lo
+/demo
diff --git a/LICENSE b/LICENSE
new file mode 100644
index 0000000..38e536c
--- /dev/null
+++ b/LICENSE
@@ -0,0 +1,15 @@
+ISC License
+
+© 2020 Mattias Andrée <maandree@kth.se>
+
+Permission to use, copy, modify, and/or distribute this software for any
+purpose with or without fee is hereby granted, provided that the above
+copyright notice and this permission notice appear in all copies.
+
+THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
diff --git a/Makefile b/Makefile
new file mode 100644
index 0000000..84c04d5
--- /dev/null
+++ b/Makefile
@@ -0,0 +1,60 @@
+.POSIX:
+
+CONFIGFILE = config.mk
+include $(CONFIGFILE)
+
+OBJ =\
+ libskrift_calculate_dpi.o\
+ libskrift_close_font.o\
+ libskrift_create_context.o\
+ libskrift_free_context.o\
+ libskrift_get_grapheme_glyph.o\
+ libskrift_get_cluster_glyph.o\
+ libskrift_get_rendering_settings.o\
+ libskrift_inches_to_pixels.o\
+ libskrift_merge_glyphs.o\
+ libskrift_millimeters_to_pixels.o\
+ libskrift_open_font.o\
+ libskrift_open_font_fd.o\
+ libskrift_open_font_file.o\
+ libskrift_open_font_mem.o\
+ libskrift_points_to_pixels.o
+
+LIB_HDR =\
+ libskrift.h
+
+HDR =\
+ common.h\
+ $(LIB_HDR)
+
+all: libskrift.a demo
+$(OBJ): $(@:.o=.c) $(HDR)
+demo.o: demo.c $(LIB_HDR)
+
+libskrift.a: $(OBJ)
+ $(AR) rc $@ $(OBJ)
+ $(AR) ts $@ > /dev/null
+
+.c.o:
+ $(CC) -c -o $@ $< $(CFLAGS) $(CPPFLAGS)
+
+demo: demo.o libskrift.a
+ $(CC) -o $@ $@.o libskrift.a $(LDFLAGS)
+
+install: libskrift.a
+ mkdir -p -- "$(DESTDIR)$(PREFIX)/lib"
+ mkdir -p -- "$(DESTDIR)$(PREFIX)/include"
+ cp -- libskrift.a "$(DESTDIR)$(PREFIX)/lib"
+ cp -- libskrift.h "$(DESTDIR)$(PREFIX)/include"
+
+uninstall:
+ -rm -f -- "$(DESTDIR)$(PREFIX)/lib/libskrift.a"
+ -rm -f -- "$(DESTDIR)$(PREFIX)/include/libskrift.h"
+
+clean:
+ -rm -f -- *.o *.lo *.su libskrift.a libskrift.so libskrift.so.*
+
+.SUFFIXES:
+.SUFFIXES: .c .o
+
+.PHONY: all install uninstall clean
diff --git a/common.h b/common.h
new file mode 100644
index 0000000..fdc17f5
--- /dev/null
+++ b/common.h
@@ -0,0 +1,32 @@
+#include "libskrift.h"
+
+#include <sys/mman.h>
+#include <sys/stat.h>
+#include <errno.h>
+#include <math.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+#include <schrift.h>
+#include <grapheme.h>
+
+#define MIN(A, B) ((A) < (B) ? (A) : (B))
+#define MAX(A, B) ((A) > (B) ? (A) : (B))
+
+struct libskrift_context {
+ LIBSKRIFT_FONT *font;
+ struct libskrift_rendering rendering;
+ struct SFT schrift_ctx;
+ char subpixel_horizontally;
+ char subpixel_vertically;
+ char subpixel_bgr;
+};
+
+struct libskrift_font {
+ SFT_Font *font;
+ void *memory_free;
+ void *memory_unmap;
+ size_t memory_size;
+ size_t refcount;
+};
diff --git a/config.mk b/config.mk
new file mode 100644
index 0000000..de63532
--- /dev/null
+++ b/config.mk
@@ -0,0 +1,10 @@
+PREFIX = /usr
+MANPREFIX = $(PREFIX)/share/man
+
+DEMO_FONT = /usr/share/fonts/liberation/LiberationSans-Regular.ttf
+
+CPPFLAGS = -D_DEFAULT_SOURCE -D_BSD_SOURCE -D_XOPEN_SOURCE=700 -D_GNU_SOURCE '-DDEMO_FONT="$(DEMO_FONT)"'
+CFLAGS = -std=c99 -Wall -g
+LDFLAGS = -lschrift -lm -lgrapheme
+
+CC = cc
diff --git a/demo.c b/demo.c
new file mode 100644
index 0000000..79cf920
--- /dev/null
+++ b/demo.c
@@ -0,0 +1,73 @@
+/* See LICENSE file for copyright and license details. */
+#include "libskrift.h"
+
+#include <stdlib.h>
+#include <string.h>
+
+#include <grapheme.h>
+
+int
+main(void)
+{
+ LIBSKRIFT_FONT *font;
+ LIBSKRIFT_CONTEXT *ctx;
+ struct libskrift_glyph *glyph;
+ struct libskrift_rendering rendering = LIBSKRIFT_DEFAULT_RENDERING;
+ uint16_t i, x, y;
+ double height;
+
+ rendering.smoothing = LIBSKRIFT_SUBPIXEL;
+ rendering.subpixel_order = LIBSKRIFT_RGB;
+ rendering.flags = 0;
+
+ if (libskrift_open_font_file(&font, DEMO_FONT)) {
+ perror("libskrift_open_font_file");
+ return -1;
+ }
+ height = libskrift_points_to_pixels(72, &rendering);
+ if (libskrift_create_context(&ctx, font, &rendering, height)) {
+ perror("libskrift_create_context");
+ return -1;
+ }
+ libskrift_close_font(font);
+
+#if 1
+ if (libskrift_get_cluster_glyph(ctx, "x̴̑", NULL, 0, 0, &glyph) < 0) {
+ perror("libskrift_get_cluster_glyph");
+ return -1;
+ }
+#else
+ if (libskrift_get_grapheme_glyph(ctx, 197 /* Å */, 0, 0, &glyph)) {
+ perror("libskrift_get_grapheme_glyph");
+ return -1;
+ }
+#endif
+
+ if (rendering.smoothing == LIBSKRIFT_GREYSCALE) {
+ printf("P2\n%u %u\n255\n", glyph->width, glyph->height);
+ printf("# x-position: %i\n", glyph->x);
+ printf("# y-position: %i\n", glyph->y);
+ printf("# advance: %lf\n", glyph->advance);
+ for (i = y = 0; y < glyph->height; y++) {
+ for (x = 0; x < glyph->width; x++, i++)
+ printf("%3u ", glyph->image[i]);
+ printf("\n\n");
+ }
+ fflush(stdout);
+ } else {
+ printf("P3\n%u %u\n255\n", glyph->width, glyph->height);
+ printf("# x-position: %i\n", glyph->x);
+ printf("# y-position: %i\n", glyph->y);
+ printf("# advance: %lf\n", glyph->advance);
+ for (i = y = 0; y < glyph->height; y++) {
+ for (x = 0; x < glyph->width * 3; x++, i++)
+ printf("%3u ", glyph->image[i]);
+ printf("\n\n");
+ }
+ fflush(stdout);
+ }
+
+ free(glyph);
+ libskrift_free_context(ctx);
+ return 0;
+}
diff --git a/libskrift.h b/libskrift.h
new file mode 100644
index 0000000..5c2e937
--- /dev/null
+++ b/libskrift.h
@@ -0,0 +1,183 @@
+/* See LICENSE file for copyright and license details. */
+#ifndef LIBSKRIFT_H
+#define LIBSKRIFT_H 1
+
+#include <stddef.h>
+#include <stdint.h>
+#include <stdio.h>
+
+#if defined(__GNUC__)
+# define _LIBSKRIFT_GCC_ONLY(...) __VA_ARGS__
+#else
+# define _LIBSKRIFT_GCC_ONLY(...)
+#endif
+
+
+#define LIBSKRIFT_RENDERING_STRUCT_VERSION 0
+
+#define LIBSKRIFT_NONE 0
+
+typedef struct libskrift_context LIBSKRIFT_CONTEXT;
+typedef struct libskrift_font LIBSKRIFT_FONT;
+typedef uint_least32_t libskrift_codepoint_t;
+
+
+enum libskrift_subpixel_order {
+ LIBSKRIFT_OTHER, /* LIBSKRIFT_NONE */
+ LIBSKRIFT_RGB,
+ LIBSKRIFT_BGR,
+ LIBSKRIFT_VRGB,
+ LIBSKRIFT_VBGR
+};
+
+enum libskrift_smoothing {
+ LIBSKRIFT_MONOCHROME, /* LIBSKRIFT_NONE */
+ LIBSKRIFT_GREYSCALE,
+ LIBSKRIFT_SUBPIXEL
+};
+
+enum libskrift_hinting {
+ LIBSKRIFT_UNHINTED = 0, /* LIBSKRIFT_NONE */
+ LIBSKRIFT_SLIGHT = 25,
+ LIBSKRIFT_MEDIUM = 50,
+ LIBSKRIFT_FULL = 100
+};
+
+enum libskrift_rendering_flags {
+ LIBSKRIFT_CORRECT_GAMMA = 0x0001,
+ LIBSKRIFT_REMOVE_GAMMA = 0x0002,
+ LIBSKRIFT_Y_INCREASES_UPWARDS = 0x0004, /* SFT_DOWNWARD_Y otherwise */
+ LIBSKRIFT_FLIP_TEXT = 0x0008,
+ LIBSKRIFT_MIRROR_TEXT = 0x0010,
+ LIBSKRIFT_MIRROR_CHARS = 0x0020,
+ LIBSKRIFT_TRANSPOSE_TEXT = 0x0040,
+ LIBSKRIFT_TRANSPOSE_CHARS = 0x0080,
+ LIBSKRIFT_NO_LIGATURES = 0x0100,
+ LIBSKRIFT_ADVANCE_TO_GRID = 0x0200,
+ LIBSKRIFT_REGRESS_TO_GRID = 0x0400, /* Combine with LIBSKRIFT_ADVANCE_TO_GRID for closest alternative */
+ LIBSKRIFT_USE_SUBPIXEL_GRID = 0x0800
+};
+
+struct libskrift_rendering {
+ int struct_version;
+ enum libskrift_subpixel_order subpixel_order;
+ enum libskrift_smoothing smoothing;
+ enum libskrift_hinting hinting;
+ enum libskrift_rendering_flags flags;
+ double horizontal_dpi;
+ double vertical_dpi;
+ double kerning;
+ double interletter_spacing;
+ double prestroke_transformation_rotation[4];
+ double left_transformation[6];
+ double right_transformation[6];
+ double top_transformation[6];
+ double bottom_transformation[6];
+ double poststroke_transformation_rotation[4];
+ double char_transformation[6];
+ double text_transformation[6];
+};
+
+struct libskrift_glyph {
+ double advance;
+ int16_t x;
+ int16_t y;
+ uint16_t width;
+ uint16_t height;
+ size_t size;
+ uint8_t image[];
+};
+
+struct libskrift_saved_grapheme {
+ libskrift_codepoint_t cp;
+ size_t len;
+};
+
+
+#define LIBSKRIFT_DEFAULT_RENDERING {\
+ .struct_version = LIBSKRIFT_RENDERING_STRUCT_VERSION,\
+ .subpixel_order = LIBSKRIFT_NONE,\
+ .smoothing = LIBSKRIFT_GREYSCALE,\
+ .hinting = LIBSKRIFT_FULL,\
+ .flags = 0,\
+ .horizontal_dpi = (double)1920 * 254 / 5180,\
+ .vertical_dpi = (double)1200 * 254 / 3240,\
+ .kerning = 1,\
+ .interletter_spacing = 0,\
+ .prestroke_transformation_rotation = {1, 0, 0, 1},\
+ .left_transformation = {1, 0, 0, 0, 1, 0},\
+ .right_transformation = {1, 0, 0, 0, 1, 0},\
+ .top_transformation = {1, 0, 0, 0, 1, 0},\
+ .bottom_transformation = {1, 0, 0, 0, 1, 0},\
+ .poststroke_transformation_rotation = {1, 0, 0, 1},\
+ .char_transformation = {1, 0, 0, 0, 1, 0},\
+ .text_transformation = {1, 0, 0, 0, 1, 0},\
+}
+
+#define LIBSKRIFT_NO_SAVED_GRAPHEME {0, 0}
+
+
+_LIBSKRIFT_GCC_ONLY(__attribute__((__const__, __warn_unused_result__)))
+inline double
+libskrift_calculate_dpi(double pixels, double millimeters)
+{
+ return pixels * 254 / 10 / millimeters;
+}
+
+_LIBSKRIFT_GCC_ONLY(__attribute__((__const__, __warn_unused_result__)))
+inline double
+libskrift_inches_to_pixels(double inches, const struct libskrift_rendering *rendering)
+{
+ return inches * rendering->vertical_dpi;
+}
+
+_LIBSKRIFT_GCC_ONLY(__attribute__((__const__, __warn_unused_result__)))
+inline double
+libskrift_millimeters_to_pixels(double millimeters, const struct libskrift_rendering *rendering)
+{
+ return millimeters * 10 / 254 * rendering->vertical_dpi;
+}
+
+_LIBSKRIFT_GCC_ONLY(__attribute__((__const__, __warn_unused_result__)))
+inline double
+libskrift_points_to_pixels(double points, const struct libskrift_rendering *rendering)
+{
+ return points / 72 * rendering->vertical_dpi;
+}
+
+
+_LIBSKRIFT_GCC_ONLY(__attribute__((__nonnull__)))
+int libskrift_open_font_file(LIBSKRIFT_FONT **, const char *);
+
+_LIBSKRIFT_GCC_ONLY(__attribute__((__nonnull__)))
+int libskrift_open_font_mem(LIBSKRIFT_FONT **, const void *, size_t);
+
+_LIBSKRIFT_GCC_ONLY(__attribute__((__nonnull__)))
+int libskrift_open_font_fd(LIBSKRIFT_FONT **, int);
+
+_LIBSKRIFT_GCC_ONLY(__attribute__((__nonnull__)))
+int libskrift_open_font(LIBSKRIFT_FONT **, FILE *);
+
+void libskrift_close_font(LIBSKRIFT_FONT *);
+
+
+_LIBSKRIFT_GCC_ONLY(__attribute__((__nonnull__(1, 2))))
+int libskrift_create_context(LIBSKRIFT_CONTEXT **, LIBSKRIFT_FONT *, const struct libskrift_rendering *, double);
+
+void libskrift_free_context(LIBSKRIFT_CONTEXT *);
+
+_LIBSKRIFT_GCC_ONLY(__attribute__((__nonnull__, __returns_nonnull__, __warn_unused_result__, __const__)))
+const struct libskrift_rendering *libskrift_get_rendering_settings(LIBSKRIFT_CONTEXT *);
+
+
+_LIBSKRIFT_GCC_ONLY(__attribute__((__nonnull__)))
+int libskrift_get_grapheme_glyph(LIBSKRIFT_CONTEXT *, libskrift_codepoint_t, double, double, struct libskrift_glyph **);
+
+_LIBSKRIFT_GCC_ONLY(__attribute__((__nonnull__(1, 2, 6))))
+ssize_t libskrift_get_cluster_glyph(LIBSKRIFT_CONTEXT *, const char *, struct libskrift_saved_grapheme *,
+ double, double, struct libskrift_glyph **);
+
+_LIBSKRIFT_GCC_ONLY(__attribute__((__nonnull__)))
+int libskrift_merge_glyphs(LIBSKRIFT_CONTEXT *, struct libskrift_glyph *, struct libskrift_glyph *, struct libskrift_glyph **);
+
+#endif
diff --git a/libskrift_calculate_dpi.c b/libskrift_calculate_dpi.c
new file mode 100644
index 0000000..fb70855
--- /dev/null
+++ b/libskrift_calculate_dpi.c
@@ -0,0 +1,4 @@
+/* See LICENSE file for copyright and license details. */
+#include "common.h"
+
+extern inline double libskrift_calculate_dpi(double pixels, double millimeters);
diff --git a/libskrift_close_font.c b/libskrift_close_font.c
new file mode 100644
index 0000000..2786666
--- /dev/null
+++ b/libskrift_close_font.c
@@ -0,0 +1,14 @@
+/* See LICENSE file for copyright and license details. */
+#include "common.h"
+
+void
+libskrift_close_font(LIBSKRIFT_FONT *font)
+{
+ if (font && !--font->refcount) {
+ sft_freefont(font->font);
+ free(font->memory_free);
+ if (font->memory_unmap)
+ munmap(font->memory_unmap, font->memory_size);
+ free(font);
+ }
+}
diff --git a/libskrift_create_context.c b/libskrift_create_context.c
new file mode 100644
index 0000000..16c215e
--- /dev/null
+++ b/libskrift_create_context.c
@@ -0,0 +1,82 @@
+/* See LICENSE file for copyright and license details. */
+#include "common.h"
+
+#define IMPLEMENTED_FLAGS (LIBSKRIFT_CORRECT_GAMMA |\
+ LIBSKRIFT_REMOVE_GAMMA) /* libschrift does not add gamma, so not handling is required */
+
+#define HAVE_MULTIPLE_FLAGS(HAVE, CHECK) (((HAVE) & (CHECK)) & (((HAVE) & (CHECK)) - 1))
+
+#define COPY_ARRAY(DEST_STRUCT, SRC_STRUCT, FIELD)\
+ memcpy((DEST_STRUCT).FIELD, (SRC_STRUCT).FIELD, sizeof((SRC_STRUCT).FIELD))
+
+static const struct libskrift_rendering default_rendering = LIBSKRIFT_DEFAULT_RENDERING;
+
+int
+libskrift_create_context(LIBSKRIFT_CONTEXT **ctxp, LIBSKRIFT_FONT *font, const struct libskrift_rendering *rendering, double height)
+{
+ if (rendering) {
+ if (HAVE_MULTIPLE_FLAGS(rendering->flags, LIBSKRIFT_CORRECT_GAMMA | LIBSKRIFT_REMOVE_GAMMA)) {
+ errno = EINVAL;
+ return -1;
+ }
+ }
+
+ *ctxp = calloc(1, sizeof(**ctxp));
+ if (!*ctxp)
+ return -1;
+
+ (*ctxp)->font = font;
+ (*ctxp)->schrift_ctx.font = font->font;
+ (*ctxp)->schrift_ctx.yScale = height;
+
+ if (rendering) {
+ memcpy(&(*ctxp)->rendering, rendering, sizeof(*rendering));
+
+ COPY_ARRAY((*ctxp)->rendering, default_rendering, prestroke_transformation_rotation);
+ COPY_ARRAY((*ctxp)->rendering, default_rendering, left_transformation);
+ COPY_ARRAY((*ctxp)->rendering, default_rendering, right_transformation);
+ COPY_ARRAY((*ctxp)->rendering, default_rendering, top_transformation);
+ COPY_ARRAY((*ctxp)->rendering, default_rendering, bottom_transformation);
+ COPY_ARRAY((*ctxp)->rendering, default_rendering, poststroke_transformation_rotation);
+ COPY_ARRAY((*ctxp)->rendering, default_rendering, char_transformation);
+ COPY_ARRAY((*ctxp)->rendering, default_rendering, text_transformation);
+ } else {
+ memcpy(&(*ctxp)->rendering, &default_rendering, sizeof(default_rendering));
+ }
+
+ (*ctxp)->rendering.struct_version = LIBSKRIFT_RENDERING_STRUCT_VERSION;
+ (*ctxp)->rendering.hinting = LIBSKRIFT_NONE;
+ (*ctxp)->rendering.flags &= IMPLEMENTED_FLAGS;
+ (*ctxp)->rendering.kerning = 0;
+ (*ctxp)->rendering.interletter_spacing = 0;
+
+ if (!(*ctxp)->rendering.smoothing)
+ (*ctxp)->rendering.smoothing = LIBSKRIFT_GREYSCALE;
+
+ (*ctxp)->schrift_ctx.xScale = (*ctxp)->schrift_ctx.yScale;
+ (*ctxp)->schrift_ctx.xScale *= (*ctxp)->rendering.horizontal_dpi / (*ctxp)->rendering.vertical_dpi;
+
+ if ((*ctxp)->rendering.smoothing == LIBSKRIFT_SUBPIXEL) {
+ if ((*ctxp)->rendering.subpixel_order == LIBSKRIFT_RGB) {
+ (*ctxp)->schrift_ctx.xScale *= 3;
+ (*ctxp)->subpixel_horizontally = 1;
+ } else if ((*ctxp)->rendering.subpixel_order == LIBSKRIFT_BGR) {
+ (*ctxp)->schrift_ctx.xScale *= 3;
+ (*ctxp)->subpixel_horizontally = 1;
+ (*ctxp)->subpixel_bgr = 1;
+ } else if ((*ctxp)->rendering.subpixel_order == LIBSKRIFT_VRGB) {
+ (*ctxp)->schrift_ctx.yScale *= 3;
+ (*ctxp)->subpixel_vertically = 1;
+ } else if ((*ctxp)->rendering.subpixel_order == LIBSKRIFT_VBGR) {
+ (*ctxp)->schrift_ctx.yScale *= 3;
+ (*ctxp)->subpixel_vertically = 1;
+ (*ctxp)->subpixel_bgr = 1;
+ } else {
+ (*ctxp)->rendering.subpixel_order = LIBSKRIFT_NONE;
+ (*ctxp)->rendering.smoothing = LIBSKRIFT_GREYSCALE;
+ }
+ }
+
+ font->refcount += 1;
+ return 0;
+}
diff --git a/libskrift_free_context.c b/libskrift_free_context.c
new file mode 100644
index 0000000..2375c05
--- /dev/null
+++ b/libskrift_free_context.c
@@ -0,0 +1,11 @@
+/* See LICENSE file for copyright and license details. */
+#include "common.h"
+
+void
+libskrift_free_context(LIBSKRIFT_CONTEXT *ctx)
+{
+ if (ctx) {
+ libskrift_close_font(ctx->font);
+ free(ctx);
+ }
+}
diff --git a/libskrift_get_cluster_glyph.c b/libskrift_get_cluster_glyph.c
new file mode 100644
index 0000000..e8d5724
--- /dev/null
+++ b/libskrift_get_cluster_glyph.c
@@ -0,0 +1,69 @@
+/* See LICENSE file for copyright and license details. */
+#include "common.h"
+
+/* TODO try normalisation if not found, then try fallback fonts */
+
+ssize_t
+libskrift_get_cluster_glyph(LIBSKRIFT_CONTEXT *ctx, const char *text, struct libskrift_saved_grapheme *saved,
+ double x, double y, struct libskrift_glyph **glyphp)
+{
+ struct libskrift_glyph *glyph0, *glyph1, *glyph2;
+ uint32_t cp0, cp1;
+ int state = 0;
+ size_t r, len;
+
+ *glyphp = NULL;
+
+ if (saved && saved->cp) {
+ cp0 = saved->cp;
+ len = saved->len;
+ } else {
+ len = grapheme_decode(text, &cp0);
+ if (!len) {
+ errno = EINVAL;
+ return -1;
+ }
+ }
+
+ if (libskrift_get_grapheme_glyph(ctx, cp0, x, y, &glyph0))
+ return -1;
+
+ x += glyph0->advance;
+ for (; cp0; cp0 = cp1, len += r) {
+ r = grapheme_decode(&text[len], &cp1);
+ if (!r) {
+ if (saved) {
+ saved->cp = 0;
+ saved->len = 0;
+ }
+ break;
+ }
+ if (grapheme_boundary(cp0, cp1, &state)) {
+ if (saved) {
+ saved->cp = cp1;
+ saved->len = r;
+ }
+ break;
+ }
+
+ if (libskrift_get_grapheme_glyph(ctx, cp1, x, y, &glyph1)) {
+ free(glyph0);
+ return -1;
+ }
+ x += glyph1->advance;
+
+ if (libskrift_merge_glyphs(ctx, glyph0, glyph1, &glyph2)) {
+ free(glyph0);
+ free(glyph1);
+ return -1;
+ }
+
+ free(glyph0);
+ free(glyph1);
+ glyph0 = glyph2;
+ }
+
+ *glyphp = glyph0;
+
+ return (ssize_t)len;
+}
diff --git a/libskrift_get_grapheme_glyph.c b/libskrift_get_grapheme_glyph.c
new file mode 100644
index 0000000..cdaee2d
--- /dev/null
+++ b/libskrift_get_grapheme_glyph.c
@@ -0,0 +1,133 @@
+/* See LICENSE file for copyright and license details. */
+#include "common.h"
+
+static const uint8_t gamma_map[] = {
+ 0, 13, 22, 28, 34, 38, 42, 46, 50, 53, 56, 59, 61, 64, 66, 69,
+ 71, 73, 75, 77, 79, 81, 83, 85, 86, 88, 90, 92, 93, 95, 96, 98,
+ 99, 101, 102, 104, 105, 106, 108, 109, 110, 112, 113, 114, 115, 117, 118, 119,
+ 120, 121, 122, 124, 125, 126, 127, 128, 129, 130, 131, 132, 133, 134, 135, 136,
+ 137, 138, 139, 140, 141, 142, 143, 144, 145, 146, 147, 148, 148, 149, 150, 151,
+ 152, 153, 154, 155, 155, 156, 157, 158, 159, 159, 160, 161, 162, 163, 163, 164,
+ 165, 166, 167, 167, 168, 169, 170, 170, 171, 172, 173, 173, 174, 175, 175, 176,
+ 177, 178, 178, 179, 180, 180, 181, 182, 182, 183, 184, 185, 185, 186, 187, 187,
+ 188, 189, 189, 190, 190, 191, 192, 192, 193, 194, 194, 195, 196, 196, 197, 197,
+ 198, 199, 199, 200, 200, 201, 202, 202, 203, 203, 204, 205, 205, 206, 206, 207,
+ 208, 208, 209, 209, 210, 210, 211, 212, 212, 213, 213, 214, 214, 215, 215, 216,
+ 216, 217, 218, 218, 219, 219, 220, 220, 221, 221, 222, 222, 223, 223, 224, 224,
+ 225, 226, 226, 227, 227, 228, 228, 229, 229, 230, 230, 231, 231, 232, 232, 233,
+ 233, 234, 234, 235, 235, 236, 236, 237, 237, 238, 238, 238, 239, 239, 240, 240,
+ 241, 241, 242, 242, 243, 243, 244, 244, 245, 245, 246, 246, 246, 247, 247, 248,
+ 248, 249, 249, 250, 250, 251, 251, 251, 252, 252, 253, 253, 254, 254, 255, 255
+};
+
+int
+libskrift_get_grapheme_glyph(LIBSKRIFT_CONTEXT *ctx, libskrift_codepoint_t codepoint,
+ double cursor_x, double cursor_y, struct libskrift_glyph **glyphp)
+{
+ struct SFT_Char sft_chr;
+ size_t size = 1, width0, width1, width2, width3, osize, off, r, c, i;
+ int top = 0, left = 0, right = 0, bottom = 0;
+ uint16_t width, height, vmul = 1, hmul = 1;
+ uint8_t *image, *in_image, t;
+
+ memset(&sft_chr, 0, sizeof(sft_chr));
+
+ ctx->schrift_ctx.x = cursor_x * (ctx->subpixel_horizontally ? 3 : 1);
+ ctx->schrift_ctx.y = cursor_y * (ctx->subpixel_vertically ? 3 : 1);
+ ctx->schrift_ctx.flags = SFT_DOWNWARD_Y | SFT_CHAR_IMAGE;
+
+ if (sft_char(&ctx->schrift_ctx, codepoint, &sft_chr))
+ return -1;
+
+ if (ctx->subpixel_horizontally) {
+ hmul = 3;
+ size = 3;
+ left = (3 + sft_chr.x % 3) % 3;
+ right = (3 - (sft_chr.width + left) % 3) % 3;
+ } else if (ctx->subpixel_vertically) {
+ vmul = 3;
+ size = 3;
+ top = (3 + sft_chr.y % 3) % 3;
+ bottom = (3 - (sft_chr.height + top) % 3) % 3;
+ }
+
+ width = (uint16_t)(sft_chr.width + left + right) / hmul;
+ height = (uint16_t)(sft_chr.height + top + bottom) / vmul;
+ size *= (size_t)width * (size_t)height;
+
+ *glyphp = malloc(offsetof(struct libskrift_glyph, image) + size);
+ if (!*glyphp) {
+ free(sft_chr.image);
+ return -1;
+ }
+
+ (*glyphp)->advance = sft_chr.advance / hmul;
+ (*glyphp)->x = (int16_t)((sft_chr.x - left) / (int16_t)hmul);
+ (*glyphp)->y = (int16_t)((sft_chr.y - top) / (int16_t)vmul);
+ (*glyphp)->width = width;
+ (*glyphp)->height = height;
+ (*glyphp)->size = size;
+
+ image = (*glyphp)->image;
+ in_image = sft_chr.image;
+ if (ctx->subpixel_horizontally && width) {
+ width3 = (size_t)width * 3;
+ for (r = 0; r < height; r++) {
+ image[0] = image[width3 - 3] = 0;
+ image[1] = image[width3 - 2] = 0;
+ image[2] = image[width3 - 1] = 0;
+ memcpy(&image[left], in_image, (size_t)sft_chr.width);
+ image += width3;
+ in_image += sft_chr.width;
+ }
+ if (ctx->subpixel_bgr) {
+ image = (*glyphp)->image;
+ for (i = 0; i < size; i += 3) {
+ t = image[i + 0];
+ image[i + 0] = image[i + 2];
+ image[i + 2] = t;
+ }
+ }
+ } else if (ctx->subpixel_vertically && height) {
+ width0 = (size_t)width * (ctx->subpixel_bgr ? 2 : 0);
+ width1 = (size_t)width * 1;
+ width2 = (size_t)width * (ctx->subpixel_bgr ? 0 : 2);
+ width3 = (size_t)width * 3;
+ in_image = realloc(in_image, size);
+ if (!in_image) {
+ free(sft_chr.image);
+ free(*glyphp);
+ *glyphp = NULL;
+ return -1;
+ }
+ sft_chr.image = in_image;
+ osize = (size_t)sft_chr.width * (size_t)sft_chr.height;
+ off = (size_t)top;
+ if (off) {
+ off *= width;
+ memmove(&in_image[off], in_image, osize);
+ memset(in_image, 0, off);
+ osize += off;
+ }
+ memset(&in_image[osize], 0, size - osize);
+ for (i = r = 0; r < height; r++) {
+ for (c = 0; c < width; c++, i += 3) {
+ image[i + 0] = in_image[c + width0];
+ image[i + 1] = in_image[c + width1];
+ image[i + 2] = in_image[c + width2];
+ }
+ in_image += width3;
+ }
+ } else {
+ memcpy(image, in_image, size);
+ }
+
+ if (ctx->rendering.flags & LIBSKRIFT_CORRECT_GAMMA) {
+ image = (*glyphp)->image;
+ for (i = 0; i < size; i++)
+ image[i] = gamma_map[image[i]];
+ }
+
+ free(sft_chr.image);
+ return 0;
+}
diff --git a/libskrift_get_rendering_settings.c b/libskrift_get_rendering_settings.c
new file mode 100644
index 0000000..4ad7f81
--- /dev/null
+++ b/libskrift_get_rendering_settings.c
@@ -0,0 +1,8 @@
+/* See LICENSE file for copyright and license details. */
+#include "common.h"
+
+const struct libskrift_rendering *
+libskrift_get_rendering_settings(LIBSKRIFT_CONTEXT *ctx)
+{
+ return &ctx->rendering;
+}
diff --git a/libskrift_inches_to_pixels.c b/libskrift_inches_to_pixels.c
new file mode 100644
index 0000000..3901175
--- /dev/null
+++ b/libskrift_inches_to_pixels.c
@@ -0,0 +1,4 @@
+/* See LICENSE file for copyright and license details. */
+#include "common.h"
+
+extern inline double libskrift_points_to_pixels(double points, const struct libskrift_rendering *rendering);
diff --git a/libskrift_merge_glyphs.c b/libskrift_merge_glyphs.c
new file mode 100644
index 0000000..548e8fb
--- /dev/null
+++ b/libskrift_merge_glyphs.c
@@ -0,0 +1,62 @@
+/* See LICENSE file for copyright and license details. */
+#include "common.h"
+
+/* TODO How common are grapheme clusters with more than two glyphs? Should we need a variadic version? */
+
+int
+libskrift_merge_glyphs(LIBSKRIFT_CONTEXT *ctx, struct libskrift_glyph *glyph1,
+ struct libskrift_glyph *glyph2, struct libskrift_glyph **glyphp)
+{
+ int16_t x1a, x1b, x2a, x2b, y1a, y1b, y2a, y2b, x1, x2, y1, y2;
+ size_t width, height, r, c, size = 1, psize = 1;
+ size_t src_off, dest_off, src_linesize, dest_linesize;
+
+ if (ctx->rendering.smoothing == LIBSKRIFT_SUBPIXEL)
+ size = psize = 3;
+
+ x1a = glyph1->x;
+ x1b = glyph2->x;
+ y1a = glyph1->y;
+ y1b = glyph2->y;
+
+ x2a = (int16_t)(x1a + (int16_t)glyph1->width);
+ x2b = (int16_t)(x1b + (int16_t)glyph2->width);
+ y2a = (int16_t)(y1a + (int16_t)glyph1->height);
+ y2b = (int16_t)(y1b + (int16_t)glyph2->height);
+
+ x1 = MIN(x1a, x1b);
+ y1 = MIN(y1a, y1b);
+ x2 = MAX(x2a, x2b);
+ y2 = MAX(y2a, y2b);
+
+ size *= width = (uint16_t)(x2 - x1);
+ size *= height = (uint16_t)(y2 - y1);
+
+ *glyphp = calloc(1, offsetof(struct libskrift_glyph, image) + size);
+ if (!*glyphp)
+ return -1;
+
+ (*glyphp)->advance = glyph1->advance + glyph2->advance;
+ (*glyphp)->x = x1;
+ (*glyphp)->y = y1;
+ (*glyphp)->width = (uint16_t)width;
+ (*glyphp)->height = (uint16_t)height;
+ (*glyphp)->size = size;
+
+ dest_linesize = width * psize;
+
+ src_linesize = glyph1->width * psize;
+ dest_off = (size_t)(glyph1->y - y1) * dest_linesize;
+ dest_off += (size_t)(glyph1->x - x1) * psize;
+ for (r = src_off = 0; r < glyph1->height; r++, dest_off += dest_linesize, src_off += src_linesize)
+ memcpy(&(*glyphp)->image[dest_off], &glyph1->image[src_off], src_linesize);
+
+ src_linesize = glyph2->width * psize;
+ dest_off = (size_t)(glyph2->y - y1) * dest_linesize;
+ dest_off += (size_t)(glyph2->x - x1) * psize;
+ for (r = src_off = 0; r < glyph2->height; r++, dest_off += dest_linesize, src_off += src_linesize)
+ for (c = 0; c < src_linesize; c++)
+ (*glyphp)->image[dest_off + c] = MAX((*glyphp)->image[dest_off + c], glyph2->image[src_off + c]);
+
+ return 0;
+}
diff --git a/libskrift_millimeters_to_pixels.c b/libskrift_millimeters_to_pixels.c
new file mode 100644
index 0000000..531a775
--- /dev/null
+++ b/libskrift_millimeters_to_pixels.c
@@ -0,0 +1,4 @@
+/* See LICENSE file for copyright and license details. */
+#include "common.h"
+
+extern inline double libskrift_inches_to_pixels(double inches, const struct libskrift_rendering *rendering);
diff --git a/libskrift_open_font.c b/libskrift_open_font.c
new file mode 100644
index 0000000..1eb8ff9
--- /dev/null
+++ b/libskrift_open_font.c
@@ -0,0 +1,50 @@
+/* See LICENSE file for copyright and license details. */
+#include "common.h"
+
+int
+libskrift_open_font(LIBSKRIFT_FONT **fontp, FILE *fp)
+{
+ int saved_errno;
+ char *mem = NULL, *new;
+ size_t size = 0, off = 0, r;
+
+ *fontp = calloc(1, sizeof(**fontp));
+
+ saved_errno = errno;
+ errno = 0;
+ for (;;) {
+ if (off + 2048 > size) {
+ size += 8192;
+ new = realloc(mem, size);
+ if (!new) {
+ free(mem);
+ return -1;
+ }
+ mem = new;
+ }
+ r = fread(&mem[off], 1, size - off, fp);
+ if (!r)
+ break;
+ off += r;
+ }
+ if (errno) {
+ free(mem);
+ return -1;
+ }
+
+ size = off;
+ new = realloc(mem, size);
+ if (new)
+ mem = new;
+ errno = saved_errno;
+
+ if (libskrift_open_font_mem(fontp, mem, size)) {
+ free(mem);
+ return -1;
+ }
+
+ (*fontp)->memory_free = mem;
+ (*fontp)->memory_size = size;
+
+ return 0;
+}
diff --git a/libskrift_open_font_fd.c b/libskrift_open_font_fd.c
new file mode 100644
index 0000000..c00422e
--- /dev/null
+++ b/libskrift_open_font_fd.c
@@ -0,0 +1,59 @@
+/* See LICENSE file for copyright and license details. */
+#include "common.h"
+
+int
+libskrift_open_font_fd(LIBSKRIFT_FONT **fontp, int fd)
+{
+ int saved_errno;
+ struct stat st;
+ char *mem = NULL, *new;
+ size_t size = 0, off = 0;
+ int mmapped = 0;
+ ssize_t r;
+
+ saved_errno = errno;
+ if (fstat(fd, &st) < 0 || !st.st_size || !S_ISREG(st.st_mode)) {
+ for (;;) {
+ if (off + 2048 > size) {
+ size += 8192;
+ new = realloc(mem, size);
+ if (!new) {
+ free(mem);
+ return -1;
+ }
+ mem = new;
+ }
+ r = read(fd, &mem[off], size - off);
+ if (r <= 0) {
+ if (!r)
+ break;
+ free(mem);
+ return -1;
+ }
+ off += (size_t)r;
+ }
+ size = off;
+ new = realloc(mem, size);
+ if (new)
+ mem = new;
+ errno = saved_errno;
+ } else {
+ mem = mmap(NULL, (size_t)st.st_size, PROT_READ, MAP_PRIVATE, fd, 0);
+ size = (size_t)st.st_size;
+ mmapped = 1;
+ }
+
+ if (libskrift_open_font_mem(fontp, mem, size)) {
+ if (mmapped)
+ munmap(mem, size);
+ else
+ free(mem);
+ return -1;
+ }
+
+ (*fontp)->memory_free = mmapped ? NULL : mem;
+ (*fontp)->memory_unmap = mmapped ? mem : NULL;
+ (*fontp)->memory_size = size;
+
+ return 0;
+}
diff --git a/libskrift_open_font_file.c b/libskrift_open_font_file.c
new file mode 100644
index 0000000..2a122ef
--- /dev/null
+++ b/libskrift_open_font_file.c
@@ -0,0 +1,18 @@
+/* See LICENSE file for copyright and license details. */
+#include "common.h"
+
+int
+libskrift_open_font_file(LIBSKRIFT_FONT **fontp, const char *path)
+{
+ *fontp = calloc(1, sizeof(**fontp));
+ if (!*fontp)
+ return -1;
+ (*fontp)->refcount = 1;
+ (*fontp)->font = sft_loadfile(path);
+ if (!(*fontp)->font) {
+ free(*fontp);
+ *fontp = NULL;
+ return -1;
+ }
+ return 0;
+}
diff --git a/libskrift_open_font_mem.c b/libskrift_open_font_mem.c
new file mode 100644
index 0000000..3828546
--- /dev/null
+++ b/libskrift_open_font_mem.c
@@ -0,0 +1,18 @@
+/* See LICENSE file for copyright and license details. */
+#include "common.h"
+
+int
+libskrift_open_font_mem(LIBSKRIFT_FONT **fontp, const void *mem, size_t size)
+{
+ *fontp = calloc(1, sizeof(**fontp));
+ if (!*fontp)
+ return -1;
+ (*fontp)->refcount = 1;
+ (*fontp)->font = sft_loadmem(mem, size);
+ if (!(*fontp)->font) {
+ free(*fontp);
+ *fontp = NULL;
+ return -1;
+ }
+ return 0;
+}
diff --git a/libskrift_points_to_pixels.c b/libskrift_points_to_pixels.c
new file mode 100644
index 0000000..3901175
--- /dev/null
+++ b/libskrift_points_to_pixels.c
@@ -0,0 +1,4 @@
+/* See LICENSE file for copyright and license details. */
+#include "common.h"
+
+extern inline double libskrift_points_to_pixels(double points, const struct libskrift_rendering *rendering);