ref: 2e10d81b777a344cc4d9fecaf50638ee29312428
parent: f5c1a9365b39548d8346227e72def71468bbf0ba
author: Sigrid Solveig Haflínudóttir <sigrid@ftrv.se>
date: Sun Jul 28 15:21:16 EDT 2024
poor person's subpixel rendering with a random FIR
--- a/otf.c.in
+++ b/otf.c.in
@@ -659,7 +659,7 @@
}
static Glyf *
-bitglyf(Otf *o, Glyf *g, double ppemX, double ppemY)
+bitglyf(Otf *o, Glyf *g, RasterOpts *opts)
{
BitmapGlyph b, best, want;
IndexSubtableRecord *isr;
@@ -667,8 +667,8 @@
IndexSubtable *is;
BitmapSize *bs;
- want.ppemX = ppemX;
- want.ppemY = ppemY;
+ want.ppemX = opts->ppemX;
+ want.ppemY = opts->ppemY;
found = 0;
r = -1;
@@ -770,7 +770,7 @@
}
Glyf *
-otfglyf(Otf *o, int index, double ppemX, double ppemY)
+otfglyf(Otf *o, int index, RasterOpts *opts)
{
int off, len, i;
Glyf *g;
@@ -839,7 +839,7 @@
goto err;
}
g->index = index;
- g = bitglyf(o, g, ppemX, ppemY);
+ g = bitglyf(o, g, opts);
}
return g;
--- a/otf.h.in
+++ b/otf.h.in
@@ -115,21 +115,50 @@
void otfprint(Otf *o, Otfile *out, int indent);
void otfclose(Otf *o);
-/* FIXME these might go eventually */
+/*
+ * FIXME this API *will* change, maybe not even once.
+ * atm it's just to get things going, for a start.
+ */
typedef struct Glyf Glyf;
typedef struct GlyfImage GlyfImage;
+typedef struct RasterOpts RasterOpts;
+typedef struct SubPx SubPx;
+enum {
+ GLYF_FMT_GREY_8,
+ GLYF_FMT_RGB_24,
+};
+
+enum {
+ LCD_RGB,
+};
+
struct GlyfImage {
int w;
int h;
int baseline;
+ int fmt;
u8int b[];
};
+struct SubPx {
+ struct {
+ u8int *c;
+ int nc;
+ }fir;
+ int lcd;
+};
+
+struct RasterOpts {
+ double ppemX;
+ double ppemY;
+ SubPx *subpx;
+};
+
int otfglyfnum(Otf *o);
int otfrune2glyph(Otf *o, Rune r);
Rune otfglyph2rune(Otf *o, int g);
-Glyf *otfglyf(Otf *o, int index, double ppemX, double ppemY);
-GlyfImage *otfdrawglyf(Otf *o, Glyf *g, double ppemX, double ppemY);
+Glyf *otfglyf(Otf *o, int index, RasterOpts *opts);
+GlyfImage *otfdrawglyf(Otf *o, Glyf *g, RasterOpts *opts);
--- a/otf.rkt
+++ b/otf.rkt
@@ -455,6 +455,103 @@
(mkcmplx FeatureList {uint16 featureCount} {FeatureRecord featureRecords [featureCount]})
+(mkcmplx SequenceLookup {uint16 sequenceIndex} {uint16 lookupListIndex})
+
+(mkcmplx ChainedSequenceRule
+ {uint16 backtrackGlyphCount}
+ {uint16 backtrackSequence [backtrackGlyphCount]}
+ {uint16 inputGlyphCount}
+ {uint16 inputSequence [- inputGlyphCount 1]}
+ {uint16 lookaheadGlyphCount}
+ {uint16 lookaheadSequence [lookaheadGlyphCount]}
+ {uint16 seqLookupCount}
+ {SequenceLookup seqLookupRecords [seqLookupCount]})
+
+(mkcmplx ChainedSequenceRuleSet
+ {uint16 chainedSeqRuleCount}
+ {Offset16 chainedSeqRuleOffsets [chainedSeqRuleCount]}
+ {ChainedSequenceRule chainedSeqRule [chainedSeqRuleCount] (at chainedSeqRuleOffsets)})
+
+(mkcmplx
+ ChainedSequenceContextFormat1
+ {Offset16 coverageOffset}
+ {uint16 chainedSeqRuleSetCount}
+ {Offset16 chainedSeqRuleSetOffsets [chainedSeqRuleSetCount]}
+ {ChainedSequenceRuleSet chainedSeqRuleSet [chainedSeqRuleSetCount] (at chainedSeqRuleSetOffsets)})
+
+(mkcmplx ChainedClassSequenceRuleSet
+ {uint16 chainedClassSeqRuleCount}
+ {Offset16 chainedClassSeqRuleOffsets [chainedClassSeqRuleCount]}
+ {ChainedSequenceRule
+ chainedClassSeqRules
+ [chainedClassSeqRuleCount]
+ (at chainedClassSeqRuleOffsets)})
+
+(mkcmplx ChainedSequenceContextFormat2
+ {Offset16 coverageOffset}
+ {Offset16 backtrackClassDefOffset}
+ {Offset16 inputClassDefOffset}
+ {Offset16 lookaheadClassDefOffset}
+ {uint16 chainedClassSeqRuleSetCount}
+ {Offset16 chainedClassSeqRuleSetOffsets [chainedClassSeqRuleSetCount]}
+ {ChainedClassSequenceRuleSet
+ chainedClassSequenceRuleSets
+ [chainedClassSeqRuleSetCount]
+ (at chainedClassSeqRuleSetOffsets)})
+
+(mkcmplx ChainedSequenceContextFormat3
+ {uint16 backtrackGlyphCount}
+ {Offset16 backtrackCoverageOffsets [backtrackGlyphCount]}
+ {uint16 inputGlyphCount}
+ {Offset16 inputCoverageOffsets [inputGlyphCount]}
+ {uint16 lookaheadGlyphCount}
+ {Offset16 lookaheadCoverageOffsets [lookaheadGlyphCount]}
+ {uint16 seqLookupCount}
+ {SequenceLookup seqLookupRecords [seqLookupCount]})
+
+(mkcmplx ChainedSequenceContext
+ {uint16 format}
+ ; FIXME union
+ {ChainedSequenceContextFormat1 f1 (== format 1)}
+ {ChainedSequenceContextFormat2 f2 (== format 2)}
+ {ChainedSequenceContextFormat3 f3 (== format 3)})
+
+(mkcmplx PairValue {uint16 secondGlyph} {ValueRecord valueRecord1} {ValueRecord valueRecord2})
+
+(mkcmplx PairSet {uint16 pairValueCount} {PairValue pairValueRecords [pairValueCount]})
+
+(mkcmplx PairPosFormat1
+ {uint16 pairSetCount}
+ {Offset16 pairSetOffsets [pairSetCount]}
+ {PairSet pairSets [pairSetCount] (at pairSetOffsets)})
+
+(mkcmplx Class2 {ValueRecord valueRecord1} {ValueRecord valueRecord2})
+
+(mkcmplx Class1 {Class2 class2Records [o->class2Count]})
+
+(mkcmplx PairPosFormat2
+ {Offset16 classDef1Offset}
+ {Offset16 classDef2Offset}
+ {uint16 class1Count}
+ {uint16 class2Count ->o}
+ {Class1 class1Records [class1Count]})
+
+(mkcmplx PairPos
+ {uint16 format (== 1 2)}
+ {Offset16 coverageOffset}
+ {uint16 valueFormat1}
+ {uint16 valueFormat2}
+ {PairPosFormat1 f1 (== format 1)}
+ {PairPosFormat2 f2 (== format 2)})
+
+(mkcmplx EntryExit {Offset16 entryAnchorOffset} {Offset16 exitAnchorOffset})
+
+(mkcmplx CursivePos
+ {uint16 format (== 1) unused}
+ {Offset16 coverageOffset}
+ {uint16 entryExitCount}
+ {EntryExit entryExitRecords [entryExitCount]})
+
(define lookupFlags
#hash((0 . LOOKUP_FL_RIGHT_TO_LEFT)
(1 . LOOKUP_FL_IGNORE_BASE_GLYPHS)
@@ -479,8 +576,29 @@
{uint16 lookupFlag hex (bits lookupFlags)}
{uint16 subTableCount}
{Offset16 subtableOffsets [subTableCount]}
- {uint16 markFilteringSet})
-; FIXME lookup subtable based on current table (GPOS vs GSUB) and lookupType
+ {uint16 markFilteringSet}
+ ; FIXME union
+ {SinglePos
+ singleAdjustment
+ [subTableCount]
+ (at subtableOffsets)
+ (== lookupType LOOKUP_TYPE_SINGLE_ADJUSTMENT)}
+ {PairPos
+ pairAdjustment
+ [subTableCount]
+ (at subtableOffsets)
+ (== lookupType LOOKUP_TYPE_PAIR_ADJUSTMENT)}
+ {CursivePos
+ cursiveAttachment
+ [subTableCount]
+ (at subtableOffsets)
+ (== lookupType LOOKUP_TYPE_CURSIVE_ATTACHMENT)}
+ ; FIXME add more
+ {ChainedSequenceContext
+ chainedContextsPositioning
+ [subTableCount]
+ (at subtableOffsets)
+ (== lookupType LOOKUP_TYPE_CHAINED_CONTEXTS_POSITIONING)})
(mkcmplx LookupList
{uint16 lookupCount}
--- a/plan9/otf.c
+++ b/plan9/otf.c
@@ -660,7 +660,7 @@
}
static Glyf *
-bitglyf(Otf *o, Glyf *g, double ppemX, double ppemY)
+bitglyf(Otf *o, Glyf *g, RasterOpts *opts)
{
BitmapGlyph b, best, want;
IndexSubtableRecord *isr;
@@ -668,8 +668,8 @@
IndexSubtable *is;
BitmapSize *bs;
- want.ppemX = ppemX;
- want.ppemY = ppemY;
+ want.ppemX = opts->ppemX;
+ want.ppemY = opts->ppemY;
found = 0;
r = -1;
@@ -771,7 +771,7 @@
}
Glyf *
-otfglyf(Otf *o, int index, double ppemX, double ppemY)
+otfglyf(Otf *o, int index, RasterOpts *opts)
{
int off, len, i;
Glyf *g;
@@ -840,7 +840,7 @@
goto err;
}
g->index = index;
- g = bitglyf(o, g, ppemX, ppemY);
+ g = bitglyf(o, g, opts);
}
return g;
@@ -3479,6 +3479,700 @@
}
int
+read_SequenceLookup(Otf *o, SequenceLookup *v)
+{
+ u8int *b = nil; USED(b);
+ if((b = otfreadn(o, 4)) == nil)
+ goto err;
+ v->sequenceIndex = b[0]<<8 | b[1];
+ v->lookupListIndex = b[2]<<8 | b[3];
+ return 0;
+err:
+ werrstr("%s: %r", "SequenceLookup");
+ return -1;
+}
+
+void
+print_SequenceLookup(Otfile *f, int indent, Otf *o, SequenceLookup *v)
+{
+ f->print(f->aux, "%*s%s: %ud\n", indent, "", "sequenceIndex", v->sequenceIndex);
+ f->print(f->aux, "%*s%s: %ud\n", indent, "", "lookupListIndex", v->lookupListIndex);
+ USED(o);
+}
+
+int
+read_ChainedSequenceRule(Otf *o, ChainedSequenceRule *v)
+{
+ u8int *b = nil; USED(b);
+ if((b = otfreadn(o, 2)) == nil)
+ goto err;
+ v->backtrackGlyphCount = b[0]<<8 | b[1];
+ if((b = otfreadn(o, v->backtrackGlyphCount*2)) == nil)
+ goto err;
+ v->backtrackSequence = malloc(v->backtrackGlyphCount*sizeof(*v->backtrackSequence));
+ for(int i = 0; i < v->backtrackGlyphCount; i++)
+ v->backtrackSequence[i] = b[0+i*2]<<8 | b[1+i*2];
+ if((b = otfreadn(o, 2)) == nil)
+ goto err;
+ v->inputGlyphCount = b[0]<<8 | b[1];
+ if((b = otfreadn(o, (v->inputGlyphCount-1)*2)) == nil)
+ goto err;
+ v->inputSequence = malloc((v->inputGlyphCount-1)*sizeof(*v->inputSequence));
+ for(int i = 0; i < (v->inputGlyphCount-1); i++)
+ v->inputSequence[i] = b[0+i*2]<<8 | b[1+i*2];
+ if((b = otfreadn(o, 2)) == nil)
+ goto err;
+ v->lookaheadGlyphCount = b[0]<<8 | b[1];
+ if((b = otfreadn(o, v->lookaheadGlyphCount*2)) == nil)
+ goto err;
+ v->lookaheadSequence = malloc(v->lookaheadGlyphCount*sizeof(*v->lookaheadSequence));
+ for(int i = 0; i < v->lookaheadGlyphCount; i++)
+ v->lookaheadSequence[i] = b[0+i*2]<<8 | b[1+i*2];
+ if((b = otfreadn(o, 2)) == nil)
+ goto err;
+ v->seqLookupCount = b[0]<<8 | b[1];
+ if(otfarray(o, &v->seqLookupRecords, read_SequenceLookup, sizeof(SequenceLookup), v->seqLookupCount) < 0){
+ werrstr("%s: %r", "seqLookupRecords");
+ goto err;
+ }
+ return 0;
+err:
+ werrstr("%s: %r", "ChainedSequenceRule");
+ return -1;
+}
+
+void
+print_ChainedSequenceRule(Otfile *f, int indent, Otf *o, ChainedSequenceRule *v)
+{
+ f->print(f->aux, "%*s%s: %ud\n", indent, "", "backtrackGlyphCount", v->backtrackGlyphCount);
+ for(int i = 0; i < v->backtrackGlyphCount; i++)
+ f->print(f->aux, "%*s%s[%d]: %ud\n", indent, "", "backtrackSequence", i, v->backtrackSequence[i]);
+ f->print(f->aux, "%*s%s: %ud\n", indent, "", "inputGlyphCount", v->inputGlyphCount);
+ for(int i = 0; i < (v->inputGlyphCount-1); i++)
+ f->print(f->aux, "%*s%s[%d]: %ud\n", indent, "", "inputSequence", i, v->inputSequence[i]);
+ f->print(f->aux, "%*s%s: %ud\n", indent, "", "lookaheadGlyphCount", v->lookaheadGlyphCount);
+ for(int i = 0; i < v->lookaheadGlyphCount; i++)
+ f->print(f->aux, "%*s%s[%d]: %ud\n", indent, "", "lookaheadSequence", i, v->lookaheadSequence[i]);
+ f->print(f->aux, "%*s%s: %ud\n", indent, "", "seqLookupCount", v->seqLookupCount);
+ for(int i = 0; i < v->seqLookupCount; i++){
+ f->print(f->aux, "%*s%s[%d]:\n", indent, "", "seqLookupRecords", i);
+ print_SequenceLookup(f, indent+indentΔ, o, &v->seqLookupRecords[i]);
+ }
+ USED(o);
+}
+
+int
+read_ChainedSequenceRuleSet(Otf *o, ChainedSequenceRuleSet *v)
+{
+ u8int *b = nil; USED(b);
+ if((b = otfreadn(o, 2)) == nil)
+ goto err;
+ v->chainedSeqRuleCount = b[0]<<8 | b[1];
+ if((b = otfreadn(o, v->chainedSeqRuleCount*2)) == nil)
+ goto err;
+ v->chainedSeqRuleOffsets = malloc(v->chainedSeqRuleCount*sizeof(*v->chainedSeqRuleOffsets));
+ for(int i = 0; i < v->chainedSeqRuleCount; i++)
+ v->chainedSeqRuleOffsets[i] = b[0+i*2]<<8 | b[1+i*2];
+ if(v->chainedSeqRuleCount > 0){
+ v->chainedSeqRule = calloc(v->chainedSeqRuleCount, sizeof(*v->chainedSeqRule));
+ for(int i = 0; i < v->chainedSeqRuleCount; i++){
+ if(v->chainedSeqRuleOffsets[i] == 0)
+ continue;
+ if(otfpushrange(o, v->chainedSeqRuleOffsets[i], -1))
+ goto err;
+ int r = read_ChainedSequenceRule(o, v->chainedSeqRule+i);
+ if(otfpoprange(o) < 0)
+ goto err;
+ if(r < 0){
+ memset(v->chainedSeqRule+i, 0, sizeof(*v->chainedSeqRule));
+ break;
+ }
+ }
+ }
+ return 0;
+err:
+ werrstr("%s: %r", "ChainedSequenceRuleSet");
+ return -1;
+}
+
+void
+print_ChainedSequenceRuleSet(Otfile *f, int indent, Otf *o, ChainedSequenceRuleSet *v)
+{
+ f->print(f->aux, "%*s%s: %ud\n", indent, "", "chainedSeqRuleCount", v->chainedSeqRuleCount);
+ for(int i = 0; i < v->chainedSeqRuleCount; i++)
+ f->print(f->aux, "%*s%s[%d]: %ud\n", indent, "", "chainedSeqRuleOffsets", i, v->chainedSeqRuleOffsets[i]);
+ for(int i = 0; i < v->chainedSeqRuleCount; i++){
+ f->print(f->aux, "%*s%s[%d]:\n", indent, "", "chainedSeqRule", i);
+ print_ChainedSequenceRule(f, indent+indentΔ, o, &v->chainedSeqRule[i]);
+ }
+ USED(o);
+}
+
+int
+read_ChainedSequenceContextFormat1(Otf *o, ChainedSequenceContextFormat1 *v)
+{
+ u8int *b = nil; USED(b);
+ if((b = otfreadn(o, 4)) == nil)
+ goto err;
+ v->coverageOffset = b[0]<<8 | b[1];
+ v->chainedSeqRuleSetCount = b[2]<<8 | b[3];
+ if((b = otfreadn(o, v->chainedSeqRuleSetCount*2)) == nil)
+ goto err;
+ v->chainedSeqRuleSetOffsets = malloc(v->chainedSeqRuleSetCount*sizeof(*v->chainedSeqRuleSetOffsets));
+ for(int i = 0; i < v->chainedSeqRuleSetCount; i++)
+ v->chainedSeqRuleSetOffsets[i] = b[0+i*2]<<8 | b[1+i*2];
+ if(v->chainedSeqRuleSetCount > 0){
+ v->chainedSeqRuleSet = calloc(v->chainedSeqRuleSetCount, sizeof(*v->chainedSeqRuleSet));
+ for(int i = 0; i < v->chainedSeqRuleSetCount; i++){
+ if(v->chainedSeqRuleSetOffsets[i] == 0)
+ continue;
+ if(otfpushrange(o, v->chainedSeqRuleSetOffsets[i], -1))
+ goto err;
+ int r = read_ChainedSequenceRuleSet(o, v->chainedSeqRuleSet+i);
+ if(otfpoprange(o) < 0)
+ goto err;
+ if(r < 0){
+ memset(v->chainedSeqRuleSet+i, 0, sizeof(*v->chainedSeqRuleSet));
+ break;
+ }
+ }
+ }
+ return 0;
+err:
+ werrstr("%s: %r", "ChainedSequenceContextFormat1");
+ return -1;
+}
+
+void
+print_ChainedSequenceContextFormat1(Otfile *f, int indent, Otf *o, ChainedSequenceContextFormat1 *v)
+{
+ f->print(f->aux, "%*s%s: %ud\n", indent, "", "coverageOffset", v->coverageOffset);
+ f->print(f->aux, "%*s%s: %ud\n", indent, "", "chainedSeqRuleSetCount", v->chainedSeqRuleSetCount);
+ for(int i = 0; i < v->chainedSeqRuleSetCount; i++)
+ f->print(f->aux, "%*s%s[%d]: %ud\n", indent, "", "chainedSeqRuleSetOffsets", i, v->chainedSeqRuleSetOffsets[i]);
+ for(int i = 0; i < v->chainedSeqRuleSetCount; i++){
+ f->print(f->aux, "%*s%s[%d]:\n", indent, "", "chainedSeqRuleSet", i);
+ print_ChainedSequenceRuleSet(f, indent+indentΔ, o, &v->chainedSeqRuleSet[i]);
+ }
+ USED(o);
+}
+
+int
+read_ChainedClassSequenceRuleSet(Otf *o, ChainedClassSequenceRuleSet *v)
+{
+ u8int *b = nil; USED(b);
+ if((b = otfreadn(o, 2)) == nil)
+ goto err;
+ v->chainedClassSeqRuleCount = b[0]<<8 | b[1];
+ if((b = otfreadn(o, v->chainedClassSeqRuleCount*2)) == nil)
+ goto err;
+ v->chainedClassSeqRuleOffsets = malloc(v->chainedClassSeqRuleCount*sizeof(*v->chainedClassSeqRuleOffsets));
+ for(int i = 0; i < v->chainedClassSeqRuleCount; i++)
+ v->chainedClassSeqRuleOffsets[i] = b[0+i*2]<<8 | b[1+i*2];
+ if(v->chainedClassSeqRuleCount > 0){
+ v->chainedClassSeqRules = calloc(v->chainedClassSeqRuleCount, sizeof(*v->chainedClassSeqRules));
+ for(int i = 0; i < v->chainedClassSeqRuleCount; i++){
+ if(v->chainedClassSeqRuleOffsets[i] == 0)
+ continue;
+ if(otfpushrange(o, v->chainedClassSeqRuleOffsets[i], -1))
+ goto err;
+ int r = read_ChainedSequenceRule(o, v->chainedClassSeqRules+i);
+ if(otfpoprange(o) < 0)
+ goto err;
+ if(r < 0){
+ memset(v->chainedClassSeqRules+i, 0, sizeof(*v->chainedClassSeqRules));
+ break;
+ }
+ }
+ }
+ return 0;
+err:
+ werrstr("%s: %r", "ChainedClassSequenceRuleSet");
+ return -1;
+}
+
+void
+print_ChainedClassSequenceRuleSet(Otfile *f, int indent, Otf *o, ChainedClassSequenceRuleSet *v)
+{
+ f->print(f->aux, "%*s%s: %ud\n", indent, "", "chainedClassSeqRuleCount", v->chainedClassSeqRuleCount);
+ for(int i = 0; i < v->chainedClassSeqRuleCount; i++)
+ f->print(f->aux, "%*s%s[%d]: %ud\n", indent, "", "chainedClassSeqRuleOffsets", i, v->chainedClassSeqRuleOffsets[i]);
+ for(int i = 0; i < v->chainedClassSeqRuleCount; i++){
+ f->print(f->aux, "%*s%s[%d]:\n", indent, "", "chainedClassSeqRules", i);
+ print_ChainedSequenceRule(f, indent+indentΔ, o, &v->chainedClassSeqRules[i]);
+ }
+ USED(o);
+}
+
+int
+read_ChainedSequenceContextFormat2(Otf *o, ChainedSequenceContextFormat2 *v)
+{
+ u8int *b = nil; USED(b);
+ if((b = otfreadn(o, 10)) == nil)
+ goto err;
+ v->coverageOffset = b[0]<<8 | b[1];
+ v->backtrackClassDefOffset = b[2]<<8 | b[3];
+ v->inputClassDefOffset = b[4]<<8 | b[5];
+ v->lookaheadClassDefOffset = b[6]<<8 | b[7];
+ v->chainedClassSeqRuleSetCount = b[8]<<8 | b[9];
+ if((b = otfreadn(o, v->chainedClassSeqRuleSetCount*2)) == nil)
+ goto err;
+ v->chainedClassSeqRuleSetOffsets = malloc(v->chainedClassSeqRuleSetCount*sizeof(*v->chainedClassSeqRuleSetOffsets));
+ for(int i = 0; i < v->chainedClassSeqRuleSetCount; i++)
+ v->chainedClassSeqRuleSetOffsets[i] = b[0+i*2]<<8 | b[1+i*2];
+ if(v->chainedClassSeqRuleSetCount > 0){
+ v->chainedClassSequenceRuleSets = calloc(v->chainedClassSeqRuleSetCount, sizeof(*v->chainedClassSequenceRuleSets));
+ for(int i = 0; i < v->chainedClassSeqRuleSetCount; i++){
+ if(v->chainedClassSeqRuleSetOffsets[i] == 0)
+ continue;
+ if(otfpushrange(o, v->chainedClassSeqRuleSetOffsets[i], -1))
+ goto err;
+ int r = read_ChainedClassSequenceRuleSet(o, v->chainedClassSequenceRuleSets+i);
+ if(otfpoprange(o) < 0)
+ goto err;
+ if(r < 0){
+ memset(v->chainedClassSequenceRuleSets+i, 0, sizeof(*v->chainedClassSequenceRuleSets));
+ break;
+ }
+ }
+ }
+ return 0;
+err:
+ werrstr("%s: %r", "ChainedSequenceContextFormat2");
+ return -1;
+}
+
+void
+print_ChainedSequenceContextFormat2(Otfile *f, int indent, Otf *o, ChainedSequenceContextFormat2 *v)
+{
+ f->print(f->aux, "%*s%s: %ud\n", indent, "", "coverageOffset", v->coverageOffset);
+ f->print(f->aux, "%*s%s: %ud\n", indent, "", "backtrackClassDefOffset", v->backtrackClassDefOffset);
+ f->print(f->aux, "%*s%s: %ud\n", indent, "", "inputClassDefOffset", v->inputClassDefOffset);
+ f->print(f->aux, "%*s%s: %ud\n", indent, "", "lookaheadClassDefOffset", v->lookaheadClassDefOffset);
+ f->print(f->aux, "%*s%s: %ud\n", indent, "", "chainedClassSeqRuleSetCount", v->chainedClassSeqRuleSetCount);
+ for(int i = 0; i < v->chainedClassSeqRuleSetCount; i++)
+ f->print(f->aux, "%*s%s[%d]: %ud\n", indent, "", "chainedClassSeqRuleSetOffsets", i, v->chainedClassSeqRuleSetOffsets[i]);
+ for(int i = 0; i < v->chainedClassSeqRuleSetCount; i++){
+ f->print(f->aux, "%*s%s[%d]:\n", indent, "", "chainedClassSequenceRuleSets", i);
+ print_ChainedClassSequenceRuleSet(f, indent+indentΔ, o, &v->chainedClassSequenceRuleSets[i]);
+ }
+ USED(o);
+}
+
+int
+read_ChainedSequenceContextFormat3(Otf *o, ChainedSequenceContextFormat3 *v)
+{
+ u8int *b = nil; USED(b);
+ if((b = otfreadn(o, 2)) == nil)
+ goto err;
+ v->backtrackGlyphCount = b[0]<<8 | b[1];
+ if((b = otfreadn(o, v->backtrackGlyphCount*2)) == nil)
+ goto err;
+ v->backtrackCoverageOffsets = malloc(v->backtrackGlyphCount*sizeof(*v->backtrackCoverageOffsets));
+ for(int i = 0; i < v->backtrackGlyphCount; i++)
+ v->backtrackCoverageOffsets[i] = b[0+i*2]<<8 | b[1+i*2];
+ if((b = otfreadn(o, 2)) == nil)
+ goto err;
+ v->inputGlyphCount = b[0]<<8 | b[1];
+ if((b = otfreadn(o, v->inputGlyphCount*2)) == nil)
+ goto err;
+ v->inputCoverageOffsets = malloc(v->inputGlyphCount*sizeof(*v->inputCoverageOffsets));
+ for(int i = 0; i < v->inputGlyphCount; i++)
+ v->inputCoverageOffsets[i] = b[0+i*2]<<8 | b[1+i*2];
+ if((b = otfreadn(o, 2)) == nil)
+ goto err;
+ v->lookaheadGlyphCount = b[0]<<8 | b[1];
+ if((b = otfreadn(o, v->lookaheadGlyphCount*2)) == nil)
+ goto err;
+ v->lookaheadCoverageOffsets = malloc(v->lookaheadGlyphCount*sizeof(*v->lookaheadCoverageOffsets));
+ for(int i = 0; i < v->lookaheadGlyphCount; i++)
+ v->lookaheadCoverageOffsets[i] = b[0+i*2]<<8 | b[1+i*2];
+ if((b = otfreadn(o, 2)) == nil)
+ goto err;
+ v->seqLookupCount = b[0]<<8 | b[1];
+ if(otfarray(o, &v->seqLookupRecords, read_SequenceLookup, sizeof(SequenceLookup), v->seqLookupCount) < 0){
+ werrstr("%s: %r", "seqLookupRecords");
+ goto err;
+ }
+ return 0;
+err:
+ werrstr("%s: %r", "ChainedSequenceContextFormat3");
+ return -1;
+}
+
+void
+print_ChainedSequenceContextFormat3(Otfile *f, int indent, Otf *o, ChainedSequenceContextFormat3 *v)
+{
+ f->print(f->aux, "%*s%s: %ud\n", indent, "", "backtrackGlyphCount", v->backtrackGlyphCount);
+ for(int i = 0; i < v->backtrackGlyphCount; i++)
+ f->print(f->aux, "%*s%s[%d]: %ud\n", indent, "", "backtrackCoverageOffsets", i, v->backtrackCoverageOffsets[i]);
+ f->print(f->aux, "%*s%s: %ud\n", indent, "", "inputGlyphCount", v->inputGlyphCount);
+ for(int i = 0; i < v->inputGlyphCount; i++)
+ f->print(f->aux, "%*s%s[%d]: %ud\n", indent, "", "inputCoverageOffsets", i, v->inputCoverageOffsets[i]);
+ f->print(f->aux, "%*s%s: %ud\n", indent, "", "lookaheadGlyphCount", v->lookaheadGlyphCount);
+ for(int i = 0; i < v->lookaheadGlyphCount; i++)
+ f->print(f->aux, "%*s%s[%d]: %ud\n", indent, "", "lookaheadCoverageOffsets", i, v->lookaheadCoverageOffsets[i]);
+ f->print(f->aux, "%*s%s: %ud\n", indent, "", "seqLookupCount", v->seqLookupCount);
+ for(int i = 0; i < v->seqLookupCount; i++){
+ f->print(f->aux, "%*s%s[%d]:\n", indent, "", "seqLookupRecords", i);
+ print_SequenceLookup(f, indent+indentΔ, o, &v->seqLookupRecords[i]);
+ }
+ USED(o);
+}
+
+int
+read_ChainedSequenceContext(Otf *o, ChainedSequenceContext *v)
+{
+ u8int *b = nil; USED(b);
+ if((b = otfreadn(o, 2)) == nil)
+ goto err;
+ v->format = b[0]<<8 | b[1];
+ if(v->format == 1){
+ if(read_ChainedSequenceContextFormat1(o, &v->f1) < 0){
+ werrstr("%s: %r", "f1");
+ goto err;
+ }
+ }
+ if(v->format == 2){
+ if(read_ChainedSequenceContextFormat2(o, &v->f2) < 0){
+ werrstr("%s: %r", "f2");
+ goto err;
+ }
+ }
+ if(v->format == 3){
+ if(read_ChainedSequenceContextFormat3(o, &v->f3) < 0){
+ werrstr("%s: %r", "f3");
+ goto err;
+ }
+ }
+ return 0;
+err:
+ werrstr("%s: %r", "ChainedSequenceContext");
+ return -1;
+}
+
+void
+print_ChainedSequenceContext(Otfile *f, int indent, Otf *o, ChainedSequenceContext *v)
+{
+ f->print(f->aux, "%*s%s: %ud\n", indent, "", "format", v->format);
+ if(v->format == 1){
+ f->print(f->aux, "%*s%s:\n", indent, "", "f1");
+ print_ChainedSequenceContextFormat1(f, indent+indentΔ, o, &v->f1);
+ }
+ if(v->format == 2){
+ f->print(f->aux, "%*s%s:\n", indent, "", "f2");
+ print_ChainedSequenceContextFormat2(f, indent+indentΔ, o, &v->f2);
+ }
+ if(v->format == 3){
+ f->print(f->aux, "%*s%s:\n", indent, "", "f3");
+ print_ChainedSequenceContextFormat3(f, indent+indentΔ, o, &v->f3);
+ }
+ USED(o);
+}
+
+int
+read_PairValue(Otf *o, PairValue *v)
+{
+ u8int *b = nil; USED(b);
+ if((b = otfreadn(o, 2)) == nil)
+ goto err;
+ v->secondGlyph = b[0]<<8 | b[1];
+ if(read_ValueRecord(o, &v->valueRecord1) < 0){
+ werrstr("%s: %r", "valueRecord1");
+ goto err;
+ }
+ if(read_ValueRecord(o, &v->valueRecord2) < 0){
+ werrstr("%s: %r", "valueRecord2");
+ goto err;
+ }
+ return 0;
+err:
+ werrstr("%s: %r", "PairValue");
+ return -1;
+}
+
+void
+print_PairValue(Otfile *f, int indent, Otf *o, PairValue *v)
+{
+ f->print(f->aux, "%*s%s: %ud\n", indent, "", "secondGlyph", v->secondGlyph);
+ f->print(f->aux, "%*s%s:\n", indent, "", "valueRecord1");
+ print_ValueRecord(f, indent+indentΔ, o, &v->valueRecord1);
+ f->print(f->aux, "%*s%s:\n", indent, "", "valueRecord2");
+ print_ValueRecord(f, indent+indentΔ, o, &v->valueRecord2);
+ USED(o);
+}
+
+int
+read_PairSet(Otf *o, PairSet *v)
+{
+ u8int *b = nil; USED(b);
+ if((b = otfreadn(o, 2)) == nil)
+ goto err;
+ v->pairValueCount = b[0]<<8 | b[1];
+ if(otfarray(o, &v->pairValueRecords, read_PairValue, sizeof(PairValue), v->pairValueCount) < 0){
+ werrstr("%s: %r", "pairValueRecords");
+ goto err;
+ }
+ return 0;
+err:
+ werrstr("%s: %r", "PairSet");
+ return -1;
+}
+
+void
+print_PairSet(Otfile *f, int indent, Otf *o, PairSet *v)
+{
+ f->print(f->aux, "%*s%s: %ud\n", indent, "", "pairValueCount", v->pairValueCount);
+ for(int i = 0; i < v->pairValueCount; i++){
+ f->print(f->aux, "%*s%s[%d]:\n", indent, "", "pairValueRecords", i);
+ print_PairValue(f, indent+indentΔ, o, &v->pairValueRecords[i]);
+ }
+ USED(o);
+}
+
+int
+read_PairPosFormat1(Otf *o, PairPosFormat1 *v)
+{
+ u8int *b = nil; USED(b);
+ if((b = otfreadn(o, 2)) == nil)
+ goto err;
+ v->pairSetCount = b[0]<<8 | b[1];
+ if((b = otfreadn(o, v->pairSetCount*2)) == nil)
+ goto err;
+ v->pairSetOffsets = malloc(v->pairSetCount*sizeof(*v->pairSetOffsets));
+ for(int i = 0; i < v->pairSetCount; i++)
+ v->pairSetOffsets[i] = b[0+i*2]<<8 | b[1+i*2];
+ if(v->pairSetCount > 0){
+ v->pairSets = calloc(v->pairSetCount, sizeof(*v->pairSets));
+ for(int i = 0; i < v->pairSetCount; i++){
+ if(v->pairSetOffsets[i] == 0)
+ continue;
+ if(otfpushrange(o, v->pairSetOffsets[i], -1))
+ goto err;
+ int r = read_PairSet(o, v->pairSets+i);
+ if(otfpoprange(o) < 0)
+ goto err;
+ if(r < 0){
+ memset(v->pairSets+i, 0, sizeof(*v->pairSets));
+ break;
+ }
+ }
+ }
+ return 0;
+err:
+ werrstr("%s: %r", "PairPosFormat1");
+ return -1;
+}
+
+void
+print_PairPosFormat1(Otfile *f, int indent, Otf *o, PairPosFormat1 *v)
+{
+ f->print(f->aux, "%*s%s: %ud\n", indent, "", "pairSetCount", v->pairSetCount);
+ for(int i = 0; i < v->pairSetCount; i++)
+ f->print(f->aux, "%*s%s[%d]: %ud\n", indent, "", "pairSetOffsets", i, v->pairSetOffsets[i]);
+ for(int i = 0; i < v->pairSetCount; i++){
+ f->print(f->aux, "%*s%s[%d]:\n", indent, "", "pairSets", i);
+ print_PairSet(f, indent+indentΔ, o, &v->pairSets[i]);
+ }
+ USED(o);
+}
+
+int
+read_Class2(Otf *o, Class2 *v)
+{
+ u8int *b = nil; USED(b);
+ if(read_ValueRecord(o, &v->valueRecord1) < 0){
+ werrstr("%s: %r", "valueRecord1");
+ goto err;
+ }
+ if(read_ValueRecord(o, &v->valueRecord2) < 0){
+ werrstr("%s: %r", "valueRecord2");
+ goto err;
+ }
+ return 0;
+err:
+ werrstr("%s: %r", "Class2");
+ return -1;
+}
+
+void
+print_Class2(Otfile *f, int indent, Otf *o, Class2 *v)
+{
+ f->print(f->aux, "%*s%s:\n", indent, "", "valueRecord1");
+ print_ValueRecord(f, indent+indentΔ, o, &v->valueRecord1);
+ f->print(f->aux, "%*s%s:\n", indent, "", "valueRecord2");
+ print_ValueRecord(f, indent+indentΔ, o, &v->valueRecord2);
+ USED(o);
+}
+
+int
+read_Class1(Otf *o, Class1 *v)
+{
+ u8int *b = nil; USED(b);
+ if(otfarray(o, &v->class2Records, read_Class2, sizeof(Class2), o->class2Count) < 0){
+ werrstr("%s: %r", "class2Records");
+ goto err;
+ }
+ return 0;
+err:
+ werrstr("%s: %r", "Class1");
+ return -1;
+}
+
+void
+print_Class1(Otfile *f, int indent, Otf *o, Class1 *v)
+{
+ for(int i = 0; i < o->class2Count; i++){
+ f->print(f->aux, "%*s%s[%d]:\n", indent, "", "class2Records", i);
+ print_Class2(f, indent+indentΔ, o, &v->class2Records[i]);
+ }
+ USED(o);
+}
+
+int
+read_PairPosFormat2(Otf *o, PairPosFormat2 *v)
+{
+ u8int *b = nil; USED(b);
+ if((b = otfreadn(o, 8)) == nil)
+ goto err;
+ v->classDef1Offset = b[0]<<8 | b[1];
+ v->classDef2Offset = b[2]<<8 | b[3];
+ v->class1Count = b[4]<<8 | b[5];
+ v->class2Count = b[6]<<8 | b[7];
+ o->class2Count = v->class2Count;
+ if(otfarray(o, &v->class1Records, read_Class1, sizeof(Class1), v->class1Count) < 0){
+ werrstr("%s: %r", "class1Records");
+ goto err;
+ }
+ return 0;
+err:
+ werrstr("%s: %r", "PairPosFormat2");
+ return -1;
+}
+
+void
+print_PairPosFormat2(Otfile *f, int indent, Otf *o, PairPosFormat2 *v)
+{
+ f->print(f->aux, "%*s%s: %ud\n", indent, "", "classDef1Offset", v->classDef1Offset);
+ f->print(f->aux, "%*s%s: %ud\n", indent, "", "classDef2Offset", v->classDef2Offset);
+ f->print(f->aux, "%*s%s: %ud\n", indent, "", "class1Count", v->class1Count);
+ f->print(f->aux, "%*s%s: %ud\n", indent, "", "class2Count", v->class2Count);
+ o->class2Count = v->class2Count;
+ for(int i = 0; i < v->class1Count; i++){
+ f->print(f->aux, "%*s%s[%d]:\n", indent, "", "class1Records", i);
+ print_Class1(f, indent+indentΔ, o, &v->class1Records[i]);
+ }
+ USED(o);
+}
+
+int
+read_PairPos(Otf *o, PairPos *v)
+{
+ u8int *b = nil; USED(b);
+ if((b = otfreadn(o, 8)) == nil)
+ goto err;
+ v->format = b[0]<<8 | b[1];
+ if(v->format != 1 && v->format != 2){
+ werrstr("%s: invalid value: %d (%#ux)", "format", v->format, v->format);
+ goto err;
+ }
+ v->coverageOffset = b[2]<<8 | b[3];
+ v->valueFormat1 = b[4]<<8 | b[5];
+ v->valueFormat2 = b[6]<<8 | b[7];
+ if(v->format == 1){
+ if(read_PairPosFormat1(o, &v->f1) < 0){
+ werrstr("%s: %r", "f1");
+ goto err;
+ }
+ }
+ if(v->format == 2){
+ if(read_PairPosFormat2(o, &v->f2) < 0){
+ werrstr("%s: %r", "f2");
+ goto err;
+ }
+ }
+ return 0;
+err:
+ werrstr("%s: %r", "PairPos");
+ return -1;
+}
+
+void
+print_PairPos(Otfile *f, int indent, Otf *o, PairPos *v)
+{
+ f->print(f->aux, "%*s%s: %ud\n", indent, "", "format", v->format);
+ f->print(f->aux, "%*s%s: %ud\n", indent, "", "coverageOffset", v->coverageOffset);
+ f->print(f->aux, "%*s%s: %ud\n", indent, "", "valueFormat1", v->valueFormat1);
+ f->print(f->aux, "%*s%s: %ud\n", indent, "", "valueFormat2", v->valueFormat2);
+ if(v->format == 1){
+ f->print(f->aux, "%*s%s:\n", indent, "", "f1");
+ print_PairPosFormat1(f, indent+indentΔ, o, &v->f1);
+ }
+ if(v->format == 2){
+ f->print(f->aux, "%*s%s:\n", indent, "", "f2");
+ print_PairPosFormat2(f, indent+indentΔ, o, &v->f2);
+ }
+ USED(o);
+}
+
+int
+read_EntryExit(Otf *o, EntryExit *v)
+{
+ u8int *b = nil; USED(b);
+ if((b = otfreadn(o, 4)) == nil)
+ goto err;
+ v->entryAnchorOffset = b[0]<<8 | b[1];
+ v->exitAnchorOffset = b[2]<<8 | b[3];
+ return 0;
+err:
+ werrstr("%s: %r", "EntryExit");
+ return -1;
+}
+
+void
+print_EntryExit(Otfile *f, int indent, Otf *o, EntryExit *v)
+{
+ f->print(f->aux, "%*s%s: %ud\n", indent, "", "entryAnchorOffset", v->entryAnchorOffset);
+ f->print(f->aux, "%*s%s: %ud\n", indent, "", "exitAnchorOffset", v->exitAnchorOffset);
+ USED(o);
+}
+
+int
+read_CursivePos(Otf *o, CursivePos *v)
+{
+ u8int *b = nil; USED(b);
+ if((b = otfreadn(o, 6)) == nil)
+ goto err;
+ u16int format = b[0]<<8 | b[1];
+ if(format != 1){
+ werrstr("%s: invalid value: %d (%#ux)", "format", format, format);
+ goto err;
+ }
+ v->coverageOffset = b[2]<<8 | b[3];
+ v->entryExitCount = b[4]<<8 | b[5];
+ if(otfarray(o, &v->entryExitRecords, read_EntryExit, sizeof(EntryExit), v->entryExitCount) < 0){
+ werrstr("%s: %r", "entryExitRecords");
+ goto err;
+ }
+ return 0;
+err:
+ werrstr("%s: %r", "CursivePos");
+ return -1;
+}
+
+void
+print_CursivePos(Otfile *f, int indent, Otf *o, CursivePos *v)
+{
+ f->print(f->aux, "%*s%s: %ud\n", indent, "", "coverageOffset", v->coverageOffset);
+ f->print(f->aux, "%*s%s: %ud\n", indent, "", "entryExitCount", v->entryExitCount);
+ for(int i = 0; i < v->entryExitCount; i++){
+ f->print(f->aux, "%*s%s[%d]:\n", indent, "", "entryExitRecords", i);
+ print_EntryExit(f, indent+indentΔ, o, &v->entryExitRecords[i]);
+ }
+ USED(o);
+}
+
+int
read_Lookup(Otf *o, Lookup *v)
{
u8int *b = nil; USED(b);
@@ -3495,6 +4189,78 @@
if((b = otfreadn(o, 2)) == nil)
goto err;
v->markFilteringSet = b[0]<<8 | b[1];
+ if((v->lookupType==LOOKUP_TYPE_SINGLE_ADJUSTMENT)){
+ if(v->subTableCount > 0){
+ v->singleAdjustment = calloc(v->subTableCount, sizeof(*v->singleAdjustment));
+ for(int i = 0; i < v->subTableCount; i++){
+ if(v->subtableOffsets[i] == 0)
+ continue;
+ if(otfpushrange(o, v->subtableOffsets[i], -1))
+ goto err;
+ int r = read_SinglePos(o, v->singleAdjustment+i);
+ if(otfpoprange(o) < 0)
+ goto err;
+ if(r < 0){
+ memset(v->singleAdjustment+i, 0, sizeof(*v->singleAdjustment));
+ break;
+ }
+ }
+ }
+ }
+ if((v->lookupType==LOOKUP_TYPE_PAIR_ADJUSTMENT)){
+ if(v->subTableCount > 0){
+ v->pairAdjustment = calloc(v->subTableCount, sizeof(*v->pairAdjustment));
+ for(int i = 0; i < v->subTableCount; i++){
+ if(v->subtableOffsets[i] == 0)
+ continue;
+ if(otfpushrange(o, v->subtableOffsets[i], -1))
+ goto err;
+ int r = read_PairPos(o, v->pairAdjustment+i);
+ if(otfpoprange(o) < 0)
+ goto err;
+ if(r < 0){
+ memset(v->pairAdjustment+i, 0, sizeof(*v->pairAdjustment));
+ break;
+ }
+ }
+ }
+ }
+ if((v->lookupType==LOOKUP_TYPE_CURSIVE_ATTACHMENT)){
+ if(v->subTableCount > 0){
+ v->cursiveAttachment = calloc(v->subTableCount, sizeof(*v->cursiveAttachment));
+ for(int i = 0; i < v->subTableCount; i++){
+ if(v->subtableOffsets[i] == 0)
+ continue;
+ if(otfpushrange(o, v->subtableOffsets[i], -1))
+ goto err;
+ int r = read_CursivePos(o, v->cursiveAttachment+i);
+ if(otfpoprange(o) < 0)
+ goto err;
+ if(r < 0){
+ memset(v->cursiveAttachment+i, 0, sizeof(*v->cursiveAttachment));
+ break;
+ }
+ }
+ }
+ }
+ if((v->lookupType==LOOKUP_TYPE_CHAINED_CONTEXTS_POSITIONING)){
+ if(v->subTableCount > 0){
+ v->chainedContextsPositioning = calloc(v->subTableCount, sizeof(*v->chainedContextsPositioning));
+ for(int i = 0; i < v->subTableCount; i++){
+ if(v->subtableOffsets[i] == 0)
+ continue;
+ if(otfpushrange(o, v->subtableOffsets[i], -1))
+ goto err;
+ int r = read_ChainedSequenceContext(o, v->chainedContextsPositioning+i);
+ if(otfpoprange(o) < 0)
+ goto err;
+ if(r < 0){
+ memset(v->chainedContextsPositioning+i, 0, sizeof(*v->chainedContextsPositioning));
+ break;
+ }
+ }
+ }
+ }
return 0;
err:
werrstr("%s: %r", "Lookup");
@@ -3510,6 +4276,30 @@
for(int i = 0; i < v->subTableCount; i++)
f->print(f->aux, "%*s%s[%d]: %ud\n", indent, "", "subtableOffsets", i, v->subtableOffsets[i]);
f->print(f->aux, "%*s%s: %ud\n", indent, "", "markFilteringSet", v->markFilteringSet);
+ if((v->lookupType==LOOKUP_TYPE_SINGLE_ADJUSTMENT)){
+ for(int i = 0; i < v->subTableCount; i++){
+ f->print(f->aux, "%*s%s[%d]:\n", indent, "", "singleAdjustment", i);
+ print_SinglePos(f, indent+indentΔ, o, &v->singleAdjustment[i]);
+ }
+ }
+ if((v->lookupType==LOOKUP_TYPE_PAIR_ADJUSTMENT)){
+ for(int i = 0; i < v->subTableCount; i++){
+ f->print(f->aux, "%*s%s[%d]:\n", indent, "", "pairAdjustment", i);
+ print_PairPos(f, indent+indentΔ, o, &v->pairAdjustment[i]);
+ }
+ }
+ if((v->lookupType==LOOKUP_TYPE_CURSIVE_ATTACHMENT)){
+ for(int i = 0; i < v->subTableCount; i++){
+ f->print(f->aux, "%*s%s[%d]:\n", indent, "", "cursiveAttachment", i);
+ print_CursivePos(f, indent+indentΔ, o, &v->cursiveAttachment[i]);
+ }
+ }
+ if((v->lookupType==LOOKUP_TYPE_CHAINED_CONTEXTS_POSITIONING)){
+ for(int i = 0; i < v->subTableCount; i++){
+ f->print(f->aux, "%*s%s[%d]:\n", indent, "", "chainedContextsPositioning", i);
+ print_ChainedSequenceContext(f, indent+indentΔ, o, &v->chainedContextsPositioning[i]);
+ }
+ }
USED(o);
}
--- a/plan9/otf.h
+++ b/plan9/otf.h
@@ -116,24 +116,53 @@
void otfprint(Otf *o, Otfile *out, int indent);
void otfclose(Otf *o);
-/* FIXME these might go eventually */
+/*
+ * FIXME this API *will* change, maybe not even once.
+ * atm it's just to get things going, for a start.
+ */
typedef struct Glyf Glyf;
typedef struct GlyfImage GlyfImage;
+typedef struct RasterOpts RasterOpts;
+typedef struct SubPx SubPx;
+enum {
+ GLYF_FMT_GREY_8,
+ GLYF_FMT_RGB_24,
+};
+
+enum {
+ LCD_RGB,
+};
+
struct GlyfImage {
int w;
int h;
int baseline;
+ int fmt;
u8int b[];
};
+struct SubPx {
+ struct {
+ u8int *c;
+ int nc;
+ }fir;
+ int lcd;
+};
+
+struct RasterOpts {
+ double ppemX;
+ double ppemY;
+ SubPx *subpx;
+};
+
int otfglyfnum(Otf *o);
int otfrune2glyph(Otf *o, Rune r);
Rune otfglyph2rune(Otf *o, int g);
-Glyf *otfglyf(Otf *o, int index, double ppemX, double ppemY);
-GlyfImage *otfdrawglyf(Otf *o, Glyf *g, double ppemX, double ppemY);
+Glyf *otfglyf(Otf *o, int index, RasterOpts *opts);
+GlyfImage *otfdrawglyf(Otf *o, Glyf *g, RasterOpts *opts);
typedef struct SubHeader SubHeader;
typedef struct MapGroup MapGroup;
@@ -194,6 +223,23 @@
typedef struct Feature Feature;
typedef struct FeatureRecord FeatureRecord;
typedef struct FeatureList FeatureList;
+typedef struct SequenceLookup SequenceLookup;
+typedef struct ChainedSequenceRule ChainedSequenceRule;
+typedef struct ChainedSequenceRuleSet ChainedSequenceRuleSet;
+typedef struct ChainedSequenceContextFormat1 ChainedSequenceContextFormat1;
+typedef struct ChainedClassSequenceRuleSet ChainedClassSequenceRuleSet;
+typedef struct ChainedSequenceContextFormat2 ChainedSequenceContextFormat2;
+typedef struct ChainedSequenceContextFormat3 ChainedSequenceContextFormat3;
+typedef struct ChainedSequenceContext ChainedSequenceContext;
+typedef struct PairValue PairValue;
+typedef struct PairSet PairSet;
+typedef struct PairPosFormat1 PairPosFormat1;
+typedef struct Class2 Class2;
+typedef struct Class1 Class1;
+typedef struct PairPosFormat2 PairPosFormat2;
+typedef struct PairPos PairPos;
+typedef struct EntryExit EntryExit;
+typedef struct CursivePos CursivePos;
typedef struct Lookup Lookup;
typedef struct LookupList LookupList;
typedef struct TableGPOS TableGPOS;
@@ -918,6 +964,175 @@
int read_FeatureList(Otf *o, FeatureList *v);
void print_FeatureList(Otfile *f, int indent, Otf *o, FeatureList *v);
+struct SequenceLookup {
+ u16int sequenceIndex;
+ u16int lookupListIndex;
+};
+
+int read_SequenceLookup(Otf *o, SequenceLookup *v);
+void print_SequenceLookup(Otfile *f, int indent, Otf *o, SequenceLookup *v);
+
+struct ChainedSequenceRule {
+ u16int backtrackGlyphCount;
+ u16int *backtrackSequence;
+ u16int inputGlyphCount;
+ u16int *inputSequence;
+ u16int lookaheadGlyphCount;
+ u16int *lookaheadSequence;
+ u16int seqLookupCount;
+ SequenceLookup *seqLookupRecords;
+};
+
+int read_ChainedSequenceRule(Otf *o, ChainedSequenceRule *v);
+void print_ChainedSequenceRule(Otfile *f, int indent, Otf *o, ChainedSequenceRule *v);
+
+struct ChainedSequenceRuleSet {
+ u16int chainedSeqRuleCount;
+ u16int *chainedSeqRuleOffsets;
+ ChainedSequenceRule *chainedSeqRule;
+};
+
+int read_ChainedSequenceRuleSet(Otf *o, ChainedSequenceRuleSet *v);
+void print_ChainedSequenceRuleSet(Otfile *f, int indent, Otf *o, ChainedSequenceRuleSet *v);
+
+struct ChainedSequenceContextFormat1 {
+ u16int coverageOffset;
+ u16int chainedSeqRuleSetCount;
+ u16int *chainedSeqRuleSetOffsets;
+ ChainedSequenceRuleSet *chainedSeqRuleSet;
+};
+
+int read_ChainedSequenceContextFormat1(Otf *o, ChainedSequenceContextFormat1 *v);
+void print_ChainedSequenceContextFormat1(Otfile *f, int indent, Otf *o, ChainedSequenceContextFormat1 *v);
+
+struct ChainedClassSequenceRuleSet {
+ u16int chainedClassSeqRuleCount;
+ u16int *chainedClassSeqRuleOffsets;
+ ChainedSequenceRule *chainedClassSeqRules;
+};
+
+int read_ChainedClassSequenceRuleSet(Otf *o, ChainedClassSequenceRuleSet *v);
+void print_ChainedClassSequenceRuleSet(Otfile *f, int indent, Otf *o, ChainedClassSequenceRuleSet *v);
+
+struct ChainedSequenceContextFormat2 {
+ u16int coverageOffset;
+ u16int backtrackClassDefOffset;
+ u16int inputClassDefOffset;
+ u16int lookaheadClassDefOffset;
+ u16int chainedClassSeqRuleSetCount;
+ u16int *chainedClassSeqRuleSetOffsets;
+ ChainedClassSequenceRuleSet *chainedClassSequenceRuleSets;
+};
+
+int read_ChainedSequenceContextFormat2(Otf *o, ChainedSequenceContextFormat2 *v);
+void print_ChainedSequenceContextFormat2(Otfile *f, int indent, Otf *o, ChainedSequenceContextFormat2 *v);
+
+struct ChainedSequenceContextFormat3 {
+ u16int backtrackGlyphCount;
+ u16int *backtrackCoverageOffsets;
+ u16int inputGlyphCount;
+ u16int *inputCoverageOffsets;
+ u16int lookaheadGlyphCount;
+ u16int *lookaheadCoverageOffsets;
+ u16int seqLookupCount;
+ SequenceLookup *seqLookupRecords;
+};
+
+int read_ChainedSequenceContextFormat3(Otf *o, ChainedSequenceContextFormat3 *v);
+void print_ChainedSequenceContextFormat3(Otfile *f, int indent, Otf *o, ChainedSequenceContextFormat3 *v);
+
+struct ChainedSequenceContext {
+ u16int format;
+ ChainedSequenceContextFormat1 f1;
+ ChainedSequenceContextFormat2 f2;
+ ChainedSequenceContextFormat3 f3;
+};
+
+int read_ChainedSequenceContext(Otf *o, ChainedSequenceContext *v);
+void print_ChainedSequenceContext(Otfile *f, int indent, Otf *o, ChainedSequenceContext *v);
+
+struct PairValue {
+ u16int secondGlyph;
+ ValueRecord valueRecord1;
+ ValueRecord valueRecord2;
+};
+
+int read_PairValue(Otf *o, PairValue *v);
+void print_PairValue(Otfile *f, int indent, Otf *o, PairValue *v);
+
+struct PairSet {
+ u16int pairValueCount;
+ PairValue *pairValueRecords;
+};
+
+int read_PairSet(Otf *o, PairSet *v);
+void print_PairSet(Otfile *f, int indent, Otf *o, PairSet *v);
+
+struct PairPosFormat1 {
+ u16int pairSetCount;
+ u16int *pairSetOffsets;
+ PairSet *pairSets;
+};
+
+int read_PairPosFormat1(Otf *o, PairPosFormat1 *v);
+void print_PairPosFormat1(Otfile *f, int indent, Otf *o, PairPosFormat1 *v);
+
+struct Class2 {
+ ValueRecord valueRecord1;
+ ValueRecord valueRecord2;
+};
+
+int read_Class2(Otf *o, Class2 *v);
+void print_Class2(Otfile *f, int indent, Otf *o, Class2 *v);
+
+struct Class1 {
+ Class2 *class2Records;
+};
+
+int read_Class1(Otf *o, Class1 *v);
+void print_Class1(Otfile *f, int indent, Otf *o, Class1 *v);
+
+struct PairPosFormat2 {
+ u16int classDef1Offset;
+ u16int classDef2Offset;
+ u16int class1Count;
+ u16int class2Count;
+ Class1 *class1Records;
+};
+
+int read_PairPosFormat2(Otf *o, PairPosFormat2 *v);
+void print_PairPosFormat2(Otfile *f, int indent, Otf *o, PairPosFormat2 *v);
+
+struct PairPos {
+ u16int format;
+ u16int coverageOffset;
+ u16int valueFormat1;
+ u16int valueFormat2;
+ PairPosFormat1 f1;
+ PairPosFormat2 f2;
+};
+
+int read_PairPos(Otf *o, PairPos *v);
+void print_PairPos(Otfile *f, int indent, Otf *o, PairPos *v);
+
+struct EntryExit {
+ u16int entryAnchorOffset;
+ u16int exitAnchorOffset;
+};
+
+int read_EntryExit(Otf *o, EntryExit *v);
+void print_EntryExit(Otfile *f, int indent, Otf *o, EntryExit *v);
+
+struct CursivePos {
+ // u16int format;
+ u16int coverageOffset;
+ u16int entryExitCount;
+ EntryExit *entryExitRecords;
+};
+
+int read_CursivePos(Otf *o, CursivePos *v);
+void print_CursivePos(Otfile *f, int indent, Otf *o, CursivePos *v);
+
enum { // Lookup
// lookupFlag
LOOKUP_FL_RIGHT_TO_LEFT = 1<<0,
@@ -943,6 +1158,10 @@
u16int subTableCount;
u16int *subtableOffsets;
u16int markFilteringSet;
+ SinglePos *singleAdjustment;
+ PairPos *pairAdjustment;
+ CursivePos *cursiveAttachment;
+ ChainedSequenceContext *chainedContextsPositioning;
};
int read_Lookup(Otf *o, Lookup *v);
--- a/plan9/otfpriv.h
+++ b/plan9/otfpriv.h
@@ -21,6 +21,7 @@
u16int storageOffset;
u16int firstGlyphIndex;
u16int lastGlyphIndex;
+ u16int class2Count;
u16int axisCount;
u16int instanceCount;
u16int instanceSize;
--- a/rast.c
+++ b/rast.c
@@ -333,9 +333,9 @@
}
static int
-qbzr(SegQ *seg, int ns, int dw, int dh, u8int *p)
+qbzr(SegQ *seg, int ns, int dw, int dh, int pitch, u8int *p)
{
- int ju², j², w, h, i, j, ju, px, py;
+ int ju², j², w, h, i, j, ju, px, py, v;
Sval rju², s₀, s, *c, *c₀x, *c₀y, *cs, zx, zy;
u64int *ma, *mb, all, nall;
@@ -451,9 +451,10 @@
}
c = c₀x + 3*j²*j²;
}
- *p++ = 255 -
- (s < QBZR_PIX_SCALE/255 ? 0 : (s >= QBZR_PIX_SCALE ? 255 : s/QBZR_PIX_SCALE*255));
+ v = s/QBZR_PIX_SCALE*255;
+ *p++ = v < 0 ? 0 : (v > 255 ? 255 : v);
}
+ p += pitch - dw;
}
free(ma);
return 0;
@@ -547,10 +548,31 @@
return npts < 2 ? 0 : npts;
}
+static void
+subpxfir(GlyfImage *im, SubPx *sub)
+{
+ u8int *in, *o;
+ int x, y, i, a;
+
+ o = im->b + im->w*im->h;
+ for(y = 0; y < im->h; y++){
+ in = im->b + y*im->w;
+ for(x = 0; x < im->w; x++){
+ a = 0;
+ for(i = 0; i < sub->fir.nc; i++){
+ if(x >= i)
+ a += sub->fir.c[i]*in[x-i]/255;
+ }
+ o[x] = a > 255 ? 255 : a;
+ }
+ memcpy(in, o, im->w);
+ }
+}
+
static GlyfImage *
-outlineglyf(Otf *o, Glyf *g, double ppemX, double ppemY)
+outlineglyf(Otf *o, Glyf *g, RasterOpts *opts)
{
- int i, j, maxptstotal, maxpts, ngs, w, h, npx, baseline;
+ int i, j, maxptstotal, maxpts, ngs, rw, rh, w, h, npx, baseline;
Sval bb[4], offY, scaleX, scaleY;
Glyf *gs[MAXCOMPONENTS] = {nil};
ComponentGlyph *cg;
@@ -566,7 +588,7 @@
gs[ngs++] = g;
}else{
for(cg = g->component; cg != nil && ngs < nelem(gs); cg = cg->next, ngs++){
- if((gs[ngs] = otfglyf(o, cg->glyphIndex, ppemX, ppemY)) == nil)
+ if((gs[ngs] = otfglyf(o, cg->glyphIndex, opts)) == nil)
goto done;
}
}
@@ -588,8 +610,10 @@
*/
pts = calloc(1, maxpts*sizeof(*pts) + maxptstotal/2*sizeof(*s));
- scaleX = ppemX / o->td.head->unitsPerEm;
- scaleY = ppemY / o->td.head->unitsPerEm;
+ scaleX = opts->ppemX / o->td.head->unitsPerEm;
+ scaleY = opts->ppemY / o->td.head->unitsPerEm;
+ if(opts->subpx != nil)
+ scaleX *= 3;
bb[0] = g->xMin*scaleX;
bb[1] = g->yMin*scaleY;
bb[2] = g->xMax*scaleX;
@@ -646,7 +670,7 @@
bb[3] += 8*ε;
}
bb[3] += offY;
- w = ceil(bb[2] - bb[0]);
+ w = ceil(bb[2]+8*ε) - floor(bb[0]-4*ε);
h = ceil(bb[3]);
for(p = s₀; p != s; p++){
@@ -658,14 +682,28 @@
p->p2.y += offY;
}
}
+ rw = w;
+ rh = h;
+ if(opts->subpx != nil){
+ rw += 2;
+ rh++;
+ if((rw % 3) != 0)
+ rw += 3 - (rw % 3);
+ }
- npx = w*h;
- im = malloc(sizeof(*im) + npx);
- im->b[0] = 0;
- if(qbzr(s₀, s-s₀, w, h, im->b) == 0){
- im->w = w;
+ npx = rw*rh;
+ im = calloc(npx, sizeof(*im));
+ if(qbzr(s₀, s-s₀, w, h, rw, im->b) == 0){
+ im->w = rw;
im->h = h;
im->baseline = baseline;
+ if(opts->subpx != nil){
+ subpxfir(im, opts->subpx);
+ im->w /= 3;
+ im->fmt = GLYF_FMT_RGB_24;
+ }else{
+ im->fmt = GLYF_FMT_GREY_8;
+ }
}else{
free(im);
im = nil;
@@ -689,11 +727,11 @@
if(bg->format == 2 || bg->format == 5){
i = y*bg->pitchBits + x;
x = i - (i & ~7);
- return (bg->image[i>>3] & (0x80>>x)) ? 255 : 0;
+ return (bg->image[i>>3] & (0x80>>x)) ? 0 : 255;
}
i = y*bg->pitchBits+x;
x &= 7;
- return (bg->image[i>>3] & (0x80>>x)) ? 255 : 0;
+ return (bg->image[i>>3] & (0x80>>x)) ? 0 : 255;
}else if(bg->bitDepth == 4){
/* FIXME */
assert(nil);
@@ -733,9 +771,9 @@
}
GlyfImage *
-bitmapglyf(Otf *o, Glyf *g, double ppemX, double ppemY)
+bitmapglyf(Otf *o, Glyf *g, RasterOpts *opts)
{
- int zoomX, zoomY, n, w, h, x, y, i, bb[4], ngs;
+ int zoomX, zoomY, w, h, x, y, i, bb[4], ngs;
Glyf *gs[MAXCOMPONENTS], *cg;
EbdtComponent *c;
BitmapGlyph *bg;
@@ -744,9 +782,9 @@
USED(o);
im = nil;
bg = g->bitmap;
- if((zoomX = ppemX/bg->ppemX) < 1)
+ if((zoomX = opts->ppemX/bg->ppemX) < 1)
zoomX = 1;
- if((zoomY = ppemY/bg->ppemY) < 1)
+ if((zoomY = opts->ppemY/bg->ppemY) < 1)
zoomY = 1;
bb[0] = g->xMin;
@@ -755,7 +793,7 @@
bb[3] = g->yMax;
for(ngs = 0; ngs < nelem(gs) && ngs < bg->numComponents;){
c = bg->components + ngs;
- if((cg = gs[ngs] = otfglyf(o, c->glyphID, ppemX, ppemY)) == nil)
+ if((cg = gs[ngs] = otfglyf(o, c->glyphID, opts)) == nil)
goto err;
ngs++;
if(cg->type != GLYPH_BITMAP){
@@ -783,10 +821,6 @@
bitblit(g, im, zoomX, zoomY, bb);
for(i = 0; i < ngs; i++)
bitblit(gs[i], im, zoomX, zoomY, bb);
- n = im->w*im->h;
- for(i = 0; i < n; i++)
- im->b[i] = 255 - im->b[i];
-
for(i = 0; i < ngs; i++){
free(gs[i]->bitmap);
free(gs[i]);
@@ -803,12 +837,12 @@
}
GlyfImage *
-otfdrawglyf(Otf *o, Glyf *g, double ppemX, double ppemY)
+otfdrawglyf(Otf *o, Glyf *g, RasterOpts *opts)
{
if(g->type == GLYPH_SIMPLE || g->type == GLYPH_COMPONENT)
- return outlineglyf(o, g, ppemX, ppemY);
+ return outlineglyf(o, g, opts);
if(g->type == GLYPH_BITMAP)
- return bitmapglyf(o, g, ppemX, ppemY);
+ return bitmapglyf(o, g, opts);
werrstr("empty glyph");
return nil;
}
--- a/test.h
+++ b/test.h
@@ -12,12 +12,21 @@
f->print(f->aux, " -H: highlight a specific glyph in the map by its ID\n");
f->print(f->aux, " -x: horizontal ppem\n");
f->print(f->aux, " -y: vertical ppem\n");
+ f->print(f->aux, " -s: use subpixel rendering if applicable\n");
f->print(f->aux, "Specifying -m more than once adds guide lines\n");
f->print(f->aux, "Specifying -m more than twice makes empty pixels filled out\n");
}
+static u8int deffir[] = {8, 77, 86, 77, 8};
+static RasterOpts ropts;
+static SubPx subpx = {
+ .lcd = LCD_RGB,
+ .fir = {
+ .c = deffir,
+ .nc = nelem(deffir),
+ },
+};
static int gind = -1, map, highlight = -1, runesonly;
-static double ppemX, ppemY;
static Rune rune = NoRune;
static char *gtypes[] = {
[GLYPH_EMPTY] = "empty",
@@ -91,12 +100,21 @@
for(d = 0; d < im[i]->h; d++, lt += bw*3){
for(t = 0; t < im[i]->w; t++){
u8int r, g, b;
- r = g = b = im[i]->b[d*im[i]->w + t];
+ u32int rgb;
+ if(im[i]->fmt == GLYF_FMT_RGB_24){
+ r = im[i]->b[(d*im[i]->w + t)*3 + 0];
+ g = im[i]->b[(d*im[i]->w + t)*3 + 1];
+ b = im[i]->b[(d*im[i]->w + t)*3 + 2];
+ }else{
+ r = g = b = im[i]->b[d*im[i]->w + t];
+ }
+ rgb = r<<16 | g<<8 | b;
border = d == 0 || t == 0 || t == im[i]->w-1 || d == im[i]->h-1;
- if((lines || highlight == i) && g == 0xff && (d == im[i]->h+im[i]->baseline)){
+ if((lines || highlight == i) && rgb == 0 && (d == im[i]->h+im[i]->baseline)){
+ r = 0xff;
g = 0;
b = 0;
- }else if((lines || highlight == i) && g == 0xff && (fill || border)){
+ }else if((lines || highlight == i) && rgb == 0 && (fill || border)){
if(fill && !border){
r = 0xff;
g = 0x80;
@@ -103,8 +121,13 @@
b = 0xff;
}else if(border){
r = 0;
+ g = 0xff;
b = 0;
}
+ }else{
+ r = 0xff - r;
+ g = 0xff - g;
+ b = 0xff - b;
}
if(r != 0xff || g != 0xff || b != 0xff){
lt[t*3+0] = b;
@@ -147,28 +170,28 @@
goto glypherr;
}
if(map && gind < 0){
- im = ppemX > 0 ? calloc(n, sizeof(*im)) : nil;
+ im = ropts.ppemX > 0 ? calloc(n, sizeof(*im)) : nil;
for(i = 0; i < n; i++){
if(runesonly && otfglyph2rune(o, i) == NoRune)
continue;
- if((g = otfglyf(o, i, ppemX, ppemY)) == nil){
+ if((g = otfglyf(o, i, &ropts)) == nil){
gind = i;
glypherr:
werrstr("glyph %d: %r", gind);
return -1;
}
- if(ppemX > 0 && g->type != GLYPH_EMPTY){
- if((im[i] = otfdrawglyf(o, g, ppemX, ppemY)) == nil){
+ if(ropts.ppemX > 0 && g->type != GLYPH_EMPTY){
+ if((im[i] = otfdrawglyf(o, g, &ropts)) == nil){
gind = i;
goto glypherr;
}
- }else if(ppemX <= 0){
+ }else if(ropts.ppemX <= 0){
out->print(out->aux, "%d (%s):\n", i, gtypes[g->type]);
print_Glyf(out, indentΔ, o, g);
}
free(g);
}
- if(ppemX > 0){
+ if(ropts.ppemX > 0){
if(out->write != otfdiscard && dumpmap(out, im, n) != 0)
return -1;
for(i = 0; i < n; i++)
@@ -178,11 +201,11 @@
}else if(gind < 0){
otfprint(o, out, indentΔ);
}else{
- if((g = otfglyf(o, gind, ppemX, ppemY)) == nil){
+ if((g = otfglyf(o, gind, &ropts)) == nil){
goto glypherr;
- }else if(ppemX > 0){
+ }else if(ropts.ppemX > 0){
GlyfImage *im;
- if((im = otfdrawglyf(o, g, ppemX, ppemY)) == nil)
+ if((im = otfdrawglyf(o, g, &ropts)) == nil)
goto glypherr;
if(out->write != otfdiscard && dumpmap(out, &im, 1) != 0)
return -1;
@@ -213,7 +236,7 @@
map++; \
break; \
case 'p': \
- ppemX = ppemY = strtod(EARGF(usage(&out)), nil); \
+ ropts.ppemX = ropts.ppemY = strtod(EARGF(usage(&out)), nil); \
break; \
case 'r': { \
char *s = EARGF(usage(&out)); \
@@ -229,11 +252,14 @@
case 'R': \
runesonly++; \
break; \
+ case 's': \
+ ropts.subpx = &subpx; \
+ break; \
case 'x': \
- ppemX = strtod(EARGF(usage(&out)), nil); \
+ ropts.ppemX = strtod(EARGF(usage(&out)), nil); \
break; \
case 'y': \
- ppemY = strtod(EARGF(usage(&out)), nil); \
+ ropts.ppemY = strtod(EARGF(usage(&out)), nil); \
break; \
default: \
usage(&out); \
@@ -240,7 +266,7 @@
}ARGEND \
if(argc != 1) \
usage(&out); \
- if(ppemX == 0 && ppemY != 0) \
- ppemX = ppemY; \
- if(ppemY == 0 && ppemX != 0) \
- ppemY = ppemX;
+ if(ropts.ppemX == 0 && ropts.ppemY != 0) \
+ ropts.ppemX = ropts.ppemY; \
+ if(ropts.ppemY == 0 && ropts.ppemX != 0) \
+ ropts.ppemY = ropts.ppemX;