зеркало из https://github.com/mozilla/pjs.git
bug 527276 - part 1 - OTS source code from google (svn r.35). r=roc,cjones a=blocking2.0
This commit is contained in:
Родитель
175f88829c
Коммит
f20e7ba703
|
@ -0,0 +1,27 @@
|
|||
// Copyright (c) 2009 The Chromium Authors. All rights reserved.
|
||||
//
|
||||
// Redistribution and use in source and binary forms, with or without
|
||||
// modification, are permitted provided that the following conditions are
|
||||
// met:
|
||||
//
|
||||
// * Redistributions of source code must retain the above copyright
|
||||
// notice, this list of conditions and the following disclaimer.
|
||||
// * Redistributions in binary form must reproduce the above
|
||||
// copyright notice, this list of conditions and the following disclaimer
|
||||
// in the documentation and/or other materials provided with the
|
||||
// distribution.
|
||||
// * Neither the name of Google Inc. nor the names of its
|
||||
// contributors may be used to endorse or promote products derived from
|
||||
// this software without specific prior written permission.
|
||||
//
|
||||
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
|
@ -0,0 +1,181 @@
|
|||
// Copyright (c) 2009 The Chromium Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style license that can be
|
||||
// found in the LICENSE file.
|
||||
|
||||
#ifndef OPENTYPE_SANITISER_H_
|
||||
#define OPENTYPE_SANITISER_H_
|
||||
|
||||
#if defined(_MSC_VER)
|
||||
typedef signed char int8_t;
|
||||
typedef unsigned char uint8_t;
|
||||
typedef short int16_t;
|
||||
typedef unsigned short uint16_t;
|
||||
typedef int int32_t;
|
||||
typedef unsigned int uint32_t;
|
||||
typedef __int64 int64_t;
|
||||
typedef unsigned __int64 uint64_t;
|
||||
#include <Winsock2.h> // for htons/ntohs
|
||||
#else
|
||||
#include <arpa/inet.h>
|
||||
#include <stdint.h>
|
||||
#endif
|
||||
|
||||
#include <algorithm> // for std::min
|
||||
#include <cassert>
|
||||
#include <cstring>
|
||||
|
||||
namespace ots {
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
// This is an interface for an abstract stream class which is used for writing
|
||||
// the serialised results out.
|
||||
// -----------------------------------------------------------------------------
|
||||
class OTSStream {
|
||||
public:
|
||||
OTSStream() {
|
||||
ResetChecksum();
|
||||
}
|
||||
|
||||
virtual ~OTSStream() {}
|
||||
|
||||
// This should be implemented to perform the actual write.
|
||||
virtual bool WriteRaw(const void *data, size_t length) = 0;
|
||||
|
||||
bool Write(const void *data, size_t length) {
|
||||
if (!length) return false;
|
||||
|
||||
const size_t orig_length = length;
|
||||
size_t offset = 0;
|
||||
if (chksum_buffer_offset_) {
|
||||
const size_t l =
|
||||
std::min(length, static_cast<size_t>(4) - chksum_buffer_offset_);
|
||||
std::memcpy(chksum_buffer_ + chksum_buffer_offset_, data, l);
|
||||
chksum_buffer_offset_ += l;
|
||||
offset += l;
|
||||
length -= l;
|
||||
}
|
||||
|
||||
if (chksum_buffer_offset_ == 4) {
|
||||
// TODO(yusukes): This cast breaks the strict-aliasing rule.
|
||||
chksum_ += ntohl(*reinterpret_cast<const uint32_t*>(chksum_buffer_));
|
||||
chksum_buffer_offset_ = 0;
|
||||
}
|
||||
|
||||
while (length >= 4) {
|
||||
chksum_ += ntohl(*reinterpret_cast<const uint32_t*>(
|
||||
reinterpret_cast<const uint8_t*>(data) + offset));
|
||||
length -= 4;
|
||||
offset += 4;
|
||||
}
|
||||
|
||||
if (length) {
|
||||
if (chksum_buffer_offset_ != 0) return false; // not reached
|
||||
if (length > 4) return false; // not reached
|
||||
std::memcpy(chksum_buffer_,
|
||||
reinterpret_cast<const uint8_t*>(data) + offset, length);
|
||||
chksum_buffer_offset_ = length;
|
||||
}
|
||||
|
||||
return WriteRaw(data, orig_length);
|
||||
}
|
||||
|
||||
virtual bool Seek(off_t position) = 0;
|
||||
virtual off_t Tell() const = 0;
|
||||
|
||||
virtual bool Pad(size_t bytes) {
|
||||
static const uint32_t kZero = 0;
|
||||
while (bytes >= 4) {
|
||||
if (!WriteTag(kZero)) return false;
|
||||
bytes -= 4;
|
||||
}
|
||||
while (bytes) {
|
||||
static const uint8_t kZerob = 0;
|
||||
if (!Write(&kZerob, 1)) return false;
|
||||
bytes--;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool WriteU16(uint16_t v) {
|
||||
v = htons(v);
|
||||
return Write(&v, sizeof(v));
|
||||
}
|
||||
|
||||
bool WriteS16(int16_t v) {
|
||||
v = htons(v);
|
||||
return Write(&v, sizeof(v));
|
||||
}
|
||||
|
||||
bool WriteU32(uint32_t v) {
|
||||
v = htonl(v);
|
||||
return Write(&v, sizeof(v));
|
||||
}
|
||||
|
||||
bool WriteS32(int32_t v) {
|
||||
v = htonl(v);
|
||||
return Write(&v, sizeof(v));
|
||||
}
|
||||
|
||||
bool WriteR64(uint64_t v) {
|
||||
return Write(&v, sizeof(v));
|
||||
}
|
||||
|
||||
bool WriteTag(uint32_t v) {
|
||||
return Write(&v, sizeof(v));
|
||||
}
|
||||
|
||||
void ResetChecksum() {
|
||||
chksum_ = 0;
|
||||
chksum_buffer_offset_ = 0;
|
||||
}
|
||||
|
||||
uint32_t chksum() const {
|
||||
assert(chksum_buffer_offset_ == 0);
|
||||
return chksum_;
|
||||
}
|
||||
|
||||
struct ChecksumState {
|
||||
uint32_t chksum;
|
||||
uint8_t chksum_buffer[4];
|
||||
unsigned chksum_buffer_offset;
|
||||
};
|
||||
|
||||
ChecksumState SaveChecksumState() const {
|
||||
ChecksumState s;
|
||||
s.chksum = chksum_;
|
||||
s.chksum_buffer_offset = chksum_buffer_offset_;
|
||||
std::memcpy(s.chksum_buffer, chksum_buffer_, 4);
|
||||
|
||||
return s;
|
||||
}
|
||||
|
||||
void RestoreChecksum(const ChecksumState &s) {
|
||||
assert(chksum_buffer_offset_ == 0);
|
||||
chksum_ += s.chksum;
|
||||
chksum_buffer_offset_ = s.chksum_buffer_offset;
|
||||
std::memcpy(chksum_buffer_, s.chksum_buffer, 4);
|
||||
}
|
||||
|
||||
protected:
|
||||
uint32_t chksum_;
|
||||
uint8_t chksum_buffer_[4];
|
||||
unsigned chksum_buffer_offset_;
|
||||
};
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
// Process a given OpenType file and write out a sanitised version
|
||||
// output: a pointer to an object implementing the OTSStream interface. The
|
||||
// sanitisied output will be written to this. In the even of a failure,
|
||||
// partial output may have been written.
|
||||
// input: the OpenType file
|
||||
// length: the size, in bytes, of |input|
|
||||
// -----------------------------------------------------------------------------
|
||||
bool Process(OTSStream *output, const uint8_t *input, size_t length);
|
||||
|
||||
// Force to disable debug output even when the library is compiled with
|
||||
// -DOTS_DEBUG.
|
||||
void DisableDebugOutput();
|
||||
|
||||
} // namespace ots
|
||||
|
||||
#endif // OPENTYPE_SANITISER_H_
|
|
@ -0,0 +1,105 @@
|
|||
// Copyright (c) 2009 The Chromium Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style license that can be
|
||||
// found in the LICENSE file.
|
||||
|
||||
#ifndef OTS_MEMORY_STREAM_H_
|
||||
#define OTS_MEMORY_STREAM_H_
|
||||
|
||||
#include <cstring>
|
||||
#include <limits>
|
||||
|
||||
#include "opentype-sanitiser.h"
|
||||
|
||||
namespace ots {
|
||||
|
||||
class MemoryStream : public OTSStream {
|
||||
public:
|
||||
MemoryStream(void *ptr, size_t length)
|
||||
: ptr_(ptr), length_(length), off_(0) {
|
||||
}
|
||||
|
||||
bool WriteRaw(const void *data, size_t length) {
|
||||
if ((off_ + length > length_) ||
|
||||
(length > std::numeric_limits<size_t>::max() - off_)) {
|
||||
return false;
|
||||
}
|
||||
std::memcpy(static_cast<char*>(ptr_) + off_, data, length);
|
||||
off_ += length;
|
||||
return true;
|
||||
}
|
||||
|
||||
bool Seek(off_t position) {
|
||||
if (position < 0) return false;
|
||||
if (static_cast<size_t>(position) > length_) return false;
|
||||
off_ = position;
|
||||
return true;
|
||||
}
|
||||
|
||||
off_t Tell() const {
|
||||
return off_;
|
||||
}
|
||||
|
||||
private:
|
||||
void* const ptr_;
|
||||
size_t length_;
|
||||
off_t off_;
|
||||
};
|
||||
|
||||
class ExpandingMemoryStream : public OTSStream {
|
||||
public:
|
||||
ExpandingMemoryStream(size_t initial, size_t limit)
|
||||
: length_(initial), limit_(limit), off_(0) {
|
||||
ptr_ = new uint8_t[length_];
|
||||
}
|
||||
|
||||
~ExpandingMemoryStream() {
|
||||
delete[] static_cast<uint8_t*>(ptr_);
|
||||
}
|
||||
|
||||
void* get() const {
|
||||
return ptr_;
|
||||
}
|
||||
|
||||
bool WriteRaw(const void *data, size_t length) {
|
||||
if ((off_ + length > length_) ||
|
||||
(length > std::numeric_limits<size_t>::max() - off_)) {
|
||||
if (length_ == limit_)
|
||||
return false;
|
||||
size_t new_length = (length_ + 1) * 2;
|
||||
if (new_length < length_)
|
||||
return false;
|
||||
if (new_length > limit_)
|
||||
new_length = limit_;
|
||||
uint8_t* new_buf = new uint8_t[new_length];
|
||||
memcpy(new_buf, ptr_, length_);
|
||||
length_ = new_length;
|
||||
delete[] static_cast<uint8_t*>(ptr_);
|
||||
ptr_ = new_buf;
|
||||
return WriteRaw(data, length);
|
||||
}
|
||||
std::memcpy(static_cast<char*>(ptr_) + off_, data, length);
|
||||
off_ += length;
|
||||
return true;
|
||||
}
|
||||
|
||||
bool Seek(off_t position) {
|
||||
if (position < 0) return false;
|
||||
if (static_cast<size_t>(position) > length_) return false;
|
||||
off_ = position;
|
||||
return true;
|
||||
}
|
||||
|
||||
off_t Tell() const {
|
||||
return off_;
|
||||
}
|
||||
|
||||
private:
|
||||
void* ptr_;
|
||||
size_t length_;
|
||||
const size_t limit_;
|
||||
off_t off_;
|
||||
};
|
||||
|
||||
} // namespace ots
|
||||
|
||||
#endif // OTS_MEMORY_STREAM_H_
|
Разница между файлами не показана из-за своего большого размера
Загрузить разницу
|
@ -0,0 +1,46 @@
|
|||
// Copyright (c) 2009 The Chromium Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style license that can be
|
||||
// found in the LICENSE file.
|
||||
|
||||
#ifndef OTS_CFF_H_
|
||||
#define OTS_CFF_H_
|
||||
|
||||
#include "ots.h"
|
||||
|
||||
#include <map>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
namespace ots {
|
||||
|
||||
struct CFFIndex {
|
||||
CFFIndex()
|
||||
: count(0), off_size(0), offset_to_next(0) {}
|
||||
uint16_t count;
|
||||
uint8_t off_size;
|
||||
std::vector<uint32_t> offsets;
|
||||
uint32_t offset_to_next;
|
||||
};
|
||||
|
||||
struct OpenTypeCFF {
|
||||
const uint8_t *data;
|
||||
size_t length;
|
||||
// Name INDEX. This name is used in name.cc as a postscript font name.
|
||||
std::string name;
|
||||
|
||||
// The number of fonts the file has.
|
||||
size_t font_dict_length;
|
||||
// A map from glyph # to font #.
|
||||
std::map<uint16_t, uint8_t> fd_select;
|
||||
|
||||
// A list of char strings.
|
||||
std::vector<CFFIndex *> char_strings_array;
|
||||
// A list of Local Subrs associated with FDArrays. Can be empty.
|
||||
std::vector<CFFIndex *> local_subrs_per_font;
|
||||
// A Local Subrs associated with Top DICT. Can be NULL.
|
||||
CFFIndex *local_subrs;
|
||||
};
|
||||
|
||||
} // namespace ots
|
||||
|
||||
#endif // OTS_CFF_H_
|
|
@ -0,0 +1,889 @@
|
|||
// Copyright (c) 2010 The Chromium Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style license that can be
|
||||
// found in the LICENSE file.
|
||||
|
||||
// A parser for the Type 2 Charstring Format.
|
||||
// http://www.adobe.com/devnet/font/pdfs/5177.Type2.pdf
|
||||
|
||||
#include "cff_type2_charstring.h"
|
||||
|
||||
#include <climits>
|
||||
#include <cstdio>
|
||||
#include <cstring>
|
||||
#include <stack>
|
||||
#include <string>
|
||||
#include <utility>
|
||||
|
||||
namespace {
|
||||
|
||||
// Type 2 Charstring Implementation Limits. See Appendix. B in Adobe Technical
|
||||
// Note #5177.
|
||||
const int32_t kMaxSubrsCount = 65536;
|
||||
const size_t kMaxCharStringLength = 65535;
|
||||
const size_t kMaxArgumentStack = 48;
|
||||
const size_t kMaxNumberOfStemHints = 96;
|
||||
const size_t kMaxSubrNesting = 10;
|
||||
|
||||
// |dummy_result| should be a huge positive integer so callsubr and callgsubr
|
||||
// will fail with the dummy value.
|
||||
const int32_t dummy_result = INT_MAX;
|
||||
|
||||
bool ExecuteType2CharString(size_t call_depth,
|
||||
const ots::CFFIndex& global_subrs_index,
|
||||
const ots::CFFIndex& local_subrs_index,
|
||||
ots::Buffer *cff_table,
|
||||
ots::Buffer *char_string,
|
||||
std::stack<int32_t> *argument_stack,
|
||||
bool *out_found_endchar,
|
||||
bool *out_found_width,
|
||||
size_t *in_out_num_stems);
|
||||
|
||||
// Converts |op| to a string and returns it.
|
||||
const char *Type2CharStringOperatorToString(ots::Type2CharStringOperator op) {
|
||||
switch (op) {
|
||||
case ots::kHStem:
|
||||
return "HStem";
|
||||
case ots::kVStem:
|
||||
return "VStem";
|
||||
case ots::kVMoveTo:
|
||||
return "VMoveTo";
|
||||
case ots::kRLineTo:
|
||||
return "RLineTo";
|
||||
case ots::kHLineTo:
|
||||
return "HLineTo";
|
||||
case ots::kVLineTo:
|
||||
return "VLineTo";
|
||||
case ots::kRRCurveTo:
|
||||
return "RRCurveTo";
|
||||
case ots::kCallSubr:
|
||||
return "CallSubr";
|
||||
case ots::kReturn:
|
||||
return "Return";
|
||||
case ots::kEndChar:
|
||||
return "EndChar";
|
||||
case ots::kHStemHm:
|
||||
return "HStemHm";
|
||||
case ots::kHintMask:
|
||||
return "HintMask";
|
||||
case ots::kCntrMask:
|
||||
return "CntrMask";
|
||||
case ots::kRMoveTo:
|
||||
return "RMoveTo";
|
||||
case ots::kHMoveTo:
|
||||
return "HMoveTo";
|
||||
case ots::kVStemHm:
|
||||
return "VStemHm";
|
||||
case ots::kRCurveLine:
|
||||
return "RCurveLine";
|
||||
case ots::kRLineCurve:
|
||||
return "RLineCurve";
|
||||
case ots::kVVCurveTo:
|
||||
return "VVCurveTo";
|
||||
case ots::kHHCurveTo:
|
||||
return "HHCurveTo";
|
||||
case ots::kCallGSubr:
|
||||
return "CallGSubr";
|
||||
case ots::kVHCurveTo:
|
||||
return "VHCurveTo";
|
||||
case ots::kHVCurveTo:
|
||||
return "HVCurveTo";
|
||||
case ots::kAnd:
|
||||
return "And";
|
||||
case ots::kOr:
|
||||
return "Or";
|
||||
case ots::kNot:
|
||||
return "Not";
|
||||
case ots::kAbs:
|
||||
return "Abs";
|
||||
case ots::kAdd:
|
||||
return "Add";
|
||||
case ots::kSub:
|
||||
return "Sub";
|
||||
case ots::kDiv:
|
||||
return "Div";
|
||||
case ots::kNeg:
|
||||
return "Neg";
|
||||
case ots::kEq:
|
||||
return "Eq";
|
||||
case ots::kDrop:
|
||||
return "Drop";
|
||||
case ots::kPut:
|
||||
return "Put";
|
||||
case ots::kGet:
|
||||
return "Get";
|
||||
case ots::kIfElse:
|
||||
return "IfElse";
|
||||
case ots::kRandom:
|
||||
return "Random";
|
||||
case ots::kMul:
|
||||
return "Mul";
|
||||
case ots::kSqrt:
|
||||
return "Sqrt";
|
||||
case ots::kDup:
|
||||
return "Dup";
|
||||
case ots::kExch:
|
||||
return "Exch";
|
||||
case ots::kIndex:
|
||||
return "Index";
|
||||
case ots::kRoll:
|
||||
return "Roll";
|
||||
case ots::kHFlex:
|
||||
return "HFlex";
|
||||
case ots::kFlex:
|
||||
return "Flex";
|
||||
case ots::kHFlex1:
|
||||
return "HFlex1";
|
||||
case ots::kFlex1:
|
||||
return "Flex1";
|
||||
}
|
||||
|
||||
return "UNKNOWN";
|
||||
}
|
||||
|
||||
// Read one or more bytes from the |char_string| buffer and stores the number
|
||||
// read on |out_number|. If the number read is an operator (ex 'vstem'), sets
|
||||
// true on |out_is_operator|. Returns true if the function read a number.
|
||||
bool ReadNextNumberFromType2CharString(ots::Buffer *char_string,
|
||||
int32_t *out_number,
|
||||
bool *out_is_operator) {
|
||||
uint8_t v = 0;
|
||||
if (!char_string->ReadU8(&v)) {
|
||||
return OTS_FAILURE();
|
||||
}
|
||||
*out_is_operator = false;
|
||||
|
||||
// The conversion algorithm is described in Adobe Technical Note #5177, page
|
||||
// 13, Table 1.
|
||||
if (v <= 11) {
|
||||
*out_number = v;
|
||||
*out_is_operator = true;
|
||||
} else if (v == 12) {
|
||||
uint16_t result = (v << 8);
|
||||
if (!char_string->ReadU8(&v)) {
|
||||
return OTS_FAILURE();
|
||||
}
|
||||
result += v;
|
||||
*out_number = result;
|
||||
*out_is_operator = true;
|
||||
} else if (v <= 27) {
|
||||
// Special handling for v==19 and v==20 are implemented in
|
||||
// ExecuteType2CharStringOperator().
|
||||
*out_number = v;
|
||||
*out_is_operator = true;
|
||||
} else if (v == 28) {
|
||||
if (!char_string->ReadU8(&v)) {
|
||||
return OTS_FAILURE();
|
||||
}
|
||||
uint16_t result = (v << 8);
|
||||
if (!char_string->ReadU8(&v)) {
|
||||
return OTS_FAILURE();
|
||||
}
|
||||
result += v;
|
||||
*out_number = result;
|
||||
} else if (v <= 31) {
|
||||
*out_number = v;
|
||||
*out_is_operator = true;
|
||||
} else if (v <= 246) {
|
||||
*out_number = static_cast<int32_t>(v) - 139;
|
||||
} else if (v <= 250) {
|
||||
uint8_t w = 0;
|
||||
if (!char_string->ReadU8(&w)) {
|
||||
return OTS_FAILURE();
|
||||
}
|
||||
*out_number = ((static_cast<int32_t>(v) - 247) * 256) +
|
||||
static_cast<int32_t>(w) + 108;
|
||||
} else if (v <= 254) {
|
||||
uint8_t w = 0;
|
||||
if (!char_string->ReadU8(&w)) {
|
||||
return OTS_FAILURE();
|
||||
}
|
||||
*out_number = -((static_cast<int32_t>(v) - 251) * 256) -
|
||||
static_cast<int32_t>(w) - 108;
|
||||
} else if (v == 255) {
|
||||
// TODO(yusukes): We should not skip the 4 bytes. Note that when v is 255,
|
||||
// we should treat the following 4-bytes as a 16.16 fixed-point number
|
||||
// rather than 32bit signed int.
|
||||
if (!char_string->Skip(4)) {
|
||||
return OTS_FAILURE();
|
||||
}
|
||||
*out_number = dummy_result;
|
||||
} else {
|
||||
return OTS_FAILURE();
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
// Executes |op| and updates |argument_stack|. Returns true if the execution
|
||||
// succeeds. If the |op| is kCallSubr or kCallGSubr, the function recursively
|
||||
// calls ExecuteType2CharString() function. The arguments other than |op| and
|
||||
// |argument_stack| are passed for that reason.
|
||||
bool ExecuteType2CharStringOperator(int32_t op,
|
||||
size_t call_depth,
|
||||
const ots::CFFIndex& global_subrs_index,
|
||||
const ots::CFFIndex& local_subrs_index,
|
||||
ots::Buffer *cff_table,
|
||||
ots::Buffer *char_string,
|
||||
std::stack<int32_t> *argument_stack,
|
||||
bool *out_found_endchar,
|
||||
bool *in_out_found_width,
|
||||
size_t *in_out_num_stems) {
|
||||
const size_t stack_size = argument_stack->size();
|
||||
|
||||
switch (op) {
|
||||
case ots::kCallSubr:
|
||||
case ots::kCallGSubr: {
|
||||
const ots::CFFIndex& subrs_index =
|
||||
(op == ots::kCallSubr ? local_subrs_index : global_subrs_index);
|
||||
|
||||
if (stack_size < 1) {
|
||||
return OTS_FAILURE();
|
||||
}
|
||||
int32_t subr_number = argument_stack->top();
|
||||
argument_stack->pop();
|
||||
if (subr_number == dummy_result) {
|
||||
// For safety, we allow subr calls only with immediate subr numbers for
|
||||
// now. For example, we allow "123 callgsubr", but does not allow "100 12
|
||||
// add callgsubr". Please note that arithmetic and conditional operators
|
||||
// always push the |dummy_result| in this implementation.
|
||||
return OTS_FAILURE();
|
||||
}
|
||||
|
||||
// See Adobe Technical Note #5176 (CFF), "16. Local/GlobalSubrs INDEXes."
|
||||
int32_t bias = 32768;
|
||||
if (subrs_index.count < 1240) {
|
||||
bias = 107;
|
||||
} else if (subrs_index.count < 33900) {
|
||||
bias = 1131;
|
||||
}
|
||||
subr_number += bias;
|
||||
|
||||
// Sanity checks of |subr_number|.
|
||||
if (subr_number < 0) {
|
||||
return OTS_FAILURE();
|
||||
}
|
||||
if (subr_number >= kMaxSubrsCount) {
|
||||
return OTS_FAILURE();
|
||||
}
|
||||
if (subrs_index.offsets.size() <= static_cast<size_t>(subr_number + 1)) {
|
||||
return OTS_FAILURE(); // The number is out-of-bounds.
|
||||
}
|
||||
|
||||
// Prepare ots::Buffer where we're going to jump.
|
||||
const size_t length =
|
||||
subrs_index.offsets[subr_number + 1] - subrs_index.offsets[subr_number];
|
||||
if (length > kMaxCharStringLength) {
|
||||
return OTS_FAILURE();
|
||||
}
|
||||
const size_t offset = subrs_index.offsets[subr_number];
|
||||
cff_table->set_offset(offset);
|
||||
if (!cff_table->Skip(length)) {
|
||||
return OTS_FAILURE();
|
||||
}
|
||||
ots::Buffer char_string_to_jump(cff_table->buffer() + offset, length);
|
||||
|
||||
return ExecuteType2CharString(call_depth + 1,
|
||||
global_subrs_index,
|
||||
local_subrs_index,
|
||||
cff_table,
|
||||
&char_string_to_jump,
|
||||
argument_stack,
|
||||
out_found_endchar,
|
||||
in_out_found_width,
|
||||
in_out_num_stems);
|
||||
}
|
||||
|
||||
case ots::kReturn:
|
||||
return true;
|
||||
|
||||
case ots::kEndChar:
|
||||
*out_found_endchar = true;
|
||||
*in_out_found_width = true; // just in case.
|
||||
return true;
|
||||
|
||||
case ots::kHStem:
|
||||
case ots::kVStem:
|
||||
case ots::kHStemHm:
|
||||
case ots::kVStemHm: {
|
||||
bool successful = false;
|
||||
if (stack_size < 2) {
|
||||
return OTS_FAILURE();
|
||||
}
|
||||
if ((stack_size % 2) == 0) {
|
||||
successful = true;
|
||||
} else if ((!(*in_out_found_width)) && (((stack_size - 1) % 2) == 0)) {
|
||||
// The -1 is for "width" argument. For details, see Adobe Technical Note
|
||||
// #5177, page 16, note 4.
|
||||
successful = true;
|
||||
}
|
||||
(*in_out_num_stems) += (stack_size / 2);
|
||||
if ((*in_out_num_stems) > kMaxNumberOfStemHints) {
|
||||
return OTS_FAILURE();
|
||||
}
|
||||
while (!argument_stack->empty())
|
||||
argument_stack->pop();
|
||||
*in_out_found_width = true; // always set true since "w" might be 0 byte.
|
||||
return successful ? true : OTS_FAILURE();
|
||||
}
|
||||
|
||||
case ots::kRMoveTo: {
|
||||
bool successful = false;
|
||||
if (stack_size == 2) {
|
||||
successful = true;
|
||||
} else if ((!(*in_out_found_width)) && (stack_size - 1 == 2)) {
|
||||
successful = true;
|
||||
}
|
||||
while (!argument_stack->empty())
|
||||
argument_stack->pop();
|
||||
*in_out_found_width = true;
|
||||
return successful ? true : OTS_FAILURE();
|
||||
}
|
||||
|
||||
case ots::kVMoveTo:
|
||||
case ots::kHMoveTo: {
|
||||
bool successful = false;
|
||||
if (stack_size == 1) {
|
||||
successful = true;
|
||||
} else if ((!(*in_out_found_width)) && (stack_size - 1 == 1)) {
|
||||
successful = true;
|
||||
}
|
||||
while (!argument_stack->empty())
|
||||
argument_stack->pop();
|
||||
*in_out_found_width = true;
|
||||
return successful ? true : OTS_FAILURE();
|
||||
}
|
||||
|
||||
case ots::kHintMask:
|
||||
case ots::kCntrMask: {
|
||||
bool successful = false;
|
||||
if (stack_size == 0) {
|
||||
successful = true;
|
||||
} else if ((!(*in_out_found_width)) && (stack_size == 1)) {
|
||||
// A number for "width" is found.
|
||||
successful = true;
|
||||
} else if ((!(*in_out_found_width)) || // in this case, any sizes are ok.
|
||||
((stack_size % 2) == 0)) {
|
||||
// The numbers are vstem definition.
|
||||
// See Adobe Technical Note #5177, page 24, hintmask.
|
||||
(*in_out_num_stems) += (stack_size / 2);
|
||||
if ((*in_out_num_stems) > kMaxNumberOfStemHints) {
|
||||
return OTS_FAILURE();
|
||||
}
|
||||
successful = true;
|
||||
}
|
||||
if (!successful) {
|
||||
return OTS_FAILURE();
|
||||
}
|
||||
|
||||
if ((*in_out_num_stems) == 0) {
|
||||
return OTS_FAILURE();
|
||||
}
|
||||
const size_t mask_bytes = (*in_out_num_stems + 7) / 8;
|
||||
if (!char_string->Skip(mask_bytes)) {
|
||||
return OTS_FAILURE();
|
||||
}
|
||||
while (!argument_stack->empty())
|
||||
argument_stack->pop();
|
||||
*in_out_found_width = true;
|
||||
return true;
|
||||
}
|
||||
|
||||
case ots::kRLineTo:
|
||||
if (!(*in_out_found_width)) {
|
||||
// The first stack-clearing operator should be one of hstem, hstemhm,
|
||||
// vstem, vstemhm, cntrmask, hintmask, hmoveto, vmoveto, rmoveto, or
|
||||
// endchar. For details, see Adobe Technical Note #5177, page 16, note 4.
|
||||
return OTS_FAILURE();
|
||||
}
|
||||
if (stack_size < 2) {
|
||||
return OTS_FAILURE();
|
||||
}
|
||||
if ((stack_size % 2) != 0) {
|
||||
return OTS_FAILURE();
|
||||
}
|
||||
while (!argument_stack->empty())
|
||||
argument_stack->pop();
|
||||
return true;
|
||||
|
||||
case ots::kHLineTo:
|
||||
case ots::kVLineTo:
|
||||
if (!(*in_out_found_width)) {
|
||||
return OTS_FAILURE();
|
||||
}
|
||||
if (stack_size < 1) {
|
||||
return OTS_FAILURE();
|
||||
}
|
||||
while (!argument_stack->empty())
|
||||
argument_stack->pop();
|
||||
return true;
|
||||
|
||||
case ots::kRRCurveTo:
|
||||
if (!(*in_out_found_width)) {
|
||||
return OTS_FAILURE();
|
||||
}
|
||||
if (stack_size < 6) {
|
||||
return OTS_FAILURE();
|
||||
}
|
||||
if ((stack_size % 6) != 0) {
|
||||
return OTS_FAILURE();
|
||||
}
|
||||
while (!argument_stack->empty())
|
||||
argument_stack->pop();
|
||||
return true;
|
||||
|
||||
case ots::kRCurveLine:
|
||||
if (!(*in_out_found_width)) {
|
||||
return OTS_FAILURE();
|
||||
}
|
||||
if (stack_size < 8) {
|
||||
return OTS_FAILURE();
|
||||
}
|
||||
if (((stack_size - 2) % 6) != 0) {
|
||||
return OTS_FAILURE();
|
||||
}
|
||||
while (!argument_stack->empty())
|
||||
argument_stack->pop();
|
||||
return true;
|
||||
|
||||
case ots::kRLineCurve:
|
||||
if (!(*in_out_found_width)) {
|
||||
return OTS_FAILURE();
|
||||
}
|
||||
if (stack_size < 8) {
|
||||
return OTS_FAILURE();
|
||||
}
|
||||
if (((stack_size - 6) % 2) != 0) {
|
||||
return OTS_FAILURE();
|
||||
}
|
||||
while (!argument_stack->empty())
|
||||
argument_stack->pop();
|
||||
return true;
|
||||
|
||||
case ots::kVVCurveTo:
|
||||
if (!(*in_out_found_width)) {
|
||||
return OTS_FAILURE();
|
||||
}
|
||||
if (stack_size < 4) {
|
||||
return OTS_FAILURE();
|
||||
}
|
||||
if (((stack_size % 4) != 0) &&
|
||||
(((stack_size - 1) % 4) != 0)) {
|
||||
return OTS_FAILURE();
|
||||
}
|
||||
while (!argument_stack->empty())
|
||||
argument_stack->pop();
|
||||
return true;
|
||||
|
||||
case ots::kHHCurveTo: {
|
||||
bool successful = false;
|
||||
if (!(*in_out_found_width)) {
|
||||
return OTS_FAILURE();
|
||||
}
|
||||
if (stack_size < 4) {
|
||||
return OTS_FAILURE();
|
||||
}
|
||||
if ((stack_size % 4) == 0) {
|
||||
// {dxa dxb dyb dxc}+
|
||||
successful = true;
|
||||
} else if (((stack_size - 1) % 4) == 0) {
|
||||
// dy1? {dxa dxb dyb dxc}+
|
||||
successful = true;
|
||||
}
|
||||
while (!argument_stack->empty())
|
||||
argument_stack->pop();
|
||||
return successful ? true : OTS_FAILURE();
|
||||
}
|
||||
|
||||
case ots::kVHCurveTo:
|
||||
case ots::kHVCurveTo: {
|
||||
bool successful = false;
|
||||
if (!(*in_out_found_width)) {
|
||||
return OTS_FAILURE();
|
||||
}
|
||||
if (stack_size < 4) {
|
||||
return OTS_FAILURE();
|
||||
}
|
||||
if (((stack_size - 4) % 8) == 0) {
|
||||
// dx1 dx2 dy2 dy3 {dya dxb dyb dxc dxd dxe dye dyf}*
|
||||
successful = true;
|
||||
} else if ((stack_size >= 5) &&
|
||||
((stack_size - 5) % 8) == 0) {
|
||||
// dx1 dx2 dy2 dy3 {dya dxb dyb dxc dxd dxe dye dyf}* dxf
|
||||
successful = true;
|
||||
} else if ((stack_size >= 8) &&
|
||||
((stack_size - 8) % 8) == 0) {
|
||||
// {dxa dxb dyb dyc dyd dxe dye dxf}+
|
||||
successful = true;
|
||||
} else if ((stack_size >= 9) &&
|
||||
((stack_size - 9) % 8) == 0) {
|
||||
// {dxa dxb dyb dyc dyd dxe dye dxf}+ dyf?
|
||||
successful = true;
|
||||
}
|
||||
while (!argument_stack->empty())
|
||||
argument_stack->pop();
|
||||
return successful ? true : OTS_FAILURE();
|
||||
}
|
||||
|
||||
case ots::kAnd:
|
||||
case ots::kOr:
|
||||
case ots::kEq:
|
||||
case ots::kAdd:
|
||||
case ots::kSub:
|
||||
if (stack_size < 2) {
|
||||
return OTS_FAILURE();
|
||||
}
|
||||
argument_stack->pop();
|
||||
argument_stack->pop();
|
||||
argument_stack->push(dummy_result);
|
||||
// TODO(yusukes): Implement this. We should push a real value for all
|
||||
// arithmetic and conditional operations.
|
||||
return true;
|
||||
|
||||
case ots::kNot:
|
||||
case ots::kAbs:
|
||||
case ots::kNeg:
|
||||
if (stack_size < 1) {
|
||||
return OTS_FAILURE();
|
||||
}
|
||||
argument_stack->pop();
|
||||
argument_stack->push(dummy_result);
|
||||
// TODO(yusukes): Implement this. We should push a real value for all
|
||||
// arithmetic and conditional operations.
|
||||
return true;
|
||||
|
||||
case ots::kDiv:
|
||||
// TODO(yusukes): Should detect div-by-zero errors.
|
||||
if (stack_size < 2) {
|
||||
return OTS_FAILURE();
|
||||
}
|
||||
argument_stack->pop();
|
||||
argument_stack->pop();
|
||||
argument_stack->push(dummy_result);
|
||||
// TODO(yusukes): Implement this. We should push a real value for all
|
||||
// arithmetic and conditional operations.
|
||||
return true;
|
||||
|
||||
case ots::kDrop:
|
||||
if (stack_size < 1) {
|
||||
return OTS_FAILURE();
|
||||
}
|
||||
argument_stack->pop();
|
||||
return true;
|
||||
|
||||
case ots::kPut:
|
||||
case ots::kGet:
|
||||
case ots::kIndex:
|
||||
// For now, just call OTS_FAILURE since there is no way to check whether the
|
||||
// index argument, |i|, is out-of-bounds or not. Fortunately, no OpenType
|
||||
// fonts I have (except malicious ones!) use the operators.
|
||||
// TODO(yusukes): Implement them in a secure way.
|
||||
return OTS_FAILURE();
|
||||
|
||||
case ots::kRoll:
|
||||
// Likewise, just call OTS_FAILURE for kRoll since there is no way to check
|
||||
// whether |N| is smaller than the current stack depth or not.
|
||||
// TODO(yusukes): Implement them in a secure way.
|
||||
return OTS_FAILURE();
|
||||
|
||||
case ots::kRandom:
|
||||
// For now, we don't handle the 'random' operator since the operator makes
|
||||
// it hard to analyze hinting code statically.
|
||||
return OTS_FAILURE();
|
||||
|
||||
case ots::kIfElse:
|
||||
if (stack_size < 4) {
|
||||
return OTS_FAILURE();
|
||||
}
|
||||
argument_stack->pop();
|
||||
argument_stack->pop();
|
||||
argument_stack->pop();
|
||||
argument_stack->pop();
|
||||
argument_stack->push(dummy_result);
|
||||
// TODO(yusukes): Implement this. We should push a real value for all
|
||||
// arithmetic and conditional operations.
|
||||
return true;
|
||||
|
||||
case ots::kMul:
|
||||
// TODO(yusukes): Should detect overflows.
|
||||
if (stack_size < 2) {
|
||||
return OTS_FAILURE();
|
||||
}
|
||||
argument_stack->pop();
|
||||
argument_stack->pop();
|
||||
argument_stack->push(dummy_result);
|
||||
// TODO(yusukes): Implement this. We should push a real value for all
|
||||
// arithmetic and conditional operations.
|
||||
return true;
|
||||
|
||||
case ots::kSqrt:
|
||||
// TODO(yusukes): Should check if the argument is negative.
|
||||
if (stack_size < 1) {
|
||||
return OTS_FAILURE();
|
||||
}
|
||||
argument_stack->pop();
|
||||
argument_stack->push(dummy_result);
|
||||
// TODO(yusukes): Implement this. We should push a real value for all
|
||||
// arithmetic and conditional operations.
|
||||
return true;
|
||||
|
||||
case ots::kDup:
|
||||
if (stack_size < 1) {
|
||||
return OTS_FAILURE();
|
||||
}
|
||||
argument_stack->pop();
|
||||
argument_stack->push(dummy_result);
|
||||
argument_stack->push(dummy_result);
|
||||
if (argument_stack->size() > kMaxArgumentStack) {
|
||||
return OTS_FAILURE();
|
||||
}
|
||||
// TODO(yusukes): Implement this. We should push a real value for all
|
||||
// arithmetic and conditional operations.
|
||||
return true;
|
||||
|
||||
case ots::kExch:
|
||||
if (stack_size < 2) {
|
||||
return OTS_FAILURE();
|
||||
}
|
||||
argument_stack->pop();
|
||||
argument_stack->pop();
|
||||
argument_stack->push(dummy_result);
|
||||
argument_stack->push(dummy_result);
|
||||
// TODO(yusukes): Implement this. We should push a real value for all
|
||||
// arithmetic and conditional operations.
|
||||
return true;
|
||||
|
||||
case ots::kHFlex:
|
||||
if (!(*in_out_found_width)) {
|
||||
return OTS_FAILURE();
|
||||
}
|
||||
if (stack_size != 7) {
|
||||
return OTS_FAILURE();
|
||||
}
|
||||
while (!argument_stack->empty())
|
||||
argument_stack->pop();
|
||||
return true;
|
||||
|
||||
case ots::kFlex:
|
||||
if (!(*in_out_found_width)) {
|
||||
return OTS_FAILURE();
|
||||
}
|
||||
if (stack_size != 13) {
|
||||
return OTS_FAILURE();
|
||||
}
|
||||
while (!argument_stack->empty())
|
||||
argument_stack->pop();
|
||||
return true;
|
||||
|
||||
case ots::kHFlex1:
|
||||
if (!(*in_out_found_width)) {
|
||||
return OTS_FAILURE();
|
||||
}
|
||||
if (stack_size != 9) {
|
||||
return OTS_FAILURE();
|
||||
}
|
||||
while (!argument_stack->empty())
|
||||
argument_stack->pop();
|
||||
return true;
|
||||
|
||||
case ots::kFlex1:
|
||||
if (!(*in_out_found_width)) {
|
||||
return OTS_FAILURE();
|
||||
}
|
||||
if (stack_size != 11) {
|
||||
return OTS_FAILURE();
|
||||
}
|
||||
while (!argument_stack->empty())
|
||||
argument_stack->pop();
|
||||
return true;
|
||||
}
|
||||
|
||||
OTS_WARNING("Undefined operator: %d (0x%x)", op, op);
|
||||
return OTS_FAILURE();
|
||||
}
|
||||
|
||||
// Executes |char_string| and updates |argument_stack|.
|
||||
//
|
||||
// call_depth: The current call depth. Initial value is zero.
|
||||
// global_subrs_index: Global subroutines.
|
||||
// local_subrs_index: Local subroutines for the current glyph.
|
||||
// cff_table: A whole CFF table which contains all global and local subroutines.
|
||||
// char_string: A charstring we'll execute. |char_string| can be a main routine
|
||||
// in CharString INDEX, or a subroutine in GlobalSubr/LocalSubr.
|
||||
// argument_stack: The stack which an operator in |char_string| operates.
|
||||
// out_found_endchar: true is set if |char_string| contains 'endchar'.
|
||||
// in_out_found_width: true is set if |char_string| contains 'width' byte (which
|
||||
// is 0 or 1 byte.)
|
||||
// in_out_num_stems: total number of hstems and vstems processed so far.
|
||||
bool ExecuteType2CharString(size_t call_depth,
|
||||
const ots::CFFIndex& global_subrs_index,
|
||||
const ots::CFFIndex& local_subrs_index,
|
||||
ots::Buffer *cff_table,
|
||||
ots::Buffer *char_string,
|
||||
std::stack<int32_t> *argument_stack,
|
||||
bool *out_found_endchar,
|
||||
bool *in_out_found_width,
|
||||
size_t *in_out_num_stems) {
|
||||
if (call_depth > kMaxSubrNesting) {
|
||||
return OTS_FAILURE();
|
||||
}
|
||||
*out_found_endchar = false;
|
||||
|
||||
const size_t length = char_string->length();
|
||||
while (char_string->offset() < length) {
|
||||
int32_t operator_or_operand = 0;
|
||||
bool is_operator = false;
|
||||
if (!ReadNextNumberFromType2CharString(char_string,
|
||||
&operator_or_operand,
|
||||
&is_operator)) {
|
||||
return OTS_FAILURE();
|
||||
}
|
||||
|
||||
/*
|
||||
You can dump all operators and operands (except mask bytes for hintmask
|
||||
and cntrmask) by the following code:
|
||||
|
||||
if (!is_operator) {
|
||||
std::fprintf(stderr, "#%d# ", operator_or_operand);
|
||||
} else {
|
||||
std::fprintf(stderr, "#%s#\n",
|
||||
Type2CharStringOperatorToString(
|
||||
Type2CharStringOperator(operator_or_operand)),
|
||||
operator_or_operand);
|
||||
}
|
||||
*/
|
||||
|
||||
if (!is_operator) {
|
||||
argument_stack->push(operator_or_operand);
|
||||
if (argument_stack->size() > kMaxArgumentStack) {
|
||||
return OTS_FAILURE();
|
||||
}
|
||||
continue;
|
||||
}
|
||||
|
||||
// An operator is found. Execute it.
|
||||
if (!ExecuteType2CharStringOperator(operator_or_operand,
|
||||
call_depth,
|
||||
global_subrs_index,
|
||||
local_subrs_index,
|
||||
cff_table,
|
||||
char_string,
|
||||
argument_stack,
|
||||
out_found_endchar,
|
||||
in_out_found_width,
|
||||
in_out_num_stems)) {
|
||||
return OTS_FAILURE();
|
||||
}
|
||||
if (*out_found_endchar) {
|
||||
return true;
|
||||
}
|
||||
if (operator_or_operand == ots::kReturn) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
// No endchar operator is found.
|
||||
return OTS_FAILURE();
|
||||
}
|
||||
|
||||
// Selects a set of subroutings for |glyph_index| from |cff| and sets it on
|
||||
// |out_local_subrs_to_use|. Returns true on success.
|
||||
bool SelectLocalSubr(const std::map<uint16_t, uint8_t> &fd_select,
|
||||
const std::vector<ots::CFFIndex *> &local_subrs_per_font,
|
||||
const ots::CFFIndex *local_subrs,
|
||||
uint16_t glyph_index, // 0-origin
|
||||
const ots::CFFIndex **out_local_subrs_to_use) {
|
||||
*out_local_subrs_to_use = NULL;
|
||||
|
||||
// First, find local subrs from |local_subrs_per_font|.
|
||||
if ((fd_select.size() > 0) &&
|
||||
(!local_subrs_per_font.empty())) {
|
||||
// Look up FDArray index for the glyph.
|
||||
std::map<uint16_t, uint8_t>::const_iterator iter =
|
||||
fd_select.find(glyph_index);
|
||||
if (iter == fd_select.end()) {
|
||||
return OTS_FAILURE();
|
||||
}
|
||||
const uint8_t fd_index = iter->second;
|
||||
if (fd_index >= local_subrs_per_font.size()) {
|
||||
return OTS_FAILURE();
|
||||
}
|
||||
*out_local_subrs_to_use = local_subrs_per_font.at(fd_index);
|
||||
} else if (local_subrs) {
|
||||
// Second, try to use |local_subrs|. Most Latin fonts don't have FDSelect
|
||||
// entries. If The font has a local subrs index associated with the Top
|
||||
// DICT (not FDArrays), use it.
|
||||
*out_local_subrs_to_use = local_subrs;
|
||||
} else {
|
||||
// Just return NULL.
|
||||
*out_local_subrs_to_use = NULL;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
namespace ots {
|
||||
|
||||
bool ValidateType2CharStringIndex(
|
||||
const CFFIndex& char_strings_index,
|
||||
const CFFIndex& global_subrs_index,
|
||||
const std::map<uint16_t, uint8_t> &fd_select,
|
||||
const std::vector<CFFIndex *> &local_subrs_per_font,
|
||||
const CFFIndex *local_subrs,
|
||||
Buffer* cff_table) {
|
||||
if (char_strings_index.offsets.size() == 0) {
|
||||
return OTS_FAILURE(); // no charstring.
|
||||
}
|
||||
|
||||
// For each glyph, validate the corresponding charstring.
|
||||
for (unsigned i = 1; i < char_strings_index.offsets.size(); ++i) {
|
||||
// Prepare a Buffer object, |char_string|, which contains the charstring
|
||||
// for the |i|-th glyph.
|
||||
const size_t length =
|
||||
char_strings_index.offsets[i] - char_strings_index.offsets[i - 1];
|
||||
if (length > kMaxCharStringLength) {
|
||||
return OTS_FAILURE();
|
||||
}
|
||||
const size_t offset = char_strings_index.offsets[i - 1];
|
||||
cff_table->set_offset(offset);
|
||||
if (!cff_table->Skip(length)) {
|
||||
return OTS_FAILURE();
|
||||
}
|
||||
Buffer char_string(cff_table->buffer() + offset, length);
|
||||
|
||||
// Get a local subrs for the glyph.
|
||||
const unsigned glyph_index = i - 1; // index in the map is 0-origin.
|
||||
const CFFIndex *local_subrs_to_use = NULL;
|
||||
if (!SelectLocalSubr(fd_select,
|
||||
local_subrs_per_font,
|
||||
local_subrs,
|
||||
glyph_index,
|
||||
&local_subrs_to_use)) {
|
||||
return OTS_FAILURE();
|
||||
}
|
||||
// If |local_subrs_to_use| is still NULL, use an empty one.
|
||||
CFFIndex default_empty_subrs;
|
||||
if (!local_subrs_to_use){
|
||||
local_subrs_to_use = &default_empty_subrs;
|
||||
}
|
||||
|
||||
// Check a charstring for the |i|-th glyph.
|
||||
std::stack<int32_t> argument_stack;
|
||||
bool found_endchar = false;
|
||||
bool found_width = false;
|
||||
size_t num_stems = 0;
|
||||
if (!ExecuteType2CharString(0 /* initial call_depth is zero */,
|
||||
global_subrs_index, *local_subrs_to_use,
|
||||
cff_table, &char_string, &argument_stack,
|
||||
&found_endchar, &found_width, &num_stems)) {
|
||||
return OTS_FAILURE();
|
||||
}
|
||||
if (!found_endchar) {
|
||||
return OTS_FAILURE();
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
} // namespace ots
|
|
@ -0,0 +1,100 @@
|
|||
// Copyright (c) 2010 The Chromium Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style license that can be
|
||||
// found in the LICENSE file.
|
||||
|
||||
#ifndef OTS_CFF_TYPE2_CHARSTRING_H_
|
||||
#define OTS_CFF_TYPE2_CHARSTRING_H_
|
||||
|
||||
#include "cff.h"
|
||||
#include "ots.h"
|
||||
|
||||
#include <map>
|
||||
#include <vector>
|
||||
|
||||
namespace ots {
|
||||
|
||||
// Validates all charstrings in |char_strings_index|. Charstring is a small
|
||||
// language for font hinting defined in Adobe Technical Note #5177.
|
||||
// http://www.adobe.com/devnet/font/pdfs/5177.Type2.pdf
|
||||
//
|
||||
// The validation will fail if one of the following conditions is met:
|
||||
// 1. The code uses more than 48 values of argument stack.
|
||||
// 2. The code uses deeply nested subroutine calls (more than 10 levels.)
|
||||
// 3. The code passes invalid number of operands to an operator.
|
||||
// 4. The code calls an undefined global or local subroutine.
|
||||
// 5. The code uses one of the following operators that are unlikely used in
|
||||
// an ordinary fonts, and could be dangerous: random, put, get, index, roll.
|
||||
//
|
||||
// Arguments:
|
||||
// global_subrs_index: Global subroutines which could be called by a charstring
|
||||
// in |char_strings_index|.
|
||||
// fd_select: A map from glyph # to font #.
|
||||
// local_subrs_per_font: A list of Local Subrs associated with FDArrays. Can be
|
||||
// empty.
|
||||
// local_subrs: A Local Subrs associated with Top DICT. Can be NULL.
|
||||
// cff_table: A buffer which contains actual byte code of charstring, global
|
||||
// subroutines and local subroutines.
|
||||
bool ValidateType2CharStringIndex(
|
||||
const CFFIndex &char_strings_index,
|
||||
const CFFIndex &global_subrs_index,
|
||||
const std::map<uint16_t, uint8_t> &fd_select,
|
||||
const std::vector<CFFIndex *> &local_subrs_per_font,
|
||||
const CFFIndex *local_subrs,
|
||||
Buffer *cff_table);
|
||||
|
||||
// The list of Operators. See Appendix. A in Adobe Technical Note #5177.
|
||||
enum Type2CharStringOperator {
|
||||
kHStem = 1,
|
||||
kVStem = 3,
|
||||
kVMoveTo = 4,
|
||||
kRLineTo = 5,
|
||||
kHLineTo = 6,
|
||||
kVLineTo = 7,
|
||||
kRRCurveTo = 8,
|
||||
kCallSubr = 10,
|
||||
kReturn = 11,
|
||||
kEndChar = 14,
|
||||
kHStemHm = 18,
|
||||
kHintMask = 19,
|
||||
kCntrMask = 20,
|
||||
kRMoveTo = 21,
|
||||
kHMoveTo = 22,
|
||||
kVStemHm = 23,
|
||||
kRCurveLine = 24,
|
||||
kRLineCurve = 25,
|
||||
kVVCurveTo = 26,
|
||||
kHHCurveTo = 27,
|
||||
kCallGSubr = 29,
|
||||
kVHCurveTo = 30,
|
||||
kHVCurveTo = 31,
|
||||
kAnd = (12 << 8) + 3,
|
||||
kOr = (12 << 8) + 4,
|
||||
kNot = (12 << 8) + 5,
|
||||
kAbs = (12 << 8) + 9,
|
||||
kAdd = (12 << 8) + 10,
|
||||
kSub = (12 << 8) + 11,
|
||||
kDiv = (12 << 8) + 12,
|
||||
kNeg = (12 << 8) + 14,
|
||||
kEq = (12 << 8) + 15,
|
||||
kDrop = (12 << 8) + 18,
|
||||
kPut = (12 << 8) + 20,
|
||||
kGet = (12 << 8) + 21,
|
||||
kIfElse = (12 << 8) + 22,
|
||||
kRandom = (12 << 8) + 23,
|
||||
kMul = (12 << 8) + 24,
|
||||
kSqrt = (12 << 8) + 26,
|
||||
kDup = (12 << 8) + 27,
|
||||
kExch = (12 << 8) + 28,
|
||||
kIndex = (12 << 8) + 29,
|
||||
kRoll = (12 << 8) + 30,
|
||||
kHFlex = (12 << 8) + 34,
|
||||
kFlex = (12 << 8) + 35,
|
||||
kHFlex1 = (12 << 8) + 36,
|
||||
kFlex1 = (12 << 8) + 37,
|
||||
// Operators that are obsoleted or undocumented, such as 'blend', will be
|
||||
// rejected.
|
||||
};
|
||||
|
||||
} // namespace ots
|
||||
|
||||
#endif // OTS_CFF_TYPE2_CHARSTRING_H_
|
|
@ -0,0 +1,832 @@
|
|||
// Copyright (c) 2009 The Chromium 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 "cmap.h"
|
||||
|
||||
#include <set>
|
||||
#include <utility>
|
||||
#include <vector>
|
||||
|
||||
#include "maxp.h"
|
||||
#include "os2.h"
|
||||
|
||||
// cmap - Character To Glyph Index Mapping Table
|
||||
// http://www.microsoft.com/opentype/otspec/cmap.htm
|
||||
|
||||
namespace {
|
||||
|
||||
struct CMAPSubtableHeader {
|
||||
uint16_t platform;
|
||||
uint16_t encoding;
|
||||
uint32_t offset;
|
||||
uint16_t format;
|
||||
uint32_t length;
|
||||
};
|
||||
|
||||
struct Subtable314Range {
|
||||
uint16_t start_range;
|
||||
uint16_t end_range;
|
||||
int16_t id_delta;
|
||||
uint16_t id_range_offset;
|
||||
uint32_t id_range_offset_offset;
|
||||
};
|
||||
|
||||
// The maximum number of groups in format 12 or 13 subtables.
|
||||
// Note: 0xFFFF is the maximum number of glyphs in a single font file.
|
||||
const unsigned kMaxCMAPGroups = 0xFFFF;
|
||||
|
||||
// Glyph array size for the Mac Roman (format 0) table.
|
||||
const size_t kFormat0ArraySize = 256;
|
||||
|
||||
// The upper limit of the Unicode code point.
|
||||
const uint32_t kUnicodeUpperLimit = 0x10FFFF;
|
||||
|
||||
// Parses either 3.0.4 or 3.1.4 tables.
|
||||
bool Parse3x4(ots::OpenTypeFile *file, int encoding,
|
||||
const uint8_t *data, size_t length, uint16_t num_glyphs) {
|
||||
ots::Buffer subtable(data, length);
|
||||
|
||||
// 3.0.4 or 3.1.4 subtables are complex and, rather than expanding the
|
||||
// whole thing and recompacting it, we validate it and include it verbatim
|
||||
// in the output.
|
||||
|
||||
if (!file->os2) {
|
||||
return OTS_FAILURE();
|
||||
}
|
||||
|
||||
if (!subtable.Skip(4)) {
|
||||
return OTS_FAILURE();
|
||||
}
|
||||
uint16_t language = 0;
|
||||
if (!subtable.ReadU16(&language)) {
|
||||
return OTS_FAILURE();
|
||||
}
|
||||
if (language) {
|
||||
// Platform ID 3 (windows) subtables should have language '0'.
|
||||
return OTS_FAILURE();
|
||||
}
|
||||
|
||||
uint16_t segcountx2, search_range, entry_selector, range_shift;
|
||||
segcountx2 = search_range = entry_selector = range_shift = 0;
|
||||
if (!subtable.ReadU16(&segcountx2) ||
|
||||
!subtable.ReadU16(&search_range) ||
|
||||
!subtable.ReadU16(&entry_selector) ||
|
||||
!subtable.ReadU16(&range_shift)) {
|
||||
return OTS_FAILURE();
|
||||
}
|
||||
|
||||
if (segcountx2 & 1 || search_range & 1) {
|
||||
return OTS_FAILURE();
|
||||
}
|
||||
const uint16_t segcount = segcountx2 >> 1;
|
||||
// There must be at least one segment according the spec.
|
||||
if (segcount < 1) {
|
||||
return OTS_FAILURE();
|
||||
}
|
||||
|
||||
// log2segcount is the maximal x s.t. 2^x < segcount
|
||||
unsigned log2segcount = 0;
|
||||
while (1u << (log2segcount + 1) <= segcount) {
|
||||
log2segcount++;
|
||||
}
|
||||
|
||||
const uint16_t expected_search_range = 2 * 1u << log2segcount;
|
||||
if (expected_search_range != search_range) {
|
||||
return OTS_FAILURE();
|
||||
}
|
||||
|
||||
if (entry_selector != log2segcount) {
|
||||
return OTS_FAILURE();
|
||||
}
|
||||
|
||||
const uint16_t expected_range_shift = segcountx2 - search_range;
|
||||
if (range_shift != expected_range_shift) {
|
||||
return OTS_FAILURE();
|
||||
}
|
||||
|
||||
std::vector<Subtable314Range> ranges(segcount);
|
||||
|
||||
for (unsigned i = 0; i < segcount; ++i) {
|
||||
if (!subtable.ReadU16(&ranges[i].end_range)) {
|
||||
return OTS_FAILURE();
|
||||
}
|
||||
}
|
||||
|
||||
uint16_t padding;
|
||||
if (!subtable.ReadU16(&padding)) {
|
||||
return OTS_FAILURE();
|
||||
}
|
||||
if (padding) {
|
||||
return OTS_FAILURE();
|
||||
}
|
||||
|
||||
for (unsigned i = 0; i < segcount; ++i) {
|
||||
if (!subtable.ReadU16(&ranges[i].start_range)) {
|
||||
return OTS_FAILURE();
|
||||
}
|
||||
}
|
||||
for (unsigned i = 0; i < segcount; ++i) {
|
||||
if (!subtable.ReadS16(&ranges[i].id_delta)) {
|
||||
return OTS_FAILURE();
|
||||
}
|
||||
}
|
||||
for (unsigned i = 0; i < segcount; ++i) {
|
||||
ranges[i].id_range_offset_offset = subtable.offset();
|
||||
if (!subtable.ReadU16(&ranges[i].id_range_offset)) {
|
||||
return OTS_FAILURE();
|
||||
}
|
||||
|
||||
if (ranges[i].id_range_offset & 1) {
|
||||
// Some font generators seem to put 65535 on id_range_offset
|
||||
// for 0xFFFF-0xFFFF range.
|
||||
// (e.g., many fonts in http://www.princexml.com/fonts/)
|
||||
if (i == segcount - 1u) {
|
||||
OTS_WARNING("bad id_range_offset");
|
||||
ranges[i].id_range_offset = 0;
|
||||
// The id_range_offset value in the transcoded font will not change
|
||||
// since this table is not actually "transcoded" yet.
|
||||
} else {
|
||||
return OTS_FAILURE();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// ranges must be ascending order, based on the end_code. Ranges may not
|
||||
// overlap.
|
||||
for (unsigned i = 1; i < segcount; ++i) {
|
||||
if ((i == segcount - 1u) &&
|
||||
(ranges[i - 1].start_range == 0xffff) &&
|
||||
(ranges[i - 1].end_range == 0xffff) &&
|
||||
(ranges[i].start_range == 0xffff) &&
|
||||
(ranges[i].end_range == 0xffff)) {
|
||||
// Some fonts (e.g., Germania.ttf) have multiple 0xffff terminators.
|
||||
// We'll accept them as an exception.
|
||||
OTS_WARNING("multiple 0xffff terminators found");
|
||||
continue;
|
||||
}
|
||||
|
||||
// Note: some Linux fonts (e.g., LucidaSansOblique.ttf, bsmi00lp.ttf) have
|
||||
// unsorted table...
|
||||
if (ranges[i].end_range <= ranges[i - 1].end_range) {
|
||||
return OTS_FAILURE();
|
||||
}
|
||||
if (ranges[i].start_range <= ranges[i - 1].end_range) {
|
||||
return OTS_FAILURE();
|
||||
}
|
||||
|
||||
// On many fonts, the value of {first, last}_char_index are incorrect.
|
||||
// Fix them.
|
||||
if (file->os2->first_char_index != 0xFFFF &&
|
||||
ranges[i].start_range != 0xFFFF &&
|
||||
file->os2->first_char_index > ranges[i].start_range) {
|
||||
file->os2->first_char_index = ranges[i].start_range;
|
||||
}
|
||||
if (file->os2->last_char_index != 0xFFFF &&
|
||||
ranges[i].end_range != 0xFFFF &&
|
||||
file->os2->last_char_index < ranges[i].end_range) {
|
||||
file->os2->last_char_index = ranges[i].end_range;
|
||||
}
|
||||
}
|
||||
|
||||
// The last range must end at 0xffff
|
||||
if (ranges[segcount - 1].end_range != 0xffff) {
|
||||
return OTS_FAILURE();
|
||||
}
|
||||
|
||||
// A format 4 CMAP subtable is complex. To be safe we simulate a lookup of
|
||||
// each code-point defined in the table and make sure that they are all valid
|
||||
// glyphs and that we don't access anything out-of-bounds.
|
||||
for (unsigned i = 1; i < segcount; ++i) {
|
||||
for (unsigned cp = ranges[i].start_range; cp <= ranges[i].end_range; ++cp) {
|
||||
const uint16_t code_point = cp;
|
||||
if (ranges[i].id_range_offset == 0) {
|
||||
// this is explictly allowed to overflow in the spec
|
||||
const uint16_t glyph = code_point + ranges[i].id_delta;
|
||||
if (glyph >= num_glyphs) {
|
||||
return OTS_FAILURE();
|
||||
}
|
||||
} else {
|
||||
const uint16_t range_delta = code_point - ranges[i].start_range;
|
||||
// this might seem odd, but it's true. The offset is relative to the
|
||||
// location of the offset value itself.
|
||||
const uint32_t glyph_id_offset = ranges[i].id_range_offset_offset +
|
||||
ranges[i].id_range_offset +
|
||||
range_delta * 2;
|
||||
// We need to be able to access a 16-bit value from this offset
|
||||
if (glyph_id_offset + 1 >= length) {
|
||||
return OTS_FAILURE();
|
||||
}
|
||||
uint16_t glyph;
|
||||
memcpy(&glyph, data + glyph_id_offset, 2);
|
||||
glyph = ntohs(glyph);
|
||||
if (glyph >= num_glyphs) {
|
||||
return OTS_FAILURE();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// We accept the table.
|
||||
// TODO(yusukes): transcode the subtable.
|
||||
if (encoding == 0) {
|
||||
file->cmap->subtable_3_0_4_data = data;
|
||||
file->cmap->subtable_3_0_4_length = length;
|
||||
} else if (encoding == 1) {
|
||||
file->cmap->subtable_3_1_4_data = data;
|
||||
file->cmap->subtable_3_1_4_length = length;
|
||||
} else {
|
||||
return OTS_FAILURE();
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool Parse31012(ots::OpenTypeFile *file,
|
||||
const uint8_t *data, size_t length, uint16_t num_glyphs) {
|
||||
ots::Buffer subtable(data, length);
|
||||
|
||||
// Format 12 tables are simple. We parse these and fully serialise them
|
||||
// later.
|
||||
|
||||
if (!subtable.Skip(8)) {
|
||||
return OTS_FAILURE();
|
||||
}
|
||||
uint32_t language = 0;
|
||||
if (!subtable.ReadU32(&language)) {
|
||||
return OTS_FAILURE();
|
||||
}
|
||||
if (language) {
|
||||
return OTS_FAILURE();
|
||||
}
|
||||
|
||||
uint32_t num_groups = 0;
|
||||
if (!subtable.ReadU32(&num_groups)) {
|
||||
return OTS_FAILURE();
|
||||
}
|
||||
if (num_groups == 0 || num_groups > kMaxCMAPGroups) {
|
||||
return OTS_FAILURE();
|
||||
}
|
||||
|
||||
std::vector<ots::OpenTypeCMAPSubtableRange> &groups
|
||||
= file->cmap->subtable_3_10_12;
|
||||
groups.resize(num_groups);
|
||||
|
||||
for (unsigned i = 0; i < num_groups; ++i) {
|
||||
if (!subtable.ReadU32(&groups[i].start_range) ||
|
||||
!subtable.ReadU32(&groups[i].end_range) ||
|
||||
!subtable.ReadU32(&groups[i].start_glyph_id)) {
|
||||
return OTS_FAILURE();
|
||||
}
|
||||
|
||||
if (groups[i].start_range > kUnicodeUpperLimit ||
|
||||
groups[i].end_range > kUnicodeUpperLimit ||
|
||||
groups[i].start_glyph_id > 0xFFFF) {
|
||||
return OTS_FAILURE();
|
||||
}
|
||||
|
||||
// [0xD800, 0xDFFF] are surrogate code points.
|
||||
if (groups[i].start_range >= 0xD800 &&
|
||||
groups[i].start_range <= 0xDFFF) {
|
||||
return OTS_FAILURE();
|
||||
}
|
||||
if (groups[i].end_range >= 0xD800 &&
|
||||
groups[i].end_range <= 0xDFFF) {
|
||||
return OTS_FAILURE();
|
||||
}
|
||||
if (groups[i].start_range < 0xD800 &&
|
||||
groups[i].end_range > 0xDFFF) {
|
||||
return OTS_FAILURE();
|
||||
}
|
||||
|
||||
// We assert that the glyph value is within range. Because of the range
|
||||
// limits, above, we don't need to worry about overflow.
|
||||
if (groups[i].end_range < groups[i].start_range) {
|
||||
return OTS_FAILURE();
|
||||
}
|
||||
if ((groups[i].end_range - groups[i].start_range) +
|
||||
groups[i].start_glyph_id > num_glyphs) {
|
||||
return OTS_FAILURE();
|
||||
}
|
||||
}
|
||||
|
||||
// the groups must be sorted by start code and may not overlap
|
||||
for (unsigned i = 1; i < num_groups; ++i) {
|
||||
if (groups[i].start_range <= groups[i - 1].start_range) {
|
||||
return OTS_FAILURE();
|
||||
}
|
||||
if (groups[i].start_range <= groups[i - 1].end_range) {
|
||||
return OTS_FAILURE();
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool Parse31013(ots::OpenTypeFile *file,
|
||||
const uint8_t *data, size_t length, uint16_t num_glyphs) {
|
||||
ots::Buffer subtable(data, length);
|
||||
|
||||
// Format 13 tables are simple. We parse these and fully serialise them
|
||||
// later.
|
||||
|
||||
if (!subtable.Skip(8)) {
|
||||
return OTS_FAILURE();
|
||||
}
|
||||
uint16_t language = 0;
|
||||
if (!subtable.ReadU16(&language)) {
|
||||
return OTS_FAILURE();
|
||||
}
|
||||
if (language) {
|
||||
return OTS_FAILURE();
|
||||
}
|
||||
|
||||
uint32_t num_groups = 0;
|
||||
if (!subtable.ReadU32(&num_groups)) {
|
||||
return OTS_FAILURE();
|
||||
}
|
||||
|
||||
// We limit the number of groups in the same way as in 3.10.12 tables. See
|
||||
// the comment there in
|
||||
if (num_groups == 0 || num_groups > kMaxCMAPGroups) {
|
||||
return OTS_FAILURE();
|
||||
}
|
||||
|
||||
std::vector<ots::OpenTypeCMAPSubtableRange> &groups
|
||||
= file->cmap->subtable_3_10_13;
|
||||
groups.resize(num_groups);
|
||||
|
||||
for (unsigned i = 0; i < num_groups; ++i) {
|
||||
if (!subtable.ReadU32(&groups[i].start_range) ||
|
||||
!subtable.ReadU32(&groups[i].end_range) ||
|
||||
!subtable.ReadU32(&groups[i].start_glyph_id)) {
|
||||
return OTS_FAILURE();
|
||||
}
|
||||
|
||||
// We conservatively limit all of the values to protect some parsers from
|
||||
// overflows
|
||||
if (groups[i].start_range > kUnicodeUpperLimit ||
|
||||
groups[i].end_range > kUnicodeUpperLimit ||
|
||||
groups[i].start_glyph_id > 0xFFFF) {
|
||||
return OTS_FAILURE();
|
||||
}
|
||||
|
||||
if (groups[i].start_glyph_id >= num_glyphs) {
|
||||
return OTS_FAILURE();
|
||||
}
|
||||
}
|
||||
|
||||
// the groups must be sorted by start code and may not overlap
|
||||
for (unsigned i = 1; i < num_groups; ++i) {
|
||||
if (groups[i].start_range <= groups[i - 1].start_range) {
|
||||
return OTS_FAILURE();
|
||||
}
|
||||
if (groups[i].start_range <= groups[i - 1].end_range) {
|
||||
return OTS_FAILURE();
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool Parse100(ots::OpenTypeFile *file, const uint8_t *data, size_t length) {
|
||||
// Mac Roman table
|
||||
ots::Buffer subtable(data, length);
|
||||
|
||||
if (!subtable.Skip(4)) {
|
||||
return OTS_FAILURE();
|
||||
}
|
||||
uint16_t language = 0;
|
||||
if (!subtable.ReadU16(&language)) {
|
||||
return OTS_FAILURE();
|
||||
}
|
||||
if (language) {
|
||||
// simsun.ttf has non-zero language id.
|
||||
OTS_WARNING("language id should be zero: %u", language);
|
||||
}
|
||||
|
||||
file->cmap->subtable_1_0_0.reserve(kFormat0ArraySize);
|
||||
for (size_t i = 0; i < kFormat0ArraySize; ++i) {
|
||||
uint8_t glyph_id = 0;
|
||||
if (!subtable.ReadU8(&glyph_id)) {
|
||||
return OTS_FAILURE();
|
||||
}
|
||||
file->cmap->subtable_1_0_0.push_back(glyph_id);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
namespace ots {
|
||||
|
||||
bool ots_cmap_parse(OpenTypeFile *file, const uint8_t *data, size_t length) {
|
||||
Buffer table(data, length);
|
||||
file->cmap = new OpenTypeCMAP;
|
||||
|
||||
uint16_t version = 0;
|
||||
uint16_t num_tables = 0;
|
||||
if (!table.ReadU16(&version) ||
|
||||
!table.ReadU16(&num_tables)) {
|
||||
return OTS_FAILURE();
|
||||
}
|
||||
|
||||
if (version != 0) {
|
||||
return OTS_FAILURE();
|
||||
}
|
||||
if (!num_tables) {
|
||||
return OTS_FAILURE();
|
||||
}
|
||||
|
||||
std::vector<CMAPSubtableHeader> subtable_headers;
|
||||
|
||||
// read the subtable headers
|
||||
subtable_headers.reserve(num_tables);
|
||||
for (unsigned i = 0; i < num_tables; ++i) {
|
||||
CMAPSubtableHeader subt;
|
||||
|
||||
if (!table.ReadU16(&subt.platform) ||
|
||||
!table.ReadU16(&subt.encoding) ||
|
||||
!table.ReadU32(&subt.offset)) {
|
||||
return OTS_FAILURE();
|
||||
}
|
||||
|
||||
subtable_headers.push_back(subt);
|
||||
}
|
||||
|
||||
const size_t data_offset = table.offset();
|
||||
|
||||
// make sure that all the offsets are valid.
|
||||
uint32_t last_id = 0;
|
||||
for (unsigned i = 0; i < num_tables; ++i) {
|
||||
if (subtable_headers[i].offset > 1024 * 1024 * 1024) {
|
||||
return OTS_FAILURE();
|
||||
}
|
||||
if (subtable_headers[i].offset < data_offset ||
|
||||
subtable_headers[i].offset >= length) {
|
||||
return OTS_FAILURE();
|
||||
}
|
||||
|
||||
// check if the table is sorted first by platform ID, then by encoding ID.
|
||||
uint32_t current_id
|
||||
= (subtable_headers[i].platform << 16) + subtable_headers[i].encoding;
|
||||
if ((i != 0) && (last_id >= current_id)) {
|
||||
return OTS_FAILURE();
|
||||
}
|
||||
last_id = current_id;
|
||||
}
|
||||
|
||||
// the format of the table is the first couple of bytes in the table. The
|
||||
// length of the table is stored in a format-specific way.
|
||||
for (unsigned i = 0; i < num_tables; ++i) {
|
||||
table.set_offset(subtable_headers[i].offset);
|
||||
if (!table.ReadU16(&subtable_headers[i].format)) {
|
||||
return OTS_FAILURE();
|
||||
}
|
||||
|
||||
uint16_t len = 0;
|
||||
switch (subtable_headers[i].format) {
|
||||
case 0:
|
||||
case 4:
|
||||
if (!table.ReadU16(&len)) {
|
||||
return OTS_FAILURE();
|
||||
}
|
||||
subtable_headers[i].length = len;
|
||||
break;
|
||||
case 12:
|
||||
case 13:
|
||||
if (!table.Skip(2)) {
|
||||
return OTS_FAILURE();
|
||||
}
|
||||
if (!table.ReadU32(&subtable_headers[i].length)) {
|
||||
return OTS_FAILURE();
|
||||
}
|
||||
break;
|
||||
default:
|
||||
subtable_headers[i].length = 0;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// Now, verify that all the lengths are sane
|
||||
for (unsigned i = 0; i < num_tables; ++i) {
|
||||
if (!subtable_headers[i].length) continue;
|
||||
if (subtable_headers[i].length > 1024 * 1024 * 1024) {
|
||||
return OTS_FAILURE();
|
||||
}
|
||||
// We know that both the offset and length are < 1GB, so the following
|
||||
// addition doesn't overflow
|
||||
const uint32_t end_byte
|
||||
= subtable_headers[i].offset + subtable_headers[i].length;
|
||||
if (end_byte > length) {
|
||||
return OTS_FAILURE();
|
||||
}
|
||||
}
|
||||
|
||||
// check that the cmap subtables are not overlapping.
|
||||
std::set<std::pair<uint32_t, uint32_t> > uniq_checker;
|
||||
std::vector<std::pair<uint32_t, uint8_t> > overlap_checker;
|
||||
for (unsigned i = 0; i < num_tables; ++i) {
|
||||
const uint32_t end_byte
|
||||
= subtable_headers[i].offset + subtable_headers[i].length;
|
||||
|
||||
if (!uniq_checker.insert(std::make_pair(subtable_headers[i].offset,
|
||||
end_byte)).second) {
|
||||
// Sometimes Unicode table and MS table share exactly the same data.
|
||||
// We'll allow this.
|
||||
continue;
|
||||
}
|
||||
overlap_checker.push_back(
|
||||
std::make_pair(subtable_headers[i].offset, 1 /* start */));
|
||||
overlap_checker.push_back(
|
||||
std::make_pair(end_byte, 0 /* end */));
|
||||
}
|
||||
std::sort(overlap_checker.begin(), overlap_checker.end());
|
||||
int overlap_count = 0;
|
||||
for (unsigned i = 0; i < overlap_checker.size(); ++i) {
|
||||
overlap_count += (overlap_checker[i].second ? 1 : -1);
|
||||
if (overlap_count > 1) {
|
||||
return OTS_FAILURE();
|
||||
}
|
||||
}
|
||||
|
||||
// we grab the number of glyphs in the file from the maxp table to make sure
|
||||
// that the character map isn't referencing anything beyound this range.
|
||||
if (!file->maxp) {
|
||||
return OTS_FAILURE();
|
||||
}
|
||||
const uint16_t num_glyphs = file->maxp->num_glyphs;
|
||||
|
||||
// We only support a subset of the possible character map tables. Microsoft
|
||||
// 'strongly recommends' that everyone supports the Unicode BMP table with
|
||||
// the UCS-4 table for non-BMP glyphs. We'll pass the following subtables:
|
||||
// Platform ID Encoding ID Format
|
||||
// 0 0 4 (Unicode Default)
|
||||
// 0 3 4 (Unicode BMP)
|
||||
// 0 3 12 (Unicode UCS-4)
|
||||
// 1 0 0 (Mac Roman)
|
||||
// 3 0 4 (MS Symbol)
|
||||
// 3 1 4 (MS Unicode BMP)
|
||||
// 3 10 12 (MS Unicode UCS-4)
|
||||
// 3 10 13 (MS UCS-4 Fallback mapping)
|
||||
//
|
||||
// Note:
|
||||
// * 0-0-4 table is (usually) written as a 3-1-4 table. If 3-1-4 table
|
||||
// also exists, the 0-0-4 table is ignored.
|
||||
// * 0-3-4 table is written as a 3-1-4 table. If 3-1-4 table also exists,
|
||||
// the 0-3-4 table is ignored.
|
||||
// * 0-3-12 table is written as a 3-10-12 table. If 3-10-12 table also
|
||||
// exists, the 0-3-12 table is ignored.
|
||||
//
|
||||
|
||||
for (unsigned i = 0; i < num_tables; ++i) {
|
||||
if (subtable_headers[i].platform == 0) {
|
||||
// Unicode platform
|
||||
|
||||
if ((subtable_headers[i].encoding == 0) &&
|
||||
(subtable_headers[i].format == 4)) {
|
||||
// parse and output the 0-0-4 table as 3-1-4 table. Sometimes the 0-0-4
|
||||
// table actually points to MS symbol data and thus should be parsed as
|
||||
// 3-0-4 table (e.g., marqueem.ttf and quixotic.ttf). This error will be
|
||||
// recovered in ots_cmap_serialise().
|
||||
if (!Parse3x4(file, 1, data + subtable_headers[i].offset,
|
||||
subtable_headers[i].length, num_glyphs)) {
|
||||
return OTS_FAILURE();
|
||||
}
|
||||
} else if ((subtable_headers[i].encoding == 3) &&
|
||||
(subtable_headers[i].format == 4)) {
|
||||
// parse and output the 0-3-4 table as 3-1-4 table.
|
||||
if (!Parse3x4(file, 1, data + subtable_headers[i].offset,
|
||||
subtable_headers[i].length, num_glyphs)) {
|
||||
return OTS_FAILURE();
|
||||
}
|
||||
} else if ((subtable_headers[i].encoding == 3) &&
|
||||
(subtable_headers[i].format == 12)) {
|
||||
// parse and output the 0-3-12 table as 3-10-12 table.
|
||||
if (!Parse31012(file, data + subtable_headers[i].offset,
|
||||
subtable_headers[i].length, num_glyphs)) {
|
||||
return OTS_FAILURE();
|
||||
}
|
||||
}
|
||||
|
||||
} else if (subtable_headers[i].platform == 1) {
|
||||
// Mac platform
|
||||
|
||||
if ((subtable_headers[i].encoding == 0) &&
|
||||
(subtable_headers[i].format == 0)) {
|
||||
// parse and output the 1-0-0 table.
|
||||
if (!Parse100(file, data + subtable_headers[i].offset,
|
||||
subtable_headers[i].length)) {
|
||||
return OTS_FAILURE();
|
||||
}
|
||||
}
|
||||
|
||||
} else if (subtable_headers[i].platform == 3) {
|
||||
// MS platform
|
||||
|
||||
switch (subtable_headers[i].encoding) {
|
||||
case 0:
|
||||
case 1:
|
||||
if (subtable_headers[i].format == 4) {
|
||||
// parse 3-0-4 or 3-1-4 table.
|
||||
if (!Parse3x4(file, subtable_headers[i].encoding,
|
||||
data + subtable_headers[i].offset,
|
||||
subtable_headers[i].length, num_glyphs)) {
|
||||
return OTS_FAILURE();
|
||||
}
|
||||
}
|
||||
break;
|
||||
case 10:
|
||||
if (subtable_headers[i].format == 12) {
|
||||
file->cmap->subtable_3_10_12.clear();
|
||||
if (!Parse31012(file, data + subtable_headers[i].offset,
|
||||
subtable_headers[i].length, num_glyphs)) {
|
||||
return OTS_FAILURE();
|
||||
}
|
||||
} else if (subtable_headers[i].format == 13) {
|
||||
file->cmap->subtable_3_10_13.clear();
|
||||
if (!Parse31013(file, data + subtable_headers[i].offset,
|
||||
subtable_headers[i].length, num_glyphs)) {
|
||||
return OTS_FAILURE();
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool ots_cmap_should_serialise(OpenTypeFile *file) {
|
||||
return file->cmap;
|
||||
}
|
||||
|
||||
bool ots_cmap_serialise(OTSStream *out, OpenTypeFile *file) {
|
||||
const bool have_100 = file->cmap->subtable_1_0_0.size();
|
||||
const bool have_304 = file->cmap->subtable_3_0_4_data;
|
||||
// MS Symbol and MS Unicode tables should not co-exist.
|
||||
// See the comment above in 0-0-4 parser.
|
||||
const bool have_314 = (!have_304) && file->cmap->subtable_3_1_4_data;
|
||||
const bool have_31012 = file->cmap->subtable_3_10_12.size();
|
||||
const bool have_31013 = file->cmap->subtable_3_10_13.size();
|
||||
const unsigned num_subtables = static_cast<unsigned>(have_100) +
|
||||
static_cast<unsigned>(have_304) +
|
||||
static_cast<unsigned>(have_314) +
|
||||
static_cast<unsigned>(have_31012) +
|
||||
static_cast<unsigned>(have_31013);
|
||||
const off_t table_start = out->Tell();
|
||||
|
||||
// Some fonts don't have 3-0-4 MS Symbol nor 3-1-4 Unicode BMP tables
|
||||
// (e.g., old fonts for Mac). We don't support them.
|
||||
if (!have_304 && !have_314) {
|
||||
return OTS_FAILURE();
|
||||
}
|
||||
|
||||
if (!out->WriteU16(0) ||
|
||||
!out->WriteU16(num_subtables)) {
|
||||
return OTS_FAILURE();
|
||||
}
|
||||
|
||||
const off_t record_offset = out->Tell();
|
||||
if (!out->Pad(num_subtables * 8)) {
|
||||
return OTS_FAILURE();
|
||||
}
|
||||
|
||||
const off_t offset_100 = out->Tell();
|
||||
if (have_100) {
|
||||
if (!out->WriteU16(0) || // format
|
||||
!out->WriteU16(6 + kFormat0ArraySize) || // length
|
||||
!out->WriteU16(0)) { // language
|
||||
return OTS_FAILURE();
|
||||
}
|
||||
if (!out->Write(&(file->cmap->subtable_1_0_0[0]), kFormat0ArraySize)) {
|
||||
return OTS_FAILURE();
|
||||
}
|
||||
}
|
||||
|
||||
const off_t offset_304 = out->Tell();
|
||||
if (have_304) {
|
||||
if (!out->Write(file->cmap->subtable_3_0_4_data,
|
||||
file->cmap->subtable_3_0_4_length)) {
|
||||
return OTS_FAILURE();
|
||||
}
|
||||
}
|
||||
|
||||
const off_t offset_314 = out->Tell();
|
||||
if (have_314) {
|
||||
if (!out->Write(file->cmap->subtable_3_1_4_data,
|
||||
file->cmap->subtable_3_1_4_length)) {
|
||||
return OTS_FAILURE();
|
||||
}
|
||||
}
|
||||
|
||||
const off_t offset_31012 = out->Tell();
|
||||
if (have_31012) {
|
||||
std::vector<OpenTypeCMAPSubtableRange> &groups
|
||||
= file->cmap->subtable_3_10_12;
|
||||
const unsigned num_groups = groups.size();
|
||||
if (!out->WriteU16(12) ||
|
||||
!out->WriteU16(0) ||
|
||||
!out->WriteU32(num_groups * 12 + 16) ||
|
||||
!out->WriteU32(0) ||
|
||||
!out->WriteU32(num_groups)) {
|
||||
return OTS_FAILURE();
|
||||
}
|
||||
|
||||
for (unsigned i = 0; i < num_groups; ++i) {
|
||||
if (!out->WriteU32(groups[i].start_range) ||
|
||||
!out->WriteU32(groups[i].end_range) ||
|
||||
!out->WriteU32(groups[i].start_glyph_id)) {
|
||||
return OTS_FAILURE();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const off_t offset_31013 = out->Tell();
|
||||
if (have_31013) {
|
||||
std::vector<OpenTypeCMAPSubtableRange> &groups
|
||||
= file->cmap->subtable_3_10_13;
|
||||
const unsigned num_groups = groups.size();
|
||||
if (!out->WriteU16(13) ||
|
||||
!out->WriteU16(0) ||
|
||||
!out->WriteU32(num_groups * 12 + 14) ||
|
||||
!out->WriteU32(0) ||
|
||||
!out->WriteU32(num_groups)) {
|
||||
return OTS_FAILURE();
|
||||
}
|
||||
|
||||
for (unsigned i = 0; i < num_groups; ++i) {
|
||||
if (!out->WriteU32(groups[i].start_range) ||
|
||||
!out->WriteU32(groups[i].end_range) ||
|
||||
!out->WriteU32(groups[i].start_glyph_id)) {
|
||||
return OTS_FAILURE();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const off_t table_end = out->Tell();
|
||||
// We might have hanging bytes from the above's checksum which the OTSStream
|
||||
// then merges into the table of offsets.
|
||||
OTSStream::ChecksumState saved_checksum = out->SaveChecksumState();
|
||||
out->ResetChecksum();
|
||||
|
||||
// Now seek back and write the table of offsets
|
||||
if (!out->Seek(record_offset)) {
|
||||
return OTS_FAILURE();
|
||||
}
|
||||
|
||||
if (have_100) {
|
||||
if (!out->WriteU16(1) ||
|
||||
!out->WriteU16(0) ||
|
||||
!out->WriteU32(offset_100 - table_start)) {
|
||||
return OTS_FAILURE();
|
||||
}
|
||||
}
|
||||
|
||||
if (have_304) {
|
||||
if (!out->WriteU16(3) ||
|
||||
!out->WriteU16(0) ||
|
||||
!out->WriteU32(offset_304 - table_start)) {
|
||||
return OTS_FAILURE();
|
||||
}
|
||||
}
|
||||
|
||||
if (have_314) {
|
||||
if (!out->WriteU16(3) ||
|
||||
!out->WriteU16(1) ||
|
||||
!out->WriteU32(offset_314 - table_start)) {
|
||||
return OTS_FAILURE();
|
||||
}
|
||||
}
|
||||
|
||||
if (have_31012) {
|
||||
if (!out->WriteU16(3) ||
|
||||
!out->WriteU16(10) ||
|
||||
!out->WriteU32(offset_31012 - table_start)) {
|
||||
return OTS_FAILURE();
|
||||
}
|
||||
}
|
||||
|
||||
if (have_31013) {
|
||||
if (!out->WriteU16(3) ||
|
||||
!out->WriteU16(10) ||
|
||||
!out->WriteU32(offset_31013 - table_start)) {
|
||||
return OTS_FAILURE();
|
||||
}
|
||||
}
|
||||
|
||||
if (!out->Seek(table_end)) {
|
||||
return OTS_FAILURE();
|
||||
}
|
||||
out->RestoreChecksum(saved_checksum);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void ots_cmap_free(OpenTypeFile *file) {
|
||||
delete file->cmap;
|
||||
}
|
||||
|
||||
} // namespace ots
|
|
@ -0,0 +1,45 @@
|
|||
// Copyright (c) 2009 The Chromium Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style license that can be
|
||||
// found in the LICENSE file.
|
||||
|
||||
#ifndef OTS_CMAP_H_
|
||||
#define OTS_CMAP_H_
|
||||
|
||||
#include <vector>
|
||||
|
||||
#include "ots.h"
|
||||
|
||||
namespace ots {
|
||||
|
||||
struct OpenTypeCMAPSubtableRange {
|
||||
uint32_t start_range;
|
||||
uint32_t end_range;
|
||||
uint32_t start_glyph_id;
|
||||
};
|
||||
|
||||
struct OpenTypeCMAP {
|
||||
OpenTypeCMAP()
|
||||
: subtable_3_0_4_data(NULL),
|
||||
subtable_3_0_4_length(0),
|
||||
subtable_3_1_4_data(NULL),
|
||||
subtable_3_1_4_length(0) {
|
||||
}
|
||||
|
||||
// Platform 3, Encoding 0, Format 4, MS Symbol table.
|
||||
const uint8_t *subtable_3_0_4_data;
|
||||
size_t subtable_3_0_4_length;
|
||||
// Platform 3, Encoding 1, Format 4, MS Unicode BMP table.
|
||||
const uint8_t *subtable_3_1_4_data;
|
||||
size_t subtable_3_1_4_length;
|
||||
|
||||
// Platform 3, Encoding 10, Format 12, MS Unicode UCS-4 table.
|
||||
std::vector<OpenTypeCMAPSubtableRange> subtable_3_10_12;
|
||||
// Platform 3, Encoding 10, Format 13, MS UCS-4 Fallback table.
|
||||
std::vector<OpenTypeCMAPSubtableRange> subtable_3_10_13;
|
||||
// Platform 1, Encoding 0, Format 0, Mac Roman table.
|
||||
std::vector<uint8_t> subtable_1_0_0;
|
||||
};
|
||||
|
||||
} // namespace ots
|
||||
|
||||
#endif
|
|
@ -0,0 +1,56 @@
|
|||
// Copyright (c) 2009 The Chromium 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 "cvt.h"
|
||||
|
||||
// cvt - Control Value Table
|
||||
// http://www.microsoft.com/opentype/otspec/cvt.htm
|
||||
|
||||
namespace ots {
|
||||
|
||||
bool ots_cvt_parse(OpenTypeFile *file, const uint8_t *data, size_t length) {
|
||||
Buffer table(data, length);
|
||||
|
||||
OpenTypeCVT *cvt = new OpenTypeCVT;
|
||||
file->cvt = cvt;
|
||||
|
||||
if (length >= 128 * 1024u) {
|
||||
return OTS_FAILURE(); // almost all cvt tables are less than 4k bytes.
|
||||
}
|
||||
|
||||
if (length % 2 != 0) {
|
||||
return OTS_FAILURE();
|
||||
}
|
||||
|
||||
if (!table.Skip(length)) {
|
||||
return OTS_FAILURE();
|
||||
}
|
||||
|
||||
cvt->data = data;
|
||||
cvt->length = length;
|
||||
return true;
|
||||
}
|
||||
|
||||
bool ots_cvt_should_serialise(OpenTypeFile *file) {
|
||||
if (!file->glyf) {
|
||||
return false; // this table is not for CFF fonts.
|
||||
}
|
||||
return g_transcode_hints && file->cvt;
|
||||
}
|
||||
|
||||
bool ots_cvt_serialise(OTSStream *out, OpenTypeFile *file) {
|
||||
const OpenTypeCVT *cvt = file->cvt;
|
||||
|
||||
if (!out->Write(cvt->data, cvt->length)) {
|
||||
return OTS_FAILURE();
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void ots_cvt_free(OpenTypeFile *file) {
|
||||
delete file->cvt;
|
||||
}
|
||||
|
||||
} // namespace ots
|
|
@ -0,0 +1,19 @@
|
|||
// Copyright (c) 2009 The Chromium Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style license that can be
|
||||
// found in the LICENSE file.
|
||||
|
||||
#ifndef OTS_CVT_H_
|
||||
#define OTS_CVT_H_
|
||||
|
||||
#include "ots.h"
|
||||
|
||||
namespace ots {
|
||||
|
||||
struct OpenTypeCVT {
|
||||
const uint8_t *data;
|
||||
uint32_t length;
|
||||
};
|
||||
|
||||
} // namespace ots
|
||||
|
||||
#endif // OTS_CVT_H_
|
|
@ -0,0 +1,50 @@
|
|||
// Copyright (c) 2009 The Chromium 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 "fpgm.h"
|
||||
|
||||
// fpgm - Font Program
|
||||
// http://www.microsoft.com/opentype/otspec/fpgm.htm
|
||||
|
||||
namespace ots {
|
||||
|
||||
bool ots_fpgm_parse(OpenTypeFile *file, const uint8_t *data, size_t length) {
|
||||
Buffer table(data, length);
|
||||
|
||||
OpenTypeFPGM *fpgm = new OpenTypeFPGM;
|
||||
file->fpgm = fpgm;
|
||||
|
||||
if (length >= 128 * 1024u) {
|
||||
return OTS_FAILURE(); // almost all fpgm tables are less than 5k bytes.
|
||||
}
|
||||
|
||||
if (!table.Skip(length)) {
|
||||
return OTS_FAILURE();
|
||||
}
|
||||
|
||||
fpgm->data = data;
|
||||
fpgm->length = length;
|
||||
return true;
|
||||
}
|
||||
|
||||
bool ots_fpgm_should_serialise(OpenTypeFile *file) {
|
||||
if (!file->glyf) return false; // this table is not for CFF fonts.
|
||||
return g_transcode_hints && file->fpgm;
|
||||
}
|
||||
|
||||
bool ots_fpgm_serialise(OTSStream *out, OpenTypeFile *file) {
|
||||
const OpenTypeFPGM *fpgm = file->fpgm;
|
||||
|
||||
if (!out->Write(fpgm->data, fpgm->length)) {
|
||||
return OTS_FAILURE();
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void ots_fpgm_free(OpenTypeFile *file) {
|
||||
delete file->fpgm;
|
||||
}
|
||||
|
||||
} // namespace ots
|
|
@ -0,0 +1,19 @@
|
|||
// Copyright (c) 2009 The Chromium Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style license that can be
|
||||
// found in the LICENSE file.
|
||||
|
||||
#ifndef OTS_FPGM_H_
|
||||
#define OTS_FPGM_H_
|
||||
|
||||
#include "ots.h"
|
||||
|
||||
namespace ots {
|
||||
|
||||
struct OpenTypeFPGM {
|
||||
const uint8_t *data;
|
||||
uint32_t length;
|
||||
};
|
||||
|
||||
} // namespace ots
|
||||
|
||||
#endif // OTS_FPGM_H_
|
|
@ -0,0 +1,106 @@
|
|||
// Copyright (c) 2009 The Chromium 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 "gasp.h"
|
||||
|
||||
// gasp - Grid-fitting And Scan-conversion Procedure
|
||||
// http://www.microsoft.com/opentype/otspec/gasp.htm
|
||||
|
||||
#define DROP_THIS_TABLE \
|
||||
do { delete file->gasp; file->gasp = 0; } while (0)
|
||||
|
||||
namespace ots {
|
||||
|
||||
bool ots_gasp_parse(OpenTypeFile *file, const uint8_t *data, size_t length) {
|
||||
Buffer table(data, length);
|
||||
|
||||
OpenTypeGASP *gasp = new OpenTypeGASP;
|
||||
file->gasp = gasp;
|
||||
|
||||
uint16_t num_ranges = 0;
|
||||
if (!table.ReadU16(&gasp->version) ||
|
||||
!table.ReadU16(&num_ranges)) {
|
||||
return OTS_FAILURE();
|
||||
}
|
||||
|
||||
if (gasp->version > 1) {
|
||||
// Lots of Linux fonts have bad version numbers...
|
||||
OTS_WARNING("bad version: %u", gasp->version);
|
||||
DROP_THIS_TABLE;
|
||||
return true;
|
||||
}
|
||||
|
||||
if (num_ranges == 0) {
|
||||
OTS_WARNING("num_ranges is zero");
|
||||
DROP_THIS_TABLE;
|
||||
return true;
|
||||
}
|
||||
|
||||
gasp->gasp_ranges.reserve(num_ranges);
|
||||
for (unsigned i = 0; i < num_ranges; ++i) {
|
||||
uint16_t max_ppem = 0;
|
||||
uint16_t behavior = 0;
|
||||
if (!table.ReadU16(&max_ppem) ||
|
||||
!table.ReadU16(&behavior)) {
|
||||
return OTS_FAILURE();
|
||||
}
|
||||
if ((i > 0) && (gasp->gasp_ranges[i - 1].first >= max_ppem)) {
|
||||
// The records in the gaspRange[] array must be sorted in order of
|
||||
// increasing rangeMaxPPEM value.
|
||||
OTS_WARNING("ranges are not sorted");
|
||||
DROP_THIS_TABLE;
|
||||
return true;
|
||||
}
|
||||
if ((i == num_ranges - 1u) && // never underflow.
|
||||
(max_ppem != 0xffffu)) {
|
||||
OTS_WARNING("The last record should be 0xFFFF as a sentinel value "
|
||||
"for rangeMaxPPEM");
|
||||
DROP_THIS_TABLE;
|
||||
return true;
|
||||
}
|
||||
|
||||
if (behavior >> 8) {
|
||||
OTS_WARNING("undefined bits are used: %x", behavior);
|
||||
// mask undefined bits.
|
||||
behavior &= 0x000fu;
|
||||
}
|
||||
|
||||
if (gasp->version == 0 && (behavior >> 2) != 0) {
|
||||
OTS_WARNING("changed the version number to 1");
|
||||
gasp->version = 1;
|
||||
}
|
||||
|
||||
gasp->gasp_ranges.push_back(std::make_pair(max_ppem, behavior));
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool ots_gasp_should_serialise(OpenTypeFile *file) {
|
||||
return file->gasp;
|
||||
}
|
||||
|
||||
bool ots_gasp_serialise(OTSStream *out, OpenTypeFile *file) {
|
||||
const OpenTypeGASP *gasp = file->gasp;
|
||||
|
||||
if (!out->WriteU16(gasp->version) ||
|
||||
!out->WriteU16(gasp->gasp_ranges.size())) {
|
||||
return OTS_FAILURE();
|
||||
}
|
||||
|
||||
for (unsigned i = 0; i < gasp->gasp_ranges.size(); ++i) {
|
||||
if (!out->WriteU16(gasp->gasp_ranges[i].first) ||
|
||||
!out->WriteU16(gasp->gasp_ranges[i].second)) {
|
||||
return OTS_FAILURE();
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void ots_gasp_free(OpenTypeFile *file) {
|
||||
delete file->gasp;
|
||||
}
|
||||
|
||||
} // namespace ots
|
|
@ -0,0 +1,23 @@
|
|||
// Copyright (c) 2009 The Chromium Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style license that can be
|
||||
// found in the LICENSE file.
|
||||
|
||||
#ifndef OTS_GASP_H_
|
||||
#define OTS_GASP_H_
|
||||
|
||||
#include <utility> // std::pair
|
||||
#include <vector>
|
||||
|
||||
#include "ots.h"
|
||||
|
||||
namespace ots {
|
||||
|
||||
struct OpenTypeGASP {
|
||||
uint16_t version;
|
||||
// A array of (max PPEM, GASP behavior) pairs.
|
||||
std::vector<std::pair<uint16_t, uint16_t> > gasp_ranges;
|
||||
};
|
||||
|
||||
} // namespace ots
|
||||
|
||||
#endif // OTS_GASP_H_
|
|
@ -0,0 +1,300 @@
|
|||
// Copyright (c) 2009 The Chromium 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 "glyf.h"
|
||||
|
||||
#include <algorithm>
|
||||
#include <limits>
|
||||
|
||||
#include "head.h"
|
||||
#include "loca.h"
|
||||
#include "maxp.h"
|
||||
|
||||
// glyf - Glyph Data
|
||||
// http://www.microsoft.com/opentype/otspec/glyf.htm
|
||||
|
||||
namespace {
|
||||
|
||||
bool ParseFlagsForSimpleGlyph(ots::Buffer *table,
|
||||
uint32_t gly_length,
|
||||
uint32_t num_flags,
|
||||
uint32_t *flags_count_logical,
|
||||
uint32_t *flags_count_physical,
|
||||
uint32_t *xy_coordinates_length) {
|
||||
uint8_t flag = 0;
|
||||
if (!table->ReadU8(&flag)) {
|
||||
return OTS_FAILURE();
|
||||
}
|
||||
|
||||
uint32_t delta = 0;
|
||||
if (flag & (1u << 1)) { // x-Short
|
||||
++delta;
|
||||
} else if (!(flag & (1u << 4))) {
|
||||
delta += 2;
|
||||
}
|
||||
|
||||
if (flag & (1u << 2)) { // y-Short
|
||||
++delta;
|
||||
} else if (!(flag & (1u << 5))) {
|
||||
delta += 2;
|
||||
}
|
||||
|
||||
if (flag & (1u << 3)) { // repeat
|
||||
if (*flags_count_logical + 1 >= num_flags) {
|
||||
return OTS_FAILURE();
|
||||
}
|
||||
uint8_t repeat = 0;
|
||||
if (!table->ReadU8(&repeat)) {
|
||||
return OTS_FAILURE();
|
||||
}
|
||||
if (repeat == 0) {
|
||||
return OTS_FAILURE();
|
||||
}
|
||||
delta += (delta * repeat);
|
||||
|
||||
*flags_count_logical += repeat;
|
||||
if (*flags_count_logical >= num_flags) {
|
||||
return OTS_FAILURE();
|
||||
}
|
||||
++(*flags_count_physical);
|
||||
}
|
||||
|
||||
if ((flag & (1u << 6)) || (flag & (1u << 7))) { // reserved flags
|
||||
return OTS_FAILURE();
|
||||
}
|
||||
|
||||
*xy_coordinates_length += delta;
|
||||
if (gly_length < *xy_coordinates_length) {
|
||||
return OTS_FAILURE();
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool ParseSimpleGlyph(ots::OpenTypeFile *file, const uint8_t *data,
|
||||
ots::Buffer *table, int16_t num_contours,
|
||||
uint32_t gly_offset, uint32_t gly_length,
|
||||
uint32_t *new_size) {
|
||||
ots::OpenTypeGLYF *glyf = file->glyf;
|
||||
|
||||
// read the end-points array
|
||||
uint16_t num_flags = 0;
|
||||
for (int i = 0; i < num_contours; ++i) {
|
||||
uint16_t tmp_index = 0;
|
||||
if (!table->ReadU16(&tmp_index)) {
|
||||
return OTS_FAILURE();
|
||||
}
|
||||
if (tmp_index == 0xffffu) {
|
||||
return OTS_FAILURE();
|
||||
}
|
||||
// check if the indices are monotonically increasing
|
||||
if (i && (tmp_index + 1 <= num_flags)) {
|
||||
return OTS_FAILURE();
|
||||
}
|
||||
num_flags = tmp_index + 1;
|
||||
}
|
||||
|
||||
uint16_t bytecode_length = 0;
|
||||
if (!table->ReadU16(&bytecode_length)) {
|
||||
return OTS_FAILURE();
|
||||
}
|
||||
if ((file->maxp->version_1) &&
|
||||
(file->maxp->max_size_glyf_instructions < bytecode_length)) {
|
||||
return OTS_FAILURE();
|
||||
}
|
||||
|
||||
const uint32_t gly_header_length = 10 + num_contours * 2 + 2;
|
||||
if (gly_length < (gly_header_length + bytecode_length)) {
|
||||
return OTS_FAILURE();
|
||||
}
|
||||
|
||||
if (ots::g_transcode_hints) {
|
||||
glyf->iov.push_back(std::make_pair(
|
||||
data + gly_offset, gly_header_length + bytecode_length));
|
||||
} else {
|
||||
// enqueue two vectors: the glyph data up to the bytecode length, then
|
||||
// a pointer to a static uint16_t 0 to overwrite the length.
|
||||
glyf->iov.push_back(std::make_pair(
|
||||
data + gly_offset, gly_header_length - 2));
|
||||
glyf->iov.push_back(std::make_pair((const uint8_t*) "\x00\x00", 2));
|
||||
}
|
||||
|
||||
if (!table->Skip(bytecode_length)) {
|
||||
return OTS_FAILURE();
|
||||
}
|
||||
|
||||
uint32_t flags_count_physical = 0; // on memory
|
||||
uint32_t xy_coordinates_length = 0;
|
||||
for (uint32_t flags_count_logical = 0;
|
||||
flags_count_logical < num_flags;
|
||||
++flags_count_logical, ++flags_count_physical) {
|
||||
if (!ParseFlagsForSimpleGlyph(table,
|
||||
gly_length,
|
||||
num_flags,
|
||||
&flags_count_logical,
|
||||
&flags_count_physical,
|
||||
&xy_coordinates_length)) {
|
||||
return OTS_FAILURE();
|
||||
}
|
||||
}
|
||||
|
||||
if (gly_length < (gly_header_length + bytecode_length +
|
||||
flags_count_physical + xy_coordinates_length)) {
|
||||
return OTS_FAILURE();
|
||||
}
|
||||
|
||||
if (gly_length - (gly_header_length + bytecode_length +
|
||||
flags_count_physical + xy_coordinates_length) > 3) {
|
||||
// We allow 0-3 bytes difference since gly_length is 4-bytes aligned,
|
||||
// zero-padded length.
|
||||
return OTS_FAILURE();
|
||||
}
|
||||
|
||||
glyf->iov.push_back(std::make_pair(
|
||||
data + gly_offset + gly_header_length + bytecode_length,
|
||||
flags_count_physical + xy_coordinates_length));
|
||||
|
||||
*new_size
|
||||
= gly_header_length + flags_count_physical + xy_coordinates_length;
|
||||
if (ots::g_transcode_hints) {
|
||||
*new_size += bytecode_length;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
namespace ots {
|
||||
|
||||
bool ots_glyf_parse(OpenTypeFile *file, const uint8_t *data, size_t length) {
|
||||
Buffer table(data, length);
|
||||
|
||||
if (!file->maxp || !file->loca || !file->head) {
|
||||
return OTS_FAILURE();
|
||||
}
|
||||
|
||||
OpenTypeGLYF *glyf = new OpenTypeGLYF;
|
||||
file->glyf = glyf;
|
||||
|
||||
const unsigned num_glyphs = file->maxp->num_glyphs;
|
||||
std::vector<uint32_t> &offsets = file->loca->offsets;
|
||||
|
||||
if (offsets.size() != num_glyphs + 1) {
|
||||
return OTS_FAILURE();
|
||||
}
|
||||
|
||||
std::vector<uint32_t> resulting_offsets(num_glyphs + 1);
|
||||
uint32_t current_offset = 0;
|
||||
|
||||
for (unsigned i = 0; i < num_glyphs; ++i) {
|
||||
const unsigned gly_offset = offsets[i];
|
||||
// The LOCA parser checks that these values are monotonic
|
||||
const unsigned gly_length = offsets[i + 1] - offsets[i];
|
||||
if (!gly_length) {
|
||||
// this glyph has no outline (e.g. the space charactor)
|
||||
resulting_offsets[i] = current_offset;
|
||||
continue;
|
||||
}
|
||||
|
||||
if (gly_offset >= length) {
|
||||
return OTS_FAILURE();
|
||||
}
|
||||
// Since these are unsigned types, the compiler is not allowed to assume
|
||||
// that they never overflow.
|
||||
if (gly_offset + gly_length < gly_offset) {
|
||||
return OTS_FAILURE();
|
||||
}
|
||||
if (gly_offset + gly_length > length) {
|
||||
return OTS_FAILURE();
|
||||
}
|
||||
|
||||
table.set_offset(gly_offset);
|
||||
int16_t num_contours, xmin, ymin, xmax, ymax;
|
||||
if (!table.ReadS16(&num_contours) ||
|
||||
!table.ReadS16(&xmin) ||
|
||||
!table.ReadS16(&ymin) ||
|
||||
!table.ReadS16(&xmax) ||
|
||||
!table.ReadS16(&ymax)) {
|
||||
return OTS_FAILURE();
|
||||
}
|
||||
|
||||
if (num_contours <= -2) {
|
||||
// -2, -3, -4, ... are reserved for future use.
|
||||
return OTS_FAILURE();
|
||||
}
|
||||
|
||||
// workaround for fonts in http://www.princexml.com/fonts/
|
||||
if ((xmin == 32767) &&
|
||||
(xmax == -32767) &&
|
||||
(ymin == 32767) &&
|
||||
(ymax == -32767)) {
|
||||
OTS_WARNING("bad xmin/xmax/ymin/ymax values");
|
||||
xmin = xmax = ymin = ymax = 0;
|
||||
}
|
||||
|
||||
if (xmin > xmax || ymin > ymax) {
|
||||
return OTS_FAILURE();
|
||||
}
|
||||
|
||||
unsigned new_size = 0;
|
||||
if (num_contours >= 0) {
|
||||
// this is a simple glyph and might contain bytecode
|
||||
if (!ParseSimpleGlyph(file, data, &table,
|
||||
num_contours, gly_offset, gly_length, &new_size)) {
|
||||
return OTS_FAILURE();
|
||||
}
|
||||
} else {
|
||||
// it's a composite glyph without any bytecode. Enqueue the whole thing
|
||||
glyf->iov.push_back(std::make_pair(data + gly_offset, gly_length));
|
||||
new_size = gly_length;
|
||||
}
|
||||
|
||||
resulting_offsets[i] = current_offset;
|
||||
// glyphs must be four byte aligned
|
||||
// TODO(yusukes): investigate whether this padding is really necessary.
|
||||
// Which part of the spec requires this?
|
||||
const unsigned padding = (4 - (new_size & 3)) % 4;
|
||||
if (padding) {
|
||||
glyf->iov.push_back(std::make_pair(
|
||||
reinterpret_cast<const uint8_t*>("\x00\x00\x00\x00"), padding));
|
||||
new_size += padding;
|
||||
}
|
||||
current_offset += new_size;
|
||||
}
|
||||
resulting_offsets[num_glyphs] = current_offset;
|
||||
|
||||
const uint16_t max16 = std::numeric_limits<uint16_t>::max();
|
||||
if ((*std::max_element(resulting_offsets.begin(),
|
||||
resulting_offsets.end()) >= (max16 * 2u)) &&
|
||||
(file->head->index_to_loc_format != 1)) {
|
||||
OTS_WARNING("2-bytes indexing is not possible (due to the padding above)");
|
||||
file->head->index_to_loc_format = 1;
|
||||
}
|
||||
|
||||
file->loca->offsets = resulting_offsets;
|
||||
return true;
|
||||
}
|
||||
|
||||
bool ots_glyf_should_serialise(OpenTypeFile *file) {
|
||||
return file->glyf;
|
||||
}
|
||||
|
||||
bool ots_glyf_serialise(OTSStream *out, OpenTypeFile *file) {
|
||||
const OpenTypeGLYF *glyf = file->glyf;
|
||||
|
||||
for (unsigned i = 0; i < glyf->iov.size(); ++i) {
|
||||
if (!out->Write(glyf->iov[i].first, glyf->iov[i].second)) {
|
||||
return OTS_FAILURE();
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void ots_glyf_free(OpenTypeFile *file) {
|
||||
delete file->glyf;
|
||||
}
|
||||
|
||||
} // namespace ots
|
|
@ -0,0 +1,21 @@
|
|||
// Copyright (c) 2009 The Chromium Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style license that can be
|
||||
// found in the LICENSE file.
|
||||
|
||||
#ifndef OTS_GLYF_H_
|
||||
#define OTS_GLYF_H_
|
||||
|
||||
#include <utility> // std::pair
|
||||
#include <vector>
|
||||
|
||||
#include "ots.h"
|
||||
|
||||
namespace ots {
|
||||
|
||||
struct OpenTypeGLYF {
|
||||
std::vector<std::pair<const uint8_t*, size_t> > iov;
|
||||
};
|
||||
|
||||
} // namespace ots
|
||||
|
||||
#endif // OTS_GLYF_H_
|
|
@ -0,0 +1,134 @@
|
|||
// Copyright (c) 2009 The Chromium 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 "hdmx.h"
|
||||
#include "head.h"
|
||||
#include "maxp.h"
|
||||
|
||||
// hdmx - Horizontal Device Metrics
|
||||
// http://www.microsoft.com/opentype/otspec/hdmx.htm
|
||||
|
||||
#define DROP_THIS_TABLE \
|
||||
do { delete file->hdmx; file->hdmx = 0; } while (0)
|
||||
|
||||
namespace ots {
|
||||
|
||||
bool ots_hdmx_parse(OpenTypeFile *file, const uint8_t *data, size_t length) {
|
||||
Buffer table(data, length);
|
||||
file->hdmx = new OpenTypeHDMX;
|
||||
OpenTypeHDMX * const hdmx = file->hdmx;
|
||||
|
||||
if (!file->head || !file->maxp) {
|
||||
return OTS_FAILURE();
|
||||
}
|
||||
|
||||
if ((file->head->flags & 0x14) == 0) {
|
||||
// http://www.microsoft.com/typography/otspec/recom.htm
|
||||
OTS_WARNING("the table should not be present when bit 2 and 4 of the "
|
||||
"head->flags are not set");
|
||||
DROP_THIS_TABLE;
|
||||
return true;
|
||||
}
|
||||
|
||||
int16_t num_recs;
|
||||
if (!table.ReadU16(&hdmx->version) ||
|
||||
!table.ReadS16(&num_recs) ||
|
||||
!table.ReadS32(&hdmx->size_device_record)) {
|
||||
return OTS_FAILURE();
|
||||
}
|
||||
if (hdmx->version != 0) {
|
||||
OTS_WARNING("bad version: %u", hdmx->version);
|
||||
DROP_THIS_TABLE;
|
||||
return true;
|
||||
}
|
||||
if (num_recs <= 0) {
|
||||
OTS_WARNING("bad num_recs: %d", num_recs);
|
||||
DROP_THIS_TABLE;
|
||||
return true;
|
||||
}
|
||||
const int32_t actual_size_device_record = file->maxp->num_glyphs + 2;
|
||||
if (hdmx->size_device_record < actual_size_device_record) {
|
||||
OTS_WARNING("bad hdmx->size_device_record: %d", hdmx->size_device_record);
|
||||
DROP_THIS_TABLE;
|
||||
return true;
|
||||
}
|
||||
|
||||
hdmx->pad_len = hdmx->size_device_record - actual_size_device_record;
|
||||
if (hdmx->pad_len > 3) {
|
||||
return OTS_FAILURE();
|
||||
}
|
||||
|
||||
uint8_t last_pixel_size = 0;
|
||||
hdmx->records.reserve(num_recs);
|
||||
for (int i = 0; i < num_recs; ++i) {
|
||||
OpenTypeHDMXDeviceRecord rec;
|
||||
|
||||
if (!table.ReadU8(&rec.pixel_size) ||
|
||||
!table.ReadU8(&rec.max_width)) {
|
||||
return OTS_FAILURE();
|
||||
}
|
||||
if ((i != 0) &&
|
||||
(rec.pixel_size <= last_pixel_size)) {
|
||||
OTS_WARNING("records are not sorted");
|
||||
DROP_THIS_TABLE;
|
||||
return true;
|
||||
}
|
||||
last_pixel_size = rec.pixel_size;
|
||||
|
||||
rec.widths.reserve(file->maxp->num_glyphs);
|
||||
for (unsigned j = 0; j < file->maxp->num_glyphs; ++j) {
|
||||
uint8_t width;
|
||||
if (!table.ReadU8(&width)) {
|
||||
return OTS_FAILURE();
|
||||
}
|
||||
rec.widths.push_back(width);
|
||||
}
|
||||
|
||||
if ((hdmx->pad_len > 0) &&
|
||||
!table.Skip(hdmx->pad_len)) {
|
||||
return OTS_FAILURE();
|
||||
}
|
||||
|
||||
hdmx->records.push_back(rec);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool ots_hdmx_should_serialise(OpenTypeFile *file) {
|
||||
if (!file->hdmx) return false;
|
||||
if (!file->glyf) return false; // this table is not for CFF fonts.
|
||||
return true;
|
||||
}
|
||||
|
||||
bool ots_hdmx_serialise(OTSStream *out, OpenTypeFile *file) {
|
||||
OpenTypeHDMX * const hdmx = file->hdmx;
|
||||
|
||||
if (!out->WriteU16(hdmx->version) ||
|
||||
!out->WriteS16(hdmx->records.size()) ||
|
||||
!out->WriteS32(hdmx->size_device_record)) {
|
||||
return OTS_FAILURE();
|
||||
}
|
||||
|
||||
for (unsigned i = 0; i < hdmx->records.size(); ++i) {
|
||||
const OpenTypeHDMXDeviceRecord& rec = hdmx->records[i];
|
||||
if (!out->Write(&rec.pixel_size, 1) ||
|
||||
!out->Write(&rec.max_width, 1) ||
|
||||
!out->Write(&rec.widths[0], rec.widths.size())) {
|
||||
return OTS_FAILURE();
|
||||
}
|
||||
if ((hdmx->pad_len > 0) &&
|
||||
!out->Write((const uint8_t *)"\x00\x00\x00", hdmx->pad_len)) {
|
||||
return OTS_FAILURE();
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void ots_hdmx_free(OpenTypeFile *file) {
|
||||
delete file->hdmx;
|
||||
}
|
||||
|
||||
} // namespace ots
|
|
@ -0,0 +1,29 @@
|
|||
// Copyright (c) 2009 The Chromium Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style license that can be
|
||||
// found in the LICENSE file.
|
||||
|
||||
#ifndef OTS_HDMX_H_
|
||||
#define OTS_HDMX_H_
|
||||
|
||||
#include <vector>
|
||||
|
||||
#include "ots.h"
|
||||
|
||||
namespace ots {
|
||||
|
||||
struct OpenTypeHDMXDeviceRecord {
|
||||
uint8_t pixel_size;
|
||||
uint8_t max_width;
|
||||
std::vector<uint8_t> widths;
|
||||
};
|
||||
|
||||
struct OpenTypeHDMX {
|
||||
uint16_t version;
|
||||
int32_t size_device_record;
|
||||
int32_t pad_len;
|
||||
std::vector<OpenTypeHDMXDeviceRecord> records;
|
||||
};
|
||||
|
||||
} // namespace ots
|
||||
|
||||
#endif
|
|
@ -0,0 +1,149 @@
|
|||
// Copyright (c) 2009 The Chromium 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 "head.h"
|
||||
|
||||
#include <cstring>
|
||||
|
||||
// head - Font Header
|
||||
// http://www.microsoft.com/opentype/otspec/head.htm
|
||||
|
||||
namespace ots {
|
||||
|
||||
bool ots_head_parse(OpenTypeFile *file, const uint8_t *data, size_t length) {
|
||||
Buffer table(data, length);
|
||||
file->head = new OpenTypeHEAD;
|
||||
|
||||
uint32_t version = 0;
|
||||
if (!table.ReadU32(&version) ||
|
||||
!table.ReadU32(&file->head->revision)) {
|
||||
return OTS_FAILURE();
|
||||
}
|
||||
|
||||
if (version >> 16 != 1) {
|
||||
return OTS_FAILURE();
|
||||
}
|
||||
|
||||
// Skip the checksum adjustment
|
||||
if (!table.Skip(4)) {
|
||||
return OTS_FAILURE();
|
||||
}
|
||||
|
||||
uint32_t magic;
|
||||
if (!table.ReadTag(&magic) ||
|
||||
std::memcmp(&magic, "\x5F\x0F\x3C\xF5", 4)) {
|
||||
return OTS_FAILURE();
|
||||
}
|
||||
|
||||
if (!table.ReadU16(&file->head->flags)) {
|
||||
return OTS_FAILURE();
|
||||
}
|
||||
|
||||
// We allow bits 0..4, 11..13
|
||||
file->head->flags &= 0x381f;
|
||||
|
||||
if (!table.ReadU16(&file->head->ppem)) {
|
||||
return OTS_FAILURE();
|
||||
}
|
||||
|
||||
// ppem must be in range
|
||||
if (file->head->ppem < 16 ||
|
||||
file->head->ppem > 16384) {
|
||||
return OTS_FAILURE();
|
||||
}
|
||||
|
||||
// ppem must be a power of two
|
||||
#if 0
|
||||
// We don't call ots_failure() for now since lots of TrueType fonts are
|
||||
// not following this rule. Putting OTS_WARNING here is too noisy.
|
||||
if ((file->head->ppem - 1) & file->head->ppem) {
|
||||
return OTS_FAILURE();
|
||||
}
|
||||
#endif
|
||||
|
||||
if (!table.ReadR64(&file->head->created) ||
|
||||
!table.ReadR64(&file->head->modified)) {
|
||||
return OTS_FAILURE();
|
||||
}
|
||||
|
||||
if (!table.ReadS16(&file->head->xmin) ||
|
||||
!table.ReadS16(&file->head->ymin) ||
|
||||
!table.ReadS16(&file->head->xmax) ||
|
||||
!table.ReadS16(&file->head->ymax)) {
|
||||
return OTS_FAILURE();
|
||||
}
|
||||
|
||||
if (file->head->xmin > file->head->xmax) {
|
||||
return OTS_FAILURE();
|
||||
}
|
||||
if (file->head->ymin > file->head->ymax) {
|
||||
return OTS_FAILURE();
|
||||
}
|
||||
|
||||
if (!table.ReadU16(&file->head->mac_style)) {
|
||||
return OTS_FAILURE();
|
||||
}
|
||||
|
||||
// We allow bits 0..6
|
||||
file->head->mac_style &= 0x7f;
|
||||
|
||||
if (!table.ReadU16(&file->head->min_ppem)) {
|
||||
return OTS_FAILURE();
|
||||
}
|
||||
|
||||
// We don't care about the font direction hint
|
||||
if (!table.Skip(2)) {
|
||||
return OTS_FAILURE();
|
||||
}
|
||||
|
||||
if (!table.ReadS16(&file->head->index_to_loc_format)) {
|
||||
return OTS_FAILURE();
|
||||
}
|
||||
if (file->head->index_to_loc_format < 0 ||
|
||||
file->head->index_to_loc_format > 1) {
|
||||
return OTS_FAILURE();
|
||||
}
|
||||
|
||||
int16_t glyph_data_format;
|
||||
if (!table.ReadS16(&glyph_data_format) ||
|
||||
glyph_data_format) {
|
||||
return OTS_FAILURE();
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool ots_head_should_serialise(OpenTypeFile *file) {
|
||||
return file->head;
|
||||
}
|
||||
|
||||
bool ots_head_serialise(OTSStream *out, OpenTypeFile *file) {
|
||||
if (!out->WriteU32(0x00010000) ||
|
||||
!out->WriteU32(file->head->revision) ||
|
||||
!out->WriteU32(0) || // check sum not filled in yet
|
||||
!out->WriteU32(0x5F0F3CF5) ||
|
||||
!out->WriteU16(file->head->flags) ||
|
||||
!out->WriteU16(file->head->ppem) ||
|
||||
!out->WriteR64(file->head->created) ||
|
||||
!out->WriteR64(file->head->modified) ||
|
||||
!out->WriteS16(file->head->xmin) ||
|
||||
!out->WriteS16(file->head->ymin) ||
|
||||
!out->WriteS16(file->head->xmax) ||
|
||||
!out->WriteS16(file->head->ymax) ||
|
||||
!out->WriteU16(file->head->mac_style) ||
|
||||
!out->WriteU16(file->head->min_ppem) ||
|
||||
!out->WriteS16(2) ||
|
||||
!out->WriteS16(file->head->index_to_loc_format) ||
|
||||
!out->WriteS16(0)) {
|
||||
return OTS_FAILURE();
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void ots_head_free(OpenTypeFile *file) {
|
||||
delete file->head;
|
||||
}
|
||||
|
||||
} // namespace
|
|
@ -0,0 +1,29 @@
|
|||
// Copyright (c) 2009 The Chromium Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style license that can be
|
||||
// found in the LICENSE file.
|
||||
|
||||
#ifndef OTS_HEAD_H_
|
||||
#define OTS_HEAD_H_
|
||||
|
||||
#include "ots.h"
|
||||
|
||||
namespace ots {
|
||||
|
||||
struct OpenTypeHEAD {
|
||||
uint32_t revision;
|
||||
uint16_t flags;
|
||||
uint16_t ppem;
|
||||
uint64_t created;
|
||||
uint64_t modified;
|
||||
|
||||
int16_t xmin, xmax;
|
||||
int16_t ymin, ymax;
|
||||
|
||||
uint16_t mac_style;
|
||||
uint16_t min_ppem;
|
||||
int16_t index_to_loc_format;
|
||||
};
|
||||
|
||||
} // namespace ots
|
||||
|
||||
#endif // OTS_HEAD_H_
|
|
@ -0,0 +1,120 @@
|
|||
// Copyright (c) 2009 The Chromium 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 "hhea.h"
|
||||
|
||||
#include "head.h"
|
||||
#include "maxp.h"
|
||||
|
||||
// hhea - Horizontal Header
|
||||
// http://www.microsoft.com/opentype/otspec/hhea.htm
|
||||
|
||||
namespace ots {
|
||||
|
||||
bool ots_hhea_parse(OpenTypeFile *file, const uint8_t *data, size_t length) {
|
||||
Buffer table(data, length);
|
||||
OpenTypeHHEA *hhea = new OpenTypeHHEA;
|
||||
file->hhea = hhea;
|
||||
|
||||
uint32_t version = 0;
|
||||
if (!table.ReadU32(&version)) {
|
||||
return OTS_FAILURE();
|
||||
}
|
||||
if (version >> 16 != 1) {
|
||||
return OTS_FAILURE();
|
||||
}
|
||||
|
||||
if (!table.ReadS16(&hhea->ascent) ||
|
||||
!table.ReadS16(&hhea->descent) ||
|
||||
!table.ReadS16(&hhea->linegap) ||
|
||||
!table.ReadU16(&hhea->adv_width_max) ||
|
||||
!table.ReadS16(&hhea->min_lsb) ||
|
||||
!table.ReadS16(&hhea->min_rsb) ||
|
||||
!table.ReadS16(&hhea->x_max_extent) ||
|
||||
!table.ReadS16(&hhea->caret_slope_rise) ||
|
||||
!table.ReadS16(&hhea->caret_slope_run) ||
|
||||
!table.ReadS16(&hhea->caret_offset)) {
|
||||
return OTS_FAILURE();
|
||||
}
|
||||
|
||||
if (hhea->ascent < 0) {
|
||||
OTS_WARNING("bad ascent: %d", hhea->ascent);
|
||||
hhea->ascent = 0;
|
||||
}
|
||||
if (hhea->linegap < 0) {
|
||||
OTS_WARNING("bad linegap: %d", hhea->linegap);
|
||||
hhea->linegap = 0;
|
||||
}
|
||||
|
||||
if (!file->head) {
|
||||
return OTS_FAILURE();
|
||||
}
|
||||
|
||||
// if the font is non-slanted, caret_offset should be zero.
|
||||
if (!(file->head->mac_style & 2) &&
|
||||
(hhea->caret_offset != 0)) {
|
||||
OTS_WARNING("bad caret offset: %d", hhea->caret_offset);
|
||||
hhea->caret_offset = 0;
|
||||
}
|
||||
|
||||
// skip the reserved bytes
|
||||
if (!table.Skip(8)) {
|
||||
return OTS_FAILURE();
|
||||
}
|
||||
|
||||
int16_t data_format;
|
||||
if (!table.ReadS16(&data_format)) {
|
||||
return OTS_FAILURE();
|
||||
}
|
||||
if (data_format) {
|
||||
return OTS_FAILURE();
|
||||
}
|
||||
|
||||
if (!table.ReadU16(&hhea->num_hmetrics)) {
|
||||
return OTS_FAILURE();
|
||||
}
|
||||
|
||||
if (!file->maxp) {
|
||||
return OTS_FAILURE();
|
||||
}
|
||||
|
||||
if (hhea->num_hmetrics > file->maxp->num_glyphs) {
|
||||
return OTS_FAILURE();
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool ots_hhea_should_serialise(OpenTypeFile *file) {
|
||||
return file->hhea;
|
||||
}
|
||||
|
||||
bool ots_hhea_serialise(OTSStream *out, OpenTypeFile *file) {
|
||||
const OpenTypeHHEA *hhea = file->hhea;
|
||||
|
||||
if (!out->WriteU32(0x00010000) ||
|
||||
!out->WriteS16(hhea->ascent) ||
|
||||
!out->WriteS16(hhea->descent) ||
|
||||
!out->WriteS16(hhea->linegap) ||
|
||||
!out->WriteU16(hhea->adv_width_max) ||
|
||||
!out->WriteS16(hhea->min_lsb) ||
|
||||
!out->WriteS16(hhea->min_rsb) ||
|
||||
!out->WriteS16(hhea->x_max_extent) ||
|
||||
!out->WriteS16(hhea->caret_slope_rise) ||
|
||||
!out->WriteS16(hhea->caret_slope_run) ||
|
||||
!out->WriteS16(hhea->caret_offset) ||
|
||||
!out->WriteR64(0) || // reserved
|
||||
!out->WriteS16(0) || // metric data format
|
||||
!out->WriteU16(hhea->num_hmetrics)) {
|
||||
return OTS_FAILURE();
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void ots_hhea_free(OpenTypeFile *file) {
|
||||
delete file->hhea;
|
||||
}
|
||||
|
||||
} // namespace ots
|
|
@ -0,0 +1,28 @@
|
|||
// Copyright (c) 2009 The Chromium Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style license that can be
|
||||
// found in the LICENSE file.
|
||||
|
||||
#ifndef OTS_HHEA_H_
|
||||
#define OTS_HHEA_H_
|
||||
|
||||
#include "ots.h"
|
||||
|
||||
namespace ots {
|
||||
|
||||
struct OpenTypeHHEA {
|
||||
int16_t ascent;
|
||||
int16_t descent;
|
||||
int16_t linegap;
|
||||
uint16_t adv_width_max;
|
||||
int16_t min_lsb;
|
||||
int16_t min_rsb;
|
||||
int16_t x_max_extent;
|
||||
int16_t caret_slope_rise;
|
||||
int16_t caret_slope_run;
|
||||
int16_t caret_offset;
|
||||
uint16_t num_hmetrics;
|
||||
};
|
||||
|
||||
} // namespace ots
|
||||
|
||||
#endif // OTS_HHEA_H_
|
|
@ -0,0 +1,107 @@
|
|||
// Copyright (c) 2009 The Chromium 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 "hmtx.h"
|
||||
|
||||
#include "hhea.h"
|
||||
#include "maxp.h"
|
||||
|
||||
// hmtx - Horizontal Metrics
|
||||
// http://www.microsoft.com/opentype/otspec/hmtx.htm
|
||||
|
||||
namespace ots {
|
||||
|
||||
bool ots_hmtx_parse(OpenTypeFile *file, const uint8_t *data, size_t length) {
|
||||
Buffer table(data, length);
|
||||
OpenTypeHMTX *hmtx = new OpenTypeHMTX;
|
||||
file->hmtx = hmtx;
|
||||
|
||||
if (!file->hhea || !file->maxp) {
|
||||
return OTS_FAILURE();
|
||||
}
|
||||
|
||||
// |num_hmetrics| is a uint16_t, so it's bounded < 65536. This limits that
|
||||
// amount of memory that we'll allocate for this to a sane amount.
|
||||
const unsigned num_hmetrics = file->hhea->num_hmetrics;
|
||||
|
||||
if (num_hmetrics > file->maxp->num_glyphs) {
|
||||
return OTS_FAILURE();
|
||||
}
|
||||
if (!num_hmetrics) {
|
||||
return OTS_FAILURE();
|
||||
}
|
||||
const unsigned num_lsbs = file->maxp->num_glyphs - num_hmetrics;
|
||||
|
||||
hmtx->metrics.reserve(num_hmetrics);
|
||||
for (unsigned i = 0; i < num_hmetrics; ++i) {
|
||||
uint16_t adv = 0;
|
||||
int16_t lsb = 0;
|
||||
if (!table.ReadU16(&adv) || !table.ReadS16(&lsb)) {
|
||||
return OTS_FAILURE();
|
||||
}
|
||||
|
||||
// Since so many fonts don't have proper value on |adv| and |lsb|,
|
||||
// we should not call ots_failure() here. For example, about 20% of fonts
|
||||
// in http://www.princexml.com/fonts/ (200+ fonts) fails these tests.
|
||||
if (adv > file->hhea->adv_width_max) {
|
||||
OTS_WARNING("bad adv: %u > %u", adv, file->hhea->adv_width_max);
|
||||
adv = file->hhea->adv_width_max;
|
||||
}
|
||||
if (lsb < file->hhea->min_lsb) {
|
||||
OTS_WARNING("bad lsb: %d < %d", lsb, file->hhea->min_lsb);
|
||||
lsb = file->hhea->min_lsb;
|
||||
}
|
||||
|
||||
hmtx->metrics.push_back(std::make_pair(adv, lsb));
|
||||
}
|
||||
|
||||
hmtx->lsbs.reserve(num_lsbs);
|
||||
for (unsigned i = 0; i < num_lsbs; ++i) {
|
||||
int16_t lsb;
|
||||
if (!table.ReadS16(&lsb)) {
|
||||
// Some Japanese fonts (e.g., mona.ttf) fail this test.
|
||||
return OTS_FAILURE();
|
||||
}
|
||||
|
||||
if (lsb < file->hhea->min_lsb) {
|
||||
// The same as above. Three fonts in http://www.fontsquirrel.com/fontface
|
||||
// (e.g., Notice2Std.otf) have weird lsb values.
|
||||
OTS_WARNING("bad lsb: %d < %d", lsb, file->hhea->min_lsb);
|
||||
lsb = file->hhea->min_lsb;
|
||||
}
|
||||
|
||||
hmtx->lsbs.push_back(lsb);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool ots_hmtx_should_serialise(OpenTypeFile *file) {
|
||||
return file->hmtx;
|
||||
}
|
||||
|
||||
bool ots_hmtx_serialise(OTSStream *out, OpenTypeFile *file) {
|
||||
const OpenTypeHMTX *hmtx = file->hmtx;
|
||||
|
||||
for (unsigned i = 0; i < hmtx->metrics.size(); ++i) {
|
||||
if (!out->WriteU16(hmtx->metrics[i].first) ||
|
||||
!out->WriteS16(hmtx->metrics[i].second)) {
|
||||
return OTS_FAILURE();
|
||||
}
|
||||
}
|
||||
|
||||
for (unsigned i = 0; i < hmtx->lsbs.size(); ++i) {
|
||||
if (!out->WriteS16(hmtx->lsbs[i])) {
|
||||
return OTS_FAILURE();
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void ots_hmtx_free(OpenTypeFile *file) {
|
||||
delete file->hmtx;
|
||||
}
|
||||
|
||||
} // namespace ots
|
|
@ -0,0 +1,22 @@
|
|||
// Copyright (c) 2009 The Chromium Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style license that can be
|
||||
// found in the LICENSE file.
|
||||
|
||||
#ifndef OTS_HMTX_H_
|
||||
#define OTS_HMTX_H_
|
||||
|
||||
#include <utility> // std::pair
|
||||
#include <vector>
|
||||
|
||||
#include "ots.h"
|
||||
|
||||
namespace ots {
|
||||
|
||||
struct OpenTypeHMTX {
|
||||
std::vector<std::pair<uint16_t, int16_t> > metrics;
|
||||
std::vector<int16_t> lsbs;
|
||||
};
|
||||
|
||||
} // namespace ots
|
||||
|
||||
#endif // OTS_HMTX_H_
|
|
@ -0,0 +1,196 @@
|
|||
// Copyright (c) 2009 The Chromium 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 "kern.h"
|
||||
|
||||
// kern - Kerning
|
||||
// http://www.microsoft.com/opentype/otspec/kern.htm
|
||||
|
||||
#define DROP_THIS_TABLE \
|
||||
do { delete file->kern; file->kern = 0; } while (0)
|
||||
|
||||
namespace ots {
|
||||
|
||||
bool ots_kern_parse(OpenTypeFile *file, const uint8_t *data, size_t length) {
|
||||
Buffer table(data, length);
|
||||
|
||||
OpenTypeKERN *kern = new OpenTypeKERN;
|
||||
file->kern = kern;
|
||||
|
||||
uint16_t num_tables = 0;
|
||||
if (!table.ReadU16(&kern->version) ||
|
||||
!table.ReadU16(&num_tables)) {
|
||||
return OTS_FAILURE();
|
||||
}
|
||||
|
||||
if (kern->version > 0) {
|
||||
DROP_THIS_TABLE;
|
||||
return true;
|
||||
}
|
||||
|
||||
if (num_tables == 0) {
|
||||
OTS_WARNING("num_tables is zero");
|
||||
DROP_THIS_TABLE;
|
||||
return true;
|
||||
}
|
||||
|
||||
kern->subtables.reserve(num_tables);
|
||||
for (unsigned i = 0; i < num_tables; ++i) {
|
||||
OpenTypeKERNFormat0 subtable;
|
||||
uint16_t sub_length = 0;
|
||||
|
||||
if (!table.ReadU16(&subtable.version) ||
|
||||
!table.ReadU16(&sub_length)) {
|
||||
return OTS_FAILURE();
|
||||
}
|
||||
|
||||
if (subtable.version > 0) {
|
||||
OTS_WARNING("Bad subtable version: %d", subtable.version);
|
||||
continue;
|
||||
}
|
||||
|
||||
const size_t current_offset = table.offset();
|
||||
if (current_offset - 4 + sub_length > length) {
|
||||
return OTS_FAILURE();
|
||||
}
|
||||
|
||||
if (!table.ReadU16(&subtable.coverage)) {
|
||||
return OTS_FAILURE();
|
||||
}
|
||||
|
||||
if (!(subtable.coverage & 0x1)) {
|
||||
OTS_WARNING(
|
||||
"We don't support vertical data as the renderer doesn't support it.");
|
||||
continue;
|
||||
}
|
||||
if (subtable.coverage & 0xF0) {
|
||||
OTS_WARNING("Reserved fields should zero-filled.");
|
||||
DROP_THIS_TABLE;
|
||||
return true;
|
||||
}
|
||||
const uint32_t format = (subtable.coverage & 0xFF00) >> 8;
|
||||
if (format != 0) {
|
||||
OTS_WARNING("Format %d is not supported.", format);
|
||||
continue;
|
||||
}
|
||||
|
||||
// Parse the format 0 field.
|
||||
uint16_t num_pairs = 0;
|
||||
if (!table.ReadU16(&num_pairs) ||
|
||||
!table.ReadU16(&subtable.search_range) ||
|
||||
!table.ReadU16(&subtable.entry_selector) ||
|
||||
!table.ReadU16(&subtable.range_shift)) {
|
||||
return OTS_FAILURE();
|
||||
}
|
||||
|
||||
if (!num_pairs) {
|
||||
OTS_WARNING("Zero length subtable is found.");
|
||||
DROP_THIS_TABLE;
|
||||
return true;
|
||||
}
|
||||
|
||||
// Sanity checks for search_range, entry_selector, and range_shift. See the
|
||||
// comment in ots.cc for details.
|
||||
const size_t kFormat0PairSize = 6; // left, right, and value. 2 bytes each.
|
||||
if (num_pairs > (65536 / kFormat0PairSize)) {
|
||||
// Some fonts (e.g. calibri.ttf, pykes_peak_zero.ttf) have pairs >= 10923.
|
||||
OTS_WARNING("Too large subtable.");
|
||||
DROP_THIS_TABLE;
|
||||
return true;
|
||||
}
|
||||
unsigned max_pow2 = 0;
|
||||
while (1u << (max_pow2 + 1) <= num_pairs) {
|
||||
++max_pow2;
|
||||
}
|
||||
const uint16_t expected_search_range = (1u << max_pow2) * kFormat0PairSize;
|
||||
if (subtable.search_range != expected_search_range) {
|
||||
OTS_WARNING("bad search range");
|
||||
subtable.search_range = expected_search_range;
|
||||
}
|
||||
if (subtable.entry_selector != max_pow2) {
|
||||
return OTS_FAILURE();
|
||||
}
|
||||
const uint32_t expected_range_shift
|
||||
= kFormat0PairSize * num_pairs - subtable.search_range;
|
||||
if (subtable.range_shift != expected_range_shift) {
|
||||
OTS_WARNING("bad range shift");
|
||||
subtable.range_shift = expected_range_shift;
|
||||
}
|
||||
|
||||
// Read kerning pairs.
|
||||
subtable.pairs.reserve(num_pairs);
|
||||
uint32_t last_pair = 0;
|
||||
for (unsigned j = 0; j < num_pairs; ++j) {
|
||||
OpenTypeKERNFormat0Pair kerning_pair;
|
||||
if (!table.ReadU16(&kerning_pair.left) ||
|
||||
!table.ReadU16(&kerning_pair.right) ||
|
||||
!table.ReadS16(&kerning_pair.value)) {
|
||||
return OTS_FAILURE();
|
||||
}
|
||||
const uint32_t current_pair
|
||||
= (kerning_pair.left << 16) + kerning_pair.right;
|
||||
if (j != 0 && current_pair <= last_pair) {
|
||||
OTS_WARNING("Kerning pairs are not sorted.");
|
||||
// Many free fonts don't follow this rule, so we don't call OTS_FAILURE
|
||||
// in order to support these fonts.
|
||||
DROP_THIS_TABLE;
|
||||
return true;
|
||||
}
|
||||
last_pair = current_pair;
|
||||
subtable.pairs.push_back(kerning_pair);
|
||||
}
|
||||
|
||||
kern->subtables.push_back(subtable);
|
||||
}
|
||||
|
||||
if (!kern->subtables.size()) {
|
||||
OTS_WARNING("All subtables are removed.");
|
||||
DROP_THIS_TABLE;
|
||||
return true;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool ots_kern_should_serialise(OpenTypeFile *file) {
|
||||
if (!file->glyf) return false; // this table is not for CFF fonts.
|
||||
return file->kern;
|
||||
}
|
||||
|
||||
bool ots_kern_serialise(OTSStream *out, OpenTypeFile *file) {
|
||||
const OpenTypeKERN *kern = file->kern;
|
||||
|
||||
if (!out->WriteU16(kern->version) ||
|
||||
!out->WriteU16(kern->subtables.size())) {
|
||||
return OTS_FAILURE();
|
||||
}
|
||||
|
||||
for (unsigned i = 0; i < kern->subtables.size(); ++i) {
|
||||
const uint16_t length = 14 + (6 * kern->subtables[i].pairs.size());
|
||||
if (!out->WriteU16(kern->subtables[i].version) ||
|
||||
!out->WriteU16(length) ||
|
||||
!out->WriteU16(kern->subtables[i].coverage) ||
|
||||
!out->WriteU16(kern->subtables[i].pairs.size()) ||
|
||||
!out->WriteU16(kern->subtables[i].search_range) ||
|
||||
!out->WriteU16(kern->subtables[i].entry_selector) ||
|
||||
!out->WriteU16(kern->subtables[i].range_shift)) {
|
||||
return OTS_FAILURE();
|
||||
}
|
||||
for (unsigned j = 0; j < kern->subtables[i].pairs.size(); ++j) {
|
||||
if (!out->WriteU16(kern->subtables[i].pairs[j].left) ||
|
||||
!out->WriteU16(kern->subtables[i].pairs[j].right) ||
|
||||
!out->WriteS16(kern->subtables[i].pairs[j].value)) {
|
||||
return OTS_FAILURE();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void ots_kern_free(OpenTypeFile *file) {
|
||||
delete file->kern;
|
||||
}
|
||||
|
||||
} // namespace ots
|
|
@ -0,0 +1,40 @@
|
|||
// Copyright (c) 2009 The Chromium Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style license that can be
|
||||
// found in the LICENSE file.
|
||||
|
||||
#ifndef OTS_KERN_H_
|
||||
#define OTS_KERN_H_
|
||||
|
||||
#include <vector>
|
||||
|
||||
#include "ots.h"
|
||||
|
||||
namespace ots {
|
||||
|
||||
struct OpenTypeKERNFormat0Pair {
|
||||
uint16_t left;
|
||||
uint16_t right;
|
||||
int16_t value;
|
||||
};
|
||||
|
||||
struct OpenTypeKERNFormat0 {
|
||||
uint16_t version;
|
||||
uint16_t coverage;
|
||||
uint16_t search_range;
|
||||
uint16_t entry_selector;
|
||||
uint16_t range_shift;
|
||||
std::vector<OpenTypeKERNFormat0Pair> pairs;
|
||||
};
|
||||
|
||||
// Format 2 is not supported. Since the format is not supported by Windows,
|
||||
// WebFonts unlikely use it. I've checked thousands of proprietary fonts and
|
||||
// free fonts, and found no font uses the format.
|
||||
|
||||
struct OpenTypeKERN {
|
||||
uint16_t version;
|
||||
std::vector<OpenTypeKERNFormat0> subtables;
|
||||
};
|
||||
|
||||
} // namespace ots
|
||||
|
||||
#endif // OTS_KERN_H_
|
|
@ -0,0 +1,98 @@
|
|||
// Copyright (c) 2009 The Chromium 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 "loca.h"
|
||||
|
||||
#include "head.h"
|
||||
#include "maxp.h"
|
||||
|
||||
// loca - Index to Location
|
||||
// http://www.microsoft.com/opentype/otspec/loca.htm
|
||||
|
||||
namespace ots {
|
||||
|
||||
bool ots_loca_parse(OpenTypeFile *file, const uint8_t *data, size_t length) {
|
||||
Buffer table(data, length);
|
||||
|
||||
// We can't do anything useful in validating this data except to ensure that
|
||||
// the values are monotonically increasing.
|
||||
|
||||
OpenTypeLOCA *loca = new OpenTypeLOCA;
|
||||
file->loca = loca;
|
||||
|
||||
if (!file->maxp || !file->head) {
|
||||
return OTS_FAILURE();
|
||||
}
|
||||
|
||||
const unsigned num_glyphs = file->maxp->num_glyphs;
|
||||
unsigned last_offset = 0;
|
||||
loca->offsets.resize(num_glyphs + 1);
|
||||
// maxp->num_glyphs is uint16_t, thus the addition never overflows.
|
||||
|
||||
if (file->head->index_to_loc_format == 0) {
|
||||
// Note that the <= here (and below) is correct. There is one more offset
|
||||
// than the number of glyphs in order to give the length of the final
|
||||
// glyph.
|
||||
for (unsigned i = 0; i <= num_glyphs; ++i) {
|
||||
uint16_t offset = 0;
|
||||
if (!table.ReadU16(&offset)) {
|
||||
return OTS_FAILURE();
|
||||
}
|
||||
if (offset < last_offset) {
|
||||
return OTS_FAILURE();
|
||||
}
|
||||
last_offset = offset;
|
||||
loca->offsets[i] = offset * 2;
|
||||
}
|
||||
} else {
|
||||
for (unsigned i = 0; i <= num_glyphs; ++i) {
|
||||
uint32_t offset = 0;
|
||||
if (!table.ReadU32(&offset)) {
|
||||
return OTS_FAILURE();
|
||||
}
|
||||
if (offset < last_offset) {
|
||||
return OTS_FAILURE();
|
||||
}
|
||||
last_offset = offset;
|
||||
loca->offsets[i] = offset;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool ots_loca_should_serialise(OpenTypeFile *file) {
|
||||
return file->loca;
|
||||
}
|
||||
|
||||
bool ots_loca_serialise(OTSStream *out, OpenTypeFile *file) {
|
||||
const OpenTypeLOCA *loca = file->loca;
|
||||
const OpenTypeHEAD *head = file->head;
|
||||
|
||||
if (!head) {
|
||||
return OTS_FAILURE();
|
||||
}
|
||||
|
||||
if (head->index_to_loc_format == 0) {
|
||||
for (unsigned i = 0; i < loca->offsets.size(); ++i) {
|
||||
if (!out->WriteU16(loca->offsets[i] >> 1)) {
|
||||
return OTS_FAILURE();
|
||||
}
|
||||
}
|
||||
} else {
|
||||
for (unsigned i = 0; i < loca->offsets.size(); ++i) {
|
||||
if (!out->WriteU32(loca->offsets[i])) {
|
||||
return OTS_FAILURE();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void ots_loca_free(OpenTypeFile *file) {
|
||||
delete file->loca;
|
||||
}
|
||||
|
||||
} // namespace ots
|
|
@ -0,0 +1,20 @@
|
|||
// Copyright (c) 2009 The Chromium Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style license that can be
|
||||
// found in the LICENSE file.
|
||||
|
||||
#ifndef OTS_LOCA_H_
|
||||
#define OTS_LOCA_H_
|
||||
|
||||
#include <vector>
|
||||
|
||||
#include "ots.h"
|
||||
|
||||
namespace ots {
|
||||
|
||||
struct OpenTypeLOCA {
|
||||
std::vector<uint32_t> offsets;
|
||||
};
|
||||
|
||||
} // namespace ots
|
||||
|
||||
#endif // OTS_LOCA_H_
|
|
@ -0,0 +1,82 @@
|
|||
// Copyright (c) 2009 The Chromium 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 "ltsh.h"
|
||||
|
||||
#include "maxp.h"
|
||||
|
||||
// LTSH - Linear Threshold
|
||||
// http://www.microsoft.com/typography/otspec/ltsh.htm
|
||||
|
||||
#define DROP_THIS_TABLE \
|
||||
do { delete file->ltsh; file->ltsh = 0; } while (0)
|
||||
|
||||
namespace ots {
|
||||
|
||||
bool ots_ltsh_parse(OpenTypeFile *file, const uint8_t *data, size_t length) {
|
||||
Buffer table(data, length);
|
||||
|
||||
if (!file->maxp) {
|
||||
return OTS_FAILURE();
|
||||
}
|
||||
|
||||
OpenTypeLTSH *ltsh = new OpenTypeLTSH;
|
||||
file->ltsh = ltsh;
|
||||
|
||||
uint16_t num_glyphs = 0;
|
||||
if (!table.ReadU16(<sh->version) ||
|
||||
!table.ReadU16(&num_glyphs)) {
|
||||
return OTS_FAILURE();
|
||||
}
|
||||
|
||||
if (ltsh->version != 0) {
|
||||
OTS_WARNING("bad version: %u", ltsh->version);
|
||||
DROP_THIS_TABLE;
|
||||
return true;
|
||||
}
|
||||
|
||||
if (num_glyphs != file->maxp->num_glyphs) {
|
||||
OTS_WARNING("bad num_glyphs: %u", num_glyphs);
|
||||
DROP_THIS_TABLE;
|
||||
return true;
|
||||
}
|
||||
|
||||
ltsh->ypels.reserve(num_glyphs);
|
||||
for (unsigned i = 0; i < num_glyphs; ++i) {
|
||||
uint8_t pel = 0;
|
||||
if (!table.ReadU8(&pel)) {
|
||||
return OTS_FAILURE();
|
||||
}
|
||||
ltsh->ypels.push_back(pel);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool ots_ltsh_should_serialise(OpenTypeFile *file) {
|
||||
if (!file->glyf) return false; // this table is not for CFF fonts.
|
||||
return file->ltsh;
|
||||
}
|
||||
|
||||
bool ots_ltsh_serialise(OTSStream *out, OpenTypeFile *file) {
|
||||
const OpenTypeLTSH *ltsh = file->ltsh;
|
||||
|
||||
if (!out->WriteU16(ltsh->version) ||
|
||||
!out->WriteU16(ltsh->ypels.size())) {
|
||||
return OTS_FAILURE();
|
||||
}
|
||||
for (unsigned i = 0; i < ltsh->ypels.size(); ++i) {
|
||||
if (!out->Write(&(ltsh->ypels[i]), 1)) {
|
||||
return OTS_FAILURE();
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void ots_ltsh_free(OpenTypeFile *file) {
|
||||
delete file->ltsh;
|
||||
}
|
||||
|
||||
} // namespace ots
|
|
@ -0,0 +1,21 @@
|
|||
// Copyright (c) 2009 The Chromium Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style license that can be
|
||||
// found in the LICENSE file.
|
||||
|
||||
#ifndef OTS_LTSH_H_
|
||||
#define OTS_LTSH_H_
|
||||
|
||||
#include <vector>
|
||||
|
||||
#include "ots.h"
|
||||
|
||||
namespace ots {
|
||||
|
||||
struct OpenTypeLTSH {
|
||||
uint16_t version;
|
||||
std::vector<uint8_t> ypels;
|
||||
};
|
||||
|
||||
} // namespace ots
|
||||
|
||||
#endif // OTS_LTSH_H_
|
|
@ -0,0 +1,128 @@
|
|||
// Copyright (c) 2009 The Chromium 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 "maxp.h"
|
||||
|
||||
// maxp - Maximum Profile
|
||||
// http://www.microsoft.com/opentype/otspec/maxp.htm
|
||||
|
||||
namespace ots {
|
||||
|
||||
bool ots_maxp_parse(OpenTypeFile *file, const uint8_t *data, size_t length) {
|
||||
Buffer table(data, length);
|
||||
|
||||
OpenTypeMAXP *maxp = new OpenTypeMAXP;
|
||||
file->maxp = maxp;
|
||||
|
||||
uint32_t version = 0;
|
||||
if (!table.ReadU32(&version)) {
|
||||
return OTS_FAILURE();
|
||||
}
|
||||
|
||||
if (version >> 16 > 1) {
|
||||
return OTS_FAILURE();
|
||||
}
|
||||
|
||||
if (!table.ReadU16(&maxp->num_glyphs)) {
|
||||
return OTS_FAILURE();
|
||||
}
|
||||
|
||||
if (!maxp->num_glyphs) {
|
||||
return OTS_FAILURE();
|
||||
}
|
||||
|
||||
if (version >> 16 == 1) {
|
||||
maxp->version_1 = true;
|
||||
if (!table.ReadU16(&maxp->max_points) ||
|
||||
!table.ReadU16(&maxp->max_contours) ||
|
||||
!table.ReadU16(&maxp->max_c_points) ||
|
||||
!table.ReadU16(&maxp->max_c_contours) ||
|
||||
!table.ReadU16(&maxp->max_zones) ||
|
||||
!table.ReadU16(&maxp->max_t_points) ||
|
||||
!table.ReadU16(&maxp->max_storage) ||
|
||||
!table.ReadU16(&maxp->max_fdefs) ||
|
||||
!table.ReadU16(&maxp->max_idefs) ||
|
||||
!table.ReadU16(&maxp->max_stack) ||
|
||||
!table.ReadU16(&maxp->max_size_glyf_instructions) ||
|
||||
!table.ReadU16(&maxp->max_c_components) ||
|
||||
!table.ReadU16(&maxp->max_c_depth)) {
|
||||
return OTS_FAILURE();
|
||||
}
|
||||
|
||||
if (maxp->max_zones == 0) {
|
||||
// workaround for ipa*.ttf Japanese fonts.
|
||||
OTS_WARNING("bad max_zones: %u", maxp->max_zones);
|
||||
maxp->max_zones = 1;
|
||||
} else if (maxp->max_zones == 3) {
|
||||
// workaround for Ecolier-*.ttf fonts.
|
||||
OTS_WARNING("bad max_zones: %u", maxp->max_zones);
|
||||
maxp->max_zones = 2;
|
||||
}
|
||||
|
||||
if ((maxp->max_zones != 1) && (maxp->max_zones != 2)) {
|
||||
return OTS_FAILURE();
|
||||
}
|
||||
} else {
|
||||
maxp->version_1 = false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool ots_maxp_should_serialise(OpenTypeFile *file) {
|
||||
return file->maxp;
|
||||
}
|
||||
|
||||
bool ots_maxp_serialise(OTSStream *out, OpenTypeFile *file) {
|
||||
const OpenTypeMAXP *maxp = file->maxp;
|
||||
|
||||
if (!out->WriteU32(maxp->version_1 ? 0x00010000 : 0x00005000) ||
|
||||
!out->WriteU16(maxp->num_glyphs)) {
|
||||
return OTS_FAILURE();
|
||||
}
|
||||
|
||||
if (!maxp->version_1) return true;
|
||||
|
||||
if (!out->WriteU16(maxp->max_points) ||
|
||||
!out->WriteU16(maxp->max_contours) ||
|
||||
!out->WriteU16(maxp->max_c_points) ||
|
||||
!out->WriteU16(maxp->max_c_contours)) {
|
||||
return OTS_FAILURE();
|
||||
}
|
||||
|
||||
if (g_transcode_hints) {
|
||||
if (!out->WriteU16(maxp->max_zones) ||
|
||||
!out->WriteU16(maxp->max_t_points) ||
|
||||
!out->WriteU16(maxp->max_storage) ||
|
||||
!out->WriteU16(maxp->max_fdefs) ||
|
||||
!out->WriteU16(maxp->max_idefs) ||
|
||||
!out->WriteU16(maxp->max_stack) ||
|
||||
!out->WriteU16(maxp->max_size_glyf_instructions)) {
|
||||
return OTS_FAILURE();
|
||||
}
|
||||
} else {
|
||||
if (!out->WriteU16(1) || // max zones
|
||||
!out->WriteU16(0) || // max twilight points
|
||||
!out->WriteU16(0) || // max storage
|
||||
!out->WriteU16(0) || // max function defs
|
||||
!out->WriteU16(0) || // max instruction defs
|
||||
!out->WriteU16(0) || // max stack elements
|
||||
!out->WriteU16(0)) { // max instruction byte count
|
||||
return OTS_FAILURE();
|
||||
}
|
||||
}
|
||||
|
||||
if (!out->WriteU16(maxp->max_c_components) ||
|
||||
!out->WriteU16(maxp->max_c_depth)) {
|
||||
return OTS_FAILURE();
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void ots_maxp_free(OpenTypeFile *file) {
|
||||
delete file->maxp;
|
||||
}
|
||||
|
||||
} // namespace ots
|
|
@ -0,0 +1,35 @@
|
|||
// Copyright (c) 2009 The Chromium Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style license that can be
|
||||
// found in the LICENSE file.
|
||||
|
||||
#ifndef OTS_MAXP_H_
|
||||
#define OTS_MAXP_H_
|
||||
|
||||
#include "ots.h"
|
||||
|
||||
namespace ots {
|
||||
|
||||
struct OpenTypeMAXP {
|
||||
uint16_t num_glyphs;
|
||||
bool version_1;
|
||||
|
||||
uint16_t max_points;
|
||||
uint16_t max_contours;
|
||||
uint16_t max_c_points;
|
||||
uint16_t max_c_contours;
|
||||
|
||||
uint16_t max_zones;
|
||||
uint16_t max_t_points;
|
||||
uint16_t max_storage;
|
||||
uint16_t max_fdefs;
|
||||
uint16_t max_idefs;
|
||||
uint16_t max_stack;
|
||||
uint16_t max_size_glyf_instructions;
|
||||
|
||||
uint16_t max_c_components;
|
||||
uint16_t max_c_depth;
|
||||
};
|
||||
|
||||
} // namespace ots
|
||||
|
||||
#endif // OTS_MAXP_H_
|
|
@ -0,0 +1,131 @@
|
|||
// Copyright (c) 2009 The Chromium 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 <cstring>
|
||||
|
||||
#include "cff.h"
|
||||
#include "ots.h"
|
||||
|
||||
// name - Naming Table
|
||||
// http://www.microsoft.com/opentype/otspec/name.htm
|
||||
|
||||
namespace ots {
|
||||
|
||||
bool ots_name_parse(OpenTypeFile *, const uint8_t *, size_t) {
|
||||
return true;
|
||||
}
|
||||
|
||||
bool ots_name_should_serialise(OpenTypeFile *) {
|
||||
return true;
|
||||
}
|
||||
|
||||
bool ots_name_serialise(OTSStream *out, OpenTypeFile *file) {
|
||||
// NAME is a required table, but we don't want anything to do with it. Thus,
|
||||
// we don't bother parsing it and we just serialise an empty name table.
|
||||
|
||||
const char* kStrings[] = {
|
||||
"Derived font data", // 0: copyright
|
||||
"OTS derived font", // 1: the name the user sees
|
||||
"Unspecified", // 2: face weight
|
||||
"UniqueID", // 3: unique id
|
||||
"OTS derivied font", // 4: human readable name
|
||||
"1.000", // 5: version
|
||||
"False", // 6: postscript name
|
||||
NULL, // 7: trademark data
|
||||
"OTS", // 8: foundary
|
||||
"OTS", // 9: designer
|
||||
};
|
||||
static const size_t kStringsLen = sizeof(kStrings) / sizeof(kStrings[0]);
|
||||
|
||||
// The spec says that "In CFF OpenType fonts, these two name strings, when
|
||||
// translated to ASCII, must also be identical to the font name as stored in
|
||||
// the CFF's Name INDEX." And actually, Mac OS X's font parser requires that.
|
||||
if (file->cff && !file->cff->name.empty()) {
|
||||
kStrings[6] = file->cff->name.c_str();
|
||||
}
|
||||
|
||||
unsigned num_strings = 0;
|
||||
for (unsigned i = 0; i < kStringsLen; ++i) {
|
||||
if (kStrings[i]) num_strings++;
|
||||
}
|
||||
|
||||
if (!out->WriteU16(0) || // version
|
||||
// Magic numbers:
|
||||
// 6: This entry (U16 * 3 = 6 bytes)
|
||||
// 2: Mac Roman & Windows Roman = 2 types
|
||||
// 12: Each string entry (U16 * 6 = 12 bytes)
|
||||
!out->WriteU16(num_strings * 2) || // count
|
||||
!out->WriteU16(6 + num_strings * 2 * 12)) { // string data offset
|
||||
return OTS_FAILURE();
|
||||
}
|
||||
|
||||
unsigned current_offset = 0;
|
||||
for (unsigned i = 0; i < kStringsLen; ++i) {
|
||||
if (!kStrings[i]) continue;
|
||||
|
||||
// string length in UTF-8 (ASCII).
|
||||
size_t len = std::strlen(kStrings[i]);
|
||||
|
||||
if (!out->WriteU16(1) || // Mac
|
||||
!out->WriteU16(0) || // Roman
|
||||
!out->WriteU16(0) || // English
|
||||
!out->WriteU16(i) ||
|
||||
!out->WriteU16(len) ||
|
||||
!out->WriteU16(current_offset)) {
|
||||
return OTS_FAILURE();
|
||||
}
|
||||
|
||||
current_offset += len;
|
||||
}
|
||||
|
||||
for (unsigned i = 0; i < kStringsLen; ++i) {
|
||||
if (!kStrings[i]) continue;
|
||||
|
||||
// string length in UTF-16.
|
||||
size_t len = std::strlen(kStrings[i]) * 2;
|
||||
|
||||
if (!out->WriteU16(3) || // Windows
|
||||
!out->WriteU16(1) || // Unicode BMP (UCS-2)
|
||||
!out->WriteU16(0x0409) || // US English
|
||||
!out->WriteU16(i) ||
|
||||
!out->WriteU16(len) ||
|
||||
!out->WriteU16(current_offset)) {
|
||||
return OTS_FAILURE();
|
||||
}
|
||||
|
||||
current_offset += len;
|
||||
}
|
||||
|
||||
// Write strings in Mac Roman compatible with ASCII.
|
||||
// Because all the entries are ASCII, we can just copy.
|
||||
for (unsigned i = 0; i < kStringsLen; ++i) {
|
||||
if (!kStrings[i]) continue;
|
||||
|
||||
const size_t len = std::strlen(kStrings[i]);
|
||||
if (!out->Write(kStrings[i], len)) {
|
||||
return OTS_FAILURE();
|
||||
}
|
||||
}
|
||||
|
||||
// Write strings in UCS-2. Because all the entries are ASCII,
|
||||
// we can just expand each byte to U16.
|
||||
for (unsigned i = 0; i < kStringsLen; ++i) {
|
||||
if (!kStrings[i]) continue;
|
||||
|
||||
const size_t len = std::strlen(kStrings[i]);
|
||||
for (size_t j = 0; j < len; ++j) {
|
||||
uint16_t v = kStrings[i][j];
|
||||
if (!out->WriteU16(v)) {
|
||||
return OTS_FAILURE();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void ots_name_free(OpenTypeFile *) {
|
||||
}
|
||||
|
||||
} // namespace
|
|
@ -0,0 +1,290 @@
|
|||
// Copyright (c) 2009 The Chromium 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 "os2.h"
|
||||
|
||||
#include "head.h"
|
||||
|
||||
// OS/2 - OS/2 and Windows Metrics
|
||||
// http://www.microsoft.com/opentype/otspec/os2.htm
|
||||
|
||||
namespace ots {
|
||||
|
||||
bool ots_os2_parse(OpenTypeFile *file, const uint8_t *data, size_t length) {
|
||||
Buffer table(data, length);
|
||||
|
||||
OpenTypeOS2 *os2 = new OpenTypeOS2;
|
||||
file->os2 = os2;
|
||||
|
||||
if (!table.ReadU16(&os2->version) ||
|
||||
!table.ReadS16(&os2->avg_char_width) ||
|
||||
!table.ReadU16(&os2->weight_class) ||
|
||||
!table.ReadU16(&os2->width_class) ||
|
||||
!table.ReadU16(&os2->type) ||
|
||||
!table.ReadS16(&os2->subscript_x_size) ||
|
||||
!table.ReadS16(&os2->subscript_y_size) ||
|
||||
!table.ReadS16(&os2->subscript_x_offset) ||
|
||||
!table.ReadS16(&os2->subscript_y_offset) ||
|
||||
!table.ReadS16(&os2->superscript_x_size) ||
|
||||
!table.ReadS16(&os2->superscript_y_size) ||
|
||||
!table.ReadS16(&os2->superscript_x_offset) ||
|
||||
!table.ReadS16(&os2->superscript_y_offset) ||
|
||||
!table.ReadS16(&os2->strikeout_size) ||
|
||||
!table.ReadS16(&os2->strikeout_position) ||
|
||||
!table.ReadS16(&os2->family_class)) {
|
||||
return OTS_FAILURE();
|
||||
}
|
||||
|
||||
if (os2->version > 4) {
|
||||
return OTS_FAILURE();
|
||||
}
|
||||
|
||||
// Some linux fonts (e.g., Kedage-t.ttf and LucidaSansDemiOblique.ttf) have
|
||||
// weird weight/width classes. Overwrite them with FW_NORMAL/1/9.
|
||||
if (os2->weight_class < 100 ||
|
||||
os2->weight_class > 900 ||
|
||||
os2->weight_class % 100) {
|
||||
OTS_WARNING("bad weight: %u", os2->weight_class);
|
||||
os2->weight_class = 400; // FW_NORMAL
|
||||
}
|
||||
if (os2->width_class < 1) {
|
||||
OTS_WARNING("bad width: %u", os2->width_class);
|
||||
os2->width_class = 1;
|
||||
} else if (os2->width_class > 9) {
|
||||
OTS_WARNING("bad width: %u", os2->width_class);
|
||||
os2->width_class = 9;
|
||||
}
|
||||
|
||||
// lowest 3 bits of fsType are exclusive.
|
||||
if (os2->type & 0x2) {
|
||||
// mask bits 2 & 3.
|
||||
os2->type &= 0xfff3u;
|
||||
} else if (os2->type & 0x4) {
|
||||
// mask bits 1 & 3.
|
||||
os2->type &= 0xfff4u;
|
||||
} else if (os2->type & 0x8) {
|
||||
// mask bits 1 & 2.
|
||||
os2->type &= 0xfff9u;
|
||||
}
|
||||
|
||||
// mask reserved bits. use only 0..3, 8, 9 bits.
|
||||
os2->type &= 0x30f;
|
||||
|
||||
if (os2->subscript_x_size < 0) {
|
||||
OTS_WARNING("bad subscript_x_size: %d", os2->subscript_x_size);
|
||||
os2->subscript_x_size = 0;
|
||||
}
|
||||
if (os2->subscript_y_size < 0) {
|
||||
OTS_WARNING("bad subscript_y_size: %d", os2->subscript_y_size);
|
||||
os2->subscript_y_size = 0;
|
||||
}
|
||||
if (os2->superscript_x_size < 0) {
|
||||
OTS_WARNING("bad superscript_x_size: %d", os2->superscript_x_size);
|
||||
os2->superscript_x_size = 0;
|
||||
}
|
||||
if (os2->superscript_y_size < 0) {
|
||||
OTS_WARNING("bad superscript_y_size: %d", os2->superscript_y_size);
|
||||
os2->superscript_y_size = 0;
|
||||
}
|
||||
if (os2->strikeout_size < 0) {
|
||||
OTS_WARNING("bad strikeout_size: %d", os2->strikeout_size);
|
||||
os2->strikeout_size = 0;
|
||||
}
|
||||
|
||||
for (unsigned i = 0; i < 10; ++i) {
|
||||
if (!table.ReadU8(&os2->panose[i])) {
|
||||
return OTS_FAILURE();
|
||||
}
|
||||
}
|
||||
|
||||
if (!table.ReadU32(&os2->unicode_range_1) ||
|
||||
!table.ReadU32(&os2->unicode_range_2) ||
|
||||
!table.ReadU32(&os2->unicode_range_3) ||
|
||||
!table.ReadU32(&os2->unicode_range_4) ||
|
||||
!table.ReadU32(&os2->vendor_id) ||
|
||||
!table.ReadU16(&os2->selection) ||
|
||||
!table.ReadU16(&os2->first_char_index) ||
|
||||
!table.ReadU16(&os2->last_char_index) ||
|
||||
!table.ReadS16(&os2->typo_ascender) ||
|
||||
!table.ReadS16(&os2->typo_descender) ||
|
||||
!table.ReadS16(&os2->typo_linegap) ||
|
||||
!table.ReadU16(&os2->win_ascent) ||
|
||||
!table.ReadU16(&os2->win_descent)) {
|
||||
return OTS_FAILURE();
|
||||
}
|
||||
|
||||
// If bit 6 is set, then bits 0 and 5 must be clear.
|
||||
if (os2->selection & 0x40) {
|
||||
os2->selection &= 0xffdeu;
|
||||
}
|
||||
|
||||
// the settings of bits 0 and 1 must be reflected in the macStyle bits
|
||||
// in the 'head' table.
|
||||
if (!file->head) {
|
||||
return OTS_FAILURE();
|
||||
}
|
||||
if ((os2->selection & 0x1) &&
|
||||
!(file->head->mac_style & 0x2)) {
|
||||
OTS_WARNING("adjusting Mac style (italic)");
|
||||
file->head->mac_style |= 0x2;
|
||||
}
|
||||
if ((os2->selection & 0x2) &&
|
||||
!(file->head->mac_style & 0x4)) {
|
||||
OTS_WARNING("adjusting Mac style (underscore)");
|
||||
file->head->mac_style |= 0x4;
|
||||
}
|
||||
|
||||
// While bit 6 on implies that bits 0 and 1 of macStyle are clear,
|
||||
// the reverse is not true.
|
||||
if ((os2->selection & 0x40) &&
|
||||
(file->head->mac_style & 0x3)) {
|
||||
OTS_WARNING("adjusting Mac style (regular)");
|
||||
file->head->mac_style &= 0xfffcu;
|
||||
}
|
||||
|
||||
if ((os2->version < 4) &&
|
||||
(os2->selection & 0x300)) {
|
||||
// bit 8 and 9 must be unset in OS/2 table versions less than 4.
|
||||
return OTS_FAILURE();
|
||||
}
|
||||
|
||||
// mask reserved bits. use only 0..9 bits.
|
||||
os2->selection &= 0x3ff;
|
||||
|
||||
if (os2->first_char_index > os2->last_char_index) {
|
||||
return OTS_FAILURE();
|
||||
}
|
||||
if (os2->typo_linegap < 0) {
|
||||
OTS_WARNING("bad linegap: %d", os2->typo_linegap);
|
||||
os2->typo_linegap = 0;
|
||||
}
|
||||
|
||||
if (os2->version < 1) {
|
||||
// http://www.microsoft.com/typography/otspec/os2ver0.htm
|
||||
return true;
|
||||
}
|
||||
|
||||
if (length < offsetof(OpenTypeOS2, code_page_range_2)) {
|
||||
OTS_WARNING("bad version number: %u", os2->version);
|
||||
// Some fonts (e.g., kredit1.ttf and quinquef.ttf) have weird version
|
||||
// numbers. Fix them.
|
||||
os2->version = 0;
|
||||
return true;
|
||||
}
|
||||
|
||||
if (!table.ReadU32(&os2->code_page_range_1) ||
|
||||
!table.ReadU32(&os2->code_page_range_2)) {
|
||||
return OTS_FAILURE();
|
||||
}
|
||||
|
||||
if (os2->version < 2) {
|
||||
// http://www.microsoft.com/typography/otspec/os2ver1.htm
|
||||
return true;
|
||||
}
|
||||
|
||||
if (length < offsetof(OpenTypeOS2, max_context)) {
|
||||
OTS_WARNING("bad version number: %u", os2->version);
|
||||
// some Japanese fonts (e.g., mona.ttf) have weird version number.
|
||||
// fix them.
|
||||
os2->version = 1;
|
||||
return true;
|
||||
}
|
||||
|
||||
if (!table.ReadS16(&os2->x_height) ||
|
||||
!table.ReadS16(&os2->cap_height) ||
|
||||
!table.ReadU16(&os2->default_char) ||
|
||||
!table.ReadU16(&os2->break_char) ||
|
||||
!table.ReadU16(&os2->max_context)) {
|
||||
return OTS_FAILURE();
|
||||
}
|
||||
|
||||
if (os2->x_height < 0) {
|
||||
OTS_WARNING("bad x_height: %d", os2->x_height);
|
||||
os2->x_height = 0;
|
||||
}
|
||||
if (os2->cap_height < 0) {
|
||||
OTS_WARNING("bad cap_height: %d", os2->cap_height);
|
||||
os2->cap_height = 0;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool ots_os2_should_serialise(OpenTypeFile *file) {
|
||||
return file->os2;
|
||||
}
|
||||
|
||||
bool ots_os2_serialise(OTSStream *out, OpenTypeFile *file) {
|
||||
const OpenTypeOS2 *os2 = file->os2;
|
||||
|
||||
if (!out->WriteU16(os2->version) ||
|
||||
!out->WriteS16(os2->avg_char_width) ||
|
||||
!out->WriteU16(os2->weight_class) ||
|
||||
!out->WriteU16(os2->width_class) ||
|
||||
!out->WriteU16(os2->type) ||
|
||||
!out->WriteS16(os2->subscript_x_size) ||
|
||||
!out->WriteS16(os2->subscript_y_size) ||
|
||||
!out->WriteS16(os2->subscript_x_offset) ||
|
||||
!out->WriteS16(os2->subscript_y_offset) ||
|
||||
!out->WriteS16(os2->superscript_x_size) ||
|
||||
!out->WriteS16(os2->superscript_y_size) ||
|
||||
!out->WriteS16(os2->superscript_x_offset) ||
|
||||
!out->WriteS16(os2->superscript_y_offset) ||
|
||||
!out->WriteS16(os2->strikeout_size) ||
|
||||
!out->WriteS16(os2->strikeout_position) ||
|
||||
!out->WriteS16(os2->family_class)) {
|
||||
return OTS_FAILURE();
|
||||
}
|
||||
|
||||
for (unsigned i = 0; i < 10; ++i) {
|
||||
if (!out->Write(&os2->panose[i], 1)) {
|
||||
return OTS_FAILURE();
|
||||
}
|
||||
}
|
||||
|
||||
if (!out->WriteU32(os2->unicode_range_1) ||
|
||||
!out->WriteU32(os2->unicode_range_2) ||
|
||||
!out->WriteU32(os2->unicode_range_3) ||
|
||||
!out->WriteU32(os2->unicode_range_4) ||
|
||||
!out->WriteU32(os2->vendor_id) ||
|
||||
!out->WriteU16(os2->selection) ||
|
||||
!out->WriteU16(os2->first_char_index) ||
|
||||
!out->WriteU16(os2->last_char_index) ||
|
||||
!out->WriteS16(os2->typo_ascender) ||
|
||||
!out->WriteS16(os2->typo_descender) ||
|
||||
!out->WriteS16(os2->typo_linegap) ||
|
||||
!out->WriteU16(os2->win_ascent) ||
|
||||
!out->WriteU16(os2->win_descent)) {
|
||||
return OTS_FAILURE();
|
||||
}
|
||||
|
||||
if (os2->version < 1) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if (!out->WriteU32(os2->code_page_range_1) ||
|
||||
!out->WriteU32(os2->code_page_range_2)) {
|
||||
return OTS_FAILURE();
|
||||
}
|
||||
|
||||
if (os2->version < 2) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if (!out->WriteS16(os2->x_height) ||
|
||||
!out->WriteS16(os2->cap_height) ||
|
||||
!out->WriteU16(os2->default_char) ||
|
||||
!out->WriteU16(os2->break_char) ||
|
||||
!out->WriteU16(os2->max_context)) {
|
||||
return OTS_FAILURE();
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void ots_os2_free(OpenTypeFile *file) {
|
||||
delete file->os2;
|
||||
}
|
||||
|
||||
} // namespace ots
|
|
@ -0,0 +1,54 @@
|
|||
// Copyright (c) 2009 The Chromium Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style license that can be
|
||||
// found in the LICENSE file.
|
||||
|
||||
#ifndef OTS_OS2_H_
|
||||
#define OTS_OS2_H_
|
||||
|
||||
#include "ots.h"
|
||||
|
||||
namespace ots {
|
||||
|
||||
struct OpenTypeOS2 {
|
||||
uint16_t version;
|
||||
int16_t avg_char_width;
|
||||
uint16_t weight_class;
|
||||
uint16_t width_class;
|
||||
uint16_t type;
|
||||
int16_t subscript_x_size;
|
||||
int16_t subscript_y_size;
|
||||
int16_t subscript_x_offset;
|
||||
int16_t subscript_y_offset;
|
||||
int16_t superscript_x_size;
|
||||
int16_t superscript_y_size;
|
||||
int16_t superscript_x_offset;
|
||||
int16_t superscript_y_offset;
|
||||
int16_t strikeout_size;
|
||||
int16_t strikeout_position;
|
||||
int16_t family_class;
|
||||
uint8_t panose[10];
|
||||
uint32_t unicode_range_1;
|
||||
uint32_t unicode_range_2;
|
||||
uint32_t unicode_range_3;
|
||||
uint32_t unicode_range_4;
|
||||
uint32_t vendor_id;
|
||||
uint16_t selection;
|
||||
uint16_t first_char_index;
|
||||
uint16_t last_char_index;
|
||||
int16_t typo_ascender;
|
||||
int16_t typo_descender;
|
||||
int16_t typo_linegap;
|
||||
uint16_t win_ascent;
|
||||
uint16_t win_descent;
|
||||
uint32_t code_page_range_1;
|
||||
uint32_t code_page_range_2;
|
||||
int16_t x_height;
|
||||
int16_t cap_height;
|
||||
uint16_t default_char;
|
||||
uint16_t break_char;
|
||||
uint16_t max_context;
|
||||
};
|
||||
|
||||
} // namespace ots
|
||||
|
||||
#endif // OTS_OS2_H_
|
|
@ -0,0 +1,626 @@
|
|||
// Copyright (c) 2009 The Chromium 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 "ots.h"
|
||||
|
||||
#include <sys/types.h>
|
||||
#include <zlib.h>
|
||||
|
||||
#include <algorithm>
|
||||
#include <cstdlib>
|
||||
#include <cstring>
|
||||
#include <map>
|
||||
#include <vector>
|
||||
|
||||
// The OpenType Font File
|
||||
// http://www.microsoft.com/opentype/otspec/otff.htm
|
||||
|
||||
#define F(name, capname) \
|
||||
namespace ots { \
|
||||
bool ots_##name##_parse(OpenTypeFile *f, const uint8_t *d, size_t l); \
|
||||
bool ots_##name##_should_serialise(OpenTypeFile *f); \
|
||||
bool ots_##name##_serialise(OTSStream *s, OpenTypeFile *f); \
|
||||
void ots_##name##_free(OpenTypeFile *f); \
|
||||
}
|
||||
// TODO(yusukes): change these function names to follow Chromium coding rule.
|
||||
FOR_EACH_TABLE_TYPE
|
||||
#undef F
|
||||
|
||||
namespace {
|
||||
|
||||
bool g_debug_output = true;
|
||||
|
||||
struct OpenTypeTable {
|
||||
uint32_t tag;
|
||||
uint32_t chksum;
|
||||
uint32_t offset;
|
||||
uint32_t length;
|
||||
uint32_t uncompressed_length;
|
||||
};
|
||||
|
||||
// Round a value up to the nearest multiple of 4. Note that this can overflow
|
||||
// and return zero.
|
||||
template<typename T> T Round4(T value) {
|
||||
return (value + 3) & ~3;
|
||||
}
|
||||
|
||||
uint32_t Tag(const char *tag_str) {
|
||||
uint32_t ret;
|
||||
std::memcpy(&ret, tag_str, 4);
|
||||
return ret;
|
||||
}
|
||||
|
||||
bool CheckTag(uint32_t tag_value) {
|
||||
for (unsigned i = 0; i < 4; ++i) {
|
||||
const uint32_t check = tag_value & 0xff;
|
||||
if (check < 32 || check > 126) {
|
||||
return false; // non-ASCII character found.
|
||||
}
|
||||
tag_value >>= 8;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
struct OutputTable {
|
||||
uint32_t tag;
|
||||
size_t offset;
|
||||
size_t length;
|
||||
uint32_t chksum;
|
||||
|
||||
static bool SortByTag(const OutputTable& a, const OutputTable& b) {
|
||||
const uint32_t atag = ntohl(a.tag);
|
||||
const uint32_t btag = ntohl(b.tag);
|
||||
return atag < btag;
|
||||
}
|
||||
};
|
||||
|
||||
struct Arena {
|
||||
public:
|
||||
~Arena() {
|
||||
for (std::vector<uint8_t*>::iterator
|
||||
i = hunks_.begin(); i != hunks_.end(); ++i) {
|
||||
delete[] *i;
|
||||
}
|
||||
}
|
||||
|
||||
uint8_t* Allocate(size_t length) {
|
||||
uint8_t* p = new uint8_t[length];
|
||||
hunks_.push_back(p);
|
||||
return p;
|
||||
}
|
||||
|
||||
private:
|
||||
std::vector<uint8_t*> hunks_;
|
||||
};
|
||||
|
||||
const struct {
|
||||
uint32_t tag;
|
||||
bool (*parse)(ots::OpenTypeFile *otf, const uint8_t *data, size_t length);
|
||||
bool (*serialise)(ots::OTSStream *out, ots::OpenTypeFile *file);
|
||||
bool (*should_serialise)(ots::OpenTypeFile *file);
|
||||
void (*free)(ots::OpenTypeFile *file);
|
||||
bool required;
|
||||
} table_parsers[] = {
|
||||
{ Tag("maxp"), ots::ots_maxp_parse, ots::ots_maxp_serialise,
|
||||
ots::ots_maxp_should_serialise, ots::ots_maxp_free, true },
|
||||
{ Tag("head"), ots::ots_head_parse, ots::ots_head_serialise,
|
||||
ots::ots_head_should_serialise, ots::ots_head_free, true },
|
||||
{ Tag("OS/2"), ots::ots_os2_parse, ots::ots_os2_serialise,
|
||||
ots::ots_os2_should_serialise, ots::ots_os2_free, true },
|
||||
{ Tag("cmap"), ots::ots_cmap_parse, ots::ots_cmap_serialise,
|
||||
ots::ots_cmap_should_serialise, ots::ots_cmap_free, true },
|
||||
{ Tag("hhea"), ots::ots_hhea_parse, ots::ots_hhea_serialise,
|
||||
ots::ots_hhea_should_serialise, ots::ots_hhea_free, true },
|
||||
{ Tag("hmtx"), ots::ots_hmtx_parse, ots::ots_hmtx_serialise,
|
||||
ots::ots_hmtx_should_serialise, ots::ots_hmtx_free, true },
|
||||
{ Tag("name"), ots::ots_name_parse, ots::ots_name_serialise,
|
||||
ots::ots_name_should_serialise, ots::ots_name_free, true },
|
||||
{ Tag("post"), ots::ots_post_parse, ots::ots_post_serialise,
|
||||
ots::ots_post_should_serialise, ots::ots_post_free, true },
|
||||
{ Tag("loca"), ots::ots_loca_parse, ots::ots_loca_serialise,
|
||||
ots::ots_loca_should_serialise, ots::ots_loca_free, false },
|
||||
{ Tag("glyf"), ots::ots_glyf_parse, ots::ots_glyf_serialise,
|
||||
ots::ots_glyf_should_serialise, ots::ots_glyf_free, false },
|
||||
{ Tag("CFF "), ots::ots_cff_parse, ots::ots_cff_serialise,
|
||||
ots::ots_cff_should_serialise, ots::ots_cff_free, false },
|
||||
{ Tag("VDMX"), ots::ots_vdmx_parse, ots::ots_vdmx_serialise,
|
||||
ots::ots_vdmx_should_serialise, ots::ots_vdmx_free, false },
|
||||
{ Tag("hdmx"), ots::ots_hdmx_parse, ots::ots_hdmx_serialise,
|
||||
ots::ots_hdmx_should_serialise, ots::ots_hdmx_free, false },
|
||||
{ Tag("gasp"), ots::ots_gasp_parse, ots::ots_gasp_serialise,
|
||||
ots::ots_gasp_should_serialise, ots::ots_gasp_free, false },
|
||||
{ Tag("cvt "), ots::ots_cvt_parse, ots::ots_cvt_serialise,
|
||||
ots::ots_cvt_should_serialise, ots::ots_cvt_free, false },
|
||||
{ Tag("fpgm"), ots::ots_fpgm_parse, ots::ots_fpgm_serialise,
|
||||
ots::ots_fpgm_should_serialise, ots::ots_fpgm_free, false },
|
||||
{ Tag("prep"), ots::ots_prep_parse, ots::ots_prep_serialise,
|
||||
ots::ots_prep_should_serialise, ots::ots_prep_free, false },
|
||||
{ Tag("LTSH"), ots::ots_ltsh_parse, ots::ots_ltsh_serialise,
|
||||
ots::ots_ltsh_should_serialise, ots::ots_ltsh_free, false },
|
||||
{ Tag("VORG"), ots::ots_vorg_parse, ots::ots_vorg_serialise,
|
||||
ots::ots_vorg_should_serialise, ots::ots_vorg_free, false },
|
||||
{ Tag("kern"), ots::ots_kern_parse, ots::ots_kern_serialise,
|
||||
ots::ots_kern_should_serialise, ots::ots_kern_free, false },
|
||||
// TODO(yusukes): Support GDEF, GPOS, GSUB, mort, base, and jstf tables.
|
||||
{ 0, NULL, NULL, NULL, NULL, false },
|
||||
};
|
||||
|
||||
bool IsValidVersionTag(uint32_t tag) {
|
||||
return tag == Tag("\x00\x01\x00\x00") ||
|
||||
// OpenType fonts with CFF data have 'OTTO' tag.
|
||||
tag == Tag("OTTO") ||
|
||||
// Older Mac fonts might have 'true' or 'typ1' tag.
|
||||
tag == Tag("true") ||
|
||||
tag == Tag("typ1");
|
||||
}
|
||||
|
||||
bool ProcessGeneric(ots::OpenTypeFile *header, ots::OTSStream *output,
|
||||
const uint8_t *data, size_t length,
|
||||
const std::vector<OpenTypeTable>& tables,
|
||||
ots::Buffer& file);
|
||||
|
||||
bool ProcessTTF(ots::OpenTypeFile *header,
|
||||
ots::OTSStream *output, const uint8_t *data, size_t length) {
|
||||
ots::Buffer file(data, length);
|
||||
|
||||
// we disallow all files > 1GB in size for sanity.
|
||||
if (length > 1024 * 1024 * 1024) {
|
||||
return OTS_FAILURE();
|
||||
}
|
||||
|
||||
if (!file.ReadTag(&header->version)) {
|
||||
return OTS_FAILURE();
|
||||
}
|
||||
if (!IsValidVersionTag(header->version)) {
|
||||
return OTS_FAILURE();
|
||||
}
|
||||
|
||||
if (!file.ReadU16(&header->num_tables) ||
|
||||
!file.ReadU16(&header->search_range) ||
|
||||
!file.ReadU16(&header->entry_selector) ||
|
||||
!file.ReadU16(&header->range_shift)) {
|
||||
return OTS_FAILURE();
|
||||
}
|
||||
|
||||
// search_range is (Maximum power of 2 <= numTables) x 16. Thus, to avoid
|
||||
// overflow num_tables is, at most, 2^16 / 16 = 2^12
|
||||
if (header->num_tables >= 4096 || header->num_tables < 1) {
|
||||
return OTS_FAILURE();
|
||||
}
|
||||
|
||||
unsigned max_pow2 = 0;
|
||||
while (1u << (max_pow2 + 1) <= header->num_tables) {
|
||||
max_pow2++;
|
||||
}
|
||||
const uint16_t expected_search_range = (1u << max_pow2) << 4;
|
||||
|
||||
// Don't call ots_failure() here since ~25% of fonts (250+ fonts) in
|
||||
// http://www.princexml.com/fonts/ have unexpected search_range value.
|
||||
if (header->search_range != expected_search_range) {
|
||||
OTS_WARNING("bad search range");
|
||||
header->search_range = expected_search_range; // Fix the value.
|
||||
}
|
||||
|
||||
// entry_selector is Log2(maximum power of 2 <= numTables)
|
||||
if (header->entry_selector != max_pow2) {
|
||||
return OTS_FAILURE();
|
||||
}
|
||||
|
||||
// range_shift is NumTables x 16-searchRange. We know that 16*num_tables
|
||||
// doesn't over flow because we range checked it above. Also, we know that
|
||||
// it's > header->search_range by construction of search_range.
|
||||
const uint32_t expected_range_shift
|
||||
= 16 * header->num_tables - header->search_range;
|
||||
if (header->range_shift != expected_range_shift) {
|
||||
OTS_WARNING("bad range shift");
|
||||
header->range_shift = expected_range_shift; // the same as above.
|
||||
}
|
||||
|
||||
// Next up is the list of tables.
|
||||
std::vector<OpenTypeTable> tables;
|
||||
|
||||
for (unsigned i = 0; i < header->num_tables; ++i) {
|
||||
OpenTypeTable table;
|
||||
if (!file.ReadTag(&table.tag) ||
|
||||
!file.ReadU32(&table.chksum) ||
|
||||
!file.ReadU32(&table.offset) ||
|
||||
!file.ReadU32(&table.length)) {
|
||||
return OTS_FAILURE();
|
||||
}
|
||||
|
||||
table.uncompressed_length = table.length;
|
||||
tables.push_back(table);
|
||||
}
|
||||
|
||||
return ProcessGeneric(header, output, data, length, tables, file);
|
||||
}
|
||||
|
||||
bool ProcessWOFF(ots::OpenTypeFile *header,
|
||||
ots::OTSStream *output, const uint8_t *data, size_t length) {
|
||||
ots::Buffer file(data, length);
|
||||
|
||||
// we disallow all files > 1GB in size for sanity.
|
||||
if (length > 1024 * 1024 * 1024) {
|
||||
return OTS_FAILURE();
|
||||
}
|
||||
|
||||
uint32_t woff_tag;
|
||||
if (!file.ReadTag(&woff_tag)) {
|
||||
return OTS_FAILURE();
|
||||
}
|
||||
|
||||
if (woff_tag != Tag("wOFF")) {
|
||||
return OTS_FAILURE();
|
||||
}
|
||||
|
||||
if (!file.ReadTag(&header->version)) {
|
||||
return OTS_FAILURE();
|
||||
}
|
||||
if (!IsValidVersionTag(header->version)) {
|
||||
return OTS_FAILURE();
|
||||
}
|
||||
|
||||
header->search_range = 0;
|
||||
header->entry_selector = 0;
|
||||
header->range_shift = 0;
|
||||
|
||||
uint32_t reported_length;
|
||||
if (!file.ReadU32(&reported_length) || length != reported_length) {
|
||||
return OTS_FAILURE();
|
||||
}
|
||||
|
||||
if (!file.ReadU16(&header->num_tables)) {
|
||||
return OTS_FAILURE();
|
||||
}
|
||||
|
||||
uint16_t reserved_value;
|
||||
if (!file.ReadU16(&reserved_value) || reserved_value) {
|
||||
return OTS_FAILURE();
|
||||
}
|
||||
|
||||
// We don't care about these fields of the header:
|
||||
// uint32_t uncompressed_size;
|
||||
// uint16_t major_version, minor_version
|
||||
// uint32_t meta_offset, meta_length, meta_length_orig
|
||||
// uint32_t priv_offset, priv_length
|
||||
if (!file.Skip(6 * 4 + 2 * 2)) {
|
||||
return OTS_FAILURE();
|
||||
}
|
||||
|
||||
// Next up is the list of tables.
|
||||
std::vector<OpenTypeTable> tables;
|
||||
|
||||
for (unsigned i = 0; i < header->num_tables; ++i) {
|
||||
OpenTypeTable table;
|
||||
if (!file.ReadTag(&table.tag) ||
|
||||
!file.ReadU32(&table.offset) ||
|
||||
!file.ReadU32(&table.length) ||
|
||||
!file.ReadU32(&table.uncompressed_length) ||
|
||||
!file.ReadU32(&table.chksum)) {
|
||||
return OTS_FAILURE();
|
||||
}
|
||||
|
||||
tables.push_back(table);
|
||||
}
|
||||
|
||||
return ProcessGeneric(header, output, data, length, tables, file);
|
||||
}
|
||||
|
||||
bool ProcessGeneric(ots::OpenTypeFile *header, ots::OTSStream *output,
|
||||
const uint8_t *data, size_t length,
|
||||
const std::vector<OpenTypeTable>& tables,
|
||||
ots::Buffer& file) {
|
||||
const size_t data_offset = file.offset();
|
||||
|
||||
uint32_t uncompressed_sum = 0;
|
||||
|
||||
for (unsigned i = 0; i < header->num_tables; ++i) {
|
||||
// the tables must be sorted by tag (when taken as big-endian numbers).
|
||||
// This also remove the possibility of duplicate tables.
|
||||
if (i) {
|
||||
const uint32_t this_tag = ntohl(tables[i].tag);
|
||||
const uint32_t prev_tag = ntohl(tables[i - 1].tag);
|
||||
if (this_tag <= prev_tag) {
|
||||
return OTS_FAILURE();
|
||||
}
|
||||
}
|
||||
|
||||
// all tag names must be built from printable ASCII characters
|
||||
if (!CheckTag(tables[i].tag)) {
|
||||
return OTS_FAILURE();
|
||||
}
|
||||
|
||||
// tables must be 4-byte aligned
|
||||
if (tables[i].offset & 3) {
|
||||
return OTS_FAILURE();
|
||||
}
|
||||
|
||||
// and must be within the file
|
||||
if (tables[i].offset < data_offset || tables[i].offset >= length) {
|
||||
return OTS_FAILURE();
|
||||
}
|
||||
// disallow all tables with a zero length
|
||||
if (tables[i].length < 1) {
|
||||
// Note: malayalam.ttf has zero length CVT table...
|
||||
return OTS_FAILURE();
|
||||
}
|
||||
// disallow all tables with a length > 1GB
|
||||
if (tables[i].length > 1024 * 1024 * 1024) {
|
||||
return OTS_FAILURE();
|
||||
}
|
||||
// disallow tables where the uncompressed size is < the compressed size.
|
||||
if (tables[i].uncompressed_length < tables[i].length) {
|
||||
return OTS_FAILURE();
|
||||
}
|
||||
if (tables[i].uncompressed_length > tables[i].length) {
|
||||
// We'll probably be decompressing this table.
|
||||
|
||||
// disallow all tables which uncompress to > 30 MB
|
||||
if (tables[i].uncompressed_length > 30 * 1024 * 1024) {
|
||||
return OTS_FAILURE();
|
||||
}
|
||||
if (uncompressed_sum + tables[i].uncompressed_length < uncompressed_sum) {
|
||||
return OTS_FAILURE();
|
||||
}
|
||||
|
||||
uncompressed_sum += tables[i].uncompressed_length;
|
||||
}
|
||||
// since we required that the file be < 1GB in length, and that the table
|
||||
// length is < 1GB, the following addtion doesn't overflow
|
||||
const uint32_t end_byte = Round4(tables[i].offset + tables[i].length);
|
||||
if (!end_byte || end_byte > length) {
|
||||
return OTS_FAILURE();
|
||||
}
|
||||
}
|
||||
|
||||
// All decompressed tables uncompressed must be <= 30MB.
|
||||
if (uncompressed_sum > 30 * 1024 * 1024) {
|
||||
return OTS_FAILURE();
|
||||
}
|
||||
|
||||
std::map<uint32_t, OpenTypeTable> table_map;
|
||||
for (unsigned i = 0; i < header->num_tables; ++i) {
|
||||
table_map[tables[i].tag] = tables[i];
|
||||
}
|
||||
|
||||
// check that the tables are not overlapping.
|
||||
std::vector<std::pair<uint32_t, uint8_t> > overlap_checker;
|
||||
for (unsigned i = 0; i < header->num_tables; ++i) {
|
||||
overlap_checker.push_back(
|
||||
std::make_pair(tables[i].offset, 1 /* start */));
|
||||
overlap_checker.push_back(
|
||||
std::make_pair(tables[i].offset + tables[i].length, 0 /* end */));
|
||||
}
|
||||
std::sort(overlap_checker.begin(), overlap_checker.end());
|
||||
int overlap_count = 0;
|
||||
for (unsigned i = 0; i < overlap_checker.size(); ++i) {
|
||||
overlap_count += (overlap_checker[i].second ? 1 : -1);
|
||||
if (overlap_count > 1) {
|
||||
return OTS_FAILURE();
|
||||
}
|
||||
}
|
||||
|
||||
Arena arena;
|
||||
|
||||
for (unsigned i = 0; ; ++i) {
|
||||
if (table_parsers[i].parse == NULL) break;
|
||||
|
||||
const std::map<uint32_t, OpenTypeTable>::const_iterator it
|
||||
= table_map.find(table_parsers[i].tag);
|
||||
|
||||
if (it == table_map.end()) {
|
||||
if (table_parsers[i].required) {
|
||||
return OTS_FAILURE();
|
||||
}
|
||||
continue;
|
||||
}
|
||||
|
||||
const uint8_t* table_data;
|
||||
size_t table_length;
|
||||
|
||||
if (it->second.uncompressed_length != it->second.length) {
|
||||
// compressed table. Need to uncompress into memory first.
|
||||
table_length = it->second.uncompressed_length;
|
||||
table_data = arena.Allocate(table_length);
|
||||
uLongf dest_len = table_length;
|
||||
int r = uncompress((Bytef*) table_data, &dest_len,
|
||||
data + it->second.offset, it->second.length);
|
||||
if (r != Z_OK || dest_len != table_length) {
|
||||
return OTS_FAILURE();
|
||||
}
|
||||
} else {
|
||||
// uncompressed table. We can process directly from memory.
|
||||
table_data = data + it->second.offset;
|
||||
table_length = it->second.length;
|
||||
}
|
||||
|
||||
if (!table_parsers[i].parse(header, table_data, table_length)) {
|
||||
return OTS_FAILURE();
|
||||
}
|
||||
}
|
||||
|
||||
if (header->cff) {
|
||||
// font with PostScript glyph
|
||||
if (header->version != Tag("OTTO")) {
|
||||
return OTS_FAILURE();
|
||||
}
|
||||
if (header->glyf || header->loca) {
|
||||
// mixing outline formats is not recommended
|
||||
return OTS_FAILURE();
|
||||
}
|
||||
} else {
|
||||
if (!header->glyf || !header->loca) {
|
||||
// No TrueType glyph found.
|
||||
// Note: bitmap-only fonts are not supported.
|
||||
return OTS_FAILURE();
|
||||
}
|
||||
}
|
||||
|
||||
unsigned num_output_tables = 0;
|
||||
for (unsigned i = 0; ; ++i) {
|
||||
if (table_parsers[i].parse == NULL) {
|
||||
break;
|
||||
}
|
||||
|
||||
if (table_parsers[i].should_serialise(header)) {
|
||||
num_output_tables++;
|
||||
}
|
||||
}
|
||||
|
||||
unsigned max_pow2 = 0;
|
||||
while (1u << (max_pow2 + 1) <= num_output_tables) {
|
||||
max_pow2++;
|
||||
}
|
||||
const uint16_t output_search_range = (1u << max_pow2) << 4;
|
||||
|
||||
output->ResetChecksum();
|
||||
if (!output->WriteTag(header->version) ||
|
||||
!output->WriteU16(num_output_tables) ||
|
||||
!output->WriteU16(output_search_range) ||
|
||||
!output->WriteU16(max_pow2) ||
|
||||
!output->WriteU16((num_output_tables << 4) - output_search_range)) {
|
||||
return OTS_FAILURE();
|
||||
}
|
||||
const uint32_t offset_table_chksum = output->chksum();
|
||||
|
||||
const size_t table_record_offset = output->Tell();
|
||||
if (!output->Pad(16 * num_output_tables)) {
|
||||
return OTS_FAILURE();
|
||||
}
|
||||
|
||||
std::vector<OutputTable> out_tables;
|
||||
|
||||
size_t head_table_offset = 0;
|
||||
for (unsigned i = 0; ; ++i) {
|
||||
if (table_parsers[i].parse == NULL) {
|
||||
break;
|
||||
}
|
||||
|
||||
if (!table_parsers[i].should_serialise(header)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
OutputTable out;
|
||||
out.tag = table_parsers[i].tag;
|
||||
out.offset = output->Tell();
|
||||
|
||||
output->ResetChecksum();
|
||||
if (table_parsers[i].tag == Tag("head")) {
|
||||
head_table_offset = out.offset;
|
||||
}
|
||||
if (!table_parsers[i].serialise(output, header)) {
|
||||
return OTS_FAILURE();
|
||||
}
|
||||
|
||||
const size_t end_offset = output->Tell();
|
||||
if (end_offset <= out.offset) {
|
||||
// paranoid check. |end_offset| is supposed to be greater than the offset,
|
||||
// as long as the Tell() interface is implemented correctly.
|
||||
return OTS_FAILURE();
|
||||
}
|
||||
out.length = end_offset - out.offset;
|
||||
|
||||
// align tables to four bytes
|
||||
if (!output->Pad((4 - (end_offset & 3)) % 4)) {
|
||||
return OTS_FAILURE();
|
||||
}
|
||||
out.chksum = output->chksum();
|
||||
out_tables.push_back(out);
|
||||
}
|
||||
|
||||
const size_t end_of_file = output->Tell();
|
||||
|
||||
// Need to sort the output tables for inclusion in the file
|
||||
std::sort(out_tables.begin(), out_tables.end(), OutputTable::SortByTag);
|
||||
if (!output->Seek(table_record_offset)) {
|
||||
return OTS_FAILURE();
|
||||
}
|
||||
|
||||
output->ResetChecksum();
|
||||
uint32_t tables_chksum = 0;
|
||||
for (unsigned i = 0; i < out_tables.size(); ++i) {
|
||||
if (!output->WriteTag(out_tables[i].tag) ||
|
||||
!output->WriteU32(out_tables[i].chksum) ||
|
||||
!output->WriteU32(out_tables[i].offset) ||
|
||||
!output->WriteU32(out_tables[i].length)) {
|
||||
return OTS_FAILURE();
|
||||
}
|
||||
tables_chksum += out_tables[i].chksum;
|
||||
}
|
||||
const uint32_t table_record_chksum = output->chksum();
|
||||
|
||||
// http://www.microsoft.com/typography/otspec/otff.htm
|
||||
const uint32_t file_chksum
|
||||
= offset_table_chksum + tables_chksum + table_record_chksum;
|
||||
const uint32_t chksum_magic = static_cast<uint32_t>(0xb1b0afba) - file_chksum;
|
||||
|
||||
// seek into the 'head' table and write in the checksum magic value
|
||||
if (!head_table_offset) {
|
||||
return OTS_FAILURE(); // not reached.
|
||||
}
|
||||
if (!output->Seek(head_table_offset + 8)) {
|
||||
return OTS_FAILURE();
|
||||
}
|
||||
if (!output->WriteU32(chksum_magic)) {
|
||||
return OTS_FAILURE();
|
||||
}
|
||||
|
||||
if (!output->Seek(end_of_file)) {
|
||||
return OTS_FAILURE();
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
namespace ots {
|
||||
|
||||
void DisableDebugOutput() {
|
||||
g_debug_output = false;
|
||||
}
|
||||
|
||||
bool Process(OTSStream *output, const uint8_t *data, size_t length) {
|
||||
OpenTypeFile header;
|
||||
if (length < 4) {
|
||||
return OTS_FAILURE();
|
||||
}
|
||||
|
||||
bool result;
|
||||
if (data[0] == 'w' && data[1] == 'O' && data[2] == 'F' && data[3] == 'F') {
|
||||
result = ProcessWOFF(&header, output, data, length);
|
||||
} else {
|
||||
result = ProcessTTF(&header, output, data, length);
|
||||
}
|
||||
|
||||
for (unsigned i = 0; ; ++i) {
|
||||
if (table_parsers[i].parse == NULL) break;
|
||||
table_parsers[i].free(&header);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
#if !defined(_MSC_VER) && defined(OTS_DEBUG)
|
||||
bool Failure(const char *f, int l, const char *fn) {
|
||||
if (g_debug_output) {
|
||||
std::fprintf(stderr, "ERROR at %s:%d (%s)\n", f, l, fn);
|
||||
std::fflush(stderr);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
void Warning(const char *f, int l, const char *format, ...) {
|
||||
if (g_debug_output) {
|
||||
std::fprintf(stderr, "WARNING at %s:%d: ", f, l);
|
||||
std::va_list va;
|
||||
va_start(va, format);
|
||||
std::vfprintf(stderr, format, va);
|
||||
va_end(va);
|
||||
std::fprintf(stderr, "\n");
|
||||
std::fflush(stderr);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
} // namespace ots
|
|
@ -0,0 +1,194 @@
|
|||
// Copyright (c) 2009 The Chromium Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style license that can be
|
||||
// found in the LICENSE file.
|
||||
|
||||
#ifndef OTS_H_
|
||||
#define OTS_H_
|
||||
|
||||
#include <cstdarg>
|
||||
#include <cstdio>
|
||||
#include <cstdlib>
|
||||
#include <cstring>
|
||||
|
||||
#include "opentype-sanitiser.h"
|
||||
|
||||
namespace ots {
|
||||
|
||||
#if defined(_MSC_VER) || !defined(OTS_DEBUG)
|
||||
#define OTS_FAILURE() false
|
||||
#else
|
||||
#define OTS_FAILURE() ots::Failure(__FILE__, __LINE__, __PRETTY_FUNCTION__)
|
||||
bool Failure(const char *f, int l, const char *fn);
|
||||
#endif
|
||||
|
||||
#if defined(_MSC_VER)
|
||||
// MSVC supports C99 style variadic macros.
|
||||
#define OTS_WARNING(format, ...)
|
||||
#else
|
||||
// GCC
|
||||
#if defined(OTS_DEBUG)
|
||||
#define OTS_WARNING(format, args...) \
|
||||
ots::Warning(__FILE__, __LINE__, format, ##args)
|
||||
void Warning(const char *f, int l, const char *format, ...)
|
||||
__attribute__((format(printf, 3, 4)));
|
||||
#else
|
||||
#define OTS_WARNING(format, args...)
|
||||
#endif
|
||||
#endif
|
||||
|
||||
// Define OTS_NO_TRANSCODE_HINTS (i.e., g++ -DOTS_NO_TRANSCODE_HINTS) if you
|
||||
// want to omit TrueType hinting instructions and variables in glyf, fpgm, prep,
|
||||
// and cvt tables.
|
||||
#if defined(OTS_NO_TRANSCODE_HINTS)
|
||||
const bool g_transcode_hints = false;
|
||||
#else
|
||||
const bool g_transcode_hints = true;
|
||||
#endif
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
// Buffer helper class
|
||||
//
|
||||
// This class perform some trival buffer operations while checking for
|
||||
// out-of-bounds errors. As a family they return false if anything is amiss,
|
||||
// updating the current offset otherwise.
|
||||
// -----------------------------------------------------------------------------
|
||||
class Buffer {
|
||||
public:
|
||||
Buffer(const uint8_t *buffer, size_t len)
|
||||
: buffer_(buffer),
|
||||
length_(len),
|
||||
offset_(0) { }
|
||||
|
||||
bool Skip(size_t n_bytes) {
|
||||
return Read(NULL, n_bytes);
|
||||
}
|
||||
|
||||
bool Read(uint8_t *buffer, size_t n_bytes) {
|
||||
if (n_bytes > 1024 * 1024 * 1024) {
|
||||
return OTS_FAILURE();
|
||||
}
|
||||
if ((offset_ + n_bytes > length_) ||
|
||||
(offset_ > length_ - n_bytes)) {
|
||||
return OTS_FAILURE();
|
||||
}
|
||||
if (buffer) {
|
||||
std::memcpy(buffer, buffer_ + offset_, n_bytes);
|
||||
}
|
||||
offset_ += n_bytes;
|
||||
return true;
|
||||
}
|
||||
|
||||
inline bool ReadU8(uint8_t *value) {
|
||||
if (offset_ + 1 > length_) {
|
||||
return OTS_FAILURE();
|
||||
}
|
||||
*value = buffer_[offset_];
|
||||
++offset_;
|
||||
return true;
|
||||
}
|
||||
|
||||
bool ReadU16(uint16_t *value) {
|
||||
if (offset_ + 2 > length_) {
|
||||
return OTS_FAILURE();
|
||||
}
|
||||
std::memcpy(value, buffer_ + offset_, sizeof(uint16_t));
|
||||
*value = ntohs(*value);
|
||||
offset_ += 2;
|
||||
return true;
|
||||
}
|
||||
|
||||
bool ReadS16(int16_t *value) {
|
||||
return ReadU16(reinterpret_cast<uint16_t*>(value));
|
||||
}
|
||||
|
||||
bool ReadU32(uint32_t *value) {
|
||||
if (offset_ + 4 > length_) {
|
||||
return OTS_FAILURE();
|
||||
}
|
||||
std::memcpy(value, buffer_ + offset_, sizeof(uint32_t));
|
||||
*value = ntohl(*value);
|
||||
offset_ += 4;
|
||||
return true;
|
||||
}
|
||||
|
||||
bool ReadS32(int32_t *value) {
|
||||
return ReadU32(reinterpret_cast<uint32_t*>(value));
|
||||
}
|
||||
|
||||
bool ReadTag(uint32_t *value) {
|
||||
if (offset_ + 4 > length_) {
|
||||
return OTS_FAILURE();
|
||||
}
|
||||
std::memcpy(value, buffer_ + offset_, sizeof(uint32_t));
|
||||
offset_ += 4;
|
||||
return true;
|
||||
}
|
||||
|
||||
bool ReadR64(uint64_t *value) {
|
||||
if (offset_ + 8 > length_) {
|
||||
return OTS_FAILURE();
|
||||
}
|
||||
std::memcpy(value, buffer_ + offset_, sizeof(uint64_t));
|
||||
offset_ += 8;
|
||||
return true;
|
||||
}
|
||||
|
||||
const uint8_t *buffer() const { return buffer_; }
|
||||
size_t offset() const { return offset_; }
|
||||
size_t length() const { return length_; }
|
||||
|
||||
void set_offset(size_t newoffset) { offset_ = newoffset; }
|
||||
|
||||
private:
|
||||
const uint8_t * const buffer_;
|
||||
const size_t length_;
|
||||
size_t offset_;
|
||||
};
|
||||
|
||||
#define FOR_EACH_TABLE_TYPE \
|
||||
F(cff, CFF) \
|
||||
F(cmap, CMAP) \
|
||||
F(cvt, CVT) \
|
||||
F(fpgm, FPGM) \
|
||||
F(gasp, GASP) \
|
||||
F(glyf, GLYF) \
|
||||
F(hdmx, HDMX) \
|
||||
F(head, HEAD) \
|
||||
F(hhea, HHEA) \
|
||||
F(hmtx, HMTX) \
|
||||
F(kern, KERN) \
|
||||
F(loca, LOCA) \
|
||||
F(ltsh, LTSH) \
|
||||
F(maxp, MAXP) \
|
||||
F(name, NAME) \
|
||||
F(os2, OS2) \
|
||||
F(post, POST) \
|
||||
F(prep, PREP) \
|
||||
F(vdmx, VDMX) \
|
||||
F(vorg, VORG)
|
||||
|
||||
#define F(name, capname) struct OpenType##capname;
|
||||
FOR_EACH_TABLE_TYPE
|
||||
#undef F
|
||||
|
||||
struct OpenTypeFile {
|
||||
OpenTypeFile() {
|
||||
#define F(name, capname) name = NULL;
|
||||
FOR_EACH_TABLE_TYPE
|
||||
#undef F
|
||||
}
|
||||
|
||||
uint32_t version;
|
||||
uint16_t num_tables;
|
||||
uint16_t search_range;
|
||||
uint16_t entry_selector;
|
||||
uint16_t range_shift;
|
||||
|
||||
#define F(name, capname) OpenType##capname *name;
|
||||
FOR_EACH_TABLE_TYPE
|
||||
#undef F
|
||||
};
|
||||
|
||||
} // namespace ots
|
||||
|
||||
#endif // OTS_H_
|
|
@ -0,0 +1,181 @@
|
|||
// Copyright (c) 2009 The Chromium 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 "post.h"
|
||||
|
||||
#include "maxp.h"
|
||||
|
||||
// post - PostScript
|
||||
// http://www.microsoft.com/opentype/otspec/post.htm
|
||||
|
||||
namespace ots {
|
||||
|
||||
bool ots_post_parse(OpenTypeFile *file, const uint8_t *data, size_t length) {
|
||||
Buffer table(data, length);
|
||||
|
||||
OpenTypePOST *post = new OpenTypePOST;
|
||||
file->post = post;
|
||||
|
||||
if (!table.ReadU32(&post->version) ||
|
||||
!table.ReadU32(&post->italic_angle) ||
|
||||
!table.ReadS16(&post->underline) ||
|
||||
!table.ReadS16(&post->underline_thickness) ||
|
||||
!table.ReadU32(&post->is_fixed_pitch)) {
|
||||
return OTS_FAILURE();
|
||||
}
|
||||
|
||||
if (post->underline_thickness < 0) {
|
||||
post->underline_thickness = 1;
|
||||
}
|
||||
|
||||
if (post->version == 0x00010000) {
|
||||
return true;
|
||||
} else if (post->version == 0x00030000) {
|
||||
return true;
|
||||
} else if (post->version != 0x00020000) {
|
||||
// 0x00025000 is deprecated. We don't accept it.
|
||||
return OTS_FAILURE();
|
||||
}
|
||||
|
||||
// We have a version 2 table with a list of Pascal strings at the end
|
||||
|
||||
// We don't care about the memory usage fields. We'll set all these to zero
|
||||
// when serialising
|
||||
if (!table.Skip(16)) {
|
||||
return OTS_FAILURE();
|
||||
}
|
||||
|
||||
uint16_t num_glyphs = 0;
|
||||
if (!table.ReadU16(&num_glyphs)) {
|
||||
return OTS_FAILURE();
|
||||
}
|
||||
|
||||
if (!file->maxp) {
|
||||
return OTS_FAILURE();
|
||||
}
|
||||
|
||||
if (num_glyphs == 0) {
|
||||
if (file->maxp->num_glyphs > 258) {
|
||||
return OTS_FAILURE();
|
||||
}
|
||||
OTS_WARNING("table version is 1, but no glyf names are found");
|
||||
// workaround for fonts in http://www.fontsquirrel.com/fontface
|
||||
// (e.g., yataghan.ttf).
|
||||
post->version = 0x00010000;
|
||||
return true;
|
||||
}
|
||||
|
||||
if (num_glyphs != file->maxp->num_glyphs) {
|
||||
// Note: Fixedsys500c.ttf seems to have inconsistent num_glyphs values.
|
||||
return OTS_FAILURE();
|
||||
}
|
||||
|
||||
post->glyph_name_index.resize(num_glyphs);
|
||||
for (unsigned i = 0; i < num_glyphs; ++i) {
|
||||
if (!table.ReadU16(&post->glyph_name_index[i])) {
|
||||
return OTS_FAILURE();
|
||||
}
|
||||
if (post->glyph_name_index[i] >= 32768) {
|
||||
// Note: droid_arialuni.ttf fails this test.
|
||||
return OTS_FAILURE(); // reserved area.
|
||||
}
|
||||
}
|
||||
|
||||
// Now we have an array of Pascal strings. We have to check that they are all
|
||||
// valid and read them in.
|
||||
const size_t strings_offset = table.offset();
|
||||
const uint8_t *strings = data + strings_offset;
|
||||
const uint8_t *strings_end = data + length;
|
||||
|
||||
for (;;) {
|
||||
if (strings == strings_end) break;
|
||||
const unsigned string_length = *strings;
|
||||
if (strings + 1 + string_length > strings_end) {
|
||||
return OTS_FAILURE();
|
||||
}
|
||||
if (std::memchr(strings + 1, '\0', string_length)) {
|
||||
return OTS_FAILURE();
|
||||
}
|
||||
post->names.push_back(
|
||||
std::string(reinterpret_cast<const char*>(strings + 1), string_length));
|
||||
strings += 1 + string_length;
|
||||
}
|
||||
const unsigned num_strings = post->names.size();
|
||||
|
||||
// check that all the references are within bounds
|
||||
for (unsigned i = 0; i < num_glyphs; ++i) {
|
||||
unsigned offset = post->glyph_name_index[i];
|
||||
if (offset < 258) {
|
||||
continue;
|
||||
}
|
||||
|
||||
offset -= 258;
|
||||
if (offset >= num_strings) {
|
||||
return OTS_FAILURE();
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool ots_post_should_serialise(OpenTypeFile *file) {
|
||||
return file->post;
|
||||
}
|
||||
|
||||
bool ots_post_serialise(OTSStream *out, OpenTypeFile *file) {
|
||||
const OpenTypePOST *post = file->post;
|
||||
|
||||
// OpenType with CFF glyphs must have v3 post table.
|
||||
if (file->post && file->cff && file->post->version != 0x00030000) {
|
||||
return OTS_FAILURE();
|
||||
}
|
||||
|
||||
if (!out->WriteU32(post->version) ||
|
||||
!out->WriteU32(post->italic_angle) ||
|
||||
!out->WriteS16(post->underline) ||
|
||||
!out->WriteS16(post->underline_thickness) ||
|
||||
!out->WriteU32(post->is_fixed_pitch) ||
|
||||
!out->WriteU32(0) ||
|
||||
!out->WriteU32(0) ||
|
||||
!out->WriteU32(0) ||
|
||||
!out->WriteU32(0)) {
|
||||
return OTS_FAILURE();
|
||||
}
|
||||
|
||||
if (post->version != 0x00020000) {
|
||||
return true; // v1.0 and v3.0 does not have glyph names.
|
||||
}
|
||||
|
||||
if (!out->WriteU16(post->glyph_name_index.size())) {
|
||||
return OTS_FAILURE();
|
||||
}
|
||||
|
||||
for (unsigned i = 0; i < post->glyph_name_index.size(); ++i) {
|
||||
if (!out->WriteU16(post->glyph_name_index[i])) {
|
||||
return OTS_FAILURE();
|
||||
}
|
||||
}
|
||||
|
||||
// Now we just have to write out the strings in the correct order
|
||||
for (unsigned i = 0; i < post->names.size(); ++i) {
|
||||
const std::string& s = post->names[i];
|
||||
const uint8_t string_length = s.size();
|
||||
if (!out->Write(&string_length, 1)) {
|
||||
return OTS_FAILURE();
|
||||
}
|
||||
// Some ttf fonts (e.g., frank.ttf on Windows Vista) have zero-length name.
|
||||
// We allow them.
|
||||
if (string_length > 0 && !out->Write(s.data(), string_length)) {
|
||||
return OTS_FAILURE();
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void ots_post_free(OpenTypeFile *file) {
|
||||
delete file->post;
|
||||
}
|
||||
|
||||
} // namespace ots
|
|
@ -0,0 +1,29 @@
|
|||
// Copyright (c) 2009 The Chromium Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style license that can be
|
||||
// found in the LICENSE file.
|
||||
|
||||
#ifndef OTS_POST_H_
|
||||
#define OTS_POST_H_
|
||||
|
||||
#include "ots.h"
|
||||
|
||||
#include <map>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
namespace ots {
|
||||
|
||||
struct OpenTypePOST {
|
||||
uint32_t version;
|
||||
uint32_t italic_angle;
|
||||
int16_t underline;
|
||||
int16_t underline_thickness;
|
||||
uint32_t is_fixed_pitch;
|
||||
|
||||
std::vector<uint16_t> glyph_name_index;
|
||||
std::vector<std::string> names;
|
||||
};
|
||||
|
||||
} // namespace ots
|
||||
|
||||
#endif // OTS_POST_H_
|
|
@ -0,0 +1,50 @@
|
|||
// Copyright (c) 2009 The Chromium 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 "prep.h"
|
||||
|
||||
// prep - Control Value Program
|
||||
// http://www.microsoft.com/opentype/otspec/prep.htm
|
||||
|
||||
namespace ots {
|
||||
|
||||
bool ots_prep_parse(OpenTypeFile *file, const uint8_t *data, size_t length) {
|
||||
Buffer table(data, length);
|
||||
|
||||
OpenTypePREP *prep = new OpenTypePREP;
|
||||
file->prep = prep;
|
||||
|
||||
if (length >= 128 * 1024u) {
|
||||
return OTS_FAILURE(); // almost all prep tables are less than 9k bytes.
|
||||
}
|
||||
|
||||
if (!table.Skip(length)) {
|
||||
return OTS_FAILURE();
|
||||
}
|
||||
|
||||
prep->data = data;
|
||||
prep->length = length;
|
||||
return true;
|
||||
}
|
||||
|
||||
bool ots_prep_should_serialise(OpenTypeFile *file) {
|
||||
if (!file->glyf) return false; // this table is not for CFF fonts.
|
||||
return g_transcode_hints && file->prep;
|
||||
}
|
||||
|
||||
bool ots_prep_serialise(OTSStream *out, OpenTypeFile *file) {
|
||||
const OpenTypePREP *prep = file->prep;
|
||||
|
||||
if (!out->Write(prep->data, prep->length)) {
|
||||
return OTS_FAILURE();
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void ots_prep_free(OpenTypeFile *file) {
|
||||
delete file->prep;
|
||||
}
|
||||
|
||||
} // namespace ots
|
|
@ -0,0 +1,19 @@
|
|||
// Copyright (c) 2009 The Chromium Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style license that can be
|
||||
// found in the LICENSE file.
|
||||
|
||||
#ifndef OTS_PREP_H_
|
||||
#define OTS_PREP_H_
|
||||
|
||||
#include "ots.h"
|
||||
|
||||
namespace ots {
|
||||
|
||||
struct OpenTypePREP {
|
||||
const uint8_t *data;
|
||||
uint32_t length;
|
||||
};
|
||||
|
||||
} // namespace ots
|
||||
|
||||
#endif // OTS_PREP_H_
|
|
@ -0,0 +1,177 @@
|
|||
// Copyright (c) 2009 The Chromium 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 "vdmx.h"
|
||||
|
||||
// VDMX - Vertical Device Metrics
|
||||
// http://www.microsoft.com/opentype/otspec/vdmx.htm
|
||||
|
||||
#define DROP_THIS_TABLE \
|
||||
do { delete file->vdmx; file->vdmx = 0; } while (0)
|
||||
|
||||
namespace ots {
|
||||
|
||||
bool ots_vdmx_parse(OpenTypeFile *file, const uint8_t *data, size_t length) {
|
||||
Buffer table(data, length);
|
||||
file->vdmx = new OpenTypeVDMX;
|
||||
OpenTypeVDMX * const vdmx = file->vdmx;
|
||||
|
||||
if (!table.ReadU16(&vdmx->version) ||
|
||||
!table.ReadU16(&vdmx->num_recs) ||
|
||||
!table.ReadU16(&vdmx->num_ratios)) {
|
||||
return OTS_FAILURE();
|
||||
}
|
||||
|
||||
if (vdmx->version > 1) {
|
||||
OTS_WARNING("bad version: %u", vdmx->version);
|
||||
DROP_THIS_TABLE;
|
||||
return true; // continue transcoding
|
||||
}
|
||||
|
||||
vdmx->rat_ranges.reserve(vdmx->num_ratios);
|
||||
for (unsigned i = 0; i < vdmx->num_ratios; ++i) {
|
||||
OpenTypeVDMXRatioRecord rec;
|
||||
|
||||
if (!table.ReadU8(&rec.charset) ||
|
||||
!table.ReadU8(&rec.x_ratio) ||
|
||||
!table.ReadU8(&rec.y_start_ratio) ||
|
||||
!table.ReadU8(&rec.y_end_ratio)) {
|
||||
return OTS_FAILURE();
|
||||
}
|
||||
|
||||
if (rec.charset > 1) {
|
||||
OTS_WARNING("bad charset: %u", rec.charset);
|
||||
DROP_THIS_TABLE;
|
||||
return true;
|
||||
}
|
||||
|
||||
if (rec.y_start_ratio > rec.y_end_ratio) {
|
||||
OTS_WARNING("bad y ratio");
|
||||
DROP_THIS_TABLE;
|
||||
return true;
|
||||
}
|
||||
|
||||
// All values set to zero signal the default grouping to use;
|
||||
// if present, this must be the last Ratio group in the table.
|
||||
if ((i < vdmx->num_ratios - 1u) &&
|
||||
(rec.x_ratio == 0) &&
|
||||
(rec.y_start_ratio == 0) &&
|
||||
(rec.y_end_ratio == 0)) {
|
||||
// workaround for fonts which have 2 or more {0, 0, 0} terminators.
|
||||
OTS_WARNING("superfluous terminator found");
|
||||
DROP_THIS_TABLE;
|
||||
return true;
|
||||
}
|
||||
|
||||
vdmx->rat_ranges.push_back(rec);
|
||||
}
|
||||
|
||||
vdmx->offsets.reserve(vdmx->num_ratios);
|
||||
const size_t current_offset = table.offset();
|
||||
// current_offset is less than (2 bytes * 3) + (4 bytes * USHRT_MAX) = 256k.
|
||||
for (unsigned i = 0; i < vdmx->num_ratios; ++i) {
|
||||
uint16_t offset;
|
||||
if (!table.ReadU16(&offset)) {
|
||||
return OTS_FAILURE();
|
||||
}
|
||||
if (current_offset + offset >= length) { // thus doesn't overflow.
|
||||
return OTS_FAILURE();
|
||||
}
|
||||
|
||||
vdmx->offsets.push_back(offset);
|
||||
}
|
||||
|
||||
vdmx->groups.reserve(vdmx->num_recs);
|
||||
for (unsigned i = 0; i < vdmx->num_recs; ++i) {
|
||||
OpenTypeVDMXGroup group;
|
||||
if (!table.ReadU16(&group.recs) ||
|
||||
!table.ReadU8(&group.startsz) ||
|
||||
!table.ReadU8(&group.endsz)) {
|
||||
return OTS_FAILURE();
|
||||
}
|
||||
group.entries.reserve(group.recs);
|
||||
for (unsigned j = 0; j < group.recs; ++j) {
|
||||
OpenTypeVDMXVTable vt;
|
||||
if (!table.ReadU16(&vt.y_pel_height) ||
|
||||
!table.ReadS16(&vt.y_max) ||
|
||||
!table.ReadS16(&vt.y_min)) {
|
||||
return OTS_FAILURE();
|
||||
}
|
||||
if (vt.y_max < vt.y_min) {
|
||||
OTS_WARNING("bad y min/max");
|
||||
DROP_THIS_TABLE;
|
||||
return true;
|
||||
}
|
||||
|
||||
// This table must appear in sorted order (sorted by yPelHeight),
|
||||
// but need not be continuous.
|
||||
if ((j != 0) && (group.entries[j - 1].y_pel_height >= vt.y_pel_height)) {
|
||||
OTS_WARNING("the table is not sorted");
|
||||
DROP_THIS_TABLE;
|
||||
return true;
|
||||
}
|
||||
|
||||
group.entries.push_back(vt);
|
||||
}
|
||||
vdmx->groups.push_back(group);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool ots_vdmx_should_serialise(OpenTypeFile *file) {
|
||||
if (!file->glyf) return false; // this table is not for CFF fonts.
|
||||
return file->vdmx;
|
||||
}
|
||||
|
||||
bool ots_vdmx_serialise(OTSStream *out, OpenTypeFile *file) {
|
||||
OpenTypeVDMX * const vdmx = file->vdmx;
|
||||
|
||||
if (!out->WriteU16(vdmx->version) ||
|
||||
!out->WriteU16(vdmx->num_recs) ||
|
||||
!out->WriteU16(vdmx->num_ratios)) {
|
||||
return OTS_FAILURE();
|
||||
}
|
||||
|
||||
for (unsigned i = 0; i < vdmx->rat_ranges.size(); ++i) {
|
||||
const OpenTypeVDMXRatioRecord& rec = vdmx->rat_ranges[i];
|
||||
if (!out->Write(&rec.charset, 1) ||
|
||||
!out->Write(&rec.x_ratio, 1) ||
|
||||
!out->Write(&rec.y_start_ratio, 1) ||
|
||||
!out->Write(&rec.y_end_ratio, 1)) {
|
||||
return OTS_FAILURE();
|
||||
}
|
||||
}
|
||||
|
||||
for (unsigned i = 0; i < vdmx->offsets.size(); ++i) {
|
||||
if (!out->WriteU16(vdmx->offsets[i])) {
|
||||
return OTS_FAILURE();
|
||||
}
|
||||
}
|
||||
|
||||
for (unsigned i = 0; i < vdmx->groups.size(); ++i) {
|
||||
const OpenTypeVDMXGroup& group = vdmx->groups[i];
|
||||
if (!out->WriteU16(group.recs) ||
|
||||
!out->Write(&group.startsz, 1) ||
|
||||
!out->Write(&group.endsz, 1)) {
|
||||
return OTS_FAILURE();
|
||||
}
|
||||
for (unsigned j = 0; j < group.entries.size(); ++j) {
|
||||
const OpenTypeVDMXVTable& vt = group.entries[j];
|
||||
if (!out->WriteU16(vt.y_pel_height) ||
|
||||
!out->WriteS16(vt.y_max) ||
|
||||
!out->WriteS16(vt.y_min)) {
|
||||
return OTS_FAILURE();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void ots_vdmx_free(OpenTypeFile *file) {
|
||||
delete file->vdmx;
|
||||
}
|
||||
|
||||
} // namespace ots
|
|
@ -0,0 +1,45 @@
|
|||
// Copyright (c) 2009 The Chromium Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style license that can be
|
||||
// found in the LICENSE file.
|
||||
|
||||
#ifndef OTS_VDMX_H_
|
||||
#define OTS_VDMX_H_
|
||||
|
||||
#include <vector>
|
||||
|
||||
#include "ots.h"
|
||||
|
||||
namespace ots {
|
||||
|
||||
struct OpenTypeVDMXRatioRecord {
|
||||
uint8_t charset;
|
||||
uint8_t x_ratio;
|
||||
uint8_t y_start_ratio;
|
||||
uint8_t y_end_ratio;
|
||||
};
|
||||
|
||||
struct OpenTypeVDMXVTable {
|
||||
uint16_t y_pel_height;
|
||||
int16_t y_max;
|
||||
int16_t y_min;
|
||||
};
|
||||
|
||||
struct OpenTypeVDMXGroup {
|
||||
uint16_t recs;
|
||||
uint8_t startsz;
|
||||
uint8_t endsz;
|
||||
std::vector<OpenTypeVDMXVTable> entries;
|
||||
};
|
||||
|
||||
struct OpenTypeVDMX {
|
||||
uint16_t version;
|
||||
uint16_t num_recs;
|
||||
uint16_t num_ratios;
|
||||
std::vector<OpenTypeVDMXRatioRecord> rat_ranges;
|
||||
std::vector<uint16_t> offsets;
|
||||
std::vector<OpenTypeVDMXGroup> groups;
|
||||
};
|
||||
|
||||
} // namespace ots
|
||||
|
||||
#endif // OTS_VDMX_H_
|
|
@ -0,0 +1,97 @@
|
|||
// Copyright (c) 2009 The Chromium 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 "vorg.h"
|
||||
|
||||
#include <vector>
|
||||
|
||||
// VORG - Vertical Origin Table
|
||||
// http://www.microsoft.com/opentype/otspec/vorg.htm
|
||||
|
||||
#define DROP_THIS_TABLE \
|
||||
do { delete file->vorg; file->vorg = 0; } while (0)
|
||||
|
||||
namespace ots {
|
||||
|
||||
bool ots_vorg_parse(OpenTypeFile *file, const uint8_t *data, size_t length) {
|
||||
Buffer table(data, length);
|
||||
file->vorg = new OpenTypeVORG;
|
||||
OpenTypeVORG * const vorg = file->vorg;
|
||||
|
||||
uint16_t num_recs;
|
||||
if (!table.ReadU16(&vorg->major_version) ||
|
||||
!table.ReadU16(&vorg->minor_version) ||
|
||||
!table.ReadS16(&vorg->default_vert_origin_y) ||
|
||||
!table.ReadU16(&num_recs)) {
|
||||
return OTS_FAILURE();
|
||||
}
|
||||
if (vorg->major_version != 1) {
|
||||
OTS_WARNING("bad major version: %u", vorg->major_version);
|
||||
DROP_THIS_TABLE;
|
||||
return true;
|
||||
}
|
||||
if (vorg->minor_version != 0) {
|
||||
OTS_WARNING("bad minor version: %u", vorg->minor_version);
|
||||
DROP_THIS_TABLE;
|
||||
return true;
|
||||
}
|
||||
|
||||
// num_recs might be zero (e.g., DFHSMinchoPro5-W3-Demo.otf).
|
||||
if (!num_recs) {
|
||||
return true;
|
||||
}
|
||||
|
||||
uint16_t last_glyph_index = 0;
|
||||
vorg->metrics.reserve(num_recs);
|
||||
for (unsigned i = 0; i < num_recs; ++i) {
|
||||
OpenTypeVORGMetrics rec;
|
||||
|
||||
if (!table.ReadU16(&rec.glyph_index) ||
|
||||
!table.ReadS16(&rec.vert_origin_y)) {
|
||||
return OTS_FAILURE();
|
||||
}
|
||||
if ((i != 0) && (rec.glyph_index <= last_glyph_index)) {
|
||||
OTS_WARNING("the table is not sorted");
|
||||
DROP_THIS_TABLE;
|
||||
return true;
|
||||
}
|
||||
last_glyph_index = rec.glyph_index;
|
||||
|
||||
vorg->metrics.push_back(rec);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool ots_vorg_should_serialise(OpenTypeFile *file) {
|
||||
if (!file->cff) return false; // this table is not for fonts with TT glyphs.
|
||||
return file->vorg;
|
||||
}
|
||||
|
||||
bool ots_vorg_serialise(OTSStream *out, OpenTypeFile *file) {
|
||||
OpenTypeVORG * const vorg = file->vorg;
|
||||
|
||||
if (!out->WriteU16(vorg->major_version) ||
|
||||
!out->WriteU16(vorg->minor_version) ||
|
||||
!out->WriteS16(vorg->default_vert_origin_y) ||
|
||||
!out->WriteU16(vorg->metrics.size())) {
|
||||
return OTS_FAILURE();
|
||||
}
|
||||
|
||||
for (unsigned i = 0; i < vorg->metrics.size(); ++i) {
|
||||
const OpenTypeVORGMetrics& rec = vorg->metrics[i];
|
||||
if (!out->WriteU16(rec.glyph_index) ||
|
||||
!out->WriteS16(rec.vert_origin_y)) {
|
||||
return OTS_FAILURE();
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void ots_vorg_free(OpenTypeFile *file) {
|
||||
delete file->vorg;
|
||||
}
|
||||
|
||||
} // namespace ots
|
|
@ -0,0 +1,28 @@
|
|||
// Copyright (c) 2009 The Chromium Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style license that can be
|
||||
// found in the LICENSE file.
|
||||
|
||||
#ifndef OTS_VORG_H_
|
||||
#define OTS_VORG_H_
|
||||
|
||||
#include <vector>
|
||||
|
||||
#include "ots.h"
|
||||
|
||||
namespace ots {
|
||||
|
||||
struct OpenTypeVORGMetrics {
|
||||
uint16_t glyph_index;
|
||||
int16_t vert_origin_y;
|
||||
};
|
||||
|
||||
struct OpenTypeVORG {
|
||||
uint16_t major_version;
|
||||
uint16_t minor_version;
|
||||
int16_t default_vert_origin_y;
|
||||
std::vector<OpenTypeVORGMetrics> metrics;
|
||||
};
|
||||
|
||||
} // namespace ots
|
||||
|
||||
#endif // OTS_VORG_H_
|
Загрузка…
Ссылка в новой задаче