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.
7 // VDMX - Vertical Device Metrics
8 // http://www.microsoft.com/typography/otspec/vdmx.htm
10 #define TABLE_NAME "VDMX"
12 #define DROP_THIS_TABLE(...) \
14 OTS_FAILURE_MSG_(font->file, TABLE_NAME ": " __VA_ARGS__); \
15 OTS_FAILURE_MSG("Table discarded"); \
22 bool ots_vdmx_parse(Font
*font
, const uint8_t *data
, size_t length
) {
23 Buffer
table(data
, length
);
24 font
->vdmx
= new OpenTypeVDMX
;
25 OpenTypeVDMX
* const vdmx
= font
->vdmx
;
27 if (!table
.ReadU16(&vdmx
->version
) ||
28 !table
.ReadU16(&vdmx
->num_recs
) ||
29 !table
.ReadU16(&vdmx
->num_ratios
)) {
30 return OTS_FAILURE_MSG("Failed to read table header");
33 if (vdmx
->version
> 1) {
34 DROP_THIS_TABLE("bad version: %u", vdmx
->version
);
35 return true; // continue transcoding
38 vdmx
->rat_ranges
.reserve(vdmx
->num_ratios
);
39 for (unsigned i
= 0; i
< vdmx
->num_ratios
; ++i
) {
40 OpenTypeVDMXRatioRecord rec
;
42 if (!table
.ReadU8(&rec
.charset
) ||
43 !table
.ReadU8(&rec
.x_ratio
) ||
44 !table
.ReadU8(&rec
.y_start_ratio
) ||
45 !table
.ReadU8(&rec
.y_end_ratio
)) {
46 return OTS_FAILURE_MSG("Failed to read ratio header %d", i
);
49 if (rec
.charset
> 1) {
50 DROP_THIS_TABLE("bad charset: %u", rec
.charset
);
54 if (rec
.y_start_ratio
> rec
.y_end_ratio
) {
55 DROP_THIS_TABLE("bad y ratio");
59 // All values set to zero signal the default grouping to use;
60 // if present, this must be the last Ratio group in the table.
61 if ((i
< vdmx
->num_ratios
- 1u) &&
63 (rec
.y_start_ratio
== 0) &&
64 (rec
.y_end_ratio
== 0)) {
65 // workaround for fonts which have 2 or more {0, 0, 0} terminators.
66 DROP_THIS_TABLE("superfluous terminator found");
70 vdmx
->rat_ranges
.push_back(rec
);
73 vdmx
->offsets
.reserve(vdmx
->num_ratios
);
74 const size_t current_offset
= table
.offset();
75 // current_offset is less than (2 bytes * 3) + (4 bytes * USHRT_MAX) = 256k.
76 for (unsigned i
= 0; i
< vdmx
->num_ratios
; ++i
) {
78 if (!table
.ReadU16(&offset
)) {
79 return OTS_FAILURE_MSG("Failed to read ratio offset %d", i
);
81 if (current_offset
+ offset
>= length
) { // thus doesn't overflow.
82 return OTS_FAILURE_MSG("Bad ratio offset %d for ration %d", offset
, i
);
85 vdmx
->offsets
.push_back(offset
);
88 vdmx
->groups
.reserve(vdmx
->num_recs
);
89 for (unsigned i
= 0; i
< vdmx
->num_recs
; ++i
) {
90 OpenTypeVDMXGroup group
;
91 if (!table
.ReadU16(&group
.recs
) ||
92 !table
.ReadU8(&group
.startsz
) ||
93 !table
.ReadU8(&group
.endsz
)) {
94 return OTS_FAILURE_MSG("Failed to read record header %d", i
);
96 group
.entries
.reserve(group
.recs
);
97 for (unsigned j
= 0; j
< group
.recs
; ++j
) {
98 OpenTypeVDMXVTable vt
;
99 if (!table
.ReadU16(&vt
.y_pel_height
) ||
100 !table
.ReadS16(&vt
.y_max
) ||
101 !table
.ReadS16(&vt
.y_min
)) {
102 return OTS_FAILURE_MSG("Failed to read reacord %d group %d", i
, j
);
104 if (vt
.y_max
< vt
.y_min
) {
105 DROP_THIS_TABLE("bad y min/max");
109 // This table must appear in sorted order (sorted by yPelHeight),
110 // but need not be continuous.
111 if ((j
!= 0) && (group
.entries
[j
- 1].y_pel_height
>= vt
.y_pel_height
)) {
112 DROP_THIS_TABLE("the table is not sorted");
116 group
.entries
.push_back(vt
);
118 vdmx
->groups
.push_back(group
);
124 bool ots_vdmx_should_serialise(Font
*font
) {
125 if (!font
->glyf
) return false; // this table is not for CFF fonts.
126 return font
->vdmx
!= NULL
;
129 bool ots_vdmx_serialise(OTSStream
*out
, Font
*font
) {
130 OpenTypeVDMX
* const vdmx
= font
->vdmx
;
132 if (!out
->WriteU16(vdmx
->version
) ||
133 !out
->WriteU16(vdmx
->num_recs
) ||
134 !out
->WriteU16(vdmx
->num_ratios
)) {
135 return OTS_FAILURE_MSG("Failed to write table header");
138 for (unsigned i
= 0; i
< vdmx
->rat_ranges
.size(); ++i
) {
139 const OpenTypeVDMXRatioRecord
& rec
= vdmx
->rat_ranges
[i
];
140 if (!out
->Write(&rec
.charset
, 1) ||
141 !out
->Write(&rec
.x_ratio
, 1) ||
142 !out
->Write(&rec
.y_start_ratio
, 1) ||
143 !out
->Write(&rec
.y_end_ratio
, 1)) {
144 return OTS_FAILURE_MSG("Failed to write ratio %d", i
);
148 for (unsigned i
= 0; i
< vdmx
->offsets
.size(); ++i
) {
149 if (!out
->WriteU16(vdmx
->offsets
[i
])) {
150 return OTS_FAILURE_MSG("Failed to write ratio offset %d", i
);
154 for (unsigned i
= 0; i
< vdmx
->groups
.size(); ++i
) {
155 const OpenTypeVDMXGroup
& group
= vdmx
->groups
[i
];
156 if (!out
->WriteU16(group
.recs
) ||
157 !out
->Write(&group
.startsz
, 1) ||
158 !out
->Write(&group
.endsz
, 1)) {
159 return OTS_FAILURE_MSG("Failed to write group %d", i
);
161 for (unsigned j
= 0; j
< group
.entries
.size(); ++j
) {
162 const OpenTypeVDMXVTable
& vt
= group
.entries
[j
];
163 if (!out
->WriteU16(vt
.y_pel_height
) ||
164 !out
->WriteS16(vt
.y_max
) ||
165 !out
->WriteS16(vt
.y_min
)) {
166 return OTS_FAILURE_MSG("Failed to write group %d entry %d", i
, j
);
174 void ots_vdmx_reuse(Font
*font
, Font
*other
) {
175 font
->vdmx
= other
->vdmx
;
176 font
->vdmx_reused
= true;
179 void ots_vdmx_free(Font
*font
) {
186 #undef DROP_THIS_TABLE