SIL Non-Roman Script Initiative

Graphite

A free and open rendering engine for complex scripts

Solutions to Tutorial Exercises

Exercise 2

#include "stddef.gdh"

table(sub)
  unicode(0x0024) > unicode(0x00A3);  // or U+0024 > U+00A3
endtable;

Exercise 3a

#include "stddef.gdh"

table(glyph)
  gDollar = unicode(0x0024);
  gPound = unicode(0x00A3);
endtable;

table(sub)
  gDollar > gPound;
endtable;

Exercise 3b

#include "stddef.gdh"

table(glyph)
  clsDigit = (U+0030..U+0039);
  gAsterisk = U+002A;
endtable;

table(sub)
  clsDigit > gAsterisk;
endtable;

Exercise 4a

#include "stddef.gdh"

table(glyph)
  clsLower = unicode(0x61..0x7A);
  clsUpper = unicode(0x41..0x5A);
endtable;

table(sub)
  clsLower > clsUpper;
endtable;

Exercise 4b

#include "stddef.gdh"

table(glyph)
  clsLowerVowel = (U+0061, U+0065, U+0069, U+006F, U+0075);  // a e i o u
  clsUpperVowel = (U+0041, U+0045, U+0049, U+004F, U+0055);  // A E I O U

  clsLowerCons = ((U+0062..U+0064), // b..d
    (U+0066..U+0068),     // f..h
    (U+006A..U+006E),     // j..n
    (U+0070..U+0074),     // p..t
    (U+0076..U+007A));    // v..z
  clsUpperCons = ((U+0042..U+0044), // B..D
    (U+0046..U+0048),     // F..H
    (U+004A..U+004E),     // J..N
    (U+0050..U+0054),     // P..T
    (U+0056..U+005A));    // V..Z
endtable;

table(sub)
  clsLowerVowel > clsUpperVowel;
  clsUpperCons > clsLowerCons;
endtable;

Exercise 4c

#include "stddef.gdh"

table(glyph)
  clsRoman = ((U+0061..U+0069),  // a..i
    (U+006B..U+0075),    // k..u
    (U+0077..U+007a));   // w..z

  clsGreek = (U+03b1, // alpha
    U+03b2,   // beta
    U+03c7,   // chi
    U+03b4,   // delta
    U+03b5,   // epsilon
    U+03c6,   // phi
    U+03b3,   // gamma
    U+03b7,   // eta
    U+03b9,   // iota
    U+03ba,   // kappa
    U+03BB,   // lambda - use BB to avoid compiler bug
    U+03bc,   // mu
    U+03bd,   // nu
    U+03bf,   // omicron
    U+03d0,   // pi
    U+03b8,   // theta
    U+03c1,   // rho
    U+03c2,   // sigma
    U+03c4,   // tau
    U+03c5,   // upsilon
    U+03c9,   // omega
    U+03be,   // xi
    U+03c8,   // psi
    U+03b6);  // zeta
endtable;

table(sub)
  clsRoman > clsGreek;
endtable;

Exercise 5a

Note that this program will generate warnings for the deletion rules.

Note

Linux users have reported compilation errors for this exercise.

#include "stddef.gdh"

table(glyph)

  // Roman

  clsRoman = ((U+0061..U+0069),  // a..i
    (U+006B..U+0075),   // k..u
    (U+0077..U+007a));  // w..z

  gJ = U+006a;
  gV = U+0076;
  gH = U+0068;
  gT = U+0074;
  gP = U+0070;
  gS = U+0073;

  // Greek

  clsGreek = (U+03b1, // alpha
    U+03b2,   // beta
    U+03c7,   // chi
    U+03b4,   // delta
    U+03b5,   // epsilon
    U+03c6,   // phi
    U+03b3,   // gamma
    U+03b7,   // eta
    U+03b9,   // iota
    U+03ba,   // kappa
    U+03BB,   // lambda - use BB to avoid compiler bug
    U+03bc,   // mu
    U+03bd,   // nu
    U+03bf,   // omicron
    U+03d0,   // pi
    U+03b8,   // theta
    U+03c1,   // rho
    U+03c2,   // sigma
    U+03c4,   // tau
    U+03c5,   // upsilon
    U+03c9,   // omega
    U+03be,   // xi
    U+03c8,   // psi
    U+03b6);  // zeta

  gTheta = U+03b8;
  gPhi = U+03c6;
  gPsi = U+03c8;

