SIL Non-Roman Script Initiative

Graphite

A free and open rendering engine for complex scripts

GDL code snippets

These snippets are intended as a quick reference. For complete documentation of the GDL language, download the reference paper:

GDL Documentation
Sharon Correll, 2012-01-13
Download "GDLdocumentation.pdf", Acrobat PDF document, 266KB [4472 downloads]

Diacritic attachment

table(glyph)

// These can also be classes, in which case the attachment point(s) 
// must be defined on each element of the class:
base = glyphid(34) {TopS = point(130m, 512m); BottomS = point(130m, 0m)};
upperdiac = glyphid(101) {TopM = point(86m, -12m)}
lowerdiac = glyphid(102) {BottomM = point(86m, 43m)};

endtable; // glyph

table(positioning)

base  upperdiac {attach {to=@1; at=TopS; with=TopM}};
base  lowerdiac {attach {to=@1; at=BottomS; with=BottomM}};

endtable; // positioning

Diacritics with calculated attachment points

table(glyph)

base = glyphid(...)
    {TopS = point(bb.lsb + bb.width/2, bb.top); 
     BottomS = point(bb.lsb + bb.width/2, bb.bottom)};
upperdiac = glyphid(...) {TopM = point(bb.width/2, bb.bottom - 10m)}
lowerdiac = glyphid(...) {BottomM = point(bb.width/2, bb.top + 10m)};

endtable; // glyph

table(positioning)

base  upperdiac {attach {to=@1; at=TopS; with=TopM}};
base  lowerdiac {attach {to=@1; at=BottomS; with=BottomM}};

endtable; // positioning

Stacking diacritics

The following example shows how to handle a stack/chain of both upper and lower diacritic attachments. Note that, as specified by the Unicode standard, lower diacritics are first.

table(glyph)

base1 = glyphid(34) {TopS = point(130m, 512m); BottomS = point(130m, 0m)};
base1 = glyphid(35) {TopS = point(130m, 5108m); BottomS = point(130m, -18m)};

upperdiac1 = glyphid(101) {TopM = point(86m, -12m); TopS = point(86m, 43m)};
upperdiac2 = glyphid(105) {TopM = point(81m, -10m); TopS = point(81m, 52m)};

lowerdiac1 = glyphid(102) {BottomM = point(79m, 43m); BottomS = point(79m, -3m)};
lowerdiac2 = glyphid(103) {BottomM = point(77m, 48m); BottomS = point(77m, -5m)};
// etc.

base = (base1, base2, ...);
upperdiac = (upperdiac1, upperdiac2, ...);
lowerdiac = (lowerdiac1, lowerdiac2, ...);
    
takes_lower = (base, lowerdiac); // lower diacritics can be attached to bases or other lower diacs
takes_upper = (base, upperdiac); // upper diacritics can be attached to bases or other upper diacs

endtable; // glyph

table(positioning)

// Optionally permit up to three intervening lower diacritics:
#define LOWERSEQ [ lowerdiac [ lowerdiac  lowerdiac? ]? ]?

takes_lower  lowerdiac {attach {to=@1; at=BottomS; with=BottomM}} / _ ^ _;

takes_upper  upperdiac {attach {to=@1; at=TopS; with=TopM}} / _ LOWERSEQ  ^ _;

endtable; // positioning

Bridging diacritic

Bridging diacritics must be centered over the two bases they bridge and must be higher than the tallest.

table(glyph)

base1 = ...;
base2 = ...;
bridgediac = ...;

endtable; // glyph

table(positioning)

bridgediac {shift.y = max(@B1.boundingbox.top, @B2.boundingbox.top) - @D.boundingbox.bottom + 100m ; // gap of 100m
            shift.x = (@B2.boundingbox.width - @B1.boundingbox.width)/2}
    / base1=B1  _=D  base2=B2;

endtable; // positioning

If the base glyphs have other diacritics attached, using @B1.boundingbox.top.1 will give the bounding box of the base plus diacritics.

Ligature (conjunct)

Ligatures or conjuncts occur when multiple characters combine to form a single glyph shape.

table(glyph)

char1 = ...;
char2 = ...;
ligature = ...;

endtable; // glyph

table(substitution)

char1  char2  >  ligature:(1 2)  _ ;

endtable; // substitution

Ligature with components

Ligature components correspond to sub-regions of the ligature glyph that visually correspond to the original characters. Selecting and manipulating the visual components allows manipulating the underlying characters.

Note

This feature is only supported by the original Graphite engine; it is not supported by Graphite2.

table(glyph)


char1 = ...;
char2 = ...;

ligature = ... // <-- specify which glyph
    // Define the visual components of the conjunct.
    // This conjunct has three components:
    // the top-left, the bottom-left, and the right side.
    { component {cTL= box(0, bb.height/2,   aw/2, bb.top);
                 cBL= box(0, bb.bottom,     aw/2, bb.height/2);
                 cR = box(aw/2, bb.bottom,  aw, bb.top) } };

