bug 527276 - part 1 - OTS source code from google (svn r.35). r=roc,cjones a=blocking2.0

This commit is contained in:
Jonathan Kew 2010-10-07 08:59:18 +01:00
Родитель 175f88829c
Коммит f20e7ba703
46 изменённых файлов: 6981 добавлений и 0 удалений

27
gfx/ots/LICENSE Normal file
Просмотреть файл

@ -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_

1003
gfx/ots/src/cff.cc Normal file

Разница между файлами не показана из-за своего большого размера Загрузить разницу

46
gfx/ots/src/cff.h Normal file
Просмотреть файл

@ -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_

832
gfx/ots/src/cmap.cc Normal file
Просмотреть файл

@ -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

45
gfx/ots/src/cmap.h Normal file
Просмотреть файл

@ -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

56
gfx/ots/src/cvt.cc Normal file
Просмотреть файл

@ -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

19
gfx/ots/src/cvt.h Normal file
Просмотреть файл

@ -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_

50
gfx/ots/src/fpgm.cc Normal file
Просмотреть файл

@ -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

19
gfx/ots/src/fpgm.h Normal file
Просмотреть файл

@ -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_

106
gfx/ots/src/gasp.cc Normal file
Просмотреть файл

@ -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

23
gfx/ots/src/gasp.h Normal file
Просмотреть файл

@ -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_

300
gfx/ots/src/glyf.cc Normal file
Просмотреть файл

@ -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

21
gfx/ots/src/glyf.h Normal file
Просмотреть файл

@ -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_

134
gfx/ots/src/hdmx.cc Normal file
Просмотреть файл

@ -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

29
gfx/ots/src/hdmx.h Normal file
Просмотреть файл

@ -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

149
gfx/ots/src/head.cc Normal file
Просмотреть файл

@ -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

29
gfx/ots/src/head.h Normal file
Просмотреть файл

@ -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_

120
gfx/ots/src/hhea.cc Normal file
Просмотреть файл

@ -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

28
gfx/ots/src/hhea.h Normal file
Просмотреть файл

@ -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_

107
gfx/ots/src/hmtx.cc Normal file
Просмотреть файл

@ -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

22
gfx/ots/src/hmtx.h Normal file
Просмотреть файл

@ -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_

196
gfx/ots/src/kern.cc Normal file
Просмотреть файл

@ -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

40
gfx/ots/src/kern.h Normal file
Просмотреть файл

@ -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_

98
gfx/ots/src/loca.cc Normal file
Просмотреть файл

@ -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

20
gfx/ots/src/loca.h Normal file
Просмотреть файл

@ -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_

82
gfx/ots/src/ltsh.cc Normal file
Просмотреть файл

@ -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(&ltsh->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

21
gfx/ots/src/ltsh.h Normal file
Просмотреть файл

@ -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_

128
gfx/ots/src/maxp.cc Normal file
Просмотреть файл

@ -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

35
gfx/ots/src/maxp.h Normal file
Просмотреть файл

@ -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_

131
gfx/ots/src/name.cc Normal file
Просмотреть файл

@ -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

290
gfx/ots/src/os2.cc Normal file
Просмотреть файл

@ -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

54
gfx/ots/src/os2.h Normal file
Просмотреть файл

@ -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_

626
gfx/ots/src/ots.cc Normal file
Просмотреть файл

@ -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

194
gfx/ots/src/ots.h Normal file
Просмотреть файл

@ -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_

181
gfx/ots/src/post.cc Normal file
Просмотреть файл

@ -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

29
gfx/ots/src/post.h Normal file
Просмотреть файл

@ -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_

50
gfx/ots/src/prep.cc Normal file
Просмотреть файл

@ -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

19
gfx/ots/src/prep.h Normal file
Просмотреть файл

@ -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_

177
gfx/ots/src/vdmx.cc Normal file
Просмотреть файл

@ -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

45
gfx/ots/src/vdmx.h Normal file
Просмотреть файл

@ -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_

97
gfx/ots/src/vorg.cc Normal file
Просмотреть файл

@ -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

28
gfx/ots/src/vorg.h Normal file
Просмотреть файл

@ -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_