Clone in a terminal:
Commit 14ba70d
Johan Mattsson - 2022-04-11T18:44:25+00:00
14ba70d3121cfcdb0c83d24442a7bd7972e9bab2johan.mattsson.m@gmail.com

Suggestions for a new OTF table: FK fixed kerning

Changed files (15)

Diff

diff --git a/fixed-kerning-example/FK-example Mac.otf b/fixed-kerning-example/FK-example Mac.otfnew file mode 100644index 0000000..39d4596Binary files /dev/null and b/fixed-kerning-example/FK-example Mac.otf differdiff --git a/fixed-kerning-example/FK-example Mac.ttf b/fixed-kerning-example/FK-example Mac.ttfnew file mode 100644index 0000000..95415d8Binary files /dev/null and b/fixed-kerning-example/FK-example Mac.ttf differdiff --git a/fixed-kerning-example/FK-example.otf b/fixed-kerning-example/FK-example.otfnew file mode 100644index 0000000..39d4596Binary files /dev/null and b/fixed-kerning-example/FK-example.otf differdiff --git a/fixed-kerning-example/FK-example.ttf b/fixed-kerning-example/FK-example.ttfnew file mode 100644index 0000000..95415d8Binary files /dev/null and b/fixed-kerning-example/FK-example.ttf differdiff --git a/fixed-kerning-example/README.txt b/fixed-kerning-example/README.txtnew file mode 100644index 0000000..3b838d6--- /dev/null+++ b/fixed-kerning-example/README.txt@@ -0,0 +1,22 @@+The FK Table++This is a reference implementation of a new kerning table that stores fixed +point 16.16 values. The idea is to get better precision for kerning of OTF +and TTF fonts. The goal is also to create a solution that is is simple and fast.++The code is small and hopefully easy to understand. The parser needs around 200+lines of code and around 40 more lines are needed to add support for writing +the table.++Please ask if you need help or clarifications.++All code related to the FK feature in this folder is example code. Copy and+paste of this code into any project with any license is permitted.++An OTF parser only needs fk.c and fk.h. A font editor might also use+fk-font-editor.c and fk-font-editor.h. The file fk-test.c provides an +example of how the code could be used.++Regards+Johan Mattsson+johan.mattsson.m@gmail.comdiff --git a/fixed-kerning-example/build.sh b/fixed-kerning-example/build.shnew file mode 100755index 0000000..efdafe3--- /dev/null+++ b/fixed-kerning-example/build.sh@@ -0,0 +1,2 @@+#!/bin/sh+gcc *.c -o kerning-example -lmdiff --git a/fixed-kerning-example/clean.sh b/fixed-kerning-example/clean.shnew file mode 100755index 0000000..39421b5--- /dev/null+++ b/fixed-kerning-example/clean.sh@@ -0,0 +1,2 @@+#!/bin/sh+rm -f kerning-example fixedkerningtable.test basic.testdiff --git a/fixed-kerning-example/fk-example.birdfont b/fixed-kerning-example/fk-example.birdfontnew file mode 100644index 0000000..b0d65da--- /dev/null+++ b/fixed-kerning-example/fk-example.birdfont@@ -0,0 +1,100 @@+<?xml version="1.0" encoding="utf-8" standalone="yes"?>+<font>+<format>3.22</format>+<program version="4.27.2" os="*nix" />++<postscript_name>FK-example</postscript_name>+<name>FK-example</name>+<subfamily>Regular</subfamily>+<bold>false</bold>+<italic>false</italic>+<full_name>FK-example</full_name>+<unique_identifier>FK-example</unique_identifier>+<version>Version 1.0</version>+<description></description>+<copyright>SIL Open Font License</copyright>+<license>SIL Open Font License</license>+<license_url>http://scripts.sil.org/OFL</license_url>+<weight>400</weight>+<units_per_em>1024</units_per_em>+<trademark></trademark>+<manufacturer></manufacturer>+<designer></designer>+<vendor_url></vendor_url>+<vendor_id>Bird</vendor_id>+<designer_url></designer_url>+<underline_position>-10</underline_position>+<underline_thickness>3.5</underline_thickness>+<open_engraving>false</open_engraving>+<same_way_back_engraving>false</same_way_back_engraving>+<external_backgrunds>false</external_backgrunds>++<horizontal>+	<top_limit>78</top_limit>+	<top_position>72</top_position>+	<x-height>56</x-height>+	<base_line>0</base_line>+	<bottom_position>-20</bottom_position>+	<bottom_limit>-21</bottom_limit>+	<line_gap>10</line_gap>+</horizontal>++<grid width="1"/>+<grid width="2"/>+<grid width="4"/>++<background scale="1" />++<base_filename>FK-example</base_filename>+<base_filename_mac>FK-example Mac</base_filename_mac>+<export_ttf_font>true</export_ttf_font>+<export_otf_font>true</export_otf_font>+<export_eot_font>false</export_eot_font>+<export_svg_font>false</export_svg_font>+<export_colr_table>false</export_colr_table>+<export_svg_table>false</export_svg_table>+<optimize_otf_for_size>false</optimize_otf_for_size>+<optimize_otf_for_details>true</optimize_otf_for_details>++<default_foreground_color></default_foreground_color>++<svg_units_per_em>100</svg_units_per_em>+<svg_decimals>5</svg_decimals>++<collection unicode="U+20" svg="false" colr="false" auto_generate="false" meta_name="">+	<selected id="0"/>+	<glyph id="0" left="0" right="27">+	</glyph>+</collection>++<collection name=".notdef" svg="false" colr="false" auto_generate="false" meta_name="">+	<selected id="0"/>+	<glyph id="0" left="-20" right="33">+		<layer name="Layer" visible="true">+			<path data="S -15,10 L 15,10 L 15,62 M -15,62 M -15,10" />+			<path data="S -20,67 L 20,67 L 20,5 M -20,5 M -20,67" />+		</layer>+	</glyph>+</collection>++<collection unicode="U+41" svg="false" colr="false" auto_generate="false" meta_name="">+	<selected id="0"/>+	<glyph id="0" left="-28" right="26">+		<layer name="Layer" visible="true">+			<path data="S -28,0 L -2,72 L 26,0 L -28,0" />+		</layer>+	</glyph>+</collection>++<collection unicode="U+56" svg="false" colr="false" auto_generate="false" meta_name="">+	<selected id="1"/>+	<glyph id="1" left="-28" right="26">+		<layer name="Layer" visible="true">+			<path data="S 26,72 L -2,0 L -28,72 L 26,72" />+		</layer>+	</glyph>+</collection>++<kerning left="A" right="V" hadjustment="-15" />+<kerning left="V" right="A" hadjustment="-13" />+</font>diff --git a/fixed-kerning-example/fk-font-editor.c b/fixed-kerning-example/fk-font-editor.cnew file mode 100644index 0000000..c164714--- /dev/null+++ b/fixed-kerning-example/fk-font-editor.c@@ -0,0 +1,48 @@+/*+ * This is example code. Using this code in any project with any license is permitted.+ * Author: Johan Mattsson+ */+#include <stdint.h>+#include <stdio.h>+#include <stdbool.h>+#include <math.h>+#include <assert.h>+#include <stdlib.h>++#include "fk.h"++void fk_write_fixed_kerning_table_header (FILE* file, uint32_t num_kerning_pairs) {+   uint16_t version_upper = 1;+   uint16_t version_lower = 0;+   uint16_t pairs = num_kerning_pairs;+   +   fk_write_u16 (file, version_upper);+   fk_write_u16 (file, version_lower);+   fk_write_u32 (file, pairs);+}++Fixed16_16 fk_double_to_fixed (double v) {+   int32_t val;+   int32_t mant;+   +   val = (int32_t) floor (v);+   mant = (int32_t) floor (0x10000 * (v - val));+   val = (val << 16) | mant;++   return val;+}++void fk_write_fixed_kerning_entry (FILE* file, uint32_t glyph_index_first, uint32_t glyph_index_second, double kerning) {+   int32_t fixed_kerning;+   +   if (kerning > INT16_MAX || kerning < INT16_MIN) {+      fprintf(stderr, "Kerning is out of bounds.\n");+      kerning = 0;+   }+   +   fixed_kerning = fk_double_to_fixed (kerning);++   fk_write_u32 (file, glyph_index_first);+   fk_write_u32 (file, glyph_index_second);+   fk_write_u32 (file, fixed_kerning);+}diff --git a/fixed-kerning-example/fk-font-editor.h b/fixed-kerning-example/fk-font-editor.hnew file mode 100644index 0000000..67986c1--- /dev/null+++ b/fixed-kerning-example/fk-font-editor.h@@ -0,0 +1,14 @@+/*+ * This is example code. Using this code in any project with any license is permitted.+ * Author: Johan Mattsson+ */+#ifndef FILE_FK_FONT_EDITOR+#define FILE_FK_FONT_EDITOR++#include <stdio.h>+#include "fk.h"++void fk_write_fixed_kerning_table_header (FILE* file, uint32_t num_kerning_pairs);+void fk_write_fixed_kerning_entry (FILE* file, uint32_t glyh_index_first, uint32_t glyh_index_second, double kerning);++#endifdiff --git a/fixed-kerning-example/fk-test.c b/fixed-kerning-example/fk-test.cnew file mode 100644index 0000000..e9e7316--- /dev/null+++ b/fixed-kerning-example/fk-test.c@@ -0,0 +1,189 @@+/*+ * This is example code. Using this code in any project with any license is permitted.+ * Author: Johan Mattsson+ */+#include <assert.h>+#include "fk.h"+#include "fk-font-editor.h"++uint8_t* read_content (const char* file_name, size_t* buffer_size) {+   FILE* file;+   file = fopen (file_name, "rb");+   assert(file);+   +   fseek (file, 0, SEEK_END);+   size_t size = ftell (file);+   *buffer_size = size;+   rewind (file);+   +   uint8_t* table_buffer = (uint8_t*) malloc(size);+   assert (table_buffer);+   +   fread (table_buffer, size, sizeof (uint8_t), file);+   fclose (file);++   return table_buffer;+}++int test_write () {+   FILE* file;+   uint32_t num_pairs = 10;+   +   file = fopen ("fixedkerningtable.test", "wb");++   fk_write_fixed_kerning_table_header (file, num_pairs);+   +   for (uint32_t i = 0; i < num_pairs; i++) {+      uint32_t gid1 = i;+      uint32_t gid2 = i;+      double kerning = -0.2;++      gid1 = 0x45678abc;+      gid2 = 0x6EEEEEE2 + i;++      // all entries needs to be sorted on gid1 and gid2.+      fk_write_fixed_kerning_entry (file, gid1, gid2, kerning);+   }++   fclose (file);+   return 0;+}++void test_datatypes () {+   FILE* file;+   file = fopen ("basic.test", "wb");++   fk_write_u16 (file, 0x1234);+   fk_write_u32 (file, 0x567abcde);++   fclose (file);++   uint8_t* table_buffer = NULL;+   size_t buffer_size;+   table_buffer = read_content ("basic.test", &buffer_size);++   assert (fk_get_uint16 (table_buffer, buffer_size, 0) == 0x1234);+   assert (fk_get_uint32 (table_buffer, buffer_size, 2) == 0x567abcde);++   free (table_buffer);+}++void test_fixed_conversion () {+   Fixed16_16 f;+   double d;++   int32_t x = 0xffffcccc;+   assert (x == 0xffffcccc);++   f = fk_double_to_fixed (-0.2);+   assert (f == 0xffffcccc);++   f = fk_double_to_fixed (-0.1);+   assert (f == 0xffffe666);++   d = fk_fixed_to_double (0xffffcccc);+   assert ((int) (d * 1000) == -200);++   d = fk_fixed_to_double (0xffffe666);+   assert ((int) (d * 1000) == -100);+}++void test_read_all () {+   FILE* file;+   uint8_t* table_buffer = NULL;+   size_t buffer_size;+   uint32_t num_pairs;+   +   table_buffer = read_content ("fixedkerningtable.test", &buffer_size);+   +   bool has_data = fk_has_fixed_kerning (table_buffer, buffer_size);+   assert (has_data);+   +   num_pairs = fk_get_num_kerning_pairs(table_buffer, buffer_size);+   assert (num_pairs > 0);+   +   printf ("Table size : %zu\n", buffer_size);+   printf ("List all kerning pairs in table. Number of pairs: %d\n", num_pairs);+   +   for (uint32_t i = 0; i < num_pairs; i++) {+      uint32_t gid1 = 0;+      uint32_t gid2 = 0;+      Fixed16_16 kern = 0;+      +      fk_get_fixed_kerning_by_index (table_buffer, buffer_size, i, &gid1, &gid2, &kern);+      +      Fixed16_16 fixed_kerning;+      fixed_kerning = fk_get_fixed_kerning (table_buffer, buffer_size, gid1, gid2);+      double kerning = fk_fixed_to_double (fixed_kerning);+      +      printf ("Kerning for gid1: %d and gid2: %d, kerning %4.4f, (raw fixed: %d)\n", gid1, gid2, kerning, fixed_kerning);+      assert (kern == fixed_kerning);+   }+   +   printf ("Total pairs %d\n\n", num_pairs);+   +   free (table_buffer);+}++void test_order () {  +   uint8_t* table_buffer = NULL;+   size_t buffer_size;+   uint32_t num_pairs;+   +   table_buffer = read_content ("fixedkerningtable.test", &buffer_size);+   +   bool has_data = fk_has_fixed_kerning (table_buffer, buffer_size);+   assert (has_data);+   +   num_pairs = fk_get_num_kerning_pairs(table_buffer, buffer_size);+   assert (num_pairs > 0);+   +   uint32_t read_index = FIXED_KERNING_HEADER_SIZE;+   +   uint32_t last_gid1 = 0;+   uint32_t last_gid2 = 0;+   +   for (uint32_t i = 0; i < num_pairs; i++) {+      uint32_t gid1 = fk_get_uint32 (table_buffer, buffer_size, read_index);+      read_index += sizeof (uint32_t);++      uint32_t gid2 = fk_get_uint32 (table_buffer, buffer_size, read_index);+      read_index += sizeof (uint32_t);++      Fixed16_16 kern = fk_get_uint32 (table_buffer, buffer_size, read_index);+      read_index += sizeof (uint32_t);++      double kerning = fk_fixed_to_double (kern);++      printf ("Row: %d gid1: %x, gid2 %x, kerning: %.4f (raw kerning %d)\n", i, gid1, gid2, kerning, kern);++      bool is_sorted = false;++      if (gid1 != last_gid1) {+         is_sorted = gid1 >= last_gid1;+         last_gid2 = 0;+      } else {+         is_sorted = gid2 >= last_gid2;+      }+       +      last_gid1 = gid1;+      last_gid2 = gid2;++      assert (is_sorted);+      assert (gid1 == 0x45678abc);+      assert (gid2 == 0x6EEEEEE2 + i);+   }+   +   printf ("Total pairs %d\n\n", num_pairs);++   free (table_buffer);+}++int main () {+   test_fixed_conversion ();+   test_datatypes ();+   test_write ();+   test_read_all ();+   test_order ();+   return 0;+}diff --git a/fixed-kerning-example/fk.c b/fixed-kerning-example/fk.cnew file mode 100644index 0000000..f9866f9--- /dev/null+++ b/fixed-kerning-example/fk.c@@ -0,0 +1,181 @@+/*+ * This is example code. Using this code in any project with any license is permitted.+ * Author: Johan Mattsson+ */+#include <stdint.h>+#include <stdio.h>+#include <stdbool.h>+#include <math.h>+#include <assert.h>+#include <stdlib.h>++#include "fk.h"++uint8_t* fk_get_subtable_entries(uint8_t* otf_table, size_t table_size, size_t* subtable_size);++double fk_fixed_to_double (Fixed16_16 val) {+   return val / 65536.0;+}++uint16_t fk_get_uint16 (uint8_t* otf_table, size_t table_size, size_t index) {+	if (index + 1 >= table_size) {+		fprintf(stderr, "Index out of bounds: %zu Buffer size: %zu.\n", index, table_size);+		return 0;+	}+	+	uint16_t val = otf_table[index + 1] << 8;+	val |= otf_table[index];+	return val;+}++uint32_t fk_get_uint32 (uint8_t* otf_table, size_t table_size, size_t index) {+	if (index + 3 >= table_size) {+		fprintf(stderr, "Index out of bounds: %zu Buffer size: %zu.\n", index, table_size);+		return 0;+	}++   uint32_t val = fk_get_uint16 (otf_table, table_size, index) << 16;+   val |= fk_get_uint16 (otf_table, table_size, index + 2);++	return val;+}++void fk_write_u8 (FILE* file, uint8_t val) {+   fwrite(&val, 1, sizeof(uint8_t), file);+}++void fk_write_u16 (FILE* file, uint16_t val) {+   fk_write_u8 (file, (uint8_t) ((val & 0x00FF) >> (8 * 0)));+   fk_write_u8 (file, (uint8_t) ((val & 0xFF00) >> (8 * 1)));+}++void fk_write_u32 (FILE* file, uint32_t val) {+	uint16_t s = (uint16_t) (val >> 16);+   +   fk_write_u16 (file, (val & 0xFFFF0000) >> 16);+   fk_write_u16 (file, val & 0x0000FFFF);+}++uint32_t fk_get_int32 (uint8_t* otf_table, size_t table_size, size_t index) {+	return (uint32_t) fk_get_uint32 (otf_table, table_size, index);+}++/*+* This function ensures that we have data for the fixed kerning table. +* The get_fixed_kerning assumes that has_fixed_kerning has been used to+* check if we have a valid subtable with kerning entries.+*/+bool fk_has_fixed_kerning (uint8_t* otf_table, size_t table_size) {+   if (otf_table == NULL) {+      fprintf(stderr, "No table for fixed kerning.\n");+      return false;+   }+   +   if (table_size <= FIXED_KERNING_HEADER_SIZE) {+      // No data in table+      return false;+   }+   +   if (table_size % 4 != 0) {+      fprintf(stderr, "Bad padding in fixed kerning table, size: %zu.\n", table_size);+      return false;+   }+   +   uint16_t version_upper = fk_get_uint16(otf_table, table_size, 0);+   uint16_t version_lower = fk_get_uint16(otf_table, table_size, 2);+   +   if (version_upper != 1 || version_lower != 0) {+      fprintf(stderr,"Bad version of fixed kerning table. Expecting 1.0 got %d.%d\n", version_upper, version_lower);+      return false;+   }+   +   uint32_t entries = fk_get_num_kerning_pairs (otf_table, table_size);+   size_t subtable_size = table_size - FIXED_KERNING_HEADER_SIZE;+   size_t expected_subtable_size = entries * 3 * sizeof(uint32_t);+   +   if (subtable_size != expected_subtable_size) {+      fprintf(stderr,"Bad subtable size in fixed kerning table. Size %zu bytes expecting %zu bytes (added entries: %d).\n", subtable_size, expected_subtable_size, entries);+      return false;+   }+   +   return true;+}++Fixed16_16 fk_get_fixed_kerning (uint8_t* otf_table, size_t table_size, uint32_t first_glyph_index, uint32_t second_glyph_index) {+   uint32_t entries = fk_get_num_kerning_pairs(otf_table, table_size);+   size_t subtable_size = 0;+   uint8_t* subtable = fk_get_subtable_entries(otf_table, table_size, &subtable_size);++   int lower = 0;+   int upper = entries;+   const int second_gid_offset = sizeof(uint32_t); // offset from start of an kerning entry to the second glyph +   const int kerning_offset = 2 * sizeof(uint32_t); // offset from start of an kerning entry to the kerning value+   +   uint64_t gid1 = first_glyph_index;+   uint64_t gid2 = second_glyph_index;+   uint64_t search_key =  (gid1 << 32) | gid2;+   +   while (lower <= upper) {+      size_t middle = lower + (upper - lower) / 2;+      size_t index = middle;+      +      size_t row_index = index * 3 * sizeof(uint32_t);+      uint64_t pair_key = fk_get_uint32 (subtable, subtable_size, row_index);+      pair_key <<= 32;+      pair_key |= fk_get_uint32 (subtable, subtable_size, row_index + second_gid_offset);+      +      if (pair_key == search_key) {+         return (Fixed16_16) fk_get_uint32 (subtable, subtable_size, row_index + kerning_offset);+      }+      +      if (pair_key < search_key) {+         lower = middle + 1;+      } else {+         upper = middle - 1;+      }   +   }+   +   return 0;+}++uint8_t* fk_get_subtable_entries(uint8_t* otf_table, size_t table_size, size_t* subtable_size) {+   if (table_size <= FIXED_KERNING_HEADER_SIZE) {+      *subtable_size = 0;+      return NULL;+   }+   +   *subtable_size = table_size - FIXED_KERNING_HEADER_SIZE;+   +   return otf_table + FIXED_KERNING_HEADER_SIZE;+}++uint32_t fk_get_num_kerning_pairs (uint8_t* otf_table, size_t table_size) {+   if (table_size < FIXED_KERNING_HEADER_SIZE) {+      printf("Kerning table is too short.");+      return 0;+   }+   +   return fk_get_uint32 (otf_table, table_size, 4);+}++void fk_get_fixed_kerning_by_index (uint8_t* otf_table, size_t table_size, size_t index, uint32_t* first_glyph_index, uint32_t* second_glyph_index, Fixed16_16* kerning) {+   uint32_t num_pairs = fk_get_num_kerning_pairs (otf_table, table_size);+   size_t subtable_size = 0;+   uint8_t* subtable = fk_get_subtable_entries (otf_table, table_size, &subtable_size);+   +   index *= FIXED_KERNING_ENTRY_SIZE;+      +   if (index + FIXED_KERNING_ENTRY_SIZE > subtable_size) {+      fprintf (stderr, "Index out of bounds in fixed kerning table, index: %zu subtable size: %zu, item size: %ld\n", index, subtable_size, FIXED_KERNING_ENTRY_SIZE);+      *first_glyph_index = 0;+      *second_glyph_index = 0;+      *kerning = 0;+      return;+   }+   +   *first_glyph_index = fk_get_uint32 (subtable, subtable_size, index);+   *second_glyph_index = fk_get_uint32 (subtable, subtable_size, index + sizeof(uint32_t));++   int32_t fixed_kerning = fk_get_int32 (subtable, subtable_size, index + 2 * sizeof(uint32_t));+   *kerning = fixed_kerning;+}diff --git a/fixed-kerning-example/fk.h b/fixed-kerning-example/fk.hnew file mode 100644index 0000000..2d0f7ed--- /dev/null+++ b/fixed-kerning-example/fk.h@@ -0,0 +1,50 @@+/*+ * This is example code. Using this code in any project with any license is permitted.+ * Author: Johan Mattsson+ */+#ifndef FILE_FK_H+#define FILE_FK_H++#include <stdbool.h>+#include <stdint.h>+#include <stdlib.h>+#include <stdio.h>++typedef int32_t Fixed16_16;++#define FIXED_KERNING_HEADER_SIZE 8+#define FIXED_KERNING_ENTRY_SIZE (3 * sizeof (uint32_t))++/* Convert fixed 16.16 values to double. */+double fk_fixed_to_double (Fixed16_16 val);++Fixed16_16 fk_double_to_fixed (double v);++/*+ * All parser functions assumes that this function has been used to check if+ * the table is valid and we have kerning pairs in the subtable.+ */+bool fk_has_fixed_kerning (uint8_t* otf_table, size_t table_size);++uint32_t fk_get_num_kerning_pairs (uint8_t* otf_table, size_t table_size);++/*+ * A function that makes it possible iterate over all kerning pairs in the+ * table from 0 to fk_get_num_kerning_pairs.+ */+void fk_get_fixed_kerning_by_index (uint8_t* otf_table, size_t table_size, size_t index, uint32_t* first_glyph_index, uint32_t* second_glyph_index, Fixed16_16* kerning);++/* + * Get kerning for two gids, (glyph ID). The Fixed 16.16 value can be converted+ * to a floating point value with the with the function fixed_to_double.+ */+Fixed16_16 fk_get_fixed_kerning (uint8_t* otf_table, size_t table_size, uint32_t first_glyph_index, uint32_t second_glyph_index);++uint32_t fk_get_uint32 (uint8_t* otf_table, size_t table_size, size_t index);+uint16_t fk_get_uint16 (uint8_t* otf_table, size_t table_size, size_t index);++void fk_write_u8 (FILE* file, uint8_t val);+void fk_write_u16 (FILE* file, uint16_t val);+void fk_write_u32 (FILE* file, uint32_t val);++#endifdiff --git a/fixed-kerning-example/otf-fk-documentation.html b/fixed-kerning-example/otf-fk-documentation.htmlnew file mode 100644index 0000000..00bfb1a--- /dev/null+++ b/fixed-kerning-example/otf-fk-documentation.html@@ -0,0 +1,181 @@+<html>+<head>+<title>Fixed Point Kerning Table in OpenType fonts</title>++<link rel="preconnect" href="https://fonts.googleapis.com">+<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>+<link href="https://fonts.googleapis.com/css2?family=Roboto&display=swap" rel="stylesheet">+<link href="https://fonts.googleapis.com/css2?family=Roboto:wght@500&display=swap" rel="stylesheet">+<link href="https://fonts.googleapis.com/css2?family=Roboto+Mono:wght@100;400&display=swap" rel="stylesheet">++<style media="screen">+    .empty {+      height: 1em;+   }+   +   td {+      font-family: 'Roboto Mono', monospace;+      padding-right: 1em;+      font-size: 1em;+   }+   +   td.right {+      text-align: right;+   }+   +   body{+      font-family: 'Roboto', sans-serif;+   }+   +   pre {+      font-family: 'Roboto Mono', monospace;+      font-size: 1em;+   }++   section.main {+      width: 40em;+   }++   section.data {+      width: 60em;+   }++   h1 {+      font-size: 2em;+      font-family: 'Roboto', sans-serif;+      font-weight: 500;+      margin-bottom: 10px;+   }++   h2 {+      font-size: 1.6em;+      font-family: 'Roboto', sans-serif;+      font-weight: 500;+      margin-bottom: 10px;+   }++   pre {+      margin-top: 0px;+   }+</style>+</head>+<body>+<section class="main">++<h1>Fixed Point Kerning Table (FK, Version 1.0)</h1>+The FK table is a simple and efficient solution that makes it possible to store+kerning information with a high accuracy in OpenType fonts.++<h2>Names</h2>+The table name for horizontal kerning is "FK" and the table name for vertical +kerning is "FKV". The name should be padded up to four bytes with space in the+TTF and OTF lists of tables (just like the CFF table). Name: FK space space++<h2>Table</h2>+<pre>+uint16 version_upper = 1;+uint16 version_lower = 0;+uint32 num_kerning_pair_entries;+KerningEntry[]++KerningEntry+uint32 first_glyph_index+uint32 second_glyph_index+fixed16_16 kerning+</pre>++<p>Padding up to four bytes will not be needed because the table already is aligned.</p>++<p>The type fixed16_16 is a fixed point value (16.16) that uses 32 bits, similar to the 255 type in the CFF table.</p>++<p>All kerning entries needs to be sorted on the first and second glyph ID (GID).</p>+</section>++<section class="data">++<h2>Example data</h2>+<pre>+<table>+<tr>+   <td>Type</td>+   <td>Hex</td>+   <td>Decimal</td>+   <td>Description</td>+</tr>++<tr>+   <td>USHORT</td>+   <td>0x0001</td>+   <td class="right">1</td>+   <td>Major verison</td>+</tr>++<tr>+   <td>USHORT</td>+   <td>0x0000</td>+   <td class="right">0</td>+   <td>Minor version</td>+</tr>++<tr>+   <td>ULONG</td>+   <td>0x00000002</td>+   <td class="right">2</td>+   <td>Number of kerning pairs</td>+</tr>++<tr class="empty">+    <td colspan="4"></td>+</tr>++<tr>+   <td>ULONG</td>+   <td>0x00000007</td>+   <td class="right">7</td>+   <td>First GID of the pair (left in LTR languages)</td>+</tr>++<tr>+   <td>ULONG</td>+   <td>0x00000008</td>+   <td class="right">8</td>+   <td>Second GID of the pair (right in LTR languages)</td>+</tr>++<tr>+   <td>Fixed</td>+   <td>0xffffcccc</td>+   <td class="right">-0.2</td>+   <td>Kerning for this pair</td>+</tr>++<tr class="empty">+    <td colspan="4"></td>+</tr>++<tr>+   <td>ULONG</td>+   <td>0x0000000A</td>+   <td class="right">10</td>+   <td>First GID of the second  pair (left in LTR languages)</td>+</tr>++<tr>+   <td>ULONG</td>+   <td>0x0000000F</td>+   <td class="right">15</td>+   <td>Second GID of second the pair (right in LTR languages)</td>+</tr>++<tr>+   <td>Fixed</td>+   <td>0xffffe666</td>+   <td class="right">-0.1</td>+   <td>Kerning for this pair</td>+</tr>+</table>+</pre>++</section>+</body>+</html>diff --git a/fixed-kerning-example/run.sh b/fixed-kerning-example/run.shnew file mode 100755index 0000000..dc75b65--- /dev/null+++ b/fixed-kerning-example/run.sh@@ -0,0 +1,2 @@+#!/bin/sh+./kerning-example

Contact

Email: johan.mattsson.m@gmail.com
Mastodon: typo.social/@birdfont
GitHub: johanmattssonm
LinkedIn: johan-mattsson-2ab324112