// Copyright (c) 2018 The OTS Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "stat.h"
#include "name.h"
namespace ots {
// -----------------------------------------------------------------------------
// OpenTypeSTAT
// -----------------------------------------------------------------------------
bool OpenTypeSTAT::ValidateNameId(uint16_t nameid) {
OpenTypeNAME* name = static_cast <OpenTypeNAME*>(
GetFont()->GetTypedTable(OTS_TAG_NAME));
if (!name || !name->IsValidNameId(nameid)) {
Drop("Invalid nameID: %d" , nameid);
return false ;
}
if ((nameid >= 26 && nameid <= 255 ) || nameid >= 32768 ) {
Warning("nameID out of range: %d" , nameid);
return true ;
}
return true ;
}
bool OpenTypeSTAT::Parse(const uint8_t* data, size_t length) {
Buffer table(data, length);
if (!table.ReadU16(&this ->majorVersion) ||
!table.ReadU16(&this ->minorVersion) ||
!table.ReadU16(&this ->designAxisSize) ||
!table.ReadU16(&this ->designAxisCount) ||
!table.ReadU32(&this ->designAxesOffset) ||
!table.ReadU16(&this ->axisValueCount) ||
!table.ReadU32(&this ->offsetToAxisValueOffsets) ||
!(this ->minorVersion < 1 || table.ReadU16(&this ->elidedFallbackNameID))) {
return Drop("Failed to read table header" );
}
if (this ->majorVersion != 1 ) {
return Drop("Unknown table version" );
}
if (this ->minorVersion > 2 ) {
Warning("Unknown minor version, downgrading to 2" );
this ->minorVersion = 2 ;
}
size_t headerEnd = table.offset();
if (this ->designAxisCount == 0 ) {
if (this ->designAxesOffset != 0 ) {
Warning("Unexpected non-zero designAxesOffset" );
this ->designAxesOffset = 0 ;
}
} else {
if (this ->designAxisSize < sizeof (AxisRecord)) {
return Drop("Invalid designAxisSize" );
}
if (this ->designAxesOffset < headerEnd ||
size_t(this ->designAxesOffset) > length ||
size_t(this ->designAxisCount) * size_t(this ->designAxisSize) >
length - size_t(this ->designAxesOffset)) {
return Drop("Invalid designAxesOffset" );
}
}
for (size_t i = 0 ; i < this ->designAxisCount; i++) {
table.set_offset(this ->designAxesOffset + i * this ->designAxisSize);
this ->designAxes.emplace_back();
auto & axis = this ->designAxes[i];
if (!table.ReadU32(&axis.axisTag) ||
!table.ReadU16(&axis.axisNameID) ||
!table.ReadU16(&axis.axisOrdering)) {
return Drop("Failed to read design axis" );
}
if (!CheckTag(axis.axisTag)) {
return Drop("Bad design axis tag" );
}
if (!ValidateNameId(axis.axisNameID)) {
return true ;
}
}
// TODO
// - check that all axes defined in fvar are covered by STAT
// - check that axisOrdering values are not duplicated (warn only)
if (this ->axisValueCount == 0 ) {
if (this ->offsetToAxisValueOffsets != 0 ) {
Warning("Unexpected non-zero offsetToAxisValueOffsets" );
this ->offsetToAxisValueOffsets = 0 ;
}
} else {
if (this ->offsetToAxisValueOffsets < headerEnd ||
size_t(this ->offsetToAxisValueOffsets) > length ||
size_t(this ->axisValueCount) * sizeof (uint16_t) >
length - size_t(this ->offsetToAxisValueOffsets)) {
return Drop("Invalid offsetToAxisValueOffsets" );
}
}
for (size_t i = 0 ; i < this ->axisValueCount; i++) {
table.set_offset(this ->offsetToAxisValueOffsets + i * sizeof (uint16_t));
uint16_t axisValueOffset;
if (!table.ReadU16(&axisValueOffset)) {
return Drop("Failed to read axis value offset" );
}
// We already checked that offsetToAxisValueOffsets doesn't exceed length,
// so this subtraction will not underflow.
if (axisValueOffset > length - this ->offsetToAxisValueOffsets) {
return Drop("Invalid axis value offset" );
}
table.set_offset(this ->offsetToAxisValueOffsets + axisValueOffset);
uint16_t format;
if (!table.ReadU16(&format)) {
return Drop("Failed to read axis value format" );
}
this ->axisValues.emplace_back(format);
auto & axisValue = axisValues[i];
switch (format) {
case 1 :
if (!table.ReadU16(&axisValue.format1.axisIndex) ||
!table.ReadU16(&axisValue.format1.flags) ||
!table.ReadU16(&axisValue.format1.valueNameID) ||
!table.ReadS32(&axisValue.format1.value)) {
return Drop("Failed to read axis value (format 1)" );
}
if (axisValue.format1.axisIndex >= this ->designAxisCount) {
return Drop("Axis index out of range" );
}
if ((axisValue.format1.flags & 0 xFFFCu) != 0 ) {
Warning("Unexpected axis value flags" );
axisValue.format1.flags &= ~0 xFFFCu;
}
if (!ValidateNameId(axisValue.format1.valueNameID)) {
return true ;
}
break ;
case 2 :
if (!table.ReadU16(&axisValue.format2.axisIndex) ||
!table.ReadU16(&axisValue.format2.flags) ||
!table.ReadU16(&axisValue.format2.valueNameID) ||
!table.ReadS32(&axisValue.format2.nominalValue) ||
!table.ReadS32(&axisValue.format2.rangeMinValue) ||
!table.ReadS32(&axisValue.format2.rangeMaxValue)) {
return Drop("Failed to read axis value (format 2)" );
}
if (axisValue.format2.axisIndex >= this ->designAxisCount) {
return Drop("Axis index out of range" );
}
if ((axisValue.format2.flags & 0 xFFFCu) != 0 ) {
Warning("Unexpected axis value flags" );
axisValue.format1.flags &= ~0 xFFFCu;
}
if (!ValidateNameId(axisValue.format2.valueNameID)) {
return true ;
}
if (!(axisValue.format2.rangeMinValue <= axisValue.format2.nominalValue &&
axisValue.format2.nominalValue <= axisValue.format2.rangeMaxValue)) {
Warning("Bad axis value range or nominal value" );
}
break ;
case 3 :
if (!table.ReadU16(&axisValue.format3.axisIndex) ||
!table.ReadU16(&axisValue.format3.flags) ||
!table.ReadU16(&axisValue.format3.valueNameID) ||
!table.ReadS32(&axisValue.format3.value) ||
!table.ReadS32(&axisValue.format3.linkedValue)) {
return Drop("Failed to read axis value (format 3)" );
}
if (axisValue.format3.axisIndex >= this ->designAxisCount) {
return Drop("Axis index out of range" );
}
if ((axisValue.format3.flags & 0 xFFFCu) != 0 ) {
Warning("Unexpected axis value flags" );
axisValue.format3.flags &= ~0 xFFFCu;
}
if (!ValidateNameId(axisValue.format3.valueNameID)) {
return true ;
}
break ;
case 4 :
if (this ->minorVersion < 2 ) {
return Drop("Invalid table minorVersion for format 4 axis values: %d" , this ->minorVersion);
}
if (!table.ReadU16(&axisValue.format4.axisCount) ||
!table.ReadU16(&axisValue.format4.flags) ||
!table.ReadU16(&axisValue.format4.valueNameID)) {
return Drop("Failed to read axis value (format 4)" );
}
if (axisValue.format4.axisCount > this ->designAxisCount) {
return Drop("Axis count out of range" );
}
if ((axisValue.format4.flags & 0 xFFFCu) != 0 ) {
Warning("Unexpected axis value flags" );
axisValue.format4.flags &= ~0 xFFFCu;
}
if (!ValidateNameId(axisValue.format4.valueNameID)) {
return true ;
}
for (unsigned j = 0 ; j < axisValue.format4.axisCount; j++) {
axisValue.format4.axisValues.emplace_back();
auto & v = axisValue.format4.axisValues[j];
if (!table.ReadU16(&v.axisIndex) ||
!table.ReadS32(&v.value)) {
return Drop("Failed to read axis value" );
}
if (v.axisIndex >= this ->designAxisCount) {
return Drop("Axis index out of range" );
}
}
break ;
default :
return Drop("Unknown axis value format" );
}
}
return true ;
}
bool OpenTypeSTAT::Serialize(OTSStream* out) {
off_t tableStart = out->Tell();
size_t headerSize = 5 * sizeof (uint16_t) + 2 * sizeof (uint32_t);
if (this ->minorVersion >= 1 ) {
headerSize += sizeof (uint16_t);
}
if (this ->designAxisCount == 0 ) {
this ->designAxesOffset = 0 ;
} else {
this ->designAxesOffset = headerSize;
}
this ->designAxisSize = sizeof (AxisRecord);
if (this ->axisValueCount == 0 ) {
this ->offsetToAxisValueOffsets = 0 ;
} else {
if (this ->designAxesOffset == 0 ) {
this ->offsetToAxisValueOffsets = headerSize;
} else {
this ->offsetToAxisValueOffsets = this ->designAxesOffset + this ->designAxisCount * this ->designAxisSize;
}
}
if (!out->WriteU16(this ->majorVersion) ||
!out->WriteU16(this ->minorVersion) ||
!out->WriteU16(this ->designAxisSize) ||
!out->WriteU16(this ->designAxisCount) ||
!out->WriteU32(this ->designAxesOffset) ||
!out->WriteU16(this ->axisValueCount) ||
!out->WriteU32(this ->offsetToAxisValueOffsets) ||
!(this ->minorVersion < 1 || out->WriteU16(this ->elidedFallbackNameID))) {
return Error("Failed to write table header" );
}
if (this ->designAxisCount > 0 ) {
if (out->Tell() - tableStart != this ->designAxesOffset) {
return Error("Error computing designAxesOffset" );
}
}
for (unsigned i = 0 ; i < this ->designAxisCount; i++) {
const auto & axis = this ->designAxes[i];
if (!out->WriteU32(axis.axisTag) ||
!out->WriteU16(axis.axisNameID) ||
!out->WriteU16(axis.axisOrdering)) {
return Error("Failed to write design axis" );
}
}
if (this ->axisValueCount > 0 ) {
if (out->Tell() - tableStart != this ->offsetToAxisValueOffsets) {
return Error("Error computing offsetToAxisValueOffsets" );
}
}
uint32_t axisValueOffset = this ->axisValueCount * sizeof (uint16_t);
for (unsigned i = 0 ; i < this ->axisValueCount; i++) {
const auto & value = this ->axisValues[i];
if (!out->WriteU16(axisValueOffset)) {
return Error("Failed to write axis value offset" );
}
axisValueOffset += value.Length();
}
for (unsigned i = 0 ; i < this ->axisValueCount; i++) {
const auto & value = this ->axisValues[i];
if (!out->WriteU16(value.format)) {
return Error("Failed to write axis value" );
}
switch (value.format) {
case 1 :
if (!out->WriteU16(value.format1.axisIndex) ||
!out->WriteU16(value.format1.flags) ||
!out->WriteU16(value.format1.valueNameID) ||
!out->WriteS32(value.format1.value)) {
return Error("Failed to write axis value" );
}
break ;
case 2 :
if (!out->WriteU16(value.format2.axisIndex) ||
!out->WriteU16(value.format2.flags) ||
!out->WriteU16(value.format2.valueNameID) ||
!out->WriteS32(value.format2.nominalValue) ||
!out->WriteS32(value.format2.rangeMinValue) ||
!out->WriteS32(value.format2.rangeMaxValue)) {
return Error("Failed to write axis value" );
}
break ;
case 3 :
if (!out->WriteU16(value.format3.axisIndex) ||
!out->WriteU16(value.format3.flags) ||
!out->WriteU16(value.format3.valueNameID) ||
!out->WriteS32(value.format3.value) ||
!out->WriteS32(value.format3.linkedValue)) {
return Error("Failed to write axis value" );
}
break ;
case 4 :
if (!out->WriteU16(value.format4.axisCount) ||
!out->WriteU16(value.format4.flags) ||
!out->WriteU16(value.format4.valueNameID)) {
return Error("Failed to write axis value" );
}
for (unsigned j = 0 ; j < value.format4.axisValues.size(); j++) {
if (!out->WriteU16(value.format4.axisValues[j].axisIndex) ||
!out->WriteS32(value.format4.axisValues[j].value)) {
return Error("Failed to write axis value" );
}
}
break ;
default :
return Error("Bad value format" );
}
}
return true ;
}
} // namespace ots
Messung V0.5 in Prozent C=97 H=95 G=95
¤ Dauer der Verarbeitung: 0.11 Sekunden
(vorverarbeitet am 2026-06-08)
¤
*© Formatika GbR, Deutschland