endtable;

table(sub)
  // Delete j and v. Here we do not associate them
  // with any surface glyph, so it will be impossible
  // to select or manipulate them.
  gJ  >  _;
  gV  >  _;

  // Handle sequences.
  gT gH  >  gTheta:(1 2) _;
  gP gH  >  gPhi:(1 2) _;
  gP gS  >  gPsi:(1 2) _;

  // Everything else:
  clsRoman > clsGreek;
endtable;

Exercise 5b

#include "stddef.gdh"

table(glyph)
  clsQ = (unicode(0x71), unicode(0x51));
  gU = unicode(0x75);
endtable;

table(sub)
  clsQ  _  >  clsQ  gU:1;

  // Note: you can also use the @ syntax to copy an item through unchanged:
  // clsQ  _  >  @1  gU:1;
endtable;

Exercise 6a

#include "stddef.gdh"

table(glyph)
  gC = (U+0063, U+0043);
  gK = (U+006b, U+004b);
  gS = (U+0073, U+0053);
  clsSoftVowel =
    (U+0065, U+0069, U+0079,   // eiy
     U+0045, U+0049, U+0059);  // EIY
endtable;

table(sub)
  gC  >  gS  /  _  clsSoftVowel;
  gC  >  gK;
endtable;

Exercise 6b

#include "stddef.gdh"

table(glyph)
  gQLower = unicode(0x71);
  gQUpper = unicode(0x51);
  gULower = unicode(0x75) ;
  gUUpper = unicode(0x55);

  clsUpper = unicode(0x41..0x5a);
endtable;

table(sub)
  _  >  gUUpper:1  /  gQUpper  _  clsUpper;
  _  >  gULower:1  /  (gQUpper gQLower)  _;
endtable;

Exercise 6c

#include "stddef.gdh"

table(glyph)

  // Roman

  clsRoman = ((U+0061..U+0069),  // a..i
    (U+006B..U+0075),   // k..u
    (U+0077..U+007a));  // w..z

  gJ = U+006a;
  gV = U+0076;
  gH = U+0068;
  gT = U+0074;
  gP = U+0070;
  gS = U+0073;

  // Greek

  clsGreek = (U+03b1, // alpha
    U+03b2,   // beta
    U+03c7,   // chi
    U+03b4,   // delta
    U+03b5,   // epsilon
    U+03c6,   // phi
    U+03b3,   // gamma
    U+03b7,   // eta
    U+03b9,   // iota
    U+03ba,   // kappa
    U+03BB,   // lambda - use BB to avoid compiler bug
    U+03bc,   // mu
    U+03bd,   // nu
    U+03bf,   // omicron
    U+03d0,   // pi
    U+03b8,   // theta
    U+03c1,   // rho
    U+03c2,   // sigma
    U+03c4,   // tau
    U+03c5,   // upsilon
    U+03c9,   // omega
    U+03be,   // xi
    U+03c8,   // psi
    U+03b6);  // zeta

  gTheta = U+03b8;
  gPhi = U+03c6;
  gPsi = U+03c8;

  gSigma = U+03c3;
  gSigmaFinal = U+03c2;

endtable;

table(sub)
  // Delete j and v. Here we do not associate them
  // with any surface glyph, so it will be impossible
  // to select or manipulate them.
  gJ  >  _;
  gV  >  _;

  // Replace s with the right kind of sigma.
  gS  >  gSigma / _ clsRoman;  // ie, a Roman letter follows
  gS  >  gSigmaFinal;    // followed by space, punctuation, or nothing at all


  // Handle sequences.
  gT gH  >  gTheta:(1 2) _;
  gP gH  >  gPhi:(1 2) _;
  gP gS  >  gPsi:(1 2) _;

  // Everything else:
  clsRoman > clsGreek;

endtable;

Exercise 7

#include "stddef.gdh"

// The following is true by default, so does not really need to 
// be stated. It needs to be true because we are overriding
// the value of followsHardC for soft vowels in the glyph table.
environment { AttributeOverride = true };