endtable; // glyph

table(substitution)

char1  char2  char3  >  ligature:(1 2 3) {component {cTL.ref=@1; cBL.ref=@2; cR=@3}}  _  _;

endtable; // substitution

Contextual shaping

Many scripts have characters that must use alternate forms depending on neighboring letters.

table(glyph)

// Define the glyphs and classes:
matraI = ...;
matraI_wide = ...;
matraI_wider = ...;
matraI_widest = ...;

class_wide = ...;
class_wider = ...;
class_widest = ...;

endtable; // glyph

table(substitution)

// Choose the width of the matra-I that matches the width of the neighboring letter:
matraI  class_wide     >  matraI_wide  @2;
matraI  class_wider    >  matraI_wider  @2;
matraI  class_widest   >  matraI_widest  @2;

endtable;

Word-contextual shaping

Arabic is an example of a script whose characters take on alternate forms depending on their location within the word: initial, medial, final, isolate.

table(glyph)

// All characters are initially isolate forms.

// Define the individual glyphs:
beh = ...;
behInit = ...;
// etc.

// Elements of these four classes must correspond:
class_isolate = (beh,     teh,     theh,     peh,     teheh,     ...);
class_initial = (behInit, tehInit, thehInit, pehInit, tehehInit, ...);
class_medial  = (behMed,  behMed,  thehMed,  pehMed,  tehehMed,  ...);
class_final   = (behFin,  behFin,  thehFin,  pehFin,  tehehFin,  ...);

endtable; // glyph

table(substitution)

// For any contiguous pair of word-forming letters:
//   turn the first from isolate to initial, or from final to medial
//   turn the second to final
//   back up and treat the second as the first of the next pair to consider
// Any unprocessed letters are left as isolates.

(class_isolate class_final)  (class_isolate)  >  (class_initial class_medial) (class_final)  /  _ ^ _;

endtable; // substitution

Reordering

Many scripts of south and southeast Asia have characters that are rendered in an order that is different from their order in the data.

table(glyph)

// Define the glyphs or classes:
consonants = ...;
vowelLeftSide = ...;

endtable; // glyph

table(substitution)

consonant  vowelLeftSide  >  @2  @1;

endtable; // substitution

Splitting

Many scripts of south and southeast Asia have characters that are rendered by two or more non-contiguous glyphs.

table(glyph)

// Define the glyphs or classes:
consonants = ...;

// If these are classes, the elements of the classes must correspond:
vowelSplit     = (vowel1,      vowel2,      vowel3, ...);
vowelLeftHalf  = (vowel1Left,  vowel2Left,  vowel3Left, ...);
vowelRightHalf = (vowel1Right, vowel2Right, vowel3Right, ...);

endtable; // glyph

table(substitution)

_  consonant  vowelSplit  >  vowelLeftHalf$3:3  @2  vowelRightHalf:3;

end-table; // substitution

Font features (variations)

Font features provide a way to define alterative renderings that can be selected by the user.

Boolean feature

table(feature)

altk {
    id = "altk";  // can be integer or 4-char string
    name.1033 = string("Alternate K");  // 1033 = LG_USENG, US English
    default = 0;
    settings {
        off {
            value = 0;
            name.1033 = string("False");
        }
        on {
            value = 1;
            name.1033 = string("True");
        }
    }
};

endtable; // feature

table(glyph)

g_k = ...;
g_k_alt = ...;

endtable; // glyph

table(substitution)

if (altk)
g_k  >  g_k_alt
endif;

endtable; // substitution

Multi-valued feature

table(feature)

altk {
    id = "altk"; // can be integer or 4-char string
    name.1033 = string("Alternate K"); // 1033 = LG_USENG, US English
    default = 0;
    settings {
        form1 {
            value = 0;
            name.1033 = string("Form 1");
        }
        form2 {
            value = 1;
            name.1033 = string("Form 2");
        }
        form3 {
            value = 2;
            name.1033 = string("Form 3");
        }
        form4 {
            value = 3;
            name.1033 = string("Form 4");
        }
    }
};

endtable; // feature

table(glyph)

g_k = ...;
g_k1 = ...;
g_k2 = ...;
g_k3 = ...;
g_k4 = ...;

endtable; // glyph

table(substitution)

// Note that for mutually exclusive conditions, successive 'if' statements are more efficient than 'elseif'.

if (altk == form1)
g_k  >  g_k1;
endif;

if (altk == form2)
g_k  >  g_k2;
endif;

if (altk == form3)
g_k  >  g_k3;
endif;

if (altk == form4)
g_k  >  g_k3;
endif;

endtable; // substitution

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