Bug 1191167 - Update graphite2 library to release 1.3.0. r=jdaggett

This commit is contained in:
Jonathan Kew 2015-08-10 09:38:46 +01:00
parent f6b5af82b5
commit 73bbff7ee6
51 changed files with 3770 additions and 277 deletions

View File

@ -1,6 +1,6 @@
This directory contains the Graphite2 library from http://hg.palaso.org/graphitedev
Current version derived from upstream changeset 1efd96aeade9
Current version derived from upstream changeset e6539b6769cf
See gfx/graphite2/moz-gr-update.sh for update procedure.

View File

@ -29,8 +29,8 @@
#include "graphite2/Types.h"
#define GR2_VERSION_MAJOR 1
#define GR2_VERSION_MINOR 2
#define GR2_VERSION_BUGFIX 4
#define GR2_VERSION_MINOR 3
#define GR2_VERSION_BUGFIX 0
#ifdef __cplusplus
extern "C"

View File

@ -120,13 +120,45 @@ enum gr_attrCode {
/// Justification weight for this glyph (not implemented)
gr_slatJWeight,
/// Amount this slot mush shrink or stretch in design units
gr_slatJWidth,
gr_slatJWidth = 29,
/// SubSegment split point
gr_slatSegSplit = gr_slatJStretch + 29,
/// User defined attribute, see subattr for user attr number
gr_slatUserDefn,
/// Bidi level
gr_slatBidiLevel,
gr_slatBidiLevel = 56,
/// Collision flags
gr_slatColFlags,
/// Collision constraint rectangle left (bl.x)
gr_slatColLimitblx,
/// Collision constraint rectangle lower (bl.y)
gr_slatColLimitbly,
/// Collision constraint rectangle right (tr.x)
gr_slatColLimittrx,
/// Collision constraint rectangle upper (tr.y)
gr_slatColLimittry,
/// Collision shift x
gr_slatColShiftx,
/// Collision shift y
gr_slatColShifty,
/// Collision margin
gr_slatColMargin,
/// Margin cost weight
gr_slatColMarginWt,
// Additional glyph that excludes movement near this one:
gr_slatColExclGlyph,
gr_slatColExclOffx,
gr_slatColExclOffy,
// Collision sequence enforcing attributes:
gr_slatSeqClass,
gr_slatSeqProxClass,
gr_slatSeqOrder,
gr_slatSeqAboveXoff,
gr_slatSeqAboveWt,
gr_slatSeqBelowXlim,
gr_slatSeqBelowWt,
gr_slatSeqValignHt,
gr_slatSeqValignWt,
/// not implemented
gr_slatMax,

View File

@ -58,12 +58,15 @@ enum gr_encform {
#endif
#endif
#define GR2_LOCAL
#else
#if __GNUC__ >= 4
#define GR2_API __attribute__ ((visibility("default")))
#define GR2_LOCAL __attribute__ ((visibility("hidden")))
#elif __GNUC__ >= 4
#if defined GRAPHITE2_STATIC
#define GR2_API __attribute__ ((visibility("hidden")))
#else
#define GR2_API
#define GR2_LOCAL
#define GR2_API __attribute__ ((visibility("default")))
#endif
#define GR2_LOCAL __attribute__ ((visibility("hidden")))
#else
#define GR2_API
#define GR2_LOCAL
#endif

View File

@ -62,8 +62,8 @@ enum DirCode { // Hungarian: dirc
};
enum DirMask {
WSflag = (1 << 7), // keep track of WS for eos handling
WSMask = ~(1 << 7)
WSflag = int8(1 << 7), // keep track of WS for eos handling
WSMask = int8(~(1 << 7))
};
inline uint8 BaseClass(Slot *s) { return s->getBidiClass() & WSMask; }
@ -540,7 +540,7 @@ void processParens(Slot *s, Segment *seg, uint8 aMirror, int level, BracketPairS
BracketPair *p;
for ( ; s; s = s->prev()) // walk the sequence
{
uint16 ogid = seg->glyphAttr(s->gid(), aMirror);
uint16 ogid = seg->glyphAttr(s->gid(), aMirror) || s->gid();
int cls = BaseClass(s);
switch(cls)
@ -571,35 +571,38 @@ void processParens(Slot *s, Segment *seg, uint8 aMirror, int level, BracketPairS
mask |= 2;
}
}
for (p = stack.start(); p; p =p->next()) // walk the stack
if (stack.size())
{
if (p->close() && p->mask())
for (p = stack.start(); p; p =p->next()) // walk the stack
{
int dir = (level & 1) + 1;
if (p->mask() & dir)
{ }
else if (p->mask() & (1 << (~level & 1))) // if inside has strong other embedding
if (p->close() && p->mask())
{
int ldir = p->before();
if ((p->before() == OPP || p->before() == CPP) && p->prev())
int dir = (level & 1) + 1;
if (p->mask() & dir)
{ }
else if (p->mask() & (1 << (~level & 1))) // if inside has strong other embedding
{
for (BracketPair *q = p->prev(); q; q = q->prev())
int ldir = p->before();
if ((p->before() == OPP || p->before() == CPP) && p->prev())
{
ldir = q->open()->getBidiClass();
if (ldir < 3) break;
ldir = q->before();
if (ldir < 3) break;
for (BracketPair *q = p->prev(); q; q = q->prev())
{
ldir = q->open()->getBidiClass();
if (ldir < 3) break;
ldir = q->before();
if (ldir < 3) break;
}
if (ldir > 2) ldir = 0;
}
if (ldir > 2) ldir = 0;
if (ldir > 0 && (ldir - 1) != (level & 1)) // is dir given opp. to level dir (ldir == R or L)
dir = (~level & 1) + 1;
}
if (ldir > 0 && (ldir - 1) != (level & 1)) // is dir given opp. to level dir (ldir == R or L)
dir = (~level & 1) + 1;
p->open()->setBidiClass(dir);
p->close()->setBidiClass(dir);
}
p->open()->setBidiClass(dir);
p->close()->setBidiClass(dir);
}
stack.clear();
}
stack.clear();
}
int GetDeferredNeutrals(int action, int level)

View File

@ -78,15 +78,18 @@ add_library(graphite2 SHARED
CachedFace.cpp
CmapCache.cpp
Code.cpp
Collider.cpp
Decompressor.cpp
Face.cpp
FeatureMap.cpp
Font.cpp
GlyphFace.cpp
GlyphCache.cpp
Intervals.cpp
Justifier.cpp
NameTable.cpp
Pass.cpp
Rule.cpp
Position.cpp
Segment.cpp
Silf.cpp
Slot.cpp
@ -112,7 +115,7 @@ endif (${CMAKE_BUILD_TYPE} STREQUAL "ClangASN")
if (${CMAKE_SYSTEM_NAME} STREQUAL "Linux")
set_target_properties(graphite2 PROPERTIES
COMPILE_FLAGS "-Wall -Wextra -Wno-unknown-pragmas -Wendif-labels -Wshadow -Wctor-dtor-privacy -Wnon-virtual-dtor -fno-rtti -fno-exceptions -fvisibility=hidden -fvisibility-inlines-hidden -fno-stack-protector"
COMPILE_FLAGS "-Wall -Wextra -Wno-unknown-pragmas -Wendif-labels -Wshadow -Wctor-dtor-privacy -Wnon-virtual-dtor -fno-rtti -fno-exceptions -fvisibility=hidden -fvisibility-inlines-hidden -fno-stack-protector -Wdouble-promotion"
LINK_FLAGS "-nodefaultlibs ${GRAPHITE_LINK_FLAGS}"
LINKER_LANGUAGE C)
if (${CMAKE_CXX_COMPILER} MATCHES ".*mingw.*")

View File

@ -77,6 +77,7 @@ struct context
} // end namespace
byte * Machine::Code::local_memory = 0;
class Machine::Code::decoder
{
@ -94,7 +95,7 @@ public:
};
decoder(const limits & lims, Code &code) throw();
decoder(limits & lims, Code &code, enum passtype pt) throw();
bool load(const byte * bc_begin, const byte * bc_end);
void apply_analysis(instr * const code, instr * code_end);
@ -107,6 +108,7 @@ private:
bool emit_opcode(opcode opc, const byte * & bc);
bool validate_opcode(const opcode opc, const byte * const bc);
bool valid_upto(const uint16 limit, const uint16 x) const throw();
bool test_context() const throw();
void failure(const status_t s) const throw() { _code.failure(s); }
Code & _code;
@ -114,14 +116,16 @@ private:
uint16 _rule_length;
instr * _instr;
byte * _data;
const limits & _max;
limits & _max;
analysis _analysis;
enum passtype _passtype;
int _stack_depth;
};
struct Machine::Code::decoder::limits
{
const byte * const bytecode;
const byte * bytecode;
const uint8 pre_context;
const uint16 rule_length,
classes,
@ -130,19 +134,21 @@ struct Machine::Code::decoder::limits
const byte attrid[gr_slatMax];
};
inline Machine::Code::decoder::decoder(const limits & lims, Code &code) throw()
inline Machine::Code::decoder::decoder(limits & lims, Code &code, enum passtype pt) throw()
: _code(code),
_pre_context(code._constraint ? 0 : lims.pre_context),
_rule_length(code._constraint ? 1 : lims.rule_length),
_instr(code._code), _data(code._data), _max(lims)
_instr(code._code), _data(code._data), _max(lims), _passtype(pt),
_stack_depth(0)
{ }
Machine::Code::Code(bool is_constraint, const byte * bytecode_begin, const byte * const bytecode_end,
uint8 pre_context, uint16 rule_length, const Silf & silf, const Face & face)
uint8 pre_context, uint16 rule_length, const Silf & silf, const Face & face,
enum passtype pt, byte * & _out)
: _code(0), _data(0), _data_size(0), _instr_count(0), _max_ref(0), _status(loaded),
_constraint(is_constraint), _modify(false), _delete(false), _own(true)
_constraint(is_constraint), _modify(false), _delete(false), _own(_out==0)
{
#ifdef GRAPHITE2_TELEMETRY
telemetry::category _code_cat(face.tele.code);
@ -150,7 +156,7 @@ Machine::Code::Code(bool is_constraint, const byte * bytecode_begin, const byte
assert(bytecode_begin != 0);
if (bytecode_begin == bytecode_end)
{
::new (this) Code();
// ::new (this) Code();
return;
}
assert(bytecode_end > bytecode_begin);
@ -158,17 +164,17 @@ Machine::Code::Code(bool is_constraint, const byte * bytecode_begin, const byte
// Allocate code and dat target buffers, these sizes are a worst case
// estimate. Once we know their real sizes the we'll shrink them.
_code = static_cast<instr *>(malloc((bytecode_end - bytecode_begin)
* sizeof(instr)));
_data = static_cast<byte *>(malloc((bytecode_end - bytecode_begin)
* sizeof(byte)));
if (_out) _code = reinterpret_cast<instr *>(_out);
else _code = static_cast<instr *>(malloc((bytecode_end - bytecode_begin)
* (sizeof(instr)+sizeof(byte))));
_data = reinterpret_cast<byte *>(_code + (bytecode_end - bytecode_begin));
if (!_code || !_data) {
failure(alloc_failed);
return;
}
const decoder::limits lims = {
decoder::limits lims = {
bytecode_end,
pre_context,
rule_length,
@ -184,7 +190,7 @@ Machine::Code::Code(bool is_constraint, const byte * bytecode_begin, const byte
0,0,0,0,0,0,0, silf.numUser()}
};
decoder dec(lims, *this);
decoder dec(lims, *this, pt);
if(!dec.load(bytecode_begin, bytecode_end))
return;
@ -211,8 +217,13 @@ Machine::Code::Code(bool is_constraint, const byte * bytecode_begin, const byte
// memory.
assert((bytecode_end - bytecode_begin) >= std::ptrdiff_t(_instr_count));
assert((bytecode_end - bytecode_begin) >= std::ptrdiff_t(_data_size));
_code = static_cast<instr *>(realloc(_code, (_instr_count+1)*sizeof(instr)));
_data = static_cast<byte *>(realloc(_data, _data_size*sizeof(byte)));
memmove(_code + (_instr_count+1), _data, _data_size*sizeof(byte));
size_t const total_sz = ((_instr_count+1) + (_data_size + sizeof(instr)-1)/sizeof(instr))*sizeof(instr);
if (_out)
_out += total_sz;
else
_code = static_cast<instr *>(realloc(_code, total_sz));
_data = reinterpret_cast<byte *>(_code + (_instr_count+1));
if (!_code)
{
@ -237,6 +248,7 @@ Machine::Code::~Code() throw ()
bool Machine::Code::decoder::load(const byte * bc, const byte * bc_end)
{
_max.bytecode = bc_end;
while (bc < bc_end)
{
const opcode opc = fetch_opcode(bc++);
@ -266,55 +278,82 @@ opcode Machine::Code::decoder::fetch_opcode(const byte * bc)
switch (opc)
{
case NOP :
break;
case PUSH_BYTE :
case PUSH_BYTEU :
case PUSH_SHORT :
case PUSH_SHORTU :
case PUSH_LONG :
++_stack_depth;
break;
case ADD :
case SUB :
case MUL :
case DIV :
case MIN_ :
case MAX_ :
case NEG :
case TRUNC8 :
case TRUNC16 :
case COND :
case AND :
case OR :
case NOT :
case EQUAL :
case NOT_EQ :
case LESS :
case GTR :
case LESS_EQ :
case GTR_EQ :
case BITOR :
case BITAND :
if (--_stack_depth <= 0)
failure(underfull_stack);
break;
case NEG :
case TRUNC8 :
case TRUNC16 :
case NOT :
case BITNOT :
case BITSET :
if (_stack_depth <= 0)
failure(underfull_stack);
break;
case COND :
_stack_depth -= 2;
if (_stack_depth <= 0)
failure(underfull_stack);
break;
case NEXT :
case NEXT_N : // runtime checked
case COPY_NEXT :
test_context();
++_pre_context;
break;
case PUT_GLYPH_8BIT_OBS :
valid_upto(_max.classes, bc[0]);
test_context();
break;
case PUT_SUBS_8BIT_OBS :
valid_upto(_rule_length, _pre_context + int8(bc[0]));
valid_upto(_max.classes, bc[1]);
valid_upto(_max.classes, bc[2]);
test_context();
break;
case PUT_COPY :
valid_upto(_rule_length, _pre_context + int8(bc[0]));
test_context();
break;
case INSERT :
--_pre_context;
if (_passtype >= PASS_TYPE_POSITIONING)
failure(invalid_opcode);
else
--_pre_context;
break;
case DELETE :
if (_passtype >= PASS_TYPE_POSITIONING)
failure(invalid_opcode);
test_context();
break;
case ASSOC :
for (uint8 num = bc[0]; num; --num)
valid_upto(_rule_length, _pre_context + int8(bc[num]));
test_context();
break;
case CNTXT_ITEM :
valid_upto(_max.rule_length, _max.pre_context + int8(bc[0]));
@ -325,39 +364,52 @@ opcode Machine::Code::decoder::fetch_opcode(const byte * bc)
case ATTR_ADD :
case ATTR_SUB :
case ATTR_SET_SLOT :
if (--_stack_depth < 0)
failure(underfull_stack);
valid_upto(gr_slatMax, bc[0]);
test_context();
break;
case IATTR_SET_SLOT :
if (--_stack_depth < 0)
failure(underfull_stack);
if (valid_upto(gr_slatMax, bc[0]))
valid_upto(_max.attrid[bc[0]], bc[1]);
test_context();
break;
case PUSH_SLOT_ATTR :
++_stack_depth;
valid_upto(gr_slatMax, bc[0]);
valid_upto(_rule_length, _pre_context + int8(bc[1]));
break;
case PUSH_GLYPH_ATTR_OBS :
++_stack_depth;
valid_upto(_max.glyf_attrs, bc[0]);
valid_upto(_rule_length, _pre_context + int8(bc[1]));
break;
case PUSH_GLYPH_METRIC :
++_stack_depth;
valid_upto(kgmetDescent, bc[0]);
valid_upto(_rule_length, _pre_context + int8(bc[1]));
// level: dp[2] no check necessary
break;
case PUSH_FEAT :
++_stack_depth;
valid_upto(_max.features, bc[0]);
valid_upto(_rule_length, _pre_context + int8(bc[1]));
break;
case PUSH_ATT_TO_GATTR_OBS :
++_stack_depth;
valid_upto(_max.glyf_attrs, bc[0]);
valid_upto(_rule_length, _pre_context + int8(bc[1]));
break;
case PUSH_ATT_TO_GLYPH_METRIC :
++_stack_depth;
valid_upto(kgmetDescent, bc[0]);
valid_upto(_rule_length, _pre_context + int8(bc[1]));
// level: dp[2] no check necessary
break;
case PUSH_ISLOT_ATTR :
++_stack_depth;
if (valid_upto(gr_slatMax, bc[0]))
{
valid_upto(_rule_length, _pre_context + int8(bc[1]));
@ -365,32 +417,42 @@ opcode Machine::Code::decoder::fetch_opcode(const byte * bc)
}
break;
case PUSH_IGLYPH_ATTR :// not implemented
++_stack_depth;
case POP_RET :
if (--_stack_depth < 0)
failure(underfull_stack);
case RET_ZERO :
case RET_TRUE :
break;
case IATTR_SET :
case IATTR_ADD :
case IATTR_SUB :
if (--_stack_depth < 0)
failure(underfull_stack);
if (valid_upto(gr_slatMax, bc[0]))
valid_upto(_max.attrid[bc[0]], bc[1]);
test_context();
break;
case PUSH_PROC_STATE : // dummy: dp[0] no check necessary
case PUSH_VERSION :
++_stack_depth;
break;
case PUT_SUBS :
valid_upto(_rule_length, _pre_context + int8(bc[0]));
valid_upto(_max.classes, uint16(bc[1]<< 8) | bc[2]);
valid_upto(_max.classes, uint16(bc[3]<< 8) | bc[4]);
test_context();
break;
case PUT_SUBS2 : // not implemented
case PUT_SUBS3 : // not implemented
break;
case PUT_GLYPH :
valid_upto(_max.classes, uint16(bc[0]<< 8) | bc[1]);
test_context();
break;
case PUSH_GLYPH_ATTR :
case PUSH_ATT_TO_GLYPH_ATTR :
++_stack_depth;
valid_upto(_max.glyf_attrs, uint16(bc[0]<< 8) | bc[1]);
valid_upto(_rule_length, _pre_context + int8(bc[2]));
break;
@ -506,16 +568,20 @@ bool Machine::Code::decoder::emit_opcode(opcode opc, const byte * & bc)
byte & instr_skip = _data[-1];
byte & data_skip = *_data++;
++_code._data_size;
const byte *curr_end = _max.bytecode;
if (load(bc, bc + instr_skip))
{
bc += instr_skip;
data_skip = instr_skip - (_code._instr_count - ctxt_start);
instr_skip = _code._instr_count - ctxt_start;
_max.bytecode = curr_end;
_rule_length = 1;
_pre_context = 0;
}
else
return false;
}
return bool(_code);
@ -554,7 +620,7 @@ bool Machine::Code::decoder::validate_opcode(const opcode opc, const byte * cons
}
const opcode_t & op = Machine::getOpcodeTable()[opc];
const size_t param_sz = op.param_sz == VARARGS ? bc[0] + 1 : op.param_sz;
if (bc + param_sz > _max.bytecode)
if (bc - 1 + param_sz > _max.bytecode)
{
failure(arguments_exhausted);
return false;
@ -570,6 +636,15 @@ bool Machine::Code::decoder::valid_upto(const uint16 limit, const uint16 x) cons
return t;
}
bool Machine::Code::decoder::test_context() const throw()
{
if (_pre_context >= _rule_length)
{
failure(out_of_range_data);
return false;
}
return true;
}
inline
void Machine::Code::failure(const status_t s) throw() {
@ -594,8 +669,8 @@ void Machine::Code::decoder::analysis::set_changed(const int index) throw() {
void Machine::Code::release_buffers() throw()
{
free(_code);
free(_data);
if (_own)
free(_code);
_code = 0;
_data = 0;
_own = false;
@ -604,13 +679,12 @@ void Machine::Code::release_buffers() throw()
int32 Machine::Code::run(Machine & m, slotref * & map) const
{
assert(_own);
// assert(_own);
assert(*this); // Check we are actually runnable
if (m.slotMap().size() <= size_t(_max_ref + m.slotMap().context()))
{
m._status = Machine::slot_offset_out_bounds;
// return (m.slotMap().end() - map);
return 1;
}

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,102 @@
/* Copyright (c) 2012, Siyuan Fu <fusiyuan2010@gmail.com>
Copyright (c) 2015, SIL International
All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:
1. Redistributions of source code must retain the above copyright notice,
this list of conditions and the following disclaimer.
2. 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.
3. Neither the name of the copyright holder 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 HOLDER 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.
*/
#include <cassert>
#include "inc/Decompressor.h"
#include "inc/Shrinker.h"
using namespace shrinker;
namespace {
u8 const LONG_DIST = 0x10;
u8 const MATCH_LEN = 0x0f;
template <int M>
inline
u32 read_literal(u8 const * &s, u8 const * const e, u32 l) {
if (unlikely(l == M))
{
u8 b = 0;
do { l += b = *s++; } while(b==0xff && s != e);
}
return l;
}
bool read_directive(u8 const * &src, u8 const * const end, u32 & literal_len, u32 & match_len, u32 & match_dist)
{
u8 const flag = *src++;
literal_len = read_literal<7>(src, end, flag >> 5);
match_len = read_literal<15>(src, end, flag & MATCH_LEN);
match_dist = *src++;
if (flag & LONG_DIST)
match_dist |= ((*src++) << 8);
return match_dist != 0xffff;
}
}
int shrinker::decompress(void const *in, size_t in_size, void *out, size_t out_size)
{
u8 const * src = static_cast<u8 const *>(in),
* const src_end = src + in_size;
u8 * dst = static_cast<u8*>(out),
* const dst_end = dst + out_size;
u32 literal_len = 0,
match_len = 0,
match_dist = 0;
while (read_directive(src, src_end, literal_len, match_len, match_dist))
{
// Copy in literal
if (unlikely(dst + literal_len + sizeof(unsigned long) > dst_end)) return -1;
dst = memcpy_nooverlap(dst, src, literal_len);
src += literal_len;
// Copy, possibly repeating, match from earlier in the
// decoded output.
u8 const * const pcpy = dst - match_dist - 1;
if (unlikely(pcpy < static_cast<u8*>(out)
|| dst + match_len + MINMATCH + sizeof(unsigned long) > dst_end)) return -1;
dst = memcpy_(dst, pcpy, match_len + MINMATCH);
}
if (unlikely(dst + literal_len > dst_end)) return -1;
dst = memcpy_nooverlap_surpass(dst, src, literal_len);
return dst - (u8*)out;
}

View File

@ -28,6 +28,7 @@ of the License or (at your option) any later version.
#include "graphite2/Segment.h"
#include "inc/CmapCache.h"
#include "inc/debug.h"
#include "inc/Decompressor.h"
#include "inc/Endian.h"
#include "inc/Face.h"
#include "inc/FileFace.h"
@ -40,6 +41,16 @@ of the License or (at your option) any later version.
using namespace graphite2;
namespace
{
enum compression
{
NONE,
SHRINKER
};
}
Face::Face(const void* appFaceHandle/*non-NULL*/, const gr_face_ops & ops)
: m_appFaceHandle(appFaceHandle),
m_pFileFace(NULL),
@ -84,19 +95,21 @@ bool Face::readGlyphs(uint32 faceOptions)
telemetry::category _glyph_cat(tele.glyph);
#endif
error_context(EC_READGLYPHS);
m_pGlyphFaceCache = new GlyphCache(*this, faceOptions);
if (e.test(!m_pGlyphFaceCache, E_OUTOFMEM)
|| e.test(m_pGlyphFaceCache->numGlyphs() == 0, E_NOGLYPHS)
|| e.test(m_pGlyphFaceCache->unitsPerEm() == 0, E_BADUPEM))
{
return error(e);
}
if (faceOptions & gr_face_cacheCmap)
m_cmap = new CachedCmap(*this);
else
m_cmap = new DirectCmap(*this);
m_pGlyphFaceCache = new GlyphCache(*this, faceOptions);
if (e.test(!m_pGlyphFaceCache, E_OUTOFMEM)
|| e.test(m_pGlyphFaceCache->numGlyphs() == 0, E_NOGLYPHS)
|| e.test(m_pGlyphFaceCache->unitsPerEm() == 0, E_BADUPEM)
|| e.test(!m_cmap, E_OUTOFMEM) || e.test(!*m_cmap, E_BADCMAP))
{
if (e.test(!m_cmap, E_OUTOFMEM) || e.test(!*m_cmap, E_BADCMAP))
return error(e);
}
if (faceOptions & gr_face_preloadGlyphs)
nameTable(); // preload the name table along with the glyphs.
@ -119,10 +132,12 @@ bool Face::readGraphite(const Table & silf)
if (version >= 0x00030000)
be::skip<uint32>(p); // compilerVersion
m_numSilf = be::read<uint16>(p);
be::skip<uint16>(p); // reserved
bool havePasses = false;
m_silfs = new Silf[m_numSilf];
if (e.test(!m_silfs, E_OUTOFMEM)) return error(e);
for (int i = 0; i < m_numSilf; i++)
{
error_context(EC_ASILF + (i << 8));
@ -158,9 +173,14 @@ bool Face::runGraphite(Segment *seg, const Silf *aSilf) const
}
#endif
bool res = aSilf->runGraphite(seg, 0, aSilf->justificationPass(), true);
bool res = aSilf->runGraphite(seg, 0, aSilf->positionPass(), true);
if (res)
res = aSilf->runGraphite(seg, aSilf->positionPass(), aSilf->numPasses(), false);
{
seg->associateChars(0, seg->charInfoCount());
if (aSilf->flags() & 0x20)
res &= seg->initCollisions();
res &= aSilf->runGraphite(seg, aSilf->positionPass(), aSilf->numPasses(), false);
}
#if !defined GRAPHITE2_NTRACING
if (dbgout)
@ -245,17 +265,24 @@ uint16 Face::languageForLocale(const char * locale) const
return 0;
}
Face::Table::Table(const Face & face, const Tag n) throw()
: _f(&face)
Face::Table::Table(const Face & face, const Tag n, uint32 version) throw()
: _f(&face), _compressed(false)
{
size_t sz = 0;
_p = reinterpret_cast<const byte *>((*_f->m_ops.get_table)(_f->m_appFaceHandle, n, &sz));
_p = static_cast<const byte *>((*_f->m_ops.get_table)(_f->m_appFaceHandle, n, &sz));
_sz = uint32(sz);
if (!TtfUtil::CheckTable(n, _p, _sz))
{
this->~Table(); // Make sure we release the table buffer even if the table filed it's checks
_p = 0; _sz = 0;
return;
}
if (be::peek<uint32>(_p) >= version)
decompress();
}
Face::Table & Face::Table::operator = (const Table & rhs) throw()
@ -267,3 +294,50 @@ Face::Table & Face::Table::operator = (const Table & rhs) throw()
return *this;
}
Error Face::Table::decompress()
{
Error e;
byte * uncompressed_table = 0;
size_t uncompressed_size = 0;
const byte * p = _p;
const uint32 version = be::read<uint32>(p); // Table version number.
// The scheme is in the top 5 bits of the 1st uint32.
const uint32 hdr = be::read<uint32>(p);
switch(compression(hdr >> 27))
{
case NONE: return e;
case SHRINKER:
{
uncompressed_size = hdr & 0x07ffffff;
uncompressed_table = gralloc<byte>(uncompressed_size);
if (!e.test(!uncompressed_table, E_OUTOFMEM))
e.test(shrinker::decompress(p, _sz - 2*sizeof(uint32), uncompressed_table, uncompressed_size) != signed(uncompressed_size), E_SHRINKERFAILED);
break;
}
default:
e.error(E_BADSCHEME);
};
// Check the uncompressed version number against the original.
if (!e)
e.test(be::peek<uint32>(uncompressed_table) != version, E_SHRINKERFAILED);
// Tell the provider to release the compressed form since were replacing
// it anyway.
this->~Table();
if (e)
{
free(uncompressed_table);
uncompressed_table = 0;
uncompressed_size = 0;
}
_p = uncompressed_table;
_sz = uncompressed_size + sizeof(uint32);
_compressed = true;
return e;
}

View File

@ -165,16 +165,16 @@ bool FeatureMap::readFeats(const Face & face)
label, uiName, flags,
uiSet, num_settings);
}
m_defaultFeatures = new Features(bits/(sizeof(uint32)*8) + 1, *this);
new (&m_defaultFeatures) Features(bits/(sizeof(uint32)*8) + 1, *this);
m_pNamedFeats = new NameAndFeatureRef[m_numFeats];
if (!m_defaultFeatures || !m_pNamedFeats)
if (!m_pNamedFeats)
{
free(defVals);
return false;
}
for (int i = 0; i < m_numFeats; ++i)
{
m_feats[i].applyValToFeature(defVals[i], *m_defaultFeatures);
m_feats[i].applyValToFeature(defVals[i], m_defaultFeatures);
m_pNamedFeats[i] = m_feats+i;
}
@ -214,7 +214,7 @@ bool SillMap::readSill(const Face & face)
uint16 numSettings = be::read<uint16>(p);
uint16 offset = be::read<uint16>(p);
if (offset + 8U * numSettings > sill.size() && numSettings > 0) return false;
Features* feats = new Features(*m_FeatureMap.m_defaultFeatures);
Features* feats = new Features(m_FeatureMap.m_defaultFeatures);
if (!feats) return false;
const byte *pLSet = sill + offset;
@ -250,7 +250,7 @@ Features* SillMap::cloneFeatures(uint32 langname/*0 means default*/) const
return new Features(*m_langFeats[i].m_pFeatures);
}
}
return new Features (*m_FeatureMap.m_defaultFeatures);
return new Features (m_FeatureMap.m_defaultFeatures);
}

View File

@ -31,6 +31,7 @@ of the License or (at your option) any later version.
#include "inc/GlyphCache.h"
#include "inc/GlyphFace.h"
#include "inc/Endian.h"
#include "inc/bits.h"
using namespace graphite2;
@ -70,13 +71,14 @@ namespace
protected:
const byte * _e, * _v;
ptrdiff_t _n;
size_t _n;
};
typedef _glat_iterator<uint8> glat_iterator;
typedef _glat_iterator<uint16> glat2_iterator;
}
const Rect GlyphCache::nullRect = Rect();
class GlyphCache::Loader
{
@ -87,8 +89,10 @@ public:
unsigned short int units_per_em() const throw();
unsigned short int num_glyphs() const throw();
unsigned short int num_attrs() const throw();
bool has_boxes() const throw();
const GlyphFace * read_glyph(unsigned short gid, GlyphFace &) const throw();
const GlyphFace * read_glyph(unsigned short gid, GlyphFace &, int *numsubs) const throw();
GlyphBox * read_box(uint16 gid, GlyphBox *curr, const GlyphFace & face) const throw();
CLASS_NEW_DELETE;
private:
@ -101,6 +105,7 @@ private:
m_pGloc;
bool _long_fmt;
bool _has_boxes;
unsigned short _num_glyphs_graphics, //i.e. boundary box and advance
_num_glyphs_attributes,
_num_attrs; // number of glyph attributes per glyph
@ -111,31 +116,49 @@ private:
GlyphCache::GlyphCache(const Face & face, const uint32 face_options)
: _glyph_loader(new Loader(face, bool(face_options & gr_face_dumbRendering))),
_glyphs(_glyph_loader && *_glyph_loader ? grzeroalloc<const GlyphFace *>(_glyph_loader->num_glyphs()) : 0),
_boxes(_glyph_loader && _glyph_loader->has_boxes() ? grzeroalloc<GlyphBox *>(_glyph_loader->num_glyphs()) : 0),
_num_glyphs(_glyphs ? _glyph_loader->num_glyphs() : 0),
_num_attrs(_glyphs ? _glyph_loader->num_attrs() : 0),
_upem(_glyphs ? _glyph_loader->units_per_em() : 0)
{
if ((face_options & gr_face_preloadGlyphs) && _glyph_loader && _glyphs)
{
int numsubs = 0;
GlyphFace * const glyphs = new GlyphFace [_num_glyphs];
if (!glyphs)
return;
// The 0 glyph is definately required.
_glyphs[0] = _glyph_loader->read_glyph(0, glyphs[0]);
_glyphs[0] = _glyph_loader->read_glyph(0, glyphs[0], &numsubs);
// glyphs[0] has the same address as the glyphs array just allocated,
// thus assigning the &glyphs[0] to _glyphs[0] means _glyphs[0] points
// to the entire array.
const GlyphFace * loaded = _glyphs[0];
for (uint16 gid = 1; loaded && gid != _num_glyphs; ++gid)
_glyphs[gid] = loaded = _glyph_loader->read_glyph(gid, glyphs[gid]);
_glyphs[gid] = loaded = _glyph_loader->read_glyph(gid, glyphs[gid], &numsubs);
if (!loaded)
{
_glyphs[0] = 0;
delete [] glyphs;
}
else if (numsubs > 0)
{
GlyphBox * boxes = (GlyphBox *)gralloc<char>(_num_glyphs * sizeof(GlyphBox) + (numsubs-1) * 8 * sizeof(float));
GlyphBox * currbox = boxes;
for (uint16 gid = 0; currbox && gid != _num_glyphs; ++gid)
{
_boxes[gid] = currbox;
currbox = _glyph_loader->read_box(gid, currbox, *_glyphs[gid]);
}
if (!currbox)
{
free(boxes);
_boxes[0] = 0;
}
}
delete _glyph_loader;
_glyph_loader = 0;
}
@ -144,6 +167,11 @@ GlyphCache::GlyphCache(const Face & face, const uint32 face_options)
{
free(_glyphs);
_glyphs = 0;
if (_boxes)
{
free(_boxes);
_boxes = 0;
}
_num_glyphs = _num_attrs = _upem = 0;
}
}
@ -163,6 +191,18 @@ GlyphCache::~GlyphCache()
delete [] _glyphs[0];
free(_glyphs);
}
if (_boxes)
{
if (_glyph_loader)
{
GlyphBox * * g = _boxes;
for (uint16 n = _num_glyphs; n; --n, ++g)
free(*g);
}
else
free(_boxes[0]);
free(_boxes);
}
delete _glyph_loader;
}
@ -171,13 +211,23 @@ const GlyphFace *GlyphCache::glyph(unsigned short glyphid) const //result m
const GlyphFace * & p = _glyphs[glyphid];
if (p == 0 && _glyph_loader)
{
int numsubs = 0;
GlyphFace * g = new GlyphFace();
if (g) p = _glyph_loader->read_glyph(glyphid, *g);
if (g) p = _glyph_loader->read_glyph(glyphid, *g, &numsubs);
if (!p)
{
delete g;
return *_glyphs;
}
if (_boxes)
{
_boxes[glyphid] = (GlyphBox *)gralloc<char>(sizeof(GlyphBox) + 8 * numsubs * sizeof(float));
if (!_glyph_loader->read_box(glyphid, _boxes[glyphid], *_glyphs[glyphid]))
{
free(_boxes[glyphid]);
_boxes[glyphid] = 0;
}
}
}
return p;
}
@ -191,6 +241,7 @@ GlyphCache::Loader::Loader(const Face & face, const bool dumb_font)
_glyf(face, Tag::glyf),
_loca(face, Tag::loca),
_long_fmt(false),
_has_boxes(false),
_num_glyphs_graphics(0),
_num_glyphs_attributes(0),
_num_attrs(0)
@ -203,7 +254,7 @@ GlyphCache::Loader::Loader(const Face & face, const bool dumb_font)
_num_glyphs_graphics = TtfUtil::GlyphCount(maxp);
// This will fail if the number of glyphs is wildly out of range.
if (_glyf && TtfUtil::LocaLookup(_num_glyphs_graphics-1, _loca, _loca.size(), _head) == size_t(-1))
if (_glyf && TtfUtil::LocaLookup(_num_glyphs_graphics-1, _loca, _loca.size(), _head) == size_t(-2))
{
_head = Face::Table();
return;
@ -211,7 +262,7 @@ GlyphCache::Loader::Loader(const Face & face, const bool dumb_font)
if (!dumb_font)
{
if ((m_pGlat = Face::Table(face, Tag::Glat)) == NULL
if ((m_pGlat = Face::Table(face, Tag::Glat, 0x00030000)) == NULL
|| (m_pGloc = Face::Table(face, Tag::Gloc)) == NULL
|| m_pGloc.size() < 6)
{
@ -219,7 +270,7 @@ GlyphCache::Loader::Loader(const Face & face, const bool dumb_font)
return;
}
const byte * p = m_pGloc;
const int version = be::read<uint32>(p);
int version = be::read<uint32>(p);
const uint16 flags = be::read<uint16>(p);
_num_attrs = be::read<uint16>(p);
// We can accurately calculate the number of attributed glyphs by
@ -231,13 +282,22 @@ GlyphCache::Loader::Loader(const Face & face, const bool dumb_font)
- sizeof(uint16)*(flags & 0x2 ? _num_attrs : 0))
/ (_long_fmt ? sizeof(uint32) : sizeof(uint16)) - 1;
if (version != 0x00010000
if (version >= 0x00020000
|| _num_attrs == 0 || _num_attrs > 0x3000 // is this hard limit appropriate?
|| _num_glyphs_graphics > _num_glyphs_attributes)
{
_head = Face::Table();
return;
}
p = m_pGlat;
version = be::read<uint32>(p);
if (version >= 0x00040000) // reject Glat tables that are too new
{
_head = Face::Table();
return;
}
_has_boxes = (version == 0x00030000);
}
}
@ -265,7 +325,13 @@ unsigned short int GlyphCache::Loader::num_attrs() const throw()
return _num_attrs;
}
const GlyphFace * GlyphCache::Loader::read_glyph(unsigned short glyphid, GlyphFace & glyph) const throw()
inline
bool GlyphCache::Loader::has_boxes () const throw()
{
return _has_boxes;
}
const GlyphFace * GlyphCache::Loader::read_glyph(unsigned short glyphid, GlyphFace & glyph, int *numsubs) const throw()
{
Rect bbox;
Position advance;
@ -312,30 +378,89 @@ const GlyphFace * GlyphCache::Loader::read_glyph(unsigned short glyphid, GlyphFa
return 0;
const uint32 glat_version = be::peek<uint32>(m_pGlat);
if (glat_version == 0x00030000)
{
const byte * p = m_pGlat + glocs;
uint16 bmap = be::read<uint16>(p);
int num = bit_set_count((uint32)bmap);
if (numsubs) *numsubs += num;
glocs += 6 + 8 * num;
if (glocs > gloce)
return 0;
}
if (glat_version < 0x00020000)
{
if (gloce - glocs < 2*sizeof(byte)+sizeof(uint16)
|| gloce - glocs > _num_attrs*(2*sizeof(byte)+sizeof(uint16)))
{
return 0;
}
return 0;
new (&glyph) GlyphFace(bbox, advance, glat_iterator(m_pGlat + glocs), glat_iterator(m_pGlat + gloce));
}
else
{
if (gloce - glocs < 3*sizeof(uint16)
if (gloce - glocs < 3*sizeof(uint16) // can a glyph have no attributes? why not?
|| gloce - glocs > _num_attrs*3*sizeof(uint16))
{
return 0;
}
return 0;
new (&glyph) GlyphFace(bbox, advance, glat2_iterator(m_pGlat + glocs), glat2_iterator(m_pGlat + gloce));
}
if (!glyph.attrs() || glyph.attrs().capacity() > _num_attrs)
return 0;
}
return &glyph;
}
inline float scale_to(uint8 t, float zmin, float zmax)
{
return (zmin + t * (zmax - zmin) / 255);
}
Rect readbox(Rect &b, uint8 zxmin, uint8 zymin, uint8 zxmax, uint8 zymax)
{
return Rect(Position(scale_to(zxmin, b.bl.x, b.tr.x), scale_to(zymin, b.bl.y, b.tr.y)),
Position(scale_to(zxmax, b.bl.x, b.tr.x), scale_to(zymax, b.bl.y, b.tr.y)));
}
GlyphBox * GlyphCache::Loader::read_box(uint16 gid, GlyphBox *curr, const GlyphFace & glyph) const throw()
{
if (gid >= _num_glyphs_attributes) return 0;
const byte * gloc = m_pGloc;
size_t glocs = 0, gloce = 0;
be::skip<uint32>(gloc);
be::skip<uint16>(gloc,2);
if (_long_fmt)
{
be::skip<uint32>(gloc, gid);
glocs = be::read<uint32>(gloc);
gloce = be::peek<uint32>(gloc);
}
else
{
be::skip<uint16>(gloc, gid);
glocs = be::read<uint16>(gloc);
gloce = be::peek<uint16>(gloc);
}
if (glocs >= m_pGlat.size() || gloce > m_pGlat.size())
return 0;
const byte * p = m_pGlat + glocs;
uint16 bmap = be::read<uint16>(p);
int num = bit_set_count((uint32)bmap);
Rect bbox = glyph.theBBox();
Rect diamax(Position(bbox.bl.x + bbox.bl.y, bbox.bl.x - bbox.tr.y),
Position(bbox.tr.x + bbox.tr.y, bbox.tr.x - bbox.bl.y));
Rect diabound = readbox(diamax, p[0], p[2], p[1], p[3]);
::new (curr) GlyphBox(num, bmap, &diabound);
be::skip<uint8>(p, 4);
for (int i = 0; i < num * 2; ++i)
{
Rect box = readbox((i & 1) ? diamax : bbox, p[0], p[2], p[1], p[3]);
curr->addSubBox(i >> 1, i & 1, &box);
be::skip<uint8>(p, 4);
}
return (GlyphBox *)((char *)(curr) + sizeof(GlyphBox) + 2 * num * sizeof(Rect));
}

View File

@ -0,0 +1,292 @@
/* GRAPHITE2 LICENSING
Copyright 2010, SIL International
All rights reserved.
This library is free software; you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as published
by the Free Software Foundation; either version 2.1 of License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should also have received a copy of the GNU Lesser General Public
License along with this library in the file named "LICENSE".
If not, write to the Free Software Foundation, 51 Franklin Street,
Suite 500, Boston, MA 02110-1335, USA or visit their web page on the
internet at http://www.fsf.org/licenses/lgpl.html.
Alternatively, the contents of this file may be used under the terms of the
Mozilla Public License (http://mozilla.org/MPL) or the GNU General Public
License, as published by the Free Software Foundation, either version 2
of the License or (at your option) any later version.
*/
#include <algorithm>
#include <cmath>
#include <limits>
#include "inc/Intervals.h"
#include "inc/Segment.h"
#include "inc/Slot.h"
#include "inc/debug.h"
#include "inc/bits.h"
using namespace graphite2;
#include <cmath>
inline
Zones::Exclusion Zones::Exclusion::split_at(float p) {
Exclusion r(*this);
r.xm = x = p;
return r;
}
inline
void Zones::Exclusion::left_trim(float p) {
x = p;
}
inline
Zones::Exclusion & Zones::Exclusion::operator += (Exclusion const & rhs) {
c += rhs.c; sm += rhs.sm; smx += rhs.smx; open = false;
return *this;
}
inline
uint8 Zones::Exclusion::outcode(float val) const {
float p = val;
return ((p >= xm) << 1) | (p < x);
}
void Zones::exclude_with_margins(float xmin, float xmax, int axis) {
remove(xmin, xmax);
weightedAxis(axis, xmin-_margin_len, xmin, 0, 0, _margin_weight, xmin-_margin_len, 0, 0, false);
weightedAxis(axis, xmax, xmax+_margin_len, 0, 0, _margin_weight, xmax+_margin_len, 0, 0, false);
}
namespace
{
inline
bool separated(float a, float b) {
return a != b;
//return std::fabs(a-b) > std::numeric_limits<float>::epsilon(); // std::epsilon may not work. but 0.5 fails exising 64 bit tests
//return std::fabs(a-b) > 0.5f;
}
}
void Zones::insert(Exclusion e)
{
#if !defined GRAPHITE2_NTRACING
addDebug(&e);
#endif
e.x = std::max(e.x, _pos);
e.xm = std::min(e.xm, _posm);
if (e.x >= e.xm) return;
for (iterator i = _exclusions.begin(), ie = _exclusions.end(); i != ie && e.x < e.xm; ++i)
{
const uint8 oca = e.outcode(i->x),
ocb = e.outcode(i->xm);
if ((oca & ocb) != 0) continue;
switch (oca ^ ocb) // What kind of overlap?
{
case 0: // e completely covers i
// split e at i.x into e1,e2
// split e2 at i.mx into e2,e3
// drop e1 ,i+e2, e=e3
*i += e;
e.left_trim(i->xm);
break;
case 1: // e overlaps on the rhs of i
// split i at e->x into i1,i2
// split e at i.mx into e1,e2
// trim i1, insert i2+e1, e=e2
if (!separated(i->xm, e.x)) break;
if (separated(i->x,e.x)) { i = _exclusions.insert(i,i->split_at(e.x)); ++i; }
*i += e;
e.left_trim(i->xm);
break;
case 2: // e overlaps on the lhs of i
// split e at i->x into e1,e2
// split i at e.mx into i1,i2
// drop e1, insert e2+i1, trim i2
if (!separated(e.xm, i->x)) return;
if (separated(e.xm, i->xm)) i = _exclusions.insert(i,i->split_at(e.xm));
*i += e;
return;
case 3: // i completely covers e
// split i at e.x into i1,i2
// split i2 at e.mx into i2,i3
// insert i1, insert e+i2
if (separated(e.xm, i->xm)) i = _exclusions.insert(i,i->split_at(e.xm));
i = _exclusions.insert(i, i->split_at(e.x));
*++i += e;
return;
}
ie = _exclusions.end();
}
}
void Zones::remove(float x, float xm)
{
#if !defined GRAPHITE2_NTRACING
removeDebug(x, xm);
#endif
x = std::max(x, _pos);
xm = std::min(xm, _posm);
if (x >= xm) return;
for (iterator i = _exclusions.begin(), ie = _exclusions.end(); i != ie; ++i)
{
const uint8 oca = i->outcode(x),
ocb = i->outcode(xm);
if ((oca & ocb) != 0) continue;
switch (oca ^ ocb) // What kind of overlap?
{
case 0: // i completely covers e
if (separated(i->x, x)) { i = _exclusions.insert(i,i->split_at(x)); ++i; }
// no break
case 1: // i overlaps on the rhs of e
i->left_trim(xm);
return;
case 2: // i overlaps on the lhs of e
i->xm = x;
if (separated(i->x, i->xm)) break;
// no break
case 3: // e completely covers i
i = _exclusions.erase(i);
--i;
break;
}
ie = _exclusions.end();
}
}
Zones::const_iterator Zones::find_exclusion_under(float x) const
{
int l = 0, h = _exclusions.size();
while (l < h)
{
int const p = (l+h) >> 1;
switch (_exclusions[p].outcode(x))
{
case 0 : return _exclusions.begin()+p;
case 1 : h = p; break;
case 2 :
case 3 : l = p+1; break;
}
}
return _exclusions.begin()+l;
}
float Zones::closest(float origin, float & cost) const
{
float best_c = std::numeric_limits<float>::max(),
best_x = 0;
const const_iterator start = find_exclusion_under(origin);
// Forward scan looking for lowest cost
for (const_iterator i = start, ie = _exclusions.end(); i != ie; ++i)
if (i->track_cost(best_c, best_x, origin)) break;
// Backward scan looking for lowest cost
// We start from the exclusion to the immediate left of start since we've
// already tested start with the right most scan above.
for (const_iterator i = start-1, ie = _exclusions.begin()-1; i != ie; --i)
if (i->track_cost(best_c, best_x, origin)) break;
cost = (best_c == std::numeric_limits<float>::max() ? -1 : best_c);
return best_x;
}
// Cost and test position functions
bool Zones::Exclusion::track_cost(float & best_cost, float & best_pos, float origin) const {
const float p = test_position(origin),
localc = cost(p - origin);
if (open && localc > best_cost) return true;
if (localc < best_cost)
{
best_cost = localc;
best_pos = p;
}
return false;
}
inline
float Zones::Exclusion::cost(float p) const {
return (sm * p - 2 * smx) * p + c;
}
float Zones::Exclusion::test_position(float origin) const {
if (sm < 0)
{
// sigh, test both ends and perhaps the middle too!
float res = x;
float cl = cost(x);
if (x < origin && xm > origin)
{
float co = cost(origin);
if (co < cl)
{
cl = co;
res = origin;
}
}
float cr = cost(xm);
return cl > cr ? xm : res;
}
else
{
float zerox = smx / sm + origin;
if (zerox < x) return x;
else if (zerox > xm) return xm;
else return zerox;
}
}
#if !defined GRAPHITE2_NTRACING
void Zones::jsonDbgOut(Segment *seg) const {
if (_dbg)
{
for (Zones::idebugs s = dbgs_begin(), e = dbgs_end(); s != e; ++s)
{
*_dbg << json::flat << json::array
<< objectid(dslot(seg, (Slot *)(s->_env[0])))
<< reinterpret_cast<ptrdiff_t>(s->_env[1]);
if (s->_isdel)
*_dbg << "remove" << Position(s->_excl.x, s->_excl.xm);
else
*_dbg << "exclude" << json::flat << json::array
<< s->_excl.x << s->_excl.xm
<< s->_excl.sm << s->_excl.smx << s->_excl.c
<< json::close;
*_dbg << json::close;
}
}
}
#endif

View File

@ -60,7 +60,7 @@ void JustifyTotal::accumulate(Slot *s, Segment *seg, int level)
m_tWeight += s->getJustify(seg, level, 3);
}
float Segment::justify(Slot *pSlot, const Font *font, float width, GR_MAYBE_UNUSED justFlags flags, Slot *pFirst, Slot *pLast)
float Segment::justify(Slot *pSlot, const Font *font, float width, GR_MAYBE_UNUSED justFlags jflags, Slot *pFirst, Slot *pLast)
{
Slot *s, *end;
float currWidth = 0.0;
@ -76,11 +76,11 @@ float Segment::justify(Slot *pSlot, const Font *font, float width, GR_MAYBE_UNUS
while (!pLast->isBase()) pLast = pLast->attachedTo();
const float base = pFirst->origin().x / scale;
width = width / scale;
if ((flags & gr_justEndInline) == 0)
if ((jflags & gr_justEndInline) == 0)
{
do {
Rect bbox = theGlyphBBoxTemporary(pLast->glyph());
if (bbox.bl.x != 0. || bbox.bl.y != 0. || bbox.tr.x != 0. || bbox.tr.y == 0.)
if (bbox.bl.x != 0.f || bbox.bl.y != 0.f || bbox.tr.x != 0.f || bbox.tr.y == 0.f)
break;
pLast = pLast->prev();
} while (pLast != pFirst);
@ -116,8 +116,7 @@ float Segment::justify(Slot *pSlot, const Font *font, float width, GR_MAYBE_UNUS
++numLevels;
}
JustifyTotal *stats = new JustifyTotal[numLevels];
if (!stats) return -1.0;
Vector<JustifyTotal> stats(numLevels);
for (s = pFirst; s != end; s = s->nextSibling())
{
float w = s->origin().x / scale + s->advance() - base;
@ -127,7 +126,7 @@ float Segment::justify(Slot *pSlot, const Font *font, float width, GR_MAYBE_UNUS
s->just(0);
}
for (int i = (width < 0.0) ? -1 : numLevels - 1; i >= 0; --i)
for (int i = (width < 0.0f) ? -1 : numLevels - 1; i >= 0; --i)
{
float diff;
float error = 0.;
@ -197,7 +196,7 @@ float Segment::justify(Slot *pSlot, const Font *font, float width, GR_MAYBE_UNUS
<< "passes" << json::array;
#endif
if (m_silf->justificationPass() != m_silf->positionPass() && (width >= 0. || (silf()->flags() & 1)))
if (m_silf->justificationPass() != m_silf->positionPass() && (width >= 0.f || (silf()->flags() & 1)))
m_silf->runGraphite(this, m_silf->justificationPass(), m_silf->positionPass());
#if !defined GRAPHITE2_NTRACING

View File

@ -31,10 +31,12 @@ of the License or (at your option) any later version.
#include <cstring>
#include <cstdlib>
#include <cassert>
#include <cmath>
#include "inc/Segment.h"
#include "inc/Code.h"
#include "inc/Rule.h"
#include "inc/Error.h"
#include "inc/Collider.h"
using namespace graphite2;
using vm::Machine;
@ -49,6 +51,8 @@ Pass::Pass()
m_startStates(0),
m_transitions(0),
m_states(0),
m_codes(0),
m_progs(0),
m_flags(0),
m_iMaxLoop(0),
m_numGlyphs(0),
@ -70,21 +74,27 @@ Pass::~Pass()
free(m_states);
free(m_ruleMap);
delete [] m_rules;
if (m_rules) delete [] m_rules;
if (m_codes) delete [] m_codes;
free(m_progs);
}
bool Pass::readPass(const byte * const pass_start, size_t pass_length, size_t subtable_base, GR_MAYBE_UNUSED Face & face, Error &e)
bool Pass::readPass(const byte * const pass_start, size_t pass_length, size_t subtable_base,
GR_MAYBE_UNUSED Face & face, passtype pt, GR_MAYBE_UNUSED uint32 version, Error &e)
{
const byte * p = pass_start,
* const pass_end = p + pass_length;
const byte * p = pass_start,
* const pass_end = p + pass_length;
size_t numRanges;
if (e.test(pass_length < 40, E_BADPASSLENGTH)) return face.error(e);
// Read in basic values
m_flags = be::read<byte>(p);
if (e.test((m_flags & 15) && pt < PASS_TYPE_POSITIONING, E_BADCOLLISIONPASS))
return face.error(e);
m_iMaxLoop = be::read<byte>(p);
be::skip<byte>(p,2); // skip maxContext & maxBackup
m_numRules = be::read<uint16>(p);
if (e.test(!m_numRules && !(m_flags & 7), E_BADEMPTYPASS)) return face.error(e);
be::skip<uint16>(p); // fsmOffset - not sure why we would want this
const byte * const pcCode = pass_start + be::read<uint32>(p) - subtable_base,
* const rcCode = pass_start + be::read<uint32>(p) - subtable_base,
@ -101,7 +111,7 @@ bool Pass::readPass(const byte * const pass_start, size_t pass_length, size_t su
if ( e.test(m_numTransition > m_numStates, E_BADNUMTRANS)
|| e.test(m_numSuccess > m_numStates, E_BADNUMSUCCESS)
|| e.test(m_numSuccess + m_numTransition < m_numStates, E_BADNUMSTATES)
|| e.test(numRanges == 0, E_NORANGES))
|| e.test(m_numRules && numRanges == 0, E_NORANGES))
return face.error(e);
m_successStart = m_numStates - m_numSuccess;
@ -131,23 +141,26 @@ bool Pass::readPass(const byte * const pass_start, size_t pass_length, size_t su
be::skip<uint16>(p, m_numRules);
const byte * const precontext = p;
be::skip<byte>(p, m_numRules);
be::skip<byte>(p); // skip reserved byte
if (e.test(p + sizeof(uint16) > pass_end, E_BADCTXTLENS)) return face.error(e);
if (e.test(p + sizeof(uint16) + sizeof(uint8) > pass_end, E_BADCTXTLENS)) return face.error(e);
m_colThreshold = be::read<uint8>(p);
if (m_colThreshold == 0) m_colThreshold = 10; // A default
const size_t pass_constraint_len = be::read<uint16>(p);
const uint16 * const o_constraint = reinterpret_cast<const uint16 *>(p);
be::skip<uint16>(p, m_numRules + 1);
const uint16 * const o_actions = reinterpret_cast<const uint16 *>(p);
be::skip<uint16>(p, m_numRules + 1);
const byte * const states = p;
if (e.test(p + 2 * m_numTransition*m_numColumns >= pass_end, E_BADPASSLENGTH)) return face.error(e);
be::skip<int16>(p, m_numTransition*m_numColumns);
be::skip<byte>(p); // skip reserved byte
if (e.test(p != pcCode, E_BADPASSCCODEPTR) || e.test(p >= pass_end, E_BADPASSLENGTH)) return face.error(e);
be::skip<uint8>(p);
if (e.test(p != pcCode, E_BADPASSCCODEPTR)) return face.error(e);
be::skip<byte>(p, pass_constraint_len);
if (e.test(p != rcCode, E_BADRULECCODEPTR) || e.test(p >= pass_end, E_BADPASSLENGTH)
if (e.test(p != rcCode, E_BADRULECCODEPTR)
|| e.test(size_t(rcCode - pcCode) != pass_constraint_len, E_BADCCODELEN)) return face.error(e);
be::skip<byte>(p, be::peek<uint16>(o_constraint + m_numRules));
if (e.test(p != aCode, E_BADACTIONCODEPTR) || e.test(p >= pass_end, E_BADPASSLENGTH)) return face.error(e);
if (e.test(p != aCode, E_BADACTIONCODEPTR)) return face.error(e);
be::skip<byte>(p, be::peek<uint16>(o_actions + m_numRules));
// We should be at the end or within the pass
@ -158,19 +171,22 @@ bool Pass::readPass(const byte * const pass_start, size_t pass_length, size_t su
{
face.error_context(face.error_context() + 1);
m_cPConstraint = vm::Machine::Code(true, pcCode, pcCode + pass_constraint_len,
precontext[0], be::peek<uint16>(sort_keys), *m_silf, face);
precontext[0], be::peek<uint16>(sort_keys), *m_silf, face, PASS_TYPE_UNKNOWN);
if (e.test(!m_cPConstraint, E_OUTOFMEM)
|| e.test(m_cPConstraint.status(), m_cPConstraint.status() + E_CODEFAILURE))
|| e.test(!m_cPConstraint, m_cPConstraint.status() + E_CODEFAILURE))
return face.error(e);
face.error_context(face.error_context() - 1);
}
if (!readRanges(ranges, numRanges, e)) return face.error(e);
if (!readRules(rule_map, numEntries, precontext, sort_keys,
o_constraint, rcCode, o_actions, aCode, face, e)) return false;
if (m_numRules)
{
if (!readRanges(ranges, numRanges, e)) return face.error(e);
if (!readRules(rule_map, numEntries, precontext, sort_keys,
o_constraint, rcCode, o_actions, aCode, face, pt, e)) return false;
}
#ifdef GRAPHITE2_TELEMETRY
telemetry::category _states_cat(face.tele.states);
#endif
return readStates(start_states, states, o_rule_map, face, e);
return m_numRules ? readStates(start_states, states, o_rule_map, face, e) : true;
}
@ -178,12 +194,11 @@ bool Pass::readRules(const byte * rule_map, const size_t num_entries,
const byte *precontext, const uint16 * sort_key,
const uint16 * o_constraint, const byte *rc_data,
const uint16 * o_action, const byte * ac_data,
Face & face, Error &e)
Face & face, passtype pt, Error &e)
{
const byte * const ac_data_end = ac_data + be::peek<uint16>(o_action + m_numRules);
const byte * const rc_data_end = rc_data + be::peek<uint16>(o_constraint + m_numRules);
if (e.test(!(m_rules = new Rule [m_numRules]), E_OUTOFMEM)) return face.error(e);
precontext += m_numRules;
sort_key += m_numRules;
o_constraint += m_numRules;
@ -193,6 +208,15 @@ bool Pass::readRules(const byte * rule_map, const size_t num_entries,
const byte * ac_begin = 0, * rc_begin = 0,
* ac_end = ac_data + be::peek<uint16>(o_action),
* rc_end = rc_data + be::peek<uint16>(o_constraint);
// Allocate pools
m_rules = new Rule [m_numRules];
m_codes = new Code [m_numRules*2];
m_progs = static_cast<byte *>(malloc((ac_end - ac_data + rc_end - rc_data)
*(sizeof(vm::instr)+sizeof(byte))));
byte * prog_pool_free = m_progs;
if (e.test(!(m_rules && m_codes && m_progs), E_OUTOFMEM)) return face.error(e);
Rule * r = m_rules + m_numRules - 1;
for (size_t n = m_numRules; n; --n, --r, ac_end = ac_begin, rc_end = rc_begin)
{
@ -211,8 +235,8 @@ bool Pass::readRules(const byte * rule_map, const size_t num_entries,
if (ac_begin > ac_end || ac_begin > ac_data_end || ac_end > ac_data_end
|| rc_begin > rc_end || rc_begin > rc_data_end || rc_end > rc_data_end)
return false;
r->action = new vm::Machine::Code(false, ac_begin, ac_end, r->preContext, r->sort, *m_silf, face);
r->constraint = new vm::Machine::Code(true, rc_begin, rc_end, r->preContext, r->sort, *m_silf, face);
r->action = new (m_codes+n*2-2) vm::Machine::Code(false, ac_begin, ac_end, r->preContext, r->sort, *m_silf, face, pt, prog_pool_free);
r->constraint = new (m_codes+n*2-1) vm::Machine::Code(true, rc_begin, rc_end, r->preContext, r->sort, *m_silf, face, pt, prog_pool_free);
if (e.test(!r->action || !r->constraint, E_OUTOFMEM)
|| e.test(r->action->status() != Code::loaded, r->action->status() + E_CODEFAILURE)
@ -221,6 +245,17 @@ bool Pass::readRules(const byte * rule_map, const size_t num_entries,
return face.error(e);
}
// Shrink the program pool
ptrdiff_t const delta = static_cast<byte *>(realloc(m_progs, prog_pool_free - m_progs)) - m_progs;
if (delta)
{
m_progs += delta;
for (Code * c = m_codes, * const ce = c + m_numRules*2; c != ce; ++c)
{
c->externalProgramMoved(delta);
}
}
// Load the rule entries map
face.error_context((face.error_context() & 0xFFFF00) + EC_APASS);
RuleEntry * re = m_ruleMap = gralloc<RuleEntry>(num_entries);
@ -325,33 +360,52 @@ bool Pass::readRanges(const byte * ranges, size_t num_ranges, Error &e)
}
void Pass::runGraphite(Machine & m, FiniteStateMachine & fsm) const
bool Pass::runGraphite(vm::Machine & m, FiniteStateMachine & fsm) const
{
Slot *s = m.slotMap().segment.first();
if (!s || !testPassConstraint(m)) return;
Slot *currHigh = s->next();
if (!s || !testPassConstraint(m)) return true;
if (m_numRules)
{
Slot *currHigh = s->next();
#if !defined GRAPHITE2_NTRACING
if (fsm.dbgout) *fsm.dbgout << "rules" << json::array;
json::closer rules_array_closer(fsm.dbgout);
if (fsm.dbgout) *fsm.dbgout << "rules" << json::array;
json::closer rules_array_closer(fsm.dbgout);
#endif
m.slotMap().highwater(currHigh);
int lc = m_iMaxLoop;
do
{
findNDoRule(s, m, fsm);
if (s && (m.slotMap().highpassed() || s == m.slotMap().highwater() || --lc == 0)) {
if (!lc)
{
// if (dbgout) *dbgout << json::item << json::flat << rule_event(-1, s, 1);
s = m.slotMap().highwater();
m.slotMap().highwater(currHigh);
int lc = m_iMaxLoop;
do
{
findNDoRule(s, m, fsm);
if (s && (s == m.slotMap().highwater() || m.slotMap().highpassed() || --lc == 0)) {
if (!lc)
s = m.slotMap().highwater();
lc = m_iMaxLoop;
if (s)
m.slotMap().highwater(s->next());
}
lc = m_iMaxLoop;
if (s)
m.slotMap().highwater(s->next());
} while (s);
}
if (!(m_flags & 15) || !m.slotMap().segment.hasCollisionInfo())
return true;
if (m_flags & 7)
{
if (!(m.slotMap().segment.flags() & Segment::SEG_INITCOLLISIONS))
{
m.slotMap().segment.positionSlots(0, 0, 0, true);
// m.slotMap().segment.flags(m.slotMap().segment.flags() | Segment::SEG_INITCOLLISIONS);
}
} while (s);
if (!collisionShift(&m.slotMap().segment, m.slotMap().segment.dir(), fsm.dbgout))
return false;
}
if ((m_flags & 24) && !collisionKern(&m.slotMap().segment, m.slotMap().segment.dir(), fsm.dbgout))
return false;
if ((m_flags & 15) && !collisionFinish(&m.slotMap().segment, fsm.dbgout))
return false;
return true;
}
bool Pass::runFSM(FiniteStateMachine& fsm, Slot * slot) const
@ -455,6 +509,7 @@ void Pass::findNDoRule(Slot * & slot, Machine &m, FiniteStateMachine & fsm) cons
}
slot = slot->next();
return;
}
#if !defined GRAPHITE2_NTRACING
@ -464,9 +519,10 @@ void Pass::dumpRuleEventConsidered(const FiniteStateMachine & fsm, const RuleEnt
*fsm.dbgout << "considered" << json::array;
for (const RuleEntry *r = fsm.rules.begin(); r != &re; ++r)
{
if (r->rule->preContext > fsm.slots.context()) continue;
*fsm.dbgout << json::flat << json::object
<< "id" << r->rule - m_rules
if (r->rule->preContext > fsm.slots.context())
continue;
*fsm.dbgout << json::flat << json::object
<< "id" << r->rule - m_rules
<< "failed" << true
<< "input" << json::flat << json::object
<< "start" << objectid(dslot(&fsm.slots.segment, input_slot(fsm.slots, -r->rule->preContext)))
@ -586,15 +642,23 @@ int Pass::doAction(const Code *codeptr, Slot * & slot_out, vm::Machine & m) cons
void Pass::adjustSlot(int delta, Slot * & slot_out, SlotMap & smap) const
{
if (delta < 0)
if (!slot_out)
{
if (!slot_out)
if (smap.highpassed() || slot_out == smap.highwater())
{
slot_out = smap.segment.last();
++delta;
if (smap.highpassed() && !smap.highwater())
if (!smap.highwater())
smap.highpassed(false);
}
else
{
slot_out = smap.segment.first();
--delta;
}
}
if (delta < 0)
{
while (++delta <= 0 && slot_out)
{
if (smap.highpassed() && smap.highwater() == slot_out)
@ -604,11 +668,6 @@ void Pass::adjustSlot(int delta, Slot * & slot_out, SlotMap & smap) const
}
else if (delta > 0)
{
if (!slot_out)
{
slot_out = smap.segment.first();
--delta;
}
while (--delta >= 0 && slot_out)
{
slot_out = slot_out->next();
@ -618,3 +677,373 @@ void Pass::adjustSlot(int delta, Slot * & slot_out, SlotMap & smap) const
}
}
bool Pass::collisionShift(Segment *seg, int dir, json * const dbgout) const
{
ShiftCollider shiftcoll(dbgout);
// bool isfirst = true;
const uint8 numLoops = m_flags & 7; // number of loops permitted to fix collisions; does not include kerning
bool hasCollisions = false;
Slot *start = seg->first(); // turn on collision fixing for the first slot
Slot *end = NULL;
bool moved = false;
#if !defined GRAPHITE2_NTRACING
if (dbgout)
*dbgout << "collisions" << json::array
<< json::flat << json::object << "num-loops" << numLoops << json::close;
#endif
while (start)
{
#if !defined GRAPHITE2_NTRACING
if (dbgout) *dbgout << json::object << "phase" << "1" << "moves" << json::array;
#endif
hasCollisions = false;
end = NULL;
// phase 1 : position shiftable glyphs, ignoring kernable glyphs
for (Slot *s = start; s; s = s->next())
{
const SlotCollision * c = seg->collisionInfo(s);
if (start && (c->flags() & (SlotCollision::COLL_FIX | SlotCollision::COLL_KERN)) == SlotCollision::COLL_FIX
&& !resolveCollisions(seg, s, start, shiftcoll, false, dir, moved, hasCollisions, dbgout))
return false;
if (s != start && c->flags() & SlotCollision::COLL_END)
{
end = s->next();
break;
}
}
#if !defined GRAPHITE2_NTRACING
if (dbgout)
*dbgout << json::close << json::close; // phase-1
#endif
// phase 2 : loop until happy.
for (int i = 0; i < numLoops - 1; ++i)
{
if (hasCollisions || moved)
{
#if !defined GRAPHITE2_NTRACING
if (dbgout)
*dbgout << json::object << "phase" << "2a" << "loop" << i << "moves" << json::array;
#endif
// phase 2a : if any shiftable glyphs are in collision, iterate backwards,
// fixing them and ignoring other non-collided glyphs. Note that this handles ONLY
// glyphs that are actually in collision from phases 1 or 2b, and working backwards
// has the intended effect of breaking logjams.
if (hasCollisions)
{
hasCollisions = false;
#if 0
moved = true;
for (Slot *s = start; s != end; s = s->next())
{
SlotCollision * c = seg->collisionInfo(s);
c->setShift(Position(0, 0));
}
#endif
Slot *lend = end ? end->prev() : seg->last();
Slot *lstart = start->prev();
for (Slot *s = lend; s != lstart; s = s->prev())
{
SlotCollision * c = seg->collisionInfo(s);
if (start && (c->flags() & (SlotCollision::COLL_FIX | SlotCollision::COLL_KERN | SlotCollision::COLL_ISCOL))
== (SlotCollision::COLL_FIX | SlotCollision::COLL_ISCOL)) // ONLY if this glyph is still colliding
{
if (!resolveCollisions(seg, s, lend, shiftcoll, true, dir, moved, hasCollisions, dbgout))
return false;
c->setFlags(c->flags() | SlotCollision::COLL_TEMPLOCK);
}
}
}
#if !defined GRAPHITE2_NTRACING
if (dbgout)
*dbgout << json::close << json::close // phase 2a
<< json::object << "phase" << "2b" << "loop" << i << "moves" << json::array;
#endif
// phase 2b : redo basic diacritic positioning pass for ALL glyphs. Each successive loop adjusts
// glyphs from their current adjusted position, which has the effect of gradually minimizing the
// resulting adjustment; ie, the final result will be gradually closer to the original location.
// Also it allows more flexibility in the final adjustment, since it is moving along the
// possible 8 vectors from successively different starting locations.
if (moved)
{
moved = false;
for (Slot *s = start; s != end; s = s->next())
{
SlotCollision * c = seg->collisionInfo(s);
if (start && (c->flags() & (SlotCollision::COLL_FIX | SlotCollision::COLL_TEMPLOCK
| SlotCollision::COLL_KERN)) == SlotCollision::COLL_FIX
&& !resolveCollisions(seg, s, start, shiftcoll, false, dir, moved, hasCollisions, dbgout))
return false;
else if (c->flags() & SlotCollision::COLL_TEMPLOCK)
c->setFlags(c->flags() & ~SlotCollision::COLL_TEMPLOCK);
}
}
// if (!hasCollisions) // no, don't leave yet because phase 2b will continue to improve things
// break;
#if !defined GRAPHITE2_NTRACING
if (dbgout)
*dbgout << json::close << json::close; // phase 2
#endif
}
}
if (!end)
break;
start = NULL;
for (Slot *s = end->prev(); s; s = s->next())
{
if (seg->collisionInfo(s)->flags() & SlotCollision::COLL_START)
{
start = s;
break;
}
}
}
return true;
}
bool Pass::collisionKern(Segment *seg, int dir, json * const dbgout) const
{
KernCollider kerncoll(dbgout);
Slot *start = seg->first();
float ymin = 1e38f;
float ymax = -1e38f;
const GlyphCache &gc = seg->getFace()->glyphs();
// phase 3 : handle kerning of clusters
#if !defined GRAPHITE2_NTRACING
if (dbgout)
*dbgout << json::object << "phase" << "3" << "moves" << json::array;
#endif
for (Slot *s = seg->first(); s; s = s->next())
{
if (!gc.check(s->gid()))
return false;
const SlotCollision * c = seg->collisionInfo(s);
const Rect &bbox = seg->theGlyphBBoxTemporary(s->gid());
float y = s->origin().y + c->shift().y;
ymax = max(y + bbox.tr.y, ymax);
ymin = min(y + bbox.bl.y, ymin);
if (start && (c->flags() & (SlotCollision::COLL_KERN | SlotCollision::COLL_FIX))
== (SlotCollision::COLL_KERN | SlotCollision::COLL_FIX))
resolveKern(seg, s, start, kerncoll, dir, ymin, ymax, dbgout);
if (c->flags() & SlotCollision::COLL_END)
start = NULL;
if (c->flags() & SlotCollision::COLL_START)
start = s;
}
#if !defined GRAPHITE2_NTRACING
if (dbgout)
*dbgout << json::close << json::close; // phase 3
#endif
return true;
}
bool Pass::collisionFinish(Segment *seg, GR_MAYBE_UNUSED json * const dbgout) const
{
for (Slot *s = seg->first(); s; s = s->next())
{
SlotCollision *c = seg->collisionInfo(s);
if (c->shift().x != 0 || c->shift().y != 0)
{
const Position newOffset = c->shift();
const Position nullPosition(0, 0);
c->setOffset(newOffset + c->offset());
c->setShift(nullPosition);
}
}
// seg->positionSlots();
#if !defined GRAPHITE2_NTRACING
if (dbgout)
*dbgout << json::close;
#endif
return true;
}
// Can slot s be kerned, or is it attached to something that can be kerned?
static bool inKernCluster(Segment *seg, Slot *s)
{
SlotCollision *c = seg->collisionInfo(s);
if (c->flags() & SlotCollision::COLL_KERN /** && c->flags() & SlotCollision::COLL_FIX **/ )
return true;
while (s->attachedTo())
{
s = s->attachedTo();
c = seg->collisionInfo(s);
if (c->flags() & SlotCollision::COLL_KERN /** && c->flags() & SlotCollision::COLL_FIX **/ )
return true;
}
return false;
}
// Fix collisions for the given slot.
// Return true if everything was fixed, false if there are still collisions remaining.
// isRev means be we are processing backwards.
bool Pass::resolveCollisions(Segment *seg, Slot *slotFix, Slot *start,
ShiftCollider &coll, GR_MAYBE_UNUSED bool isRev, int dir, bool &moved, bool &hasCol,
json * const dbgout) const
{
Slot * nbor; // neighboring slot
SlotCollision *cFix = seg->collisionInfo(slotFix);
if (!coll.initSlot(seg, slotFix, cFix->limit(), cFix->margin(), cFix->marginWt(),
cFix->shift(), cFix->offset(), dir, dbgout))
return false;
bool collides = false;
// When we're processing forward, ignore kernable glyphs that preceed the target glyph.
// When processing backward, don't ignore these until we pass slotFix.
bool ignoreForKern = !isRev;
bool rtl = dir & 1;
Slot *base = slotFix;
while (base->attachedTo())
base = base->attachedTo();
Position zero(0., 0.);
// Look for collisions with the neighboring glyphs.
for (nbor = start; nbor; nbor = isRev ? nbor->prev() : nbor->next())
{
SlotCollision *cNbor = seg->collisionInfo(nbor);
bool sameCluster = nbor->isChildOf(base);
if (nbor != slotFix // don't process if this is the slot of interest
&& !(cNbor->flags() & SlotCollision::COLL_IGNORE) // don't process if ignoring
&& (nbor == base || sameCluster // process if in the same cluster as slotFix
|| !inKernCluster(seg, nbor) // or this cluster is not to be kerned
|| (rtl ^ ignoreForKern)) // or it comes before(ltr) or after(rtl)
&& (!isRev // if processing forwards then good to merge otherwise only:
|| !(cNbor->flags() & SlotCollision::COLL_FIX) // merge in immovable stuff
|| (cNbor->flags() & SlotCollision::COLL_KERN && !sameCluster) // ignore other kernable clusters
|| (cNbor->flags() & SlotCollision::COLL_ISCOL)) // test against other collided glyphs
&& !coll.mergeSlot(seg, nbor, cNbor->shift(), !ignoreForKern, sameCluster, collides, false, dbgout))
return false;
else if (nbor == slotFix)
// Switching sides of this glyph - if we were ignoring kernable stuff before, don't anymore.
ignoreForKern = !ignoreForKern;
if (nbor != start && (cNbor->flags() & (isRev ? SlotCollision::COLL_START : SlotCollision::COLL_END)))
break;
}
bool isCol = false;
if (collides || cFix->shift().x != 0.f || cFix->shift().y != 0.f)
{
Position shift = coll.resolve(seg, isCol, dbgout);
// isCol has been set to true if a collision remains.
if (std::fabs(shift.x) < 1e38f && std::fabs(shift.y) < 1e38f)
{
if (sqr(shift.x-cFix->shift().x) + sqr(shift.y-cFix->shift().y) >= m_colThreshold * m_colThreshold)
moved = true;
cFix->setShift(shift);
if (slotFix->firstChild())
{
Rect bbox;
Position here = slotFix->origin() + shift;
float clusterMin = here.x;
slotFix->firstChild()->finalise(seg, NULL, here, bbox, 0, clusterMin, false);
}
}
}
else
{
// This glyph is not colliding with anything.
#if !defined GRAPHITE2_NTRACING
if (dbgout)
{
*dbgout << json::object
<< "missed" << objectid(dslot(seg, slotFix));
coll.outputJsonDbg(dbgout, seg, -1);
*dbgout << json::close;
}
#endif
}
// Set the is-collision flag bit.
if (isCol)
{ cFix->setFlags(cFix->flags() | SlotCollision::COLL_ISCOL | SlotCollision::COLL_KNOWN); }
else
{ cFix->setFlags((cFix->flags() & ~SlotCollision::COLL_ISCOL) | SlotCollision::COLL_KNOWN); }
hasCol |= isCol;
return true;
}
float Pass::resolveKern(Segment *seg, Slot *slotFix, GR_MAYBE_UNUSED Slot *start, KernCollider &coll, int dir,
float &ymin, float &ymax, json *const dbgout) const
{
Slot *nbor; // neighboring slot
float currSpace = 0.;
bool collides = false;
unsigned int space_count = 0;
Slot *base = slotFix;
while (base->attachedTo())
base = base->attachedTo();
SlotCollision *cFix = seg->collisionInfo(base);
const GlyphCache &gc = seg->getFace()->glyphs();
if (base != slotFix)
{
cFix->setFlags(cFix->flags() | SlotCollision::COLL_KERN | SlotCollision::COLL_FIX);
return 0;
}
bool seenEnd = (cFix->flags() & SlotCollision::COLL_END) != 0;
bool isInit = false;
for (nbor = slotFix->next(); nbor; nbor = nbor->next())
{
if (nbor->isChildOf(base))
continue;
if (!gc.check(nbor->gid()))
return 0.;
const Rect &bb = seg->theGlyphBBoxTemporary(nbor->gid());
SlotCollision *cNbor = seg->collisionInfo(nbor);
if (bb.bl.y == 0.f && bb.tr.y == 0.f)
{
if ((m_flags & 24) == 16)
break;
// Add space for a space glyph.
currSpace += nbor->advance();
++space_count;
}
else
{
space_count = 0;
float y = nbor->origin().y + cNbor->shift().y;
ymax = max(y + bb.tr.y, ymax);
ymin = min(y + bb.bl.y, ymin);
if (nbor != slotFix && !(cNbor->flags() & SlotCollision::COLL_IGNORE))
{
seenEnd = true;
if (!isInit)
{
if (!coll.initSlot(seg, slotFix, cFix->limit(), cFix->margin(),
cFix->shift(), cFix->offset(), dir, ymin, ymax, dbgout))
return 0.;
isInit = true;
}
collides |= coll.mergeSlot(seg, nbor, cNbor->shift(), currSpace, dir, dbgout);
}
}
if (cNbor->flags() & SlotCollision::COLL_END)
{
if (seenEnd && space_count < 2)
break;
else
seenEnd = true;
}
}
if (collides)
{
Position mv = coll.resolve(seg, slotFix, dir, cFix->margin(), dbgout);
coll.shift(mv, dir);
Position delta = slotFix->advancePos() + mv - cFix->shift();
slotFix->advance(delta);
cFix->setShift(mv);
return mv.x;
}
return 0.;
}

View File

@ -0,0 +1,98 @@
/* GRAPHITE2 LICENSING
Copyright 2010, SIL International
All rights reserved.
This library is free software; you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as published
by the Free Software Foundation; either version 2.1 of License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should also have received a copy of the GNU Lesser General Public
License along with this library in the file named "LICENSE".
If not, write to the Free Software Foundation, 51 Franklin Street,
Suite 500, Boston, MA 02110-1335, USA or visit their web page on the
internet at http://www.fsf.org/licenses/lgpl.html.
Alternatively, the contents of this file may be used under the terms of the
Mozilla Public License (http://mozilla.org/MPL) or the GNU General Public
License, as published by the Free Software Foundation, either version 2
of the License or (at your option) any later version.
*/
#include "inc/Position.h"
#include <cmath>
using namespace graphite2;
bool Rect::hitTest(Rect &other)
{
if (bl.x > other.tr.x) return false;
if (tr.x < other.bl.x) return false;
if (bl.y > other.tr.y) return false;
if (tr.y < other.bl.y) return false;
return true;
}
Position Rect::overlap(Position &offset, Rect &other, Position &othero)
{
float ax = (bl.x + offset.x) - (other.tr.x + othero.x);
float ay = (bl.y + offset.y) - (other.tr.y + othero.y);
float bx = (other.bl.x + othero.x) - (tr.x + offset.x);
float by = (other.bl.y + othero.y) - (tr.y + offset.y);
return Position((ax > bx ? ax : bx), (ay > by ? ay : by));
}
float boundmin(float move, float lim1, float lim2, float &error)
{
// error is always positive for easy comparison
if (move < lim1 && move < lim2)
{ error = 0.; return move; }
else if (lim1 < lim2)
{ error = std::fabs(move - lim1); return lim1; }
else
{ error = std::fabs(move - lim2); return lim2; }
}
#if 0
Position Rect::constrainedAvoid(Position &offset, Rect &box, Rect &sdbox, Position &other, Rect &obox, Rect &osdbox)
{
// a = max, i = min, s = sum, d = diff
float eax, eay, eix, eiy, eas, eis, ead, eid;
float beste = INF;
Position res;
// calculate the movements in each direction and the error (amount of remaining overlap)
// first param is movement, second and third are movement over the constraining box
float ax = boundmin(obox.tr.x + other.x - box.bl.x - offset.x + 1, tr.x - offset.x, INF, &eax);
float ay = boundmin(obox.tr.y + other.y - box.bl.y - offset.y + 1, tr.y - offset.y, INF, &eay);
float ix = boundmin(obox.bl.x + other.x - box.tr.x - offset.x + 1, bl.x - offset.x, INF, &eix);
float iy = boundmin(obox.bl.y + other.y - box.tr.y - offset.y + 1, bl.y - offset.y, INF, &eiy);
float as = boundmin(ISQRT2 * (osdbox.tr.x + other.x + other.y - sdbox.bl.x - offset.x - offset.y) + 1, tr.x - offset.x, tr.y - offset.y, &eas);
float is = boundmin(ISQRT2 * (osdbox.bl.x + other.x + other.y - sdbox.tr.x - offset.x - offset.y) + 1, bl.x - offset.x, bl.y - offset.y, &eis);
float ad = boundmin(ISQRT2 * (osdbox.tr.y + other.x - other.y - sdbox.bl.y - offset.x + offset.y) + 1, tr.y - offset.y, tr.x - offset.x, &ead);
float id = boundmin(ISQRT2 * (osdbox.bl.y + other.x - other.y - sdbox.tr.y - offset.x + offset.y) + 1, bl.y - offset.y, bl.x - offset.x, &eid);
if (eax < beste)
{ res = Position(ax, 0); beste = eax; }
if (eay < beste)
{ res = Position(0, ay); beste = eay; }
if (eix < beste)
{ res = Position(ix, 0); beste = eix; }
if (eiy < beste)
{ res = Position(0, iy); beste = eiy; }
if (SQRT2 * (eas) < beste)
{ res = Position(as, ad); beste = SQRT2 * (eas); }
if (SQRT2 * (eis) < beste)
{ res = Position(is, is); beste = SQRT2 * (eis); }
if (SQRT2 * (ead) < beste)
{ res = Position(ad, ad); beste = SQRT2 * (ead); }
if (SQRT2 * (eid) < beste)
{ res = Position(id, id); beste = SQRT2 * (eid); }
return res;
}
#endif

View File

@ -37,6 +37,7 @@ of the License or (at your option) any later version.
#include "inc/Main.h"
#include "inc/CmapCache.h"
#include "inc/Bidi.h"
#include "inc/Collider.h"
#include "graphite2/Segment.h"
@ -46,6 +47,7 @@ Segment::Segment(unsigned int numchars, const Face* face, uint32 script, int tex
: m_freeSlots(NULL),
m_freeJustifies(NULL),
m_charinfo(new CharInfo[numchars]),
m_collisions(NULL),
m_face(face),
m_silf(face->chooseSilf(script)),
m_first(NULL),
@ -55,7 +57,8 @@ Segment::Segment(unsigned int numchars, const Face* face, uint32 script, int tex
m_numCharinfo(numchars),
m_passBits(m_silf->aPassBits() ? -1 : 0),
m_defaultOriginal(0),
m_dir(textDir)
m_dir(textDir),
m_flags(0)
{
freeSlot(newSlot());
m_bufSize = log_binary(numchars)+1;
@ -335,7 +338,7 @@ void Segment::linkClusters(Slot *s, Slot * end)
}
}
Position Segment::positionSlots(const Font *font, Slot * iStart, Slot * iEnd)
Position Segment::positionSlots(const Font *font, Slot * iStart, Slot * iEnd, bool isFinal)
{
Position currpos(0., 0.);
float clusterMin = 0.;
@ -349,7 +352,7 @@ Position Segment::positionSlots(const Font *font, Slot * iStart, Slot * iEnd)
for (Slot * s = iEnd, * const end = iStart->prev(); s && s != end; s = s->prev())
{
if (s->isBase())
currpos = s->finalise(this, font, currpos, bbox, 0, clusterMin = currpos.x);
currpos = s->finalise(this, font, currpos, bbox, 0, clusterMin = currpos.x, isFinal);
}
}
else
@ -357,7 +360,7 @@ Position Segment::positionSlots(const Font *font, Slot * iStart, Slot * iEnd)
for (Slot * s = iStart, * const end = iEnd->next(); s && s != end; s = s->next())
{
if (s->isBase())
currpos = s->finalise(this, font, currpos, bbox, 0, clusterMin = currpos.x);
currpos = s->finalise(this, font, currpos, bbox, 0, clusterMin = currpos.x, isFinal);
}
}
return currpos;
@ -434,11 +437,6 @@ bool Segment::read_text(const Face *face, const Features* pFeats/*must not be NU
return true;
}
void Segment::prepare_pos(const Font * /*font*/)
{
// copy key changeable metrics into slot (if any);
}
Slot *process_bidi(Slot *start, int level, int prelevel, int &nextLevel, int dirover, int isol, int &cisol, int &isolerr, int &embederr, int init, Segment *seg, uint8 aMirror, BracketPairStack &stack);
void resolveImplicit(Slot *s, Segment *seg, uint8 aMirror);
void resolveWhitespace(int baseLevel, Slot *s);
@ -462,7 +460,7 @@ void Segment::bidiPass(uint8 aBidi, int paradir, uint8 aMirror)
}
bmask |= (1 << s->getBidiClass());
s->setBidiLevel(baseLevel);
if (glyphAttr(s->gid(), aMirror) && s->getBidiClass() == 21)
if (s->getBidiClass() == 21)
++ssize;
}
@ -492,3 +490,16 @@ void Segment::bidiPass(uint8 aBidi, int paradir, uint8 aMirror)
}
}
bool Segment::initCollisions()
{
if (m_collisions) free(m_collisions);
Slot *p = m_first;
m_collisions = gralloc<SlotCollision>(slotCount());
if (!m_collisions) return false;
for (unsigned short i = 0; i < slotCount(); ++i)
{
::new (m_collisions + p->index()) SlotCollision(this, p);
p = p->next();
}
return true;
}

View File

@ -93,6 +93,10 @@ bool Silf::readGraphite(const byte * const silf_start, size_t lSilf, Face& face,
* const silf_end = p + lSilf;
Error e;
if (e.test(version >= 0x00060000, E_BADSILFVERSION))
{
releaseBuffers(); return face.error(e);
}
if (version >= 0x00030000)
{
if (e.test(lSilf < 28, E_BADSIZE)) { releaseBuffers(); return face.error(e); }
@ -140,7 +144,9 @@ bool Silf::readGraphite(const byte * const silf_start, size_t lSilf, Face& face,
m_aLig = be::read<uint16>(p);
m_aUser = be::read<uint8>(p);
m_iMaxComp = be::read<uint8>(p);
be::skip<byte>(p,5); // direction and 4 reserved bytes
be::skip<byte>(p); // direction
m_aCollision = be::read<uint8>(p);
be::skip<byte>(p,3);
be::skip<uint16>(p, be::read<uint8>(p)); // don't need critical features yet
be::skip<byte>(p); // reserved
if (e.test(p >= silf_end, E_BADCRITFEATURES)) { releaseBuffers(); return face.error(e); }
@ -155,6 +161,7 @@ bool Silf::readGraphite(const byte * const silf_start, size_t lSilf, Face& face,
|| e.test(m_aBreak >= num_attrs, E_BADABREAK)
|| e.test(m_aBidi >= num_attrs, E_BADABIDI)
|| e.test(m_aMirror>= num_attrs, E_BADAMIRROR)
|| e.test(m_aCollision && m_aCollision >= num_attrs - 5, E_BADACOLLISION)
|| e.test(m_numPasses > 128, E_BADNUMPASSES) || e.test(passes_start >= silf_end, E_BADPASSESSTART)
|| e.test(m_pPass < m_sPass, E_BADPASSBOUND) || e.test(m_pPass > m_numPasses, E_BADPPASS) || e.test(m_sPass > m_numPasses, E_BADSPASS)
|| e.test(m_jPass < m_pPass, E_BADJPASSBOUND) || e.test(m_jPass > m_numPasses, E_BADJPASS)
@ -168,11 +175,12 @@ bool Silf::readGraphite(const byte * const silf_start, size_t lSilf, Face& face,
if (e.test(p + sizeof(uint16) >= passes_start, E_BADPASSESSTART)) { releaseBuffers(); return face.error(e); }
m_numPseudo = be::read<uint16>(p);
be::skip<uint16>(p, 3); // searchPseudo, pseudoSelector, pseudoShift
if (e.test(p + m_numPseudo*(sizeof(uint32) + sizeof(uint16)) >= passes_start, E_BADNUMPSEUDO))
m_pseudos = new Pseudo[m_numPseudo];
if (e.test(p + m_numPseudo*(sizeof(uint32) + sizeof(uint16)) >= passes_start, E_BADNUMPSEUDO)
|| e.test(!m_pseudos, E_OUTOFMEM))
{
releaseBuffers(); return face.error(e);
}
m_pseudos = new Pseudo[m_numPseudo];
for (int i = 0; i < m_numPseudo; i++)
{
m_pseudos[i].uid = be::read<uint32>(p);
@ -180,9 +188,11 @@ bool Silf::readGraphite(const byte * const silf_start, size_t lSilf, Face& face,
}
const size_t clen = readClassMap(p, passes_start - p, version, e);
if (e || e.test(p + clen > passes_start, E_BADPASSESSTART)) { releaseBuffers(); return face.error(e); }
m_passes = new Pass[m_numPasses];
if (e || e.test(p + clen > passes_start, E_BADPASSESSTART)
|| e.test(!m_passes, E_OUTOFMEM))
{ releaseBuffers(); return face.error(e); }
for (size_t i = 0; i < m_numPasses; ++i)
{
const byte * const pass_start = silf_start + be::read<uint32>(o_passes),
@ -192,8 +202,15 @@ bool Silf::readGraphite(const byte * const silf_start, size_t lSilf, Face& face,
releaseBuffers(); return face.error(e);
}
enum passtype pt = PASS_TYPE_UNKNOWN;
if (i >= m_jPass) pt = PASS_TYPE_JUSTIFICATION;
else if (i >= m_pPass) pt = PASS_TYPE_POSITIONING;
else if (i >= m_sPass) pt = PASS_TYPE_SUBSTITUTE;
else pt = PASS_TYPE_LINEBREAK;
m_passes[i].init(this);
if (!m_passes[i].readPass(pass_start, pass_end - pass_start, pass_start - silf_start, face, e))
if (!m_passes[i].readPass(pass_start, pass_end - pass_start, pass_start - silf_start, face, pt,
version, e))
{
releaseBuffers();
return false;
@ -373,7 +390,7 @@ bool Silf::runGraphite(Segment *seg, uint8 firstPass, uint8 lastPass, int dobidi
if (!(seg->dir() & 2))
seg->bidiPass(m_aBidi, seg->dir() & 1, m_aMirror);
else if (m_aMirror)
else if (m_aMirror && (seg->dir() & 1))
{
Slot * s;
for (s = seg->first(); s; s = s->next())
@ -403,8 +420,9 @@ bool Silf::runGraphite(Segment *seg, uint8 firstPass, uint8 lastPass, int dobidi
#endif
// test whether to reorder, prepare for positioning
if (i >= 32 || (seg->passBits() & (1 << i)) == 0)
m_passes[i].runGraphite(m, fsm);
if ((i >= 32 || (seg->passBits() & (1 << i)) == 0 || (m_passes[i].flags() & 7))
&& !m_passes[i].runGraphite(m, fsm))
return false;
// only subsitution passes can change segment length, cached subsegments are short for their text
if (m.status() != vm::Machine::finished
|| (i < m_pPass && (seg->slotCount() > initSize * MAX_SEG_GROWTH_FACTOR

View File

@ -29,6 +29,7 @@ of the License or (at your option) any later version.
#include "inc/Silf.h"
#include "inc/CharInfo.h"
#include "inc/Rule.h"
#include "inc/Collider.h"
using namespace graphite2;
@ -85,19 +86,26 @@ void Slot::update(int /*numGrSlots*/, int numCharInfo, Position &relpos)
m_position = m_position + relpos;
}
Position Slot::finalise(const Segment *seg, const Font *font, Position & base, Rect & bbox, uint8 attrLevel, float & clusterMin)
Position Slot::finalise(const Segment *seg, const Font *font, Position & base, Rect & bbox, uint8 attrLevel, float & clusterMin, bool isFinal)
{
SlotCollision *coll = NULL;
if (attrLevel && m_attLevel > attrLevel) return Position(0, 0);
float scale = 1.0;
float scale = font ? font->scale() : 1.0f;
Position shift(m_shift.x * ((seg->dir() & 1) * -2 + 1) + m_just, m_shift.y);
float tAdvance = m_advance.x + m_just;
if (isFinal && (coll = seg->collisionInfo(this)))
{
const Position &collshift = coll->offset();
if (!(coll->flags() & SlotCollision::COLL_KERN) || (seg->dir() & 1))
shift = shift + collshift;
}
const GlyphFace * glyphFace = seg->getFace()->glyphs().glyphSafe(glyph());
if (font)
{
scale = font->scale();
shift *= scale;
if (font->isHinted() && glyphFace)
tAdvance = (m_advance.x - glyphFace->theAdvance().x + m_just) * scale + font->advance(m_glyphid);
tAdvance = (m_advance.x - glyphFace->theAdvance().x + m_just) * scale + font->advance(glyph());
else
tAdvance *= scale;
}
@ -107,15 +115,15 @@ Position Slot::finalise(const Segment *seg, const Font *font, Position & base, R
if (!m_parent)
{
res = base + Position(tAdvance, m_advance.y * scale);
clusterMin = base.x;
clusterMin = m_position.x;
}
else
{
float tAdv;
m_position += (m_attach - m_with) * scale;
tAdv = m_advance.x >= 0.5 ? m_position.x + tAdvance - shift.x : 0.f;
tAdv = m_advance.x >= 0.5f ? m_position.x + tAdvance - shift.x : 0.f;
res = Position(tAdv, 0);
if ((m_advance.x >= 0.5 || m_position.x < 0) && m_position.x < clusterMin) clusterMin = m_position.x;
if ((m_advance.x >= 0.5f || m_position.x < 0) && m_position.x < clusterMin) clusterMin = m_position.x;
}
if (glyphFace)
@ -126,19 +134,19 @@ Position Slot::finalise(const Segment *seg, const Font *font, Position & base, R
if (m_child && m_child != this && m_child->attachedTo() == this)
{
Position tRes = m_child->finalise(seg, font, m_position, bbox, attrLevel, clusterMin);
if ((!m_parent || m_advance.x >= 0.5) && tRes.x > res.x) res = tRes;
Position tRes = m_child->finalise(seg, font, m_position, bbox, attrLevel, clusterMin, isFinal);
if ((!m_parent || m_advance.x >= 0.5f) && tRes.x > res.x) res = tRes;
}
if (m_parent && m_sibling && m_sibling != this && m_sibling->attachedTo() == m_parent)
{
Position tRes = m_sibling->finalise(seg, font, base, bbox, attrLevel, clusterMin);
Position tRes = m_sibling->finalise(seg, font, base, bbox, attrLevel, clusterMin, isFinal);
if (tRes.x > res.x) res = tRes;
}
if (!m_parent && clusterMin < base.x)
{
Position adj = Position(base.x - clusterMin, 0.);
Position adj = Position(m_position.x - clusterMin, 0.);
res += adj;
m_position += adj;
if (m_child) m_child->floodShift(adj);
@ -151,7 +159,7 @@ int32 Slot::clusterMetric(const Segment *seg, uint8 metric, uint8 attrLevel)
Position base;
Rect bbox = seg->theGlyphBBoxTemporary(glyph());
float clusterMin = 0.;
Position res = finalise(seg, NULL, base, bbox, attrLevel, clusterMin);
Position res = finalise(seg, NULL, base, bbox, attrLevel, clusterMin, false);
switch (metrics(metric))
{
@ -180,6 +188,8 @@ int32 Slot::clusterMetric(const Segment *seg, uint8 metric, uint8 attrLevel)
}
}
#define SLOTGETCOLATTR(x) { SlotCollision *c = seg->collisionInfo(this); return c ? int(c-> x) : 0; }
int Slot::getAttr(const Segment *seg, attrCode ind, uint8 subindex) const
{
if (!this) return 0;
@ -211,7 +221,7 @@ int Slot::getAttr(const Segment *seg, attrCode ind, uint8 subindex) const
case gr_slatBreak : return seg->charinfo(m_original)->breakWeight();
case gr_slatCompRef : return 0;
case gr_slatDir : if (m_bidiCls == -1)
const_cast<Slot *>(this)->setBidiClass(seg->glyphAttr(gid(), seg->silf()->aBidi()));
const_cast<Slot *>(this)->setBidiClass(int8(seg->glyphAttr(gid(), seg->silf()->aBidi())));
return m_bidiCls;
case gr_slatInsert : return isInsertBefore();
case gr_slatPosX : return int(m_position.x); // but need to calculate it
@ -224,10 +234,42 @@ int Slot::getAttr(const Segment *seg, attrCode ind, uint8 subindex) const
case gr_slatUserDefn : return m_userAttr[subindex];
case gr_slatSegSplit : return seg->charinfo(m_original)->flags() & 3;
case gr_slatBidiLevel: return m_bidiLevel;
default : return 0;
case gr_slatColFlags : { SlotCollision *c = seg->collisionInfo(this); return c ? c->flags() : 0; }
case gr_slatColLimitblx : SLOTGETCOLATTR(limit().bl.x)
case gr_slatColLimitbly : SLOTGETCOLATTR(limit().bl.y)
case gr_slatColLimittrx : SLOTGETCOLATTR(limit().tr.x)
case gr_slatColLimittry : SLOTGETCOLATTR(limit().tr.y)
case gr_slatColShiftx : SLOTGETCOLATTR(offset().x)
case gr_slatColShifty : SLOTGETCOLATTR(offset().y)
case gr_slatColMargin : SLOTGETCOLATTR(margin())
case gr_slatColMarginWt : SLOTGETCOLATTR(marginWt())
case gr_slatColExclGlyph : SLOTGETCOLATTR(exclGlyph())
case gr_slatColExclOffx : SLOTGETCOLATTR(exclOffset().x)
case gr_slatColExclOffy : SLOTGETCOLATTR(exclOffset().y)
case gr_slatSeqClass : SLOTGETCOLATTR(seqClass())
case gr_slatSeqProxClass : SLOTGETCOLATTR(seqProxClass())
case gr_slatSeqOrder : SLOTGETCOLATTR(seqOrder())
case gr_slatSeqAboveXoff : SLOTGETCOLATTR(seqAboveXoff())
case gr_slatSeqAboveWt : SLOTGETCOLATTR(seqAboveWt())
case gr_slatSeqBelowXlim : SLOTGETCOLATTR(seqBelowXlim())
case gr_slatSeqBelowWt : SLOTGETCOLATTR(seqBelowWt())
case gr_slatSeqValignHt : SLOTGETCOLATTR(seqValignHt())
case gr_slatSeqValignWt : SLOTGETCOLATTR(seqValignWt())
default : return 0;
}
}
#define SLOTCOLSETATTR(x) { \
SlotCollision *c = seg->collisionInfo(this); \
if (c) { c-> x ; c->setFlags(c->flags() & ~SlotCollision::COLL_KNOWN); } \
break; }
#define SLOTCOLSETCOMPLEXATTR(t, y, x) { \
SlotCollision *c = seg->collisionInfo(this); \
if (c) { \
const t &s = c-> y; \
c-> x ; c->setFlags(c->flags() & ~SlotCollision::COLL_KNOWN); } \
break; }
void Slot::setAttr(Segment *seg, attrCode ind, uint8 subindex, int16 value, const SlotMap & map)
{
if (!this) return;
@ -252,9 +294,9 @@ void Slot::setAttr(Segment *seg, attrCode ind, uint8 subindex, int16 value, cons
if (idx < map.size() && map[idx])
{
Slot *other = map[idx];
if (other == this) break;
if (other == this || other == m_parent) break;
if (m_parent) m_parent->removeChild(this);
if (other->child(this))
if (!other->isChildOf(this) && other->child(this))
{
attachTo(other);
if (((seg->dir() & 1) != 0) ^ (idx > subindex))
@ -280,7 +322,7 @@ void Slot::setAttr(Segment *seg, attrCode ind, uint8 subindex, int16 value, cons
seg->charinfo(m_original)->breakWeight(value);
break;
case gr_slatCompRef : break; // not sure what to do here
case gr_slatDir : m_bidiCls = value; break;
case gr_slatDir : m_bidiCls = int8(value); break;
case gr_slatInsert :
markInsertBefore(value? true : false);
break;
@ -293,6 +335,29 @@ void Slot::setAttr(Segment *seg, attrCode ind, uint8 subindex, int16 value, cons
case gr_slatJWidth : just(value); break;
case gr_slatSegSplit : seg->charinfo(m_original)->addflags(value & 3); break;
case gr_slatUserDefn : m_userAttr[subindex] = value; break;
case gr_slatColFlags : {
SlotCollision *c = seg->collisionInfo(this);
if (c)
c->setFlags(value);
break; }
case gr_slatColLimitblx : SLOTCOLSETCOMPLEXATTR(Rect, limit(), setLimit(Rect(Position(value, s.bl.y), s.tr)))
case gr_slatColLimitbly : SLOTCOLSETCOMPLEXATTR(Rect, limit(), setLimit(Rect(Position(s.bl.x, value), s.tr)))
case gr_slatColLimittrx : SLOTCOLSETCOMPLEXATTR(Rect, limit(), setLimit(Rect(s.bl, Position(value, s.tr.y))))
case gr_slatColLimittry : SLOTCOLSETCOMPLEXATTR(Rect, limit(), setLimit(Rect(s.bl, Position(s.tr.x, value))))
case gr_slatColMargin : SLOTCOLSETATTR(setMargin(value))
case gr_slatColMarginWt : SLOTCOLSETATTR(setMarginWt(value))
case gr_slatColExclGlyph : SLOTCOLSETATTR(setExclGlyph(value))
case gr_slatColExclOffx : SLOTCOLSETCOMPLEXATTR(Position, exclOffset(), setExclOffset(Position(value, s.y)))
case gr_slatColExclOffy : SLOTCOLSETCOMPLEXATTR(Position, exclOffset(), setExclOffset(Position(s.x, value)))
case gr_slatSeqClass : SLOTCOLSETATTR(setSeqClass(value))
case gr_slatSeqProxClass : SLOTCOLSETATTR(setSeqProxClass(value))
case gr_slatSeqOrder : SLOTCOLSETATTR(setSeqOrder(value))
case gr_slatSeqAboveXoff : SLOTCOLSETATTR(setSeqAboveXoff(value))
case gr_slatSeqAboveWt : SLOTCOLSETATTR(setSeqAboveWt(value))
case gr_slatSeqBelowXlim : SLOTCOLSETATTR(setSeqBelowXlim(value))
case gr_slatSeqBelowWt : SLOTCOLSETATTR(setSeqBelowWt(value))
case gr_slatSeqValignHt : SLOTCOLSETATTR(setSeqValignHt(value))
case gr_slatSeqValignWt : SLOTCOLSETATTR(setSeqValignWt(value))
default :
break;
}
@ -374,6 +439,7 @@ bool Slot::removeSibling(Slot *ap)
else if (ap == m_sibling)
{
m_sibling = m_sibling->nextSibling();
ap->sibling(NULL);
return true;
}
else
@ -425,3 +491,29 @@ void SlotJustify::LoadSlot(const Slot *s, const Segment *seg)
v[3] = seg->glyphAttr(s->gid(), justs->attrWeight());
}
}
Slot * Slot::nextInCluster(const Slot *s) const
{
Slot *base;
if (s->firstChild())
return s->firstChild();
else if (s->nextSibling())
return s->nextSibling();
while ((base = s->attachedTo()))
{
if (base->firstChild() == s && base->nextSibling())
return base->nextSibling();
s = base;
}
return NULL;
}
bool Slot::isChildOf(const Slot *base) const
{
if (m_parent == base)
return true;
else if (!m_parent)
return false;
else
return m_parent->isChildOf(base);
}

View File

@ -873,11 +873,11 @@ const void * FindCmapSubtable(const void * pCmap, int nPlatformId, /* =3 */ int
/*----------------------------------------------------------------------------------------------
Check the Microsoft Unicode subtable for expected values
----------------------------------------------------------------------------------------------*/
bool CheckCmapSubtable4(const void * pCmapSubtable4)
bool CheckCmapSubtable4(const void * pCmapSubtable4 /*, unsigned int maxgid*/)
{
if (!pCmapSubtable4) return false;
const Sfnt::CmapSubTable * pTable = reinterpret_cast<const Sfnt::CmapSubTable *>(pCmapSubtable4);
// Bob H says ome freeware TT fonts have version 1 (eg, CALIGULA.TTF)
// Bob H say some freeware TT fonts have version 1 (eg, CALIGULA.TTF)
// so don't check subtable version. 21 Mar 2002 spec changes version to language.
if (be::swap(pTable->format) != 4) return false;
const Sfnt::CmapSubTableFormat4 * pTable4 = reinterpret_cast<const Sfnt::CmapSubTableFormat4 *>(pCmapSubtable4);
@ -889,7 +889,37 @@ bool CheckCmapSubtable4(const void * pCmapSubtable4)
return false;
// check last range is properly terminated
uint16 chEnd = be::peek<uint16>(pTable4->end_code + nRanges - 1);
return (chEnd == 0xFFFF);
if (chEnd != 0xFFFF)
return false;
#if 0
int lastend = -1;
for (int i = 0; i < nRanges; ++i)
{
uint16 end = be::peek<uint16>(pTable4->end_code + i);
uint16 start = be::peek<uint16>(pTable4->end_code + nRanges + 1 + i);
int16 delta = be::peek<int16>(pTable4->end_code + 2*nRanges + 1 + i);
uint16 offset = be::peek<uint16>(pTable4->end_code + 3*nRanges + 1 + i);
if (lastend >= end || lastend >= start)
return false;
if (offset)
{
const uint16 *gstart = pTable4->end_code + 3*nRanges + 1 + i + (offset >> 1);
const uint16 *gend = gstart + end - start;
if ((char *)gend >= (char *)pCmapSubtable4 + length)
return false;
while (gstart <= gend)
{
uint16 g = be::peek<uint16>(gstart++);
if (g && ((g + delta) & 0xFFFF) > maxgid)
return false;
}
}
else if (((delta + end) & 0xFFFF) > maxgid)
return false;
lastend = end;
}
#endif
return true;;
}
/*----------------------------------------------------------------------------------------------
@ -1032,7 +1062,7 @@ unsigned int CmapSubtable4NextCodepoint(const void *pCmap31, unsigned int nUnico
/*----------------------------------------------------------------------------------------------
Check the Microsoft UCS-4 subtable for expected values.
----------------------------------------------------------------------------------------------*/
bool CheckCmapSubtable12(const void *pCmapSubtable12)
bool CheckCmapSubtable12(const void *pCmapSubtable12 /*, unsigned int maxgid*/)
{
if (!pCmapSubtable12) return false;
const Sfnt::CmapSubTable * pTable = reinterpret_cast<const Sfnt::CmapSubTable *>(pCmapSubtable12);
@ -1042,9 +1072,19 @@ bool CheckCmapSubtable12(const void *pCmapSubtable12)
uint32 length = be::swap(pTable12->length);
if (length < sizeof(Sfnt::CmapSubTableFormat12))
return false;
return (length == (sizeof(Sfnt::CmapSubTableFormat12) + (be::swap(pTable12->num_groups) - 1)
* sizeof(uint32) * 3));
uint32 num_groups = be::swap(pTable12->num_groups);
if (length != (sizeof(Sfnt::CmapSubTableFormat12) + (num_groups - 1) * sizeof(uint32) * 3))
return false;
#if 0
for (unsigned int i = 0; i < num_groups; ++i)
{
if (be::swap(pTable12->group[i].end_char_code) - be::swap(pTable12->group[i].start_char_code) + be::swap(pTable12->group[i].start_glyph_id) > maxgid)
return false;
if (i > 0 && be::swap(pTable12->group[i].start_char_code) <= be::swap(pTable12->group[i-1].end_char_code))
return false;
}
#endif
return true;
}
/*----------------------------------------------------------------------------------------------
@ -1145,6 +1185,7 @@ size_t LocaLookup(gid16 nGlyphId,
const void * pHead) // throw (std::out_of_range)
{
const Sfnt::FontHeader * pTable = reinterpret_cast<const Sfnt::FontHeader *>(pHead);
size_t res = -2;
// CheckTable verifies the index_to_loc_format is valid
if (be::swap(pTable->index_to_loc_format) == Sfnt::FontHeader::ShortIndexLocFormat)
@ -1152,21 +1193,24 @@ size_t LocaLookup(gid16 nGlyphId,
if (nGlyphId < (lLocaSize >> 1) - 1) // allow sentinel value to be accessed
{
const uint16 * pShortTable = reinterpret_cast<const uint16 *>(pLoca);
return (be::peek<uint16>(pShortTable + nGlyphId) << 1);
res = be::peek<uint16>(pShortTable + nGlyphId) << 1;
if (res == static_cast<size_t>(be::peek<uint16>(pShortTable + nGlyphId + 1) << 1))
return -1;
}
}
if (be::swap(pTable->index_to_loc_format) == Sfnt::FontHeader::LongIndexLocFormat)
else if (be::swap(pTable->index_to_loc_format) == Sfnt::FontHeader::LongIndexLocFormat)
{ // loca entries are four bytes
if (nGlyphId < (lLocaSize >> 2) - 1)
{
const uint32 * pLongTable = reinterpret_cast<const uint32 *>(pLoca);
return be::peek<uint32>(pLongTable + nGlyphId);
res = be::peek<uint32>(pLongTable + nGlyphId);
if (res == static_cast<size_t>(be::peek<uint32>(pLongTable + nGlyphId + 1)))
return -1;
}
}
// only get here if glyph id was bad
return -1;
return res;
//throw std::out_of_range("glyph id out of range for font");
}
@ -1177,7 +1221,7 @@ size_t LocaLookup(gid16 nGlyphId,
void * GlyfLookup(const void * pGlyf, size_t nGlyfOffset, size_t nTableLen)
{
const uint8 * pByte = reinterpret_cast<const uint8 *>(pGlyf);
if (nGlyfOffset == size_t(-1) || nGlyfOffset >= nTableLen)
if (nGlyfOffset == size_t(-1) || nGlyfOffset == size_t(-2) || nGlyfOffset >= nTableLen)
return NULL;
return const_cast<uint8 *>(pByte + nGlyfOffset);
}

View File

@ -51,15 +51,19 @@ $(_NS)_SOURCES = \
$($(_NS)_BASE)/src/CachedFace.cpp \
$($(_NS)_BASE)/src/CmapCache.cpp \
$($(_NS)_BASE)/src/Code.cpp \
$($(_NS)_BASE)/src/Collider.cpp \
$($(_NS)_BASE)/src/Decompressor.cpp \
$($(_NS)_BASE)/src/Face.cpp \
$($(_NS)_BASE)/src/FeatureMap.cpp \
$($(_NS)_BASE)/src/FileFace.cpp \
$($(_NS)_BASE)/src/Font.cpp \
$($(_NS)_BASE)/src/GlyphCache.cpp \
$($(_NS)_BASE)/src/GlyphFace.cpp \
$($(_NS)_BASE)/src/Intervals.cpp \
$($(_NS)_BASE)/src/Justifier.cpp \
$($(_NS)_BASE)/src/NameTable.cpp \
$($(_NS)_BASE)/src/Pass.cpp \
$($(_NS)_BASE)/src/Position.cpp \
$($(_NS)_BASE)/src/SegCache.cpp \
$($(_NS)_BASE)/src/SegCacheEntry.cpp \
$($(_NS)_BASE)/src/SegCacheStore.cpp \
@ -74,10 +78,14 @@ $(_NS)_PRIVATE_HEADERS = \
$($(_NS)_BASE)/src/inc/bits.h \
$($(_NS)_BASE)/src/inc/debug.h \
$($(_NS)_BASE)/src/inc/json.h \
$($(_NS)_BASE)/src/inc/locale2lcid.h \
$($(_NS)_BASE)/src/inc/Bidi.h \
$($(_NS)_BASE)/src/inc/CachedFace.h \
$($(_NS)_BASE)/src/inc/CharInfo.h \
$($(_NS)_BASE)/src/inc/CmapCache.h \
$($(_NS)_BASE)/src/inc/Code.h \
$($(_NS)_BASE)/src/inc/Collider.h \
$($(_NS)_BASE)/src/inc/Decompressor.h \
$($(_NS)_BASE)/src/inc/Endian.h \
$($(_NS)_BASE)/src/inc/Error.h \
$($(_NS)_BASE)/src/inc/Face.h \
@ -87,6 +95,7 @@ $(_NS)_PRIVATE_HEADERS = \
$($(_NS)_BASE)/src/inc/Font.h \
$($(_NS)_BASE)/src/inc/GlyphCache.h \
$($(_NS)_BASE)/src/inc/GlyphFace.h \
$($(_NS)_BASE)/src/inc/Intervals.h \
$($(_NS)_BASE)/src/inc/List.h \
$($(_NS)_BASE)/src/inc/locale2lcid.h \
$($(_NS)_BASE)/src/inc/Machine.h \
@ -101,6 +110,7 @@ $(_NS)_PRIVATE_HEADERS = \
$($(_NS)_BASE)/src/inc/SegCacheEntry.h \
$($(_NS)_BASE)/src/inc/SegCacheStore.h \
$($(_NS)_BASE)/src/inc/Segment.h \
$($(_NS)_BASE)/src/inc/Shrinker.h \
$($(_NS)_BASE)/src/inc/Silf.h \
$($(_NS)_BASE)/src/inc/Slot.h \
$($(_NS)_BASE)/src/inc/Sparse.h \

View File

@ -46,7 +46,7 @@ namespace
#ifdef GRAPHITE2_TELEMETRY
telemetry::category _misc_cat(face.tele.misc);
#endif
Face::Table silf(face, Tag::Silf);
Face::Table silf(face, Tag::Silf, 0x00050000);
if (silf) options &= ~gr_face_dumbRendering;
else if (!(options & gr_face_dumbRendering))
return false;

View File

@ -32,6 +32,7 @@ of the License or (at your option) any later version.
#include "inc/Slot.h"
#include "inc/Segment.h"
#include "inc/json.h"
#include "inc/Collider.h"
#if defined _WIN32
#include "windows.h"
@ -184,6 +185,7 @@ json & graphite2::operator << (json & j, const dslot & ds) throw()
assert(ds.second);
const Segment & seg = *ds.first;
const Slot & s = *ds.second;
const SlotCollision *cslot = seg.collisionInfo(ds.second);
j << json::object
<< "id" << objectid(ds)
@ -220,6 +222,28 @@ json & graphite2::operator << (json & j, const dslot & ds) throw()
j << objectid(dslot(&seg, c));
j << json::close;
}
if (cslot)
{
// Note: the reason for using Positions to lump together related attributes is to make the
// JSON output slightly more compact.
j << "collision" << json::flat << json::object
// << "shift" << cslot->shift() -- not used pass level, only within the collision routine itself
<< "offset" << cslot->offset()
<< "limit" << cslot->limit()
<< "flags" << cslot->flags()
<< "margin" << Position(cslot->margin(), cslot->marginWt())
<< "exclude" << cslot->exclGlyph()
<< "excludeoffset" << cslot->exclOffset();
if (cslot->seqOrder() != 0)
{
j << "seqclass" << Position(cslot->seqClass(), cslot->seqProxClass())
<< "seqorder" << cslot->seqOrder()
<< "seqabove" << Position(cslot->seqAboveXoff(), cslot->seqAboveWt())
<< "seqbelow" << Position(cslot->seqBelowXlim(), cslot->seqBelowWt())
<< "seqvalign" << Position(cslot->seqValignHt(), cslot->seqValignWt());
}
j << json::close;
}
return j << json::close;
}

View File

@ -48,10 +48,6 @@ namespace
delete pRes;
return NULL;
}
// run the line break passes
// run the substitution passes
pRes->prepare_pos(font);
// run the positioning passes
pRes->finalise(font);
return static_cast<gr_segment*>(pRes);

View File

@ -103,11 +103,11 @@ float gr_slot_advance_X(const gr_slot* p/*not NULL*/, const gr_face *face, const
return res;
}
float gr_slot_advance_Y(const gr_slot *p/*not NULL*/, const gr_face *face, const gr_font *font)
float gr_slot_advance_Y(const gr_slot *p/*not NULL*/, GR_MAYBE_UNUSED const gr_face *face, const gr_font *font)
{
assert(p);
float res = p->advancePos().y;
if (font && (face || !face))
if (font)
return res * font->scale();
else
return res;

View File

@ -41,6 +41,14 @@ namespace graphite2 {
class Silf;
class Face;
enum passtype {
PASS_TYPE_UNKNOWN = 0,
PASS_TYPE_LINEBREAK,
PASS_TYPE_SUBSTITUTE,
PASS_TYPE_POSITIONING,
PASS_TYPE_JUSTIFICATION
};
namespace vm {
class Machine::Code
@ -56,10 +64,12 @@ public:
jump_past_end,
arguments_exhausted,
missing_return,
nested_context_item
nested_context_item,
underfull_stack
};
private:
static byte * local_memory;
class decoder;
instr * _code;
@ -79,7 +89,8 @@ private:
public:
Code() throw();
Code(bool is_constraint, const byte * bytecode_begin, const byte * const bytecode_end,
uint8 pre_context, uint16 rule_length, const Silf &, const Face &);
uint8 pre_context, uint16 rule_length, const Silf &, const Face &,
enum passtype pt, byte * & _out = local_memory);
Code(const Machine::Code &) throw();
~Code() throw();
@ -92,12 +103,14 @@ public:
bool immutable() const throw();
bool deletes() const throw();
size_t maxRef() const throw();
void externalProgramMoved(ptrdiff_t) throw();
int32 run(Machine &m, slotref * & map) const;
CLASS_NEW_DELETE;
};
inline Machine::Code::Code() throw()
: _code(0), _data(0), _data_size(0), _instr_count(0), _max_ref(0),
_status(loaded), _constraint(false), _modify(false),_delete(false),
@ -171,5 +184,14 @@ inline size_t Machine::Code::maxRef() const throw()
return _max_ref;
}
inline void Machine::Code::externalProgramMoved(ptrdiff_t dist) throw()
{
if (_code && !_own)
{
_code += dist / sizeof(instr);
_data += dist;
}
}
} // namespace vm
} // namespace graphite2

View File

@ -0,0 +1,220 @@
/* GRAPHITE2 LICENSING
Copyright 2010, SIL International
All rights reserved.
This library is free software; you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as published
by the Free Software Foundation; either version 2.1 of License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should also have received a copy of the GNU Lesser General Public
License along with this library in the file named "LICENSE".
If not, write to the Free Software Foundation, 51 Franklin Street,
Suite 500, Boston, MA 02110-1335, USA or visit their web page on the
internet at http://www.fsf.org/licenses/lgpl.html.
Alternatively, the contents of this file may be used under the terms of the
Mozilla Public License (http://mozilla.org/MPL) or the GNU General Public
License, as published by the Free Software Foundation, either version 2
of the License or (at your option) any later version.
*/
#pragma once
#include <utility>
#include "inc/List.h"
#include "inc/Slot.h"
#include "inc/Position.h"
#include "inc/Intervals.h"
#include "inc/debug.h"
//#include "inc/Segment.h"
namespace graphite2 {
class json;
class Slot;
class Segment;
#define SLOTCOLSETUINTPROP(x, y) uint16 x() const { return _ ##x; } void y (uint16 v) { _ ##x = v; }
#define SLOTCOLSETINTPROP(x, y) int16 x() const { return _ ##x; } void y (int16 v) { _ ##x = v; }
#define SLOTCOLSETPOSITIONPROP(x, y) const Position &x() const { return _ ##x; } void y (const Position &v) { _ ##x = v; }
// Slot attributes related to collision-fixing
class SlotCollision
{
public:
enum {
// COLL_TESTONLY = 0, // default - test other glyphs for collision with this one, but don't move this one
COLL_FIX = 1, // fix collisions involving this glyph
COLL_IGNORE = 2, // ignore this glyph altogether
COLL_START = 4, // start of range of possible collisions
COLL_END = 8, // end of range of possible collisions
COLL_KERN = 16, // collisions with this glyph are fixed by adding kerning space after it
COLL_ISCOL = 32, // this glyph has a collision
COLL_KNOWN = 64, // we've figured out what's happening with this glyph
COLL_TEMPLOCK = 128, // Lock glyphs that have been given priority positioning
////COLL_JUMPABLE = 128, // moving glyphs may jump this stationary glyph in any direction - DELETE
////COLL_OVERLAP = 256, // use maxoverlap to restrict - DELETE
};
// Behavior for the collision.order attribute. To GDL this is an enum, to us it's a bitfield, with only 1 bit set
// Allows for easier inversion.
enum {
SEQ_ORDER_LEFTDOWN = 1,
SEQ_ORDER_RIGHTUP = 2,
SEQ_ORDER_NOABOVE = 4,
SEQ_ORDER_NOBELOW = 8,
SEQ_ORDER_NOLEFT = 16,
SEQ_ORDER_NORIGHT = 32
};
SlotCollision(Segment *seg, Slot *slot);
void initFromSlot(Segment *seg, Slot *slot);
const Rect &limit() const { return _limit; }
void setLimit(const Rect &r) { _limit = r; }
SLOTCOLSETPOSITIONPROP(shift, setShift)
SLOTCOLSETPOSITIONPROP(offset, setOffset)
SLOTCOLSETPOSITIONPROP(exclOffset, setExclOffset)
SLOTCOLSETUINTPROP(margin, setMargin)
SLOTCOLSETUINTPROP(marginWt, setMarginWt)
SLOTCOLSETUINTPROP(flags, setFlags)
SLOTCOLSETUINTPROP(exclGlyph, setExclGlyph)
SLOTCOLSETUINTPROP(seqClass, setSeqClass)
SLOTCOLSETUINTPROP(seqProxClass, setSeqProxClass)
SLOTCOLSETUINTPROP(seqOrder, setSeqOrder)
SLOTCOLSETINTPROP(seqAboveXoff, setSeqAboveXoff)
SLOTCOLSETUINTPROP(seqAboveWt, setSeqAboveWt)
SLOTCOLSETINTPROP(seqBelowXlim, setSeqBelowXlim)
SLOTCOLSETUINTPROP(seqBelowWt, setSeqBelowWt)
SLOTCOLSETUINTPROP(seqValignHt, setSeqValignHt)
SLOTCOLSETUINTPROP(seqValignWt, setSeqValignWt)
float getKern(int dir) const;
private:
Rect _limit;
Position _shift; // adjustment within the given pass
Position _offset; // total adjustment for collisions
Position _exclOffset;
uint16 _margin;
uint16 _marginWt;
uint16 _flags;
uint16 _exclGlyph;
uint16 _seqClass;
uint16 _seqProxClass;
uint16 _seqOrder;
int16 _seqAboveXoff;
uint16 _seqAboveWt;
int16 _seqBelowXlim;
uint16 _seqBelowWt;
uint16 _seqValignHt;
uint16 _seqValignWt;
}; // end of class SlotColllision
class BBox;
class SlantBox;
class ShiftCollider
{
public:
typedef std::pair<float, float> fpair;
typedef Vector<fpair> vfpairs;
typedef vfpairs::iterator ivfpairs;
ShiftCollider(GR_MAYBE_UNUSED json *dbgout)
{
#if !defined GRAPHITE2_NTRACING
for (int i = 0; i < 4; ++i)
_ranges[i].setdebug(dbgout);
#endif
}
~ShiftCollider() throw() { };
bool initSlot(Segment *seg, Slot *aSlot, const Rect &constraint,
float margin, float marginMin, const Position &currShift,
const Position &currOffset, int dir, GR_MAYBE_UNUSED json * const dbgout);
bool mergeSlot(Segment *seg, Slot *slot, const Position &currShift, bool isAfter,
bool sameCluster, bool &hasCol, bool isExclusion, GR_MAYBE_UNUSED json * const dbgout);
Position resolve(Segment *seg, bool &isCol, GR_MAYBE_UNUSED json * const dbgout);
void addBox_slope(bool isx, const Rect &box, const BBox &bb, const SlantBox &sb, const Position &org, float weight, float m, bool minright, int mode);
void removeBox(const Rect &box, const BBox &bb, const SlantBox &sb, const Position &org, int mode);
const Position &origin() const { return _origin; }
#if !defined GRAPHITE2_NTRACING
void outputJsonDbg(json * const dbgout, Segment *seg, int axis);
void outputJsonDbgStartSlot(json * const dbgout, Segment *seg);
void outputJsonDbgEndSlot(json * const dbgout, Position resultPos, int bestAxis, bool isCol);
void outputJsonDbgOneVector(json * const dbgout, Segment *seg, int axis, float tleft, float bestCost, float bestVal);
void outputJsonDbgRawRanges(json * const dbgout, int axis);
void outputJsonDbgRemovals(json * const dbgout, int axis, Segment *seg);
#endif
CLASS_NEW_DELETE;
protected:
Zones _ranges[4]; // possible movements in 4 directions (horizontally, vertically, diagonally);
Slot * _target; // the glyph to fix
Rect _limit;
Position _currShift;
Position _currOffset;
Position _origin; // Base for all relative calculations
float _margin;
float _marginWt;
float _len[4];
uint16 _seqClass;
uint16 _seqProxClass;
uint16 _seqOrder;
//bool _scraping[4];
}; // end of class ShiftCollider
class KernCollider
{
public:
KernCollider(GR_MAYBE_UNUSED json *dbg) : _miny(-1e38f), _maxy(1e38f) { };
~KernCollider() throw() { };
bool initSlot(Segment *seg, Slot *aSlot, const Rect &constraint, float margin,
const Position &currShift, const Position &offsetPrev, int dir,
float ymin, float ymax, json * const dbgout);
bool mergeSlot(Segment *seg, Slot *slot, const Position &currShift, float currSpace, int dir, json * const dbgout);
Position resolve(Segment *seg, Slot *slot, int dir, float margin, json * const dbgout);
void shift(const Position &mv, int dir);
CLASS_NEW_DELETE;
private:
Slot * _target; // the glyph to fix
Rect _limit;
float _margin;
Position _offsetPrev; // kern from a previous pass
Position _currShift; // NOT USED??
float _miny; // y-coordinates offset by global slot position
float _maxy;
Vector<float> _edges; // edges of horizontal slices
float _sliceWidth; // width of each slice
float _mingap;
float _xbound; // max or min edge
#if !defined GRAPHITE2_NTRACING
// Debugging
Segment * _seg;
Vector<float> _nearEdges; // closest potential collision in each slice
Vector<Slot*> _slotNear;
#endif
}; // end of class KernCollider
inline
float sqr(float x) { return x * x; }
}; // end of namespace graphite2

View File

@ -0,0 +1,52 @@
/* Copyright (c) 2012, Siyuan Fu <fusiyuan2010@gmail.com>
Copyright (c) 2015, SIL International
All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:
1. Redistributions of source code must retain the above copyright notice,
this list of conditions and the following disclaimer.
2. 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.
3. Neither the name of the copyright holder 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 HOLDER 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.
*/
#pragma once
#include <cstddef>
namespace shrinker
{
int decompress(void const *in, size_t in_size, void *out, size_t out_size);
/*
in: inbuf --- compressed data
out: outbuf --- decompressed data to place in
size: decompressed(original) data size should be
return value:
positive integer means decompress success and it's the sizeof decompressed data,
which should be equal to size.
or -1 means decompress failed
*/
} // end of namespace shrinker

View File

@ -111,16 +111,23 @@ enum errors {
E_BADRULEMAPPING = 50, // The structure of the rule mapping is bad
E_BADRANGE = 51, // Bad column range structure including a glyph in more than one column
E_BADRULENUM = 52, // A reference to a rule is out of range (too high)
E_BADACOLLISION = 53, // Bad Silf table collision attribute number (too high)
E_BADEMPTYPASS = 54, // Can't have empty passes (no rules) except for collision passes
E_BADSILFVERSION = 55, // The Silf table has a bad version (probably too high)
E_BADCOLLISIONPASS = 56, // Collision flags set on a non positioning pass
// Code errors
E_CODEFAILURE = 60, // Base code error. The following subcodes must align with Machine::Code::status_t in Code.h
E_CODEALLOC = 61, // Out of memory
E_INVALIDOPCODE = 62, // Invalid op code
E_UNIMPOPCODE = 63, // Unimplemented op code encountered
E_OUTOFRANGECODE = 64, // Code argument out of range
E_BADJUMPCODE = 65, // Code jumps past end of op codes
E_CODEBADARGS = 66, // Code arguments exhausted
E_CODENORETURN = 67, // Missing return type op code at end of code
E_CODENESTEDCTXT = 68 // Nested context encountered in code
E_CODEALLOC = 61, // Out of memory
E_INVALIDOPCODE = 62, // Invalid op code
E_UNIMPOPCODE = 63, // Unimplemented op code encountered
E_OUTOFRANGECODE = 64, // Code argument out of range
E_BADJUMPCODE = 65, // Code jumps past end of op codes
E_CODEBADARGS = 66, // Code arguments exhausted
E_CODENORETURN = 67, // Missing return type op code at end of code
E_CODENESTEDCTXT = 68, // Nested context encountered in code
// Compression errors
E_BADSCHEME = 69,
E_SHRINKERFAILED = 70,
};
}

View File

@ -170,10 +170,13 @@ class Face::Table
const Face * _f;
mutable const byte * _p;
uint32 _sz;
bool _compressed;
Error decompress();
public:
Table() throw();
Table(const Face & face, const Tag n) throw();
Table(const Face & face, const Tag n, uint32 version=0xffffffff) throw();
Table(const Table & rhs) throw();
~Table() throw();
@ -185,13 +188,13 @@ public:
inline
Face::Table::Table() throw()
: _f(0), _p(0), _sz(0)
: _f(0), _p(0), _sz(0), _compressed(false)
{
}
inline
Face::Table::Table(const Table & rhs) throw()
: _f(rhs._f), _p(rhs._p), _sz(rhs._sz)
: _f(rhs._f), _p(rhs._p), _sz(rhs._sz), _compressed(rhs._compressed)
{
rhs._p = 0;
}
@ -199,7 +202,9 @@ Face::Table::Table(const Table & rhs) throw()
inline
Face::Table::~Table() throw()
{
if (_p && _f->m_ops.release_table)
if (_compressed)
free(const_cast<byte *>(_p));
else if (_p && _f->m_ops.release_table)
(*_f->m_ops.release_table)(_f->m_appFaceHandle, _p);
}

View File

@ -117,9 +117,8 @@ class NameAndFeatureRef
class FeatureMap
{
public:
FeatureMap() : m_numFeats(0), m_feats(NULL), m_pNamedFeats(NULL),
m_defaultFeatures(NULL) {}
~FeatureMap() { delete [] m_feats; delete[] m_pNamedFeats; delete m_defaultFeatures; }
FeatureMap() : m_numFeats(0), m_feats(NULL), m_pNamedFeats(NULL) {}
~FeatureMap() { delete [] m_feats; delete[] m_pNamedFeats; }
bool readFeats(const Face & face);
const FeatureRef *findFeatureRef(uint32 name) const;
@ -135,7 +134,7 @@ friend class SillMap;
FeatureRef *m_feats;
NameAndFeatureRef* m_pNamedFeats; //owned
FeatureVal* m_defaultFeatures; //owned
FeatureVal m_defaultFeatures; //owned
private: //defensive on m_feats, m_pNamedFeats, and m_defaultFeatures
FeatureMap(const FeatureMap&);

View File

@ -29,14 +29,65 @@ of the License or (at your option) any later version.
#include "graphite2/Font.h"
#include "inc/Main.h"
#include "inc/Position.h"
#include "inc/GlyphFace.h"
namespace graphite2 {
class Face;
class FeatureVal;
class GlyphFace;
class Segment;
class SlantBox
{
public:
SlantBox(float psi = 0., float pdi = 0., float psa = 0., float pda = 0.) : si(psi), di(pdi), sa(psa), da(pda) {};
float width() const { return sa - si; }
float height() const { return da - di; }
float si; // min
float di; // min
float sa; // max
float da; // max
};
static SlantBox nullSlant(0, 0, 0, 0);
class BBox
{
public:
BBox(float pxi = 0, float pyi = 0., float pxa = 0., float pya = 0.) : xi(pxi), yi(pyi), xa(pxa), ya(pya) {};
float width() const { return xa - xi; }
float height() const { return ya - yi; }
float xi; // min
float yi; // min
float xa; // max
float ya; // max
};
static BBox nullBBox(0, 0, 0, 0);
class GlyphBox
{
GlyphBox(const GlyphBox &);
GlyphBox & operator = (const GlyphBox &);
public:
GlyphBox(uint8 numsubs, unsigned short bitmap, Rect *slanted) : _num(numsubs), _bitmap(bitmap), _slant(*slanted) {};
void addSubBox(int subindex, int boundary, Rect *val) { _subs[subindex * 2 + boundary] = *val; }
Rect &subVal(int subindex, int boundary) { return _subs[subindex * 2 + boundary]; }
const Rect &slant() const { return _slant; }
uint8 num() const { return _num; }
const Rect *subs() const { return _subs; }
private:
uint8 _num;
unsigned short _bitmap;
Rect _slant;
Rect _subs[1];
};
class GlyphCache
{
class Loader;
@ -44,6 +95,8 @@ class GlyphCache
GlyphCache(const GlyphCache&);
GlyphCache& operator=(const GlyphCache&);
static const Rect nullRect;
public:
GlyphCache(const Face & face, const uint32 face_options);
~GlyphCache();
@ -54,12 +107,22 @@ public:
const GlyphFace *glyph(unsigned short glyphid) const; //result may be changed by subsequent call with a different glyphid
const GlyphFace *glyphSafe(unsigned short glyphid) const;
float getBoundingMetric(unsigned short glyphid, uint8 metric) const;
uint8 numSubBounds(unsigned short glyphid) const;
float getSubBoundingMetric(unsigned short glyphid, uint8 subindex, uint8 metric) const;
const Rect & slant(unsigned short glyphid) const { return _boxes[glyphid] ? _boxes[glyphid]->slant() : nullRect; }
const SlantBox & getBoundingSlantBox(unsigned short glyphid) const;
const BBox & getBoundingBBox(unsigned short glyphid) const;
const SlantBox & getSubBoundingSlantBox(unsigned short glyphid, uint8 subindex) const;
const BBox & getSubBoundingBBox(unsigned short glyphid, uint8 subindex) const;
bool check(unsigned short glyphid) const;
CLASS_NEW_DELETE;
private:
const Loader * _glyph_loader;
const GlyphFace * * _glyphs;
GlyphBox * * _boxes;
unsigned short _num_glyphs,
_num_attrs,
_upem;
@ -83,10 +146,82 @@ unsigned short GlyphCache::unitsPerEm() const throw()
return _upem;
}
inline
bool GlyphCache::check(unsigned short glyphid) const
{
return glyphid < _num_glyphs;
}
inline
const GlyphFace *GlyphCache::glyphSafe(unsigned short glyphid) const
{
return glyphid < _num_glyphs ? glyph(glyphid) : NULL;
}
inline
float GlyphCache::getBoundingMetric(unsigned short glyphid, uint8 metric) const
{
if (glyphid >= _num_glyphs) return 0.;
switch (metric) {
case 0: return (float)(glyph(glyphid)->theBBox().bl.x); // x_min
case 1: return (float)(glyph(glyphid)->theBBox().bl.y); // y_min
case 2: return (float)(glyph(glyphid)->theBBox().tr.x); // x_max
case 3: return (float)(glyph(glyphid)->theBBox().tr.y); // y_max
case 4: return (float)(_boxes[glyphid] ? _boxes[glyphid]->slant().bl.x : 0.f); // sum_min
case 5: return (float)(_boxes[glyphid] ? _boxes[glyphid]->slant().bl.y : 0.f); // diff_min
case 6: return (float)(_boxes[glyphid] ? _boxes[glyphid]->slant().tr.x : 0.f); // sum_max
case 7: return (float)(_boxes[glyphid] ? _boxes[glyphid]->slant().tr.y : 0.f); // diff_max
default: return 0.;
}
}
inline const SlantBox &GlyphCache::getBoundingSlantBox(unsigned short glyphid) const
{
return _boxes[glyphid] ? *(SlantBox *)(&(_boxes[glyphid]->slant())) : nullSlant;
}
inline const BBox &GlyphCache::getBoundingBBox(unsigned short glyphid) const
{
return *(BBox *)(&(glyph(glyphid)->theBBox()));
}
inline
float GlyphCache::getSubBoundingMetric(unsigned short glyphid, uint8 subindex, uint8 metric) const
{
GlyphBox *b = _boxes[glyphid];
if (b == NULL || subindex >= b->num()) return 0;
switch (metric) {
case 0: return b->subVal(subindex, 0).bl.x;
case 1: return b->subVal(subindex, 0).bl.y;
case 2: return b->subVal(subindex, 0).tr.x;
case 3: return b->subVal(subindex, 0).tr.y;
case 4: return b->subVal(subindex, 1).bl.x;
case 5: return b->subVal(subindex, 1).bl.y;
case 6: return b->subVal(subindex, 1).tr.x;
case 7: return b->subVal(subindex, 1).tr.y;
default: return 0.;
}
}
inline const SlantBox &GlyphCache::getSubBoundingSlantBox(unsigned short glyphid, uint8 subindex) const
{
GlyphBox *b = _boxes[glyphid];
// if (b == NULL || subindex >= b->num()) return nullSlant;
return *(SlantBox *)(b->subs() + 2 * subindex + 1);
}
inline const BBox &GlyphCache::getSubBoundingBBox(unsigned short glyphid, uint8 subindex) const
{
GlyphBox *b = _boxes[glyphid];
// if (b == NULL || subindex >= b->num()) return nullBBox;
return *(BBox *)(b->subs() + 2 * subindex);
}
inline
uint8 GlyphCache::numSubBounds(unsigned short glyphid) const
{
return _boxes[glyphid] ? _boxes[glyphid]->num() : 0;
}
} // namespace graphite2

View File

@ -0,0 +1,231 @@
/* GRAPHITE2 LICENSING
Copyright 2010, SIL International
All rights reserved.
This library is free software; you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as published
by the Free Software Foundation; either version 2.1 of License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should also have received a copy of the GNU Lesser General Public
License along with this library in the file named "LICENSE".
If not, write to the Free Software Foundation, 51 Franklin Street,
Suite 500, Boston, MA 02110-1335, USA or visit their web page on the
internet at http://www.fsf.org/licenses/lgpl.html.
Alternatively, the contents of this file may be used under the terms of the
Mozilla Public License (http://mozilla.org/MPL) or the GNU General Public
License, as published by the Free Software Foundation, either version 2
of the License or (at your option) any later version.
*/
#pragma once
#include <utility>
#include "inc/Main.h"
#include "inc/List.h"
#include "inc/json.h"
#include "inc/Position.h"
// An IntervalSet represents the possible movement of a given glyph in a given direction
// (horizontally, vertically, or diagonally).
// A vector is needed to represent disjoint ranges, eg, -300..-150, 20..200, 500..750.
// Each pair represents the min/max of a sub-range.
namespace graphite2 {
class Segment;
enum zones_t {SD, XY};
class Zones
{
struct Exclusion
{
template<zones_t O>
static Exclusion weighted(float xmin, float xmax, float f, float a0,
float m, float xi, float ai, float c, bool nega);
float x, // x position
xm, // xmax position
c, // constant + sum(MiXi^2)
sm, // sum(Mi)
smx; // sum(MiXi)
bool open;
Exclusion(float x, float w, float smi, float smxi, float c);
Exclusion & operator += (Exclusion const & rhs);
uint8 outcode(float p) const;
Exclusion split_at(float p);
void left_trim(float p);
bool track_cost(float & cost, float & x, float origin) const;
private:
float test_position(float origin) const;
float cost(float x) const;
};
typedef Vector<Exclusion> exclusions;
typedef exclusions::iterator iterator;
typedef Exclusion * pointer;
typedef Exclusion & reference;
typedef std::reverse_iterator<iterator> reverse_iterator;
public:
typedef exclusions::const_iterator const_iterator;
typedef Exclusion const * const_pointer;
typedef Exclusion const & const_reference;
typedef std::reverse_iterator<const_iterator> const_reverse_iterator;
#if !defined GRAPHITE2_NTRACING
struct Debug
{
Exclusion _excl;
bool _isdel;
Vector<void *> _env;
Debug(Exclusion *e, bool isdel, json *dbg) : _excl(*e), _isdel(isdel), _env(dbg->getenvs()) { };
};
typedef Vector<Debug> debugs;
typedef debugs::const_iterator idebugs;
void addDebug(Exclusion *e);
void removeDebug(float pos, float posm);
void setdebug(json *dbgout) { _dbg = dbgout; }
idebugs dbgs_begin() const { return _dbgs.begin(); }
idebugs dbgs_end() const { return _dbgs.end(); }
void jsonDbgOut(Segment *seg) const;
Position position() const { return Position(_pos, _posm); }
#endif
Zones();
template<zones_t O>
void initialise(float xmin, float xmax, float margin_len, float margin_weight, float ao);
void exclude(float xmin, float xmax);
void exclude_with_margins(float xmin, float xmax, int axis);
template<zones_t O>
void weighted(float xmin, float xmax, float f, float a0, float mi, float xi, float ai, float c, bool nega);
void weightedAxis(int axis, float xmin, float xmax, float f, float a0, float mi, float xi, float ai, float c, bool nega);
float closest( float origin, float &cost) const;
const_iterator begin() const { return _exclusions.begin(); }
const_iterator end() const { return _exclusions.end(); }
private:
exclusions _exclusions;
#if !defined GRAPHITE2_NTRACING
json * _dbg;
debugs _dbgs;
#endif
float _margin_len,
_margin_weight,
_pos,
_posm;
void insert(Exclusion e);
void remove(float x, float xm);
const_iterator find_exclusion_under(float x) const;
};
inline
Zones::Zones()
: _margin_len(0), _margin_weight(0), _pos(0), _posm(0)
{
_exclusions.reserve(8);
}
inline
Zones::Exclusion::Exclusion(float x_, float xm_, float smi, float smxi, float c_)
: x(x_), xm(xm_), c(c_), sm(smi), smx(smxi), open(false)
{ }
template<zones_t O>
inline
void Zones::initialise(float xmin, float xmax, float margin_len,
float margin_weight, float a0)
{
_margin_len = margin_len;
_margin_weight = margin_weight;
_pos = xmin;
_posm = xmax;
_exclusions.clear();
_exclusions.push_back(Exclusion::weighted<O>(xmin, xmax, 1, a0, 0, 0, 0, 0, false));
_exclusions.front().open = true;
#if !defined GRAPHITE2_NTRACING
_dbgs.clear();
#endif
}
inline
void Zones::exclude(float xmin, float xmax) {
remove(xmin, xmax);
}
template<zones_t O>
inline
void Zones::weighted(float xmin, float xmax, float f, float a0,
float m, float xi, float ai, float c, bool nega) {
insert(Exclusion::weighted<O>(xmin, xmax, f, a0, m, xi, ai, c, nega));
}
inline
void Zones::weightedAxis(int axis, float xmin, float xmax, float f, float a0,
float m, float xi, float ai, float c, bool nega) {
if (axis < 2)
weighted<XY>(xmin, xmax, f, a0, m, xi, ai, c, nega);
else
weighted<SD>(xmin, xmax, f, a0, m, xi, ai, c, nega);
}
#if !defined GRAPHITE2_NTRACING
inline
void Zones::addDebug(Exclusion *e) {
if (_dbg)
_dbgs.push_back(Debug(e, false, _dbg));
}
inline
void Zones::removeDebug(float pos, float posm) {
if (_dbg)
{
Exclusion e(pos, posm, 0, 0, 0);
_dbgs.push_back(Debug(&e, true, _dbg));
}
}
#endif
template<>
inline
Zones::Exclusion Zones::Exclusion::weighted<XY>(float xmin, float xmax, float f, float a0,
float m, float xi, GR_MAYBE_UNUSED float ai, float c, GR_MAYBE_UNUSED bool nega) {
return Exclusion(xmin, xmax,
m + f,
m * xi,
m * xi * xi + f * a0 * a0 + c);
}
template<>
inline
Zones::Exclusion Zones::Exclusion::weighted<SD>(float xmin, float xmax, float f, float a0,
float m, float xi, float ai,float c, bool nega) {
float xia = nega ? xi - ai : xi + ai;
return Exclusion(xmin, xmax,
0.25f * (m + 2.f * f),
0.25f * m * xia,
0.25f * (m * xia * xia + 2.f * f * a0 * a0) + c);
}
} // end of namespace graphite2

View File

@ -70,6 +70,7 @@ public:
size_t capacity() const{ return m_end - m_first; }
void reserve(size_t n);
void resize(size_t n, const T & v = T());
reference front() { assert(size() > 0); return *begin(); }
const_reference front() const { assert(size() > 0); return *begin(); }
@ -82,7 +83,7 @@ public:
void assign(size_t n, const T& u) { clear(); insert(begin(), n, u); }
void assign(const_iterator first, const_iterator last) { clear(); insert(begin(), first, last); }
iterator insert(iterator p, const T & x) { p = _insert_default(p, 1); *p = x; return p; }
iterator insert(iterator p, const T & x) { p = _insert_default(p, 1); new (p) T(x); return p; }
void insert(iterator p, size_t n, const T & x);
void insert(iterator p, const_iterator first, const_iterator last);
void pop_back() { assert(size() > 0); --m_last; }
@ -109,13 +110,21 @@ void Vector<T>::reserve(size_t n)
}
}
template <typename T>
inline
void Vector<T>::resize(size_t n, const T & v) {
const ptrdiff_t d = n-size();
if (d < 0) erase(end()+d, end());
else if (d > 0) insert(end(), d, v);
}
template<typename T>
inline
typename Vector<T>::iterator Vector<T>::_insert_default(iterator p, size_t n)
{
assert(begin() <= p && p <= end());
const ptrdiff_t i = p - begin();
reserve((size() + n + 7) >> 3 << 3);
reserve(((size() + n + 7) >> 3) << 3);
p = begin() + i;
// Move tail if there is one
if (p != end()) memmove(p + n, p, distance(p,end())*sizeof(T));

View File

@ -110,6 +110,8 @@ enum opcode {
PUSH_PROC_STATE, PUSH_VERSION,
PUT_SUBS, PUT_SUBS2, PUT_SUBS3,
PUT_GLYPH, PUSH_GLYPH_ATTR, PUSH_ATT_TO_GLYPH_ATTR,
BITOR, BITAND, BITNOT,
BITSET, SET_FEAT,
MAX_OPCODE,
// private opcodes for internal use only, comes after all other on disk opcodes
TEMP_COPY = MAX_OPCODE
@ -148,7 +150,7 @@ public:
SlotMap & slotMap() const throw();
status_t status() const throw();
operator bool () const throw();
// operator bool () const throw();
private:
void check_final_stack(const stack_t * const sp);

View File

@ -125,3 +125,8 @@ inline T max(const T a, const T b)
#else
#define GR_MAYBE_UNUSED
#endif
#ifdef _MSC_VER
#pragma warning(disable: 4800)
#pragma warning(once: 4355)
#endif

View File

@ -39,6 +39,11 @@ struct RuleEntry;
struct State;
class FiniteStateMachine;
class Error;
class ShiftCollider;
class KernCollider;
class json;
enum passtype;
class Pass
{
@ -46,10 +51,11 @@ public:
Pass();
~Pass();
bool readPass(const byte * pPass, size_t pass_length, size_t subtable_base, Face & face, Error &e);
void runGraphite(vm::Machine & m, FiniteStateMachine & fsm) const;
bool readPass(const byte * pPass, size_t pass_length, size_t subtable_base, Face & face,
enum passtype pt, uint32 version, Error &e);
bool runGraphite(vm::Machine & m, FiniteStateMachine & fsm) const;
void init(Silf *silf) { m_silf = silf; }
byte spaceContextuals() const { return (m_flags & 0x0E) >> 1; }
byte flags() const { return m_flags; }
CLASS_NEW_DELETE
private:
@ -61,7 +67,7 @@ private:
const byte *precontext, const uint16 * sort_key,
const uint16 * o_constraint, const byte *constraint_data,
const uint16 * o_action, const byte * action_data,
Face &, Error &e);
Face &, enum passtype pt, Error &e);
bool readStates(const byte * starts, const byte * states, const byte * o_rule_map, Face &, Error &e);
bool readRanges(const byte * ranges, size_t num_ranges, Error &e);
uint16 glyphToCol(const uint16 gid) const;
@ -69,13 +75,23 @@ private:
void dumpRuleEventConsidered(const FiniteStateMachine & fsm, const RuleEntry & re) const;
void dumpRuleEventOutput(const FiniteStateMachine & fsm, const Rule & r, Slot * os) const;
void adjustSlot(int delta, Slot * & slot_out, SlotMap &) const;
const Silf* m_silf;
uint16 * m_cols;
Rule * m_rules; // rules
RuleEntry * m_ruleMap;
uint16 * m_startStates; // prectxt length
uint16 * m_transitions;
State * m_states;
bool collisionShift(Segment *seg, int dir, json * const dbgout) const;
bool collisionKern(Segment *seg, int dir, json * const dbgout) const;
bool collisionFinish(Segment *seg, GR_MAYBE_UNUSED json * const dbgout) const;
bool resolveCollisions(Segment *seg, Slot *slot, Slot *start, ShiftCollider &coll, bool isRev,
int dir, bool &moved, bool &hasCol, json * const dbgout) const;
float resolveKern(Segment *seg, Slot *slot, Slot *start, KernCollider &coll, int dir,
float &ymin, float &ymax, json *const dbgout) const;
const Silf * m_silf;
uint16 * m_cols;
Rule * m_rules; // rules
RuleEntry * m_ruleMap;
uint16 * m_startStates; // prectxt length
uint16 * m_transitions;
State * m_states;
vm::Machine::Code * m_codes;
byte * m_progs;
byte m_flags;
byte m_iMaxLoop;
@ -88,6 +104,7 @@ private:
uint16 m_numColumns;
byte m_minPreCtxt;
byte m_maxPreCtxt;
byte m_colThreshold;
vm::Machine::Code m_cPConstraint;
private: //defensive

View File

@ -50,7 +50,16 @@ public :
Rect(const Position& botLeft, const Position& topRight): bl(botLeft), tr(topRight) {}
Rect widen(const Rect& other) { return Rect(Position(bl.x > other.bl.x ? other.bl.x : bl.x, bl.y > other.bl.y ? other.bl.y : bl.y), Position(tr.x > other.tr.x ? tr.x : other.tr.x, tr.y > other.tr.y ? tr.y : other.tr.y)); }
Rect operator + (const Position &a) const { return Rect(Position(bl.x + a.x, bl.y + a.y), Position(tr.x + a.x, tr.y + a.y)); }
Rect operator - (const Position &a) const { return Rect(Position(bl.x - a.x, bl.y - a.y), Position(tr.x - a.x, tr.y - a.y)); }
Rect operator * (float m) const { return Rect(Position(bl.x, bl.y) * m, Position(tr.x, tr.y) * m); }
float width() const { return tr.x - bl.x; }
float height() const { return tr.y - bl.y; }
bool hitTest(Rect &other);
// returns Position(overlapx, overlapy) where overlap<0 if overlapping else positive)
Position overlap(Position &offset, Rect &other, Position &otherOffset);
//Position constrainedAvoid(Position &offset, Rect &box, Rect &sdbox, Position &other, Rect &obox, Rect &osdbox);
Position bl;
Position tr;

View File

@ -53,8 +53,6 @@ private:
inline Rule::~Rule()
{
delete constraint;
delete action;
}

View File

@ -40,6 +40,7 @@ of the License or (at your option) any later version.
#include "inc/Position.h"
#include "inc/List.h"
#include "inc/Bidi.h"
#include "inc/Collider.h"
#define MAX_SEG_GROWTH_FACTOR 256
@ -86,6 +87,11 @@ class Segment
Segment& operator=(const Segment&);
public:
enum {
SEG_INITCOLLISIONS = 1
};
unsigned int slotCount() const { return m_numGlyphs; } //one slot per glyph
void extendLength(int num) { m_numGlyphs += num; }
Position advance() const { return m_advance; }
@ -107,6 +113,8 @@ public:
Slot * endSlot, const Slot * srcSlot,
const size_t numGlyphs);
#endif
uint8 flags() const { return m_flags; }
void flags(uint8 f) { m_flags = f; }
Slot *first() { return m_first; }
void first(Slot *p) { m_first = p; }
Slot *last() { return m_last; }
@ -116,13 +124,20 @@ public:
void freeSlot(Slot *);
SlotJustify *newJustify();
void freeJustify(SlotJustify *aJustify);
Position positionSlots(const Font *font, Slot *first=0, Slot *last=0);
Position positionSlots(const Font *font=0, Slot *first=0, Slot *last=0, bool isFinal = true);
void associateChars(int offset, int num);
void linkClusters(Slot *first, Slot *last);
uint16 getClassGlyph(uint16 cid, uint16 offset) const { return m_silf->getClassGlyph(cid, offset); }
uint16 findClassIndex(uint16 cid, uint16 gid) const { return m_silf->findClassIndex(cid, gid); }
int addFeatures(const Features& feats) { m_feats.push_back(feats); return m_feats.size() - 1; }
uint32 getFeature(int index, uint8 findex) const { const FeatureRef* pFR=m_face->theSill().theFeatureMap().featureRef(findex); if (!pFR) return 0; else return pFR->getFeatureVal(m_feats[index]); }
void setFeature(int index, uint8 findex, uint32 val) {
const FeatureRef* pFR=m_face->theSill().theFeatureMap().featureRef(findex);
if (pFR)
{
if (val > pFR->maxVal()) val = pFR->maxVal();
pFR->applyValToFeature(val, m_feats[index]);
} }
void dir(int8 val) { m_dir = val; }
unsigned int passBits() const { return m_passBits; }
void mergePassBits(const unsigned int val) { m_passBits &= val; }
@ -141,14 +156,16 @@ public:
bool hasJustification() const { return m_justifies.size() != 0; }
bool isWhitespace(const int cid) const;
bool hasCollisionInfo() const { return m_collisions != 0; }
SlotCollision *collisionInfo(const Slot *s) const { return m_collisions ? m_collisions + s->index() : NULL; }
CLASS_NEW_DELETE
public: //only used by: GrSegment* makeAndInitialize(const GrFont *font, const GrFace *face, uint32 script, const FeaturesHandle& pFeats/*must not be IsNull*/, encform enc, const void* pStart, size_t nChars, int dir);
bool read_text(const Face *face, const Features* pFeats/*must not be NULL*/, gr_encform enc, const void*pStart, size_t nChars);
void prepare_pos(const Font *font);
void finalise(const Font *font);
float justify(Slot *pSlot, const Font *font, float width, enum justFlags flags, Slot *pFirst, Slot *pLast);
bool initCollisions();
private:
Position m_advance; // whole segment advance
@ -159,6 +176,7 @@ private:
Slot * m_freeSlots; // linked list of free slots
SlotJustify * m_freeJustifies; // Slot justification blocks free list
CharInfo * m_charinfo; // character info, one per input character
SlotCollision * m_collisions; // Array of SlotCollisions for each slot
const Face * m_face; // GrFace
const Silf * m_silf;
Slot * m_first; // first slot in segment
@ -169,6 +187,7 @@ private:
m_passBits; // if bit set then skip pass
int m_defaultOriginal; // number of whitespace chars in the string
int8 m_dir;
uint8 m_flags; // General purpose flags
};
@ -179,7 +198,7 @@ void Segment::finalise(const Font *font)
if (!m_first) return;
m_advance = positionSlots(font);
associateChars(0, m_numCharinfo);
//associateChars(0, m_numCharinfo);
linkClusters(m_first, m_last);
}

View File

@ -0,0 +1,119 @@
/* Copyright (c) 2012, Siyuan Fu <fusiyuan2010@gmail.com>
Copyright (c) 2015, SIL International
All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:
1. Redistributions of source code must retain the above copyright notice,
this list of conditions and the following disclaimer.
2. 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.
3. Neither the name of the copyright holder 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 HOLDER 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.
*/
#pragma once
#include <cassert>
#include <cstddef>
#include <cstring>
#include <iterator>
//the code from LZ4
#if (GCC_VERSION >= 302) || (__INTEL_COMPILER >= 800) || defined(__clang__)
# define expect(expr,value) (__builtin_expect ((expr),(value)) )
#else
# define expect(expr,value) (expr)
#endif
#define likely(expr) expect((expr) != 0, 1)
#define unlikely(expr) expect((expr) != 0, 0)
////////////////////
namespace
{
#if defined(_MSC_VER)
typedef unsigned __int8 u8;
typedef unsigned __int16 u16;
typedef unsigned __int32 u32;
typedef unsigned __int64 u64;
#else
#include <stdint.h>
typedef uint8_t u8;
typedef uint16_t u16;
typedef uint32_t u32;
typedef uint64_t u64;
#endif
ptrdiff_t const MINMATCH = 4;
template<int S>
inline
void unaligned_copy(void * d, void const * s) {
::memcpy(d, s, S);
}
inline
u8 * memcpy_nooverlap(u8 * d, u8 const * s, size_t n) {
size_t const WS = sizeof(unsigned long);
u8 const * e = s + n;
do
{
unaligned_copy<WS>(d, s);
d += WS;
s += WS;
}
while (s < e);
d-=(s-e);
return d;
}
inline
u8 * memcpy_nooverlap_surpass(u8 * d, u8 const * s, size_t n) {
size_t const WS = sizeof(unsigned long);
size_t wn = n/WS;
while (wn--)
{
unaligned_copy<WS>(d, s);
d += WS;
s += WS;
}
n &= WS-1;
while (n--) {*d++ = *s++; }
return d;
}
inline
u8 * memcpy_(u8 * d, u8 const * s, size_t n) {
if (likely(d>s+sizeof(unsigned long)))
return memcpy_nooverlap(d,s,n);
else while (n--) *d++ = *s++;
return d;
}
} // end of anonymous namespace

View File

@ -85,6 +85,7 @@ public:
uint8 aMirror() const {return m_aMirror; }
uint8 aPassBits() const { return m_aPassBits; }
uint8 aBidi() const { return m_aBidi; }
uint8 aCollision() const { return m_aCollision; }
uint8 substitutionPass() const { return m_sPass; }
uint8 positionPass() const { return m_pPass; }
uint8 justificationPass() const { return m_jPass; }
@ -114,13 +115,10 @@ private:
uint8 m_sPass, m_pPass, m_jPass, m_bPass,
m_flags;
uint8 m_aPseudo, m_aBreak, m_aUser, m_aBidi, m_aMirror, m_aPassBits,
m_iMaxComp;
uint16 m_aLig,
m_numPseudo,
m_nClass,
m_nLinear,
m_gEndLine;
uint8 m_aPseudo, m_aBreak, m_aUser, m_aBidi, m_aMirror, m_aPassBits,
m_iMaxComp, m_aCollision;
uint16 m_aLig, m_numPseudo, m_nClass, m_nLinear,
m_gEndLine;
gr_faceinfo m_silfinfo;
void releaseBuffers() throw();

View File

@ -75,6 +75,7 @@ public:
unsigned short gid() const { return m_glyphid; }
Position origin() const { return m_position; }
float advance() const { return m_advance.x; }
void advance(Position &val) { m_advance = val; }
Position advancePos() const { return m_advance; }
int before() const { return m_before; }
int after() const { return m_after; }
@ -98,7 +99,7 @@ public:
void after(int ind) { m_after = ind; }
bool isBase() const { return (!m_parent); }
void update(int numSlots, int numCharInfo, Position &relpos);
Position finalise(const Segment* seg, const Font* font, Position & base, Rect & bbox, uint8 attrLevel, float & clusterMin);
Position finalise(const Segment* seg, const Font* font, Position & base, Rect & bbox, uint8 attrLevel, float & clusterMin, bool isFinal);
bool isDeleted() const { return (m_flags & DELETED) ? true : false; }
void markDeleted(bool state) { if (state) m_flags |= DELETED; else m_flags &= ~DELETED; }
bool isCopied() const { return (m_flags & COPIED) ? true : false; }
@ -122,8 +123,10 @@ public:
Slot *attachedTo() const { return m_parent; }
Position attachOffset() const { return m_attach - m_with; }
Slot* firstChild() const { return m_child; }
void firstChild(Slot *ap) { m_child = ap; }
bool child(Slot *ap);
Slot* nextSibling() const { return m_sibling; }
void nextSibling(Slot *ap) { m_sibling = ap; }
bool sibling(Slot *ap);
bool removeChild(Slot *ap);
bool removeSibling(Slot *ap);
@ -132,6 +135,8 @@ public:
void floodShift(Position adj);
float just() const { return m_just; }
void just(float j) { m_just = j; }
Slot *nextInCluster(const Slot *s) const;
bool isChildOf(const Slot *base) const;
CLASS_NEW_DELETE

View File

@ -137,11 +137,11 @@ public:
////////////////////////////////// cmap lookup tools
const void * FindCmapSubtable(const void * pCmap, int nPlatformId = 3,
int nEncodingId = 1, size_t length = 0);
bool CheckCmapSubtable4(const void * pCmap31);
bool CheckCmapSubtable4(const void * pCmap31 /*, unsigned int maxgid*/);
gid16 CmapSubtable4Lookup(const void * pCmapSubtabel4, unsigned int nUnicodeId, int rangeKey = 0);
unsigned int CmapSubtable4NextCodepoint(const void *pCmap31, unsigned int nUnicodeId,
int * pRangeKey = 0);
bool CheckCmapSubtable12(const void *pCmap310);
bool CheckCmapSubtable12(const void *pCmap310 /*, unsigned int maxgid*/);
gid16 CmapSubtable12Lookup(const void * pCmap310, unsigned int uUnicodeId, int rangeKey = 0);
unsigned int CmapSubtable12NextCodepoint(const void *pCmap310, unsigned int nUnicodeId,
int * pRangeKey = 0);

View File

@ -29,15 +29,63 @@ of the License or (at your option) any later version.
namespace graphite2
{
#if defined GRAPHITE2_BUILTINS && (defined __GNUC__ || defined __clang__)
template<typename T>
inline unsigned int bit_set_count(T v)
{
v = v - ((v >> 1) & T(~T(0)/3)); // temp
v = (v & T(~T(0)/15*3)) + ((v >> 2) & T(~T(0)/15*3)); // temp
v = (v + (v >> 4)) & T(~T(0)/255*15); // temp
return (T)(v * T(~T(0)/255)) >> (sizeof(T)-1)*8; // count
return __builtin_popcount(v);
}
template<>
inline unsigned int bit_set_count(int16 v)
{
return __builtin_popcount(static_cast<uint16>(v));
}
template<>
inline unsigned int bit_set_count(int8 v)
{
return __builtin_popcount(static_cast<uint8>(v));
}
template<>
inline unsigned int bit_set_count(unsigned long v)
{
return __builtin_popcountl(v);
}
template<>
inline unsigned int bit_set_count(signed long v)
{
return __builtin_popcountl(v);
}
template<>
inline unsigned int bit_set_count(unsigned long long v)
{
return __builtin_popcountll(v);
}
template<>
inline unsigned int bit_set_count(signed long long v)
{
return __builtin_popcountll(v);
}
#else
template<typename T>
inline unsigned int bit_set_count(T v)
{
v = v - ((v >> 1) & T(~(0UL)/3)); // temp
v = (v & T(~(0UL)/15*3)) + ((v >> 2) & T(~(0UL)/15*3)); // temp
v = (v + (v >> 4)) & T(~(0UL)/255*15); // temp
return (T)(v * T(~(0UL)/255)) >> (sizeof(T)-1)*8; // count
}
#endif
template<int S>
inline unsigned long _mask_over_val(unsigned long v)
@ -87,4 +135,12 @@ inline T zero_bytes(const T x, unsigned char n)
return T((has_zero(x^t) >> 7)*n);
}
#if 0
inline float float_round(float x, uint32 m)
{
*reinterpret_cast<unsigned int *>(&x) &= m;
return *reinterpret_cast<float *>(&x);
}
#endif
}

View File

@ -54,6 +54,7 @@ struct objectid
json & operator << (json & j, const Position &) throw();
json & operator << (json & j, const Rect &) throw();
json & operator << (json & j, const CharInfo &) throw();
json & operator << (json & j, const dslot &) throw();
json & operator << (json & j, const objectid &) throw();
@ -68,6 +69,13 @@ json & operator << (json & j, const Position & p) throw()
}
inline
json & operator << (json & j, const Rect & p) throw()
{
return j << json::flat << json::array << p.bl.x << p.bl.y << p.tr.x << p.tr.y << json::close;
}
inline
json & operator << (json & j, const objectid & sid) throw()
{

View File

@ -29,9 +29,11 @@ of the License or (at your option) any later version.
// Author: Tim Eves
#pragma once
#include "inc/Main.h"
#include <cassert>
#include <stdio.h>
#include "inc/List.h"
namespace graphite2 {
@ -49,6 +51,7 @@ class json
* _context, // current context (top of stack)
* _flatten; // if !0 points to context above which
// pretty printed output should occur.
Vector<void *> _env;
void context(const char current) throw();
void indent(const int d=0) throw();
@ -64,6 +67,10 @@ public:
typedef bool boolean;
static const _null_t null;
void setenv(unsigned int index, void *val) { _env.reserve(index + 1); if (index >= _env.size()) _env.insert(_env.end(), _env.size() - index + 1, 0); _env[index] = val; }
void *getenv(unsigned int index) const { return _env[index]; }
const Vector<void *> &getenvs() const { return _env; }
static void flat(json &) throw();
static void close(json &) throw();
static void object(json &) throw();

View File

@ -43,7 +43,7 @@ of the License or (at your option) any later version.
// level - any byte
static const opcode_t opcode_table[] =
{
{{do2(nop)}, 0, "NOP"},
{{do2(nop)}, 0, "NOP"},
{{do2(push_byte)}, 1, "PUSH_BYTE"}, // number
{{do2(push_byte_u)}, 1, "PUSH_BYTE_U"}, // number
@ -114,6 +114,11 @@ static const opcode_t opcode_table[] =
{{do_(put_glyph), NILOP}, 2, "PUT_GLYPH"}, // output_class output_class
{{do2(push_glyph_attr)}, 3, "PUSH_GLYPH_ATTR"}, // gattrnum gattrnum slot
{{do2(push_att_to_glyph_attr)}, 3, "PUSH_ATT_TO_GLYPH_ATTR"}, // gattrnum gattrnum slot
{{do2(bor)}, 0, "BITOR"},
{{do2(band)}, 0, "BITAND"},
{{do2(bnot)}, 0, "BITNOT"}, // 0x40
{{do2(setbits)}, 4, "BITSET"},
{{do2(set_feat)}, 2, "SET_FEAT"},
// private opcodes for internal use only, comes after all other on disk opcodes.
{{do_(temp_copy), NILOP}, 0, "TEMP_COPY"}
};

View File

@ -241,20 +241,24 @@ ENDOP
STARTOP(put_copy)
declare_params(1);
const int slot_ref = int8(*param);
if (is && (slot_ref ||is != *map))
if (is)
{
int16 *tempUserAttrs = is->userAttrs();
slotref ref = slotat(slot_ref);
if (ref)
if (ref && ref != is)
{
memcpy(tempUserAttrs, ref->userAttrs(), seg.numAttrs() * sizeof(uint16));
int16 *tempUserAttrs = is->userAttrs();
if (is->attachedTo() || is->firstChild()) DIE
Slot *prev = is->prev();
Slot *next = is->next();
memcpy(is, slotat(slot_ref), sizeof(Slot));
memcpy(tempUserAttrs, ref->userAttrs(), seg.numAttrs() * sizeof(uint16));
memcpy(is, ref, sizeof(Slot));
is->firstChild(NULL);
is->nextSibling(NULL);
is->userAttrs(tempUserAttrs);
is->next(next);
is->prev(prev);
is->sibling(NULL);
if (is->attachedTo())
is->attachedTo()->child(is);
}
is->markCopied(false);
is->markDeleted(false);
@ -316,7 +320,7 @@ STARTOP(insert)
ENDOP
STARTOP(delete_)
if (!is) DIE
if (!is || is->isDeleted()) DIE
is->markDeleted(true);
if (is->prev())
is->prev()->next(is->next());
@ -644,3 +648,37 @@ STARTOP(temp_copy)
newSlot->markCopied(true);
*map = newSlot;
ENDOP
STARTOP(band)
binop(&);
ENDOP
STARTOP(bor)
binop(|);
ENDOP
STARTOP(bnot)
*sp = ~*sp;
ENDOP
STARTOP(setbits)
declare_params(4);
const uint16 m = uint16(param[0]) << 8
| uint8(param[1]);
const uint16 v = uint16(param[2]) << 8
| uint8(param[3]);
*sp = ((*sp) & ~m) | v;
ENDOP
STARTOP(set_feat)
declare_params(2);
const unsigned int feat = uint8(param[0]);
const int slot_ref = int8(param[1]);
slotref slot = slotat(slot_ref);
if (slot)
{
uint8 fid = seg.charinfo(slot->original())->fid();
seg.setFeature(fid, feat, pop());
}
ENDOP

View File

@ -30,6 +30,7 @@ of the License or (at your option) any later version.
#if !defined GRAPHITE2_NTRACING
#include <stdio.h>
#include <limits>
#include "inc/json.h"
using namespace graphite2;
@ -116,7 +117,20 @@ json & json::operator << (json::string s) throw()
return *this;
}
json & json::operator << (json::number f) throw() { context(seq); fprintf(_stream, "%g", f); return *this; }
json & json::operator << (json::number f) throw()
{
context(seq);
if (std::numeric_limits<json::number>::infinity() == f)
fputs("Infinity", _stream);
else if (-std::numeric_limits<json::number>::infinity() == f)
fputs("-Infinity", _stream);
else if (std::numeric_limits<json::number>::quiet_NaN() == f ||
std::numeric_limits<json::number>::signaling_NaN() == f)
fputs("NaN", _stream);
else
fprintf(_stream, "%g", f);
return *this;
}
json & json::operator << (json::integer d) throw() { context(seq); fprintf(_stream, "%ld", d); return *this; }
json & json::operator << (long unsigned d) throw() { context(seq); fprintf(_stream, "%ld", d); return *this; }
json & json::operator << (json::boolean b) throw() { context(seq); fputs(b ? "true" : "false", _stream); return *this; }

View File

@ -27,6 +27,8 @@ UNIFIED_SOURCES += [
'CachedFace.cpp',
'CmapCache.cpp',
'Code.cpp',
'Collider.cpp',
'Decompressor.cpp',
'Face.cpp',
'FeatureMap.cpp',
'FileFace.cpp',
@ -40,10 +42,12 @@ UNIFIED_SOURCES += [
'gr_logging.cpp',
'gr_segment.cpp',
'gr_slot.cpp',
'Intervals.cpp',
'json.cpp',
'Justifier.cpp',
'NameTable.cpp',
'Pass.cpp',
'Position.cpp',
'SegCache.cpp',
'SegCacheEntry.cpp',
'SegCacheStore.cpp',