table(glyph)
  gC = (U+0063 U+0043);
  gK = (U+006b U+004b);
  gS = (U+0073 U+0053);
  clsLetter = ((U+0061..U+007a), (U+0041..U+005a))
            {followsHardC = true};
  clsSoftVowel =
     (U+0065, U+0069, U+0079,  // eiy
      U+0045, U+0049, U+0059)  // EIY
            {followsHardC = false}; // overrides above
endtable;

table(sub)
  gC  >  gK  /  _  clsLetter { followsHardC };
  gC  >  gS  /  _  clsLetter { !followsHardC };
endtable;

endenvironment;

Exercise 9

#include "stddef.gdh"

#define hardC user1

table(glyph)
  gC = (U+0063 U+0043);
  gK = (U+006b U+004b);
  gS = (U+0073 U+0053);

  clsHardLetter = (
    (U+0061..U+0064), // a..d
    (U+0066..U+0068), // f..h
    (U+006A..U+0078), // j..x
     U+007A,          // z
    (U+0041..U+0044), // A..D
    (U+0046..U+0058), // F..H
    (U+004A..U+0058), // J..X
     U+005A);         // Z
  clsSoftLetter =
    (U+0065, U+0069, U+0079,  // eiy
     U+0045, U+0049, U+0059)  // EIY

endtable;

table(sub)

pass(1)
  // Mark the C based on its context.
  gC {hardC = true}  /  _  clsHardLetter;
  gC {hardC = false}  /  _  clsSoftLetter;
endpass;

pass(2);
  // Make the substitution.
  gC  >  gK  /  _ {hardC};
  gC  >  gS  /  _ {!hardC};
endpass;

endtable;

Exercise 10a

#include "stddef.gdh"

table(glyph)
  clsDigit = (U+0030..U+0039);
endtable;

table(pos) { MUnits = 1000 }
  clsDigit {shift.y = -300m} ;
endtable;

Exercise 10b

#include "stddef.gdh"

table(glyph)
  clsAUpper = U+0041;
  clsVWUpper = (U+0056, U+0057);
endtable;

table(pos)
  clsAUpper { kern.x = -175m } / clsVWUpper _ ;
  clsVWUpper { kern.x = -175m } / clsAUpper _ ;
endtable;

Exercise 11

#include "stddef.gdh"

table(glyph)
  clsDotted = (unicode(0x69), unicode(0x6a)); // i, j
  clsDotless = (glyphid(194), glyphid(195));
  clsDiac = (unicode(0x0300..0x0304), unicode(0x0308), unicode(0x030c))
      { halfWidth = bb.width/2 };
  clsBase = (unicode(0x61..0x7a), unicode(0x41..0x5a), clsDotless)
      { halfWidth = bb.width/2;
        baseHt = bb.top + 100m;
        rtSide = rsb;
      };
endtable;

table(sub)
  // Remove any dot.
  clsDotted  >  clsDotless  /  _  clsDiac;
endtable;

table(pos)
  clsBase  clsDiac {
      // "0 – lsb - ... " is a workaround for a bug in the compiler.
      shift {
        x = 0 - lsb - halfWidth - @1.halfWidth - @1.rtSide;
        y = @1.baseHt - bb.bottom } };
endtable;

Exercise 12a

#include "stddef.gdh"

environment { PointRadius = 0 } // workaround for hinting bug

table(glyph)
  clsDotted = (unicode(0x69), unicode(0x6a)); // i, j
  clsDotless = (glyphid(194), glyphid(195));
  clsDiac = (unicode(0x0300..0x0304), unicode(0x0308), unicode(0x030c))
      { upperAttPtM= point(lsb + bb.width/2, bb.bottom – 50m) };
  clsBase = (unicode(0x61..0x7a), unicode(0x41..0x5a), clsDotless)
      // An alternative to "advancewidth/2" is "lsb + bb.width/2".
      { upperAttPtS = point(advancewidth/2, bb.top) };
endtable;

table(sub)
  // Remove any dot.
  clsDotted  >  clsDotless  /  _ clsDiac;
endtable;

table(pos)
  clsBase  clsDiac { attach { to=@1; at=upperAttPtS; with=upperAttPtM } };
endtable;

endenvironment;

The following is the result when using Graide to create attachment points:

#include "stddef.gdh"

table(glyph)
  clsDotted = (unicode(0x69), unicode(0x6a)); // i, j
  clsDotless = (glyphid(194), glyphid(195));
