Home | History | Annotate | Download | only in ports
      1 
      2 /*
      3  * Copyright 2006 The Android Open Source Project
      4  *
      5  * Use of this source code is governed by a BSD-style license that can be
      6  * found in the LICENSE file.
      7  */
      8 
      9 
     10 #include <Carbon/Carbon.h>
     11 #include "SkFontHost.h"
     12 #include "SkDescriptor.h"
     13 #include "SkEndian.h"
     14 #include "SkFloatingPoint.h"
     15 #include "SkPaint.h"
     16 #include "SkPoint.h"
     17 
     18 const char* gDefaultfont = "Arial"; // hard code for now
     19 SK_DECLARE_STATIC_MUTEX(gFTMutex);
     20 
     21 static inline SkPoint F32PtToSkPoint(const Float32Point p) {
     22     SkPoint sp = { SkFloatToScalar(p.x), SkFloatToScalar(p.y) };
     23     return sp;
     24 }
     25 
     26 static inline uint32_t _rotl(uint32_t v, uint32_t r) {
     27     return (v << r | v >> (32 - r));
     28 }
     29 
     30 class SkTypeface_Mac : public SkTypeface {
     31 public:
     32     SkTypeface_Mac(SkTypeface::Style style, uint32_t id)
     33         : SkTypeface(style, id) {}
     34 };
     35 
     36 #pragma mark -
     37 
     38 static uint32_t find_from_name(const char name[]) {
     39     CFStringRef str = CFStringCreateWithCString(NULL, name,
     40                                                 kCFStringEncodingUTF8);
     41     uint32_t fontID = ::ATSFontFindFromName(str, kATSOptionFlagsDefault);
     42     CFRelease(str);
     43     return fontID;
     44 }
     45 
     46 static uint32_t find_default_fontID() {
     47     static const char* gDefaultNames[] = { "Arial", "Tahoma", "Helvetica" };
     48 
     49     uint32_t fontID;
     50     for (size_t i = 0; i < SK_ARRAY_COUNT(gDefaultNames); i++) {
     51         fontID = find_from_name(gDefaultNames[i]);
     52         if (fontID) {
     53             return fontID;
     54         }
     55     }
     56     sk_throw();
     57     return 0;
     58 }
     59 
     60 static SkTypeface* CreateTypeface_(const char name[], SkTypeface::Style style) {
     61     uint32_t fontID = 0;
     62     if (NULL != name) {
     63         fontID = find_from_name(name);
     64     }
     65     if (0 == fontID) {
     66         fontID = find_default_fontID();
     67     }
     68     // we lie (for now) and report that we found the exact style bits
     69     return new SkTypeface_Mac(style, fontID);
     70 }
     71 
     72 #pragma mark -
     73 
     74 class SkScalerContext_Mac : public SkScalerContext {
     75 public:
     76     SkScalerContext_Mac(const SkDescriptor* desc);
     77     virtual ~SkScalerContext_Mac();
     78 
     79 protected:
     80     virtual unsigned generateGlyphCount();
     81     virtual uint16_t generateCharToGlyph(SkUnichar uni);
     82     virtual void generateAdvance(SkGlyph* glyph);
     83     virtual void generateMetrics(SkGlyph* glyph);
     84     virtual void generateImage(const SkGlyph& glyph);
     85     virtual void generatePath(const SkGlyph& glyph, SkPath* path);
     86     virtual void generateFontMetrics(SkPaint::FontMetrics* mX, SkPaint::FontMetrics* mY);
     87 
     88 private:
     89     ATSUTextLayout  fLayout;
     90     ATSUStyle       fStyle;
     91     CGColorSpaceRef fGrayColorSpace;
     92     CGAffineTransform   fTransform;
     93 
     94     static OSStatus MoveTo(const Float32Point *pt, void *cb);
     95     static OSStatus Line(const Float32Point *pt, void *cb);
     96     static OSStatus Curve(const Float32Point *pt1, const Float32Point *pt2, const Float32Point *pt3, void *cb);
     97     static OSStatus Close(void *cb);
     98 };
     99 
    100 void SkFontHost::FilterRec(SkScalerContext::Rec* rec) {
    101     // we only support 2 levels of hinting
    102     SkPaint::Hinting h = rec->getHinting();
    103     if (SkPaint::kSlight_Hinting == h) {
    104         h = SkPaint::kNo_Hinting;
    105     } else if (SkPaint::kFull_Hinting == h) {
    106         h = SkPaint::kNormal_Hinting;
    107     }
    108     rec->setHinting(h);
    109 
    110     // we don't support LCD text
    111     if (SkMask::kLCD16_Format == rec->fMaskFormat ||
    112         SkMask::kLCD32_Format == rec->fMaskFormat) {
    113         rec->fMaskFormat = SkMask::kA8_Format;
    114     }
    115 }
    116 
    117 SkScalerContext_Mac::SkScalerContext_Mac(const SkDescriptor* desc)
    118     : SkScalerContext(desc), fLayout(0), fStyle(0)
    119 {
    120     SkAutoMutexAcquire  ac(gFTMutex);
    121     OSStatus err;
    122 
    123     err = ::ATSUCreateStyle(&fStyle);
    124     SkASSERT(0 == err);
    125 
    126     SkMatrix    m;
    127     fRec.getSingleMatrix(&m);
    128 
    129     fTransform = CGAffineTransformMake(SkScalarToFloat(m[SkMatrix::kMScaleX]),
    130                                        SkScalarToFloat(m[SkMatrix::kMSkewX]),
    131                                        SkScalarToFloat(m[SkMatrix::kMSkewY]),
    132                                        SkScalarToFloat(m[SkMatrix::kMScaleY]),
    133                                        SkScalarToFloat(m[SkMatrix::kMTransX]),
    134                                        SkScalarToFloat(m[SkMatrix::kMTransY]));
    135 
    136     ATSStyleRenderingOptions renderOpts = kATSStyleApplyAntiAliasing;
    137     switch (fRec.getHinting()) {
    138         case SkPaint::kNo_Hinting:
    139         case SkPaint::kSlight_Hinting:
    140             renderOpts |= kATSStyleNoHinting;
    141             break;
    142         case SkPaint::kNormal_Hinting:
    143         case SkPaint::kFull_Hinting:
    144             renderOpts |= kATSStyleApplyHints;
    145             break;
    146     }
    147 
    148     ATSUFontID fontID = FMGetFontFromATSFontRef(fRec.fFontID);
    149     // we put everything in the matrix, so our pt size is just 1.0
    150     Fixed fixedSize = SK_Fixed1;
    151     static const ATSUAttributeTag tags[] = {
    152         kATSUFontTag, kATSUSizeTag, kATSUFontMatrixTag, kATSUStyleRenderingOptionsTag
    153     };
    154     static const ByteCount sizes[] = {
    155         sizeof(fontID), sizeof(fixedSize), sizeof(fTransform), sizeof(renderOpts)
    156     };
    157     const ATSUAttributeValuePtr values[] = {
    158         &fontID, &fixedSize, &fTransform, &renderOpts
    159     };
    160     err = ::ATSUSetAttributes(fStyle, SK_ARRAY_COUNT(tags),
    161                               tags, sizes, values);
    162     SkASSERT(0 == err);
    163 
    164     err = ::ATSUCreateTextLayout(&fLayout);
    165     SkASSERT(0 == err);
    166 
    167     fGrayColorSpace = ::CGColorSpaceCreateDeviceGray();
    168 }
    169 
    170 SkScalerContext_Mac::~SkScalerContext_Mac() {
    171     ::CGColorSpaceRelease(fGrayColorSpace);
    172     ::ATSUDisposeTextLayout(fLayout);
    173     ::ATSUDisposeStyle(fStyle);
    174 }
    175 
    176 // man, we need to consider caching this, since it is just dependent on
    177 // fFontID, and not on any of the other settings like matrix or flags
    178 unsigned SkScalerContext_Mac::generateGlyphCount() {
    179     // The 'maxp' table stores the number of glyphs a offset 4, in 2 bytes
    180     uint16_t numGlyphs;
    181     if (SkFontHost::GetTableData(fRec.fFontID,
    182                                  SkSetFourByteTag('m', 'a', 'x', 'p'),
    183                                  4, 2, &numGlyphs) != 2) {
    184         return 0xFFFF;
    185     }
    186     return SkEndian_SwapBE16(numGlyphs);
    187 }
    188 
    189 uint16_t SkScalerContext_Mac::generateCharToGlyph(SkUnichar uni)
    190 {
    191     SkAutoMutexAcquire  ac(gFTMutex);
    192 
    193     OSStatus err;
    194     UniChar achar = uni;
    195     err = ::ATSUSetTextPointerLocation(fLayout,&achar,0,1,1);
    196     err = ::ATSUSetRunStyle(fLayout,fStyle,kATSUFromTextBeginning,kATSUToTextEnd);
    197 
    198     ATSLayoutRecord *layoutPtr;
    199     ItemCount count;
    200     ATSGlyphRef glyph;
    201 
    202     err = ::ATSUDirectGetLayoutDataArrayPtrFromTextLayout(fLayout,0,kATSUDirectDataLayoutRecordATSLayoutRecordCurrent,(void**)&layoutPtr,&count);
    203     glyph = layoutPtr->glyphID;
    204     ::ATSUDirectReleaseLayoutDataArrayPtr(NULL,kATSUDirectDataLayoutRecordATSLayoutRecordCurrent,(void**)&layoutPtr);
    205     return glyph;
    206 }
    207 
    208 static void set_glyph_metrics_on_error(SkGlyph* glyph) {
    209     glyph->fRsbDelta = 0;
    210     glyph->fLsbDelta = 0;
    211     glyph->fWidth    = 0;
    212     glyph->fHeight   = 0;
    213     glyph->fTop      = 0;
    214     glyph->fLeft     = 0;
    215     glyph->fAdvanceX = 0;
    216     glyph->fAdvanceY = 0;
    217 }
    218 
    219 void SkScalerContext_Mac::generateAdvance(SkGlyph* glyph) {
    220     this->generateMetrics(glyph);
    221 }
    222 
    223 void SkScalerContext_Mac::generateMetrics(SkGlyph* glyph) {
    224     GlyphID glyphID = glyph->getGlyphID(fBaseGlyphCount);
    225     ATSGlyphScreenMetrics screenMetrics;
    226     ATSGlyphIdealMetrics idealMetrics;
    227 
    228     OSStatus err = ATSUGlyphGetScreenMetrics(fStyle, 1, &glyphID, 0, true, true,
    229                                              &screenMetrics);
    230     if (noErr != err) {
    231         set_glyph_metrics_on_error(glyph);
    232         return;
    233     }
    234     err = ATSUGlyphGetIdealMetrics(fStyle, 1, &glyphID, 0, &idealMetrics);
    235     if (noErr != err) {
    236         set_glyph_metrics_on_error(glyph);
    237         return;
    238     }
    239 
    240     if ((fRec.fFlags & SkScalerContext::kSubpixelPositioning_Flag) == 0) {
    241         glyph->fAdvanceX = SkFloatToFixed(screenMetrics.deviceAdvance.x);
    242         glyph->fAdvanceY = -SkFloatToFixed(screenMetrics.deviceAdvance.y);
    243     } else {
    244         glyph->fAdvanceX = SkFloatToFixed(idealMetrics.advance.x);
    245         glyph->fAdvanceY = -SkFloatToFixed(idealMetrics.advance.y);
    246     }
    247 
    248     // specify an extra 1-pixel border, go tive CG room for its antialiasing
    249     // i.e. without this, I was seeing some edges chopped off!
    250     glyph->fWidth = screenMetrics.width + 2;
    251     glyph->fHeight = screenMetrics.height + 2;
    252     glyph->fLeft = sk_float_round2int(screenMetrics.topLeft.x) - 1;
    253     glyph->fTop = -sk_float_round2int(screenMetrics.topLeft.y) - 1;
    254 }
    255 
    256 void SkScalerContext_Mac::generateImage(const SkGlyph& glyph)
    257 {
    258     SkAutoMutexAcquire  ac(gFTMutex);
    259     SkASSERT(fLayout);
    260 
    261     sk_bzero(glyph.fImage, glyph.fHeight * glyph.rowBytes());
    262     CGContextRef contextRef = ::CGBitmapContextCreate(glyph.fImage,
    263                                               glyph.fWidth, glyph.fHeight, 8,
    264                                               glyph.rowBytes(), fGrayColorSpace,
    265                                               kCGImageAlphaNone);
    266     if (!contextRef) {
    267         SkASSERT(false);
    268         return;
    269     }
    270 
    271     ::CGContextSetGrayFillColor(contextRef, 1.0, 1.0);
    272     ::CGContextSetTextDrawingMode(contextRef, kCGTextFill);
    273 
    274     CGGlyph glyphID = glyph.getGlyphID(fBaseGlyphCount);
    275     CGFontRef fontRef = CGFontCreateWithPlatformFont(&fRec.fFontID);
    276     CGContextSetFont(contextRef, fontRef);
    277     CGContextSetFontSize(contextRef, 1);
    278     CGContextSetTextMatrix(contextRef, fTransform);
    279     CGContextShowGlyphsAtPoint(contextRef, -glyph.fLeft,
    280                                glyph.fTop + glyph.fHeight, &glyphID, 1);
    281 
    282     ::CGContextRelease(contextRef);
    283 }
    284 
    285 #if 0
    286 static void convert_metrics(SkPaint::FontMetrics* dst,
    287                             const ATSFontMetrics& src) {
    288     dst->fTop     = -SkFloatToScalar(src.ascent);
    289     dst->fAscent  = -SkFloatToScalar(src.ascent);
    290     dst->fDescent = SkFloatToScalar(src.descent);
    291     dst->fBottom  = SkFloatToScalar(src.descent);
    292     dst->fLeading = SkFloatToScalar(src.leading);
    293 }
    294 #endif
    295 
    296 static void* get_font_table(ATSFontRef fontID, uint32_t tag) {
    297     ByteCount size;
    298     OSStatus err = ATSFontGetTable(fontID, tag, 0, 0, NULL, &size);
    299     if (err) {
    300         return NULL;
    301     }
    302     void* data = sk_malloc_throw(size);
    303     err = ATSFontGetTable(fontID, tag, 0, size, data, &size);
    304     if (err) {
    305         sk_free(data);
    306         data = NULL;
    307     }
    308     return data;
    309 }
    310 
    311 static int get_be16(const void* data, size_t offset) {
    312     const char* ptr = reinterpret_cast<const char*>(data);
    313     uint16_t value = *reinterpret_cast<const uint16_t*>(ptr + offset);
    314     int n = SkEndian_SwapBE16(value);
    315     // now force it to be signed
    316     return n << 16 >> 16;
    317 }
    318 
    319 #define SFNT_HEAD_UPEM_OFFSET       18
    320 #define SFNT_HEAD_YMIN_OFFSET       38
    321 #define SFNT_HEAD_YMAX_OFFSET       42
    322 #define SFNT_HEAD_STYLE_OFFSET      44
    323 
    324 #define SFNT_HHEA_ASCENT_OFFSET     4
    325 #define SFNT_HHEA_DESCENT_OFFSET    6
    326 #define SFNT_HHEA_LEADING_OFFSET    8
    327 
    328 static bool init_vertical_metrics(ATSFontRef font, SkPoint pts[5]) {
    329     void* head = get_font_table(font, 'head');
    330     if (NULL == head) {
    331         return false;
    332     }
    333     void* hhea = get_font_table(font, 'hhea');
    334     if (NULL == hhea) {
    335         sk_free(head);
    336         return false;
    337     }
    338 
    339     int upem = get_be16(head, SFNT_HEAD_UPEM_OFFSET);
    340     int ys[5];
    341 
    342     ys[0] = -get_be16(head, SFNT_HEAD_YMAX_OFFSET);
    343     ys[1] = -get_be16(hhea, SFNT_HHEA_ASCENT_OFFSET);
    344     ys[2] = -get_be16(hhea, SFNT_HHEA_DESCENT_OFFSET);
    345     ys[3] = -get_be16(head, SFNT_HEAD_YMIN_OFFSET);
    346     ys[4] =  get_be16(hhea, SFNT_HHEA_LEADING_OFFSET);
    347 
    348     // now do some cleanup, to ensure y[max,min] are really that
    349     if (ys[0] > ys[1]) {
    350         ys[0] = ys[1];
    351     }
    352     if (ys[3] < ys[2]) {
    353         ys[3] = ys[2];
    354     }
    355 
    356     for (int i = 0; i < 5; i++) {
    357         pts[i].set(0, SkIntToScalar(ys[i]) / upem);
    358     }
    359 
    360     sk_free(hhea);
    361     sk_free(head);
    362     return true;
    363 }
    364 
    365 void SkScalerContext_Mac::generateFontMetrics(SkPaint::FontMetrics* mx,
    366                                               SkPaint::FontMetrics* my) {
    367     SkPoint pts[5];
    368 
    369     if (!init_vertical_metrics(fRec.fFontID, pts)) {
    370         // these are not as accurate as init_vertical_metrics :(
    371         ATSFontMetrics metrics;
    372         ATSFontGetVerticalMetrics(fRec.fFontID, kATSOptionFlagsDefault,
    373                                   &metrics);
    374         pts[0].set(0, -SkFloatToScalar(metrics.ascent));
    375         pts[1].set(0, -SkFloatToScalar(metrics.ascent));
    376         pts[2].set(0, -SkFloatToScalar(metrics.descent));
    377         pts[3].set(0, -SkFloatToScalar(metrics.descent));
    378         pts[4].set(0, SkFloatToScalar(metrics.leading));    //+ or -?
    379     }
    380 
    381     SkMatrix m;
    382     fRec.getSingleMatrix(&m);
    383     m.mapPoints(pts, 5);
    384 
    385     if (mx) {
    386         mx->fTop = pts[0].fX;
    387         mx->fAscent = pts[1].fX;
    388         mx->fDescent = pts[2].fX;
    389         mx->fBottom = pts[3].fX;
    390         mx->fLeading = pts[4].fX;
    391         // FIXME:
    392         mx->fAvgCharWidth = 0;
    393         mx->fXMin = 0;
    394         mx->fXMax = 0;
    395         mx->fXHeight = 0;
    396     }
    397     if (my) {
    398         my->fTop = pts[0].fY;
    399         my->fAscent = pts[1].fY;
    400         my->fDescent = pts[2].fY;
    401         my->fBottom = pts[3].fY;
    402         my->fLeading = pts[4].fY;
    403         // FIXME:
    404         my->fAvgCharWidth = 0;
    405         my->fXMin = 0;
    406         my->fXMax = 0;
    407         my->fXHeight = 0;
    408     }
    409 }
    410 
    411 void SkScalerContext_Mac::generatePath(const SkGlyph& glyph, SkPath* path)
    412 {
    413     SkAutoMutexAcquire  ac(gFTMutex);
    414     OSStatus err,result;
    415 
    416     err = ::ATSUGlyphGetCubicPaths(
    417             fStyle,glyph.fID,
    418             &SkScalerContext_Mac::MoveTo,
    419             &SkScalerContext_Mac::Line,
    420             &SkScalerContext_Mac::Curve,
    421             &SkScalerContext_Mac::Close,
    422             path,&result);
    423     SkASSERT(err == noErr);
    424 }
    425 
    426 OSStatus SkScalerContext_Mac::MoveTo(const Float32Point *pt, void *cb)
    427 {
    428     reinterpret_cast<SkPath*>(cb)->moveTo(F32PtToSkPoint(*pt));
    429     return noErr;
    430 }
    431 
    432 OSStatus SkScalerContext_Mac::Line(const Float32Point *pt, void *cb)
    433 {
    434     reinterpret_cast<SkPath*>(cb)->lineTo(F32PtToSkPoint(*pt));
    435     return noErr;
    436 }
    437 
    438 OSStatus SkScalerContext_Mac::Curve(const Float32Point *pt1,
    439                                     const Float32Point *pt2,
    440                                     const Float32Point *pt3, void *cb)
    441 {
    442     reinterpret_cast<SkPath*>(cb)->cubicTo(F32PtToSkPoint(*pt1),
    443                                            F32PtToSkPoint(*pt2),
    444                                            F32PtToSkPoint(*pt3));
    445     return noErr;
    446 }
    447 
    448 OSStatus SkScalerContext_Mac::Close(void *cb)
    449 {
    450     reinterpret_cast<SkPath*>(cb)->close();
    451     return noErr;
    452 }
    453 
    454 #pragma mark -
    455 
    456 void SkFontHost::Serialize(const SkTypeface* face, SkWStream* stream) {
    457     SkDEBUGFAIL("SkFontHost::Serialize unimplemented");
    458 }
    459 
    460 SkTypeface* SkFontHost::Deserialize(SkStream* stream) {
    461     SkDEBUGFAIL("SkFontHost::Deserialize unimplemented");
    462     return NULL;
    463 }
    464 
    465 SkTypeface* SkFontHost::CreateTypefaceFromStream(SkStream* stream) {
    466     return NULL;
    467 }
    468 
    469 SkTypeface* SkFontHost::CreateTypefaceFromFile(const char path[]) {
    470     return NULL;
    471 }
    472 
    473 // static
    474 SkAdvancedTypefaceMetrics* SkFontHost::GetAdvancedTypefaceMetrics(
    475         uint32_t fontID,
    476         SkAdvancedTypefaceMetrics::PerGlyphInfo perGlyphInfo,
    477         const uint32_t* glyphIDs,
    478         uint32_t glyphIDsCount) {
    479     SkDEBUGFAIL("SkFontHost::GetAdvancedTypefaceMetrics unimplemented");
    480     return NULL;
    481 }
    482 
    483 SkScalerContext* SkFontHost::CreateScalerContext(const SkDescriptor* desc) {
    484     return new SkScalerContext_Mac(desc);
    485 }
    486 
    487 SkFontID SkFontHost::NextLogicalFont(SkFontID currFontID, SkFontID origFontID) {
    488     uint32_t newFontID = find_default_fontID();
    489     if (newFontID == currFontID) {
    490         newFontID = 0;
    491     }
    492     return newFontID;
    493 }
    494 
    495 SkTypeface* SkFontHost::CreateTypeface(const SkTypeface* familyFace,
    496                             const char familyName[],
    497                             const void* data, size_t bytelength,
    498                             SkTypeface::Style style) {
    499     // todo: we don't know how to respect style bits
    500     if (NULL == familyName && NULL != familyFace) {
    501         familyFace->ref();
    502         return const_cast<SkTypeface*>(familyFace);
    503     } else {
    504         return CreateTypeface_(familyName, style);
    505     }
    506 }
    507 
    508 ///////////////////////////////////////////////////////////////////////////////
    509 
    510 struct SkSFNTHeader {
    511     uint32_t    fVersion;
    512     uint16_t    fNumTables;
    513     uint16_t    fSearchRange;
    514     uint16_t    fEntrySelector;
    515     uint16_t    fRangeShift;
    516 };
    517 
    518 struct SkSFNTDirEntry {
    519     uint32_t    fTag;
    520     uint32_t    fChecksum;
    521     uint32_t    fOffset;
    522     uint32_t    fLength;
    523 };
    524 
    525 struct SfntHeader {
    526     SfntHeader(SkFontID fontID, bool needDir) : fCount(0), fData(NULL) {
    527         ByteCount size;
    528         if (ATSFontGetTableDirectory(fontID, 0, NULL, &size)) {
    529             return;
    530         }
    531 
    532         SkAutoMalloc storage(size);
    533         SkSFNTHeader* header = reinterpret_cast<SkSFNTHeader*>(storage.get());
    534         if (ATSFontGetTableDirectory(fontID, size, header, &size)) {
    535             return;
    536         }
    537 
    538         fCount = SkEndian_SwapBE16(header->fNumTables);
    539         fData = header;
    540         storage.detach();
    541     }
    542 
    543     ~SfntHeader() {
    544         sk_free(fData);
    545     }
    546 
    547     int count() const { return fCount; }
    548     const SkSFNTDirEntry* entries() const {
    549         return reinterpret_cast<const SkSFNTDirEntry*>
    550             (reinterpret_cast<char*>(fData) + sizeof(SkSFNTHeader));
    551     }
    552 
    553 private:
    554     int     fCount;
    555     void*   fData;
    556 };
    557 
    558 int SkFontHost::CountTables(SkFontID fontID) {
    559     SfntHeader header(fontID, false);
    560     return header.count();
    561 }
    562 
    563 int SkFontHost::GetTableTags(SkFontID fontID, SkFontTableTag tags[]) {
    564     SfntHeader header(fontID, true);
    565     int count = header.count();
    566     const SkSFNTDirEntry* entry = header.entries();
    567     for (int i = 0; i < count; i++) {
    568         tags[i] = SkEndian_SwapBE32(entry[i].fTag);
    569     }
    570     return count;
    571 }
    572 
    573 size_t SkFontHost::GetTableSize(SkFontID fontID, SkFontTableTag tag) {
    574     ByteCount size;
    575     if (ATSFontGetTable(fontID, tag, 0, 0, NULL, &size)) {
    576         return 0;
    577     }
    578     return size;
    579 }
    580 
    581 size_t SkFontHost::GetTableData(SkFontID fontID, SkFontTableTag tag,
    582                                 size_t offset, size_t length, void* data) {
    583     ByteCount size;
    584     if (ATSFontGetTable(fontID, tag, offset, length, data, &size)) {
    585         return 0;
    586     }
    587     if (offset >= size) {
    588         return 0;
    589     }
    590     if (offset + length > size) {
    591         length = size - offset;
    592     }
    593     return length;
    594 }
    595