Home | History | Annotate | Download | only in src
      1 // Copyright (c) 2009 The Chromium Authors. All rights reserved.
      2 // Use of this source code is governed by a BSD-style license that can be
      3 // found in the LICENSE file.
      4 
      5 #include "vdmx.h"
      6 
      7 // VDMX - Vertical Device Metrics
      8 // http://www.microsoft.com/opentype/otspec/vdmx.htm
      9 
     10 #define DROP_THIS_TABLE \
     11   do { delete file->vdmx; file->vdmx = 0; } while (0)
     12 
     13 namespace ots {
     14 
     15 bool ots_vdmx_parse(OpenTypeFile *file, const uint8_t *data, size_t length) {
     16   Buffer table(data, length);
     17   file->vdmx = new OpenTypeVDMX;
     18   OpenTypeVDMX * const vdmx = file->vdmx;
     19 
     20   if (!table.ReadU16(&vdmx->version) ||
     21       !table.ReadU16(&vdmx->num_recs) ||
     22       !table.ReadU16(&vdmx->num_ratios)) {
     23     return OTS_FAILURE();
     24   }
     25 
     26   if (vdmx->version > 1) {
     27     OTS_WARNING("bad version: %u", vdmx->version);
     28     DROP_THIS_TABLE;
     29     return true;  // continue transcoding
     30   }
     31 
     32   vdmx->rat_ranges.reserve(vdmx->num_ratios);
     33   for (unsigned i = 0; i < vdmx->num_ratios; ++i) {
     34     OpenTypeVDMXRatioRecord rec;
     35 
     36     if (!table.ReadU8(&rec.charset) ||
     37         !table.ReadU8(&rec.x_ratio) ||
     38         !table.ReadU8(&rec.y_start_ratio) ||
     39         !table.ReadU8(&rec.y_end_ratio)) {
     40       return OTS_FAILURE();
     41     }
     42 
     43     if (rec.charset > 1) {
     44       OTS_WARNING("bad charset: %u", rec.charset);
     45       DROP_THIS_TABLE;
     46       return true;
     47     }
     48 
     49     if (rec.y_start_ratio > rec.y_end_ratio) {
     50       OTS_WARNING("bad y ratio");
     51       DROP_THIS_TABLE;
     52       return true;
     53     }
     54 
     55     // All values set to zero signal the default grouping to use;
     56     // if present, this must be the last Ratio group in the table.
     57     if ((i < vdmx->num_ratios - 1u) &&
     58         (rec.x_ratio == 0) &&
     59         (rec.y_start_ratio == 0) &&
     60         (rec.y_end_ratio == 0)) {
     61       // workaround for fonts which have 2 or more {0, 0, 0} terminators.
     62       OTS_WARNING("superfluous terminator found");
     63       DROP_THIS_TABLE;
     64       return true;
     65     }
     66 
     67     vdmx->rat_ranges.push_back(rec);
     68   }
     69 
     70   vdmx->offsets.reserve(vdmx->num_ratios);
     71   const size_t current_offset = table.offset();
     72   // current_offset is less than (2 bytes * 3) + (4 bytes * USHRT_MAX) = 256k.
     73   for (unsigned i = 0; i < vdmx->num_ratios; ++i) {
     74     uint16_t offset;
     75     if (!table.ReadU16(&offset)) {
     76       return OTS_FAILURE();
     77     }
     78     if (current_offset + offset >= length) {  // thus doesn't overflow.
     79       return OTS_FAILURE();
     80     }
     81 
     82     vdmx->offsets.push_back(offset);
     83   }
     84 
     85   vdmx->groups.reserve(vdmx->num_recs);
     86   for (unsigned i = 0; i < vdmx->num_recs; ++i) {
     87     OpenTypeVDMXGroup group;
     88     if (!table.ReadU16(&group.recs) ||
     89         !table.ReadU8(&group.startsz) ||
     90         !table.ReadU8(&group.endsz)) {
     91       return OTS_FAILURE();
     92     }
     93     group.entries.reserve(group.recs);
     94     for (unsigned j = 0; j < group.recs; ++j) {
     95       OpenTypeVDMXVTable vt;
     96       if (!table.ReadU16(&vt.y_pel_height) ||
     97           !table.ReadS16(&vt.y_max) ||
     98           !table.ReadS16(&vt.y_min)) {
     99         return OTS_FAILURE();
    100       }
    101       if (vt.y_max < vt.y_min) {
    102         OTS_WARNING("bad y min/max");
    103         DROP_THIS_TABLE;
    104         return true;
    105       }
    106 
    107       // This table must appear in sorted order (sorted by yPelHeight),
    108       // but need not be continuous.
    109       if ((j != 0) && (group.entries[j - 1].y_pel_height >= vt.y_pel_height)) {
    110         OTS_WARNING("the table is not sorted");
    111         DROP_THIS_TABLE;
    112         return true;
    113       }
    114 
    115       group.entries.push_back(vt);
    116     }
    117     vdmx->groups.push_back(group);
    118   }
    119 
    120   return true;
    121 }
    122 
    123 bool ots_vdmx_should_serialise(OpenTypeFile *file) {
    124   if (!file->glyf) return false;  // this table is not for CFF fonts.
    125   return file->vdmx != NULL;
    126 }
    127 
    128 bool ots_vdmx_serialise(OTSStream *out, OpenTypeFile *file) {
    129   OpenTypeVDMX * const vdmx = file->vdmx;
    130 
    131   if (!out->WriteU16(vdmx->version) ||
    132       !out->WriteU16(vdmx->num_recs) ||
    133       !out->WriteU16(vdmx->num_ratios)) {
    134     return OTS_FAILURE();
    135   }
    136 
    137   for (unsigned i = 0; i < vdmx->rat_ranges.size(); ++i) {
    138     const OpenTypeVDMXRatioRecord& rec = vdmx->rat_ranges[i];
    139     if (!out->Write(&rec.charset, 1) ||
    140         !out->Write(&rec.x_ratio, 1) ||
    141         !out->Write(&rec.y_start_ratio, 1) ||
    142         !out->Write(&rec.y_end_ratio, 1)) {
    143       return OTS_FAILURE();
    144     }
    145   }
    146 
    147   for (unsigned i = 0; i < vdmx->offsets.size(); ++i) {
    148     if (!out->WriteU16(vdmx->offsets[i])) {
    149       return OTS_FAILURE();
    150     }
    151   }
    152 
    153   for (unsigned i = 0; i < vdmx->groups.size(); ++i) {
    154     const OpenTypeVDMXGroup& group = vdmx->groups[i];
    155     if (!out->WriteU16(group.recs) ||
    156         !out->Write(&group.startsz, 1) ||
    157         !out->Write(&group.endsz, 1)) {
    158       return OTS_FAILURE();
    159     }
    160     for (unsigned j = 0; j < group.entries.size(); ++j) {
    161       const OpenTypeVDMXVTable& vt = group.entries[j];
    162       if (!out->WriteU16(vt.y_pel_height) ||
    163           !out->WriteS16(vt.y_max) ||
    164           !out->WriteS16(vt.y_min)) {
    165         return OTS_FAILURE();
    166       }
    167     }
    168   }
    169 
    170   return true;
    171 }
    172 
    173 void ots_vdmx_free(OpenTypeFile *file) {
    174   delete file->vdmx;
    175 }
    176 
    177 }  // namespace ots
    178