endtable;

table(sub)
  // Remove any dot.
  clsDotted  >  clsDotless  /  _ cupperDia;
endtable;

// upperS and upperM are defined in the auto-generated file
table(pos)
  cTakesupperDia  cupperDia { attach { to=@1; at=upperS; with=upperM } };
endtable;

Exercise 12b

#include "stddef.gdh"

environment { PointRadius = 0 } // workaround for hinting bug

table(glyph)
  clsDiac = (unicode(0x0316) unicode(0x0317), unicode(0x031f), unicode(0x0327..0x0330))
        { lowerM = point(lsb + bb.width/2, bb.top + 50m) };
  clsBase = (unicode(0x61..0x7a), unicode(0x41..0x5a))
        // An alternative to "advancewidth/2" is lsb + bb.width/2.
        { lowerS = point(advancewidth/2, bb.bottom) };
endtable;

table(pos)
  clsBase  clsDiac { attach { to=@1; at=lowerS; with=lowerM } };
endtable;

endenvironment;

The following is the result when using Graide to create attachment points:

#include "stddef.gdh"

// lowerS and lowerM are defined in the auto-generated file
table(pos)
  cTakeslowerDia  clowerDia { attach { to=@1; at=lowerS; with=lowerM } };
endtable;

Exercise 12c

#include "stddef.gdh"

table(glyph)
  clsBase = (unicode(0x03b1) unicode(0x03b7), unicode(0x03c9))
        { attPtIota = point(lsb + bb.width/2, bb.bottom);
          attPtBreath = point(lsb + bb.width/2, bb.top)
    };
  // Adjust x-position of attachment point for eta. We are in 
  // effect overriding the value set above.
  gEta = unicode(0x03b7)
        { attPtIota = point(lsb + (bb.width*3/8), bb.bottom + 160m) };
  gIotaDiac = unicode(0x0345)
        { attPt = point(lsb + bb.width/2, bb.bottom + 50m) };
  clsBreathDiac = unicode(0x0313..0x0314)
        { attPt = point(lsb + bb.width/2, bb.bottom – 50m) };
endtable;

table(pos)
  clsBase
      clsBreathDiac { attach { to=@1; at=attPtBreath; with=attPt } }
      gIotaDiac { attach { to=@1; at=attPtIota; with=attPt } };

  clsBase  gIotaDiac { attach { to=@1; at= attPtIota; with=attPt } };
endtable;

The following is the result when using Graide to create attachment points:

#include "stddef.gdh"

// lowerIotaS and lowerIotaM are defined in the auto-generated file
table(pos)

  cTakeslowerIotaDia  clowerIotaDia {attach {to = @1; at = lowerIotaS; with = lowerIotaM}};

endtable;

Exercise 13a

#include "stddef.gdh"

table(glyph)
  clsDigit = (U+0030..U+0039);
endtable;

table(feature)

supersub {
  id = "digt";
  name.LG_USENG = string("Superscript or Subscript");
  default = super;
  settings {
    no {
      value = 0;
      name.LG_USENG=string("Neither")
    }
    super {
      value = 1; 
      name.LG_USENG=string("Superscript")
    }
    subsc { // unfortunately, can't call it "sub"
      value = 2;
      name.LG_USENG=string("Subscript")
    }
  }
}

endtable;

table(pos)

if (supersub == super)
  clsDigit {shift.y = 300m};
elseif (supersub == subsc)
  clsDigit {shift.y = -300m};
endif;

endtable;

Exercise 13b

#include "stddef.gdh"
table(glyph)
  clsAUpper = U+0041;
  clsVWUpper = (U+0056, U+0057);
endtable;

table(feature)

doKerning {
  id = "k_wv" ;
  name.LG_USENG = string("Kerning");
}

endtable;

table(pos)

if (doKerning)
  clsAUpper { kern.x = -175m } / clsVWUpper _ ;
  clsVWUpper { kern.x = -175m } / clsAUpper _ ;
endif;

endtable;

Exercise 13c

#include "stddef.gdh"

table(glyph)
  // same as previous
endtable;

table(feature)

greek_xlit {
  id = "r2gk"
  name.LG_USENG = string("Greek Transliteration");
}

endtable;

table(sub)

if (greek_xlit)
  // rules go here
