• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 package com.google.typography.font.sfntly.table.truetype;
2 
3 import com.google.typography.font.sfntly.data.ReadableFontData;
4 import com.google.typography.font.sfntly.data.WritableFontData;
5 import com.google.typography.font.sfntly.table.SubTable;
6 import com.google.typography.font.sfntly.table.truetype.GlyphTable.Offset;
7 
8 public abstract class Glyph extends SubTable {
9 
10   public enum GlyphType {
11     Simple,
12     Composite;
13   }
14 
15   protected volatile boolean initialized = false;
16   // TOO(stuartg): should we replace this with a shared lock? more contention
17   // but less space
18   protected final Object initializationLock = new Object();
19 
20   private final Glyph.GlyphType glyphType;
21   private final int numberOfContours;
22 
Glyph(ReadableFontData data, Glyph.GlyphType glyphType)23   protected Glyph(ReadableFontData data, Glyph.GlyphType glyphType) {
24     super(data);
25     this.glyphType = glyphType;
26 
27     if (this.data.length() == 0) {
28       this.numberOfContours = 0;
29     } else {
30       // -1 if composite
31       this.numberOfContours = this.data.readShort(Offset.numberOfContours.offset);
32     }
33   }
34 
Glyph(ReadableFontData data, int offset, int length, Glyph.GlyphType glyphType)35   protected Glyph(ReadableFontData data, int offset, int length, Glyph.GlyphType glyphType) {
36     super(data, offset, length);
37     this.glyphType = glyphType;
38 
39     if (this.data.length() == 0) {
40       this.numberOfContours = 0;
41     } else {
42       // -1 if composite
43       this.numberOfContours = this.data.readShort(Offset.numberOfContours.offset);
44     }
45   }
46 
glyphType(ReadableFontData data, int offset, int length)47   private static Glyph.GlyphType glyphType(ReadableFontData data, int offset, int length) {
48     if (offset > data.length()) {
49       throw new IndexOutOfBoundsException();
50     }
51     if (length == 0) {
52       return GlyphType.Simple;
53     }
54     int numberOfContours = data.readShort(offset);
55     if (numberOfContours >= 0) {
56       return GlyphType.Simple;
57     }
58     return GlyphType.Composite;
59   }
60 
61 //  @SuppressWarnings("unchecked")
62 //  static <T extends Glyph> T getGlyph(
63 //      GlyphTable table, ReadableFontData data, int offset, int length) {
64 //    Glyph.GlyphType type = Glyph.glyphType(data, offset, length);
65 //    if (type == GlyphType.Simple) {
66 //      return (T) new SimpleGlyph(data, offset, length);
67 //    }
68 //    return (T) new CompositeGlyph(data, offset, length);
69 //  }
70 
getGlyph( GlyphTable table, ReadableFontData data, int offset, int length)71   static Glyph getGlyph(
72       GlyphTable table, ReadableFontData data, int offset, int length) {
73     Glyph.GlyphType type = Glyph.glyphType(data, offset, length);
74     if (type == GlyphType.Simple) {
75       return new SimpleGlyph(data, offset, length);
76     }
77     return new CompositeGlyph(data, offset, length);
78   }
79 
initialize()80   protected abstract void initialize();
81 
82 
83   @Override
padding()84   public int padding() {
85     this.initialize();
86     return super.padding();
87   }
88 
glyphType()89   public Glyph.GlyphType glyphType() {
90     return this.glyphType;
91   }
92 
93   /**
94    * Gets the number of contours in the glyph. If this returns a number greater
95    * than or equal to zero it is the actual number of contours and this is a
96    * simple glyph. If there are zero contours in the glyph then none of the
97    * other data operations will return usable values. If it -1 then the glyph is
98    * a composite glyph.
99    *
100    * @return number of contours
101    */
numberOfContours()102   public int numberOfContours() {
103     return this.numberOfContours;
104   }
105 
xMin()106   public int xMin() {
107     return this.data.readShort(Offset.xMin.offset);
108   }
109 
xMax()110   public int xMax() {
111     return this.data.readShort(Offset.xMax.offset);
112   }
113 
yMin()114   public int yMin() {
115     return this.data.readShort(Offset.yMin.offset);
116   }
117 
yMax()118   public int yMax() {
119     return this.data.readShort(Offset.yMax.offset);
120   }
121 
instructionSize()122   public abstract int instructionSize();
123 
instructions()124   public abstract ReadableFontData instructions();
125 
126   @Override
toString()127   public String toString() {
128     return this.toString(0);
129   }
130 
toString(int length)131   public String toString(int length) {
132     StringBuilder sb = new StringBuilder();
133     sb.append(this.glyphType());
134     sb.append(", contours=");
135     sb.append(this.numberOfContours());
136     sb.append(", [xmin=");
137     sb.append(this.xMin());
138     sb.append(", ymin=");
139     sb.append(this.yMin());
140     sb.append(", xmax=");
141     sb.append(this.xMax());
142     sb.append(", ymax=");
143     sb.append(this.yMax());
144     sb.append("]");
145     sb.append("\n");
146     return sb.toString();
147   }
148 
149   // TODO(stuartg): interface? need methods from Composite?
150   public abstract static class Contour {
Contour()151     protected Contour() {
152     }
153   }
154 
155   public abstract static class Builder<T extends Glyph> extends SubTable.Builder<T> {
156     protected int format;
157 
Builder(WritableFontData data)158     protected Builder(WritableFontData data) {
159       super(data);
160     }
161 
Builder(ReadableFontData data)162     protected Builder(ReadableFontData data) {
163       super(data);
164     }
165 
166     /**
167      * @param data
168      * @param offset
169      * @param length
170      */
Builder(WritableFontData data, int offset, int length)171     protected Builder(WritableFontData data, int offset, int length) {
172       this(data.slice(offset, length));
173     }
174 
getBuilder( GlyphTable.Builder tableBuilder, ReadableFontData data)175     static Glyph.Builder<? extends Glyph> getBuilder(
176         GlyphTable.Builder tableBuilder, ReadableFontData data) {
177       return Glyph.Builder.getBuilder(tableBuilder, data, 0, data.length());
178     }
179 
getBuilder( GlyphTable.Builder tableBuilder, ReadableFontData data, int offset, int length)180     static Glyph.Builder<? extends Glyph> getBuilder(
181         GlyphTable.Builder tableBuilder, ReadableFontData data, int offset, int length) {
182       Glyph.GlyphType type = Glyph.glyphType(data, offset, length);
183       if (type == GlyphType.Simple) {
184         return new SimpleGlyph.SimpleGlyphBuilder(data, offset, length);
185       }
186       return new CompositeGlyph.CompositeGlyphBuilder(data, offset, length);
187     }
188 
189     @Override
subDataSet()190     protected void subDataSet() {
191       // NOP
192     }
193 
194     @Override
subDataSizeToSerialize()195     protected int subDataSizeToSerialize() {
196       return this.internalReadData().length();
197     }
198 
199     @Override
subReadyToSerialize()200     protected boolean subReadyToSerialize() {
201       return true;
202     }
203 
204     @Override
subSerialize(WritableFontData newData)205     protected int subSerialize(WritableFontData newData) {
206       return this.internalReadData().copyTo(newData);
207     }
208   }
209 }