blob: 5b7974b42c3e7b5d5e24916f39b20848767aa4fb [file] [log] [blame]
// Copyright (c) 2009-2017 The OTS Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#ifndef OTS_H_
#define OTS_H_
#ifdef HAVE_CONFIG_H
#include "config.h"
#endif
#include <stddef.h>
#include <cstdarg>
#include <cstddef>
#if !defined(STARBOARD)
#include <cstring>
#define MEMCPY_OTS std::memcpy
#else
#include "starboard/memory.h"
#define MEMCPY_OTS SbMemoryCopy
#endif
#if defined(STARBOARD)
#include "starboard/common/byte_swap.h"
#define NTOHL_OTS(x) SB_NET_TO_HOST_U32(x)
#define NTOHS_OTS(x) SB_NET_TO_HOST_U16(x)
#elif defined(_WIN32)
#include <stdlib.h>
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;
#define NTOHL_OTS(x) _byteswap_ulong (x)
#define NTOHS_OTS(x) _byteswap_ushort (x)
#else
#include <arpa/inet.h>
#include <stdint.h>
#define NTOHL_OTS(x) ntohl (x)
#define NTOHS_OTS(x) ntohs (x)
#endif
#include <cstdio>
#include <cstdlib>
#include <limits>
#include <map>
#include "opentype-sanitiser.h"
// arraysize borrowed from base/basictypes.h
template <typename T, size_t N>
char (&ArraySizeHelper(T (&array)[N]))[N];
#define arraysize(array) (sizeof(ArraySizeHelper(array)))
namespace ots {
#if defined(STARBOARD) || !defined(OTS_DEBUG)
#define OTS_FAILURE() false
#else
#define OTS_FAILURE() \
(\
std::fprintf(stderr, "ERROR at %s:%d (%s)\n", \
__FILE__, __LINE__, __FUNCTION__) \
&& false\
)
#endif
// All OTS_FAILURE_* macros ultimately evaluate to 'false', just like the original
// message-less OTS_FAILURE(), so that the current parser will return 'false' as
// its result (indicating a failure).
#if defined(STARBOARD) || !defined(OTS_DEBUG)
#define OTS_MESSAGE_(level,otf_,...) \
(otf_)->context->Message(level,__VA_ARGS__)
#else
#define OTS_MESSAGE_(level,otf_,...) \
OTS_FAILURE(), \
(otf_)->context->Message(level,__VA_ARGS__)
#endif
// Generate a simple message
#define OTS_FAILURE_MSG_(otf_,...) \
(OTS_MESSAGE_(0,otf_,__VA_ARGS__), false)
#define OTS_WARNING_MSG_(otf_,...) \
OTS_MESSAGE_(1,otf_,__VA_ARGS__)
// Convenience macros for use in files that only handle a single table tag,
// defined as TABLE_NAME at the top of the file; the 'file' variable is
// expected to be the current FontFile pointer.
#define OTS_FAILURE_MSG(...) OTS_FAILURE_MSG_(font->file, TABLE_NAME ": " __VA_ARGS__)
#define OTS_WARNING(...) OTS_WARNING_MSG_(font->file, TABLE_NAME ": " __VA_ARGS__)
// -----------------------------------------------------------------------------
// 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 *buf, size_t len)
: buffer_(buf),
length_(len),
offset_(0) { }
bool Skip(size_t n_bytes) {
return Read(NULL, n_bytes);
}
bool Read(uint8_t *buf, 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 (buf) {
MEMCPY_OTS(buf, 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();
}
MEMCPY_OTS(value, buffer_ + offset_, sizeof(uint16_t));
*value = NTOHS_OTS(*value);
offset_ += 2;
return true;
}
bool ReadS16(int16_t *value) {
return ReadU16(reinterpret_cast<uint16_t*>(value));
}
bool ReadU24(uint32_t *value) {
if (offset_ + 3 > length_) {
return OTS_FAILURE();
}
*value = static_cast<uint32_t>(buffer_[offset_]) << 16 |
static_cast<uint32_t>(buffer_[offset_ + 1]) << 8 |
static_cast<uint32_t>(buffer_[offset_ + 2]);
offset_ += 3;
return true;
}
bool ReadU32(uint32_t *value) {
if (offset_ + 4 > length_) {
return OTS_FAILURE();
}
MEMCPY_OTS(value, buffer_ + offset_, sizeof(uint32_t));
*value = NTOHL_OTS(*value);
offset_ += 4;
return true;
}
bool ReadS32(int32_t *value) {
return ReadU32(reinterpret_cast<uint32_t*>(value));
}
bool ReadR64(uint64_t *value) {
if (offset_ + 8 > length_) {
return OTS_FAILURE();
}
MEMCPY_OTS(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_; }
size_t remaining() const { return length_ - offset_; }
void set_offset(size_t newoffset) { offset_ = newoffset; }
private:
const uint8_t * const buffer_;
const size_t length_;
size_t offset_;
};
// Round a value up to the nearest multiple of 4. Don't round the value in the
// case that rounding up overflows.
template<typename T> T Round4(T value) {
if (std::numeric_limits<T>::max() - value < 3) {
return value;
}
return (value + 3) & ~3;
}
template<typename T> T Round2(T value) {
if (value == std::numeric_limits<T>::max()) {
return value;
}
return (value + 1) & ~1;
}
bool IsValidVersionTag(uint32_t tag);
#define OTS_TAG_CFF OTS_TAG('C','F','F',' ')
#define OTS_TAG_CMAP OTS_TAG('c','m','a','p')
#define OTS_TAG_CVT OTS_TAG('c','v','t',' ')
#define OTS_TAG_FEAT OTS_TAG('F','e','a','t')
#define OTS_TAG_FPGM OTS_TAG('f','p','g','m')
#define OTS_TAG_GASP OTS_TAG('g','a','s','p')
#define OTS_TAG_GDEF OTS_TAG('G','D','E','F')
#define OTS_TAG_GLAT OTS_TAG('G','l','a','t')
#define OTS_TAG_GLOC OTS_TAG('G','l','o','c')
#define OTS_TAG_GLYF OTS_TAG('g','l','y','f')
#define OTS_TAG_GPOS OTS_TAG('G','P','O','S')
#define OTS_TAG_GSUB OTS_TAG('G','S','U','B')
#define OTS_TAG_HDMX OTS_TAG('h','d','m','x')
#define OTS_TAG_HEAD OTS_TAG('h','e','a','d')
#define OTS_TAG_HHEA OTS_TAG('h','h','e','a')
#define OTS_TAG_HMTX OTS_TAG('h','m','t','x')
#define OTS_TAG_KERN OTS_TAG('k','e','r','n')
#define OTS_TAG_LOCA OTS_TAG('l','o','c','a')
#define OTS_TAG_LTSH OTS_TAG('L','T','S','H')
#define OTS_TAG_MATH OTS_TAG('M','A','T','H')
#define OTS_TAG_MAXP OTS_TAG('m','a','x','p')
#define OTS_TAG_NAME OTS_TAG('n','a','m','e')
#define OTS_TAG_OS2 OTS_TAG('O','S','/','2')
#define OTS_TAG_POST OTS_TAG('p','o','s','t')
#define OTS_TAG_PREP OTS_TAG('p','r','e','p')
#define OTS_TAG_SILE OTS_TAG('S','i','l','e')
#define OTS_TAG_SILF OTS_TAG('S','i','l','f')
#define OTS_TAG_SILL OTS_TAG('S','i','l','l')
#define OTS_TAG_VDMX OTS_TAG('V','D','M','X')
#define OTS_TAG_VHEA OTS_TAG('v','h','e','a')
#define OTS_TAG_VMTX OTS_TAG('v','m','t','x')
#define OTS_TAG_VORG OTS_TAG('V','O','R','G')
struct Font;
struct FontFile;
struct TableEntry;
struct Arena;
class Table {
public:
explicit Table(Font *font, uint32_t tag, uint32_t type)
: m_tag(tag),
m_type(type),
m_font(font),
m_shouldSerialize(true) {
}
virtual ~Table() { }
virtual bool Parse(const uint8_t *data, size_t length) = 0;
virtual bool Serialize(OTSStream *out) = 0;
virtual bool ShouldSerialize();
// Return the tag (table type) this Table was parsed as, to support
// "poor man's RTTI" so that we know if we can safely down-cast to
// a specific Table subclass. The m_type field is initialized to the
// appropriate tag when a subclass is constructed, or to zero for
// TablePassthru (indicating unparsed data).
uint32_t Type() { return m_type; }
Font* GetFont() { return m_font; }
bool Error(const char *format, ...);
bool Warning(const char *format, ...);
bool Drop(const char *format, ...);
bool DropGraphite(const char *format, ...);
private:
void Message(int level, const char *format, va_list va);
uint32_t m_tag;
uint32_t m_type;
Font *m_font;
bool m_shouldSerialize;
};
class TablePassthru : public Table {
public:
explicit TablePassthru(Font *font, uint32_t tag)
: Table(font, tag, 0),
m_data(NULL),
m_length(0) {
}
bool Parse(const uint8_t *data, size_t length);
bool Serialize(OTSStream *out);
private:
const uint8_t *m_data;
size_t m_length;
};
struct Font {
explicit Font(FontFile *f)
: file(f),
version(0),
num_tables(0),
search_range(0),
entry_selector(0),
range_shift(0),
dropped_graphite(false) {
}
bool ParseTable(const TableEntry& tableinfo, const uint8_t* data,
Arena &arena);
Table* GetTable(uint32_t tag) const;
// This checks that the returned Table is actually of the correct subclass
// for |tag|, so it can safely be downcast to the corresponding OpenTypeXXXX;
// if not (i.e. if the table was treated as Passthru), it will return NULL.
Table* GetTypedTable(uint32_t tag) const;
// Drop all Graphite tables and don't parse new ones.
void DropGraphite();
FontFile *file;
uint32_t version;
uint16_t num_tables;
uint16_t search_range;
uint16_t entry_selector;
uint16_t range_shift;
bool dropped_graphite;
private:
std::map<uint32_t, Table*> m_tables;
};
struct TableEntry {
uint32_t tag;
uint32_t offset;
uint32_t length;
uint32_t uncompressed_length;
uint32_t chksum;
bool operator<(const TableEntry& other) const {
return tag < other.tag;
}
};
struct FontFile {
~FontFile();
OTSContext *context;
std::map<TableEntry, Table*> tables;
std::map<uint32_t, TableEntry> table_entries;
};
} // namespace ots
#undef MEMCPY_OTS
#undef NTOHL_OTS
#undef NTOHS_OTS
#endif // OTS_H_