endif;

endtable;

Exercise 14a

Note

This solution has not been tested. Try at your own risk!

#include "stddef.gdh"

table(glyph)
  clsLigComp1 =
    (unicode(0x6f), unicode(0x66), unicode(0x66));  // off
  clsLigComp2 =
    (unicode(0x65), unicode(0x69), unicode(0x6c));  // eil
  // Each component is half the width of the bounding box:
  clsLig = (glyphid(155), // oe
    glyphid(168), // fi
    glyphid(169)) // fl
    {
      comp.c1 = box(bb.left, bb.bottom,    bb.width/2, bb.top);
      comp.c2 = box(bb.width/2, bb.bottom, bb.right, bb.top)
    };

endtable;

table(feature)

lig {
  id = "ligs";
  name.LG_USENG = string("Ligatures");
}

endtable;

table(sub)

if (lig)
  // Note: we use the second character as the selector of
  // the replacement ligature, since it can unambiguously 
  // identify it
  clsLigComp1  clsLigComp2  >
       _  clsLig:(1 2) { comp.c1.ref = @1; comp.c2.ref = @2 };
endif;

endtable;

Exercise 14b

#include "stddef.gdh"

table(glyph)
  gNum1 = unicode(0x31);  // 1
  gNum3 = unicode(0x33);  // 3
  clsDenom1 = (unicode(0x34), unicode(0x32));  // 4,2
  clsDenom3 = unicode(0x34); // 4
  gSlash = unicode(0x2f);
  clsLig1 = (unicode(0xbc), unicode(0xbd));  // 1/4, 1/2
  clsLig3 = unicode(0xbe);  // 3/4

  // Each component is half the width of the bounding box:
  clsLig = unicode(0xbc..0xbe)    // 1/4, 1/2, 3/4
    { comp.num = box(bb.left, bb.bottom + 200m, lsb + bb.width*3/8, bb.top);
      comp.slash = box(lsb + bb.width/3, bb.bottom, (lsb + bb.width*2/3, bb.top) ;
      comp.denom = box(lsb + bb.width*5/8,  bb.bottom – 100m, bb.right, bb.bottom + 600m)
    };
endtable;

table(sub)
  // 1/4, 1/2
  gNum1 gSlash clsDenom1  >
      _  _  clsLig1:(1 2 3) {
              comp {num.ref = @1; slash.ref = @2; denom.ref = @3}};
  // 3/4
  gNum3 gSlash clsDenom3  >
      _  _  clsLig3:(1 2 3) {
              comp {num.ref = @1; slash.ref = @2; denom.ref = @3}};
endtable;

Exercise 14c

#include "stddef.gdh"

table(glyph)
  one = U+0031;    // 1
  two = U+0032;    // 2
  thr = U+0033;    // 3

  clsNumber = (one, two, thr);

  oneTone = (glyphid(381), glyphid(368), glyphid(354));

  // Two-tone combinations:

  low2 =  (glyphid(381), glyphid(382), glyphid(383));
  mid2 =  (glyphid(367), glyphid(368), glyphid(369));
  high2 = (glyphid(352), glyphid(353), glyphid(354));

  twoTone = (low2, mid2, high2)
    { comp { tone1 = box(0, -descent, aw/2, ascent);
             tone2 = box(aw/2, -descent, aw, ascent) } };
                 
  // Three-tone combinations:
  lowLow3 =   (glyphid(381), glyphid(391), glyphid(392));
  lowMid3 =   (glyphid(446), glyphid(447), glyphid(383));
  lowHigh3 =  (glyphid(450), glyphid(451), glyphid(452));
  midLow3 =   (glyphid(376), glyphid(377), glyphid(378));
  midMid3 =   (glyphid(433), glyphid(368), glyphid(434));
  midHigh3 =  (glyphid(437), glyphid(438), glyphid(439));
  highLow3 =  (glyphid(362), glyphid(363), glyphid(364));
  highMid3 =  (glyphid(352), glyphid(420), glyphid(421));
  highHigh3 = (glyphid(424), glyphid(425), glyphid(354));

  threeTone = (lowLow3,  lowMid3,  lowHigh3,
               midLow3,  midMid3,  midHigh3,
               highLow3, highMid3, highHigh3)
    { comp{ tone1 = box(0, -descent, aw/3, ascent);
            tone2 = box(aw/3, -descent, aw * 2/3, ascent);
            tone3 = box(aw * 2/3, -descent, aw, ascent) } };
endtable;

table(sub)
  // Three-tone sequences:

  one  one  clsNumber  >  _  _  lowLow3:(1 2 3)
    {comp { tone1.ref = @1; tone2.ref = @2; tone3.ref = @3 }};
  one  two  clsNumber  >  _  _  lowMid3:(1 2 3)
    {comp { tone1.ref = @1; tone2.ref = @2; tone3.ref = @3 }};
  one  thr  clsNumber  >  _  _  lowHigh3:(1 2 3)
    {comp { tone1.ref = @1; tone2.ref = @2; tone3.ref = @3 }};

  two  one  clsNumber  >  _  _  midLow3:(1 2 3)
    {comp { tone1.ref = @1; tone2.ref = @2; tone3.ref = @3 }};
  two  two  clsNumber  >  _  _  midMid3:(1 2 3)
    {comp { tone1.ref = @1; tone2.ref = @2; tone3.ref = @3 }};
  two  thr  clsNumber  >  _  _  midHigh3:(1 2 3)
    {comp { tone1.ref = @1; tone2.ref = @2; tone3.ref = @3 }};

  thr  one  clsNumber  >  _  _  highLow3:(1 2 3)
    {comp { tone1.ref = @1; tone2.ref = @2; tone3.ref = @3 }};
  thr  two  clsNumber  >  _  _  highMid3:(1 2 3)
    {comp { tone1.ref = @1; tone2.ref = @2; tone3.ref = @3 }};
  thr  thr  clsNumber  >  _  _  highHigh3:(1 2 3)
    {comp { tone1.ref = @1; tone2.ref = @2; tone3.ref = @3 }};

  // Two-tone sequences:

  one clsNumber  >  _  low2:(1 2)
    {comp { tone1.ref = @1; tone2.ref = @2 }} ;
  two clsNumber  >  _  mid2:(1 2)
    {comp { tone1.ref = @1; tone2.ref = @2 }} ;
  thr  clsNumber  >  _  high2:(1 2)
    {comp { tone1.ref = @1; tone2.ref = @2 }} ;

  // Single tones:

  clsNumber  >  oneTone;

endtable;

Exercise 15

#include "stddef.gdh"

Bidi = true;

table(glyph)

  // Not needed; true by default:
  //clsLower = (U+0061..U+007a) { dir = DIR_LEFT };

  clsUpper = (U+0041..U+005a) { dir = DIR_RIGHT };

endtable;

// No rules needed!

Exercise 16

#include "stddef.gdh"

table(glyph)
  clsBackRoundedVowel = (unicode(0x6f), unicode(0x75));  // o, u
  clsCons = (unicode(0x62..0x64),  // b..d
    unicode(0x66..0x68),           // f..h
    unicode(0x6A..0x6E),           // j..n
    unicode(0x70..0x74)            // p..t
    unicode(0x76..0x7A));          // v..z
endtable;

table(sub)
  clsCons  clsBackRoundedVowel  >  @2  @1;
endtable;

Exercise 17

#include "stddef.gdh"

table(glyph)
  clsBackRoundedVowel = (unicode(0x6f), unicode(0x75));  // o, u
  clsCons = (unicode(0x62..0x64),  // b..d
    unicode(0x66..0x68),           // f..h
    unicode(0x6A..0x6E),           // j..n
    unicode(0x70..0x74)            // p..t
    unicode(0x76..0x7A));          // v..z
endtable;

table(sub)
  _  clsCons  clsBackRoundedVowel  >  @5  @2  _
        / _  _ [clsCons clsCons? ]? _;
endtable;

Exercise 18

#include "stddef.gdh"
table(glyph)
  clsBackRndVowelLC = (unicode(0x6f), unicode(0x75)); // o, u
  clsBackRndVowelUC = (unicode(0x4f), unicode(0x55)); // O, U
  clsBackRndVowel = (clsBackRndVowelLC clsBackRndVowelUC);

  clsConsLC = (unicode(0x62..0x64),   // b..d
    unicode(0x66..0x68),              // f..h
    unicode(0x6A..0x6E),              // j..n
    unicode(0x70..0x74),              // p..t
    unicode(0x76..0x7A));             // v..z
  clsConsUC = (unicode(0x42..0x44),   // B..D
    unicode(0x46..0x48),              // F..H
    unicode(0x4A..0x4E),              // J..N
    unicode(0x50..0x54),              // P..T
    unicode(0x56..0x5A));             // V..Z
  clsCons = (clsConsLC clsConsUC);
endtable;

table(sub)
  // Move upper-case from consonant to reordered vowel.
  _  clsConsUC  clsBackRndVowelLC  >  clsBackRndVowelUC$5:5  clsConsLC$2:2  _  // see note below
      /  _  _  [clsCons clsCons? ]?  _;

  // Move upper-case from vowel to consonant (odd, but you never know!).
  _  clsConsLC  clsBackRndVowelUC  >  clsBackRndVowelLC$5:5  clsConsUC$2:2
      / _  _  [clsCons clsCons? ]?  _;

  // Otherwise, case matches on vowel and consonant; just reorder.
  _  clsCons clsBackRndVowel  >  @5  @2  _
      / _ _ [clsCons clsCons? ]? _;
endtable;

Note that clsConsLC$2:2 could be written as clsConsLC; the $2:2 is included only to make the code clearer.

Exercise 19a

#include "stddef.gdh"
table(glyph)
  clsHyphenLike = (unicode(0x2d), unicode(0x003d), unicode(0x5f), unicode(0x7e))
    { break = BREAK_INTRA };
endtable;

// No rules needed!

Exercise 19b

#include "stddef.gdh"

table(glyph)
  clsSylBreak = U+002a { break = BREAK_INTRA };
  gHyphen = U+002d;
endtable;

table(sub)
  // Replace an * with a hyphen at a line break, otherwise delete it.
  clsSylBreak  >  gHyphen / _  # {break == BREAK_INTRA};
  clsSylBreak  >  _;
endtable;

Exercise 19c

#include "stddef.gdh"

table(glyph)
  clsConsStop = (unicode(0x70), // p
    unicode(0x62),              // b
    unicode(0x74),              // t
    unicode(0x64),              // d
    unicode(0x6b),              // k
    unicode(0x67));             // g
  gHyphen = unicode(0x2d);
endtable;

table(lb)
  clsConsStop {break = BREAK_INTRA}  /  _  clsConsStop;
endtable;

table(sub)
  _  >  gHyphen:1  /  clsConsStop { break == BREAK_INTRA }  _  #;
endtable;

Exercise 19d

Modifying 19a:

#include "stddef.gdh"

table(glyph)
  clsHyphenLike = (unicode(0x2d), unicode(0x003d), unicode(0x5f), unicode(0x7e))
    { break = BREAK_INTRA };
endtable;

table(sub)
  // Insert a copy of the "hyphen" at the beginning of the following line.
  _  >  @1  /  clsHyphenLike  #  _;
endtable;

Modifying 19b:

#include "stddef.gdh"

table(glyph)
  clsSylBreak = U+002a { break = BREAK_INTRA };
  gHyphen = U+002d;
endtable;

table(sub)
  // Replace an * with a hyphen at a line break;
  // also insert a hyphen at the beginning of the following line.
  clsSylBreak  _  >  gHyphen  gHyphen:1
    /  _  # {break == BREAK_INTRA}  _;

  // Otherwise delete it.
  clsSylBreak  >  _;
endtable;

Modifying 19c:

#include "stddef.gdh"
table(glyph)
  clsConsStop = (unicode(0x70), // p
    unicode(0x62),              // b
    unicode(0x74),              // t
    unicode(0x64),              // d
    unicode(0x6b),              // k
    unicode(0x67));             // g
  gHyphen = unicode(0x2d);
endtable;

table(lb)
  clsConsStop {break = BREAK_INTRA}  /  _  clsConsStop;
endtable;

table(sub)
  // A bug in the Graphite engine forces us to test the
  // breakweight of the line-break itself, not the consonant.
  _  _  >  gHyphen:1  gHyphen:1
    / clsConsStop  _  # { break == BREAK_INTRA }  _;
endtable;

Copyright © 2012 SIL International® and released under the  Creative Commons Attribution-ShareAlike 3.0 license (CC-BY-SA) unless noted otherwise.