## Summary **What is the goal of this PR?** Improved typesetting, including [kerning](https://en.wikipedia.org/wiki/Kerning) and [ligatures](https://en.wikipedia.org/wiki/Ligature_(writing)#Latin_alphabet). **What changes are included?** - The script to convert built-in fonts now adds kerning and ligature information to the generated font headers. - Epub page layout calculates proper kerning spaces and makes ligature substitutions according to the selected font.    ## Additional Context - I am not a typography expert. - The implementation has been reworked from the earlier version, so it is no longer necessary to omit Open Dyslexic, and kerning data now covers all fonts, styles, and codepoints for which we include bitmap data. - Claude Opus 4.6 helped with a lot of this. - There's an included test epub document with lots of kerning and ligature examples, shown in the photos. **_After some time to mature, I think this change is in decent shape to merge and get people testing._** After opening this PR I came across #660, which overlaps in adding ligature support. --- ### AI Usage While CrossPoint doesn't have restrictions on AI tools in contributing, please be transparent about their usage as it helps set the right context for reviewers. Did you use AI tools to help write this code? _**YES, Claude Opus 4.6**_ --------- Co-authored-by: Cursor <cursoragent@cursor.com>
70 lines
3.6 KiB
C++
70 lines
3.6 KiB
C++
// From
|
|
// https://github.com/vroland/epdiy/blob/c61e9e923ce2418150d54f88cea5d196cdc40c54/src/epd_internals.h
|
|
|
|
#pragma once
|
|
#include <cstdint>
|
|
|
|
/// Font data stored PER GLYPH
|
|
typedef struct {
|
|
uint8_t width; ///< Bitmap dimensions in pixels
|
|
uint8_t height; ///< Bitmap dimensions in pixels
|
|
uint8_t advanceX; ///< Distance to advance cursor (x axis)
|
|
int16_t left; ///< X dist from cursor pos to UL corner
|
|
int16_t top; ///< Y dist from cursor pos to UL corner
|
|
uint16_t dataLength; ///< Size of the font data.
|
|
uint32_t dataOffset; ///< Pointer into EpdFont->bitmap (or within-group offset for compressed fonts)
|
|
} EpdGlyph;
|
|
|
|
/// Compressed font group: a DEFLATE-compressed block of glyph bitmaps
|
|
typedef struct {
|
|
uint32_t compressedOffset; ///< Byte offset into compressed data array
|
|
uint32_t compressedSize; ///< Compressed DEFLATE stream size
|
|
uint32_t uncompressedSize; ///< Decompressed size
|
|
uint16_t glyphCount; ///< Number of glyphs in this group
|
|
uint16_t firstGlyphIndex; ///< First glyph index in the global glyph array
|
|
} EpdFontGroup;
|
|
|
|
/// Glyph interval structure
|
|
typedef struct {
|
|
uint32_t first; ///< The first unicode code point of the interval
|
|
uint32_t last; ///< The last unicode code point of the interval
|
|
uint32_t offset; ///< Index of the first code point into the glyph array
|
|
} EpdUnicodeInterval;
|
|
|
|
/// Maps a codepoint to a kerning class ID, sorted by codepoint for binary search.
|
|
/// Class IDs are 1-based; codepoints not in the table have implicit class 0 (no kerning).
|
|
typedef struct {
|
|
uint16_t codepoint; ///< Unicode codepoint
|
|
uint8_t classId; ///< 1-based kerning class ID
|
|
} __attribute__((packed)) EpdKernClassEntry;
|
|
|
|
/// Ligature substitution for a specific glyph pair, sorted by `pair` for binary search.
|
|
/// `pair` encodes (leftCodepoint << 16 | rightCodepoint) for single-key lookup.
|
|
typedef struct {
|
|
uint32_t pair; ///< Packed codepoint pair (left << 16 | right)
|
|
uint32_t ligatureCp; ///< Codepoint of the replacement ligature glyph
|
|
} __attribute__((packed)) EpdLigaturePair;
|
|
|
|
/// Data stored for FONT AS A WHOLE
|
|
typedef struct {
|
|
const uint8_t* bitmap; ///< Glyph bitmaps, concatenated
|
|
const EpdGlyph* glyph; ///< Glyph array
|
|
const EpdUnicodeInterval* intervals; ///< Valid unicode intervals for this font
|
|
uint32_t intervalCount; ///< Number of unicode intervals.
|
|
uint8_t advanceY; ///< Newline distance (y axis)
|
|
int ascender; ///< Maximal height of a glyph above the base line
|
|
int descender; ///< Maximal height of a glyph below the base line
|
|
bool is2Bit;
|
|
const EpdFontGroup* groups; ///< NULL for uncompressed fonts
|
|
uint16_t groupCount; ///< 0 for uncompressed fonts
|
|
const EpdKernClassEntry* kernLeftClasses; ///< Sorted left-side class map (nullptr if none)
|
|
const EpdKernClassEntry* kernRightClasses; ///< Sorted right-side class map (nullptr if none)
|
|
const int8_t* kernMatrix; ///< Flat leftClassCount x rightClassCount matrix
|
|
uint16_t kernLeftEntryCount; ///< Entries in kernLeftClasses
|
|
uint16_t kernRightEntryCount; ///< Entries in kernRightClasses
|
|
uint8_t kernLeftClassCount; ///< Number of distinct left classes (matrix rows)
|
|
uint8_t kernRightClassCount; ///< Number of distinct right classes (matrix cols)
|
|
const EpdLigaturePair* ligaturePairs; ///< Sorted ligature pair table (nullptr if none)
|
|
uint32_t ligaturePairCount; ///< Number of entries in ligaturePairs
|
|
} EpdFontData;
|