• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright 2010 Google Inc. All Rights Reserved.
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  * http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 package com.google.typography.font.sfntly.table.core;
18 
19 import com.google.typography.font.sfntly.data.ReadableFontData;
20 import com.google.typography.font.sfntly.data.WritableFontData;
21 import com.google.typography.font.sfntly.table.Header;
22 import com.google.typography.font.sfntly.table.Table;
23 import com.google.typography.font.sfntly.table.TableBasedTableBuilder;
24 import com.google.typography.font.sfntly.table.truetype.LocaTable;
25 
26 import java.util.EnumSet;
27 
28 /**
29  * A Font Header table.
30  *
31  * @author Stuart Gill
32  */
33 public final class FontHeaderTable extends Table {
34 
35   /**
36    * Checksum adjustment base value. To compute the checksum adjustment:
37    * 1) set it to 0; 2) sum the entire font as ULONG, 3) then store 0xB1B0AFBA - sum.
38    */
39   public static final long CHECKSUM_ADJUSTMENT_BASE = 0xB1B0AFBAL;
40 
41   /**
42    * Magic number value stored in the magic number field.
43    */
44   public static final long MAGIC_NUMBER = 0x5F0F3CF5L;
45 
46   /**
47    * The ranges to use for checksum calculation.
48    */
49   private static final int[] CHECKSUM_RANGES =
50     new int[] {0, Offset.checkSumAdjustment.offset, Offset.magicNumber.offset};
51 
52   /**
53    * Offsets to specific elements in the underlying data. These offsets are relative to the
54    * start of the table or the start of sub-blocks within the table.
55    */
56   private enum Offset {
57     tableVersion(0),
58     fontRevision(4),
59     checkSumAdjustment(8),
60     magicNumber(12),
61     flags(16),
62     unitsPerEm(18),
63     created(20),
64     modified(28),
65     xMin(36),
66     yMin(38),
67     xMax(40),
68     yMax(42),
69     macStyle(44),
70     lowestRecPPEM(46),
71     fontDirectionHint(48),
72     indexToLocFormat(50),
73     glyphDataFormat(52);
74 
75     private final int offset;
76 
Offset(int offset)77     private Offset(int offset) {
78       this.offset = offset;
79     }
80   }
81 
82   /**
83    * Constructor.
84    *
85    * @param header the table header
86    * @param data the readable data for the table
87    */
FontHeaderTable(Header header, ReadableFontData data)88   private FontHeaderTable(Header header, ReadableFontData data) {
89     super(header, data);
90     data.setCheckSumRanges(0, Offset.checkSumAdjustment.offset, Offset.magicNumber.offset);
91   }
92 
93   /**
94    * Get the table version.
95    *
96    * @return the table version
97    */
tableVersion()98   public int tableVersion() {
99     return this.data.readFixed(Offset.tableVersion.offset);
100   }
101 
102   /**
103    * Get the font revision.
104    *
105    * @return the font revision
106    */
fontRevision()107   public int fontRevision() {
108     return this.data.readFixed(Offset.fontRevision.offset);
109   }
110 
111   /**
112    * Get the checksum adjustment. To compute: set it to 0, sum the entire font
113    * as ULONG, then store 0xB1B0AFBA - sum.
114    *
115    * @return checksum adjustment
116    */
checkSumAdjustment()117   public long checkSumAdjustment() {
118     return this.data.readULong(Offset.checkSumAdjustment.offset);
119   }
120 
121   /**
122    * Get the magic number. Set to 0x5F0F3CF5.
123    *
124    * @return the magic number
125    */
magicNumber()126   public long magicNumber() {
127     return this.data.readULong(Offset.magicNumber.offset);
128   }
129 
130   /**
131    * Flag values in the font header table.
132    *
133    */
134   public enum Flags {
135     BaselineAtY0,
136     LeftSidebearingAtX0,
137     InstructionsDependOnPointSize,
138     ForcePPEMToInteger,
139     InstructionsAlterAdvanceWidth,
140     //Apple Flags
141     Apple_Vertical,
142     Apple_Zero,
143     Apple_RequiresLayout,
144     Apple_GXMetamorphosis,
145     Apple_StrongRTL,
146     Apple_IndicRearrangement,
147 
148     FontDataLossless,
149     FontConverted,
150     OptimizedForClearType,
151     Reserved14,
152     Reserved15;
153 
mask()154     public int mask() {
155       return 1 << this.ordinal();
156     }
157 
asSet(int value)158     public static EnumSet<Flags> asSet(int value) {
159       EnumSet<Flags> set = EnumSet.noneOf(Flags.class);
160       for (Flags flag : Flags.values()) {
161         if ((value & flag.mask()) == flag.mask()) {
162           set.add(flag);
163         }
164       }
165       return set;
166     }
167 
value(EnumSet<Flags> set)168     static public int value(EnumSet<Flags> set) {
169       int value = 0;
170       for (Flags flag : set) {
171         value |= flag.mask();
172       }
173       return value;
174     }
175 
cleanValue(EnumSet<Flags> set)176     static public int cleanValue(EnumSet<Flags> set) {
177       EnumSet<Flags> clean = EnumSet.copyOf(set);
178       clean.remove(Flags.Reserved14);
179       clean.remove(Flags.Reserved15);
180       return value(clean);
181     }
182   }
183 
184   /**
185    * Get the flags as an int value.
186    *
187    * @return the flags
188    */
flagsAsInt()189   public int flagsAsInt() {
190     return this.data.readUShort(Offset.flags.offset);
191   }
192 
193   /**
194    * Get the flags as an enum set.
195    *
196    * @return the enum set of the flags
197    */
flags()198   public EnumSet<Flags> flags() {
199     return Flags.asSet(this.flagsAsInt());
200   }
201 
202   /**
203    * Get the units per em.
204    *
205    * @return the units per em
206    */
unitsPerEm()207   public int unitsPerEm() {
208     return this.data.readUShort(Offset.unitsPerEm.offset);
209   }
210 
211   /**
212    * Get the created date. Number of seconds since 12:00 midnight, January 1,
213    * 1904. 64-bit integer.
214    *
215    * @return created date
216    */
created()217   public long created() {
218     return this.data.readDateTimeAsLong(Offset.created.offset);
219   }
220 
221   /**
222    * Get the modified date. Number of seconds since 12:00 midnight, January 1,
223    * 1904. 64-bit integer.
224    *
225    * @return created date
226    */
modified()227   public long modified() {
228     return this.data.readDateTimeAsLong(Offset.modified.offset);
229   }
230 
231   /**
232    * Get the x min. For all glyph bounding boxes.
233    *
234    * @return the x min
235    */
xMin()236   public int xMin() {
237     return this.data.readShort(Offset.xMin.offset);
238   }
239 
240   /**
241    * Get the y min. For all glyph bounding boxes.
242    *
243    * @return the y min
244    */
yMin()245   public int yMin() {
246     return this.data.readShort(Offset.yMin.offset);
247   }
248 
249   /**
250    * Get the x max. For all glyph bounding boxes.
251    *
252    * @return the xmax
253    */
xMax()254   public int xMax() {
255     return this.data.readShort(Offset.xMax.offset);
256   }
257 
258   /**
259    * Get the y max. For all glyph bounding boxes.
260    *
261    * @return the ymax
262    */
yMax()263   public int yMax() {
264     return this.data.readShort(Offset.yMax.offset);
265   }
266 
267   /**
268    * Mac style bits set in the font header table.
269    *
270    */
271   public enum MacStyle {
272     Bold,
273     Italic,
274     Underline,
275     Outline,
276     Shadow,
277     Condensed,
278     Extended,
279     Reserved7,
280     Reserved8,
281     Reserved9,
282     Reserved10,
283     Reserved11,
284     Reserved12,
285     Reserved13,
286     Reserved14,
287     Reserved15;
288 
mask()289     public int mask() {
290       return 1 << this.ordinal();
291     }
292 
asSet(int value)293     public static EnumSet<MacStyle> asSet(int value) {
294       EnumSet<MacStyle> set = EnumSet.noneOf(MacStyle.class);
295       for (MacStyle style : MacStyle.values()) {
296         if ((value & style.mask()) == style.mask()) {
297           set.add(style);
298         }
299       }
300       return set;
301     }
302 
value(EnumSet<MacStyle> set)303     public static int value(EnumSet<MacStyle> set) {
304       int value = 0;
305       for (MacStyle style : set) {
306         value |= style.mask();
307       }
308       return value;
309     }
310 
cleanValue(EnumSet<MacStyle> set)311     public static int cleanValue(EnumSet<MacStyle> set) {
312       EnumSet<MacStyle> clean = EnumSet.copyOf(set);
313       clean.removeAll(reserved);
314       return value(clean);
315     }
316 
317     private static final EnumSet<MacStyle> reserved =
318       EnumSet.range(MacStyle.Reserved7, MacStyle.Reserved15);
319   }
320 
321   /**
322    * Get the Mac style bits as an int.
323    *
324    * @return the Mac style bits
325    */
macStyleAsInt()326   public int macStyleAsInt() {
327     return this.data.readUShort(Offset.macStyle.offset);
328   }
329 
330   /**
331    * Get the Mac style bits as an enum set.
332    *
333    * @return the Mac style bits
334    */
macStyle()335   public EnumSet<MacStyle> macStyle() {
336     return MacStyle.asSet(this.macStyleAsInt());
337   }
338 
lowestRecPPEM()339   public int lowestRecPPEM() {
340     return this.data.readUShort(Offset.lowestRecPPEM.offset);
341   }
342 
343   /**
344    * Font direction hint values in the font header table.
345    *
346    */
347   public enum FontDirectionHint {
348     FullyMixed(0),
349     OnlyStrongLTR(1),
350     StrongLTRAndNeutral(2),
351     OnlyStrongRTL(-1),
352     StrongRTLAndNeutral(-2);
353 
354     private final int value;
355 
FontDirectionHint(int value)356     private FontDirectionHint(int value) {
357       this.value = value;
358     }
359 
value()360     public int value() {
361       return this.value;
362     }
363 
equals(int value)364     public boolean equals(int value) {
365       return value == this.value;
366     }
367 
valueOf(int value)368     public static FontDirectionHint valueOf(int value) {
369       for (FontDirectionHint hint : FontDirectionHint.values()) {
370         if (hint.equals(value)) {
371           return hint;
372         }
373       }
374       return null;
375     }
376   }
377 
fontDirectionHintAsInt()378   public int fontDirectionHintAsInt() {
379     return this.data.readShort(Offset.fontDirectionHint.offset);
380   }
381 
fontDirectionHint()382   public FontDirectionHint fontDirectionHint() {
383     return FontDirectionHint.valueOf(this.fontDirectionHintAsInt());
384   }
385 
386   /**
387    * The index to location format used in the LocaTable.
388    *
389    * @see LocaTable
390    */
391   public enum IndexToLocFormat {
392     shortOffset(0),
393     longOffset(1);
394 
395     private final int value;
396 
IndexToLocFormat(int value)397     private IndexToLocFormat(int value) {
398       this.value = value;
399     }
400 
value()401     public int value() {
402       return this.value;
403     }
404 
equals(int value)405     public boolean equals(int value) {
406       return value == this.value;
407     }
408 
valueOf(int value)409     public static IndexToLocFormat valueOf(int value) {
410       for (IndexToLocFormat format : IndexToLocFormat.values()) {
411         if (format.equals(value)) {
412           return format;
413         }
414       }
415       return null;
416     }
417   }
418 
indexToLocFormatAsInt()419   public int indexToLocFormatAsInt() {
420     return this.data.readShort(Offset.indexToLocFormat.offset);
421   }
422 
indexToLocFormat()423   public IndexToLocFormat indexToLocFormat() {
424     return IndexToLocFormat.valueOf(this.indexToLocFormatAsInt());
425   }
426 
glyphdataFormat()427   public int glyphdataFormat() {
428     return this.data.readShort(Offset.glyphDataFormat.offset);
429   }
430 
431   public static class Builder extends TableBasedTableBuilder<FontHeaderTable> {
432     private boolean fontChecksumSet = false;
433     private long fontChecksum = 0;
434 
435     /**
436      * Create a new builder using the header information and data provided.
437      *
438      * @param header the header information
439      * @param data the data holding the table
440      * @return a new builder
441      */
createBuilder(Header header, WritableFontData data)442     public static Builder createBuilder(Header header, WritableFontData data) {
443       return new Builder(header, data);
444     }
445 
Builder(Header header, WritableFontData data)446     protected Builder(Header header, WritableFontData data) {
447       super(header, data);
448       data.setCheckSumRanges(0, Offset.checkSumAdjustment.offset, Offset.magicNumber.offset);
449     }
450 
Builder(Header header, ReadableFontData data)451     protected Builder(Header header, ReadableFontData data) {
452       super(header, data);
453       data.setCheckSumRanges(FontHeaderTable.CHECKSUM_RANGES);
454     }
455 
456     @Override
subReadyToSerialize()457     protected boolean subReadyToSerialize() {
458       if (this.dataChanged()) {
459         ReadableFontData data = this.internalReadData();
460         data.setCheckSumRanges(FontHeaderTable.CHECKSUM_RANGES);
461       }
462       if (this.fontChecksumSet) {
463         ReadableFontData data = this.internalReadData();
464         data.setCheckSumRanges(FontHeaderTable.CHECKSUM_RANGES);
465         long checksumAdjustment =
466           FontHeaderTable.CHECKSUM_ADJUSTMENT_BASE - (this.fontChecksum + data.checksum());
467         this.setCheckSumAdjustment(checksumAdjustment);
468       }
469       return super.subReadyToSerialize();
470     }
471 
472     @Override
subBuildTable(ReadableFontData data)473     protected FontHeaderTable subBuildTable(ReadableFontData data) {
474       return new FontHeaderTable(this.header(), data);
475     }
476 
477     /**
478      * Sets the font checksum to be used when calculating the the checksum
479      * adjustment for the header table during build time.
480      *
481      * The font checksum is the sum value of all tables but the font header
482      * table. If the font checksum has been set then further setting will be
483      * ignored until the font check sum has been cleared with
484      * {@link #clearFontChecksum()}. Most users will never need to set this. It
485      * is used when the font is being built. If set by a client it can interfere
486      * with that process.
487      *
488      * @param checksum
489      *          the font checksum
490      */
setFontChecksum(long checksum)491     public void setFontChecksum(long checksum) {
492       if (this.fontChecksumSet) {
493         return;
494       }
495       this.fontChecksumSet = true;
496       this.fontChecksum = checksum;
497     }
498 
499     /**
500      * Clears the font checksum to be used when calculating the the checksum
501      * adjustment for the header table during build time.
502      *
503      * The font checksum is the sum value of all tables but the font header
504      * table. If the font checksum has been set then further setting will be
505      * ignored until the font check sum has been cleared.
506      *
507      */
clearFontChecksum()508     public void clearFontChecksum() {
509       this.fontChecksumSet = false;
510     }
511 
tableVersion()512     public int tableVersion() {
513       return this.table().tableVersion();
514     }
515 
setTableVersion(int version)516     public void setTableVersion(int version) {
517       this.internalWriteData().writeFixed(Offset.tableVersion.offset, version);
518     }
519 
fontRevision()520     public int fontRevision() {
521       return this.table().fontRevision();
522     }
523 
setFontRevision(int revision)524     public void setFontRevision(int revision) {
525       this.internalWriteData().writeFixed(Offset.fontRevision.offset, revision);
526     }
527 
checkSumAdjustment()528     public long checkSumAdjustment() {
529       return this.table().checkSumAdjustment();
530     }
531 
setCheckSumAdjustment(long adjustment)532     public void setCheckSumAdjustment(long adjustment) {
533       this.internalWriteData().writeULong(Offset.checkSumAdjustment.offset, adjustment);
534     }
535 
magicNumber()536     public long magicNumber() {
537       return this.table().magicNumber();
538     }
539 
setMagicNumber(long magicNumber)540     public void setMagicNumber(long magicNumber) {
541       this.internalWriteData().writeULong(Offset.magicNumber.offset, magicNumber);
542     }
543 
flagsAsInt()544     public int flagsAsInt() {
545       return this.table().flagsAsInt();
546     }
547 
flags()548     public EnumSet<Flags> flags() {
549       return this.table().flags();
550     }
551 
setFlagsAsInt(int flags)552     public void setFlagsAsInt(int flags) {
553       this.internalWriteData().writeUShort(Offset.flags.offset, flags);
554     }
555 
setFlags(EnumSet<Flags> flags)556     public void setFlags(EnumSet<Flags> flags) {
557       setFlagsAsInt(Flags.cleanValue(flags));
558     }
559 
unitsPerEm()560     public int unitsPerEm() {
561       return this.table().unitsPerEm();
562     }
563 
setUnitsPerEm(int units)564     public void setUnitsPerEm(int units) {
565       this.internalWriteData().writeUShort(Offset.unitsPerEm.offset, units);
566     }
567 
created()568     public long created() {
569       return this.table().created();
570     }
571 
setCreated(long date)572     public void setCreated(long date) {
573       this.internalWriteData().writeDateTime(Offset.created.offset, date);
574     }
575 
modified()576     public long modified() {
577       return this.table().modified();
578     }
579 
setModified(long date)580     public void setModified(long date) {
581       this.internalWriteData().writeDateTime(Offset.modified.offset, date);
582     }
583 
xMin()584     public int xMin() {
585       return this.table().xMin();
586     }
587 
setXMin(int xmin)588     public void setXMin(int xmin) {
589       this.internalWriteData().writeShort(Offset.xMin.offset, xmin);
590     }
591 
yMin()592     public int yMin() {
593       return this.table().yMin();
594     }
595 
setYMin(int ymin)596     public void setYMin(int ymin) {
597       this.internalWriteData().writeShort(Offset.yMin.offset, ymin);
598     }
599 
xMax()600     public int xMax() {
601       return this.table().xMax();
602     }
603 
setXMax(int xmax)604     public void setXMax(int xmax) {
605       this.internalWriteData().writeShort(Offset.xMax.offset, xmax);
606     }
607 
yMax()608     public int yMax() {
609       return this.table().yMax();
610     }
611 
setYMax(int ymax)612     public void setYMax(int ymax) {
613       this.internalWriteData().writeShort(Offset.yMax.offset, ymax);
614     }
615 
macStyleAsInt()616     public int macStyleAsInt() {
617       return this.table().macStyleAsInt();
618     }
619 
setMacStyleAsInt(int style)620     public void setMacStyleAsInt(int style) {
621       this.internalWriteData().writeUShort(Offset.macStyle.offset, style);
622     }
623 
macStyle()624     public EnumSet<MacStyle> macStyle() {
625       return this.table().macStyle();
626     }
627 
macStyle(EnumSet<MacStyle> style)628     public void macStyle(EnumSet<MacStyle> style) {
629       this.setMacStyleAsInt(MacStyle.cleanValue(style));
630     }
631 
lowestRecPPEM()632     public int lowestRecPPEM() {
633       return this.table().lowestRecPPEM();
634     }
635 
setLowestRecPPEM(int size)636     public void setLowestRecPPEM(int size) {
637       this.internalWriteData().writeUShort(Offset.lowestRecPPEM.offset, size);
638     }
639 
fontDirectionHintAsInt()640     public int fontDirectionHintAsInt() {
641       return this.table().fontDirectionHintAsInt();
642     }
643 
setFontDirectionHintAsInt(int hint)644     public void setFontDirectionHintAsInt(int hint) {
645       this.internalWriteData().writeShort(Offset.fontDirectionHint.offset, hint);
646     }
647 
fontDirectionHint()648     public FontDirectionHint fontDirectionHint() {
649       return this.table().fontDirectionHint();
650     }
651 
setFontDirectionHint(FontDirectionHint hint)652     public void setFontDirectionHint(FontDirectionHint hint) {
653       this.setFontDirectionHintAsInt(hint.value());
654     }
655 
indexToLocFormatAsInt()656     public int indexToLocFormatAsInt() {
657       return this.table().indexToLocFormatAsInt();
658     }
659 
setIndexToLocFormatAsInt(int format)660     public void setIndexToLocFormatAsInt(int format) {
661       this.internalWriteData().writeShort(Offset.indexToLocFormat.offset, format);
662     }
663 
indexToLocFormat()664     public IndexToLocFormat indexToLocFormat() {
665       return this.table().indexToLocFormat();
666     }
667 
setIndexToLocFormat(IndexToLocFormat format)668     public void setIndexToLocFormat(IndexToLocFormat format) {
669       this.setIndexToLocFormatAsInt(format.value());
670     }
671 
glyphdataFormat()672     public int glyphdataFormat() {
673       return this.table().glyphdataFormat();
674     }
675 
setGlyphdataFormat(int format)676     public void setGlyphdataFormat(int format) {
677       this.internalWriteData().writeShort(Offset.glyphDataFormat.offset, format);
678     }
679   }
680 }
681