• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 package com.google.typography.font.sfntly.table.truetype;
2 
3 import com.google.typography.font.sfntly.data.FontData;
4 import com.google.typography.font.sfntly.data.ReadableFontData;
5 import com.google.typography.font.sfntly.data.WritableFontData;
6 
7 import java.util.LinkedList;
8 import java.util.List;
9 
10 public final class CompositeGlyph extends Glyph {
11   public static final int FLAG_ARG_1_AND_2_ARE_WORDS = 0x01;
12   public static final int FLAG_ARGS_ARE_XY_VALUES = 0x01 << 1;
13   public static final int FLAG_ROUND_XY_TO_GRID = 0x01 << 2;
14   public static final int FLAG_WE_HAVE_A_SCALE = 0x01 << 3;
15   public static final int FLAG_RESERVED = 0x01 << 4;
16   public static final int FLAG_MORE_COMPONENTS = 0x01 << 5;
17   public static final int FLAG_WE_HAVE_AN_X_AND_Y_SCALE = 0x01 << 6;
18   public static final int FLAG_WE_HAVE_A_TWO_BY_TWO = 0x01 << 7;
19   public static final int FLAG_WE_HAVE_INSTRUCTIONS = 0x01 << 8;
20   public static final int FLAG_USE_MY_METRICS = 0x01 << 9;
21   public static final int FLAG_OVERLAP_COMPOUND = 0x01 << 10;
22   public static final int FLAG_SCALED_COMPONENT_OFFSET = 0x01 << 11;
23   public static final int FLAG_UNSCALED_COMPONENT_OFFSET = 0x01 << 12;
24 
25   private final List<Integer> contourIndex = new LinkedList<Integer>();
26   private int instructionsOffset;
27   private int instructionSize;
28 
CompositeGlyph(ReadableFontData data, int offset, int length)29   protected CompositeGlyph(ReadableFontData data, int offset, int length) {
30     super(data, offset, length, GlyphType.Composite);
31     initialize();
32   }
33 
CompositeGlyph(ReadableFontData data)34   protected CompositeGlyph(ReadableFontData data) {
35     super(data, GlyphType.Composite);
36     initialize();
37   }
38 
39   @Override
initialize()40   protected void initialize() {
41     if (this.initialized) {
42       return;
43     }
44     synchronized (this.initializationLock) {
45       if (this.initialized) {
46         return;
47       }
48 
49       int index = 5 * FontData.DataSize.USHORT.size(); // header
50       int flags = FLAG_MORE_COMPONENTS;
51       while ((flags & FLAG_MORE_COMPONENTS) == FLAG_MORE_COMPONENTS) {
52         contourIndex.add(index);
53         flags = this.data.readUShort(index);
54         index += 2 * FontData.DataSize.USHORT.size(); // flags and
55         // glyphIndex
56         if ((flags & FLAG_ARG_1_AND_2_ARE_WORDS) == FLAG_ARG_1_AND_2_ARE_WORDS) {
57           index += 2 * FontData.DataSize.SHORT.size();
58         } else {
59           index += 2 * FontData.DataSize.BYTE.size();
60         }
61         if ((flags & FLAG_WE_HAVE_A_SCALE) == FLAG_WE_HAVE_A_SCALE) {
62           index += FontData.DataSize.F2DOT14.size();
63         } else if ((flags & FLAG_WE_HAVE_AN_X_AND_Y_SCALE) == FLAG_WE_HAVE_AN_X_AND_Y_SCALE) {
64           index += 2 * FontData.DataSize.F2DOT14.size();
65         } else if ((flags & FLAG_WE_HAVE_A_TWO_BY_TWO) == FLAG_WE_HAVE_A_TWO_BY_TWO) {
66           index += 4 * FontData.DataSize.F2DOT14.size();
67         }
68       }
69       int nonPaddedDataLength = index;
70       if ((flags & FLAG_WE_HAVE_INSTRUCTIONS) == FLAG_WE_HAVE_INSTRUCTIONS) {
71         this.instructionSize = this.data.readUShort(index);
72         index += FontData.DataSize.USHORT.size();
73         this.instructionsOffset = index;
74         nonPaddedDataLength = index + (this.instructionSize * FontData.DataSize.BYTE.size());
75       }
76       this.setPadding(this.dataLength() - nonPaddedDataLength);
77     }
78   }
79 
flags(int contour)80   public int flags(int contour) {
81     return this.data.readUShort(this.contourIndex.get(contour));
82   }
83 
numGlyphs()84   public int numGlyphs() {
85     return this.contourIndex.size();
86   }
87 
glyphIndex(int contour)88   public int glyphIndex(int contour) {
89     return this.data.readUShort(FontData.DataSize.USHORT.size() + this.contourIndex.get(contour));
90   }
91 
argument1(int contour)92   public int argument1(int contour) {
93     int index = 2 * FontData.DataSize.USHORT.size() + this.contourIndex.get(contour);
94     int flags = this.flags(contour);
95     if ((flags & FLAG_ARG_1_AND_2_ARE_WORDS) == FLAG_ARG_1_AND_2_ARE_WORDS) {
96       return this.data.readUShort(index);
97     }
98     return this.data.readByte(index);
99   }
100 
argument2(int contour)101   public int argument2(int contour) {
102     int index = 2 * FontData.DataSize.USHORT.size() + this.contourIndex.get(contour);
103     int flags = this.flags(contour);
104     if ((flags & FLAG_ARG_1_AND_2_ARE_WORDS) == FLAG_ARG_1_AND_2_ARE_WORDS) {
105       return this.data.readUShort(index + FontData.DataSize.USHORT.size());
106     }
107     return this.data.readByte(index + FontData.DataSize.BYTE.size());
108   }
109 
transformationSize(int contour)110   public int transformationSize(int contour) {
111     int flags = this.flags(contour);
112     if ((flags & FLAG_WE_HAVE_A_SCALE) == FLAG_WE_HAVE_A_SCALE) {
113       return FontData.DataSize.F2DOT14.size();
114     } else if ((flags & FLAG_WE_HAVE_AN_X_AND_Y_SCALE) == FLAG_WE_HAVE_AN_X_AND_Y_SCALE) {
115       return 2 * FontData.DataSize.F2DOT14.size();
116     } else if ((flags & FLAG_WE_HAVE_A_TWO_BY_TWO) == FLAG_WE_HAVE_A_TWO_BY_TWO) {
117       return 4 * FontData.DataSize.F2DOT14.size();
118     }
119     return 0;
120   }
121 
transformation(int contour)122   public byte[] transformation(int contour) {
123     int flags = this.flags(contour);
124     int index = this.contourIndex.get(contour) + 2 * FontData.DataSize.USHORT.size();
125     if ((flags & FLAG_ARG_1_AND_2_ARE_WORDS) == FLAG_ARG_1_AND_2_ARE_WORDS) {
126       index += 2 * FontData.DataSize.SHORT.size();
127     } else {
128       index += 2 * FontData.DataSize.BYTE.size();
129     }
130 
131     int tsize = transformationSize(contour);
132     byte[] transformation = new byte[tsize];
133     this.data.readBytes(index, transformation, 0, tsize);
134     return transformation;
135   }
136 
137   @Override
instructionSize()138   public int instructionSize() {
139     return this.instructionSize;
140   }
141 
142   @Override
instructions()143   public ReadableFontData instructions() {
144     return this.data.slice(this.instructionsOffset, this.instructionSize());
145   }
146 
147   @Override
toString()148   public String toString() {
149     StringBuilder sb = new StringBuilder(super.toString());
150     sb.append("\ncontourOffset.length = ");
151     sb.append(this.contourIndex.size());
152     sb.append("\ninstructionSize = ");
153     sb.append(this.instructionSize);
154     sb.append("\n\tcontour index = [");
155     for (int contour = 0; contour < this.contourIndex.size(); contour++) {
156       if (contour != 0) {
157         sb.append(", ");
158       }
159       sb.append(this.contourIndex.get(contour));
160     }
161     sb.append("]\n");
162     for (int contour = 0; contour < this.contourIndex.size(); contour++) {
163       sb.append("\t" + contour + " = [gid = " + this.glyphIndex(contour) + ", arg1 = "
164           + this.argument1(contour) + ", arg2 = " + this.argument2(contour) + "]\n");
165     }
166     return sb.toString();
167   }
168 
169   public static class CompositeGlyphBuilder extends Glyph.Builder<CompositeGlyph> {
CompositeGlyphBuilder(WritableFontData data, int offset, int length)170     protected CompositeGlyphBuilder(WritableFontData data, int offset, int length) {
171       super(data.slice(offset, length));
172     }
173 
CompositeGlyphBuilder(ReadableFontData data, int offset, int length)174     protected CompositeGlyphBuilder(ReadableFontData data, int offset, int length) {
175       super(data.slice(offset, length));
176     }
177 
178     @Override
subBuildTable(ReadableFontData data)179     protected CompositeGlyph subBuildTable(ReadableFontData data) {
180       return new CompositeGlyph(data);
181     }
182   }
183 }