diff options
Diffstat (limited to 'Build/source/libs/graphite-engine/src/segment')
38 files changed, 31314 insertions, 0 deletions
diff --git a/Build/source/libs/graphite-engine/src/segment/FileInput.cpp b/Build/source/libs/graphite-engine/src/segment/FileInput.cpp new file mode 100644 index 00000000000..abd4fb800c3 --- /dev/null +++ b/Build/source/libs/graphite-engine/src/segment/FileInput.cpp @@ -0,0 +1,266 @@ +/*--------------------------------------------------------------------*//*:Ignore this sentence. +Copyright (C) 1999, 2001 SIL International. All rights reserved. + +Distributable under the terms of either the Common Public License or the +GNU Lesser General Public License, as specified in the LICENSING.txt file. + +File: FileInput.cpp +Responsibility: Sharon Correll +Last reviewed: Not yet. + +Description: + Contains the functions for reading from a TT file. (These are functions, not methods + associated with a class.) +----------------------------------------------------------------------------------------------*/ + +//:>******************************************************************************************** +//:> Include files +//:>******************************************************************************************** + +#include "GrCommon.h" +#include "GrData.h" +#ifndef _WIN32 +#include "GrMstypes.h" +#endif +#include "GrDebug.h" +#include <fstream> +#include <iostream> +#include <string> +#include "FileInput.h" +#include "GrResult.h" + +//#ifndef _MSC_VER +//#include "config.h" +//#endif + +#ifdef _MSC_VER +#pragma hdrstop +#endif +#undef THIS_FILE +DEFINE_THIS_FILE + +//:End Ignore + +//:>******************************************************************************************** +//:> Forward declarations +//:>******************************************************************************************** + +//:>******************************************************************************************** +//:> Local Constants and static variables +//:>******************************************************************************************** + +namespace gr +{ + +//:>******************************************************************************************** +//:> Methods of GrBufferIStream +//:>******************************************************************************************** + +/*---------------------------------------------------------------------------------------------- + Constructor. +----------------------------------------------------------------------------------------------*/ +GrBufferIStream::GrBufferIStream() +{ + m_pbStart = NULL; + m_pbNext = NULL; + m_pbLim = NULL; +} + +/*---------------------------------------------------------------------------------------------- + Destructor. +----------------------------------------------------------------------------------------------*/ +GrBufferIStream::~GrBufferIStream() +{ + Close(); +} + +/*---------------------------------------------------------------------------------------------- + Initialize the stream. +----------------------------------------------------------------------------------------------*/ +#ifdef GR_FW +bool GrBufferIStream::Open(std::wstring stuFileName, int kMode) +#else +bool GrBufferIStream::Open(const char * pcFileName, std::ios::openmode kMode) +#endif +{ + Assert(false); // use OpenBuffer + return false; +} + +/*---------------------------------------------------------------------------------------------- + Initialize the stream to a buffer. +----------------------------------------------------------------------------------------------*/ +bool GrBufferIStream::OpenBuffer(byte * pbBuffer, int cb) +{ + Assert(m_pbStart == NULL); + Assert(m_pbNext == NULL); + Assert(m_pbLim == NULL); + + m_pbStart = pbBuffer; + m_pbNext = pbBuffer; + if (cb >= 0) + m_pbLim = m_pbStart + cb; + // otherwise we don't know the length + + return true; +} + +/*---------------------------------------------------------------------------------------------- + Close the stream. +----------------------------------------------------------------------------------------------*/ +void GrBufferIStream::Close() +{ + m_pbStart = NULL; + m_pbNext = NULL; + m_pbLim = NULL; +} + +/*---------------------------------------------------------------------------------------------- + Read a byte from the stream. +----------------------------------------------------------------------------------------------*/ +byte GrBufferIStream::ReadByteFromFont() +{ + byte bInput = *m_pbNext; + m_pbNext += isizeof(byte); + if (m_pbLim && m_pbNext > m_pbLim) + THROW(kresReadFault); + return bInput; +} + +/*---------------------------------------------------------------------------------------------- + Read a short (signed 16-bit) word from the stream. Switch the bytes from big-endian + to little-endian format. +----------------------------------------------------------------------------------------------*/ +short GrBufferIStream::ReadShortFromFont() +{ + short snInput = *(short *)m_pbNext; + m_pbNext += isizeof(short); + if (m_pbLim && m_pbNext > m_pbLim) + THROW(kresReadFault); + snInput = lsbf(snInput); + return snInput; +} + +/*---------------------------------------------------------------------------------------------- + Read a wide character (unsigned 16-bit word) from the stream. + Switch the bytes from big-endian to little-endian format. +----------------------------------------------------------------------------------------------*/ +utf16 GrBufferIStream::ReadUShortFromFont() +{ + utf16 chwInput = *(utf16 *)m_pbNext; + m_pbNext += isizeof(utf16); + if (m_pbLim && m_pbNext > m_pbLim) + THROW(kresReadFault); + chwInput = lsbf(chwInput); + return chwInput; +} + +/*---------------------------------------------------------------------------------------------- + Read a standard (32-bit) word from the stream. Switch the bytes from big-endian + to little-endian format. +----------------------------------------------------------------------------------------------*/ +int GrBufferIStream::ReadIntFromFont() +{ + int nInput = *(int *)m_pbNext; + m_pbNext += isizeof(int); + if (m_pbLim && m_pbNext > m_pbLim) + THROW(kresReadFault); + nInput = lsbf(nInput); + return nInput; +} + +/*---------------------------------------------------------------------------------------------- + Read a block of data from the stream. DON'T switch the bytes from big-endian + to little-endian format. +----------------------------------------------------------------------------------------------*/ +void GrBufferIStream::ReadBlockFromFont(void * pvInput, int cb) +{ + std::copy(m_pbNext, m_pbNext + cb, reinterpret_cast<byte*>(pvInput)); + m_pbNext += cb; + if (m_pbLim && m_pbNext > m_pbLim) + THROW(kresReadFault); +} + +/*---------------------------------------------------------------------------------------------- + Get the absolute position of the font-file stream (relative to the beginning of + the file). For buffers, we just return the byte position in the buffer. +----------------------------------------------------------------------------------------------*/ +void GrBufferIStream::GetPositionInFont(long * plPos) +{ + *plPos = (m_pbNext - m_pbStart); +} + +/*---------------------------------------------------------------------------------------------- + Set the position of the font-file stream to the given absolute position (relative + to the beginning of the file). For buffers, assume the position is relative to the + beginning of the buffer. +----------------------------------------------------------------------------------------------*/ +void GrBufferIStream::SetPositionInFont(long lPos) +{ + m_pbNext = m_pbStart + lPos; + if (m_pbLim && m_pbNext > m_pbLim) + THROW(kresReadFault); +} + + +//:>******************************************************************************************** +//:> Swap byte order. +//:>******************************************************************************************** +int swapb(int nArg) +{ +#if WORDS_BIGENDIAN +return nArg; +#else + int b1, b2, b3, b4; + b1 = ((nArg & 0xFF000000) >> 24) & 0x000000FF; // remove sign extension + b2 = ((nArg & 0x00FF0000) >> 8); // & 0x0000FF00; + b3 = ((nArg & 0x0000FF00) << 8); // & 0x00FF0000; + b4 = ((nArg & 0x000000FF) << 24); // & 0xFF000000; + int nRet = b1 | b2 | b3 | b4; + return nRet; +#endif +} + +unsigned int swapb(unsigned int nArg) +{ +#if WORDS_BIGENDIAN +return nArg; +#else + int b1, b2, b3, b4; + b1 = ((nArg & 0xFF000000) >> 24) & 0x000000FF; // remove sign extension + b2 = ((nArg & 0x00FF0000) >> 8); // & 0x0000FF00; + b3 = ((nArg & 0x0000FF00) << 8); // & 0x00FF0000; + b4 = ((nArg & 0x000000FF) << 24); // & 0xFF000000; + int nRet = b1 | b2 | b3 | b4; + return nRet; +#endif +} + +utf16 swapb(utf16 chwArg) +{ +#if WORDS_BIGENDIAN +return chwArg; +#else + utf16 b1, b2; + b1 = ((chwArg & 0xFF00) >> 8) & 0x00FF; // remove sign extension + b2 = ((chwArg & 0x00FF) << 8); // & 0xFF00; + utf16 chwRet = b1 | b2; + return chwRet; +#endif +} + +short swapb(short snArg) +{ +#if WORDS_BIGENDIAN +return snArg; +#else + short b1, b2; + b1 = ((snArg & 0xFF00) >> 8) & 0x00FF; // remove sign extension + b2 = ((snArg & 0x00FF) << 8); // & 0xFF00; + short snRet = b1 | b2; + return snRet; +#endif +} + +} //namespace gr + diff --git a/Build/source/libs/graphite-engine/src/segment/FileInput.h b/Build/source/libs/graphite-engine/src/segment/FileInput.h new file mode 100644 index 00000000000..f8c32b762eb --- /dev/null +++ b/Build/source/libs/graphite-engine/src/segment/FileInput.h @@ -0,0 +1,104 @@ +/*--------------------------------------------------------------------*//*:Ignore this sentence. +Copyright (C) 1999, 2001 SIL International. All rights reserved. + +Distributable under the terms of either the Common Public License or the +GNU Lesser General Public License, as specified in the LICENSING.txt file. + +File: FileInput.h +Responsibility: Sharon Correll +Last reviewed: not yet + +Description: + Defines utility functions for reading from a font file. +----------------------------------------------------------------------------------------------*/ + +#ifdef _MSC_VER +#pragma once +#endif +#ifndef FILEINPUT_INCLUDED +#define FILEINPUT_INCLUDED + +//:End Ignore + +namespace gr +{ + +int swapb(int nArg); +unsigned int swapb(unsigned int nArg); +utf16 swapb(utf16 chwArg); +short swapb(short snArg); + +// Most significant byte first (converting from least-sig-first): +inline int msbf(int nArg) { return swapb(nArg); } +inline unsigned int msbf(unsigned int nArg) { return swapb(nArg); } +inline utf16 msbf(utf16 chwArg) { return swapb(chwArg); } +inline short msbf(short chwArg) { return swapb(chwArg); } + +// Least significant byte first (converting from most-sig first): +inline int lsbf(int nArg) { return swapb(nArg); } +inline unsigned int lsbf(unsigned int nArg) { return swapb(nArg); }; +inline utf16 lsbf(utf16 chwArg) { return swapb(chwArg); } +inline short lsbf(short chwArg) { return swapb(chwArg); } + +class GrIStream +{ +public: + virtual void Close() = 0; + + virtual byte ReadByteFromFont() = 0; + virtual short ReadShortFromFont() = 0; + virtual utf16 ReadUShortFromFont() = 0; + virtual int ReadIntFromFont() = 0; + virtual void ReadBlockFromFont(void * pvInput, int cb) = 0; + + virtual void GetPositionInFont(long * plPos) = 0; + virtual void SetPositionInFont(long lPos) = 0; + + virtual bool OpenBuffer(byte * pbBuffer, int cb) = 0; + virtual void CloseBuffer() = 0; + +protected: + virtual ~GrIStream() {} +}; + + +/*---------------------------------------------------------------------------------------------- + A stream that reads from a buffer rather than a file. +----------------------------------------------------------------------------------------------*/ +class GrBufferIStream : public GrIStream +{ +public: + GrBufferIStream(); + ~GrBufferIStream(); + + #ifdef GR_FW + virtual bool Open(std::wstring stuFileName, std::ios::openmode kMode); + #else + virtual bool Open(const char * pcFileName, std::ios::openmode kMode); + #endif + virtual void Close(); + + virtual byte ReadByteFromFont(); + virtual short ReadShortFromFont(); + virtual utf16 ReadUShortFromFont(); + virtual int ReadIntFromFont(); + virtual void ReadBlockFromFont(void * pvInput, int cb); + + virtual void GetPositionInFont(long * plPos); + virtual void SetPositionInFont(long lPos); + + virtual bool OpenBuffer(byte * pbBuffer, int cb); + virtual void CloseBuffer() + { + Close(); + } + +protected: + byte * m_pbStart; + byte * m_pbNext; + byte * m_pbLim; +}; + +} // namespace gr + +#endif // !FILEINPUT_INCLUDED diff --git a/Build/source/libs/graphite-engine/src/segment/FontCache.cpp b/Build/source/libs/graphite-engine/src/segment/FontCache.cpp new file mode 100644 index 00000000000..041f449a401 --- /dev/null +++ b/Build/source/libs/graphite-engine/src/segment/FontCache.cpp @@ -0,0 +1,299 @@ +/*--------------------------------------------------------------------*//*:Ignore this sentence. +Copyright (C) 1999, 2001 SIL International. All rights reserved. + +Distributable under the terms of either the Common Public License or the +GNU Lesser General Public License, as specified in the LICENSING.txt file. + +File: FontCache.cpp +Responsibility: Sharon Correll +Last reviewed: Not yet. + +Description: + A cache of all the font-face objects. +----------------------------------------------------------------------------------------------*/ + +//:>******************************************************************************************** +//:> Include files +//:>******************************************************************************************** +#include "Main.h" +#ifdef _MSC_VER +#pragma hdrstop +#endif +// any other headers (not precompiled) + +#undef THIS_FILE +DEFINE_THIS_FILE + +//:End Ignore + +//:>******************************************************************************************** +//:> Forward declarations +//:>******************************************************************************************** + +//:>******************************************************************************************** +//:> Local Constants and static variables +//:>******************************************************************************************** + +//:>******************************************************************************************** +//:> Methods +//:>******************************************************************************************** + +namespace gr +{ + +/*---------------------------------------------------------------------------------------------- + Store the given font-face in the cache. Overwrite whatever was there before. + Return NULL if there is nothing appropriate there. +----------------------------------------------------------------------------------------------*/ +void FontCache::GetFontFace(std::wstring strFaceName, bool fBold, bool fItalic, + FontFace ** ppfface) +{ + int ifci = FindCacheItem(strFaceName); + if (ifci < 0) // no fonts of that family present + { + *ppfface = NULL; + return; + } + + CacheItem * pfci = m_prgfci + ifci; + if (fBold) + { + if (fItalic) + *ppfface = pfci->pffaceBI; + else + *ppfface = pfci->pffaceBold; + } + else + { + if (fItalic) + *ppfface = pfci->pffaceItalic; + else + *ppfface = pfci->pffaceRegular; + } +} + +/*---------------------------------------------------------------------------------------------- + Store the given font-face in the cache. Overwrite whatever was there before. +----------------------------------------------------------------------------------------------*/ +void FontCache::CacheFontFace(std::wstring strFaceName, bool fBold, bool fItalic, + FontFace * pfface) +{ + if (m_prgfci == NULL) + Initialize(); + + int ifciIns = FindCacheItem(strFaceName); + int ifci = ifciIns; + if (ifciIns < 0) + { + ifci = (ifciIns + 1) * -1; + InsertCacheItem(ifci); + std::copy(strFaceName.c_str(), strFaceName.c_str() + + (strFaceName.size() + 1), m_prgfci[ifci].szFaceName); + } + + CacheItem * pfci = m_prgfci + ifci; + + bool fPrevNull; + if (fBold) + { + if (fItalic) + { + fPrevNull = (pfci->pffaceBI == NULL); + pfci->pffaceBI = pfface; + } + else + { + fPrevNull = (pfci->pffaceBold == NULL); + pfci->pffaceBold = pfface; + } + } + else + { + if (fItalic) + { + fPrevNull = (pfci->pffaceItalic == NULL); + pfci->pffaceItalic = pfface; + } + else + { + fPrevNull = (pfci->pffaceRegular == NULL); + pfci->pffaceRegular = pfface; + } + } + + if (fPrevNull && (pfface != NULL)) + m_cfface++; +} + +/*---------------------------------------------------------------------------------------------- + Remove the given font-face from the cache. Return false if it couldn't be found. +----------------------------------------------------------------------------------------------*/ +bool FontCache::RemoveFontFace(std::wstring strFaceName, bool fBold, bool fItalic, + bool fZapCache) +{ + int ifci = FindCacheItem(strFaceName); + if (ifci < 0) + { + return false; + } + + bool fPrevVal; + CacheItem * pfci = m_prgfci + ifci; + if (fBold) + { + if (fItalic) + { + fPrevVal = (pfci->pffaceBI != NULL); + pfci->pffaceBI = NULL; + } + else + { + fPrevVal = (pfci->pffaceBold != NULL); + pfci->pffaceBold = NULL; + } + } + else + { + if (fItalic) + { + fPrevVal = (pfci->pffaceItalic != NULL); + pfci->pffaceItalic = NULL; + } + else + { + fPrevVal = (pfci->pffaceRegular != NULL); + pfci->pffaceRegular = NULL; + } + } + + if (fPrevVal) + m_cfface--; + + Assert(m_cfface >= 0); + + if (m_cfface <= 0 && m_flush == kflushAuto && fZapCache) + { + // All items are NULL; delete this cache. Probably this is because + // the program is exiting. + FontFace::ZapFontCache(); + } + + return fPrevVal; +} + +/*---------------------------------------------------------------------------------------------- + Search for the font-family in the cache and return its index. + If not present, return a negative number indicating where it was expected. +----------------------------------------------------------------------------------------------*/ +int FontCache::FindCacheItem(std::wstring strFaceName) +{ + if (m_cfci == 0) + return -1; + + Assert(m_prgfci); + + // Use a binary-chop search. + + int ifciLow = 0; + int ifciHigh = m_cfci; + while (true) + { + int ifciMid = (ifciHigh + ifciLow) >> 1; // divide by 2 + CacheItem * pfci = m_prgfci + ifciMid; + int tst = wcscmp(strFaceName.c_str(), (const wchar_t *)pfci->szFaceName); + if (tst == 0) + return ifciMid; // found it + if (ifciLow + 1 == ifciHigh) + { + // not there; return where expected: 0 -> -1, 1 -> -2 + Assert(ifciMid == ifciLow); + if (tst < 0) + return (ifciLow * -1) - 1; + else + return (ifciHigh * -1) - 1; + } + + // Keep looking. + if (tst < 0) + ifciHigh = ifciMid; + else + ifciLow = ifciMid; + } +} + +/*---------------------------------------------------------------------------------------------- + Insert space in the cache at location ifci. +----------------------------------------------------------------------------------------------*/ +void FontCache::InsertCacheItem(int ifci) +{ + if (m_cfci == m_cfciMax) + { + // Cache is full; double the space. + CacheItem * m_prgfciOld = m_prgfci; + m_prgfci = new CacheItem[m_cfciMax * 2]; + std::copy(m_prgfciOld, m_prgfciOld + m_cfciMax, m_prgfci); + delete[] m_prgfciOld; + m_cfciMax *= 2; + } + + // This copy involves overlapping ranges, so we need copy_backward not copy + // to satisfy the preconditions + std::copy_backward(m_prgfci + ifci, m_prgfci + m_cfci, m_prgfci + m_cfci + 1); + m_cfci++; + + // Initialize inserted item. + CacheItem * pfci = m_prgfci + ifci; + pfci->pffaceRegular = NULL; + pfci->pffaceBold = NULL; + pfci->pffaceItalic = NULL; + pfci->pffaceBI = NULL; +} + +/*---------------------------------------------------------------------------------------------- + Check that the cache is indeed empty. +----------------------------------------------------------------------------------------------*/ +void FontCache::AssertEmpty() +{ +#ifdef _DEBUG + for (int ifci = 0; ifci < m_cfci; ifci++) + { + CacheItem * pfci = m_prgfci + ifci; + Assert(pfci->pffaceRegular == NULL); + Assert(pfci->pffaceBold == NULL); + Assert(pfci->pffaceItalic == NULL); + Assert(pfci->pffaceBI == NULL); + } +#endif // _DEBUG +} + +/*---------------------------------------------------------------------------------------------- + Set the flush mode on the cache to indicate whether or not it should be deleted when + it becomes empty. +----------------------------------------------------------------------------------------------*/ +void FontCache::SetFlushMode(int flush) +{ + m_flush = flush; + + if (m_flush == kflushAuto) + { + // Delete any font faces that have no remaining corresponding fonts. + // Work backwards so as we remove items the loop still works. + for (int ifci = m_cfci; --ifci >= 0; ) + { + CacheItem * pfci = m_prgfci + ifci; + if (pfci->pffaceRegular && pfci->pffaceRegular->NoFonts()) + RemoveFontFace(pfci->szFaceName, false, false, false); + if (pfci->pffaceBold && pfci->pffaceBold->NoFonts()) + RemoveFontFace(pfci->szFaceName, true, false, false); + if (pfci->pffaceItalic && pfci->pffaceItalic->NoFonts()) + RemoveFontFace(pfci->szFaceName, false, true, false); + if (pfci->pffaceBI && pfci->pffaceBI->NoFonts()) + RemoveFontFace(pfci->szFaceName, true, true, false); + } + + if (m_cfface <= 0) + FontFace::ZapFontCache(); + } +} + +} // namespace gr diff --git a/Build/source/libs/graphite-engine/src/segment/FontCache.h b/Build/source/libs/graphite-engine/src/segment/FontCache.h new file mode 100644 index 00000000000..7159ea35d98 --- /dev/null +++ b/Build/source/libs/graphite-engine/src/segment/FontCache.h @@ -0,0 +1,100 @@ +/*--------------------------------------------------------------------*//*:Ignore this sentence. +Copyright (C) 1999, 2001 SIL International. All rights reserved. + +Distributable under the terms of either the Common Public License or the +GNU Lesser General Public License, as specified in the LICENSING.txt file. + +File: FontCache.h +Responsibility: Sharon Correll +Last reviewed: Not yet. + +Description: + A cache of all the font-face objects known to mankind. There is exactly one instance + of a FontCache. +----------------------------------------------------------------------------------------------*/ +#ifdef _MSC_VER +#pragma once +#endif +#ifndef FONTCACHE_INCLUDED +#define FONTCACHE_INCLUDED + +//:End Ignore + +namespace gr +{ + +class FontFace; + +/*---------------------------------------------------------------------------------------------- + TODO: change from a sorted list to a hash table, if performance so requires. +----------------------------------------------------------------------------------------------*/ +class FontCache { + +public: + FontCache() + { + m_cfci = 0; + m_prgfci = NULL; + m_cfciMax = 0; + m_cfface = 0; + m_flush = kflushAuto; + } + + ~FontCache() + { + delete[] m_prgfci; + m_prgfci = NULL; + m_cfci = 0; + m_cfciMax = 0; + m_cfface = 0; + } + + void Initialize() + { + m_cfci = 0; + m_prgfci = new CacheItem[12]; + m_cfciMax = 12; + m_cfface = 0; + } + + struct CacheItem + { + wchar_t szFaceName[32]; // type should match std::wstring + FontFace * pffaceRegular; + FontFace * pffaceBold; + FontFace * pffaceItalic; + FontFace * pffaceBI; + }; + + void GetFontFace(std::wstring strFaceName, bool fBold, bool fItalic, FontFace ** ppfface); + void CacheFontFace(std::wstring strFaceName, bool fBold, bool fItalic, FontFace * pfface); + bool RemoveFontFace(std::wstring strFaceName, bool fBold, bool fItalic, bool fZapCache = true); + void AssertEmpty(); + + int GetFlushMode() + { + return m_flush; + } + void SetFlushMode(int); + + // Debugging: + //bool DbgCheckFontCache(); + +protected: + int FindCacheItem(std::wstring strFaceName); + void InsertCacheItem(int ifci); + +protected: + // member variables; + int m_cfci; // number of items (font-families) + int m_cfciMax; // amount of space available + int m_cfface; // number of font-faces + CacheItem * m_prgfci; + + int m_flush; +}; + +} // namespace gr + + +#endif // !FONTCACHE_INCLUDED diff --git a/Build/source/libs/graphite-engine/src/segment/FontFace.cpp b/Build/source/libs/graphite-engine/src/segment/FontFace.cpp new file mode 100644 index 00000000000..838685ebc65 --- /dev/null +++ b/Build/source/libs/graphite-engine/src/segment/FontFace.cpp @@ -0,0 +1,487 @@ +/*--------------------------------------------------------------------*//*:Ignore this sentence. +Copyright (C) 1999, 2001 SIL International. All rights reserved. + +Distributable under the terms of either the Common Public License or the +GNU Lesser General Public License, as specified in the LICENSING.txt file. + +File: GrEngine.cpp +Responsibility: Sharon Correll +Last reviewed: Not yet. + +Description: Contains the implementation of the FontFace class. +----------------------------------------------------------------------------------------------*/ + +//:>******************************************************************************************** +//:> Include files +//:>******************************************************************************************** +#include "Main.h" +#include <functional> +#ifdef _MSC_VER +#pragma hdrstop +#endif +// any other headers (not precompiled) + +#undef THIS_FILE +DEFINE_THIS_FILE + +//:End Ignore + +namespace gr +{ + +//:>******************************************************************************************** +//:> Forward declarations +//:>******************************************************************************************** + +//:>******************************************************************************************** +//:> Local Constants and static variables +//:>******************************************************************************************** + +FontCache * FontFace::s_pFontCache = 0; + +//:>******************************************************************************************** +//:> New interface +//:>******************************************************************************************** +/*---------------------------------------------------------------------------------------------- + Return the appropriate FontFace, initialized with Graphite tables. + Called from the font constructor. +----------------------------------------------------------------------------------------------*/ +FontFace * FontFace::GetFontFace(Font * pfont, + std::wstring strFaceName, bool fBold, bool fItalic) +{ + if (s_pFontCache == NULL) + s_pFontCache = new FontCache; + + FontFace * pfface; + s_pFontCache->GetFontFace(strFaceName, fBold, fItalic, &pfface); + if (pfface) + return pfface; + + // Create a new font face. + pfface = new FontFace(); + pfface->InitFontFace(pfont, strFaceName, fBold, fItalic); + return pfface; +} + +/*---------------------------------------------------------------------------------------------- + Initialize the engine using the given Graphite font. +----------------------------------------------------------------------------------------------*/ +GrResult FontFace::InitFontFace(Font * pfont, + std::wstring stuFaceName, bool fBold, bool fItalic) +{ + AssertPtrN(pfont); + + m_pgreng = new GrEngine; + m_pgreng->m_pfface = this; + + m_pgreng->m_nScriptTag = 0; // not currently used, but set it to something + + std::wstring stuFeatures; + + //stuFeatures = stuFeaturesArg; + + if (wcscmp(stuFaceName.c_str(), m_pgreng->m_stuFaceName.c_str()) != 0) + { + s_pFontCache->RemoveFontFace(m_pgreng->FaceName(), m_pgreng->Bold(), m_pgreng->Italic()); + + m_pgreng->DestroyEverything(); // nothing we've cached is safe or useful. + m_pgreng->m_stuFaceName = stuFaceName; // if this is buggy, using assign might fix it + } + + //GrResult res = kresFail; +//#ifdef GR_FW +// // Read from the control file indicated by the registry, if any. +// std::wstring stuFaceNameTmp; +// stuFaceNameTmp.assign(rgchFaceName, wcslen(rgchFaceName)); +// res = InitFromControlFile(pfont, stuFaceName.c_str(), fBold, fItalic); +//#endif // GR_FW + + //if (ResultFailed(res)) + //{ + + + // Read directly from the font. + m_pgreng->DestroyContents(); + GrResult res = m_pgreng->ReadFontTables(pfont, fItalic); + + //} + + m_pgreng->m_resFontValid = res; + + m_pgreng->m_fBold = fBold; + m_pgreng->m_fItalic = fItalic; + s_pFontCache->CacheFontFace(m_pgreng->FaceName(), fBold, fItalic, this); + + return m_pgreng->m_resFontValid; +} + +/*---------------------------------------------------------------------------------------------- + Read the cmap and Graphite tables from the font that is selected into the graphics + device. + + @return A GrResult indicating whether we were successful in loading the Graphite font: + kresOk means success, kresFail means the Graphite font could not be found, + kresUnexpected means the Graphite tables could not be loaded; kresFalse means it is + not a Graphite font at all (has no Silf table). +----------------------------------------------------------------------------------------------*/ +GrResult GrEngine::ReadFontTables(Font * pfont, bool fItalic) +{ + GrResult res = kresOk; + m_ferr = kferrOkay; + GrBufferIStream grstrm; + + FontException fexptn; + fexptn.version = -1; + fexptn.subVersion = -1; + + bool fOk = false; + + const void * pHeadTbl; const void * pCmapTbl; const void * pSileTbl; const void * pSilfTbl; + const void * pFeatTbl; const void * pGlatTbl; const void * pGlocTbl; const void * pNameTbl; const void * pSillTbl; + size_t cbHeadSz, cbCmapSz, /*cbSileSz,*/ cbSilfSz, cbFeatSz, cbGlatSz, cbGlocSz, cbNameSz, cbSillSz; + + m_fFakeItalic = false; + + bool fSilf = false; // does the font have some sort of Silf table? + int nCheckSum = 0; + + bool fBadBase = false, + fMismatchedBase = false, + fFontIsItalic = false; + + // First read the head table. This gives us the checksum that we are using as a + // unique identifer. If it is the same as the one stored, and things appear set up, + // don't reload the tables. + res = (pHeadTbl = pfont->getTable(TtfUtil::TableIdTag(ktiHead), &cbHeadSz)) ? kresOk : kresFail; + fOk = pHeadTbl && (cbHeadSz == 0 || TtfUtil::CheckTable(ktiHead, pHeadTbl, cbHeadSz)); + if (res == kresFail) + { + m_stuInitError = L"could not locate head table for Graphite rendering"; + m_ferr = kferrFindHeadTable; + goto LUnexpected; + } + if (!fOk) + { + m_stuInitError = L"could not read design units for Graphite rendering"; + m_ferr = kferrReadDesignUnits; + goto LUnexpected; + } + m_mFontEmUnits = TtfUtil::DesignUnits(pHeadTbl); + nCheckSum = TtfUtil::HeadTableCheckSum(pHeadTbl); + fFontIsItalic = TtfUtil::IsItalic(pHeadTbl); + + if (m_nFontCheckSum == nCheckSum && m_ptman) + return m_resFontRead; // already initialized in some form + + DestroyContents(); + + Assert(!m_ptman); + Assert(!m_pctbl); + Assert(!m_pgtbl); + Assert(!m_prgpsd); + m_prgpsd = NULL; + m_cpsd = 0; + + m_ptman = new GrTableManager(this); + + m_fFakeItalic = (fItalic && !fFontIsItalic); + + // Look for an Sile table. If there is one, this is an extension font containing only + // the Graphite tables. Read the base font name out of the Sile table, and use it to + // read the cmap. Then replace the original font name to read the Graphite tables. + + m_fUseSepBase = false; + m_stuBaseFaceName.erase(); + + // TODO: rework the handling of the Sile table. + pSileTbl = NULL; + //pSileTbl = pfont->getTable(TtfUtil::TableIdTag(ktiSile)); + //if (pSileTbl) + //{ + // pgg->get_FontCharProperties(&chrpOriginal); + // grstrm.OpenBuffer(sile_tbl, sile_tbl.size()); + // m_fUseSepBase = ReadSileTable(pgg, grstrm, 0, &m_mFontEmUnits, &fMismatchedBase); + // grstrm.Close(); + + // if (!m_fUseSepBase) + // { + // SwitchGraphicsFont(pgg, false); // back to Graphite table font + // m_stuBaseFaceName.erase(); + // fBadBase = true; + // m_fUseSepBase = false; + // } + // // Otherwise leave the GrGraphics in a state to read from the base font. + //} + + // We don't need the offset table, and there's no way to get it anyway + // without a font file. + + // cmap + res = (pCmapTbl = pfont->getTable(TtfUtil::TableIdTag(ktiCmap), &cbCmapSz)) ? kresOk : kresFail; + fOk = pCmapTbl && (cbCmapSz == 0 || TtfUtil::CheckTable(ktiCmap, pCmapTbl, cbCmapSz)); + if (!fOk) + { + m_stuInitError = L"could not locate cmap table"; + m_ferr = kferrFindCmapTable; + ReturnResult(kresFail); + } + + // Make a private copy of the cmap for the engine's use. + m_pCmapTbl = new byte[cbCmapSz]; + std::copy(reinterpret_cast<const byte*>(pCmapTbl), + reinterpret_cast<const byte*>(pCmapTbl) + cbCmapSz, m_pCmapTbl); + + // MS Unicode cmap + m_pCmap_3_1 = TtfUtil::FindCmapSubtable(m_pCmapTbl, 3, 1); + m_pCmap_3_10 = TtfUtil::FindCmapSubtable(m_pCmapTbl, 3, 10); + if (!m_pCmap_3_1) + m_pCmap_3_1 = TtfUtil::FindCmapSubtable(m_pCmapTbl, 3, 0); + if (!m_pCmap_3_1) + { + m_stuInitError = L"failure to load cmap subtable"; + m_ferr = kferrLoadCmapSubtable; + ReturnResult(kresFail); + } + if (!TtfUtil::CheckCmap31Subtable(m_pCmap_3_1)) + { + m_stuInitError = L"checking cmap subtable failed"; + m_ferr = kferrCheckCmapSubtable; + ReturnResult(kresFail); + } + + // If we have a bad base file, don't do Graphite stuff. + if (fBadBase || fMismatchedBase) + goto LUnexpected; + + // name + + // Currently the only stuff we're getting from the name table are our feature names, + // so use the version from the Graphite font (not the base font if any). + //////if (m_fUseSepBase) + ////// pgg->SetupGraphics(&chrpOriginal); + + // name - need feature names later + res = (pNameTbl = (byte *)pfont->getTable(TtfUtil::TableIdTag(ktiName), &cbNameSz)) ? kresOk : kresFail; + fOk = pNameTbl && (cbNameSz == 0 || TtfUtil::CheckTable(ktiName, pNameTbl, cbNameSz)); + if (!fOk) + { + m_stuInitError = L"could not locate name table"; + m_ferr = kferrFindNameTable; + ReturnResult(kresFail); + } + // Make a private copy of the name table for the engine's use. + m_pNameTbl = new byte[cbNameSz]; + std::copy(reinterpret_cast<const byte*>(pNameTbl), + reinterpret_cast<const byte*>(pNameTbl) + cbNameSz, m_pNameTbl); + + /**** + Obtain font name from InitNew() now instead of reading from font file. InitNew should + should have a correct font name passed to it since it should come from a font registered + by GrFontInst.exe. This commented code could be use to verify name in font file matches. + NOTE: if we ever use this code again, make sure we're using the base font name table, + not the Graphite wrapper font name table. + // find the font family name + if (!TtfUtil::Get31EngFamilyInfo(vbName.Begin(), lnNameOff, lnNameSz)) + { // use Name table which is Symbol encode instead + // this could cause problems if a real Symbol writing system is used in the name table + // however normally real Unicode values are used instead a Symbol writing system + if (!TtfUtil::Get30EngFamilyInfo(vbName.Begin(), lnNameOff, lnNameSz)) + { + ReturnResult(kresFail); + } + // test for Symbol writing system. first byte of Unicode id should be 0xF0 + if (vbName[lnNameOff + 1] == (unsigned char)0xF0) // 1 - Unicode id is big endian + ReturnResult(kresFail); + } + if (!TtfUtil::SwapWString(vbName.Begin() + lnNameOff, lnNameSz / isizeof(utf16))) + ReturnResult(kresFail); + + m_stuFaceName = std::wstring((utf16 *)(vbName.begin() + lnNameOff), lnNameSz / isizeof(utf16)); + ****/ + + // Silf + res = (pSilfTbl = pfont->getTable(TtfUtil::TableIdTag(ktiSilf), &cbSilfSz)) ? kresOk : kresFail; + fOk = pSilfTbl && (cbSilfSz == 0 || TtfUtil::CheckTable(ktiSilf, pSilfTbl, cbSilfSz)); + if (!fOk) + { + m_stuInitError = L"could not load Silf table for Graphite rendering"; + m_ferr = kferrLoadSilfTable; + goto LUnexpected; + } + + // Feat + res = (pFeatTbl = pfont->getTable(TtfUtil::TableIdTag(ktiFeat), &cbFeatSz)) ? kresOk : kresFail; + fOk = pFeatTbl && (cbFeatSz == 0 || TtfUtil::CheckTable(ktiFeat, pFeatTbl, cbFeatSz)); + if (!fOk) + { + // TODO: just create an empty set of features, since this is not disastrous. + m_stuInitError = L"could not load Feat table for Graphite rendering"; + m_ferr = kferrLoadFeatTable; + goto LUnexpected; + } + + // Glat + res = (pGlatTbl = pfont->getTable(TtfUtil::TableIdTag(ktiGlat), &cbGlatSz)) ? kresOk : kresFail; + fOk = pGlatTbl && (cbGlatSz == 0 || TtfUtil::CheckTable(ktiGlat, pGlatTbl, cbGlatSz)); + if (!fOk) + { + m_stuInitError = L"could not load Glat table for Graphite rendering"; + m_ferr = kferrLoadGlatTable; + goto LUnexpected; + } + + // Gloc + res = (pGlocTbl = pfont->getTable(TtfUtil::TableIdTag(ktiGloc), &cbGlocSz)) ? kresOk : kresFail; + fOk = pGlocTbl && (cbGlocSz == 0 || TtfUtil::CheckTable(ktiGloc, pGlocTbl, cbGlocSz)); + if (!fOk) + { + m_stuInitError = L"could not load Gloc table for Graphite rendering"; + m_ferr = kferrLoadGlocTable; + goto LUnexpected; + } + + // Sill + try { + res = (pSillTbl = pfont->getTable(TtfUtil::TableIdTag(ktiSill), &cbSillSz)) ? kresOk : kresFail; + fOk = pSillTbl && (cbSillSz == 0 || TtfUtil::CheckTable(ktiSill, pSillTbl, cbSillSz)); + } + catch (...) + { + fOk = true; + pSillTbl = NULL; + } + // if table couldn't be loaded, this is not disastrous. + +// ibGlocStart = cbGlatTbl; + fOk = CheckTableVersions(&grstrm, + (byte *)pSilfTbl, 0, + (byte *)pGlocTbl, 0, + (byte *)pFeatTbl, 0, + &m_fxdBadVersion); + if (!fOk) + { + wchar_t rgch[20]; + swprintf(rgch, 20, L"%d", m_fxdBadVersion >> 16); + std::wstring stu = L"unsupported version ("; + stu.append(rgch); + swprintf(rgch, 20, L"%d", m_fxdBadVersion & 0x0000FFFF); + stu.append(L"."); + stu.append(rgch); + stu.append(L") of Graphite tables"); + m_stuInitError.assign(stu.c_str()); + m_ferr = kferrBadVersion; + goto LUnexpected; + } + + try + { + // Parse the "Silf" table. + grstrm.OpenBuffer((byte*)pSilfTbl, cbSilfSz); + int chwGlyphIDMax, fxdVersion; + bool f = ReadSilfTable(grstrm, 0, 0, &chwGlyphIDMax, &fxdVersion); + grstrm.Close(); + if (!f) + { + m_ferr = kferrReadSilfTable; + fexptn.errorCode = m_ferr; + throw fexptn; + } + + // Parse the "Gloc" and "Glat" tables. + { + GrBufferIStream grstrmGlat; + + grstrm.OpenBuffer((byte *)pGlocTbl, cbGlocSz); + grstrmGlat.OpenBuffer((byte *)pGlatTbl, cbGlatSz); + f = ReadGlocAndGlatTables(grstrm, 0, grstrmGlat, 0, chwGlyphIDMax, fxdVersion); + grstrm.Close(); + grstrmGlat.Close(); + if (!f) + { + m_ferr = kferrReadGlocGlatTable; + fexptn.errorCode = m_ferr; + throw fexptn; + } + } + + // Parse the "Feat" table. + grstrm.OpenBuffer((byte *)pFeatTbl, cbFeatSz); + f = ReadFeatTable(grstrm, 0); + grstrm.Close(); + if (!f) + { + m_ferr = kferrReadFeatTable; + fexptn.errorCode = m_ferr; + throw fexptn; + } + + // Parse the "Sill" table. + if (pSillTbl) + { + grstrm.OpenBuffer((byte *)pSillTbl, cbFeatSz); + f = ReadSillTable(grstrm, 0); + grstrm.Close(); + if (!f) + { + m_ferr = kferrReadSillTable; + fexptn.errorCode = m_ferr; + throw fexptn; + } + } + else + m_langtbl.CreateEmpty(); + } + catch (...) + { + fSilf = false; + m_resFontRead = kresUnexpected; + try { + DestroyContents(false); + } + catch (...) + {} + goto LUnexpected; + } + + m_stuErrCtrlFile.erase(); + m_nFontCheckSum = nCheckSum; + m_resFontRead = kresOk; + m_ferr = kferrOkay; + ReturnResult(kresOk); + +LUnexpected: + // Don't do this, because it is possible to use a base font with an empty Graphite + // engine: + //if (m_fUseSepBase || m_stuBaseFaceName.Length() > 0) + //{ + // SwitchGraphicsFont(pgg, false); // back to Graphite table font + // m_stuBaseFaceName.erase(); + // m_fUseSepBase = false; + //} + + CreateEmpty(); + m_nFontCheckSum = nCheckSum; + m_resFontRead = (fSilf) ? kresUnexpected : kresFalse; + + fexptn.errorCode = m_ferr; + fexptn.version = m_fxdBadVersion >> 16; + fexptn.subVersion = m_fxdBadVersion & 0x0000FFFF; + throw fexptn; + + ReturnResult(m_resFontRead); +} + + +//void FontFace::DbgCheckFontFace() +//{ +// Assert(m_cfonts < 5000); +// Assert(m_cfonts >= 0); +// wchar_t chw0 = m_pgreng->m_stuFaceName[0]; +// Assert(chw0 >= 0x0040); // A +// Assert(chw0 <= 0x007A); // z +//} + + +} // namespace gr + +//:End Ignore diff --git a/Build/source/libs/graphite-engine/src/segment/FontFace.h b/Build/source/libs/graphite-engine/src/segment/FontFace.h new file mode 100644 index 00000000000..70be57d5cd2 --- /dev/null +++ b/Build/source/libs/graphite-engine/src/segment/FontFace.h @@ -0,0 +1,228 @@ +/*-------------------------------------------------------------------- +Copyright (C) 1999, 2001 SIL International. All rights reserved. + +Distributable under the terms of either the Common Public License or the +GNU Lesser General Public License, as specified in the LICENSING.txt file. + +File: FontFace.h +Responsibility: Sharon Correll +Last reviewed: Not yet. + +Description: + Contains the definition of the FontFace class. + TODO: Merge with GrEngine. +----------------------------------------------------------------------------------------------*/ +#ifdef _MSC_VER +#pragma once +#endif +#ifndef FONTFACE_INCLUDED +#define FONTFACE_INCLUDED +#include <limits> + +#ifdef _MSC_VER +#include <crtdbg.h> +#endif + +namespace gr +{ + +class IGrJustifier; +class Segment; + + +/*---------------------------------------------------------------------------------------------- + The GrEngine serves as the top level object that knows how to run Graphite tables + and generate Graphite segments. + + Primarily, this class implements IRenderEngine, which allows it to serve as a FW + rendering engine. It also implements ISimpleInit, a general interface for initializing + using a string. Finally, it implements ITraceControl, a very simple interface which + allows a client to flip a flag indicating whether or not we want to output a log of + the Graphite transduction process. + + Hungarian: greng +----------------------------------------------------------------------------------------------*/ +class FontFace +{ +public: + FontFace() + { + m_cfonts = 0; + } + + ~FontFace() + { + s_pFontCache->RemoveFontFace(m_pgreng->FaceName(), m_pgreng->Bold(), m_pgreng->Italic()); + delete m_pgreng; + } + + void IncFontCount() + { + m_cfonts++; + } + void DecFontCount() + { + m_cfonts--; + if (m_cfonts <= 0 && s_pFontCache->GetFlushMode() == kflushAuto) + delete this; + } + + bool NoFonts() + { + return (m_cfonts <= 0); + } + + static FontFace * GetFontFace(Font * pfont, + std::wstring strFaceName, bool fBold, bool fItalic); + + GrResult InitFontFace(Font * pfont, + std::wstring stuFaceName, bool fBold, bool fItalic); + + FontErrorCode IsValidForGraphite(int * pnVersion, int * pnSubVersion) + { + return m_pgreng->IsValidForGraphite(pnVersion, pnSubVersion); + } + + static void ZapFontCache() + { + if (s_pFontCache) + { + s_pFontCache->AssertEmpty(); + delete s_pFontCache; + } + s_pFontCache = NULL; + } + + static void SetFlushMode(int flush) + { + if (s_pFontCache == NULL) + s_pFontCache = new FontCache; + s_pFontCache->SetFlushMode(flush); + } + static int GetFlushMode() + { + if (s_pFontCache == NULL) + s_pFontCache = new FontCache; + return s_pFontCache->GetFlushMode(); + } + + // Temporary, until interface gets thoroughly reworked: + GrEngine * GraphiteEngine() + { + return m_pgreng; + } + + // Feature access: + size_t NumberOfFeatures() + { + return m_pgreng->NumberOfFeatures_ff(); + } + featid FeatureID(size_t ifeat) + { + return m_pgreng->FeatureID_ff(ifeat); + } + size_t FeatureWithID(featid id) + { + return m_pgreng->FeatureWithID_ff(id); + } + bool GetFeatureLabel(size_t ifeat, lgid language, utf16 * label) + { + return m_pgreng->GetFeatureLabel_ff(ifeat, language, label); + } + int GetFeatureDefault(size_t ifeat) // index of default setting + { + return m_pgreng->GetFeatureDefault_ff(ifeat); + } + size_t NumberOfSettings(size_t ifeat) + { + return m_pgreng->NumberOfSettings_ff(ifeat); + } + int GetFeatureSettingValue(size_t ifeat, size_t ifset) + { + return m_pgreng->GetFeatureSettingValue_ff(ifeat, ifset); + } + bool GetFeatureSettingLabel(size_t ifeat, size_t ifset, lgid language, utf16 * label) + { + return m_pgreng->GetFeatureSettingLabel_ff(ifeat, ifset, language, label); + } + // Feature-label language access: + size_t NumberOfFeatLangs() + { + return m_pgreng->NumberOfFeatLangs_ff(); + } + short FeatLabelLang(int ilang) + { + return m_pgreng->GetFeatLabelLang_ff(ilang); + } + // Language access: + size_t NumberOfLanguages() + { + return m_pgreng->NumberOfLanguages_ff(); + } + isocode LanguageCode(size_t ilang) + { + return m_pgreng->GetLanguageCode_ff(ilang); + } + + // Script Direction access: + ScriptDirCode ScriptDirection() const throw() + { + unsigned int script_dirs = 0; + OLECHAR err_dummy = 0; + m_pgreng->get_ScriptDirection(&script_dirs, &err_dummy, 1); + return ScriptDirCode(script_dirs); + } +public: + // For use in segment creation: + void RenderLineFillSegment(Segment * pseg, Font * pfont, ITextSource * pts, + LayoutEnvironment & layout, + toffset ichStart, toffset ichStop, float xsMaxWidth, bool fBacktracking) + { + m_pgreng->MakeSegment(pseg, pfont, pts, NULL, layout, + ichStart, ichStop, xsMaxWidth, fBacktracking, false, 0, kestMoreLines); + } + void RenderRangeSegment(Segment * pseg, Font * pfont, + ITextSource * pts, LayoutEnvironment & layout, + toffset ichStart, toffset ichStop) + { + m_pgreng->MakeSegment(pseg, pfont, pts, NULL, layout, + ichStart, ichStop, kPosInfFloat, false, false, 0, kestMoreLines); + } + void RenderJustifiedSegment(Segment * pseg, Font * pfont, + ITextSource * pts, LayoutEnvironment & layout, + toffset ichStart, toffset ichStop, float xsCurrentWidth, float xsDesiredWidth) + { + m_pgreng->MakeSegment(pseg, pfont, pts, NULL, layout, + ichStart, ichStop, xsCurrentWidth, false, true, xsDesiredWidth, kestMoreLines); + } + + // Debugging. + //static bool DbgCheckFontCache() + //{ + // if (s_pFontCache) + // return s_pFontCache->DbgCheckFontCache(); + // else + // return true; + //} + //void DbgCheckFontFace(); + +protected: + // Number of fonts in existence that use this face; when it goes to zero, delete. + int m_cfonts; + + // Static variable: + static FontCache * s_pFontCache; + + // Member variables: + GrEngine * m_pgreng; +}; + +} // namespace gr + + +#if defined(GR_NO_NAMESPACE) +using namespace gr; +#endif + + +#endif // !FONTFACE_INCLUDED diff --git a/Build/source/libs/graphite-engine/src/segment/GrCharStream.cpp b/Build/source/libs/graphite-engine/src/segment/GrCharStream.cpp new file mode 100644 index 00000000000..20b5a46ab76 --- /dev/null +++ b/Build/source/libs/graphite-engine/src/segment/GrCharStream.cpp @@ -0,0 +1,652 @@ +/*--------------------------------------------------------------------*//*:Ignore this sentence. +Copyright (C) 1999, 2001 SIL International. All rights reserved. + +Distributable under the terms of either the Common Public License or the +GNU Lesser General Public License, as specified in the LICENSING.txt file. + +File: GrCharStream.cpp +Responsibility: Sharon Correll +Last reviewed: Not yet. + +Description: + Implements the GrCharStream class. +----------------------------------------------------------------------------------------------*/ + +//:>******************************************************************************************** +//:> Include files +//:>******************************************************************************************** +#include "Main.h" + +#ifdef _MSC_VER +#pragma hdrstop +#endif +#undef THIS_FILE +DEFINE_THIS_FILE + +//:End Ignore + +//:>******************************************************************************************** +//:> Forward declarations +//:>******************************************************************************************** + +//:>******************************************************************************************** +//:> Local Constants and static variables +//:>******************************************************************************************** + +namespace gr +{ + +//:>******************************************************************************************** +//:> Methods +//:>******************************************************************************************** + +/*---------------------------------------------------------------------------------------------- + Constructor. +----------------------------------------------------------------------------------------------*/ +GrCharStream::GrCharStream(ITextSource * pgts, int ichrMin, int ichrLim, + bool fStartLine, bool fEndLine) + : m_pgts(pgts), + m_ichrMin(ichrMin), + m_ichrLim(ichrLim), + m_ichrPos(ichrMin), + m_fStartLine(fStartLine), + m_fEndLine(fEndLine), + m_cchrBackedUp(0), + m_ichrRunMin(0), + m_ichrRunLim(0), + m_ichlRunOffset(kPosInfinity), + m_ichrRunOffset(kPosInfinity) +{ + #if 0 // ??? surely m_cnRunMax and m_prgnRunText are undefined at this point + if (m_cchlRunMax > -1) + delete[] m_prgchlRunText; + #endif + m_cchlRunMax = -1; + m_prgchlRunText = NULL; + + m_vislotNextChunkMap.clear(); + + m_utf = m_pgts->utfEncodingForm(); +} + +/*---------------------------------------------------------------------------------------------- + Restart the stream when an error has a occurred and we need to fall back to dumb rendering. +----------------------------------------------------------------------------------------------*/ +void GrCharStream::Restart() +{ + if (m_cchlRunMax > -1) + delete[] m_prgchlRunText; + + m_ichrPos = m_ichrMin; + m_cchrBackedUp = 0; + m_ichrRunMin = 0; + m_ichrRunLim = 0; + m_ichlRunOffset = kPosInfinity; + m_ichrRunOffset = kPosInfinity; + + m_cchlRunMax = -1; + m_prgchlRunText = NULL; + + m_vislotNextChunkMap.clear(); +} + +/*---------------------------------------------------------------------------------------------- + Get the next character from the stream. + + @param pichwSegOffset - index of this character from the beginning of the segment + @param pcchRaw - number of raw (UTF-16 or UTF-8) chars consumed to get one complete + Unicode codepoint. +----------------------------------------------------------------------------------------------*/ +int GrCharStream::NextGet(GrTableManager * ptman, + GrFeatureValues * pfval, int * pichrSegOffset, int * pcchr) +{ + if (AtEnd()) + { + Assert(m_ichrPos == m_ichrLim); + return 0; + } + + int m_ichrMinBackedUp = m_ichrMin - m_cchrBackedUp; + + if ((m_ichrRunOffset + m_ichrRunMin) >= m_ichrRunLim) + { + // Get the next run of characters + + std::pair<toffset, toffset> pairRange = m_pgts->propertyRange(m_ichrPos); + m_ichrRunMin = pairRange.first; + m_ichrRunLim = pairRange.second; + + // The only time the run extends before what we want is on the first run. + Assert((m_ichrRunMin == m_ichrPos) // m_ichwPos is beginning of run + || (m_ichrPos == m_ichrMinBackedUp)); // first run + // Pretend like the run starts where we need it: + m_ichrRunMin = max(m_ichrRunMin, m_ichrMinBackedUp); + Assert(m_ichrRunMin == m_ichrPos); + + if (m_cchlRunMax != -1 && m_cchlRunMax < m_ichrRunLim - m_ichrRunMin) + { + // Text buffer is too small: delete it and create a new one. + delete[] m_prgchlRunText; + m_cchlRunMax = -1; + } + + if (m_cchlRunMax == -1) + { + // Note that combining surrogates or UTF-8 will always result in the same or fewer + // number of characters needed in m_prgchlRunText, so the following is safe: + m_cchlRunMax = m_ichrRunLim - m_ichrRunMin; + m_prgchlRunText = new int[m_cchlRunMax]; + } + + utf32 * prgchlRunText32 = NULL; + utf16 * prgchwRunText16 = NULL; + utf8 * prgchsRunText8 = NULL; + + int cchGot; + switch (m_utf) + { + case kutf8: + prgchsRunText8 = new utf8[m_ichrRunLim - m_ichrRunMin]; + cchGot = m_pgts->fetch(m_ichrRunMin, m_ichrRunLim - m_ichrRunMin, prgchsRunText8); + break; + case kutf16: + prgchwRunText16 = new utf16[m_ichrRunLim - m_ichrRunMin]; + cchGot = m_pgts->fetch(m_ichrRunMin, m_ichrRunLim - m_ichrRunMin, prgchwRunText16); + break; + default: + Assert(false); + // Assume UTF-32: fall through + case kutf32: + prgchlRunText32 = new utf32[m_ichrRunLim - m_ichrRunMin]; + cchGot = m_pgts->fetch(m_ichrRunMin, m_ichrRunLim - m_ichrRunMin, prgchlRunText32); + break; + } + + int ichlUtf32 = 0; + for (int ichr = 0; ichr < m_ichrRunLim - m_ichrRunMin; ichr++) + { + if (ichr == m_ichrPos - m_ichrRunMin) + { + m_ichrRunOffset = ichr; + m_ichlRunOffset = ichlUtf32; + } + Assert(m_ichrRunOffset != kPosInfinity || ichr < m_ichrPos - m_ichrRunMin); + + int cchrUsed; + switch (m_utf) + { + case kutf8: + m_prgchlRunText[ichlUtf32] = Utf8ToUtf32(prgchsRunText8 + ichr, + m_ichrRunLim - m_ichrRunMin - ichr, &cchrUsed); + break; + case kutf16: + m_prgchlRunText[ichlUtf32] = Utf16ToUtf32(prgchwRunText16 + ichr, + m_ichrRunLim - m_ichrRunMin - ichr, &cchrUsed); + break; + default: + Assert(false); + // Assume UTF-32: fall through + case kutf32: + m_prgchlRunText[ichlUtf32] = prgchlRunText32[ichlUtf32]; + cchrUsed = 1; + break; + } + + m_vislotNextChunkMap.push_back(ichlUtf32); + for (int i = 1; i < cchrUsed; i++) + m_vislotNextChunkMap.push_back(-1); + ichr += cchrUsed - 1; + ichlUtf32++; + } + delete[] prgchlRunText32; + delete[] prgchwRunText16; + delete[] prgchsRunText8; + + while (m_ichrLim - m_ichrRunMin < signed(m_vislotNextChunkMap.size()) + && m_vislotNextChunkMap[m_ichrLim - m_ichrRunMin] == -1) + { + // Invalid surrogate boundary; this is really a bug in the application program, + // but adjust here to avoid a crash! + m_ichrLim--; + } + + SetUpFeatureValues(ptman, m_ichrPos); + + Assert(m_ichrRunOffset >= 0); + Assert(m_ichrRunOffset < m_ichrRunLim - m_ichrRunMin); + Assert(m_vislotNextChunkMap.size() == (unsigned) m_ichrRunLim - m_ichrMinBackedUp); + } + + int chlRet = m_prgchlRunText[m_ichlRunOffset]; + *pfval = m_fvalRunFeats; + *pichrSegOffset = m_ichrPos - m_ichrMin; // offset from the official start of the segment + // of the first 16-bit char + int ichrPosPrev = m_ichrPos; + ++m_ichlRunOffset; + do { + ++m_ichrPos; + ++m_ichrRunOffset; + } while (m_ichrPos - m_ichrMinBackedUp < signed(m_vislotNextChunkMap.size()) + && m_vislotNextChunkMap[m_ichrPos - m_ichrMinBackedUp] == -1); + + *pcchr = m_ichrPos - ichrPosPrev; // number of raw chars consumed + Assert(m_utf != kutf8 || *pcchr <= 6); + Assert(m_utf != kutf16 || *pcchr <= 2); + + Assert(m_ichrPos == (m_ichrRunMin + m_ichrRunOffset)); + Assert(m_ichrPos <= m_ichrLim); + + /* KLUDGE for debugging + if (m_ichrPos == 1) + nRet = 0x1018; + else if (m_ichrPos == 2) + nRet = 0x1039; + else if (m_ichrPos == 3) + nRet = 0x101b; + else if (m_ichrPos == 4) + nRet = 0x1032; + else if (m_ichrPos == 5) + nRet = 0x1037; + else if (m_ichrPos == 6) + nRet = 0x1037; + */ + + return chlRet; +} + +/*---------------------------------------------------------------------------------------------- + Convert UTF-8 to UTF-32--a single character's worth. Also pass back the number of 8-bit + items consumed. +----------------------------------------------------------------------------------------------*/ +utf32 GrCharStream::Utf8ToUtf32(utf8 * prgchs, int cchs, int * pcchsUsed) +{ + if (cchs <= 0) + { + *pcchsUsed = 0; + return 0; + } + + long chlRet = DecodeUtf8(prgchs, cchs, pcchsUsed); + Assert(chlRet > 0); + if (chlRet == -1) + { + // Some error occurred. Just treat the UTF-8 as UTF-32. + *pcchsUsed = 1; + return (utf32)prgchs[0]; + } + else + return chlRet; +} + +/*---------------------------------------------------------------------------------------------- + Convert UTF-16 to UTF-32--a single character's worth. Also pass back the number of 16-bit + items consumed. +----------------------------------------------------------------------------------------------*/ +utf32 GrCharStream::Utf16ToUtf32(utf16 * prgchw, int cchw, int * pcchwUsed) +{ + if (cchw <= 0) + { + *pcchwUsed = 0; + return 0; + } + + unsigned int nUtf32; + bool fSurrogate = FromSurrogatePair(prgchw[0], ((cchw < 2) ? 0 : prgchw[1]), &nUtf32); + *pcchwUsed = (fSurrogate) ? 2 : 1; + return (int)nUtf32; + +/* + // For testing: + int nCharRet; + if (cchw >= 2 && prgchw[0] == '^' && prgchw[1] == 'a') + { + nCharRet = 'A'; + *pcchwUsed = 2; + } + else if (cchw >= 3 && prgchw[0] == '#' && prgchw[1] == '#' && + prgchw[2] == 'B') + { + nCharRet = 'b'; + *pcchwUsed = 3; + } + else if (cchw >= 1) + { + nCharRet = prgchw[0]; + *pcchwUsed = 1; + } + else + { + nCharRet = 0; + *pcchwUsed = 0; + } + return nCharRet; +*/ +} + +/*---------------------------------------------------------------------------------------------- + Return true if the given position is at the boundary of a Unicode character; return false + if it is between two parts of pair of surrogates. +----------------------------------------------------------------------------------------------*/ +bool GrCharStream::AtUnicodeCharBoundary(ITextSource * pgts, int ichr) +{ + int cchr = pgts->getLength(); + if (ichr <= 0 || ichr >= cchr) + return true; + + UtfType utf = pgts->utfEncodingForm(); + + // Note that we never need more than a handful of characters. + int ichrMinGet = max(0, ichr - 1); + int ichrLimGet = ichr + 1; + utf16 rgchwText16[3]; + utf8 rgchsText8[3]; + bool fRet; + switch (utf) + { + case kutf8: + pgts->fetch(ichrMinGet, ichrLimGet - ichrMinGet, rgchsText8); + fRet = AtUnicodeCharBoundary(rgchsText8, ichrLimGet - ichrMinGet, + ichr - ichrMinGet, utf); + break; + case kutf16: + pgts->fetch(ichrMinGet, ichrLimGet - ichrMinGet, rgchwText16); + fRet = AtUnicodeCharBoundary(rgchwText16, ichrLimGet - ichrMinGet, + ichr - ichrMinGet, utf); + break; + default: + Assert(false); + case kutf32: + fRet = true; + break; + } + + return fRet; +} + +/*---------------------------------------------------------------------------------------------- + Return true if the given position is at the boundary of a Unicode character; return false + if it is between two parts of pair of surrogates. +----------------------------------------------------------------------------------------------*/ +bool GrCharStream::AtUnicodeCharBoundary(utf16 * prgchw, int cchw, int ichr, UtfType utf) +{ + Assert(ichr >= 0); + Assert(ichr <= cchw); + + if (ichr == 0) + return true; + if (ichr >= cchw) + return true; + + switch (utf) + { + case kutf16: + { + unsigned int nUtf32; + bool fMiddleOfPair = FromSurrogatePair(prgchw[ichr - 1], prgchw[ichr], &nUtf32); + return !fMiddleOfPair; + } + case kutf8: + { + // 16-bit buffer being treated like UTF-8. + utf8 rgchs[2]; + rgchs[1] = (utf8)prgchw[ichr]; + return AtUnicodeCharBoundary(rgchs, 2, 1, kutf8); + } + case kutf32: + default: + return true; + } + +/* + // temp stuff for debugging: + if (ichr > 0 && cchw >= 1 && prgchw[ichr-1] == '^' && prgchw[ichr] == 'a') + { + return false; + } + if (ichr > 0 && cchw >= 2 && prgchw[ichr-1] == '#' && prgchw[ichr] == '#' && + prgchw[ichr+1] == 'B') + { + return false; + } + if (ichr > 1 && cchw >= 1 && prgchw[ichr-2] == '#' && prgchw[ichr-1] == '#' && + prgchw[ichr] == 'B') + { + return false; + } + + return true; +*/ +} + +bool GrCharStream::AtUnicodeCharBoundary(utf8 * prgchs, int cchs, int ichs, UtfType utf) +{ + Assert(ichs >= 0); + Assert(ichs <= cchs); + + Assert(utf == kutf8); + + if (ichs == 0) + return true; + if (ichs >= cchs) + return true; + + if (cchs == 0) + return true; + + // A bit pattern of 10xxxxxx indicates a continuation of a sequence + // (11xxxxxx means the first byte of a sequence, 0xxxxxxx means a single character). + + utf8 chsTest = prgchs[ichs] & 0xC0; + return (chsTest != 0x80); +} + +/*---------------------------------------------------------------------------------------------- + Convert the given pair of characters into a single 32-bit Unicode character. Return + true if they are a legitimate surrogate pair. If not, just return the first of the + two 16-bit characters. +----------------------------------------------------------------------------------------------*/ +bool GrCharStream::FromSurrogatePair(utf16 chwIn1, utf16 chwIn2, unsigned int * pchlOut) +{ + if ((chwIn1 < kzUtf16HighFirst) || (chwIn1 > kzUtf16HighLast) + || (chwIn2 < kzUtf16LowFirst) || (chwIn2 > kzUtf16LowLast)) + { + // Not a surrogate + *pchlOut = (unsigned int)chwIn1; + return false; + } + else + { + *pchlOut = ((chwIn1 - kzUtf16HighFirst) << kzUtf16Shift) + chwIn2 + kzUtf16Inc; + return true; + } +} + +/*---------------------------------------------------------------------------------------------- + Decode 1-6 bytes in the character string from UTF-8 format to Unicode (UCS-4). + As a side-effect, cbOut is set to the number of UTF-8 bytes consumed. + + @param rgchUtf8 Pointer to a a character array containing UTF-8 data. + @param cchUtf8 Number of characters in the array. + @param cbOut Reference to an integer for holding the number of input (8-bit) characters + consumed to produce the single output Unicode character. + + @return A single Unicode (UCS-4) character. If an error occurs, return -1. +----------------------------------------------------------------------------------------------*/ +long GrCharStream::DecodeUtf8(const utf8 * rgchUtf8, int cchUtf8, int * pcbOut) +{ + // check for valid input + AssertArray(rgchUtf8, cchUtf8); + if ((cchUtf8 == 0) || (rgchUtf8[0] == '\0')) + { + *pcbOut = (cchUtf8) ? 1 : 0; + return 0; + } + // + // decode the first byte of the UTF-8 sequence + // + long lnUnicode; + int cbExtra; + int chsUtf8 = *rgchUtf8++ & 0xFF; + if (chsUtf8 >= kzUtf8Flag6) // 0xFC + { + lnUnicode = chsUtf8 & kzUtf8Mask6; + cbExtra = 5; + } + else if (chsUtf8 >= kzUtf8Flag5) // 0xF8 + { + lnUnicode = chsUtf8 & kzUtf8Mask5; + cbExtra = 4; + } + else if (chsUtf8 >= kzUtf8Flag4) // 0xF0 + { + lnUnicode = chsUtf8 & kzUtf8Mask4; + cbExtra = 3; + } + else if (chsUtf8 >= kzUtf8Flag3) // 0xE0 + { + lnUnicode = chsUtf8 & kzUtf8Mask3; + cbExtra = 2; + } + else if (chsUtf8 >= kzUtf8Flag2) // 0xC0 + { + lnUnicode = chsUtf8 & kzUtf8Mask2; + cbExtra = 1; + } + else // 0x00 + { + lnUnicode = chsUtf8; + cbExtra = 0; + } + if (cbExtra >= cchUtf8) + { + return -1; + } + + switch (cbExtra) + { + case 5: + lnUnicode <<= kzUtf8ByteShift; + chsUtf8 = *rgchUtf8++ & 0xFF; + if ((chsUtf8 & ~kzByteMask) != 0x80) + return -1; + lnUnicode += chsUtf8 & kzByteMask; + // fall through + case 4: + lnUnicode <<= kzUtf8ByteShift; + chsUtf8 = *rgchUtf8++ & 0xFF; + if ((chsUtf8 & ~kzByteMask) != 0x80) + return -1; + lnUnicode += chsUtf8 & kzByteMask; + // fall through + case 3: + lnUnicode <<= kzUtf8ByteShift; + chsUtf8 = *rgchUtf8++ & 0xFF; + if ((chsUtf8 & ~kzByteMask) != 0x80) + return -1; + lnUnicode += chsUtf8 & kzByteMask; + // fall through + case 2: + lnUnicode <<= kzUtf8ByteShift; + chsUtf8 = *rgchUtf8++ & 0xFF; + if ((chsUtf8 & ~kzByteMask) != 0x80) + return -1; + lnUnicode += chsUtf8 & kzByteMask; + // fall through + case 1: + lnUnicode <<= kzUtf8ByteShift; + chsUtf8 = *rgchUtf8++ & 0xFF; + if ((chsUtf8 & ~kzByteMask) != 0x80) + return -1; + lnUnicode += chsUtf8 & kzByteMask; + break; + case 0: + // already handled + break; + default: + Assert(false); + } + if ((unsigned long)lnUnicode > kzUnicodeMax) + { + return -1; + } + *pcbOut = cbExtra + 1; + return lnUnicode; +} + +/*---------------------------------------------------------------------------------------------- + Get the current feature values and character properties from the stream. +----------------------------------------------------------------------------------------------*/ +void GrCharStream::CurrentFeatures(GrTableManager * ptman, GrFeatureValues * pfval) +{ + if (m_ichrRunOffset == kPosInfinity) + { + // Not yet set up. + if (AtEnd()) + { + // Empty stream; no valid values. + Assert(false); + return; + } + + // Get the settings from the first character. + int ichrSegOffset; + int ichrPosSave = m_ichrPos; + int ichrRunOffsetSave = m_ichrRunOffset; + int ichlRunOffsetSave = m_ichlRunOffset; + int cMapSize = m_vislotNextChunkMap.size(); + int cchrConsumed; + NextGet(ptman, pfval, &ichrSegOffset, &cchrConsumed); + // Put the character back. + m_ichrPos = ichrPosSave; + m_ichrRunOffset = ichrRunOffsetSave; + m_ichlRunOffset = ichlRunOffsetSave; + while (signed(m_vislotNextChunkMap.size()) > cMapSize) + m_vislotNextChunkMap.pop_back(); + } + else + { + *pfval = m_fvalRunFeats; + //*ppchrp = &m_chrpRun; + } +} + +/*---------------------------------------------------------------------------------------------- + Read the feature settings from the text properties. + + Eventually, handle style index here? +----------------------------------------------------------------------------------------------*/ +void GrCharStream::SetUpFeatureValues(GrTableManager * ptman, int ichr) +{ + // Set all the features to their default values. + for (int i = 0; i < kMaxFeatures; ++i) + { + m_fvalRunFeats.m_rgnFValues[i] = ptman->DefaultForFeatureAt(i); + } + m_fvalRunFeats.m_nStyleIndex = 0; + + // Add in the defaults for the language of the text. + std::vector<featid> vnFeats; + std::vector<int> vnValues; + isocode lgcode = m_pgts->getLanguage(ichr); + ptman->DefaultsForLanguage(lgcode, vnFeats, vnValues); + size_t ifeatLp; + int ifeat; + for (ifeatLp = 0; ifeatLp < vnFeats.size(); ifeatLp++) + { + ptman->FeatureWithID(vnFeats[ifeatLp], &ifeat); + m_fvalRunFeats.m_rgnFValues[ifeat] = vnValues[ifeatLp]; + } + + // Add in the explicit feature settings. + FeatureSetting rgfset[kMaxFeatures]; + int cfeat = m_pgts->getFontFeatures(ichr, rgfset); + + for (ifeatLp = 0; ifeatLp < (size_t)cfeat; ifeatLp++) + { + ptman->FeatureWithID(rgfset[ifeatLp].id, &ifeat); + if (ifeat >= 0) + m_fvalRunFeats.m_rgnFValues[ifeat] = rgfset[ifeatLp].value; + } +} + +} // namespace gr diff --git a/Build/source/libs/graphite-engine/src/segment/GrCharStream.h b/Build/source/libs/graphite-engine/src/segment/GrCharStream.h new file mode 100644 index 00000000000..7a49b8fa030 --- /dev/null +++ b/Build/source/libs/graphite-engine/src/segment/GrCharStream.h @@ -0,0 +1,183 @@ +/*--------------------------------------------------------------------*//*:Ignore this sentence. +Copyright (C) 1999, 2001 SIL International. All rights reserved. + +Distributable under the terms of either the Common Public License or the +GNU Lesser General Public License, as specified in the LICENSING.txt file. + +File: GrCharStream.h +Responsibility: Sharon Correll +Last reviewed: Not yet. + +Description: + + +----------------------------------------------------------------------------------------------*/ +#ifdef _MSC_VER +#pragma once +#endif +#ifndef GR_CHARSTREAM_INCLUDED +#define GR_CHARSTREAM_INCLUDED + +//:End Ignore + +namespace gr +{ + +/*---------------------------------------------------------------------------------------------- + Makes a stream out of a text string that is serving as input to the process. + + Hungarian: chstrm +----------------------------------------------------------------------------------------------*/ +class GrCharStream +{ +public: + // Constructor and destructor: + GrCharStream(ITextSource * pgts, int ichrMin, int ichrLim, + bool fStartLine, bool fEndLine); + + ~GrCharStream() + { + if (m_cchlRunMax >= 0) + delete[] m_prgchlRunText; + } + + // Getters: + ITextSource * String() { return m_pgts; } + int Min() { return m_ichrMin; } + int Pos() { return m_ichrPos; } + int Lim() { return m_ichrLim; } + bool StartLine() { return m_fStartLine; } + bool EndLine() { return m_fEndLine; } + + // offset from the beginning of the segment + int SegOffset() { return m_ichrPos - m_ichrMin; } + int SegOffset(int ichr) { return ichr - m_ichrMin; } + + bool AtEnd() + { + return (m_ichrPos >= m_ichrLim); + } + + // The input string is empty. + bool IsEmpty() + { + return (m_ichrLim - m_ichrMin == 0); + } + + // When we are restarting in order to create a segment other than the first, + // back up the position in the stream slightly in order to reprocess a few characters + // before the line-break. + void Backup(int cchrToReprocess) + { + Assert(m_ichrPos >= cchrToReprocess); + m_ichrPos -= cchrToReprocess; + m_ichrRunOffset = kPosInfinity; + m_cchrBackedUp = cchrToReprocess; + } + + // When we found a hard-break in the input stream, we need to put it back, and since + // we're done, we set the end of the stream to that position. + void HitHardBreak() + { + m_ichrPos -= 1; + m_ichrRunOffset -= 1; + m_ichrLim = m_ichrPos; + } + + void Restart(); + + int NextGet(GrTableManager *, GrFeatureValues * pfval, int * ichrSegOffset, int * pcchr); + + void CurrentFeatures(GrTableManager * ptman, GrFeatureValues * pfval); + + // For transduction logging: + int GetLogData(GrTableManager * ptman, int * rgnChars, bool * rgfNewRun, + GrFeatureValues * rgfval, int cchwBackup, + int * pcchwMaxRaw); + void GetLogDataRaw(GrTableManager * ptman, int cchw, int cchwBackup, + int cchwMax16bit, int * prgnChars, + utf16 * prgchw2, utf16 * prgchw3, utf16 * prgchw4, utf16 * prgchw5, utf16 * prgchw6, + int * prgichwRaw); + +protected: + void SetUpFeatureValues(GrTableManager * ptman, int ichr); +public: + static utf32 Utf8ToUtf32(utf8 * prgchs8bit, int cchs, int * pcchsUsed); + static utf32 Utf16ToUtf32(utf16 * prgchw16bit, int cchw, int * pcchwUsed); + bool AtUnicodeCharBoundary(int cchr) + { + return AtUnicodeCharBoundary(m_pgts, cchr); + } + static bool AtUnicodeCharBoundary(ITextSource * pgts, int ichr); + static bool AtUnicodeCharBoundary(utf8 * prgchs, int cchs, int ichs, UtfType utf); + static bool AtUnicodeCharBoundary(utf16 * prgchw, int cchw, int ichr, UtfType utf); + static bool FromSurrogatePair(utf16 chwIn1, utf16 chwIn2, unsigned int * pch32Out); + static long DecodeUtf8(const utf8 * rgchUtf8, int cchUtf8, int * pcbOut); + +protected: + // Instance variables: + // Hungarian note: chr = raw characters, chl = long characters (UTF-32) + // chs = short characters (UTF-8), chw = wide characters (UTF-16) + ITextSource * m_pgts; // string to render + UtfType m_utf; // what encoding form the text-source uses + int m_ichrMin; // official start of segment relative to beginning of string + int m_ichrLim; // end of stream (potential end of seg) relative to beginning of string + int m_ichrPos; // stream position (0 = start of string) + bool m_fStartLine; // true if a line-break character should be prepended + bool m_fEndLine; // true if a line-break character should be appended + int m_cchrBackedUp; // number of characters backed up before beginning of segment + + // We read a run's worth of data at a time and cache it in the following variables: + int m_cchlRunMax; // size of buffer allocated + int * m_prgchlRunText; // buffer containing current run + int m_ichrRunMin; // start of run relative to beginning of string + int m_ichrRunLim; // end of run relative to beginning of string + int m_ichlRunOffset; // index into m_prgchlRunText; kPosInfinity if nothing set up + int m_ichrRunOffset; // corresponding index into text source + GrFeatureValues m_fvalRunFeats; + + std::vector<int> m_vislotNextChunkMap; // maps from 16-bit chars to 32-bit chars & glyphs + + enum + { + kzUtf8Mask1 = 0x7F, + kzUtf8Mask2 = 0x1F, + kzUtf8Mask3 = 0x0F, + kzUtf8Mask4 = 0x07, + kzUtf8Mask5 = 0x03, + kzUtf8Mask6 = 0x01 + }; + + enum + { + kzUtf8Flag1 = 0x00, + kzUtf8Flag2 = 0xC0, + kzUtf8Flag3 = 0xE0, + kzUtf8Flag4 = 0xF0, + kzUtf8Flag5 = 0xF8, + kzUtf8Flag6 = 0xFC + }; + + enum + { + kzByteMask = 0x3F, + kzByteMark = 0x80, + kzUtf8ByteShift = 6, + kzUnicodeMax = 0x7FFFFFFF, + }; + + enum + { + kzUtf16Shift = 10, + kzUtf16Inc = 0x2400, + kzUtf16HighFirst = 0xD800, + kzUtf16HighLast = 0xDBFF, + kzUtf16LowFirst = 0xDC00, + kzUtf16LowLast = 0xDFFF + }; +}; + +} // namespace gr + +#endif // !GR_CHARSTREAM_INCLUDED + diff --git a/Build/source/libs/graphite-engine/src/segment/GrClassTable.cpp b/Build/source/libs/graphite-engine/src/segment/GrClassTable.cpp new file mode 100644 index 00000000000..43316fdc04a --- /dev/null +++ b/Build/source/libs/graphite-engine/src/segment/GrClassTable.cpp @@ -0,0 +1,382 @@ +/*--------------------------------------------------------------------*//*:Ignore this sentence. +Copyright (C) 1999, 2001 SIL International. All rights reserved. + +Distributable under the terms of either the Common Public License or the +GNU Lesser General Public License, as specified in the LICENSING.txt file. + +File: GrClassTable.cpp +Responsibility: Sharon Correll +Last reviewed: Not yet. + +Description: + Implements the GrClassTable class and related classes. +----------------------------------------------------------------------------------------------*/ + +//:>******************************************************************************************** +//:> Include files +//:>******************************************************************************************** +#include "Main.h" + +#ifdef _MSC_VER +#pragma hdrstop +#endif +#undef THIS_FILE +DEFINE_THIS_FILE + +//:End Ignore + +//:>******************************************************************************************** +//:> Forward declarations +//:>******************************************************************************************** + +//:>******************************************************************************************** +//:> Local Constants and static variables +//:>******************************************************************************************** + +namespace gr +{ + +//:>******************************************************************************************** +//:> Methods +//:>******************************************************************************************** + +/*---------------------------------------------------------------------------------------------- + Fill in by reading from the file stream. +----------------------------------------------------------------------------------------------*/ +bool GrClassTable::ReadFromFont(GrIStream & grstrm, int fxdVersion) +{ + long lClassMapStart; + grstrm.GetPositionInFont(&lClassMapStart); + + // number of classes + m_ccls = grstrm.ReadUShortFromFont(); + if (m_ccls >= kMaxReplcmtClasses) + return false; // bad table + if (fxdVersion < 0x00030000 && m_ccls > kMaxReplcmtClassesV1_2) + return false; // bad table + + // number of linear classes + m_cclsLinear = grstrm.ReadUShortFromFont(); + if (m_cclsLinear > m_ccls) + return false; // bad table + + // class offsets + m_prgichwOffsets = new data16[m_ccls + 1]; + gAssert(m_prgichwOffsets); + if (!m_prgichwOffsets) + return false; // bad table + + data16 * pchw = m_prgichwOffsets; + int icls; + for (icls = 0; icls <= m_ccls; icls++, pchw++) + { + *pchw = grstrm.ReadUShortFromFont(); + } + + // Offsets are relative to the start class map; make them relative to the class list + // itself, and in terms of utf16s, not bytes. + long lClassesPos; + grstrm.GetPositionInFont(&lClassesPos); + int cbDiff = lClassesPos - lClassMapStart; + for (icls = 0; icls <= m_ccls; icls++) + { + m_prgichwOffsets[icls] = data16(int(m_prgichwOffsets[icls]) - cbDiff); + gAssert((m_prgichwOffsets[icls] & 0x00000001) == 0); + if ((m_prgichwOffsets[icls] & 0x00000001) != 0) + return false; // bad table + m_prgichwOffsets[icls] >>= 1; // divide by 2 + } + + // classes - slurp entire block (data remains in big-endian format) + m_prgchwBIGGlyphList = new data16[m_prgichwOffsets[m_ccls]]; + gAssert(m_prgchwBIGGlyphList); + if (!m_prgchwBIGGlyphList) + return false; // bad table + grstrm.ReadBlockFromFont(m_prgchwBIGGlyphList, + m_prgichwOffsets[m_ccls] * isizeof(data16)); + + return true; +} + +/*---------------------------------------------------------------------------------------------- + Set up an empty class table. +----------------------------------------------------------------------------------------------*/ +void GrClassTable::CreateEmpty() +{ + // number of classes + m_ccls = 0; + // number of linear classes + m_cclsLinear = 0; +} + +/*---------------------------------------------------------------------------------------------- + Search for the glyph ID using a fast binary search, and return the matching index. +----------------------------------------------------------------------------------------------*/ +int GrInputClass::FindIndex(gid16 gid) +{ + int digixInit = InitialSearchRange(); + int igixStart = StartSearch(); +#ifdef _DEBUG + int cgix = NumberOfGlyphs(); + int nPowerOf2 = 1; + while (nPowerOf2 <= cgix) + nPowerOf2 <<= 1; + nPowerOf2 >>= 1; + // Now nPowerOf2 is the max power of 2 <= cgix + gAssert((1 << LoopCount()) == nPowerOf2); // LoopCount() == log2(nPowerOf2) + gAssert(digixInit == nPowerOf2); + gAssert(igixStart == cgix - digixInit); +#endif // _DEBUG + + int digixCurr = digixInit; + + GrGlyphIndexPair * pgixCurr = m_pgixFirst + igixStart; + while (digixCurr > 0) + { + int nTest; + if (pgixCurr < m_pgixFirst) + nTest = -1; + else + nTest = pgixCurr->GlyphID() - gid; + + if (nTest == 0) + return pgixCurr->Index(); + + digixCurr >>= 1; // divide by 2 + if (nTest < 0) + pgixCurr += digixCurr; + else // (nTest > 0) + pgixCurr -= digixCurr; + } + + return -1; +} + +/*---------------------------------------------------------------------------------------------- + Return the selector index for the given glyph ID. +----------------------------------------------------------------------------------------------*/ +int GrClassTable::FindIndex(int icls, gid16 chwGlyphID) +{ + if (icls >= m_ccls) + { + gAssert(false); // bad compiler problem + return 0; + } + + if (icls < m_cclsLinear) + { + // The class is an output class--uses linear format--and is being used as an + // input class. Shouldn't happen if the compiler is working right. + gAssert(false); // comment out for test procedures + + // Do a slow linear search to find the glyph ID. + int ichwMin = m_prgichwOffsets[icls]; + int ichwLim = m_prgichwOffsets[icls+1]; + int cchw = ichwLim - ichwMin; + + for (int ichw = 0; ichw < cchw; ichw++) + { + if (GlyphAt(ichwMin + ichw) == chwGlyphID) + return ichw; + } + return -1; + } + + // Slurp the class into an instance so we can see what's there. + GrInputClass clsin; + int ichwMin = m_prgichwOffsets[icls]; + int ichwLim = m_prgichwOffsets[icls+1]; + clsin.CopyFrom(GlyphListLoc(ichwMin), (ichwLim - ichwMin)); + + // Do a fast binary search to find our glyph. + int iRet = clsin.FindIndex(chwGlyphID); + return iRet; +} + +/*---------------------------------------------------------------------------------------------- + Return the glyph ID at the selector index. +----------------------------------------------------------------------------------------------*/ +gid16 GrClassTable::GetGlyphID(int icls, int ichw) +{ + if (ichw < 0) + { + gAssert(false); + return 0; + } + + if (icls >= m_cclsLinear) + { + // The class is an input class--sorted by glyph ID--and is being used as an + // output class. Shouldn't happen if the compiler is working right. + gAssert(false); // comment out for test procedures + + if (icls >= m_ccls) // bad compiler problem + return 0; + + // Do a slow linear search to find the index and answer the matching glyph. + // Slurp the class into an instance so we can see what's there. + GrInputClass clsin; + int ichwMin = m_prgichwOffsets[icls]; + int ichwLim = m_prgichwOffsets[icls+1]; + clsin.CopyFrom(GlyphListLoc(ichwMin), (ichwLim - ichwMin)); + + int cgix = clsin.NumberOfGlyphs(); + for (int igix = 0; igix < cgix; igix++) + { + GrGlyphIndexPair * m_pgix = clsin.m_pgixFirst + igix; + if (m_pgix->Index() == ichw) + return m_pgix->GlyphID(); + } + return 0; + } + + int ichwMin = m_prgichwOffsets[icls]; + int ichwLim = m_prgichwOffsets[icls+1]; + + if (ichw >= ichwLim - ichwMin) + return 0; + else + return GlyphAt(ichwMin + ichw); +} + +/*---------------------------------------------------------------------------------------------- + Return the number of glyphs in the class. +----------------------------------------------------------------------------------------------*/ +int GrClassTable::NumberOfGlyphsInClass(int icls) +{ + int ichwMin = m_prgichwOffsets[icls]; + int ichwLim = m_prgichwOffsets[icls+1]; + + if (icls >= m_cclsLinear) + { + if (icls >= m_ccls) // bad compiler problem + return 0; + + GrInputClass clsin; + clsin.CopyFrom(GlyphListLoc(ichwMin), (ichwLim - ichwMin)); + + int cgix = clsin.NumberOfGlyphs(); + return cgix; + } + else + { + gAssert(false); // this method should not be used for output classes. + return ichwLim - ichwMin; + } +} + +//:>******************************************************************************************** +//:> For test procedures +//:>******************************************************************************************** + +//:Ignore + +#ifdef OLD_TEST_STUFF +/*---------------------------------------------------------------------------------------------- + General test of class table. +----------------------------------------------------------------------------------------------*/ +void GrClassTable::SetUpTestData() +{ + m_ccls = 7; // number of classes + m_cclsLinear = 4; // number of classes in linear format + + m_prgchwBIGGlyphList = new gid16[100]; + + m_prgichwOffsets = new data16[7+1]; + + gid16 * pchw = m_prgchwBIGGlyphList; + + // Output class 0: uppercase consonants B - H + m_prgichwOffsets[0] = 0; + *pchw++ = msbf(gid16(66)); *pchw++ = msbf(utf16(67)); *pchw++ = msbf(utf16(68)); + *pchw++ = msbf(gid16(70)); *pchw++ = msbf(utf16(71)); *pchw++ = msbf(utf16(72)); + + // Output class 1: grave vowels + m_prgichwOffsets[1] = 6; + *pchw++ = msbf(gid16(192)); // A + *pchw++ = msbf(gid16(224)); // a + *pchw++ = msbf(gid16(200)); // E + *pchw++ = msbf(gid16(232)); // e + *pchw++ = msbf(gid16(204)); // I + *pchw++ = msbf(gid16(236)); // i + *pchw++ = msbf(gid16(210)); // O + *pchw++ = msbf(gid16(243)); // o + *pchw++ = msbf(gid16(217)); // U + *pchw++ = msbf(gid16(249)); // u + + // Output class 2: circumflex vowels + m_prgichwOffsets[2] = 6 + 10; + *pchw++ = msbf(gid16(194)); // A + *pchw++ = msbf(gid16(226)); // a + *pchw++ = msbf(gid16(202)); // E + *pchw++ = msbf(gid16(234)); // e + *pchw++ = msbf(gid16(206)); // I + *pchw++ = msbf(gid16(238)); // i + *pchw++ = msbf(gid16(212)); // O + *pchw++ = msbf(gid16(244)); // o + *pchw++ = msbf(gid16(219)); // U + *pchw++ = msbf(gid16(251)); // u + + // Output class 3: diaeresis vowels, uppercase + m_prgichwOffsets[3] = 16 + 10; + *pchw++ = msbf(gid16(196)); // A + *pchw++ = msbf(gid16(196)); // A + *pchw++ = msbf(gid16(203)); // E + *pchw++ = msbf(gid16(203)); // E + *pchw++ = msbf(gid16(207)); // I + *pchw++ = msbf(gid16(207)); // I + *pchw++ = msbf(gid16(214)); // O + *pchw++ = msbf(gid16(214)); // O + *pchw++ = msbf(gid16(220)); // U + *pchw++ = msbf(gid16(220)); // U + + // Input class 4: lowercase consonants b - h + m_prgichwOffsets[4] = 26 + 10; // = 36 + *pchw++ = msbf(gid16(6)); + *pchw++ = msbf(gid16(4)); *pchw++ = msbf(gid16(2)); *pchw++ = msbf(gid16(6-4)); + *pchw++ = msbf(gid16(98)); *pchw++ = msbf(gid16(0)); + *pchw++ = msbf(gid16(99)); *pchw++ = msbf(gid16(1)); + *pchw++ = msbf(gid16(100)); *pchw++ = msbf(gid16(2)); + *pchw++ = msbf(gid16(102)); *pchw++ = msbf(gid16(3)); + *pchw++ = msbf(gid16(103)); *pchw++ = msbf(gid16(4)); + *pchw++ = msbf(gid16(104)); *pchw++ = msbf(gid16(5)); + + // Input class 5: vowels + m_prgichwOffsets[5] = 36 + 4 + 6*2; // = 52 + *pchw++ = msbf(gid16(10)); + *pchw++ = msbf(gid16(8)); *pchw++ = msbf(utf16(3)); *pchw++ = msbf(utf16(10-8)); + *pchw++ = msbf(gid16(65)); *pchw++ = msbf(gid16(0)); // A + *pchw++ = msbf(gid16(69)); *pchw++ = msbf(gid16(2)); // E + *pchw++ = msbf(gid16(73)); *pchw++ = msbf(gid16(4)); // I + *pchw++ = msbf(gid16(79)); *pchw++ = msbf(gid16(6)); // O + *pchw++ = msbf(gid16(85)); *pchw++ = msbf(gid16(8)); // U + *pchw++ = msbf(gid16(97)); *pchw++ = msbf(gid16(1)); // a + *pchw++ = msbf(gid16(101)); *pchw++ = msbf(gid16(3)); // e + *pchw++ = msbf(gid16(105)); *pchw++ = msbf(gid16(5)); // i + *pchw++ = msbf(gid16(111)); *pchw++ = msbf(gid16(7)); // o + *pchw++ = msbf(gid16(117)); *pchw++ = msbf(gid16(9)); // u + + // Input class 6: acute vowels + m_prgichwOffsets[6] = 52 + 4 + 10*2; // = 76 + *pchw++ = msbf(gid16(10)); + *pchw++ = msbf(gid16(8)); *pchw++ = msbf(gid16(3)); *pchw++ = msbf(gid16(10-8)); + *pchw++ = msbf(gid16(193)); *pchw++ = msbf(gid16(0)); // A + *pchw++ = msbf(gid16(201)); *pchw++ = msbf(gid16(2)); // E + *pchw++ = msbf(gid16(205)); *pchw++ = msbf(gid16(4)); // I + *pchw++ = msbf(gid16(211)); *pchw++ = msbf(gid16(6)); // O + *pchw++ = msbf(gid16(218)); *pchw++ = msbf(gid16(8)); // U + *pchw++ = msbf(gid16(225)); *pchw++ = msbf(gid16(1)); // a + *pchw++ = msbf(gid16(233)); *pchw++ = msbf(gid16(3)); // e + *pchw++ = msbf(gid16(237)); *pchw++ = msbf(gid16(5)); // i + *pchw++ = msbf(gid16(243)); *pchw++ = msbf(gid16(7)); // o + *pchw++ = msbf(gid16(250)); *pchw++ = msbf(gid16(9)); // u + + m_prgichwOffsets[7] = 76 + 4 + 10*2; // = 100 +}; + +#endif // OLD_TEST_STUFF + +} // namespace gr + +//:End Ignore + diff --git a/Build/source/libs/graphite-engine/src/segment/GrClassTable.h b/Build/source/libs/graphite-engine/src/segment/GrClassTable.h new file mode 100644 index 00000000000..5677fdfd21b --- /dev/null +++ b/Build/source/libs/graphite-engine/src/segment/GrClassTable.h @@ -0,0 +1,186 @@ +/*--------------------------------------------------------------------*//*:Ignore this sentence. +Copyright (C) 1999, 2001 SIL International. All rights reserved. + +Distributable under the terms of either the Common Public License or the +GNU Lesser General Public License, as specified in the LICENSING.txt file. + +File: GrClassTable.h +Responsibility: Sharon Correll +Last reviewed: Not yet. + +Description: + The GrClassTable and related classes that store the classes that are used in substitutions. +----------------------------------------------------------------------------------------------*/ +#ifdef _MSC_VER +#pragma once +#endif +#ifndef GR_CTABLE_INCLUDED +#define GR_CTABLE_INCLUDED + +//:End Ignore + +namespace gr +{ + +/*---------------------------------------------------------------------------------------------- + A glyph ID / index pair, a member of the GrInputClass where the items are sorted + by glyph ID. + + Hungarian: gix +----------------------------------------------------------------------------------------------*/ +class GrGlyphIndexPair +{ + friend class GrInputClass; + friend class GrClassTable; + + gid16 GlyphID() { return lsbf(m_gidBIG); } + data16 Index() { return lsbf(m_nBIGIndex); } + + gid16 m_gidBIG; + data16 m_nBIGIndex; +}; + +/*---------------------------------------------------------------------------------------------- + A class consisting of a mapping from glyph ID to index, used for classes that + function as input classes (eg, occurrng in the left-hand side of a rule). + + Hungarian: clsin +----------------------------------------------------------------------------------------------*/ +class GrInputClass +{ + friend class GrClassTable; + +protected: + /*------------------------------------------------------------------------------------------ + Copy the raw memory into the instance. + ------------------------------------------------------------------------------------------*/ + void CopyFrom(data16 * pchwStart, int cchw) + { + m_cgixBIG = pchwStart[0]; + m_digixBIGInit = pchwStart[1]; + m_cBIGLoop = pchwStart[2]; + m_igixBIGStart = pchwStart[3]; + + int cgix = NumberOfGlyphs(); + m_pgixFirst = m_prggixBuffer; + if (cgix > 64) + { + m_vgix.resize(cgix); + m_pgixFirst = &m_vgix[0]; + } + gAssert((4 + (cgix * 2)) == cchw); + #ifdef _DEBUG + memset(m_pgixFirst, 0, cgix * isizeof(GrGlyphIndexPair)); + #endif + Assert(sizeof(GrGlyphIndexPair) == sizeof(gid16) + sizeof(data16)); + GrGlyphIndexPair * pgixStart = reinterpret_cast<GrGlyphIndexPair*>(pchwStart + 4); + std::copy(pgixStart, pgixStart + cgix, m_pgixFirst); + } + + int NumberOfGlyphs() { return lsbf(m_cgixBIG); } + int LoopCount() { return lsbf(m_cBIGLoop); } + int InitialSearchRange() { return lsbf(m_digixBIGInit); } + int StartSearch() { return lsbf(m_igixBIGStart); } + + int FindIndex(gid16 gid); + +protected: + // Instance variables: + data16 m_cgixBIG; // number of glyphs in the class + + // constants for fast binary search + data16 m_digixBIGInit; // (max power of 2 <= m_cgix); + // size of initial range to consider + data16 m_cBIGLoop; // log2(max power of 2 <= m_cgix); + // indicates how many iterations are necessary + data16 m_igixBIGStart; // m_cgix - m_digixInit; + // where to start search + + GrGlyphIndexPair m_prggixBuffer[64]; + std::vector<GrGlyphIndexPair> m_vgix; + GrGlyphIndexPair * m_pgixFirst; +}; + + +/*---------------------------------------------------------------------------------------------- + Contains all the classes used for substitution rules. + + Hungarian: ctbl +----------------------------------------------------------------------------------------------*/ + +class GrClassTable +{ +public: + // Constructor & destructor: + GrClassTable() + : m_prgichwOffsets(NULL), + m_prgchwBIGGlyphList(NULL) + { + } + + ~GrClassTable() + { + delete[] m_prgichwOffsets; + delete[] m_prgchwBIGGlyphList; + } + + int NumberOfClasses() { return m_ccls; } + int NumberOfInputClasses() { return m_ccls - m_cclsLinear; } + int NumberOfOutputClasses() { return m_cclsLinear; } + + bool ReadFromFont(GrIStream & grstrm, int fxdVersion); + void CreateEmpty(); + + int FindIndex(int icls, gid16 chwGlyphID); + gid16 GetGlyphID(int icls, int ichw); + int NumberOfGlyphsInClass(int icls); + + gid16 GlyphAt(int ichw) + { + return lsbf(m_prgchwBIGGlyphList[ichw]); + } + data16 * GlyphListLoc(int ichw) + { + return m_prgchwBIGGlyphList + ichw; + } + +protected: + // Instance variables: + int m_ccls; // number of classes + int m_cclsLinear; // number of classes in linear format + + data16 * m_prgichwOffsets; + + // Two formats are included in the following array: the first section consists of + // flat ordered lists of glyphs, used for the "output" classes that use linear format. + // This provides an index-to-glyph mapping. + // The second section contains data in the format of GrInputClasses, used for "input" + // classes that need a binary-search format. This provides a glyph-to-index mapping. + // We don't create instances of GrInputClasses, because that would make reading + // from the ECF file slow. Instead we just set up a single instance at the time + // we're interested in it. + // NOTE that all this data has been slurped directly from the ECF file and therefore + // uses BIG-ENDIAN format. + gid16 * m_prgchwBIGGlyphList; + +//:Ignore +#ifdef OLD_TEST_STUFF +public: + // For test procedures: + void SetUpTestData(); + void SetUpRuleActionTest(); + void SetUpRuleAction2Test(); + void SetUpAssocTest(); + void SetUpAssoc2Test(); + void SetUpDefaultAssocTest(); + void SetUpFeatureTest(); + void SetUpLigatureTest(); + void SetUpLigature2Test(); +#endif // OLD_TEST_STUFF +//:End Ignore +}; + +} // namespace gr + +#endif // !GR_CTABLE_INCLUDED + diff --git a/Build/source/libs/graphite-engine/src/segment/GrEngine.cpp b/Build/source/libs/graphite-engine/src/segment/GrEngine.cpp new file mode 100644 index 00000000000..d7c2aaf320c --- /dev/null +++ b/Build/source/libs/graphite-engine/src/segment/GrEngine.cpp @@ -0,0 +1,2025 @@ +/*--------------------------------------------------------------------*//*:Ignore this sentence. +Copyright (C) 1999, 2001 SIL International. All rights reserved. + +Distributable under the terms of either the Common Public License or the +GNU Lesser General Public License, as specified in the LICENSING.txt file. + +File: GrEngine.cpp +Responsibility: Sharon Correll +Last reviewed: Not yet. + +Description: Contains the implementation of the GrEngine class. +----------------------------------------------------------------------------------------------*/ + +//:>******************************************************************************************** +//:> Include files +//:>******************************************************************************************** +#include "Main.h" +#include <functional> +#ifdef _MSC_VER +#pragma hdrstop +#endif +// any other headers (not precompiled) + +#undef THIS_FILE +DEFINE_THIS_FILE + +//:End Ignore + +namespace gr +{ + +//:>******************************************************************************************** +//:> Forward declarations +//:>******************************************************************************************** + +//:>******************************************************************************************** +//:> Local Constants and static variables +//:>******************************************************************************************** + + +//:>******************************************************************************************** +//:> General functions +//:>******************************************************************************************** + +bool compareCmap(const byte *lhs, const byte *rhs) +{ + using namespace gr; + + typedef const struct index {data16 version; data16 numberSubtables;} *const IndexPtr; + typedef const struct enc {data16 platID; data16 platSpecID; data32 offset;} *const EncPtr; + + // Calculate the size of the cmap. + + IndexPtr rhsIndex = IndexPtr(rhs); + size_t numSubTbl = lsbf(rhsIndex->numberSubtables); + size_t cbCmapSz = sizeof(index) + sizeof(enc) * numSubTbl; + EncPtr encTbl = EncPtr(rhs + sizeof(index)); + + for (size_t iSubTbl = 0; iSubTbl < numSubTbl; iSubTbl++) + //for (int iSubTbl = numSubTbl - 1; iSubTbl >= 0; --iSubTbl) + { + const data16 *const mapTbl = reinterpret_cast<const data16 *>(rhs + lsbf((int)encTbl[iSubTbl].offset)); + int format = lsbf(*mapTbl); + switch (format) + { + // 16-bit lengths + case 0: + case 2: + case 4: + case 6: + cbCmapSz += lsbf((data16)unsigned(mapTbl[1])); + break; + // 32-bit lengths + case 8: + case 10: + case 12: + cbCmapSz += lsbf((int)unsigned(reinterpret_cast<const data32 *>(mapTbl)[1])); + break; + default: + Assert(false); + } + } + + // Do the comparison. + return (memcmp(lhs, rhs, cbCmapSz) ? false : true); +} + + +//:>******************************************************************************************** +//:> GrEngine class. +//:>******************************************************************************************** + +GrEngine::GrEngine() +{ + /////////////_CrtSetDbgFlag(_CRTDBG_CHECK_ALWAYS_DF); //////////////// + + + m_cref = 1; + BasicInit(); +} + +void GrEngine::BasicInit() +{ + m_pfface = NULL; + + m_ptman = NULL; + m_pctbl = NULL; + m_pgtbl = NULL; + m_prgpsd = NULL; + m_cpsd = 0; + m_cfeat = 0; + m_mFontEmUnits = -1; + m_dysOffset = 0; + + ////m_tbufCmap.reset(); + m_pCmap_3_1 = NULL; + m_pCmap_3_10 = NULL; + ////m_tbufNameTbl.reset(); + // new interface stuff: + m_pCmapTbl = NULL; + m_pNameTbl = NULL; + + m_fLogXductn = false; + + m_resFontValid = kresInvalidArg; // not yet initialized + m_ferr = kferrUninitialized; + m_fxdBadVersion = 0; + + m_fSmartReg = false; + m_fSmartBold = false; + m_fSmartItalic = false; + m_fSmartBI = false; + m_fFakeItalicCache = false; + m_fFakeBICache = false; + m_strCtrlFileReg.erase(); + m_strCtrlFileBold.erase(); + m_strCtrlFileItalic.erase(); + m_strCtrlFileBI.erase(); + m_fFakeItalic = false; + m_stuCtrlFile.erase(); + m_stuInitError.erase(); + m_stuErrCtrlFile.erase(); + m_stuFaceName.erase(); + m_stuBaseFaceName.erase(); + m_fUseSepBase = false; + m_stuFeatures.erase(); + + m_nFontCheckSum = 0; + + m_fInErrorState = false; + + m_rglcidFeatLabelLangs = NULL; // initialize lazily, when needed + m_clcidFeatLabelLangs = 0; +} + +GrEngine::~GrEngine() +{ + DestroyEverything(); + #ifdef _MSC_VER + if (!_CrtCheckMemory()) + { + OutputDebugString(L"bad memory"); + } + #endif +} + +/*---------------------------------------------------------------------------------------------- + Clean everything out (destructor or reinitializing with a different face name). +----------------------------------------------------------------------------------------------*/ +void GrEngine::DestroyEverything() +{ + DestroyContents(); + #ifdef _MSC_VER + if (!_CrtCheckMemory()) + { + OutputDebugString(L"bad memory"); + } + #endif + + m_strCtrlFileReg.erase(); + m_strCtrlFileBold.erase(); + m_strCtrlFileItalic.erase(); + m_strCtrlFileBI.erase(); + m_fSmartReg = false; + m_fSmartBold = false; + m_fSmartItalic = false; + m_fSmartBI = false; + m_fFakeItalicCache = false; + m_fFakeBICache = false; + m_stuBaseFaceName.erase(); + + //m_stuFaceName.erase(); + //m_stuFaceName = L"this is not a usable or valid string"; + m_fUseSepBase = false; + m_stuFeatures.erase(); +} + +void GrEngine::DestroyContents(bool fDestroyCmap) +{ + if (fDestroyCmap) + { + ////m_tbufCmap.reset(); + m_pCmap_3_1 = NULL; + m_pCmap_3_10 = NULL; + + delete[] m_pCmapTbl; + delete[] m_pNameTbl; + m_pCmapTbl = NULL; + m_pNameTbl = NULL; + } + + delete m_ptman; + delete m_pctbl; + delete m_pgtbl; + delete[] m_prgpsd; + m_ptman = NULL; + m_pctbl = NULL; + m_pgtbl = NULL; + m_prgpsd = NULL; + + ////m_tbufNameTbl.reset(); + + m_stuCtrlFile.erase(); + m_stuInitError.erase(); + + m_resFontValid = kresInvalidArg; + m_ferr = kferrUninitialized; +} + +/*---------------------------------------------------------------------------------------------- + For FontFace: return the number of features. +----------------------------------------------------------------------------------------------*/ +size_t GrEngine::NumberOfFeatures_ff() +{ + return (size_t)m_cfeat; +} + +/*---------------------------------------------------------------------------------------------- + For FontFace: return the feature ID for the ifeat-th feature. +----------------------------------------------------------------------------------------------*/ +featid GrEngine::FeatureID_ff(size_t ifeat) +{ + return (featid)m_rgfeat[ifeat].ID(); +} + +/*---------------------------------------------------------------------------------------------- + For FontFace: return the index of the feature with the given ID. +----------------------------------------------------------------------------------------------*/ +size_t GrEngine::FeatureWithID_ff(featid fid) +{ + int ifeatRet; + /* GrFeature * pfeat = */ + FeatureWithID(fid, &ifeatRet); + return (size_t)ifeatRet; +} + +/*---------------------------------------------------------------------------------------------- + For FontFace: return the label for the ifeat-th feature. +----------------------------------------------------------------------------------------------*/ +bool GrEngine::GetFeatureLabel_ff(size_t ifeat, lgid nLanguage, utf16 * rgchwLabel) +{ + std::wstring stu = m_rgfeat[ifeat].Label(this, nLanguage); + + int cch = stu.size(); + cch = min(cch, 127); // 1 char for zero-termination + std::copy(stu.data(), stu.data() + cch, rgchwLabel); + rgchwLabel[cch] = 0; + + return (cch > 0); +} + +/*---------------------------------------------------------------------------------------------- + For FontFace: return the index of the default setting. Return -1 if it cannot be found. +----------------------------------------------------------------------------------------------*/ +int GrEngine::GetFeatureDefault_ff(size_t ifeat) +{ + GrFeature * pfeat = m_rgfeat + ifeat; + int defaultValue = pfeat->DefaultValue(); + int cfset = pfeat->NumberOfSettings(); + int rgnSettings[100]; + pfeat->Settings(100, rgnSettings); + Assert(cfset < 100); // TODO: improve this + for (int ifset = 0; ifset < cfset; ifset++) + { + if (rgnSettings[ifset] == defaultValue) + return ifset; + } + return -1; +} + +/*---------------------------------------------------------------------------------------------- + For FontFace: return the number of settings for the ifeat-th feature. +----------------------------------------------------------------------------------------------*/ +size_t GrEngine::NumberOfSettings_ff(size_t ifeat) +{ + return m_rgfeat[ifeat].NumberOfSettings(); +} + +/*---------------------------------------------------------------------------------------------- + For FontFace: return the value of the ifset-th setting for the ifeat-th feature. +----------------------------------------------------------------------------------------------*/ +int GrEngine::GetFeatureSettingValue_ff(size_t ifeat, size_t ifset) +{ + return m_rgfeat[ifeat].NthSetting(ifset); +} + +/*---------------------------------------------------------------------------------------------- + For FontFace: return the UI label for the given feature setting. +----------------------------------------------------------------------------------------------*/ +bool GrEngine::GetFeatureSettingLabel_ff(size_t ifeat, size_t ifset, lgid language, + utf16 * rgchwLabel) +{ + std::wstring stu = m_rgfeat[ifeat].NthSettingLabel(this, ifset, language); + + int cch = stu.size(); + cch = min(cch, 127); // 1 char for zero-termination + // Note: the wchar_t label was originally assigned from utf16 data, so although wchar_t is + // utf32 on some platforms the conversion back to utf16 should still give correct results. + std::copy(stu.data(), stu.data() + cch, rgchwLabel); + rgchwLabel[cch] = 0; + + return (cch > 0); +} + +/*---------------------------------------------------------------------------------------------- + For FontFace: return the number of languages that are possible among the feature labels. +----------------------------------------------------------------------------------------------*/ +size_t GrEngine::NumberOfFeatLangs_ff() +{ + SetUpFeatLangList(); + return m_clcidFeatLabelLangs; +} + +/*---------------------------------------------------------------------------------------------- + For FontFace: return the language LCID for the feature-label language with the given index. +----------------------------------------------------------------------------------------------*/ +short GrEngine::GetFeatLabelLang_ff(size_t ilang) +{ + SetUpFeatLangList(); + return m_rglcidFeatLabelLangs[ilang]; +} + +/*---------------------------------------------------------------------------------------------- + For FontFace: return the number of supported languages. +----------------------------------------------------------------------------------------------*/ +size_t GrEngine::NumberOfLanguages_ff() +{ + return (size_t)m_langtbl.NumberOfLanguages(); +} + +/*---------------------------------------------------------------------------------------------- + For FontFace: return the language code for the language with the given index. +----------------------------------------------------------------------------------------------*/ +isocode GrEngine::GetLanguageCode_ff(size_t ilang) +{ + return m_langtbl.LanguageCode(ilang); +} + +/*---------------------------------------------------------------------------------------------- + Set up the list of all the languages that are present in the feature labels. +----------------------------------------------------------------------------------------------*/ +void GrEngine::SetUpFeatLangList() +{ + if (m_rglcidFeatLabelLangs) + return; + + int rgnNameIDs[kMaxFeatures]; + for (int ifeat = 0; ifeat < m_cfeat; ifeat++) + rgnNameIDs[ifeat] = m_rgfeat[ifeat].NameId(); + short rglcid[128]; // 128 is the number expected by TtfUtil::GetLangsForNames + m_clcidFeatLabelLangs = TtfUtil::GetLangsForNames(m_pNameTbl, 3, 1, rgnNameIDs, m_cfeat, rglcid); + m_rglcidFeatLabelLangs = new short[m_clcidFeatLabelLangs]; + memcpy(m_rglcidFeatLabelLangs, rglcid, sizeof(short) * m_clcidFeatLabelLangs); +} + +/*---------------------------------------------------------------------------------------------- + Return the maximum size needed for the block of data to pass between segments, that is, + to reinitialize the engine based on the results of the previously generated segment. + This value must match what is in GrTableManager::InitializeStreams() and + InitializeForNextSeg(). 256 is an absolute maximum imposed by the interface. + + In Graphite, what this block of data will contain is information about cross-line + contextualization and some directionality information. + + Assumes InitNew() has already been called to set the font name. +----------------------------------------------------------------------------------------------*/ +GrResult GrEngine::get_SegDatMaxLength(int * pcb) +{ + ChkGrOutPtr(pcb); + + if (m_resFontValid == kresInvalidArg) + ReturnResult(kresUnexpected); // engine not initialized + + GrResult res = m_resFontValid; + if (m_resFontValid == kresFail || m_resFontValid == kresUnexpected || m_resFontValid == kresFalse) + res = kresOk; // invalid font + if (ResultFailed(res)) + ReturnResult(res); + + Assert(m_ptman); + if (!m_ptman) + *pcb = 256; + else + *pcb = m_ptman->NumberOfPasses() + 4; + + ReturnResult(res); +} + +/*---------------------------------------------------------------------------------------------- + @return The supported script direction(s). If more than one, the application is + responsible for choosing the most appropriate. +----------------------------------------------------------------------------------------------*/ +GrResult GrEngine::get_ScriptDirection(unsigned int * pgrfsdc, OLECHAR * prgchwErrMsg, int cchMaxErrMsg) +{ + ChkGrOutPtr(pgrfsdc); + ChkGrArrayArg(prgchwErrMsg, cchMaxErrMsg); + + if (m_resFontValid == kresInvalidArg) + ReturnResult(kresUnexpected); // engine not initialized + + GrResult res = m_resFontValid; + + *pgrfsdc = m_grfsdc; + +// ClearFontError(prgchwErrMsg, cchMaxErrMsg); +// try +// { +// if (m_resFontValid == kresFail || m_resFontValid == kresUnexpected || m_resFontValid == kresFalse) +// { +// RecordFontLoadError(prgchwErrMsg, cchMaxErrMsg); +// res = kresOk; +// } +// *pgrfsdc = m_grfsdc; +// } +// catch (Throwable & thr) +// { +// res = (GrResult)thr.Error(); +// *pgrfsdc = kfsdcHorizLtr; +// } +// catch (...) +// { +// res = WARN(kresFail); +// *pgrfsdc = kfsdcHorizLtr; +// } + + ReturnResult(res); +} + +/*---------------------------------------------------------------------------------------------- + Get an glyph attribute from the engine that will help the GrJustifier in its work. +----------------------------------------------------------------------------------------------*/ +GrResult GrEngine::getGlyphAttribute(int iGlyph, int jgat, int nLevel, float * pValueRet) +{ + return m_ptman->State()->GetGlyphAttrForJustification(iGlyph, jgat, nLevel, pValueRet); +} + +GrResult GrEngine::getGlyphAttribute(int iGlyph, int jgat, int nLevel, int * pValueRet) +{ + return m_ptman->State()->GetGlyphAttrForJustification(iGlyph, jgat, nLevel, pValueRet); +} + +/*---------------------------------------------------------------------------------------------- + Set an glyph attribute in the engine as a result of the decisions made by the + GrJustifier. +----------------------------------------------------------------------------------------------*/ +GrResult GrEngine::setGlyphAttribute(int iGlyph, int jgat, int nLevel, float value) +{ + return m_ptman->State()->SetGlyphAttrForJustification(iGlyph, jgat, nLevel, value); +} + +GrResult GrEngine::setGlyphAttribute(int iGlyph, int jgat, int nLevel, int value) +{ + return m_ptman->State()->SetGlyphAttrForJustification(iGlyph, jgat, nLevel, value); +} + +//:>******************************************************************************************** +//:> Non-FieldWorks interface methods +//:>******************************************************************************************** + +/*---------------------------------------------------------------------------------------------- + Make a complete segment that can be used to measure and do line-breaking. + + OBSOLETE - delete +----------------------------------------------------------------------------------------------*/ +/* +GrResult GrEngine::MakeMeasuredSegment( + Font * pfont, ITextSource * pgts, + int ichMin, int ichLim, + bool fParaRtl, + Segment ** ppsegRet, SegEnd * pest, + std::ostream * pstrmLog, + OLECHAR * prgchwErrMsg, int cchMaxErrMsg) +{ + int dichContext, dichLimSeg; + float dxWidth; + return MakeSegment(ksegmodeMsfr, + pfont, pgts, NULL, ichMin, ichLim, false, false, + 100000, klbClipBreak, ktwshAll, // not used + fParaRtl, + ppsegRet, &dxWidth, + 0, NULL, 0, NULL, NULL, &dichContext, // not used + pstrmLog, + prgchwErrMsg, cchMaxErrMsg, + -1, false, false, klbClipBreak, // not used + &dichLimSeg, pest, + false, 100000, kestMoreLines); // not used +} +*/ + +/*---------------------------------------------------------------------------------------------- + Make a segment from the given range, regardless of the kind of line-break that results. + + OBSOLETE - delete +----------------------------------------------------------------------------------------------*/ +/* +GrResult GrEngine::MakeSegmentFromRange( + Font * pfont, ITextSource * pgts, IGrJustifier * pgj, + int ichMin, int ichLim, + bool fStartLine, bool fEndLine, + TrWsHandling twsh, bool fParaRtl, + Segment ** ppsegRet, float * pdxWidth, SegEnd * pest, + int cbPrev, byte * pbPrevSegDat, int cbNextMax, byte * pbNextSegDat, int * pcbNextSegDat, + int * pdichwContext, + std::ostream * pstrmLog, + OLECHAR * prgchwErrMsg, int cchwMaxErrMsg) +{ + int dichLimSegBogus; + return MakeSegment(ksegmodeMsfr, pfont, pgts, pgj, + ichMin, ichLim, + fStartLine, fEndLine, + 10000, klbWordBreak, // not used + twsh, fParaRtl, + ppsegRet, pdxWidth, + //cbPrev, pbPrevSegDat, cbNextMax, pbNextSegDat, pcbNextSegDat, pdichwContext, + 0, NULL, 0, NULL, NULL, NULL, + pstrmLog, + prgchwErrMsg, cchwMaxErrMsg, + ichLim, false, false, klbClipBreak, // not used + &dichLimSegBogus, pest, + 10000, kestMoreLines); // not used +} +*/ + + +//:>******************************************************************************************** +//:> Other methods +//:>******************************************************************************************** + +/*---------------------------------------------------------------------------------------------- + Create a segment. +----------------------------------------------------------------------------------------------*/ +void GrEngine::MakeSegment( + // Common parameters + Segment * psegNew, + Font * pfont, ITextSource * pgts, IGrJustifier * pjus, + LayoutEnvironment & layout, + int ichMin, int ichLim, + // for finding a break point + float dxMaxWidth, + bool fBacktracking, + // for justification + bool fJust, float dxJustifiedWidthJ, SegEnd estJ) +{ + ChkGrArgPtr(pgts); + ChkGrArgPtrN(pjus); + ChkGrOutPtr(psegNew); + Assert(layout.bestBreak() <= layout.worstBreak()); + + if (pjus == NULL) + pjus = layout.justifier(); + + if (m_resFontValid == kresInvalidArg) + // Uninitialized. + return; // ReturnResult(kresUnexpected); + + int segmode = ksegmodeRange; // segment from range + if (fJust) + segmode = ksegmodeJust; // justified segment + else if (dxMaxWidth < kPosInfFloat) + segmode = ksegmodeBreak; // find break point + + // Temporary: + if (!this->m_ptman) + { + Warn("No Graphite tables"); + m_ptman = new GrTableManager(this); + CreateEmpty(); + } + + // GrResult hr = kresOk; + m_fInErrorState = false; + + bool fBold, fItalic; + GetStyles(pfont, ichMin, &fBold, &fItalic); + + GetWritingSystemDirection(pgts, ichMin); + + // Find the end of the range to render with the current font. + int nDirDepth; + int ichFontLim, ichStrmLim, ichSegLim; + switch (segmode) + { + case ksegmodeBreak: // find break point + // Find the end of the range to render with the current font. Here ichLim should be + // the end of the text-source, unless we're backtracking. + ichFontLim = FindFontLim(pgts, ichMin, &nDirDepth); + ichStrmLim = min(ichFontLim, ichLim); + ichSegLim = -1; // unknown + break; + case ksegmodeJust: // stretching an existing segment to achieve justification + ichFontLim = FindFontLim(pgts, ichMin, &nDirDepth); + ichStrmLim = ichFontLim; + ichSegLim = ichLim; + break; + //case ksegmodeMms: // MakeMeasuredSegment + // ichFontLim = FindFontLim(pgts, ichMin, ichLim, &nDirDepth); + // ichLim = ichFontLim; + // ichStrmLim = ichFontLim; + // ichSegLim = -1; // process to the end of the stream + // break; + case ksegmodeRange: // segment from range + // Find the end of the range to render with the current font. + ichFontLim = FindFontLim(pgts, ichMin, &nDirDepth); + ichLim = min(ichLim, ichFontLim); + ichStrmLim = ichFontLim; + ichSegLim = (ichLim == kPosInfinity) ? -1 : ichLim; + break; + default: + Assert(false); + } + + // Initialize the graphics object with the font and character properties. + // SILE + ////if (m_fUseSepBase) + ////{ + //// Assert(m_stuBaseFaceName.size() > 0); + //// SetUpGraphics(pgg, pgts, ichMin, m_stuBaseFaceName); + ////} + ////else + //// SetUpGraphics(pgg, pgts, ichMin, m_stuFaceName); + + GrResult res = kresOk; + + Assert(m_ptman); + m_ptman->State()->SetFont(pfont); + + // Create a character stream on the text source. + GrCharStream * pchstrm = new GrCharStream(pgts, ichMin, ichStrmLim, + layout.startOfLine(), layout.endOfLine()); + + // TODO: change vertical offset to work on a character-by-character basis + m_dysOffset = (pgts->getVerticalOffset(ichMin) * pfont->getDPIy()) / 72; + + // Run the tables and get back a segment. + + bool fMoreText; + int ichCallerBtLim; + bool fInfiniteWidth; + switch (segmode) + { + //case ksegmodeMms: + case ksegmodeBreak: + case ksegmodeRange: + if (segmode == ksegmodeBreak) + { + fMoreText = ((ichFontLim < ichLim) || fBacktracking); + ichCallerBtLim = (fBacktracking) ? ichLim : -1; + fInfiniteWidth = false; + } + //else if (segmode == ksegmodeMms) + //{ + // fMoreText = false; + // ichCallerBtLim = -1; + // fInfiniteWidth = true; + // Assert(twsh == ktwshAll); + //} + else if (segmode == ksegmodeRange) + { + int ichTextLim; + ichTextLim = pgts->getLength(); + fMoreText = (ichLim < ichTextLim); + ichCallerBtLim = -1; + fInfiniteWidth = true; + } + try { + m_ptman->Run(psegNew, pfont, pchstrm, pjus, + ((pjus) ? kjmodiCanShrink : kjmodiNormal), + layout, + ichSegLim, dxMaxWidth, 0, + fBacktracking, fMoreText, ichFontLim, fInfiniteWidth, false, + ichCallerBtLim, + nDirDepth, estJ); + } + catch (...) + { + // Disastrous problem in rendering. + if (!layout.dumbFallback()) + throw; // throws original exception + + // Try dumb rendering. + if (m_fUseSepBase) + SwitchGraphicsFont(true); // use the base font, which has real glyphs + + m_fInErrorState = true; + res = RunUsingEmpty(psegNew, pfont, pchstrm, layout, + ichSegLim, + dxMaxWidth, + fBacktracking, fMoreText, fInfiniteWidth, + ichCallerBtLim, + nDirDepth, estJ); + } + break; + + case ksegmodeJust: // justified segment + { + m_ptman->Run(psegNew, pfont, pchstrm, pjus, kjmodiJustify, layout, + ichSegLim, + dxJustifiedWidthJ, dxMaxWidth, + false, false, // not used + ichFontLim, + true, // infinite width + false, // kludge + -1, + nDirDepth, estJ); + //&dxWidth, &est); + Assert((ichLim - ichMin) == (psegNew->stopCharacter() - psegNew->startCharacter())); + break; + } + + default: + Assert(false); + } + + delete pchstrm; + + if (m_fUseSepBase) + SwitchGraphicsFont(false); +} + +/*---------------------------------------------------------------------------------------------- + Return an indication of whether an error happened in trying to load Graphite tables. +----------------------------------------------------------------------------------------------*/ +FontErrorCode GrEngine::IsValidForGraphite(int * pnVersion, int * pnSubVersion) +{ + if (pnVersion) + *pnVersion = m_fxdBadVersion >> 16; + if (pnSubVersion) + *pnSubVersion = m_fxdBadVersion & 0x0000FFFF; + + return m_ferr; +} + +/*---------------------------------------------------------------------------------------------- + Record a system error indicating that the font could not be loaded properly. + OBSOLETE +----------------------------------------------------------------------------------------------*/ +void GrEngine::RecordFontLoadError(OLECHAR * prgchwErrMsg, int cchMax) +{ + if (prgchwErrMsg == NULL || cchMax == 0) + return; + + std::wstring stuMessage = L"Error in initializing Graphite font \""; + stuMessage.append(m_stuFaceName); + if (m_stuErrCtrlFile.size()) + { + stuMessage.append(L"\" ("); + stuMessage.append(m_stuErrCtrlFile); + stuMessage.append(L")"); + } + else + stuMessage.append(L"\""); + if (m_stuInitError.size()) + { + stuMessage.append(L"--\n"); + stuMessage.append(m_stuInitError); + } + + std::fill_n(prgchwErrMsg, cchMax, 0); + std::copy(stuMessage.data(), stuMessage.data() + min(cchMax - 1, signed(stuMessage.size())), + prgchwErrMsg); +} + +/*---------------------------------------------------------------------------------------------- + Record a system error indicating a bad error in rendering using a supposedly valid font. + OBSOLETE +----------------------------------------------------------------------------------------------*/ +void GrEngine::RecordFontRunError(OLECHAR * prgchwErrMsg, int cchMax, GrResult res) +{ + if (prgchwErrMsg == NULL || cchMax == 0) + return; + + std::wstring stuMessage = L"Error in rendering using Graphite font \""; + stuMessage.append(m_stuFaceName); + if (m_stuErrCtrlFile.size()) + { + stuMessage.append(L"\" ("); + stuMessage.append(m_stuErrCtrlFile); + stuMessage.append(L")"); + } + else + stuMessage.append(L"\""); + + std::fill_n(prgchwErrMsg, cchMax, 0); + std::copy(stuMessage.data(), stuMessage.data() + min(cchMax - 1, signed(stuMessage.size())), + prgchwErrMsg); +} + +/*---------------------------------------------------------------------------------------------- + Initialize the font error message buffer to zero. This way we can be sure to recognize + an error condition by the fact that the buffer is non-zero. + OBSOLETE +----------------------------------------------------------------------------------------------*/ +void GrEngine::ClearFontError(OLECHAR * prgchwErrMsg, int cchMaxErrMsg) +{ + std::fill_n(prgchwErrMsg, cchMaxErrMsg, 0); +} + +/*---------------------------------------------------------------------------------------------- + Set the overall direction of the writing system based on the text properties. +----------------------------------------------------------------------------------------------*/ +void GrEngine::GetWritingSystemDirection(ITextSource * pgts, int ichwMin) +{ +#ifdef OLD_TEST_STUFF + // for test procedures: + if (m_stuInitialize == "RightToLeftLayout") + { + m_fRightToLeft = true; + return; + } +#endif // OLD_TEST_STUFF + + m_fRightToLeft = pgts->getRightToLeft(ichwMin); +} + +/*---------------------------------------------------------------------------------------------- + Find the end of the range that uses the current font. + + @param pgts - contains text to render + @param ichwMinFont - beginning of range to render with this font + @param pnDirDepth - return direction depth: 0=LTR, 1=RTL + + @return The lim of the range that could be rendered by this font. Also the direction depth. +----------------------------------------------------------------------------------------------*/ +int GrEngine::FindFontLim(ITextSource * pgts, int ichwMinFont, int * pnDirDepth) +{ + int ichwTextLen = (int)pgts->getLength(); + + int ichwLimRange; // range that can be rendered without breaking contextualization. + int ichwMinNext = ichwMinFont; + + while (true) + { + std::pair<toffset, toffset> pairRange = pgts->propertyRange(ichwMinNext); + // int ichwMinRun = pairRange.first; + int ichwLimRun = pairRange.second; + *pnDirDepth = pgts->getDirectionDepth(ichwMinNext); + + //if (ichwLim > -1 && ichwLim < ichwLimRun) + //{ + // // Stopping in the middle of a run + // return ichwLim; + //} + + // We can at least go to the end of this run. + ichwLimRange = ichwLimRun; + if (ichwLimRun >= ichwTextLen) + { + // Hit the end of the text-source + return ichwLimRange; + } + else + { + // If the following run is the same as this run except for color and underlining, + // we don't have to break here. + if (!pgts->sameSegment(ichwMinNext, ichwLimRun)) + return ichwLimRange; + } + ichwMinNext = ichwLimRun; + } + + return ichwLimRange; +} + +/*---------------------------------------------------------------------------------------------- + Read the version number from the tables, and return false if any are a version this + implementation of the engine can't handle. +----------------------------------------------------------------------------------------------*/ +bool GrEngine::CheckTableVersions(GrIStream * pgrstrm, + byte *silf_tbl, int lSilfStart, + byte *gloc_tbl, int lGlocStart, + byte *feat_tbl, int lFeatStart, + int * pfxdBadVersion) +{ + pgrstrm->OpenBuffer(silf_tbl, isizeof(int)); + pgrstrm->SetPositionInFont(lSilfStart); + *pfxdBadVersion = ReadVersion(*pgrstrm); + pgrstrm->CloseBuffer(); + if (*pfxdBadVersion > kSilfVersion) + return false; + + pgrstrm->OpenBuffer(gloc_tbl, lGlocStart + isizeof(int)); + pgrstrm->SetPositionInFont(lGlocStart); + *pfxdBadVersion = ReadVersion(*pgrstrm); + pgrstrm->CloseBuffer(); + if (*pfxdBadVersion > kGlocVersion) + return false; + + pgrstrm->OpenBuffer(feat_tbl, isizeof(int)); + pgrstrm->SetPositionInFont(lFeatStart); + *pfxdBadVersion = ReadVersion(*pgrstrm); + pgrstrm->CloseBuffer(); + if (*pfxdBadVersion > kFeatVersion) + return false; + + *pfxdBadVersion = 0; + return true; +} + +/*---------------------------------------------------------------------------------------------- + Reinterpret the version number from a font table. +----------------------------------------------------------------------------------------------*/ +int GrEngine::ReadVersion(GrIStream & grstrm) +{ + int fxdVersion = grstrm.ReadIntFromFont(); + + if (fxdVersion < 0x00010000) + fxdVersion = 0x00010000; // kludge for bug with which some fonts were generated + + return fxdVersion; +} + +/*---------------------------------------------------------------------------------------------- + Initialize the engine from the test file. Used for test procedures. +----------------------------------------------------------------------------------------------*/ +//:Ignore +#ifdef OLD_TEST_STUFF +void GrEngine::InitFromControlFileTest() +{ + IStreamPtr qstrm; + FileStream::Create(m_stuCtrlFile, STGM_READ, &qstrm); + + int nSilfOffset, nGlocOffset, nGlatOffset, nFeatOffset; + + nSilfOffset = ReadIntFromFont(qstrm); + nGlocOffset = ReadIntFromFont(qstrm); + nGlatOffset = ReadIntFromFont(qstrm); + nFeatOffset = ReadIntFromFont(qstrm); + nSill + + // Read the "Silf" table. + int chwMaxGlyphID; + int fxdVersion; + ReadSilfTable(qstrm, nSilOffset, 0, &chwMaxGlyphID, &fxdVersion); + + // Read the "Gloc" and "Glat" tables. + ReadGlocAndGlatTables(qstrm, nGlocOffset, qstrm, nGlatOffset, chwMaxGlyphID, fxdVersion); + + // Read the "Feat" table. + ReadFeatTable(qstrm, nFeatOffset); +} +#endif // OLD_TEST_STUFF +//:End Ignore + + +/*---------------------------------------------------------------------------------------------- + Return whether the text is asking for bold and/or italic text. +----------------------------------------------------------------------------------------------*/ +void GrEngine::GetStyles(Font * pfont, int ichwMin, bool * pfBold, bool * pfItalic) +{ + *pfBold = pfont->bold(); + *pfItalic = pfont->italic(); +} + +/*---------------------------------------------------------------------------------------------- + Switch the Graphics object between the Graphite table file and the base font. + Should only be called when we know we are using a base font, or when we are reading + the base font to see if it is valid. +----------------------------------------------------------------------------------------------*/ +void GrEngine::SwitchGraphicsFont(bool fBase) +{ + Assert(!fBase || m_stuBaseFaceName.size() > 0); + + //LgCharRenderProps chrp; + //pgg->get_FontCharProperties(&chrp); + //if (fBase) + //{ + // wcsncpy(chrp.szFaceName, m_stuBaseFaceName.data(), m_stuBaseFaceName.size() + 1); + // chrp.szFaceName[31] = 0; + //} + //else + //{ + // wcsncpy(chrp.szFaceName, m_stuFaceName.data(), m_stuFaceName.size() + 1); + // chrp.szFaceName[31] = 0; + //} + //pgg->SetupGraphics(&chrp); +} + +/*---------------------------------------------------------------------------------------------- + Read the contents of the "Silf" table from the stream, which is on an extended + TrueType font file. Specifically, read the iSubTable-th sub-table (for now there is + only one). + + WARNING: any changes to this method must be accompanied by equivalent changes to + CreateEmpty(). +----------------------------------------------------------------------------------------------*/ +bool GrEngine::ReadSilfTable(GrIStream & grstrm, long lTableStart, int iSubTable, + int * pchwMaxGlyphID, int * pfxdSilfVersion) +{ + grstrm.SetPositionInFont(lTableStart); + + // version + *pfxdSilfVersion = ReadVersion(grstrm); + if (*pfxdSilfVersion > kSilfVersion) + // Version we don't know how to handle. + return false; + + if (*pfxdSilfVersion >= 0x00030000) + // compiler version + grstrm.ReadIntFromFont(); + + // number of tables + unsigned short cSubTables = grstrm.ReadUShortFromFont(); + Assert(cSubTables == 1); // for now + Assert(cSubTables <= kMaxSubTablesInFont); + if (cSubTables != 1 || cSubTables > kMaxSubTablesInFont) + return false; + + if (*pfxdSilfVersion >= 0x00020000) + // reserved + grstrm.ReadShortFromFont(); + + // subtable offsets + int nSubTableOffsets[kMaxSubTablesInFont]; + int i; + for (i = 0; i < cSubTables; i++) + { + nSubTableOffsets[i] = grstrm.ReadIntFromFont(); + } + + grstrm.SetPositionInFont(lTableStart + nSubTableOffsets[iSubTable]); + + // Now we are at the beginning of the desired sub-table. + + // Get the position of the start of the table. + long lSubTableStart; + grstrm.GetPositionInFont(&lSubTableStart); + + // rule version + int fxdRuleVersion = (*pfxdSilfVersion >= 0x00030000) ? + ReadVersion(grstrm) : + *pfxdSilfVersion; + + long lPassBlockPos = -1; + long lPseudosPos = -1; + if (*pfxdSilfVersion >= 0x00030000) + { + lPassBlockPos = grstrm.ReadUShortFromFont() + lSubTableStart; + lPseudosPos = grstrm.ReadUShortFromFont() + lSubTableStart; + } + + // maximum glyph ID + data16 chwTmp; + *pchwMaxGlyphID = grstrm.ReadUShortFromFont(); + + // extra ascent + m_mXAscent = grstrm.ReadShortFromFont(); + m_mXDescent = grstrm.ReadShortFromFont(); + + // TODO: decide whether we want these: + m_mXAscent = 0; + m_mXDescent = 0; + + // number of passes + byte cPasses = grstrm.ReadByteFromFont(); + // index of first substitution pass + byte ipassSub1 = grstrm.ReadByteFromFont(); + // index of first positioning pass + byte ipassPos1 = grstrm.ReadByteFromFont(); + // index of first justification pass + byte ipassJust1 = grstrm.ReadByteFromFont(); + // index of first reordered pass, or 0xFF if no reordering + byte ipassReordered1 = grstrm.ReadByteFromFont(); + if (*pfxdSilfVersion < 0x00020000) + { + Assert(ipassJust1 == cPasses || ipassJust1 == ipassPos1); + ipassJust1 = ipassPos1; + } + + // Sanity checks. + if (cPasses > kMaxPasses || ipassSub1 > cPasses || ipassPos1 > cPasses) + return false; // bad table + + // line-break flag + int nLineBreak = grstrm.ReadByteFromFont(); + if (nLineBreak != 0 && nLineBreak != 1) + return false; // bad table + m_fLineBreak = (bool)nLineBreak; + + // range of possible cross-line-boundary contextualization + m_cchwPreXlbContext = grstrm.ReadByteFromFont(); + m_cchwPostXlbContext = grstrm.ReadByteFromFont(); + + // actual glyph ID for pseudo-glyph (ID of bogus attribute) + byte bTmp; // unsigned + bTmp = grstrm.ReadByteFromFont(); + m_chwPseudoAttr = bTmp; + // breakweight + bTmp = grstrm.ReadByteFromFont(); + m_chwBWAttr = bTmp; + // directionality + bTmp = grstrm.ReadByteFromFont(); + m_chwDirAttr = bTmp; + + // Sanity checks. + if (m_chwPseudoAttr > 100 || m_chwBWAttr > 100 || m_chwDirAttr > 100) + return false; // bad table + + if (*pfxdSilfVersion >= 0x00020000) + { + // reserved + grstrm.ReadByteFromFont(); + grstrm.ReadByteFromFont(); + + // justification levels + m_cJLevels = grstrm.ReadByteFromFont(); + if (m_cJLevels > kMaxJLevels) + return false; // bad table + m_fBasicJust = (m_cJLevels == 0); + m_chwJStretch0 = 0xffff; // if no justification + m_chwJShrink0 = 0xffff; + m_chwJStep0 = 0xffff; + m_chwJWeight0 = 0xffff; + for (int i = 0; i < m_cJLevels; i++) + { + // justification glyph attribute IDs + bTmp = grstrm.ReadByteFromFont(); + if (i == 0) + m_chwJStretch0 = bTmp; + bTmp = grstrm.ReadByteFromFont(); + if (i == 0) + m_chwJShrink0 = bTmp; + bTmp = grstrm.ReadByteFromFont(); + if (i == 0) + m_chwJStep0 = bTmp; + bTmp = grstrm.ReadByteFromFont(); + if (i == 0) + m_chwJWeight0 = bTmp; + bTmp = grstrm.ReadByteFromFont(); // runto + // reserved + grstrm.ReadByteFromFont(); + grstrm.ReadByteFromFont(); + grstrm.ReadByteFromFont(); + } + } + else + { + m_cJLevels = 0; + m_fBasicJust = true; + m_chwJStretch0 = 0xffff; + m_chwJShrink0 = 0xffff; + m_chwJStep0 = 0xffff; + m_chwJWeight0 = 0xffff; + } + + // number of component attributes + chwTmp = grstrm.ReadUShortFromFont(); + m_cComponents = chwTmp; + + // number of user-defined slot attributes + m_cnUserDefn = grstrm.ReadByteFromFont(); + if (m_cnUserDefn > kMaxUserDefinableSlotAttrs) + return false; // bad table + + // max number of ligature components per glyph + m_cnCompPerLig = grstrm.ReadByteFromFont(); + if (m_cnCompPerLig > 16) + return false; // bad table + + // directions supported + bTmp = grstrm.ReadByteFromFont(); + m_grfsdc = bTmp; + if (m_grfsdc > kfsdcHorizLtr + kfsdcHorizRtl + kfsdcVertFromLeft + kfsdcVertFromRight) + return false; // bad table + + // reserved + bTmp = grstrm.ReadByteFromFont(); + bTmp = grstrm.ReadByteFromFont(); + bTmp = grstrm.ReadByteFromFont(); + + // critical features + int cCriticalFeatures; + if (*pfxdSilfVersion >= 0x00020000) + { + // reserved + bTmp = grstrm.ReadByteFromFont(); + + cCriticalFeatures = grstrm.ReadByteFromFont(); + Assert(cCriticalFeatures == 0); + if (cCriticalFeatures != 0) + return false; // bad table + + // reserved + bTmp = grstrm.ReadByteFromFont(); + } + + // rendering behaviors--ignore for now + byte cBehaviors = grstrm.ReadByteFromFont(); + data16 chwBehaviors[kMaxRenderingBehavior]; + for (i = 0; i < cBehaviors; i++) + { + chwBehaviors[i] = grstrm.ReadUShortFromFont(); + } + + // linebreak glyph ID + m_chwLBGlyphID = grstrm.ReadUShortFromFont(); + + // Jump to the beginning of the pass offset block, if we have this information. + if (*pfxdSilfVersion >= 0x00030000) + grstrm.SetPositionInFont(lPassBlockPos); + else + // Otherwise assume that's where we are! + Assert(lPassBlockPos == -1); + + // offsets to passes, relative to the start of this subtable; + // note that we read (cPasses + 1) of these + int nPassOffsets[kMaxPasses]; + for (i = 0; i <= cPasses; i++) + { + nPassOffsets[i] = grstrm.ReadIntFromFont(); + } + + // Jump to the beginning of the pseudo-glyph info block, if we have this information. + if (*pfxdSilfVersion >= 0x00030000) + grstrm.SetPositionInFont(lPseudosPos); + else + // Otherwise assume that's where we are! + Assert(lPseudosPos == -1); + + // number of pseudo-glyphs and search constants + short snTmp; + snTmp = grstrm.ReadShortFromFont(); + m_cpsd = snTmp; + snTmp = grstrm.ReadShortFromFont(); + m_dipsdInit = snTmp; + snTmp = grstrm.ReadShortFromFont(); + m_cPsdLoop = snTmp; + snTmp = grstrm.ReadShortFromFont(); + m_ipsdStart = snTmp; + + // unicode-to-pseudo map + m_prgpsd = new GrPseudoMap[m_cpsd]; + for (i = 0; i < m_cpsd; i++) + { + if (*pfxdSilfVersion <= 0x00010000) + { + utf16 chwUnicode = grstrm.ReadUShortFromFont(); + m_prgpsd[i].SetUnicode(chwUnicode); + } + else + { + int nUnicode = grstrm.ReadIntFromFont(); + m_prgpsd[i].SetUnicode(nUnicode); + } + gid16 chwPseudo = grstrm.ReadUShortFromFont(); + m_prgpsd[i].SetPseudoGlyph(chwPseudo); + } + + // class table + m_pctbl = new GrClassTable(); + if (!m_pctbl->ReadFromFont(grstrm, *pfxdSilfVersion)) + return false; + + // passes + return m_ptman->CreateAndReadPasses(grstrm, *pfxdSilfVersion, fxdRuleVersion, + cPasses, lSubTableStart, nPassOffsets, + ipassSub1, ipassPos1, ipassJust1, ipassReordered1); +} + +/*---------------------------------------------------------------------------------------------- + Set up the engine with no Graphite smarts at all, just dummy tables. +----------------------------------------------------------------------------------------------*/ +void GrEngine::CreateEmpty() +{ + // Silf table + + m_mXAscent = 0; + m_mXDescent = 0; + + m_fLineBreak = false; + + // range of possible cross-line-boundary contextualization + m_cchwPreXlbContext = 0; + m_cchwPostXlbContext = 0; + + // Bogus attribute IDs: + m_chwPseudoAttr = 1; // actual glyph ID for pseudo-glyph (ID of bogus attribute) + m_chwBWAttr = 2; // breakweight + m_chwDirAttr = 3; // directionality + + m_cComponents = 0; // number of component attributes + + m_cnUserDefn = 0; // number of user-defined slot attributes + m_cnCompPerLig = 0; // max number of ligature components + + m_grfsdc = kfsdcHorizLtr; // supported script direction + + // linebreak glyph ID + m_chwLBGlyphID = 0xFFFE; + + // number of pseudo-glyphs and search constants + m_cpsd = 0; + m_dipsdInit = 0; + m_cPsdLoop = 0; + m_ipsdStart = 0; + + // class table + m_pctbl = new GrClassTable(); + m_pctbl->CreateEmpty(); + + // passes + if (m_ptman != 0) + m_ptman->CreateEmpty(); + + // Gloc and Glat tables + m_pgtbl = new GrGlyphTable(); + m_pgtbl->SetNumberOfGlyphs(0); + m_pgtbl->SetNumberOfComponents(0); + m_pgtbl->SetNumberOfStyles(1); // for now + + m_pgtbl->CreateEmpty(); + + // Feat table + m_cfeat = 0; + m_rglcidFeatLabelLangs = NULL; + m_clcidFeatLabelLangs = 0; + + // Language table + m_langtbl.CreateEmpty(); +} + +/*---------------------------------------------------------------------------------------------- + Create the glyph table and fill it in from the font stream. +----------------------------------------------------------------------------------------------*/ +bool GrEngine::ReadGlocAndGlatTables(GrIStream & grstrmGloc, long lGlocStart, + GrIStream & grstrmGlat, long lGlatStart, + int chwGlyphIDMax, int fxdSilfVersion) +{ + // Determine the highest used glyph ID number. +// int chwGlyphIDMax = m_chwLBGlyphID; +// for (int ipsd = 0; ipsd < m_cpsd; ipsd++) +// { +// if (m_prgpsd[ipsd].PseudoGlyph() > chwGlyphIDMax) +// chwGlyphIDMax = m_prgpsd[ipsd].PseudoGlyph(); +// } + + // Create the glyph table. + m_pgtbl = new GrGlyphTable(); + m_pgtbl->SetNumberOfGlyphs(chwGlyphIDMax + 1); + m_pgtbl->SetNumberOfComponents(m_cComponents); + m_pgtbl->SetNumberOfStyles(1); // for now + + return m_pgtbl->ReadFromFont(grstrmGloc, lGlocStart, grstrmGlat, lGlatStart, + m_chwBWAttr, m_chwJStretch0, m_cJLevels, m_cnCompPerLig, fxdSilfVersion); +} + +/*---------------------------------------------------------------------------------------------- + Read the contents of the "Feat" table from the stream, which is on an extended + TrueType font file. +----------------------------------------------------------------------------------------------*/ +bool GrEngine::ReadFeatTable(GrIStream & grstrm, long lTableStart) +{ + short snTmp; + data16 chwTmp; + int nTmp; + + grstrm.SetPositionInFont(lTableStart); + + // version + int fxdVersion = ReadVersion(grstrm); + if (fxdVersion > kFeatVersion) + return false; + + // number of features + int cfeat; + cfeat = grstrm.ReadUShortFromFont(); + if (cfeat > kMaxFeatures) + return false; // bad table; + + // reserved + chwTmp = grstrm.ReadUShortFromFont(); + nTmp = grstrm.ReadIntFromFont(); + + std::vector<featid> vnIDs; // unsigned ints + std::vector<int> vnOffsets; + std::vector<int> vcfset; + + m_cfeat = 0; + int ifeat; + for (ifeat = 0; ifeat < cfeat; ifeat++) + { + // ID + featid nID; + if (fxdVersion >= 0x00020000) + nID = (unsigned int)grstrm.ReadIntFromFont(); + else + nID = grstrm.ReadUShortFromFont(); + vnIDs.push_back(nID); + // number of settings + data16 cfset = grstrm.ReadUShortFromFont(); + vcfset.push_back(cfset); + if (fxdVersion >= 0x00020000) + grstrm.ReadShortFromFont(); // pad bytes + // offset to settings list + nTmp = grstrm.ReadIntFromFont(); + vnOffsets.push_back(nTmp); + // flags + chwTmp = grstrm.ReadUShortFromFont(); + Assert(chwTmp == 0x8000); // mutually exclusive + // index into name table of UI label + int nNameId = grstrm.ReadShortFromFont(); + + if (fxdVersion <= 0x00020000 && nID == GrFeature::knLangFeatV2) + { + // Ignore the obsolete "lang" feature which has ID = 1 + vnIDs.pop_back(); + vcfset.pop_back(); + vnOffsets.pop_back(); + continue; + } + AddFeature(nID, nNameId, cfset); + } + + // default feature setting value and name strings; + for (ifeat = 0; ifeat < m_cfeat; ifeat++) + { + GrFeature * pfeat = m_rgfeat + ifeat; + Assert(pfeat->ID() == vnIDs[ifeat]); + + grstrm.SetPositionInFont(lTableStart + vnOffsets[ifeat]); + + // Each feature has been initialized with blank spots for the number of settings + // it has. + int cfset = vcfset[ifeat]; + Assert(pfeat->NumberOfSettings() == cfset); + for (int ifset = 0; ifset < cfset; ifset++) + { + snTmp = grstrm.ReadShortFromFont(); // yes, signed (TT feat table spec says unsigned) + short snTmp2 = grstrm.ReadShortFromFont(); + pfeat->AddSetting(snTmp, snTmp2); + + if (ifset == 0) + { + // default setting value--first in list + pfeat->SetDefault(snTmp); + } + } + } + + return true; +} + +/*---------------------------------------------------------------------------------------------- + Read the contents of the "Sill" table from the stream, which is on an extended + TrueType font file. +----------------------------------------------------------------------------------------------*/ +bool GrEngine::ReadSillTable(GrIStream & grstrm, long lTableStart) +{ + grstrm.SetPositionInFont(lTableStart); + + // version + int fxdVersion = ReadVersion(grstrm); + if (fxdVersion > kSillVersion) + return false; + + return m_langtbl.ReadFromFont(&grstrm, fxdVersion); +} + +/*---------------------------------------------------------------------------------------------- + An error happened in running the tables. Set up temporary dummy tables, and try running + with dumb rendering. + Return kresUnexpected if even running the empty tables crashed, kresFail otherwise. +----------------------------------------------------------------------------------------------*/ +GrResult GrEngine::RunUsingEmpty(Segment * psegNew, Font * pfont, + GrCharStream * pchstrm, LayoutEnvironment & layout, + int ichStop, + float dxMaxWidth, + bool fNeedFinalBreak, bool fMoreText, bool fInfiniteWidth, + int ichwCallerBtLim, + int nDirDepth, SegEnd estJ) +{ + // Save the current state of the engine. + int mXAscentSave = m_mXAscent; + int mXDescentSave = m_mXDescent; + bool fLineBreakSave = m_fLineBreak; + int cchwPreXlbContextSave = m_cchwPreXlbContext; + int cchwPostXlbContextSave = m_cchwPostXlbContext; + data16 chwPseudoAttrSave = m_chwPseudoAttr; + data16 chwBWAttrSave = m_chwBWAttr; + data16 chwDirAttrSave = m_chwDirAttr; + int cComponentsSave = m_cComponents; + int cnUserDefnSave = m_cnUserDefn; + int cnCompPerLigSave = m_cnCompPerLig; + unsigned int grfsdcSave = m_grfsdc; + gid16 chwLBGlyphIDSave = m_chwLBGlyphID; + int cpsdSave = m_cpsd; + int dipsdInitSave = m_dipsdInit; + int cPsdLoopSave = m_cPsdLoop; + int ipsdStartSave = m_ipsdStart; + GrTableManager * ptmanSave = m_ptman; + GrClassTable * pctblSave = m_pctbl; + GrGlyphTable * pgtblSave = m_pgtbl; + int cfeatSave = m_cfeat; + GrResult resFontReadSave = m_resFontRead; + + GrResult res = kresFail; + + pchstrm->Restart(); + + // Create dummy versions of the tables and run them. + m_pctbl = NULL; + m_pgtbl = NULL; + m_ptman = new GrTableManager(this); + m_ptman->State()->SetFont(pfont); + CreateEmpty(); + + try + { + m_ptman->Run(psegNew, pfont, pchstrm, NULL, kjmodiNormal, layout, + ichStop, dxMaxWidth, 0, + fNeedFinalBreak, fMoreText, -1, fInfiniteWidth, false, + ichwCallerBtLim, + nDirDepth, estJ); + } + catch(...) + { + // Still didn't work. Oh, well, not much we can do. + res = kresUnexpected; + } + + psegNew->SetErroneous(true); + + if (m_ptman) + delete m_ptman; + if (m_pctbl) + delete m_pctbl; + if (m_pgtbl) + delete m_pgtbl; + + // Put everything back the way it was. + m_mXAscent = mXAscentSave; + m_mXDescent = mXDescentSave; + m_fLineBreak = fLineBreakSave; + m_cchwPreXlbContext = cchwPreXlbContextSave; + m_cchwPostXlbContext = cchwPostXlbContextSave; + m_chwPseudoAttr = chwPseudoAttrSave; + m_chwBWAttr = chwBWAttrSave; + m_chwDirAttr = chwDirAttrSave; + m_cComponents = cComponentsSave; + m_cnUserDefn = cnUserDefnSave; + m_cnCompPerLig = cnCompPerLigSave; + m_grfsdc = grfsdcSave; + m_chwLBGlyphID = chwLBGlyphIDSave; + m_cpsd = cpsdSave; + m_dipsdInit = dipsdInitSave; + m_cPsdLoop = cPsdLoopSave; + m_ipsdStart = ipsdStartSave; + m_ptman = ptmanSave; + m_pctbl = pctblSave; + m_pgtbl = pgtblSave; + m_cfeat = cfeatSave; + m_resFontRead = resFontReadSave; + + return res; +} + +/*---------------------------------------------------------------------------------------------- + Return the glyph ID for the given Unicode value. +----------------------------------------------------------------------------------------------*/ +gid16 GrEngine::GetGlyphIDFromUnicode(int nUnicode) +{ + gid16 chwGlyphID = MapToPseudo(nUnicode); + if (chwGlyphID != 0) + return chwGlyphID; // return a pseudo-glyph + +#ifdef OLD_TEST_STUFF + if (!m_pCmap31) + // test procedures + return chwUnicode; +#endif // OLD_TEST_STUFF + + // Otherwise, get the glyph ID from the font's cmap. + + if (m_pCmap_3_10) + return gid16(TtfUtil::Cmap310Lookup(m_pCmap_3_10, nUnicode)); + else if (m_pCmap_3_1) + return gid16(TtfUtil::Cmap31Lookup(m_pCmap_3_1, nUnicode)); + else + return 0; +} + +/*---------------------------------------------------------------------------------------------- + Return the pseudo-glyph corresponding to the given Unicode input, or 0 if none. +----------------------------------------------------------------------------------------------*/ +gid16 GrEngine::MapToPseudo(int nUnicode) +{ + if (m_cpsd == 0) + return 0; + +#ifdef _DEBUG + int nPowerOf2 = 1; + while (nPowerOf2 <= m_cpsd) + nPowerOf2 <<= 1; + nPowerOf2 >>= 1; + // Now nPowerOf2 is the max power of 2 <= m_cpds + Assert((1 << m_cPsdLoop) == nPowerOf2); // m_cPsdLoop == log2(nPowerOf2) + Assert(m_dipsdInit == nPowerOf2); + Assert(m_ipsdStart == m_cpsd - m_dipsdInit); +#endif // _DEBUG + + int dipsdCurr = m_dipsdInit; + + GrPseudoMap * ppsdCurr = m_prgpsd + m_ipsdStart; + while (dipsdCurr > 0) + { + int nTest; + if (ppsdCurr < m_prgpsd) + nTest = -1; + else + nTest = ppsdCurr->Unicode() - nUnicode; + + if (nTest == 0) + return ppsdCurr->PseudoGlyph(); + + dipsdCurr >>= 1; // divide by 2 + if (nTest < 0) + ppsdCurr += dipsdCurr; + else // (nTest > 0) + ppsdCurr -= dipsdCurr; + } + + return 0; +} + +/*---------------------------------------------------------------------------------------------- + Return the actual glyph ID to use for glyph metrics and output. For most glyphs, this + is just the glyph ID we're already working with, but for pseudo-glyphs it will something + different. +----------------------------------------------------------------------------------------------*/ +gid16 GrEngine::ActualGlyphForOutput(gid16 chwGlyphID) +{ + gid16 chwActual = gid16(GlyphAttrValue(chwGlyphID, m_chwPseudoAttr)); + if (chwActual == 0) + return chwGlyphID; // not a pseudo, we're already working with the actual glyph ID + else + return chwActual; +} + +/*---------------------------------------------------------------------------------------------- + Look up name based on lang id and name id in name table. + Currently used to look up feature names. +----------------------------------------------------------------------------------------------*/ +std::wstring GrEngine::StringFromNameTable(int nLangID, int nNameID) +{ + std::wstring stuName; + stuName.erase(); + size_t lOffset = 0, lSize = 0; + + // The Graphite compiler stores our names in either + // the MS (platform id = 3) Unicode (writing system id = 1) table + // or the MS Symbol (writing system id = 0) table. Try MS Unicode first. + // lOffset & lSize are in bytes. + // new interface: + if (!TtfUtil::GetNameInfo(m_pNameTbl, 3, 1, nLangID, nNameID, lOffset, lSize)) + { + if (!TtfUtil::GetNameInfo(m_pNameTbl, 3, 0, nLangID, nNameID, lOffset, lSize)) + { + return stuName; + } + } + + size_t cchw = (unsigned(lSize) / sizeof(utf16)); + utf16 * pchwName = new utf16[cchw+1]; // lSize - byte count for Uni str + const utf16 *pchwSrcName = reinterpret_cast<const utf16*>(m_pNameTbl + lOffset); + std::transform(pchwSrcName, pchwSrcName + cchw, pchwName, std::ptr_fun<utf16,utf16>(lsbf)); + pchwName[cchw] = 0; // zero terminate + #ifdef _WIN32 + stuName.assign((const wchar_t*)pchwName, cchw); + #else + wchar_t * pchwName32 = new wchar_t[cchw+1]; // lSize - byte count for Uni str + for (int i = 0; i <= signed(cchw); i++) { + pchwName32[i] = pchwName[i]; + } + stuName.assign(pchwName32, cchw); + delete [] pchwName32; + #endif + + delete [] pchwName; + return stuName; +} + +/*---------------------------------------------------------------------------------------------- + Add a feature (eg, when loading from the font). +----------------------------------------------------------------------------------------------*/ +void GrEngine::AddFeature(featid nID, int nNameId, int cfset, int nDefault) +{ + if (m_cfeat >= kMaxFeatures) + return; + + int ifeat = m_cfeat; + m_rgfeat[ifeat].Initialize(nID, nNameId, cfset, nDefault); +// m_hmnifeat.Insert(nID, ifeat); + m_cfeat++; +} + +/*---------------------------------------------------------------------------------------------- + Return the feature with the given ID, or NULL if none. +----------------------------------------------------------------------------------------------*/ +GrFeature * GrEngine::FeatureWithID(featid nID, int * pifeat) +{ + for (int ifeat = 0; ifeat < m_cfeat; ifeat++) + { + if (m_rgfeat[ifeat].ID() == nID) + { + *pifeat = ifeat; + return m_rgfeat + ifeat; + } + } + *pifeat = -1; + return NULL; + + + // Alternate implementation if we end up with so many features that we need to use + // a hash map: +// int ifeat; +// if (m_hmnifeat.Retrieve(nID, &ifeat)) +// { +// Assert(ifeat < kMaxFeatures); +// return m_rgfeat + ifeat; // may be NULL +// } +// else +// return NULL; +} + +/*---------------------------------------------------------------------------------------------- + Return the default for the feature at the given index in the array. +----------------------------------------------------------------------------------------------*/ +int GrEngine::DefaultForFeatureAt(int ifeat) +{ + Assert(ifeat < kMaxFeatures); + if (ifeat >= m_cfeat || ifeat < 0) + return 0; // undefined feature + + return m_rgfeat[ifeat].DefaultValue(); +} + +/*---------------------------------------------------------------------------------------------- + Set the default for the feature at the given index in the array. +----------------------------------------------------------------------------------------------*/ +void GrEngine::SetDefaultForFeatureAt(int ifeat, int nValue) +{ + Assert(ifeat < kMaxFeatures); + Assert(ifeat < m_cfeat); + if (ifeat >= 0) + m_rgfeat[ifeat].SetDefault(nValue); +} + +/*---------------------------------------------------------------------------------------------- + Return the default feature settings for the given language. +----------------------------------------------------------------------------------------------*/ +void GrEngine::DefaultsForLanguage(isocode lgcode, + std::vector<featid> & vnFeats, std::vector<int> & vnValues) +{ + m_langtbl.LanguageFeatureSettings(lgcode, vnFeats, vnValues); +} + +/*---------------------------------------------------------------------------------------------- + Initialize the directionality and breakweight attributes of a new slot based on its + glyph ID. + REVIEW: we could optimize by not initializing from the glyph table right away, + but waiting until we need the value. +----------------------------------------------------------------------------------------------*/ +void GrEngine::InitSlot(GrSlotState * pslot, int nUnicode) +{ + gid16 chwGlyphID = pslot->GlyphID(); + + if (m_ptman->InternalJustificationMode() != kjmodiNormal) + { + if (m_cJLevels > 0) + { + Assert(m_cJLevels == 1); // for now + // TODO: do this at level 0. + pslot->SetJStretch(m_pgtbl->GlyphAttrValue(chwGlyphID, m_chwJStretch0)); + pslot->SetJShrink( m_pgtbl->GlyphAttrValue(chwGlyphID, m_chwJShrink0)); + pslot->SetJStep( m_pgtbl->GlyphAttrValue(chwGlyphID, m_chwJStep0)); + pslot->SetJWeight( m_pgtbl->GlyphAttrValue(chwGlyphID, m_chwJWeight0)); + } + else if (nUnicode == knSpace) + { + // Basic stretch/shrink for whitespace. + // TODO: do this at level 0. + Assert(m_fBasicJust); + int mAdv = pslot->AdvanceX(m_ptman); + pslot->SetJStretch(mAdv * 100); // stretch up to 100 times natural width + pslot->SetJShrink(mAdv / 4); // shrink to 75% + pslot->SetJWeight(1); + } + } + + if (m_pgtbl && !m_pgtbl->IsEmpty()) + { + // Initialize from glyph table. + pslot->SetBreakWeight(m_pgtbl->GlyphAttrValue(chwGlyphID, m_chwBWAttr)); + + DirCode dirc = DirCode(m_pgtbl->GlyphAttrValue(chwGlyphID, m_chwDirAttr)); + if (BidiCode(nUnicode) && (chwGlyphID == 0 || dirc == kdircNeutral)) + // A built-in character for the bidi algorithm that is not defined in the font but + // must be given a special directionality: + goto LDefaults; + + pslot->SetDirectionality(dirc); + + return; + } + +LDefaults: + // Otherwise, in fill defaults for some basic values. + if (pslot->BreakWeight() == GrSlotState::kNotYetSet) + { + switch (nUnicode) + { + case knSpace: + pslot->SetBreakWeight(klbWordBreak); + break; + case knHyphen: + pslot->SetBreakWeight(klbHyphenBreak); + break; + default: + pslot->SetBreakWeight(klbLetterBreak); + } + } + + if ((int)pslot->Directionality() == GrSlotState::kNotYetSet) + { + switch (nUnicode) + { + case knSpace: + pslot->SetDirectionality(kdircWhiteSpace); + break; + case knLRM: + pslot->SetDirectionality(kdircL); + break; + case knRLM: + pslot->SetDirectionality(kdircR); + break; + case knLRO: + pslot->SetDirectionality(kdircLRO); + break; + case knRLO: + pslot->SetDirectionality(kdircRLO); + break; + case knLRE: + pslot->SetDirectionality(kdircLRE); + break; + case knRLE: + pslot->SetDirectionality(kdircRLE); + break; + case knPDF: + pslot->SetDirectionality(kdircPDF); + break; + default: + if (chwGlyphID == LBGlyphID()) + pslot->SetDirectionality(kdircNeutral); + else + pslot->SetDirectionality(kdircL); + } + } + else + Assert(false); // currently no way to set directionality ahead of time +} + +/*---------------------------------------------------------------------------------------------- + We have just changed this slot's glyph ID. Change the directionality and breakweight + attributes of a new slot based on its glyph ID. +----------------------------------------------------------------------------------------------*/ +void GrEngine::SetSlotAttrsFromGlyphAttrs(GrSlotState * pslot) +{ + InitSlot(pslot); +} + +/*---------------------------------------------------------------------------------------------- + Create a new segment. +----------------------------------------------------------------------------------------------*/ +void GrEngine::NewSegment(Segment ** ppseg) +{ + *ppseg = new Segment; +} + +/*---------------------------------------------------------------------------------------------- + Methods to pass on to the tables. +----------------------------------------------------------------------------------------------*/ +gid16 GrEngine::GetClassGlyphIDAt(int nClass, int nIndex) +{ + if (nIndex < 0) + return 0; + if (nClass < 0) + return 0; + + try { + return m_pctbl->GetGlyphID(nClass, nIndex); + } + catch (...) + { + return 0; + } +} + +int GrEngine::GetIndexInGlyphClass(int nClass, gid16 chwGlyphID) +{ + try { + return m_pctbl->FindIndex(nClass, chwGlyphID); + } + catch (...) + { + return -1; + } +} + +size_t GrEngine::NumberOfGlyphsInClass(int nClass) +{ + try { + return m_pctbl->NumberOfGlyphsInClass(nClass); + } + catch (...) + { + return 0; + } +} + +int GrEngine::GlyphAttrValue(gid16 chwGlyphID, int nAttrID) +{ +#ifdef OLD_TEST_STUFF + if (!m_pgtbl) // test procedures + return 0; +#endif // OLD_TEST_STUFF + + return m_pgtbl->GlyphAttrValue(chwGlyphID, nAttrID); +} + +int GrEngine::ComponentIndexForGlyph(gid16 chwGlyphID, int nCompID) +{ +#ifdef OLD_TEST_STUFF + if (!m_pgtbl) // test procedures + return -1; +#endif // OLD_TEST_STUFF + + return m_pgtbl->ComponentIndexForGlyph(chwGlyphID, nCompID); +} + + +//:>******************************************************************************************** +//:> For test procedures +//:>******************************************************************************************** + +//:Ignore + +#ifdef OLD_TEST_STUFF + +void GrEngine::SetUpPseudoGlyphTest() +{ + m_cpsd = 5; + + m_prgpsd = new GrPseudoMap[m_cpsd]; + + m_prgpsd[0].SetUnicode(65); + m_prgpsd[0].SetPseudoGlyph(1065); + + m_prgpsd[1].SetUnicode(66); + m_prgpsd[1].SetPseudoGlyph(1066); + + m_prgpsd[2].SetUnicode(67); + m_prgpsd[2].SetPseudoGlyph(1067); + + m_prgpsd[3].SetUnicode(68); + m_prgpsd[3].SetPseudoGlyph(1068); + + m_prgpsd[4].SetUnicode(69); + m_prgpsd[4].SetPseudoGlyph(1069); + + m_dipsdInit = 4; + m_cPsdLoop = 2; + m_ipsdStart = 1; +} + +#endif // OLD_TEST_STUFF + + +//:>******************************************************************************************** +//:> New interface +//:>******************************************************************************************** +/*---------------------------------------------------------------------------------------------- + Store the default features values as specified by the client. +----------------------------------------------------------------------------------------------*/ +void GrEngine::AssignDefaultFeatures(int cfeat, FeatureSetting * prgfset) +{ + for (int ifeatIn = 0; ifeatIn < cfeat; ifeatIn++) + { + int ifeat; + FeatureWithID(prgfset[ifeatIn].id, &ifeat); + if (ifeat >= 0 ) + SetDefaultForFeatureAt(ifeat, prgfset[ifeatIn].value); + } +} + +bool GrEngine::LoggingTransduction() +{ + Assert(false); + //return m_pfface->getLogging(); + //return m_fLogXductn; + return true; +} + +} // namespace gr + +//:End Ignore + diff --git a/Build/source/libs/graphite-engine/src/segment/GrEngine.h b/Build/source/libs/graphite-engine/src/segment/GrEngine.h new file mode 100644 index 00000000000..ee7d8de0e77 --- /dev/null +++ b/Build/source/libs/graphite-engine/src/segment/GrEngine.h @@ -0,0 +1,601 @@ +/*-------------------------------------------------------------------- +Copyright (C) 1999, 2001 SIL International. All rights reserved. + +Distributable under the terms of either the Common Public License or the +GNU Lesser General Public License, as specified in the LICENSING.txt file. + +File: GrEngine.h +Responsibility: Sharon Correll +Last reviewed: Not yet. + +Description: + Contains the definition of the GrEngine class. +----------------------------------------------------------------------------------------------*/ +#ifdef _MSC_VER +#pragma once +#endif +#ifndef GR_ENGINE_INCLUDED +#define GR_ENGINE_INCLUDED +#include <limits> + +#ifdef _MSC_VER +#include <crtdbg.h> +#endif + +namespace gr +{ +class IGrJustifier; +class Segment; + + +/*---------------------------------------------------------------------------------------------- + The GrEngine serves as the top level object that knows how to run Graphite tables + and generate Graphite segments. + + Primarily, this class implements IRenderEngine, which allows it to serve as a FW + rendering engine. It also implements ISimpleInit, a general interface for initializing + using a string. Finally, it implements ITraceControl, a very simple interface which + allows a client to flip a flag indicating whether or not we want to output a log of + the Graphite transduction process. + + Hungarian: greng +----------------------------------------------------------------------------------------------*/ +class GrEngine : public GraphiteProcess +{ + friend class FontFace; + +public: + // Constructors & destructors: + GrEngine(); + void BasicInit(); + virtual ~GrEngine(); + void DestroyContents(bool fDestroyCmap = true); + + // ISimpleInit methods: + ////GrResult InitNew(const byte * prgb, int cb); -- obsolete + ////GrResult get_InitializationData(int cchMax, OLECHAR * rgchw, int * pcch); -- obsolete + + // ITraceControl methods: + //GrResult SetTracing(int n); + //GrResult GetTracing(int * pnOptions); + + // IRenderingFeatures methods: + //GrResult GetFeatureIDs(int cMax, int * prgFids, int * pcfid); + //GrResult GetFeatureLabel(int fid, int nLanguage, int cchMax, OLECHAR * rgchw, int * pcch); + //GrResult GetFeatureValues(int fid, int cfvalMax, + // int * prgfval, int * pcfval, int * pfvalDefault); + //GrResult GetFeatureValueLabel(int fid, int fval, int nLanguage, + // int cchMax, OLECHAR * rgchw, int * pcch); + + // IRenderEngine methods: + ////GrResult InitRenderer(IGrGraphics * pgg, OLECHAR * prgchData, int cchData); -- obsolete + ////GrResult FontIsValid(OLECHAR * prgchwErrMsg, int cchMaxErrMsg); + + GrResult get_SegDatMaxLength(int * pcb); + + //GrResult FindBreakPoint(Font * pfont, ITextSource * pgts, DELETE + // int ichMin, int ichLim, int ichLimBacktrack, + // bool fNeedFinalBreak, + // bool fStartLine, + // float dxMaxWidth, + // LineBrk lbPref, LineBrk lbMax, + // TrWsHandling twsh, bool fParaRtoL, + // Segment ** ppsegRet, + // int * pdichwLimSeg, float * pdxWidth, SegEnd * pest, + // int cbPrev, byte * pbPrevSegDat, + // int cbNextMax, byte * pbNextSegDat, int * pcbNextSegDat, + // int * pdichwContext, + // std::ostream * pstrmLog, + // OLECHAR * prgchwErrMsg, int cchMaxErrMsg); + + //GrResult FindBreakPointWJust(Font * pfont, ITextSource * pgts, IGrJustifier * pgjus, + // int ichMin, int ichLim, int ichLimBacktrack, DELETE + // bool fNeedFinalBreak, + // bool fStartLine, + // float dxMaxWidth, + // LineBrk lbPref, LineBrk lbMax, + // TrWsHandling twsh, bool fParaRtoL, + // Segment ** ppsegRet, + // int * pdichwLimSeg, float * pdxWidth, SegEnd * pest, + // int cbPrev, byte * pbPrevSegDat, + // int cbNextMax, byte * pbNextSegDat, int * pcbNextSegDat, + // int * pdichwContext, + // std::ostream * pstrmLog, + // OLECHAR * prgchwErrMsg, int cchMaxErrMsg); + + GrResult get_ScriptDirection(unsigned int * pgrfsdc, OLECHAR * prgchwErrMsg, int cchMaxErrMsg); + + // DELETE + //GrResult MakeJustifiedSegment(Font * pfont, ITextSource * pgts, IGrJustifier * pgj, + // int ichMin, int ichLim, + // bool fStartLine, bool fEndLine, + // float dxUnjustifiedWidth, float dxJustifiedWidth, + // TrWsHandling twsh, SegEnd est, LineBrk lb, + // bool fParaRtl, + // Segment ** ppsegRet, float * pdxWidth, + // //int cbPrev, byte * pbPrevSegDat, int cbNextMax, byte * pbNextSegDat, int * pcbNextSegDat, int * pdichwContext, + // std::ostream * pstrmLog, + // OLECHAR * prgchwErrMsg, int cchwMaxErrMsg); + + // IJustifyingRenderer methods: + // These methods return kresInvalidArg if the attribute ID is invalid or inappropriate; + // kresFail if the engine is not in an appropriate state to return the information. + virtual GrResult getGlyphAttribute(int iGlyph, int jgat, int nLevel, float * pValueRet); + virtual GrResult getGlyphAttribute(int iGlyph, int jgat, int nLevel, int * pValueRet); + virtual GrResult setGlyphAttribute(int iGlyph, int jgat, int nLevel, float value); + virtual GrResult setGlyphAttribute(int iGlyph, int jgat, int nLevel, int value); + + // Interface methods not used by FW: + + ////GrResult IsGraphiteFont(IGrGraphics * pgg); -- obsolete + + ////GrResult FontAscentAndDescent(IGrGraphics * pgg, int * pysAscent, int * pysDescent); -- obsolete + + //GrResult MakeMeasuredSegment( + // Font * pfont, ITextSource * pgts, + // int ichMin, int ichLim, + // bool fParaRtl, + // Segment ** ppsegRet, SegEnd * pest, + // std::ostream * pstrmLog, + // OLECHAR * prgchwErrMsg, int cchMaxErrMsg); + + //GrResult MakeSegmentFromRange(Font * pfont, ITextSource * pgts, IGrJustifier * pgj, + // int ichMin, int ichLim, + // bool fStartLine, bool fEndLine, + // TrWsHandling twsh, bool fParaRtl, + // Segment ** ppsegRet, float * pdxWidth, SegEnd * pest, + // int cbPrev, byte * pbPrevSegDat, int cbNextMax, byte * pbNextSegDat, int * pcbNextSegDat, + // int * pdichwContext, + // std::ostream * pstrmLog, + // OLECHAR * prgchwErrMsg, int cchwMaxErrMsg); + + // NEW interface + + GrResult ReadFontTables(Font * pfont, bool fItalic); + + FontErrorCode IsValidForGraphite(int * pnVersion, int * pnSubVersion); + + // Other public methods + + GrResult FindBreakPointAux(Font * pfont, ITextSource * pgts, IGrJustifier * pgjus, + int ichwMin, int ichwLim, int ichwLimBacktrack, + bool fNeedFinalBreakArg, bool fStartLine, bool fEndLine, + float dxMaxWidth, bool fWidthIsCharCount, + LineBrk lbPref, LineBrk lbMax, TrWsHandling twsh, bool fParaRtl, + Segment ** ppsegRet, + int * pdichwLimSeg, + float * pdxWidth, SegEnd * pest, + int cbPrev, byte * pbPrevSegDat, + int cbNextMax, byte * pbNextSegDat, int * pcbNextSegDat, + int * pdichwContext, + std::ostream * pstrmLog, + OLECHAR * prgchwErrMsg, int cchMaxErrMsg); + + gid16 GetGlyphIDFromUnicode(int nUnicode); + gid16 ActualGlyphForOutput(utf16 chwGlyphID); + + int GetFontEmUnits() + { + return m_mFontEmUnits; + } + + gid16 LBGlyphID() + { + return m_chwLBGlyphID; + } + + gid16 GetClassGlyphIDAt(int nClass, int nIndex); + int GetIndexInGlyphClass(int nClass, gid16 chwGlyphID); + size_t NumberOfGlyphsInClass(int nClass); + int GlyphAttrValue(gid16 chwGlyphID, int nAttrID); + int ComponentIndexForGlyph(gid16 chwGlyphID, int nCompID); + + ////void InitFontName(std::wstring stuInitialize, std::wstring & stuFaceName, std::wstring & stuFeatures); -- obsolete + void RecordFontLoadError(OLECHAR * prgchwErrMsg, int cchMaxErrMsg); + void RecordFontRunError(OLECHAR * prgchwErrMsg, int cchMaxErrMsg, GrResult res); + void ClearFontError(OLECHAR * prgchwErrMsg, int cchMaxErrMsg); + void GetWritingSystemDirection(ITextSource * pgts, int ichwMin); + ////GrResult SetUp(IGrGraphics * pgg, std::wstring stuFeatures); -- obsolete + ////GrResult SetUp(IGrGraphics * pgg, std::wstring stuFeaturesArg, bool fBold, bool fItalic); -- obsolete + + GrResult RunUsingEmpty(Segment * psegNew, Font * pfont, + GrCharStream * pchstrm, LayoutEnvironment & layout, + int ichStop, + float dxMaxWidth, + bool fNeedFinalBreak, bool fMoreText, bool fInfiniteWidth, + int ichwCallerBtLim, + int nDirDepth, SegEnd estJ); + bool InErrorState() + { + return m_fInErrorState; + } + + std::wstring FaceName() + { + return m_stuFaceName; + } + std::wstring BaseFaceName() + { + Assert(m_stuBaseFaceName.size() == 0 || m_fUseSepBase); + return m_stuBaseFaceName; // empty if not using separate base font + } + bool Bold() + { + return m_fBold; + } + bool Italic() + { + return m_fItalic; + } + + void GetStyles(Font * pfont, int ichwMin, bool * pfBold, bool * pfItalic); + void SwitchGraphicsFont(bool fBase); + + void AddFeature(featid nID, int nNameId, int cfset, int nDefault = 0); + GrFeature * FeatureWithID(featid nID, int * pifeat); + int DefaultForFeatureAt(int ifeat); + void SetDefaultForFeatureAt(int ifeat, int nValue); + GrFeature * Feature(int ifeat) + { + if (ifeat >= kMaxFeatures) + return NULL; + return m_rgfeat + ifeat; + } + void DefaultsForLanguage(isocode lgcode, + std::vector<featid> & vnFeats, std::vector<int> & vnValues); + std::wstring StringFromNameTable(int nNameId, int nLang); + + void InitSlot(GrSlotState *, int nUnicode = -1); + void SetSlotAttrsFromGlyphAttrs(GrSlotState *); + + bool RightToLeft() + { + return m_fRightToLeft; + } + int TopDirectionLevel() + { + return (m_fRightToLeft) ? 1 : 0; + } + + GrGlyphTable * GlyphTable() + { + return m_pgtbl; + } + + GrClassTable * ClassTable() + { + return m_pctbl; + } + + int NumFeat() + { + return m_cfeat; + } + + bool LineBreakFlag() + { + return m_fLineBreak; + } + + int PreXlbContext() + { + return m_cchwPreXlbContext; + } + + int PostXlbContext() + { + return m_cchwPostXlbContext; + } + + int ExtraAscent() + { + return m_mXAscent; + } + + int ExtraDescent() + { + return m_mXDescent; + } + + float VerticalOffset() + { + return m_dysOffset; + } + + int NumUserDefn() + { + return m_cnUserDefn; + } + + int NumCompPerLig() + { + return m_cnCompPerLig; + } + + bool BasicJustification() + { + return m_fBasicJust; + } + + bool LoggingTransduction(); + //{ + // return m_pfface->getLogging(); + // //return m_fLogXductn; + //} + + bool FakeItalic() + { + return m_fFakeItalic; + } + + void NewSegment(Segment ** ppseg); + + static int ReadVersion(GrIStream & grstrm); + + /*---------------------------------------------------------------------------------------------- + Function to convert between coordinate systems. + This is identical to the built-in MulDiv function except that it rounds with better + precision. + + TODO: move these out of GrEngine to someplace more general. + ----------------------------------------------------------------------------------------------*/ + inline static int GrIntMulDiv(const int v, const int n, const int d) + { + return int(n < 0 ? + (double(v * n)/double(d)) - 0.5 : + (double(v * n)/double(d)) + 0.5); + } + inline static float GrFloatMulDiv(const float v, const float n, const float d) + { + return v * n / d; + } + inline static float GrIFIMulDiv(const int v, const float n, const int d) + { + return float(double(v) * n / double(d)); + } + inline static int GrFIFMulDiv(const float v, const int n, const float d) + { + return int(n < 0 ? + (v * double(n) / double(d)) - 0.5 : + (v * double(n) / double(d)) + 0.5); + } + inline static int RoundFloat(const float n) + { + return int(n < 0 ? n - 0.5 : n + 0.5); + } + +protected: + // Member variables: + + long m_cref; + + //////////// Font information //////////// + + bool m_fBold; + bool m_fItalic; + + // do we have the capacity to perform smart rendering for the various styles? + bool m_fSmartReg; + bool m_fSmartBold; + bool m_fSmartItalic; + bool m_fSmartBI; + // are the italics faked using a slant? + bool m_fFakeItalicCache; + bool m_fFakeBICache; + // control files for styled text; empty means not yet determined + std::wstring m_strCtrlFileReg; // regular + std::wstring m_strCtrlFileBold; // bold + std::wstring m_strCtrlFileItalic; // italic + std::wstring m_strCtrlFileBI; // bold-italic + + bool m_fFakeItalic; // true if we are simulating an italic slant in positioning + + std::wstring m_stuCtrlFile; // control file (.ttf file) from which we got + // currently loaded files + + std::wstring m_stuInitError; // description of any error that happened while initializing + // the engine + std::wstring m_stuErrCtrlFile; // file for which the error occurred + + std::wstring m_stuFaceName; // eg, "Times New Roman" + std::wstring m_stuFeatures; + + bool m_fUseSepBase; + std::wstring m_stuBaseFaceName; // from Sile table; empty if we are not using a separate + // control file + + GrResult m_resFontRead; // kresOk if the Graphite font was loaded successfully; + // kresFail if the Graphite font could not be found; + // kresUnexpected if the Graphite tables could not be loaded + + GrResult m_resFontValid; // kresOk if Graphite font & dc font match cmaps + // kresUnexpected if dumb rendering + // kresFail if dumb rendering with no cmap + // kresInvalidArg if the engine is not yet initialized + + FontErrorCode m_ferr; + int m_fxdBadVersion; + + int m_nFontCheckSum; // when loading a font using the GrGraphics, remember the + // check sum as a unique identifer + + int m_nScriptTag; // from the pertinent writing system; currently not used + unsigned int m_grfsdc; // supported script directions + + bool m_fRightToLeft; // overall writing-system direction + + int m_mXAscent; // extra ascent, in font's em-units + int m_mXDescent; // extra descent, in font's em_units + + float m_dysOffset; // vertical offset, in logical units (eg, for super/subscript) + + bool m_fBasicJust; // true if there are no complex justification tables/rules in the + // font, so we use basic whitespace justification + int m_cJLevels; // number of justification levels + + GrTableManager * m_ptman; + GrClassTable * m_pctbl; + GrGlyphTable * m_pgtbl; + + FontFace * m_pfface; + + GrFeature m_rgfeat[kMaxFeatures]; +// HashMap<int, int> m_hmnifeat; // maps from feature IDs to indices in the array +// // Review: do we need this, or is a linear search adequate? + int m_cfeat; // number of features present + + short * m_rglcidFeatLabelLangs; // LCIDs (language IDs) for all the labels in the feature table + size_t m_clcidFeatLabelLangs; + + GrLangTable m_langtbl; + + // are line-breaks relevant at all? + bool m_fLineBreak; + // ranges for possible cross-line-boundary contextualization + int m_cchwPreXlbContext; + int m_cchwPostXlbContext; + + // magic glyph attribute numbers + data16 m_chwPseudoAttr; // actual-for-pseudo fake glyph attribute + data16 m_chwBWAttr; // break-weight + data16 m_chwDirAttr; // directionality + data16 m_chwJStretch0; // justify.0.stretch + data16 m_chwJShrink0; // justify.0.shrink + data16 m_chwJStep0; // justify.0.step + data16 m_chwJWeight0; // justify.0.weight + + gid16 m_chwLBGlyphID; // magic line-break glyph ID + + int m_cComponents; // number of glyph attributes at the beginning of the glyph table + // that actually represent ligature components + + int m_cnUserDefn; // number of user-defined slot attributes + int m_cnCompPerLig; // max number of components needed per ligature + + int m_mFontEmUnits; // number of design units in the Em square for the font + + // for pseudo-glyph mappings + int m_cpsd; // number of psuedo-glyphs + GrPseudoMap * m_prgpsd; + int m_dipsdInit; // (max power of 2 <= m_cpsd); + // size of initial range to consider + int m_cPsdLoop; // log2(max power of 2 <= m_cpsd); + // indicates how many iterations are necessary + int m_ipsdStart; // m_cpsd - m_dipsdInit; + // where to start search + + // for Unicode to glyph ID mapping + ////TableBuffer m_tbufCmap; // hold the full cmap table + void * m_pCmap_3_1; // point to the MS Unicode cmap subtable + // uses platform 3 writing system 1 or 0, preferably 1 + // use for Unicode to Glyph ID conversion + void * m_pCmap_3_10; + byte * m_pCmapTbl; + + // for feature names and maybe other strings from font later + ////TableBuffer m_tbufNameTbl; // hold full name table; use Name() method to access + byte * m_pNameTbl; + + bool m_fLogXductn; // true if we want to log the transduction process + + bool m_fInErrorState; // true if we are handling a rendering error + + // Other protected methods: + + enum { + ksegmodeBreak = 0, // FindBreakPoint + ksegmodeJust, // MakeJustifiedSegment + //ksegmodeMms, // MakeMeasuredSegment + ksegmodeRange // MakeSegmentFromRange + }; + + void MakeSegment( + // Common parameters + Segment * psegRet, + Font * pfont, ITextSource * pgts, IGrJustifier * pjus, + LayoutEnvironment & layout, + int ichMin, int ichLim, + // for finding a line break + float dxMaxWidth, + bool fBacktracking, + // for making a justified segment + bool fJust, float dxJustifiedWidthJ, SegEnd estJ); + + int FindFontLim(ITextSource * pgts, int ichFontMin, int * pnDirDepth); + +#ifdef GR_FW + ////GrResult InitFromControlFile(IGrGraphics * pgg, std::wstring stuFaceName, + //// bool fBold, bool fItalic); -- obsolete + ////GrResult ReadFromControlFile(std::wstring stuFontFile); -- obsolete +// GrResult GetFallBackFont(std::wstring * pstu); +#endif + + ////GrResult ReadFromGraphicsFont(IGrGraphics * pgg, bool fItalic); -- obsolete + void DestroyEverything(); + + bool CheckTableVersions(GrIStream * pgrstrm, + byte *silf_tbl, int lSilfStart, + byte *gloc_tbl, int lGlocStart, + byte *feat_tbl, int lFeatStart, + int * pfxdBadVersion); + //bool ReadSileTable(GrIStream & grstrm, long lTableSTart, + // int * pmFontEmUnits, bool * pfMismatchedBase); + bool ReadSilfTable(GrIStream & grstrm, long lTableStart, int iSubTable, + int * pchwMaxGlyphID, int * pfxdVersion); + bool ReadGlocAndGlatTables(GrIStream & grstrm, long lGlocStart, GrIStream & glat_strm, long lGlatStart, + int chwMaxGlyphID, int fxdSilfVersion); + bool ReadFeatTable(GrIStream & grstrm, long lTableStart); + bool ReadSillTable(GrIStream & grstrm, long lTableStart); + + void CreateEmpty(); + + gid16 MapToPseudo(int nUnicode); + + // NEW interface + void AssignDefaultFeatures(int cfeat, FeatureSetting * prgfset); + + // Feature access for FontFace: + size_t NumberOfFeatures_ff(); + featid FeatureID_ff(size_t ifeat); + size_t FeatureWithID_ff(featid id); + bool GetFeatureLabel_ff(size_t ifeat, lgid language, utf16 * label); + int GetFeatureDefault_ff(size_t ifeat); + size_t NumberOfSettings_ff(size_t ifeat); + int GetFeatureSettingValue_ff(size_t ifeat, size_t ifset); + bool GetFeatureSettingLabel_ff(size_t ifeat, size_t ifset, lgid language, utf16 * label); + // Feature-label languages: + size_t NumberOfFeatLangs_ff(); + short GetFeatLabelLang_ff(size_t ilang); + // Language access for FontFace: + size_t NumberOfLanguages_ff(); + isocode GetLanguageCode_ff(size_t ilang); + + void SetUpFeatLangList(); + +//:Ignore +#ifdef OLD_TEST_STUFF + // For test procedures: + void SetUpCrossLineContextTest(); + void SetUpPseudoGlyphTest(); + void SetUpRuleActionTest(); + void SetUpRuleAction2Test(); + void SetUpAssocTest(); + void SetUpAssoc2Test(); + void SetUpDefaultAssocTest(); + void SetUpFeatureTest(); + void SetUpLigatureTest(); + void SetUpLigature2Test(); + + void InitFromControlFileTest(); +#endif // OLD_TEST_STUFF +//:End Ignore +}; + +} // namespace gr + +#if defined(GR_NO_NAMESPACE) +using namespace gr; +#endif + + +#endif // !GR_ENGINE_INCLUDED diff --git a/Build/source/libs/graphite-engine/src/segment/GrFSM.cpp b/Build/source/libs/graphite-engine/src/segment/GrFSM.cpp new file mode 100644 index 00000000000..bd4d13a24d0 --- /dev/null +++ b/Build/source/libs/graphite-engine/src/segment/GrFSM.cpp @@ -0,0 +1,509 @@ +/*--------------------------------------------------------------------*//*:Ignore this sentence. +Copyright (C) 1999, 2001 SIL International. All rights reserved. + +Distributable under the terms of either the Common Public License or the +GNU Lesser General Public License, as specified in the LICENSING.txt file. + +File: GrFSM.cpp +Responsibility: Sharon Correll +Last reviewed: Not yet. + +Description: + Implements the finite state machine. +----------------------------------------------------------------------------------------------*/ + +//:>******************************************************************************************** +//:> Include files +//:>******************************************************************************************** +#include "Main.h" +#ifdef _MSC_VER +#pragma hdrstop +#endif +// any other headers (not precompiled) + +#undef THIS_FILE +DEFINE_THIS_FILE + +//:End Ignore + +//:>******************************************************************************************** +//:> Forward declarations +//:>******************************************************************************************** + +//:>******************************************************************************************** +//:> Local Constants and static variables +//:>******************************************************************************************** + +namespace gr +{ + +//:>******************************************************************************************** +//:> Methods +//:>******************************************************************************************** + +/*---------------------------------------------------------------------------------------------- + Fill in the FSM by reading from the font stream. + Assumes the stream is in the correct position. +----------------------------------------------------------------------------------------------*/ +bool GrFSM::ReadFromFont(GrIStream & grstrm, int fxdVersion) +{ + short snTmp; + + // number of FSM states + snTmp = grstrm.ReadShortFromFont(); + m_crow = snTmp; + // number of transitional states + snTmp = grstrm.ReadShortFromFont(); + int crowTransitional = snTmp; + // number of success states + snTmp = grstrm.ReadShortFromFont(); + int crowSuccess = snTmp; + m_crowFinal = m_crow - crowTransitional; + m_crowNonAcpt = m_crow - crowSuccess; + m_rowFinalMin = crowTransitional; + // number of columns + snTmp = grstrm.ReadShortFromFont(); + m_ccol = snTmp; + + // Sanity checks. + if (crowTransitional > m_crow || crowSuccess > m_crow) + return false; // bad table + // TODO: add a sanity check for m_ccol. + + // number of FSM glyph ranges and search constants + snTmp = grstrm.ReadShortFromFont(); + m_cmcr = snTmp; + snTmp = grstrm.ReadShortFromFont(); + m_dimcrInit = snTmp; + snTmp = grstrm.ReadShortFromFont(); + m_cLoop = snTmp; + snTmp = grstrm.ReadShortFromFont(); + m_imcrStart = snTmp; + + m_prgmcr = new GrFSMClassRange[m_cmcr]; + for (int imcr = 0; imcr < m_cmcr; imcr++) + { + m_prgmcr[imcr].m_chwFirst = grstrm.ReadUShortFromFont(); + m_prgmcr[imcr].m_chwLast = grstrm.ReadUShortFromFont(); + m_prgmcr[imcr].m_col = grstrm.ReadUShortFromFont(); + } + + // rule map and offsets (extra item at end gives final offset, ie, total) + m_prgirulnMin = new data16[crowSuccess + 1]; + data16 * pchw = m_prgirulnMin; + int i; + for (i = 0; i < (crowSuccess + 1); i++, pchw++) + { + *pchw = grstrm.ReadUShortFromFont(); + } + + // Last offset functions as the total length of the rule list. + // Note that the number of rules in the map is not necessarily equal to the number of + // rules in the pass; some rules may be listed multiply, if they are matched by more + // than one state. + int crulInMap = m_prgirulnMin[crowSuccess]; + + m_prgrulnMatched = new data16[crulInMap]; + pchw = m_prgrulnMatched; + for (i = 0; i < crulInMap; i++, pchw++) + { + *pchw = grstrm.ReadUShortFromFont(); + } + + // min rule pre-context number of items + byte bTmp = grstrm.ReadByteFromFont(); + m_critMinRulePreContext = bTmp; + // max rule pre-context number of items + bTmp = grstrm.ReadByteFromFont(); + m_critMaxRulePreContext = bTmp; + + if (m_critMinRulePreContext > kMaxSlotsPerRule || m_critMaxRulePreContext > kMaxSlotsPerRule) + return false; // bad table + + int cStartStates = m_critMaxRulePreContext - m_critMinRulePreContext + 1; + + // start states + m_prgrowStartStates = new short[cStartStates]; + short * psn = m_prgrowStartStates; + for (int ic = 0; ic < cStartStates; ic++, psn++) + { + *psn = grstrm.ReadShortFromFont(); + } + + return true; +} + +/*---------------------------------------------------------------------------------------------- + Fill in the FSM's state table by reading from the font stream. + Assumes the stream is in the correct position. +----------------------------------------------------------------------------------------------*/ +bool GrFSM::ReadStateTableFromFont(GrIStream & grstrm, int fxdVersion) +{ + int cCells = ((m_crow - m_crowFinal) * m_ccol); + m_prgrowTransitions = new short[cCells]; + short * psn = m_prgrowTransitions; + for (int iCell = 0; iCell < cCells; iCell++, psn++) + { + *psn = grstrm.ReadShortFromFont(); + } + + return true; +} + +/*---------------------------------------------------------------------------------------------- + Use the transition table to find a rule or rules that match; then run the contraint + code to see if the rule is active. Return the rule number of the rule to be applied. + + @param ppass - pass, which knows how to run constraint code + @param psstrmIn - input stream + @param psstrmOut - output stream (from which to get recently output slots) +----------------------------------------------------------------------------------------------*/ +int GrFSM::GetRuleToApply(GrTableManager * ptman, GrPass * ppass, + GrSlotStream * psstrmIn, GrSlotStream * psstrmOut) +{ + // List of accepting states and corresponding count of slots that were + // matched by each. + int prgrowAccepting[kMaxSlotsPerRule]; + int prgcslotMatched[kMaxSlotsPerRule]; + + // Run the transition table until it jams. + int crowAccepting = RunTransitionTable(psstrmIn, psstrmOut, + prgrowAccepting, prgcslotMatched); + + // Nothing matched; fail quickly. + if (crowAccepting == 0) + return -1; + + int * prow = prgrowAccepting + crowAccepting - 1; + int * pcslot = prgcslotMatched + crowAccepting - 1; // # of slots matched AFTER the curr pos + + // Quick test for common case of exactly one rule matched. + if (crowAccepting == 1 && + (m_prgirulnMin[*prow - m_crowNonAcpt + 1] - m_prgirulnMin[*prow - m_crowNonAcpt]) == 1) + { + int iruln = m_prgirulnMin[*prow - m_crowNonAcpt]; + int ruln = m_prgrulnMatched[iruln]; + if (RunConstraintAndRecordLog(ptman, ppass, ruln, psstrmIn, psstrmOut, + ppass->PreModContextForRule(ruln), *pcslot)) + { + return ruln; + } + else + return -1; + } + + // Several rules were matched. We need to sort them and then run the constraints + // for each rule, accepting the first one whose constraints succeed. + + // Count up the number of rules that matched and allocate a buffer of the right size. + int crulMatched = 0; + while (prow >= prgrowAccepting) + { + crulMatched += + m_prgirulnMin[*prow - m_crowNonAcpt + 1] - m_prgirulnMin[*prow - m_crowNonAcpt]; + --prow; + } + MatchedRule * prgmr = new MatchedRule[crulMatched]; + + prow = prgrowAccepting + crowAccepting - 1; + + // Do a dumb insertion sort, ordering primarily by sort key (largest first) + // and secondarily by the order in the original file (rule number). + // Review: is this kind of sort fast enough? We assuming that we usually only + // have a handful of matched rules. + int cmr = 0; + while (prow >= prgrowAccepting) + { + int iruln = m_prgirulnMin[*prow - m_crowNonAcpt]; + // Get all the rules matches by a single state. + while (iruln < m_prgirulnMin[*prow - m_crowNonAcpt + 1]) + { + int ruln = m_prgrulnMatched[iruln]; + int nSortKey = ppass->SortKeyForRule(ruln); + int cslot = *pcslot; + int imr; + for (imr = 0; imr < cmr; imr++) + { + if (nSortKey > prgmr[imr].nSortKey || + (nSortKey == prgmr[imr].nSortKey && ruln < prgmr[imr].ruln)) + { + // Insert here. + memmove(prgmr+imr+1, prgmr+imr, (isizeof(MatchedRule) * (cmr - imr))); + prgmr[imr].ruln = ruln; + prgmr[imr].nSortKey = nSortKey; + prgmr[imr].cslot = cslot; + break; + } + } + if (imr >= cmr) + { + prgmr[cmr].ruln = ruln; + prgmr[cmr].nSortKey = nSortKey; + prgmr[cmr].cslot = cslot; + } + cmr++; + + iruln++; + } + --prow; + --pcslot; + } + + + for (int imr = 0; imr < cmr; imr++) + { + int ruln = prgmr[imr].ruln; + if (RunConstraintAndRecordLog(ptman, ppass, ruln, psstrmIn, psstrmOut, + ppass->PreModContextForRule(ruln), prgmr[imr].cslot)) + { + // Success! + delete[] prgmr; + return ruln; + } + } + + // No rule matched or the constraints failed. + delete[] prgmr; + return -1; +} + +/*---------------------------------------------------------------------------------------------- + Run the transition table, generating a list of rows corresponding to accepting states. + + @param psstrmIn - input stream + @param prgrowAccepting - array to fill in with accepting rows + @param prgcslotMatched - corresponding array with number of slots matched +----------------------------------------------------------------------------------------------*/ +int GrFSM::RunTransitionTable(GrSlotStream * psstrmIn, GrSlotStream * psstrmOut, + int * prgrowAccepting, int * prgcslotMatched) +{ + // Remember we're reading the pre-context items from the output stream, not the input. + int cslotPreContextAvail = psstrmOut->WritePos(); + + if (cslotPreContextAvail < m_critMinRulePreContext) + return 0; // not enough input to match any rule + + // Look backwards by the number of pre-context items for this pass, but not earlier + // than the beginning of the stream. + int cslot = -1 * min(cslotPreContextAvail, m_critMaxRulePreContext); + + // Normally the start state is state 0. But if we are near the beginning of the stream, + // we may need to adjust due to the fact that we don't have enough input yet to + // match the pre-context items in all the rules. So in effect we skip some states + // corresponding to the earlier items that aren't present. + int row = m_prgrowStartStates[max(0, m_critMaxRulePreContext - cslotPreContextAvail)]; + + int * prowTop = prgrowAccepting; + int * pcslotTop = prgcslotMatched; + while (true) + { + if (row >= m_rowFinalMin) + // final state--jammed + return (prowTop - prgrowAccepting); + + if (psstrmIn->SlotsPendingInContext() <= cslot) + // no more input + return (prowTop - prgrowAccepting); + + // Get next glyph. + gid16 chwGlyphID = (cslot < 0) ? + psstrmOut->PeekBack(cslot)->GlyphID() : + psstrmIn->Peek(cslot)->GlyphID(); + + int col = FindColumn(chwGlyphID); + int rowNext; + if (col < 0) + rowNext = 0; + else + rowNext = m_prgrowTransitions[(row * m_ccol) + col]; + + if (rowNext == 0) // jammed + return (prowTop - prgrowAccepting); + + cslot++; + if (rowNext >= m_crowNonAcpt) // or (rowNext < 0) + { + // Accepting state--push it on the stack. + //rowNext *= -1; + gAssert((prowTop - prgrowAccepting) < kMaxSlotsPerRule); + *prowTop++ = rowNext; + *pcslotTop++ = cslot; + } + row = rowNext; + + gAssert((prowTop - prgrowAccepting) == (pcslotTop - prgcslotMatched)); + } +} + +/*---------------------------------------------------------------------------------------------- + Run the transition table, generating a list of rows corresponding to accepting states. + Optimized to avoid matrix arithmetic for non-accepting states. + + Review: do we need to use this version? + + @param psstrmIn - input stream + @param prgrowAccepting - array to fill in with accepting rows + @param prgcslotMatched - corresponding array with number of slots matched +----------------------------------------------------------------------------------------------*/ +#if 0 + +int GrFSM::RunTransitionTableOptimized(GrSlotStream * psstrmIn, GrSlotStream * psstrmOut, + int * prgrowAccepting, int * prgcslotMatched) +{ + int cslot = 0; + int ichwRowOffset = 0; + int ichwRowOffsetLim = m_crowFinal * m_ccol; // number of cells + int * prowTop = prgrowAccepting; + int * pcslotTop = prgcslotMatched; + while (true) + { + if (ichwRowOffset >= ichwRowOffsetLim) + // final state--jammed + return (prowTop - prgrowAccepting); + + if (psstrmIn->SlotsPendingInContext() < cslot) + // no more input + return (prowTop - prgrowAccepting); + + gid16 chwGlyphID = psstrmIn->Peek(cslot)->GlyphID(); + int col = FindColumn(chwGlyphID); + short cellValue = *(m_prgprgrowXitions + ichwRowOffset + col); + + if (cellValue == 0) // jammed + return (prowTop - prgrowAccepting); + + cslot++; + if (cellValue < 0) + { + // Accepting state--push it on the stack. + int rowNext = cellValue * -1; + gAssert((prowTop - prgrowAccepting) < kMaxSlotsPerRule); + *prowTop++ = rowNext; + *pcslotTop++ = cslot; + ichwRowOffset = rowNext * m_ccol; + } + else + ichwRowOffset = cellValue; + + gAssert((prowTop - prgrowAccepting) == (pcslotTop - prgcslotMatched)); + } +} + +#endif // 0 + + +/*---------------------------------------------------------------------------------------------- + Run all the constraints for the rules matched by the state that we have reached. + The rules are ordered as they were in the GDL file. When we hit a constraint that + matches, return the corresponding rule number. Return -1 if no rule passed. + + @param ppass - pass, which knows how to actually run the constraints + @param psstrmIn - input stream + @param cslotMatched - number of slots matched; constraints must be run for each of these +----------------------------------------------------------------------------------------------*/ +//:Ignore +int GrFSM::RunConstraints_Obsolete(GrTableManager * ptman, GrPass * ppass, int row, + GrSlotStream * psstrmIn, int cslotMatched) +{ + int iruln = m_prgirulnMin[row - m_crowNonAcpt]; + while (iruln < m_prgirulnMin[row - m_crowNonAcpt + 1]) + { + int ruln = m_prgrulnMatched[iruln]; + bool fSuccess = ppass->RunConstraint(ptman, ruln, psstrmIn, NULL, 0, cslotMatched); + if (fSuccess) + { + if (ptman->LoggingTransduction()) + ppass->RecordRuleFired(psstrmIn->ReadPosForNextGet(), ruln); + return ruln; + } + else + { + if (ptman->LoggingTransduction()) + ppass->RecordRuleFailed(psstrmIn->ReadPosForNextGet(), ruln); + } + + iruln++; + } + return -1; // no rule succeeded +} +//:End Ignore + +/*---------------------------------------------------------------------------------------------- + Run the constraints for the given rule, and record whether it failed or succeeded in the + transduction log. + + @param ppass - pass, which knows how to actually run the constraints + @param psstrmIn - input stream + @param cslotPreModContext - number of slots before the current position that must be tested + @param islotLimMatched - number of slots matched AFTER the current position + + @return True if the constraints succeeded. +----------------------------------------------------------------------------------------------*/ +bool GrFSM::RunConstraintAndRecordLog(GrTableManager * ptman, GrPass * ppass, int ruln, + GrSlotStream * psstrmIn, GrSlotStream * psstrmOut, + int cslotPreModContext, int islotLimMatched) +{ + bool fSuccess = ppass->RunConstraint(ptman, ruln, psstrmIn, psstrmOut, + cslotPreModContext, islotLimMatched); + + if (ptman->LoggingTransduction()) + { + if (fSuccess) + ppass->RecordRuleFired(psstrmIn->ReadPosForNextGet(), ruln); + else + ppass->RecordRuleFailed(psstrmIn->ReadPosForNextGet(), ruln); + } + + return fSuccess; +} + +/*---------------------------------------------------------------------------------------------- + Search for the class range containing the glyph ID, and return the matching FSM column. + Does a fast binary search that uses only shifts, no division. +----------------------------------------------------------------------------------------------*/ +int GrFSM::FindColumn(gid16 chwGlyphID) +{ + #ifdef _DEBUG + // Check that the pre-calculated constants are correct. + int nPowerOf2 = 1; + while (nPowerOf2 <= m_cmcr) + nPowerOf2 <<= 1; + nPowerOf2 >>= 1; + // Now nPowerOf2 is the max power of 2 <= m_cclsrg. + // m_cLoop is not needed for our purposes, but it is included because it is part of + // the TrueType standard. + gAssert(1 << m_cLoop == nPowerOf2); // m_cloop == log2(nPowerOf2) + gAssert(m_dimcrInit == nPowerOf2); + gAssert(m_imcrStart == m_cmcr - m_dimcrInit); + #endif + + int dimcrCurr = m_dimcrInit; + + GrFSMClassRange * pmcrCurr = m_prgmcr + m_imcrStart; + while (dimcrCurr > 0) + { + int nTest; + if (pmcrCurr < m_prgmcr) + nTest = -1; + else + { + nTest = pmcrCurr->m_chwFirst - chwGlyphID; + if (nTest < 0) + nTest = (chwGlyphID <= pmcrCurr->m_chwLast) ? 0 : nTest; + } + + if (nTest == 0) + // found it + return pmcrCurr->m_col; + + dimcrCurr >>= 1; // divide by 2 + if (nTest < 0) + pmcrCurr += dimcrCurr; + else // (nTest > 0) + pmcrCurr -= dimcrCurr; + } + + return -1; +} + +} // namespace gr diff --git a/Build/source/libs/graphite-engine/src/segment/GrFSM.h b/Build/source/libs/graphite-engine/src/segment/GrFSM.h new file mode 100644 index 00000000000..483d7bc2be8 --- /dev/null +++ b/Build/source/libs/graphite-engine/src/segment/GrFSM.h @@ -0,0 +1,242 @@ +/*--------------------------------------------------------------------*//*:Ignore this sentence. +Copyright (C) 1999, 2001 SIL International. All rights reserved. + +Distributable under the terms of either the Common Public License or the +GNU Lesser General Public License, as specified in the LICENSING.txt file. + +File: GrFSM.h +Responsibility: Sharon Correll +Last reviewed: Not yet. + +Description: + The GrFSM class, which is the mechanism that examines input in a glyph stream and + determines which rule matches and should be run. +----------------------------------------------------------------------------------------------*/ +#ifdef _MSC_VER +#pragma once +#endif +#ifndef FSM_INCLUDED +#define FSM_INCLUDED + +//:End Ignore + +namespace gr +{ + +class GrPass; + +/*---------------------------------------------------------------------------------------------- + A machine class range consists of a range of contiguous glyph IDs that map to + a single column in the FSM (a machine class). (Note that there might be more than one + range mapping to a given column.) + + Keep in mind that these "classes" are completely different from the classes that + are used for substitution. These are used only for matching by the FSM. + + Hungarian: mcr +----------------------------------------------------------------------------------------------*/ +class GrFSMClassRange +{ + friend class GrFSM; + +protected: + gid16 m_chwFirst; // first glyph ID in range + gid16 m_chwLast; // last glyph ID in range + data16 m_col; // column (machine class) to which this range maps +}; + +/*---------------------------------------------------------------------------------------------- + The finite state machine that is used to match sequences of glyphs and + determine which rule to apply. There is one FSM per pass. + + The states in the FSM are grouped and ordered in the following way: + - non-accepting states (no rule completely matched) + - accepting non-final states (a rule matched, but a longer rule is possible) + - final states (a rule matched and no longer rule is possible) + In other words, we have: + - transition, non-accepting + - transition, accepting + - non-transition, accepting + The transition states have information about the next state to transition to; + the accepting states have information about the rule(s) that matched. + + There are three main data structures that are part of the finite state machine: + + (1) the transition matrix: m_prgprgrowXitions. It contains a row for each transition + state (non-accepting states plus accepting non-final states), and a column for each + machine class. The cell values indicate the next state to transition to for the + matched input. A positive number indicates that the next state is a non-accepting state; + a negative value indicates an accepting state. Zero means there is no next state; + no more matches are possible; machine has "jammed." + + (2) the matched-rule list: m_prgrulnMatched. This is a list of rule numbers, + which are indices into the pass's action- and constraint-code arrays. + It contains the numbers of the rules matched by the first accepting state, + followed by those matched by the second accepting state, etc. For each state, + the rules must be ordered using the same order of the rules in the RDL file. + + (3) the matched-rule-offsets list: m_prgirulnMin. This gives the starting index into + the matched-rule list for each accepting state. (Non-accepting states are not included, + so the first item is for state m_crowNonAcpt.) + + In addition, the FSM interacts with the action- and constraint-code lists in the pass + itself. These lists are indexed by rule number (the values of m_prgrulnMatched). + When some input matches the rule, the constraint-code is run; if it succeeds, + the rule number is returned to the caller and that rule is applied (ie, the action-code + is run). + + Hungarian: fsm + + Other hungarian: + row - row (state) + col - column (machine class) + ruln - rule number + + REVIEW: This is quite a time-critical class, and there are two potential optimizations + that have been proposed for the value of a cell: + + (1) use a negative cell number to indicate an accepting state and a positive number + to indicate a non-accepting state. So this gives a comparison with zero rather than + some arbitrary number + + (2) have the positive numbers--the most common case--be the actual byte offset into the + table rather than the row number. This saves matrix multiplication at each step, + ie, "m_prgsnTransitions[(row * m_ccol) + col]". + + The two versions are implemented in RunTransitionTable and RunTransitionTableOptimized. + Do these seem to be worth the inconvenience of added complexity in understanding + and debugging? +----------------------------------------------------------------------------------------------*/ +class GrFSM { + +public: + GrFSM() : + m_prgirulnMin(NULL), + m_prgrulnMatched(NULL), + m_prgrowTransitions(NULL), + m_prgibStateDebug(NULL), + m_prgmcr(NULL), + m_prgrowStartStates(NULL) + { + } + + ~GrFSM() + { + delete[] m_prgirulnMin; + delete[] m_prgrulnMatched; + + delete[] m_prgrowTransitions; + + delete[] m_prgibStateDebug; + + delete[] m_prgmcr; + + delete[] m_prgrowStartStates; + } + + bool ReadFromFont(GrIStream & grstrm, int fxdVersion); + bool ReadStateTableFromFont(GrIStream & grstrm, int fxdVersion); + + int GetRuleToApply(GrTableManager *, GrPass * ppass, + GrSlotStream * psstrmIn, GrSlotStream * psstrmOut); + + int RunTransitionTable(GrSlotStream * psstrmIn, GrSlotStream * psstrmOut, + int * prgrowAccepting, int * prgcslotMatched); + int RunTransitionTableOptimized(GrSlotStream * psstrmIn, GrSlotStream * psstrmOut, + int * prgrowAccepting, int * prgcslotMatched); + bool RunConstraintAndRecordLog(GrTableManager *, GrPass * ppass, int ruln, + GrSlotStream * psstrmIn, GrSlotStream * psstrmOut, + int cslotPreModContext, int cslotMatched); + + int RunConstraints_Obsolete(GrTableManager *, GrPass * ppass, int row, GrSlotStream * psstrmIn, + int cslotMatched); + + // For sorting matched rules + struct MatchedRule // mr + { + int ruln; + int nSortKey; + int cslot; // number of slots matched AFTER the current stream position + }; + + int MaxRulePreContext() + { + return m_critMaxRulePreContext; + } + +protected: + int FindColumn(gid16 chwGlyphID); +protected: + // Instance variables: + int m_crow; // number of rows (states) + int m_crowFinal; // number of final states; no transitions for these + int m_rowFinalMin; // index of first final row + int m_crowNonAcpt; // number of non-accepting states; no rule indices for these + + int m_ccol; // number of columns (machine classes) + + data16 * m_prgirulnMin; // m_crow-m_crowNonAcpt+1 of these; + // index within m_prgrulnMatched, start of matched + // rules for each accepting state + + data16 * m_prgrulnMatched; // long ordered list of rule indices matched by + // subsequent states; total length is sum of number + // of rules matched for each accepting state + + // Transition matrix--for optimized version: +// short ** m_prgprgrowXitions; // ((m_crow-m_crowFinal) * m_ccol) of these; + // positive number indicates + // next state is non-accepting; negative number is + // negative of accepting state. + + // Transition matrix--for current version: + short * m_prgrowTransitions; // ((m_crow-m_crowFinal) * m_ccol) of these + + // debugger string offsets + data16 * m_prgibStateDebug; // for transition states; (m_crow-m_crul+1) of these + + // constants for fast binary search; these are generated by the compiler so that the + // engine doesn't have to take time to do it + data16 m_dimcrInit; // (max power of 2 <= m_cmcr); + // size of initial range to consider + data16 m_cLoop; // log2(max power of 2 <= m_cmcr); + // indicates how many iterations are necessary + data16 m_imcrStart; // m_cmcr - m_dimcrInit; + // where to start search + + int m_cmcr; // number of machine-class-ranges + GrFSMClassRange * m_prgmcr; // array of class ranges; we search these to find the + // mapping to the machine-class-column + + // minimum and maximum number of items in the rule contexts before the first modified + // item. + int m_critMinRulePreContext; + int m_critMaxRulePreContext; + + // start states--row in the FSM to start on depending on how many bogus slots we + // are skipping; (max rule-precontext - min rule-precontext + 1) of these; + // first always = zero + short * m_prgrowStartStates; + +//:Ignore +#if OLD_TEST_STUFF +public: + // For test procedures: + void SetUpSimpleFSMTest(); + void SetUpRuleActionTest(); + void SetUpRuleAction2Test(int); + void SetUpAssocTest(int); + void SetUpAssoc2Test(int); + void SetUpDefaultAssocTest(); + void SetUpFeatureTest(); + void SetUpLigatureTest(int); + void SetUpLigature2Test(int); +#endif // OLD_TEST_STUFF +//:End Ignore + +}; + +} // namespace gr + + +#endif // !FSM_INCLUDED diff --git a/Build/source/libs/graphite-engine/src/segment/GrFeature.cpp b/Build/source/libs/graphite-engine/src/segment/GrFeature.cpp new file mode 100644 index 00000000000..3e488462a74 --- /dev/null +++ b/Build/source/libs/graphite-engine/src/segment/GrFeature.cpp @@ -0,0 +1,319 @@ +/*--------------------------------------------------------------------*//*:Ignore this sentence. +Copyright (C) 1999, 2001 SIL International. All rights reserved. + +Distributable under the terms of either the Common Public License or the +GNU Lesser General Public License, as specified in the LICENSING.txt file. + +File: GrFeature.cpp +Responsibility: Sharon Correll +Last reviewed: Not yet. + +Description: + Implements the GrFeature class. +-------------------------------------------------------------------------------*//*:End Ignore*/ + +//:>******************************************************************************************** +//:> Include files +//:>******************************************************************************************** +#include "Main.h" + +#ifdef _MSC_VER +#pragma hdrstop +#endif +#undef THIS_FILE +DEFINE_THIS_FILE + +//:>******************************************************************************************** +//:> Forward declarations +//:>******************************************************************************************** + +//:>******************************************************************************************** +//:> Local Constants and static variables +//:>******************************************************************************************** + +namespace gr +{ + +//:>******************************************************************************************** +//:> GrFeature Methods +//:>******************************************************************************************** + +/*---------------------------------------------------------------------------------------------- + Initialize the feature. +----------------------------------------------------------------------------------------------*/ +void GrFeature::Initialize(featid nID, int nNameId, int cfset, int nDefault) +{ + m_nID = nID; + m_nNameId = nNameId; + m_nDefault = nDefault; + + m_vnVal.resize(cfset); + for (unsigned int ifset = 0; ifset < m_vnVal.size(); ifset++) + m_vnVal[ifset] = INT_MAX; + m_vnNameId.resize(cfset); +} + +/*---------------------------------------------------------------------------------------------- + Return the feature settings. +----------------------------------------------------------------------------------------------*/ +int GrFeature::Settings(int cMax, int * prgnVal) +{ + int cRet = min(cMax, signed(m_vnVal.size())); + for (int ifset = 0; ifset < cRet; ifset++) + prgnVal[ifset] = m_vnVal[ifset]; + return cRet; +} + +/*---------------------------------------------------------------------------------------------- + Add a feature setting. +----------------------------------------------------------------------------------------------*/ +void GrFeature::AddSetting(int nVal, int nNameId) +{ + Assert(nVal != INT_MAX); + unsigned int ifset; + for (ifset = 0; ifset < m_vnVal.size(); ifset++) + { + if (m_vnVal[ifset] == nVal) + return; // already there + } + for (ifset = 0; ifset < m_vnVal.size(); ifset++) + { + if (m_vnVal[ifset] == INT_MAX) + { + m_vnVal[ifset] = nVal; + m_vnNameId[ifset] = nNameId; + return; + } + } + m_vnVal.push_back(nVal); + m_vnNameId.push_back(nNameId); +} + +/*---------------------------------------------------------------------------------------------- + Return true if the given setting is valid according to the font's feature table. +----------------------------------------------------------------------------------------------*/ +bool GrFeature::IsValidSetting(int nVal) +{ + for (unsigned int ifset = 0; ifset < m_vnVal.size(); ifset++) + { + if (m_vnVal[ifset] == nVal) + return true; + } + return false; +} + +/*---------------------------------------------------------------------------------------------- + Read the feature label from the name table. +----------------------------------------------------------------------------------------------*/ +std::wstring GrFeature::Label(GrEngine * pgreng, int nLang) +{ + std::wstring stu = pgreng->StringFromNameTable(nLang, m_nNameId); + if (stu == L"NoName") + stu.erase(); + return stu; +} + +/*---------------------------------------------------------------------------------------------- + Read the feature value label from the name table. +----------------------------------------------------------------------------------------------*/ +std::wstring GrFeature::SettingLabel(GrEngine * pgreng, int nVal, int nLang) +{ + for (unsigned int ifset = 0; ifset < m_vnVal.size(); ifset++) + { + if (m_vnVal[ifset] == nVal) + { + std::wstring stu = pgreng->StringFromNameTable(nLang, m_vnNameId[ifset]); + if (stu == L"NoName") + stu.erase(); + return stu; + } + } + Assert(false); // setting is not valid + return L""; +} + +/*---------------------------------------------------------------------------------------------- + Return the given setting value. +----------------------------------------------------------------------------------------------*/ +int GrFeature::NthSetting(int ifset) +{ + if (ifset >= (int)m_vnVal.size()) + return -1; + else + return m_vnVal[ifset]; +} + +/*---------------------------------------------------------------------------------------------- + Read the feature value label for the given index. +----------------------------------------------------------------------------------------------*/ +std::wstring GrFeature::NthSettingLabel(GrEngine * pgreng, int ifset, int nLang) +{ + std::wstring stu; + + if (ifset >= (int)m_vnVal.size()) + stu.erase(); + else + { + stu = pgreng->StringFromNameTable(nLang, m_vnNameId[ifset]); + if (stu == L"NoName") + stu.erase(); + } + return stu; +} + +/*---------------------------------------------------------------------------------------------- + Return the feature setting label. +----------------------------------------------------------------------------------------------*/ +// void GrFeature::SetSettingLabel(int nVal, std::wstring stuLabel, int nLang) +// { +// for (int ifset = 0; ifset < m_vfset.size(); ifset++) +// { +// if (m_vfset[ifset].m_nVal == nVal) +// { +// m_vfset[ifset].m_hmnstuLabels.Insert(nLang, stuLabel, true); +// return; +// } +// } +// Assert(false); // setting is not valid +// } + + +//:>******************************************************************************************** +//:> GrLangTable Methods +//:>******************************************************************************************** + +/*---------------------------------------------------------------------------------------------- + Read the languages from the font. +----------------------------------------------------------------------------------------------*/ +bool GrLangTable::ReadFromFont(GrIStream * pgrstrm, int fxdVersion) +{ + GrIStream & grstrm = *pgrstrm; + + // number of languages + m_clang = (size_t)grstrm.ReadUShortFromFont(); + + // search constants + m_dilangInit = grstrm.ReadUShortFromFont(); + m_cLoop = grstrm.ReadUShortFromFont(); + m_ilangStart = grstrm.ReadUShortFromFont(); + + // Slurp the data into the buffers. The "+1" represents a bogus ending entry that quickly + // gives us the size of the feature list. Note that the resulting data is all big-endian. + int cb = (m_clang + 1) * sizeof(LangEntry); + m_prglang = new LangEntry[m_clang + 1]; + grstrm.ReadBlockFromFont(m_prglang, cb); + + m_cbOffset0 = (lsbf)(m_prglang[0].cbOffsetBIG); + + Assert((lsbf)(m_prglang[m_clang].cFeaturesBIG) == 0); // bogus entry has no settings + cb = (lsbf)(m_prglang[m_clang].cbOffsetBIG) - m_cbOffset0; + Assert(cb % sizeof(FeatSet) == 0); // # of bytes fits nicely into FeatSet class + int cfset = cb / sizeof(FeatSet); + m_prgfset = new FeatSet[cfset]; + grstrm.ReadBlockFromFont(m_prgfset, cb); + + return true; +} + +/*---------------------------------------------------------------------------------------------- + Create an empty language table to use for dumb rendering, or when there is no valid + Sill table. +----------------------------------------------------------------------------------------------*/ +void GrLangTable::CreateEmpty() +{ + m_prglang = NULL; + m_prgfset = NULL; + m_clang = 0; + m_dilangInit = 0; + m_cLoop = 0; + m_ilangStart = 0; +} + +/*---------------------------------------------------------------------------------------------- + Return information about the language with the given ID by filling in the given vectors + with the feature ids and values. Will wipe out any data in the vectors already. +----------------------------------------------------------------------------------------------*/ +void GrLangTable::LanguageFeatureSettings(isocode lgcode, + std::vector<featid> & vnFeatId, std::vector<int> & vnValues) +{ + vnFeatId.clear(); + vnValues.clear(); + + int ilang = FindIndex(lgcode); + if (ilang == -1) + return; // no such language: leave vectors empty + + LangEntry * plang = m_prglang + ilang; + int cbOffset = lsbf(plang->cbOffsetBIG) - m_cbOffset0; + Assert(cbOffset % sizeof(FeatSet) == 0); + byte * pbFeatSet = reinterpret_cast<byte*>(m_prgfset) + cbOffset; + FeatSet * pfset = reinterpret_cast<FeatSet*>(pbFeatSet); + for (int ifset = 0; ifset < lsbf(plang->cFeaturesBIG); ifset++) + { + vnFeatId.push_back(lsbf(pfset[ifset].featidBIG)); + vnValues.push_back(lsbf(pfset[ifset].valueBIG)); + } +} + +/*---------------------------------------------------------------------------------------------- + Return the code for the language at the given index. +----------------------------------------------------------------------------------------------*/ +isocode GrLangTable::LanguageCode(size_t ilang) +{ + isocode lgcodeRet; + if (ilang > m_clang) + { + std::fill_n(lgcodeRet.rgch, 4, '\0'); + return lgcodeRet; + } + LangEntry * plang = m_prglang + ilang; + std::copy(plang->rgchCode, plang->rgchCode + 4, lgcodeRet.rgch); + return lgcodeRet; +} + +/*---------------------------------------------------------------------------------------------- + Return the index of the language in the list. +----------------------------------------------------------------------------------------------*/ +int GrLangTable::FindIndex(isocode lgcode) +{ + if (m_clang == 0) + return -1; +#ifdef _DEBUG + int nPowerOf2 = 1; + while (nPowerOf2 <= signed(m_clang)) + nPowerOf2 <<= 1; + nPowerOf2 >>= 1; + // Now nPowerOf2 is the max power of 2 <= m_clang + gAssert((1 << m_cLoop) == nPowerOf2); // m_cLoop == log2(nPowerOf2) + gAssert(m_dilangInit == nPowerOf2); + gAssert(m_ilangStart == m_clang - m_dilangInit); +#endif // _DEBUG + + int dilangCurr = m_dilangInit; + + int ilangCurr = m_ilangStart; // may become < 0 + while (dilangCurr > 0) + { + int nTest; + if (ilangCurr < 0) + nTest = -1; + else + { + LangEntry * plangCurr = m_prglang + ilangCurr; + nTest = strcmp(plangCurr->rgchCode, lgcode.rgch); + } + + if (nTest == 0) + return ilangCurr; + + dilangCurr >>= 1; // divide by 2 + if (nTest < 0) + ilangCurr += dilangCurr; + else // (nTest > 0) + ilangCurr -= dilangCurr; + } + + return -1; +} + +} // namespace gr diff --git a/Build/source/libs/graphite-engine/src/segment/GrFeatureValues.h b/Build/source/libs/graphite-engine/src/segment/GrFeatureValues.h new file mode 100644 index 00000000000..97e6a1fe33c --- /dev/null +++ b/Build/source/libs/graphite-engine/src/segment/GrFeatureValues.h @@ -0,0 +1,66 @@ +/*--------------------------------------------------------------------*//*:Ignore this sentence. +Copyright (C) 1999, 2001 SIL International. All rights reserved. + +Distributable under the terms of either the Common Public License or the +GNU Lesser General Public License, as specified in the LICENSING.txt file. + +File: GrFeatureValues.h +Responsibility: Sharon Correll +Last reviewed: Not yet. + +Description: + + +----------------------------------------------------------------------------------------------*/ +#ifdef _MSC_VER +#pragma once +#endif +#ifndef GR_FEATVAL_INCLUDED +#define GR_FEATVAL_INCLUDED + +//:End Ignore + +namespace gr +{ + +/*---------------------------------------------------------------------------------------------- + A convenient way to group together style and feature information that interacts + with the rules. + + Hungarian: fval +----------------------------------------------------------------------------------------------*/ +class GrFeatureValues +{ + friend class GrCharStream; + friend class GrSlotAbstract; + friend class GrSlotState; + +public: + // Standard constructor: + GrFeatureValues() + { + m_nStyleIndex = 0; + std::fill(m_rgnFValues, m_rgnFValues + kMaxFeatures, 0); + } + + // Copy constructor: + GrFeatureValues(const GrFeatureValues & fval) + { + m_nStyleIndex = fval.m_nStyleIndex; + std::copy(fval.m_rgnFValues, fval.m_rgnFValues + kMaxFeatures, m_rgnFValues); + } + + // For transduction logging: +#ifdef TRACING + void WriteXductnLog(GrTableManager * ptman, std::ostream &); +#endif // TRACING + +protected: + int m_nStyleIndex; + int m_rgnFValues[kMaxFeatures]; +}; + +} // namespace gr + + +#endif // !GR_FEATVAL_INCLUDED diff --git a/Build/source/libs/graphite-engine/src/segment/GrGlyphTable.cpp b/Build/source/libs/graphite-engine/src/segment/GrGlyphTable.cpp new file mode 100644 index 00000000000..8643eae8cb8 --- /dev/null +++ b/Build/source/libs/graphite-engine/src/segment/GrGlyphTable.cpp @@ -0,0 +1,650 @@ +/*--------------------------------------------------------------------*//*:Ignore this sentence. +Copyright (C) 1999, 2001 SIL International. All rights reserved. + +Distributable under the terms of either the Common Public License or the +GNU Lesser General Public License, as specified in the LICENSING.txt file. + +File: GrGlyphTable.cpp +Responsibility: Sharon Correll +Last reviewed: 10Aug99, JT, quick look over + +Description: + Implements the GrGlyphTable class and related classes. +----------------------------------------------------------------------------------------------*/ + +//:>******************************************************************************************** +//:> Include files +//:>******************************************************************************************** +#include "Main.h" + +#ifdef _MSC_VER +#pragma hdrstop +#endif +#undef THIS_FILE +DEFINE_THIS_FILE + +//:End Ignore + +//:>******************************************************************************************** +//:> Forward declarations +//:>******************************************************************************************** + +//:>******************************************************************************************** +//:> Local Constants and static variables +//:>******************************************************************************************** + +namespace gr +{ + +//:>******************************************************************************************** +//:> Methods +//:>******************************************************************************************** + +/*---------------------------------------------------------------------------------------------- + Fill in the glyph table from the font stream. +----------------------------------------------------------------------------------------------*/ +bool GrGlyphTable::ReadFromFont(GrIStream & grstrmGloc, long lGlocStart, + GrIStream & grstrmGlat, long lGlatStart, + data16 chwBWAttr, data16 chwJStrAttr, int cJLevels, int cnCompPerLig, + int fxdSilfVersion) +{ + // Create the glyph sub-table for the single style. + GrGlyphSubTable * pgstbl = new GrGlyphSubTable(); + + // Gloc table--offsets into Glat table + grstrmGloc.SetPositionInFont(lGlocStart); + + // version + int fxdGlocVersion = GrEngine::ReadVersion(grstrmGloc); + if (fxdGlocVersion > kGlocVersion) + return false; + + // flags + short snFlags = grstrmGloc.ReadShortFromFont(); + + // number of attributes + unsigned short cAttrs = grstrmGloc.ReadUShortFromFont(); + + // Create a single sub-table for the single style. + pgstbl->Initialize(fxdSilfVersion, snFlags, chwBWAttr, chwJStrAttr, data16(chwJStrAttr + cJLevels), + m_cglf, cAttrs, cnCompPerLig); + + SetSubTable(0, pgstbl); + + return pgstbl->ReadFromFont(grstrmGloc, m_cglf, grstrmGlat, lGlatStart); +} + +bool GrGlyphSubTable::ReadFromFont(GrIStream & grstrmGloc, int cGlyphs, + GrIStream & grstrmGlat, long lGlatStart) +{ + // attribute value offsets--slurp + if (m_fGlocShort) + { + grstrmGloc.ReadBlockFromFont(m_prgibBIGAttrValues, ((cGlyphs + 1) * sizeof(data16))); + } + else + { + grstrmGloc.ReadBlockFromFont(m_prgibBIGAttrValues, ((cGlyphs + 1) * sizeof(data32))); + } + + // length of buffer needed for glyph attribute values: + int cbBufLen = GlocLookup(data16(cGlyphs)); + + // TODO: slurp debugger offsets: 'm_prgibBIGGlyphAttrDebug' + + m_pgatbl = new GrGlyphAttrTable(); + m_pgatbl->Initialize(m_fxdSilfVersion, cbBufLen); + + // Glat table + grstrmGlat.SetPositionInFont(lGlatStart); + + // version + int fxdGlatVersion = grstrmGlat.ReadIntFromFont(); + if (fxdGlatVersion > kGlatVersion) + return false; + + // Back up over the version number and include it right in the attribute value entry + // buffer, since the offsets take it into account. + grstrmGlat.SetPositionInFont(lGlatStart); + + // Slurp the entire block of entries. + grstrmGlat.ReadBlockFromFont(m_pgatbl->m_prgbBIGEntries, cbBufLen); + + return true; +} + +/*---------------------------------------------------------------------------------------------- + Create an empty glyph table with no glyph attributes. +----------------------------------------------------------------------------------------------*/ +void GrGlyphTable::CreateEmpty() +{ + // Create the glyph sub-table for the single style. + GrGlyphSubTable * pgstbl = new GrGlyphSubTable(); + + // Create a single sub-table for the single style. + pgstbl->Initialize(0, 0, 0, 0, 0, m_cglf, 0, 0); + + SetSubTable(0, pgstbl); + + pgstbl->CreateEmpty(); +} + +void GrGlyphSubTable::CreateEmpty() +{ + m_pgatbl = new GrGlyphAttrTable(); + m_pgatbl->Initialize(0, 0); +} + +/*---------------------------------------------------------------------------------------------- + Initialization. +----------------------------------------------------------------------------------------------*/ +void GrGlyphSubTable::Initialize(int fxdSilfVersion, utf16 chwFlags, + data16 chwBWAttr, data16 chwJStrAttr, data16 chwJStrHWAttr, + int cGlyphs, int cAttrs, int cnCompPerLig) +{ + m_fxdSilfVersion = fxdSilfVersion; + m_fHasDebugStrings = HasAttrNames(chwFlags); + m_nAttrIDLim = cAttrs; + m_chwBWAttr = chwBWAttr; + m_chwJStrAttr = chwJStrAttr; + m_chwJStrHWAttr = chwJStrHWAttr; + m_fGlocShort = !LongFormat(chwFlags); + + if (m_fGlocShort) + { // item # cGlyphs holds length, which is convenient for dealing with last item + m_prgibBIGAttrValues = new byte[(cGlyphs + 1) * sizeof(data16)]; + } + else + { + m_prgibBIGAttrValues = new byte[(cGlyphs + 1) * sizeof(data32)]; + } + + + if (m_fHasDebugStrings) + { + m_prgibBIGGlyphAttrDebug = new data16[cAttrs + 1]; + } + + // Make a cache to hold a list of defined components for each glyph. This is + // calculated lazily, as needed, so we also include (as the first item) a flag to + // indicate whether or not it has been calculated; hence the "+1". + m_cnCompPerLig = cnCompPerLig; + m_prgnDefinedComponents = new int[(m_cnCompPerLig + 1) * cGlyphs]; + std::fill_n(m_prgnDefinedComponents, (m_cnCompPerLig + 1) * cGlyphs, 0); + + // Now the instance is ready to have the locations and the debug strings + // read from the file. +} + +/*---------------------------------------------------------------------------------------------- + Return the attribute value for the given glyph. +----------------------------------------------------------------------------------------------*/ +int GrGlyphSubTable::GlyphAttrValue(gid16 chwGlyphID, int nAttrID) +{ + if (m_nAttrIDLim == 0) + { + // No attributes defined (eg, invalid font) + return 0; + } + if (nAttrID >= m_nAttrIDLim || nAttrID >= 0xFF) + { + gAssert(false); + return 0; + } + int ibMin = GlocLookup(chwGlyphID); // where this glyph's attrs start + int ibLim = GlocLookup(chwGlyphID + 1); // where next glyph starts + + int nRet = m_pgatbl->GlyphAttr16BitValue(ibMin, ibLim, byte(nAttrID)); + if ((data16)nAttrID == m_chwJStrAttr) + { + // For justify.stretch, which can be a 32-bit value, add on the high word. + unsigned int nRetHW = m_pgatbl->GlyphAttr16BitValue(ibMin, ibLim, byte(m_chwJStrHWAttr)); + nRet = (nRetHW << 16) | nRet; + } + return ConvertValueForVersion(nRet, nAttrID); +} + +/*---------------------------------------------------------------------------------------------- + Convert the glyph attribute value from the version of the font table to what is + expected by the engine. +----------------------------------------------------------------------------------------------*/ +int GrGlyphSubTable::ConvertValueForVersion(int nValue, int nAttrID) +{ + return ConvertValueForVersion(nValue, nAttrID, (int)m_chwBWAttr, m_fxdSilfVersion); +} + +int GrGlyphSubTable::ConvertValueForVersion(int nValue, int nAttrID, int nBWAttr, int fxdVersion) +{ + if ((nBWAttr >= 0 && nAttrID == (int)nBWAttr) || + (nBWAttr == -1 && nAttrID == (int)kslatBreak)) + { + if (fxdVersion < 0x00020000) + { + //switch (nValue) + //{ + //case klbv1WordBreak: return klbWordBreak; + //case klbv1HyphenBreak: return klbHyphenBreak; + //case klbv1LetterBreak: return klbLetterBreak; + //case klbv1ClipBreak: return klbClipBreak; + //default: + // break; + //} + //switch (nValue * -1) + //{ + //case klbv1WordBreak: return (klbWordBreak * -1); + //case klbv1HyphenBreak: return (klbHyphenBreak * -1); + //case klbv1LetterBreak: return (klbLetterBreak * -1); + //case klbv1ClipBreak: return (klbClipBreak * -1); + //default: + // break; + //} + + // Breakweight values in version 2 are 10 times what they were in version 1. + if (abs(nValue) <= 7) + return nValue * 10; + } + } + return nValue; +} + +/*---------------------------------------------------------------------------------------------- + Return the attribute value a glyph. Loop through the attribute runs, looking + for the one containing the given attribute. Assume the value is 0 if not found. + + @param ibMin - byte offset of first run for the glyph of interest + @param ibLim - byte offset of first run for the following glyph + @param bAttrID - attribute to find +----------------------------------------------------------------------------------------------*/ +int GrGlyphAttrTable::GlyphAttr16BitValue(int ibMin, int ibLim, byte bAttrID) +{ + GrGlyphAttrRun gatrun; + byte * pbBIGEnt = m_prgbBIGEntries + ibMin; + + while (pbBIGEnt < m_prgbBIGEntries + ibLim) + { + // Suck the run of attribute values into the instance, so we can see + // what's there. + gatrun.CopyFrom(pbBIGEnt); + + if (bAttrID < gatrun.m_bMinAttrID) + // Attribute not found--assume value is 0. + return 0; + else if (gatrun.m_bMinAttrID + gatrun.m_cAttrs > bAttrID) + { + // Found the value in this run. + data16 chw = lsbf(gatrun.m_rgchwBIGValues[bAttrID - gatrun.m_bMinAttrID]); + if ((chw & 0x8000) == 0) + return (int)chw; + else + return 0xFFFF0000 | chw; // sign extension + } + else + // Go to next run. + pbBIGEnt += gatrun.ByteCount(); + } + + // Attribute not found--assume value is 0; + return 0; +} + +/*---------------------------------------------------------------------------------------------- + Given a point on the glyph, determine which component (if any) the point is in, and return + its index relative to the components that are defined for this glyph; ie, the index within + the compRef array. + Return -1 if the click with not inside any component, or if there are no components + defined for this glyph. + CURRENTLY NOT USED - if ever used, need to incorporate floating point +----------------------------------------------------------------------------------------------*/ +int GrGlyphSubTable::ComponentContainingPoint(gid16 chwGlyphID, int x, int y) +{ + int i = CalculateDefinedComponents(chwGlyphID); + for (int inComp = 0; inComp < m_cnCompPerLig; inComp++) + { + // The m_pnComponents list holds a list of the component attributes that are defined + // for this glyph, padded with -1's. + int nCompID = m_prgnDefinedComponents[i + inComp]; + if (nCompID == -1) + break; // hit the end of the list + + // The value of the component attribute is actually the attribute ID for the + // first of the component box fields--the top. There should a contiguous group of + // four fields: top, bottom, left, right. + int nFieldIDMin = GlyphAttrValue(chwGlyphID, nCompID); + gAssert(nFieldIDMin != 0); + int nTop = GlyphAttrValue(chwGlyphID, nFieldIDMin); + int nBottom = GlyphAttrValue(chwGlyphID, nFieldIDMin + 1); + int nLeft = GlyphAttrValue(chwGlyphID, nFieldIDMin + 2); + int nRight = GlyphAttrValue(chwGlyphID, nFieldIDMin + 3); + + if (nLeft <= x && x < nRight && + nBottom <= y && y < nTop) + { + return inComp; + } + } + return -1; +} + +/*---------------------------------------------------------------------------------------------- + Fill in the box with the source-device coordinates corresponding to the given component. + The rectangle is relative to the top-left of the glyph. + Return false if the component is not defined. + + @param xysEmSquare - of the font, in logical units + @param chwGlyphID - the glyph of interest + @param icomp - the component of interest (eg, for the "oe" ligature, + the index for component.o would be 0, + and the index for component.e would be 1); + these is as defined by the glyph, not as used in the rules + @param mFontEmUnits - the number of em-units in the font, for scaling + @param dysFontAscent - ascent of the font, for adjusting coordinate systems + @param pxsLeft, pxsRight, pysTop, pysBottom + - return values, in source device coordinates + @param fTopOrigin - convert values to a coordinate system where the origin is the + top of the line, not the baseline +----------------------------------------------------------------------------------------------*/ +bool GrGlyphSubTable::ComponentBoxLogUnits(float xysEmSquare, + gid16 chwGlyphID, int icomp, int mFontEmUnits, float dysFontAscent, + float * pxsLeft, float * pysTop, float * pxsRight, float * pysBottom, + bool fTopOrigin) +{ + int i = CalculateDefinedComponents(chwGlyphID); + + // The m_pnComponents list holds a list of the component attributes that are defined + // for this glyph, padded with -1's. + int nCompID = m_prgnDefinedComponents[i + icomp]; + if (nCompID == -1) + { + // Ooops, the component is undefined for this glyph. + *pxsLeft = 0; + *pxsRight = 0; + *pysTop = 0; + *pysBottom = 0; + return false; + } + + // The value of the component attribute is actually the attribute ID for the + // first of the component box fields--the top. There should a contiguous group of + // four fields: top, bottom, left, right. + int nFieldIDMin = GlyphAttrValue(chwGlyphID, nCompID); + if (nFieldIDMin == 0) + { + // Component inadequately defined. This can happen when we defined components + // for a glyph and then substituted another glyph for it. + *pxsLeft = 0; + *pxsRight = 0; + *pysTop = 0; + *pysBottom = 0; + return false; + } + int mTop = GlyphAttrValue(chwGlyphID, nFieldIDMin); + int mBottom = GlyphAttrValue(chwGlyphID, nFieldIDMin + 1); + int mLeft = GlyphAttrValue(chwGlyphID, nFieldIDMin + 2); + int mRight = GlyphAttrValue(chwGlyphID, nFieldIDMin + 3); + + ////int xysFontEmUnits; + ////// ysHeight should correspond to the pixels in the font's em square + ////GrResult res = pgg->GetFontEmSquare(&xysFontEmUnits); + ////if (ResultFailed(res)) + //// return false; + + *pxsLeft = GrEngine::GrIFIMulDiv(mLeft, xysEmSquare, mFontEmUnits); + *pxsRight = GrEngine::GrIFIMulDiv(mRight, xysEmSquare, mFontEmUnits); + *pysTop = GrEngine::GrIFIMulDiv(mTop, xysEmSquare, mFontEmUnits); + *pysBottom = GrEngine::GrIFIMulDiv(mBottom, xysEmSquare, mFontEmUnits); + + if (*pxsLeft > *pxsRight) + { + float xsTmp = *pxsLeft; + *pxsLeft = *pxsRight; + *pxsRight = xsTmp; + } + if (*pysTop < *pysBottom) + { + float ysTmp = *pysTop; + *pysTop = *pysBottom; + *pysBottom = ysTmp; + } + + if (fTopOrigin) + { + // Currently top and bottom coordinates are such that 0 is the baseline and positive is + // upwards, while for the box we want to return, 0 is the top of the glyph and positive + // is downwards. So switch systems. + *pysTop = (dysFontAscent - *pysTop); + *pysBottom = (dysFontAscent - *pysBottom); + } + + return true; +} + +/*---------------------------------------------------------------------------------------------- + Calculate the list of defined components for the given glyph, if any. + Return the index to the first relevant item in the array. + Private. + TODO: With the current implementation that stores the glyph ids in each slot, this + mechanism is redundant and obsolete, and should be removed. +----------------------------------------------------------------------------------------------*/ +int GrGlyphSubTable::CalculateDefinedComponents(gid16 chwGlyphID) +{ + // The first item is a flag indicating whether the list has been calculated. + int iFlag = chwGlyphID * (m_cnCompPerLig + 1); // index of has-been-calculated flag + int iMin = iFlag + 1; // first actual value; +1 in order to skip the flag + if (m_prgnDefinedComponents[iFlag] == 0) + { + int iTmp = iMin; + for (int nCompID = 0; nCompID < m_cComponents; nCompID++) + { + if (ComponentIsDefined(chwGlyphID, nCompID)) + m_prgnDefinedComponents[iTmp++] = nCompID; + if (iTmp - iMin >= m_cnCompPerLig) + break; // ignore any components beyond the maximum allowed + } + // Fill in the rest of the list with -1. + for( ; iTmp < iMin + m_cnCompPerLig; iTmp++) + m_prgnDefinedComponents[iTmp] = -1; + + m_prgnDefinedComponents[iFlag] = 1; // has been calculated + } + return iMin; +} + +/*---------------------------------------------------------------------------------------------- + Return true if the given attr ID, which is a component definition attribute, indicates + that that component is defined for the given glyph. + + @param chwGlyphID - the glyph of interest + @param nAttrID - the attribute of interest, assumed to be a component glyph + attribute (eg. component.e.top) +----------------------------------------------------------------------------------------------*/ +bool GrGlyphSubTable::ComponentIsDefined(gid16 chwGlyphID, int nAttrID) +{ + gAssert(nAttrID < m_cComponents); + if (nAttrID >= m_cComponents) + return false; + + return (GlyphAttrValue(chwGlyphID, nAttrID) != 0); +} + +/*---------------------------------------------------------------------------------------------- + Return the index of the given component as defined for the given glyph ID. that is, + convert the global ID for the component to the one within this glyph. Return -1 + if the component is not defined for the given glyph. + For instance, given the glyph ID "oe" (the oe ligature) and the attr ID for component.o, + the result would be 0; for component.e the result would be 1. +----------------------------------------------------------------------------------------------*/ +int GrGlyphSubTable::ComponentIndexForGlyph(gid16 chwGlyphID, int nCompID) +{ + int i = CalculateDefinedComponents(chwGlyphID); + + for (int iLp = 0; iLp < m_cnCompPerLig; iLp++) + { + if (m_prgnDefinedComponents[i + iLp] == nCompID) + return iLp; + } + return -1; +} + +/*---------------------------------------------------------------------------------------------- + Return the ID of the nth defined component attribute for the given glyph. +----------------------------------------------------------------------------------------------*/ +//int GrGlyphSubTable::NthComponentID(gid16 chwGlyphID, int n) +//{ +// int i = CalculateDefinedComponents(chwGlyphID); +// return m_pnComponents[i + 1 + n]; +//} + + +//:>******************************************************************************************** +//:> For test procedures +//:>******************************************************************************************** + +//:Ignore +#ifdef OLD_TEST_STUFF + +void GrGlyphTable::SetUpTestData() +{ + SetNumberOfGlyphs(5); + SetNumberOfStyles(1); + + GrGlyphSubTable * pgstbl = new GrGlyphSubTable(); + gAssert(pgstbl); + + pgstbl->Initialize(1, 0, 5, 10, 4); + SetSubTable(0, pgstbl); + + pgstbl->SetUpTestData(); +} + +/*********************************************************************************************** + TODO: This method is BROKEN because m_prgibBIGAttrValues has been changed. It is no + longer a utf16 *. The Gloc table can contain 16-bit or 32-bit entries and must be + accessed accordingly. +***********************************************************************************************/ +void GrGlyphSubTable::SetUpTestData() +{ + m_pgatbl = new GrGlyphAttrTable(); + m_pgatbl->Initialize(0, 48); + + m_prgibBIGAttrValues[0] = byte(msbf(data16(0))); + m_prgibBIGAttrValues[1] = byte(msbf(data16(6)))); + m_prgibBIGAttrValues[2] = byte(msbf(data16(20))); + m_prgibBIGAttrValues[3] = byte(msbf(data16(20))); + m_prgibBIGAttrValues[4] = byte(msbf(data16(40))); + m_prgibBIGAttrValues[5] = byte(msbf(data16(48))); + + m_pgatbl->SetUpTestData(); +} + +void GrGlyphAttrTable::SetUpTestData() +{ + // Glyph 0 (1 run; offset = 0): + // 1 = 11 = 0x0B + // 2 = 22 = 0x16 + // 0102 000B 0016 + // + // Glyph 1 (2 runs; offset = 3*2 = 6): + // 0 = 5 + // 1 = 6 + // 2 = 7 + // 8 = 8 + // 9 = 9 + // 0003 0005 0006 0007 + // 0802 0008 0009 + // + // Glyph 2 (0 runs; offset = 6 + 7*2 = 20): + // all values = 0 + // + // Glyph 3 (5 runs; offset = 20): + // 1 = 111 = 0x006F + // 3 = 333 = 0x00DE + // 5 = 555 = 0x022B + // 7 = 777 = 0x0309 + // 9 = 999 = 0x03E7 + // 0101 006F + // 0301 00DE + // 0501 022B + // 0701 0309 + // 0901 03E7 + // + // Glyph 4 (1 run; offset = 20 + 10*2 = 40): + // 5 = 4 + // 6 = 4 + // 7 = 4 + // 0503 0004 0004 0004 + // + // Glyph 5 (offset = 40 + 4*2 = 48) + + byte * pbBIG = m_prgbBIGEntries; + + GrGlyphAttrRun gatrun; + + // Glyph 0 + gatrun.m_bMinAttrID = 1; + gatrun.m_cAttrs = 2; + gatrun.m_rgchwBIGValues[0] = msbf(data16(11)); + gatrun.m_rgchwBIGValues[1] = msbf(data16(22)); + memcpy(pbBIG, &gatrun, 6); + pbBIG += 6; + + // Glyph 1 + gatrun.m_bMinAttrID = 0; + gatrun.m_cAttrs = 3; + gatrun.m_rgchwBIGValues[0] = msbf(data16(5)); + gatrun.m_rgchwBIGValues[1] = msbf(data16(6)); + gatrun.m_rgchwBIGValues[2] = msbf(data16(7)); + memcpy(pbBIG, &gatrun, 8); + pbBIG += 8; + gatrun.m_bMinAttrID = 8; + gatrun.m_cAttrs = 2; + gatrun.m_rgchwBIGValues[0] = msbf(data16(8)); + gatrun.m_rgchwBIGValues[1] = msbf(data16(9)); + memcpy(pbBIG, &gatrun, 6); + pbBIG += 6; + + // Glyph 2: no data + + // Glyph 3 + gatrun.m_bMinAttrID = 1; + gatrun.m_cAttrs = 1; + gatrun.m_rgchwBIGValues[0] = msbf(data16(111)); + memcpy(pbBIG, &gatrun, 4); + pbBIG += 4; + gatrun.m_bMinAttrID = 3; + gatrun.m_cAttrs = 1; + gatrun.m_rgchwBIGValues[0] = msbf(data16(333)); + memcpy(pbBIG, &gatrun, 4); + pbBIG += 4; + gatrun.m_bMinAttrID = 5; + gatrun.m_cAttrs = 1; + gatrun.m_rgchwBIGValues[0] = msbf(data16(555)); + memcpy(pbBIG, &gatrun, 4); + pbBIG += 4; + gatrun.m_bMinAttrID = 7; + gatrun.m_cAttrs = 1; + gatrun.m_rgchwBIGValues[0] = msbf(data16(777)); + memcpy(pbBIG, &gatrun, 4); + pbBIG += 4; + gatrun.m_bMinAttrID = 9; + gatrun.m_cAttrs = 1; + gatrun.m_rgchwBIGValues[0] = msbf(data16(999)); + memcpy(pbBIG, &gatrun, 4); + pbBIG += 4; + + // Glyph 4 + gatrun.m_bMinAttrID = 5; + gatrun.m_cAttrs = 3; + gatrun.m_rgchwBIGValues[0] = msbf(data16(4)); + gatrun.m_rgchwBIGValues[1] = msbf(data16(4)); + gatrun.m_rgchwBIGValues[2] = msbf(data16(4)); + memcpy(pbBIG, &gatrun, 8); + pbBIG += 8; + + gAssert(pbBIG == m_prgbBIGEntries + 48); +} + +#endif // OLD_TEST_STUFF + +} // namespace gr + +//:End Ignore diff --git a/Build/source/libs/graphite-engine/src/segment/GrGlyphTable.h b/Build/source/libs/graphite-engine/src/segment/GrGlyphTable.h new file mode 100644 index 00000000000..b2a558ebfbd --- /dev/null +++ b/Build/source/libs/graphite-engine/src/segment/GrGlyphTable.h @@ -0,0 +1,426 @@ +/*--------------------------------------------------------------------*//*:Ignore this sentence. +Copyright (C) 1999, 2001 SIL International. All rights reserved. + +Distributable under the terms of either the Common Public License or the +GNU Lesser General Public License, as specified in the LICENSING.txt file. + +File: GrGlyphTable.h +Responsibility: Sharon Correll +Last reviewed: 10Aug99, JT, quick look over + +Description: + The GrGlyphTable and related classes that store the values of glyph attributes. + + For each glyph in a TrueType font, Graphite has a list of glyph attributes (which + are defined in the GDL script file). Since most of the values will be zero, we save space + by only storing the non-zero values. + + Eventually it may be possible to have more than one TrueType font compiled into a single + glyph table, hence the need for multiple sub-tables. For now we assume that there is only + one font file and only one sub-table. + + Note that BIG in a variable name means that the data is in big-endian format, or is a range + of bytes that must be interpreted as a run of wide chars or ints in big-endian format. +-------------------------------------------------------------------------------*//*:End Ignore*/ + +//:Ignore +#ifdef _MSC_VER +#pragma once +#endif +#ifndef GR_GTABLE_INCLUDED +#define GR_GTABLE_INCLUDED +//:End Ignore + +namespace gr +{ + +class Font; + +/*---------------------------------------------------------------------------------------------- + A run of non-zero attribute values for a single glyph, where the attribute IDs + are contiguous. + + NOTE: it is assumed that the format of this class matches the byte order of the data as it + is read directly from the TTF/ECF file--that is, it is big-endian. + + Hungarian: gatrun +----------------------------------------------------------------------------------------------*/ + +class GrGlyphAttrRun +{ + friend class GrGlyphTable; + friend class GrGlyphSubTable; + friend class GrGlyphAttrTable; + + enum { + kMaxAttrsPerRun = 255 + }; + + /*------------------------------------------------------------------------------------------ + Copy the raw memory (starting at the given byte) into the instance. This method must + reflect the format of the "Glat_entry" as it is stored in the file and slurped into + memory. + ------------------------------------------------------------------------------------------*/ + void CopyFrom(byte * pbBIGEnt) + { + m_bMinAttrID = *pbBIGEnt; + m_cAttrs = *(pbBIGEnt + 1); + if (m_cAttrs >= kMaxAttrsPerRun) + { + gAssert(false); + m_cAttrs = kMaxAttrsPerRun; + } + #ifdef _DEBUG + // Probably not strictly necessary to zero the array, but convenient for debugging. + // Removed from release build for optimization + std::fill(m_rgchwBIGValues, m_rgchwBIGValues + kMaxAttrsPerRun, 0); + #endif + // this is mixing types of data16 and byte pointers! + const data16 * prgchw = reinterpret_cast<const data16*>(pbBIGEnt + 2); + std::copy(prgchw, prgchw + m_cAttrs, m_rgchwBIGValues); + } + + int ByteCount() + { + return (2 + (m_cAttrs * isizeof(data16))); + } + +protected: + byte m_bMinAttrID; // ID of first attribute in the run + byte m_cAttrs; // number of attributes in the run + data16 m_rgchwBIGValues[kMaxAttrsPerRun]; +}; + +/*---------------------------------------------------------------------------------------------- + Contains runs of attribute values for all the glyphs in the font; corresponds to + the "Glat" table in the ECF file. + + Hungarian: gatbl +----------------------------------------------------------------------------------------------*/ + +class GrGlyphAttrTable +{ + friend class GrGlyphTable; + friend class GrGlyphSubTable; + +protected: + // Constructor: + GrGlyphAttrTable() + { + m_prgbBIGEntries = NULL; + } + + // Destructor: + ~GrGlyphAttrTable() + { + delete[] m_prgbBIGEntries; + } + + // Initialization: + void Initialize(int fxdSilfVersion, int cbBufLen) + { + m_fxdSilfVersion = fxdSilfVersion; + m_cbEntryBufLen = cbBufLen; + m_prgbBIGEntries = new byte[cbBufLen]; + // Now the instance is ready to have all the glyph attr entries slurped into + // the byte array from the file. + } + + int GlyphAttr16BitValue(int ibMin, int ibLim, byte bAttrID); + +protected: + int m_fxdSilfVersion; // version number of the Silf table, which is used + // to interpret the attribute values + + // Block of variable-length glyph attribute runs, matching the format of + // GrGlyphAttrRun. We don't store instances of that class here because they + // are variable length, and it would be too slow to read them individually from the + // ECF file. Instead, we set up a single instance in the method that accesses the values; + // having this instance will help debugging. Eventually we may want to do without it, + // if it would help efficiency. + int m_cbEntryBufLen; // do we really need this? + byte * m_prgbBIGEntries; + +//:Ignore +#ifdef OLD_TEST_STUFF +public: + // For test procedures: + void SetUpTestData(); + void SetUpLigatureTest(); + void SetUpLigature2Test(); +#endif // OLD_TEST_STUFF + +//:End Ignore + +}; + +/*---------------------------------------------------------------------------------------------- + One glyph table per font (style) file; corresponds to the "Gloc" table in the ECF. + Currently there is only considered to be one style per file, so there is only one of + these. It holds the (non-zero) glyph attribute values for every glyph in the font. + + Hungarian: gstbl + + Review: Eventually we may need to make a subclass that uses 32-values for the offsets, + or just use a separate array pointer in this class. Which would be preferable? +----------------------------------------------------------------------------------------------*/ + +class GrGlyphSubTable +{ + friend class GrGlyphTable; + +public: + // Constructor: + GrGlyphSubTable() : + m_pgatbl(NULL), + m_prgibBIGAttrValues(NULL), + m_prgibBIGGlyphAttrDebug(NULL), + m_prgnDefinedComponents(NULL) + { + } + // Destructor: + ~GrGlyphSubTable() + { + delete m_pgatbl; + delete[] m_prgibBIGAttrValues; + if (m_fHasDebugStrings) + delete[] m_prgibBIGGlyphAttrDebug; + delete[] m_prgnDefinedComponents; + } + + // Initialization: + bool ReadFromFont(GrIStream & gloc_strm, int cGlyphs, + GrIStream & glat_strm, long lGlatStart); + void Initialize(int fxdSilfVersion, data16 chwFlags, + data16 chwBWAttr, data16 chwJStrAttr, data16 chwJStrHWAttr, + int cGlyphs, int cAttrs, int cnCompPerLig); + + void CreateEmpty(); + + void SetNumberOfComponents(int c) + { + m_cComponents = c; + } + + // General: + int NumberOfGlyphAttrs() { return m_nAttrIDLim; } // 0..(m_nAttrIDLim-1) + + int GlyphAttrValue(gid16 chwGlyphID, int nAttrID); + + // Ligatures: + int ComponentContainingPoint(gid16 chwGlyphID, int x, int y); + bool ComponentBoxLogUnits(float xysEmSquare, gid16 chwGlyphID, int icomp, + int mFontEmUnits, float dysAscent, + float * pxsLeft, float * pysTop, float * pxsRight, float * pysBottom, + bool fTopOrigin = true); +protected: + int CalculateDefinedComponents(gid16 chwGlyphID); + bool ComponentIsDefined(gid16 chwGlyphID, int nAttrID); + int ComponentIndexForGlyph(gid16 chwGlyphID, int nCompID); +// int NthComponentID(gid16 chwGlyphID, int n); + int ConvertValueForVersion(int nRet, int nAttrID); + +public: + static int ConvertValueForVersion(int nRet, int nAttrID, int nBWAttr, int fxdVersion); + + // Flags: + bool LongFormat(data16 chwFlags) // 32-bit values? + { + return ((chwFlags & 0x01) == 0x01); // bit 0 + } + bool HasAttrNames(data16 chwFlags) + { + return ((chwFlags & 0x02) == 0x02); // bit 1 + } + int GlocLookup(data16 chwGlyphId) + { + if (m_fGlocShort) + { + unsigned short su = ((unsigned short *)m_prgibBIGAttrValues)[chwGlyphId]; + return lsbf(su); + } + else + { unsigned int u = ((unsigned int *)m_prgibBIGAttrValues)[chwGlyphId]; + return lsbf((int)u); + } + } + +protected: + int m_fxdSilfVersion; // version number of the Silf table, which is used + // to interpret the attribute values + bool m_fHasDebugStrings; // are debugging strings loaded into memory? + + int m_nAttrIDLim; // number of glyph attributes + int m_cComponents; // number of initial glyph attributes that + // represent ligature components + int m_cnCompPerLig; + + GrGlyphAttrTable * m_pgatbl; + byte * m_prgibBIGAttrValues; // byte offsets for glyph attr values + bool m_fGlocShort; // flag for Gloc table format + data16 * m_prgibBIGGlyphAttrDebug; // byte offsets for glyph attr debug strings + + data16 m_chwBWAttr; // breakweight attr ID; needed for converting + // between versions + + // Attr IDs for justify.0.stretch and justify.0.stretchHW; these must always be looked + // up in tandem. + data16 m_chwJStrAttr; + data16 m_chwJStrHWAttr; + + int * m_prgnDefinedComponents; // for each glyph, cache list of component attributes that + // are defined + +//:Ignore +#ifdef OLD_TEST_STUFF +public: + // For test procedures: + void SetUpTestData(); + void SetUpLigatureTest(); + void SetUpLigature2Test(); +#endif // OLD_TEST_STUFF + +//:End Ignore +}; + +/*---------------------------------------------------------------------------------------------- + Holds all the information about glyph attributes. + + Hungarian: gtbl +----------------------------------------------------------------------------------------------*/ + +class GrGlyphTable +{ + friend class GrGlyphSubTable; + +public: + // Constructor: + GrGlyphTable() + { + m_cglf = 0; + m_cComponents = 0; + m_cgstbl = 0; + m_vpgstbl.clear(); + } + + // Destructor: + ~GrGlyphTable() + { + for (int i = 0; i < m_cgstbl; ++i) + delete m_vpgstbl[i]; + } + + bool ReadFromFont(GrIStream & gloc_strm, long lGlocStart, + GrIStream & glat_strm, long lGlatStart, + data16 chwBWAttr, data16 chwJStrAttr, int cJLevels, int cnCompPerLig, + int fxdSilfVersion); + + void CreateEmpty(); + + // Setters: + void SetNumberOfGlyphs(int c) + { + m_cglf = c; + } + void SetNumberOfStyles(int c) + { + m_cgstbl = c; + m_vpgstbl.resize(c); + } + void SetNumberOfComponents(int c) + { + m_cComponents = c; + for (unsigned int ipgstbl = 0; ipgstbl < m_vpgstbl.size(); ipgstbl++) + m_vpgstbl[ipgstbl]->SetNumberOfComponents(c); + } + void SetSubTable(int i, GrGlyphSubTable * pgstbl) + { + if (signed(m_vpgstbl.size()) <= i) + { + gAssert(false); + m_vpgstbl.resize(i+1); + } + m_vpgstbl[i] = pgstbl; + m_vpgstbl[i]->SetNumberOfComponents(m_cComponents); + } + + // Getters: + int NumberOfGlyphs() { return m_cglf; } + int NumberOfStyles() { return m_cgstbl; } + + int NumberOfGlyphAttrs() + { + // All of the sub-tables should have the same number of glyph attributes, + // so just ask the first. + return m_vpgstbl[0]->NumberOfGlyphAttrs(); + } + + int GlyphAttrValue(gid16 chwGlyphID, int nAttrID) + { + gAssert(m_cgstbl == 1); + return m_vpgstbl[0]->GlyphAttrValue(chwGlyphID, nAttrID); + } + // Eventually: + //int GlyphAttrValue(gid16 chwGlyphID, int nAttrID, int nStyleIndex) + //{ + // return m_vpgstbl[nStyleIndex]->GlyphAttrValue(chwGlyphID, nAttrID); + //} + + int ComponentContainingPoint(gid16 chwGlyphID, int x, int y) + { + gAssert(m_cgstbl == 1); + return m_vpgstbl[0]->ComponentContainingPoint(chwGlyphID, x, y); + } + // Eventually: + //int ComponentContainingPoint(gid16 chwGlyphID, int nStyleIndex, int x, int y) + //{ + // gAssert(m_cgstbl == 1); + // return m_vpgstbl[nStyleIndex]->ComponentContainingPoint(chwGlyphID, nAttrID); + //} + + bool ComponentBoxLogUnits(float xysEmSquare, gid16 chwGlyphID, int icomp, + int mFontEmUnits, float dysAscent, + float * pxsLeft, float * pysTop, float * pxsRight, float * pysBottom, + bool fTopOrigin = true) + { + gAssert(m_cgstbl == 1); + return m_vpgstbl[0]->ComponentBoxLogUnits(xysEmSquare, + chwGlyphID, icomp, mFontEmUnits, dysAscent, + pxsLeft, pysTop, pxsRight, pysBottom, + fTopOrigin); + } + + int ComponentIndexForGlyph(gid16 chwGlyphID, int nCompID) + { + gAssert(m_cgstbl == 1); + return m_vpgstbl[0]->ComponentIndexForGlyph(chwGlyphID, nCompID); + } + + bool IsEmpty() + { + return (m_cglf == 0); + } + +protected: + int m_cglf; // number of glyphs + int m_cComponents; // number of defined components + int m_cgstbl; // number of sub-tables, corresponding to font files; + // for now there is only one + + std::vector<GrGlyphSubTable *> m_vpgstbl; + +//:Ignore +#ifdef OLD_TEST_STUFF +public: + // For test procedures: + void SetUpTestData(); + void SetUpLigatureTest(); + void SetUpLigature2Test(); +#endif // OLD_TEST_STUFF +//:End Ignore + +}; + +} // namespace gr + +#endif // !GR_GTABLE_INCLUDED diff --git a/Build/source/libs/graphite-engine/src/segment/GrPass.cpp b/Build/source/libs/graphite-engine/src/segment/GrPass.cpp new file mode 100644 index 00000000000..96e88ac150e --- /dev/null +++ b/Build/source/libs/graphite-engine/src/segment/GrPass.cpp @@ -0,0 +1,1977 @@ +/*--------------------------------------------------------------------*//*:Ignore this sentence. +Copyright (C) 1999, 2001 SIL International. All rights reserved. + +Distributable under the terms of either the Common Public License or the +GNU Lesser General Public License, as specified in the LICENSING.txt file. + +File: GrPass.cpp +Responsibility: Sharon Correll +Last reviewed: Not yet. + +Description: + Implements the GrPass class and subclasses. +----------------------------------------------------------------------------------------------*/ + +//:>******************************************************************************************** +//:> Include files +//:>******************************************************************************************** +#include "Main.h" + +#ifdef _MSC_VER +#pragma hdrstop +#endif +#undef THIS_FILE +DEFINE_THIS_FILE + +//:End Ignore + +//:>******************************************************************************************** +//:> Forward declarations +//:>******************************************************************************************** + +//:>******************************************************************************************** +//:> Local Constants and static variables +//:>******************************************************************************************** + +namespace gr +{ + +//:>******************************************************************************************** +//:> Methods +//:>******************************************************************************************** +/*---------------------------------------------------------------------------------------------- + Constructors and initializers +----------------------------------------------------------------------------------------------*/ +GrPass::GrPass(int i) + : m_ipass(i), + vnStack(128), + m_nMaxRuleContext(0), + m_pfsm(NULL), + m_nMaxRuleLoop(0), + m_nMaxBackup(0), + m_prgchwRuleSortKeys(NULL), + m_prgcritRulePreModContext(NULL), + m_cbPassConstraint(0), + m_prgibConstraintStart(NULL), + m_prgibActionStart(NULL), + m_prgbPConstraintBlock(NULL), + m_prgbConstraintBlock(NULL), + m_prgbActionBlock(NULL), + m_prgibConstraintDebug(NULL), + m_prgibRuleDebug(NULL), + m_fCheckRules(false), + m_prgfRuleOkay(NULL) +{ +} + +void PassState::InitForNewSegment(int ipass, int nMaxChunk) +{ + m_ipass = ipass; + m_nRulesSinceAdvance = 0; + m_nMaxChunk = nMaxChunk; + m_cslotSkipToResync = 0; + m_fDidResyncSkip = false; + InitializeLogInfo(); +} + +void PassState::InitializeLogInfo() +{ + m_crulrec = 0; + std::fill_n(m_rgcslotDeletions, 128, 0); + std::fill_n(m_rgfInsertion, 128, false); +} + +/*---------------------------------------------------------------------------------------------- + Destructors +----------------------------------------------------------------------------------------------*/ +GrPass::~GrPass() +{ + delete m_pfsm; + + delete[] m_prgchwRuleSortKeys; + + delete[] m_prgcritRulePreModContext; + + delete[] m_prgibConstraintStart; + delete[] m_prgibActionStart; + + delete[] m_prgbPConstraintBlock; + delete[] m_prgbConstraintBlock; + delete[] m_prgbActionBlock; + + delete[] m_prgfRuleOkay; + + delete[] m_prgibConstraintDebug; + delete[] m_prgibRuleDebug; +} + +/*---------------------------------------------------------------------------------------------- + Fill in the pass by reading from the font stream. +----------------------------------------------------------------------------------------------*/ +bool GrPass::ReadFromFont(GrIStream & grstrm, int fxdSilfVersion, int fxdRuleVersion, + int nOffset) +{ + long lPassInfoStart; + grstrm.GetPositionInFont(&lPassInfoStart); + + m_fxdVersion = fxdSilfVersion; + + m_fCheckRules = (fxdRuleVersion > kRuleVersion); + +// Assert(nOffset == lPassInfoStart); + if (lPassInfoStart != nOffset) + { + grstrm.SetPositionInFont(nOffset); + } + + // flags - ignore for now + //byte bTmp = grstrm.ReadByteFromFont(); + grstrm.ReadByteFromFont(); + + // MaxRuleLoop + m_nMaxRuleLoop = grstrm.ReadByteFromFont(); + + // max rule context + m_nMaxRuleContext = grstrm.ReadByteFromFont(); + + // MaxBackup + m_nMaxBackup = grstrm.ReadByteFromFont(); + + // number of rules + m_crul = grstrm.ReadShortFromFont(); + // TODO: add a sanity check for the number of rules. + + // offset to pass constraint code, relative to start of subtable + int nPConstraintOffset = 0; + long lFsmPos = -1; + if (fxdSilfVersion >= 0x00020000) + { + if (fxdSilfVersion >= 0x00030000) + lFsmPos = grstrm.ReadUShortFromFont() + nOffset; // offset to row info + else + grstrm.ReadShortFromFont(); // pad bytes + nPConstraintOffset = grstrm.ReadIntFromFont(); + } + // offset to rule constraint code, relative to start of subtable + //int nConstraintOffset = grstrm.ReadIntFromFont(); + grstrm.ReadIntFromFont(); + // offset to action code, relative to start of subtable + //int nActionOffset = grstrm.ReadIntFromFont(); + grstrm.ReadIntFromFont(); + // offset to debug strings; 0 if stripped + //int nDebugOffset = grstrm.ReadIntFromFont(); + grstrm.ReadIntFromFont(); + + // Jump to beginning of FSM, if we have this information. + if (fxdSilfVersion >= 0x00030000) + grstrm.SetPositionInFont(lFsmPos); + else + // Otherwise assume that's where we are! + Assert(lFsmPos == -1); + + m_pfsm = new GrFSM(); + + m_pfsm->ReadFromFont(grstrm, fxdSilfVersion); + + // rule sort keys + m_prgchwRuleSortKeys = new data16[m_crul]; + data16 * pchw = m_prgchwRuleSortKeys; + int irul; + for (irul = 0; irul < m_crul; irul++, pchw++) + { + *pchw = grstrm.ReadUShortFromFont(); + } + + // rule pre-mod-context item counts + m_prgcritRulePreModContext = new byte[m_crul]; + byte * pb = m_prgcritRulePreModContext; + for (irul = 0; irul < m_crul; irul++, pb++) + { + *pb = grstrm.ReadByteFromFont(); + } + + // constraint offset for pass-level constraints + if (fxdSilfVersion >= 0x00020000) + { + // reserved - pad byte + grstrm.ReadByteFromFont(); + // Note: pass constraints have not been fully implemented. + m_cbPassConstraint = grstrm.ReadUShortFromFont(); + } + else + m_cbPassConstraint = 0; + + // constraint and action offsets for rules + m_prgibConstraintStart = new data16[m_crul + 1]; + pchw = m_prgibConstraintStart; + for (irul = 0; irul <= m_crul; irul++, pchw++) + { + *pchw = grstrm.ReadUShortFromFont(); + } + + m_prgibActionStart = new data16[m_crul + 1]; + pchw = m_prgibActionStart; + for (irul = 0; irul <= m_crul; irul++, pchw++) + { + *pchw = grstrm.ReadUShortFromFont(); + } + + // FSM state table + m_pfsm->ReadStateTableFromFont(grstrm, fxdSilfVersion); + + if (fxdSilfVersion >= 0x00020000) + // reserved - pad byte + grstrm.ReadByteFromFont(); + + // Constraint and action blocks + int cb = m_cbPassConstraint; + m_prgbPConstraintBlock = new byte[cb]; + grstrm.ReadBlockFromFont(m_prgbPConstraintBlock, cb); + + cb = m_prgibConstraintStart[m_crul]; + m_prgbConstraintBlock = new byte[cb]; + grstrm.ReadBlockFromFont(m_prgbConstraintBlock, cb); + + cb = m_prgibActionStart[m_crul]; + m_prgbActionBlock = new byte[cb]; + grstrm.ReadBlockFromFont(m_prgbActionBlock, cb); + + // Rule-validity flags + m_prgfRuleOkay = new bool[m_crul]; + for (irul = 0; irul < m_crul; irul++) + m_prgfRuleOkay[irul] = !m_fCheckRules; + + // TODO SharonC/AlanW: debuggers + + return true; +} + +/*---------------------------------------------------------------------------------------------- + Initialize a bogus pass with no rules. Currently this is used to make a single positioning + pass if there was none in the font. +----------------------------------------------------------------------------------------------*/ +void GrPass::InitializeWithNoRules() +{ + m_crul = 0; + m_nMaxRuleContext = 1; + m_nMaxRuleLoop = 1; + m_nMaxBackup = 0; + m_pfsm = NULL; + m_prgchwRuleSortKeys = NULL; + m_prgcritRulePreModContext = NULL; +} + +/*---------------------------------------------------------------------------------------------- + Extend the output stream by the given number of characters. + (Overridden by GrBidiPass.) + + @param ptman - table manager + @param psstrmIn/Out - streams being processed + @param cslotNeededByNext - the number of slots being requested by the following pass + @param twsh - how we are handling trailing white-space + @param pnRet - return value + @param pcslotGot - return the number of slots gotten + @param pislotFinalBreak - return the index of the final slot, when we are removing + the trailing white-space and so the end of the segment + will be before the any actual line-break slot + + @return kNextPass if we were able to generated the number requested, or processing is + complete; otherwise return the number of slots needed from the previous pass. +----------------------------------------------------------------------------------------------*/ +void GrPass::ExtendOutput(GrTableManager * ptman, + GrSlotStream * psstrmIn, GrSlotStream * psstrmOut, + int cslotNeededByNext, TrWsHandling twsh, + int * pnRet, int * pcslotGot, int * pislotFinalBreak) +{ + // Kludge to generate an error in rendering: + //int z = 50; + //int t = ((5 * 10) / z) - 1; + //if (this->m_ipass == 4) + //{ + // int x; x = z / t; + //} + + int islotInitReadPos = psstrmIn->ReadPos(); + int islotInitWritePos = psstrmOut->WritePos(); + + int cslotToGet = max(cslotNeededByNext, + m_pzpst->NeededToResync() - psstrmOut->WritePos()); + int cslotGot = 0; + + gid16 chwLB = ptman->LBGlyphID(); + + // While we haven't got the number of slot we've been asked for, and there is enough + // available in the input, run rules, filling up the output. + + while ((cslotToGet > 0 && cslotGot < cslotToGet) + // Don't leave this pass until we've got the index offset. This is especially + // needed for input to the positioning pass, but it doesn't hurt to make it a + // general rule. + || !psstrmOut->GotIndexOffset() + // Don't leave this pass until all the slots to reprocess have been + // completely handled. + || (psstrmIn->SlotsToReprocess() > 0) + // Don't leave this pass until there is a complete cluster in the output + || (islotInitWritePos == psstrmOut->WritePos()) + || (psstrmOut->MaxClusterSlot(islotInitWritePos, psstrmOut->WritePos()) > 0)) + { + int cslotAvailable = psstrmIn->TotalSlotsPending(); + int cslotNeedMore = MaxRuleContext() - cslotAvailable + ptman->PrevPassMaxBackup(m_ipass); + + // This test should not be necessary, but just in case. + if (psstrmIn->PastEndOfPositioning(false)) + cslotNeedMore = 0; // the input stream is done + + if (// Not enough available in the input: + (cslotNeedMore > 0 && !psstrmIn->FullyWritten()) + + // && psstrmIn->SegLimIfKnown() == -1) && -- no, because substitution passes may need + // to consider slots beyond the seg limit + + // Positioning passes need to know where they are in relation to the initial + // line-break. + || (IsPosPass() && !psstrmIn->GotIndexOffset()) + + // The following can happen on backtracking, when we undo stuff before the + // beginning of the segment: + || !ptman->Pass(m_ipass - 1)->DidResyncSkip()) + { + // Ask previous pass for more input. + *pnRet = max(cslotNeedMore, 1); + *pnRet = max(*pnRet, cslotNeededByNext - cslotGot); + *pcslotGot = cslotGot; + return; + } + + Assert(ptman->Pass(m_ipass - 1)->DidResyncSkip()); + + bool fDoneThisPass = (cslotAvailable == 0); + if (psstrmIn->SlotsToReprocess() == 0) + { + fDoneThisPass = fDoneThisPass || + psstrmIn->PastEndOfPositioning(false) || + psstrmOut->PastEndOfPositioning(true); + } + + if (fDoneThisPass) + { + // No more input to process--this pass is done. + Assert(psstrmIn->SlotsToReprocess() == 0); + psstrmIn->ClearReprocBuffer(); + + if (twsh == ktwshNoWs && m_ipass == ptman->NumberOfLbPasses()) + { + *pnRet = RemoveTrailingWhiteSpace(ptman, psstrmOut, twsh, pislotFinalBreak); + if (*pnRet == kBacktrack) // entire segment was white-space: + return; // backtrack, which will fail + } + DoResyncSkip(psstrmOut); + DoCleanUpSegMin(ptman, psstrmIn, islotInitReadPos, psstrmOut); + psstrmOut->MarkFullyWritten(); + *pnRet = kNextPass; + *pcslotGot = cslotGot; + return; + } + + if (twsh == ktwshOnlyWs && m_ipass == ptman->NumberOfLbPasses() + 1) + { + // Note that this is the first pass after the linebreak pass, so psstrmInput + // is the output of the linebreak pass. + GrSlotState * pslotNext = psstrmIn->Peek(); + if (!pslotNext->IsLineBreak(chwLB) && + pslotNext->Directionality() != kdircWhiteSpace && + psstrmIn->SegMin() > -1 && psstrmIn->SegMin() <= psstrmIn->ReadPos()) + { + // We are only allowing white-space in this segment and we hit + // something else. Don't process any further. + if (psstrmIn->SegLimIfKnown() > -1 && + psstrmIn->SegLimIfKnown() <= psstrmIn->ReadPos()) + { + // Already inserted a line-break; we're done. + DoResyncSkip(psstrmOut); + psstrmOut->MarkFullyWritten(); + *pnRet = kNextPass; + *pcslotGot = cslotGot; + return; + } + while (psstrmIn->SlotsToReprocess() > 0) + { + psstrmOut->CopyOneSlotFrom(psstrmIn); + psstrmOut->SetPosForNextRule(0, psstrmIn, IsPosPass()); + } + psstrmIn->ClearReprocBuffer(); + *pnRet = kBacktrack; + return; + } + } + + // Otherwise, we have enough input to run a rule. + + psstrmIn->SetRuleStartReadPos(); + psstrmOut->SetRuleStartWritePos(); + + int ruln = -1; + if (m_pfsm) + ruln = m_pfsm->GetRuleToApply(ptman, this, psstrmIn, psstrmOut); + ruln = CheckRuleValidity(ruln); + RunRule(ptman, ruln, psstrmIn, psstrmOut); + + cslotGot = psstrmOut->WritePos() - islotInitWritePos; + psstrmOut->CalcIndexOffset(ptman); + } + + DoResyncSkip(psstrmOut); + DoCleanUpSegMin(ptman, psstrmIn, islotInitReadPos, psstrmOut); + + // We're past the point where we care about anything in the reprocessing buffer. + Assert(psstrmIn->NoReproc()); + psstrmIn->ClearReprocBuffer(); + + if (psstrmOut->PastEndOfPositioning(true)) + psstrmOut->MarkFullyWritten(); + + *pnRet = kNextPass; + *pcslotGot = cslotGot; +} + +/*---------------------------------------------------------------------------------------------- + Extend the output stream by the given number of characters. + For a GrBidiPass, if we are at a direction change, get the entire + range of upstream glyphs, reverse them, and treat them as one chunk. + Otherwise just pass one slot through. + + @param ptman - table manager + @param psstrmInput/Output - streams being processed + @param cslotNeededByNext - the number of slots being requested by the following pass + @param twsh - how we are handling trailing white-space + @param pnRet - return value + @param pcslotGot - return the number of slots gotten + @param pislotFinalBreak - not used in this version + + @return 1 if we need more glyphs from the previous pass +----------------------------------------------------------------------------------------------*/ +void GrBidiPass::ExtendOutput(GrTableManager * ptman, + GrSlotStream* psstrmIn, GrSlotStream* psstrmOut, + int cslotNeededByNext, TrWsHandling twsh, + int * pnRet, int * pcslotGot, int * pislotFinalBreak) +{ + Assert(psstrmIn->SlotsToReprocess() == 0); + + int islotInitReadPos = psstrmIn->ReadPos(); + int islotInitWritePos = psstrmOut->WritePos(); + + Assert(m_pzpst->NeededToResync() == 0); + + int cslotToGet = max(cslotNeededByNext, + m_pzpst->NeededToResync() - psstrmOut->WritePos()); + int cslotGot = 0; + int nTopDir; + if (twsh == ktwshOnlyWs) + nTopDir = (ptman->State()->ParaRightToLeft()) ? 1 : 0; + else + nTopDir = ptman->TopDirectionLevel(); + + while (cslotToGet > 0 && cslotGot < cslotToGet) + { + int islotChunkO = psstrmOut->WritePos(); + int islotChunkI = psstrmIn->ReadPos(); + + // Need at least one character to test. + if (psstrmIn->SlotsPending() < 1 || !ptman->Pass(m_ipass-1)->DidResyncSkip()) + { + if (!psstrmIn->FullyWritten()) + { + // Ask previous pass for more input. + *pnRet = max(cslotToGet - psstrmIn->SlotsPending(), 1); + *pnRet = max(*pnRet, cslotNeededByNext - cslotGot); + *pcslotGot = cslotGot; + return; + } + else + { + Assert(ptman->Pass(m_ipass-1)->DidResyncSkip()); + Assert(psstrmIn->SlotsToReprocess() == 0); + psstrmIn->ClearReprocBuffer(); + DoResyncSkip(psstrmOut); + DoCleanUpSegMin(ptman, psstrmIn, islotInitReadPos, psstrmOut); + psstrmOut->MarkFullyWritten(); + *pnRet = kNextPass; + *pcslotGot = cslotGot; + return; + } + } + + std::vector<int> vislotStarts; + std::vector<int> vislotStops; + int islotReverseLim = psstrmIn->DirLevelRange(ptman->State(), + psstrmIn->ReadPos(), nTopDir, + vislotStarts, vislotStops); + //int islotReverseLim = psstrmIn->OldDirLevelRange(pengst, psstrmIn->ReadPos(), nTopDir); + if (islotReverseLim == -1) + { + // We haven't got the full range of reversed text yet-- + // ask for more input. + *pnRet = max(1, cslotNeededByNext - cslotGot); + *pcslotGot = cslotGot; + return; + } + + // Okay, we have enough input to do the reversal, if any. + + int cslotToReverse = islotReverseLim - psstrmIn->ReadPos(); + + // Never reverse the final linebreak; leave it at the end. + if (cslotToReverse > 0 && islotReverseLim > 0) + { + GrSlotState * pslotLast = psstrmIn->SlotAt(islotReverseLim - 1); + if (pslotLast->IsFinalLineBreak(ptman->LBGlyphID())) + { + for (size_t i = 0; i < vislotStops.size(); i++) + if (vislotStops.back() == islotReverseLim - 1) + vislotStops.back() = islotReverseLim - 2; + islotReverseLim--; + cslotToReverse--; + } + } + + psstrmIn->SetRuleStartReadPos(); + psstrmOut->SetRuleStartWritePos(); + + if (cslotToReverse == 0) + { + psstrmOut->CopyOneSlotFrom(psstrmIn); + } + else + { + int islotNextWritePos = psstrmOut->WritePos() + cslotToReverse; + int islotNextReadPos = psstrmIn->ReadPos() + cslotToReverse; + if (vislotStarts.size() == 0) + { + Assert(false); // this should have been done by DirLevelRange + vislotStarts.push_back(psstrmIn->ReadPos()); + vislotStops.push_back(islotNextReadPos - 1); + } + Assert(vislotStarts.back() == psstrmIn->ReadPos()); + Assert(vislotStops.back() == islotNextReadPos - 1); + int cslotNotCopied = Reverse(ptman, psstrmIn, psstrmOut, vislotStarts, vislotStops); + //Reverse(nTopDir + 1, + // psstrmIn, psstrmIn->ReadPos(), islotReverseLim, + // psstrmOut, psstrmOut->WritePos() + cslotToReverse - 1, psstrmOut->WritePos()-1); + + islotNextWritePos -= cslotNotCopied; // bidi markers that are not passed through + + psstrmIn->SetReadPos(islotNextReadPos); + psstrmOut->SetWritePos(islotNextWritePos); + + // It's quite strange to have the segment start or end in the middle of stuff to + // reverse (because the LB forms a natural terminator), but at any rate, + // if that happens, record the segment lim at the corresponding place in the + // output stream. + int islotSegMinIn = psstrmIn->SegMin(); + if (islotSegMinIn > -1 && + psstrmIn->ReadPos() - cslotToReverse <= islotSegMinIn && + islotSegMinIn < psstrmIn->ReadPos()) + { + Assert(islotSegMinIn == psstrmIn->ReadPos() - cslotToReverse); // normal situation + psstrmOut->SetSegMin( + psstrmOut->WritePos() - (psstrmIn->ReadPos() - islotSegMinIn)); + } + int islotSegLimIn = psstrmIn->SegLimIfKnown(); + if (islotSegLimIn > -1 && + psstrmIn->ReadPos() - cslotToReverse <= islotSegLimIn && + islotSegLimIn < psstrmIn->ReadPos()) + { + Assert(islotSegLimIn == psstrmIn->ReadPos() - cslotToReverse); // normal situation + psstrmOut->SetSegLim( + psstrmOut->WritePos() - (psstrmIn->ReadPos() - islotSegLimIn)); + } + } + + psstrmOut->SetPosForNextRule(0, psstrmIn, false); + + // Record the chunk mappings: + MapChunks(psstrmIn, psstrmOut, islotChunkI, islotChunkO, 0); + + cslotGot = psstrmOut->WritePos() - islotInitWritePos; + psstrmOut->CalcIndexOffset(ptman); + } + + DoResyncSkip(psstrmOut); + DoCleanUpSegMin(ptman, psstrmIn, islotInitReadPos, psstrmOut); + + // We're past the point where we care about anything in the reprocessing buffer. + Assert(psstrmIn->NoReproc()); + psstrmIn->ClearReprocBuffer(); + + Assert(psstrmIn->SlotsToReprocess() == 0); + + if (psstrmOut->PastEndOfPositioning(true)) + psstrmOut->MarkFullyWritten(); + + *pnRet = kNextPass; + *pcslotGot = cslotGot; +} + +/*---------------------------------------------------------------------------------------------- + Generate slots containing glyph IDs for the underlying character data, + incrementing the input pointer as we go. + + @param ptman - table manager + @param pchstrm - input character stream + @param psstrmOutput - output slot stream + @param ichSegLim - known end of segment, index into the text; or -1; + the lim of the of char-stream itself represents + the end of the text-source; this lim is the + known desired end of the segment + @param cchwPostXlbContext - number of characters that may be needed from the following line; + valid when ichSegLim > -1 + @param lb - breakweight to use for inserted LB + @param cslotToGet - the number of slots being requested by the following pass + @param fNeedFinalBreak - true if the end of the segment needs to be a valid break point + @param pislotFinalBreak - the end of this segment +----------------------------------------------------------------------------------------------*/ +int GrPass::ExtendGlyphIDOutput(GrTableManager * ptman, + GrCharStream * pchstrm, GrSlotStream * psstrmOut, int ichSegLim, int cchwPostXlbContext, + LineBrk lb, int cslotToGet, bool fNeedFinalBreak, TrWsHandling twsh, + int * pislotFinalBreak) +{ + EngineState * pengst = ptman->State(); + + // This pass should be the glyph-generation pass. + Assert(dynamic_cast<GrGlyphGenPass*>(this)); + Assert(m_pzpst->m_cslotSkipToResync == 0); + m_pzpst->m_fDidResyncSkip = true; + + for (int islot = 0; islot < cslotToGet; ++islot) + { + int islotChunkO = psstrmOut->WritePos(); + int islotChunkI = pchstrm->SegOffset(); + + if (pchstrm->AtEnd() + || (ichSegLim > -1 && pchstrm->Pos() == ichSegLim)) + { + if (psstrmOut->SegLimIfKnown() > -1 && + psstrmOut->SegLimIfKnown() <= psstrmOut->WritePos()) + { + // Already found the end of this stream. + } + else + { + if (pchstrm->EndLine() && !fNeedFinalBreak) + // (if we need a good final break, don't just append an LB; + // make it backtrack and find a valid break point) + { + // Only need to get a good break when we're backtracking; otherwise we + // already know it. + Assert(ichSegLim == -1 || !fNeedFinalBreak); + // Notice that we're cheating here: we're putting the LB in the zeroth + // stream instead of in the output of the linebreak table. + psstrmOut->AppendLineBreak(ptman, pchstrm, + (pchstrm->AtEnd() ? klbWordBreak : lb), + ((ptman->RightToLeft()) ? kdircRlb : kdircLlb), -1, false, + pchstrm->SegOffset()); + if (pchstrm->AtEnd()) + pengst->SetFinalLB(); + else + pengst->SetInsertedLB(true); + } + else + { + // Don't actually insert a line-break glyph. + psstrmOut->SetSegLimToWritePos(); + } + *pislotFinalBreak = psstrmOut->WritePos() - 1; + if (ptman->NumberOfLbPasses() > 0 && pengst->HasInitialLB()) + { + // Because we cheated above: the output stream of the linekbreak table + // has an initial LB, which this stream doesn't have. Adjust the position + // of the final break to match what it will be in the output of the + // lb table. + *pislotFinalBreak += 1; + } + } + + if (twsh == ktwshNoWs && m_ipass == ptman->NumberOfLbPasses()) + { + int nRet = RemoveTrailingWhiteSpace(ptman, psstrmOut, twsh, pislotFinalBreak); + if (nRet == kBacktrack) // entire segment was white-space: + return kBacktrack; // backtrack, which will fail + } + if (pchstrm->AtEnd()) + { + psstrmOut->MarkFullyWritten(); + return kNextPass; + } + // otherwise we may need a few more characters for line-boundary contextualization. + } + + int ichwSegOffset; // offset from the official start of the segment + int cchw; // number of 16-bit chars consumed + GrFeatureValues fval; + int nUnicode = pchstrm->NextGet(ptman, &fval, &ichwSegOffset, &cchw); + gid16 chwGlyphID = ptman->GetGlyphIDFromUnicode(nUnicode); + + if (nUnicode == knCR || nUnicode == knLF || nUnicode == knLineSep || nUnicode == knParaSep || + nUnicode == knORC) + { + // Hard line-break: we're done. + // Note that we don't include the hard-break character in this segment. + pchstrm->HitHardBreak(); + pengst->SetHitHardBreak(); + psstrmOut->MarkFullyWritten(); + return kNextPass; + } + + GrSlotState * pslotNew; + ptman->State()->NewSlot(chwGlyphID, fval, 0, ichwSegOffset, nUnicode, &pslotNew); + + psstrmOut->NextPut(pslotNew); + psstrmOut->MapInputChunk(islotChunkI, islotChunkO, pchstrm->SegOffset(), false, false); + // Mapping the output chunks of the char stream has already been handled by the + // char stream. + + } + + psstrmOut->CalcIndexOffset(ptman); + + if (psstrmOut->PastEndOfPositioning(true) + || (ichSegLim > -1 && pchstrm->Pos() > ichSegLim + cchwPostXlbContext)) + { + // We have enough for this segment. + psstrmOut->MarkFullyWritten(); + } + + return kNextPass; +} + +/*---------------------------------------------------------------------------------------------- + Extend the output stream until it is using up the allotted amount of + physical space. Return kNextPass when all data has been processed successfully. + Return kBacktrack when space overflows, indicating that we need to backtrack + and find a break point. Otherwise, return the number of slots we + need from the previous pass. + + @param fWidthIsCharCount - kludge for test procedures + @param fInfiniteWidth - don't test for more space + @param fMustBacktrack - true if we need a good final break and haven't found one yet + @param lbMax - max allowed for the final slot + @param twsh - how we are handling trailing white-space +----------------------------------------------------------------------------------------------*/ +int GrPass::ExtendFinalOutput(GrTableManager * ptman, + GrSlotStream * psstrmInput, GrSlotStream * psstrmOutput, + float xsSpaceAllotted, bool fWidthIsCharCount, bool fInfiniteWidth, + bool fHaveLineBreak, bool fMustBacktrack, LineBrk lbMax, TrWsHandling twsh, + int * pislotLB, float * pxsWidth) +{ + EngineState * pengst = ptman->State(); + + // This pass should be the final positioning pass. + Assert(dynamic_cast<GrPosPass*>(this)); + + // It would be very strange to be positioning based on something that happened in + // previous line. -- not true any more, since this will include the max-precontext-length. +// Assert(m_cslotSkipToResync == 0); + + int islotOutputLB = -1; + int islotInitWritePos = psstrmOutput->WritePos(); + + while (true) + { + // Do this right up front, so we are only measuring actual output. + if (m_pzpst->CanResyncSkip(psstrmOutput)) + m_pzpst->DoResyncSkip(psstrmOutput); + + bool fMoreSpace; + if (!m_pzpst->DidResyncSkip()) + fMoreSpace = true; + else if (fInfiniteWidth) + fMoreSpace = true; + else + fMoreSpace = psstrmOutput->MoreSpace(ptman, + xsSpaceAllotted, fWidthIsCharCount, + true, // always ignore trailing white space when we are first making the segment + twsh, + pxsWidth); + if (!fMoreSpace) + { + // Overflowed available space; backtrack and find a line break. + while (psstrmInput->SlotsToReprocess() > 0) + { + psstrmOutput->CopyOneSlotFrom(psstrmInput); + psstrmOutput->SetPosForNextRule(0, psstrmInput, IsPosPass()); + } + psstrmInput->ClearReprocBuffer(); + + *pislotLB = -1; + pengst->SetExceededSpace(); + pengst->SetHitHardBreak(false); + return kBacktrack; + } + + if (islotOutputLB != -1 && psstrmInput->SlotsToReprocess() == 0) + { + // Hit the inserted line break--we're done. + psstrmInput->ClearReprocBuffer(); + *pislotLB = islotOutputLB; + return kNextPass; + } + + int nslotAvailable = psstrmInput->SlotsPending(); + if ((nslotAvailable - ptman->PrevPassMaxBackup(m_ipass) + < MaxRuleContext() && !psstrmInput->FullyWritten()) + || !ptman->Pass(m_ipass-1)->DidResyncSkip()) + { + // Not enough available in the input--ask previous pass for more input. + // Ten is an arbitrary value--we ask for more that we really need to cut down + // on the number of times we loop between passes. + return max(MaxRuleContext() - (nslotAvailable - 10), 1); + } + + Assert(ptman->Pass(m_ipass - 1)->DidResyncSkip()); + + if (nslotAvailable == 0) + { + // No more input to process. If we have a valid line-break, or we don't care, + // we're done. Otherwise backtrack to find a valid break point. + Assert(psstrmInput->SlotsToReprocess() == 0); + psstrmInput->ClearReprocBuffer(); + if (fMustBacktrack) + { + *pislotLB = -1; + return kBacktrack; + } + else + { + psstrmOutput->MarkFullyWritten(); + return kNextPass; + } + } + + // Otherwise, we have enough input to run a rule. + + psstrmInput->SetRuleStartReadPos(); + psstrmOutput->SetRuleStartWritePos(); + + int ruln = -1; + if (m_pfsm) + ruln = m_pfsm->GetRuleToApply(ptman, this, psstrmInput, psstrmOutput); + ruln = CheckRuleValidity(ruln); + RunRule(ptman, ruln, psstrmInput, psstrmOutput); + + if (fHaveLineBreak) + islotOutputLB = + psstrmOutput->FindFinalLineBreak(ptman->LBGlyphID(), + islotInitWritePos, psstrmOutput->WritePos()); + + psstrmOutput->CalcIndexOffset(ptman); + } + + Assert(false); + + psstrmInput->ClearReprocBuffer(); + *pislotLB = -1; + return kNextPass; +} + +/*---------------------------------------------------------------------------------------------- + Remove undesirable trailing white-space. +----------------------------------------------------------------------------------------------*/ +int GrPass::RemoveTrailingWhiteSpace(GrTableManager * ptman, GrSlotStream * psstrmOut, + TrWsHandling twsh, int * pislotFinalBreak) +{ + EngineState * pengst = ptman->State(); + + Assert(twsh == ktwshNoWs); + Assert(m_ipass == ptman->NumberOfLbPasses()); // output of (final) lb pass + + int islotTmp = psstrmOut->FinalSegLim(); + if (islotTmp <= 0) + return kNextPass; + + GrSlotState * pslotLast = psstrmOut->SlotAt(islotTmp-1); + if (islotTmp > 0 && pslotLast->IsFinalLineBreak(ptman->LBGlyphID())) + { + islotTmp--; + pslotLast = (islotTmp > 0) ? psstrmOut->SlotAt(islotTmp-1) : NULL; + } + bool fRemovedWs = false; + while (islotTmp > 0 && pslotLast->Directionality() == kdircWhiteSpace) + { + islotTmp--; + pslotLast = (islotTmp > 0) ? psstrmOut->SlotAt(islotTmp-1) : NULL; + fRemovedWs = true; + } + if (fRemovedWs) + { + if (islotTmp <= 0) + { + // Entire segment was white-space: backtrack, which will fail. + return kBacktrack; + } + psstrmOut->SetSegLim(islotTmp); + *pislotFinalBreak = islotTmp - 1; + pengst->SetFinalLB(false); + pengst->SetRemovedTrWhiteSpace(); + ptman->UnwindAndReinit(islotTmp - 1); + } + + return kNextPass; +} + +/*---------------------------------------------------------------------------------------------- + Keep track of whether we're advancing through the input satisfactorily; + if not, forcibly advance. This is a safety net to avoid infinite loops; it + should never be necessary if they've set up their tables right. +----------------------------------------------------------------------------------------------*/ +void GrPass::CheckInputProgress(GrSlotStream * psstrmInput, GrSlotStream * psstrmOutput, + int islotOrigInput) +{ + int islotInput = psstrmInput->ReadPosForNextGet(); +// Assert(islotInput >= islotOrigInput); -- no longer true now that we can back up + + if (islotInput <= psstrmInput->ReadPosMax()) + { + // Didn't advance. + if (m_pzpst->m_nRulesSinceAdvance >= m_nMaxRuleLoop) + { + bool fAdvanced = false; + // Forcibly advance. First try to advance to where we backed up from. + while (!psstrmInput->AtEnd() && + psstrmInput->ReadPosForNextGet() < psstrmInput->ReadPosMax()) + { + RecordHitMaxRuleLoop(psstrmInput->ReadPosForNextGet()); + psstrmOutput->CopyOneSlotFrom(psstrmInput); + fAdvanced = true; + } + // If that didn't do anything productive, just advance one slot. + if (!fAdvanced && !psstrmInput->AtEndOfContext()) + { + RecordHitMaxRuleLoop(psstrmInput->ReadPosForNextGet()); + psstrmOutput->CopyOneSlotFrom(psstrmInput); + } + + m_pzpst->m_nRulesSinceAdvance = 0; + } + else m_pzpst->m_nRulesSinceAdvance++; + } + else m_pzpst->m_nRulesSinceAdvance = 0; + + psstrmInput->SetReadPosMax(islotInput); +} + +/*---------------------------------------------------------------------------------------------- + Record the chunks in the streams' chunk maps. + + psstrmIn/Out - streams being processed + islotChunkIn/Out - start of chunks + cslotReprocessed - number of slots to reprocess before the rule was run +----------------------------------------------------------------------------------------------*/ +void GrPass::MapChunks(GrSlotStream * psstrmIn, GrSlotStream * psstrmOut, + int islotChunkIn, int islotChunkOut, int cslotReprocessed) +{ + if (islotChunkOut > psstrmOut->WritePos()) + { + // backing up + int islotReadPosTmp = psstrmIn->ReadPosForNextGet(); +/// Assert((islotChunkIn - islotReadPosTmp) == (islotChunkOut - psstrmOut->WritePos())); +// psstrmIn->MapOutputChunk(psstrmOut->WritePos() - 1, islotReadPosTmp - 1, +// islotChunkOut, true, 0, true); +// psstrmOut->MapInputChunk(islotReadPosTmp - 1, psstrmOut->WritePos() - 1, +// islotChunkIn, true, true); + + // Resync. + if (psstrmOut->WritePos() == 0) + { + // Clear all the chunks. + psstrmIn->MapOutputChunk(-1, -1, 0, true, 0, true); + psstrmOut->MapInputChunk(-1, -1, islotReadPosTmp, true, true); + psstrmIn->AssertChunkMapsValid(psstrmOut); + return; + } + else if (islotReadPosTmp == 0) + { + // Clear all the chunks. + psstrmIn->MapOutputChunk(-1, -1, psstrmOut->WritePos(), true, 0, true); + psstrmOut->MapInputChunk(-1, -1, 0, true, true); + psstrmIn->AssertChunkMapsValid(psstrmOut); + return; + } + // Find the beginning of the current chunk. + int islotChunkOutAdj = min(islotChunkOut, psstrmOut->WritePos() - 1); + int islotChunkInAdj = psstrmOut->ChunkInPrev(islotChunkOutAdj); + while (islotChunkInAdj == -1 && islotChunkOutAdj > 0) + islotChunkInAdj = psstrmOut->ChunkInPrev(--islotChunkOutAdj); + + if (islotChunkInAdj == -1) + { + // Couldn't find the beginning of any chunk; zap them all. + psstrmIn->MapOutputChunk(-1, -1, psstrmOut->WritePos(), true, 0, true); + psstrmOut->MapInputChunk(-1, -1, psstrmOut->ReadPos(), true, true); + psstrmIn->AssertChunkMapsValid(psstrmOut); + return; + } + + if (psstrmIn->ChunkInNext(islotChunkInAdj) != islotChunkOutAdj) + { + islotChunkOutAdj = psstrmIn->ChunkInNext(islotChunkInAdj); + while (islotChunkOutAdj == -1 && islotChunkInAdj > 0) + islotChunkOutAdj = psstrmIn->ChunkInNext(--islotChunkInAdj); + } + + psstrmIn->MapOutputChunk(islotChunkOutAdj, islotChunkInAdj, + psstrmOut->WritePos(), false, 0, true); + psstrmOut->MapInputChunk(islotChunkInAdj, islotChunkOutAdj, + psstrmIn->ReadPos(), false, true); + } + else if (islotChunkOut == psstrmOut->WritePos()) + // no output generated--continue the previous chunk + ; + else if (islotChunkIn == psstrmIn->ReadPos()) + // no input consumed + ; + else + { + psstrmIn->MapOutputChunk(islotChunkOut, islotChunkIn, psstrmOut->WritePos(), + (cslotReprocessed > 0), cslotReprocessed, false); + psstrmOut->MapInputChunk(islotChunkIn, islotChunkOut, psstrmIn->ReadPos(), + (cslotReprocessed > 0), false); + } + + psstrmIn->AssertChunkMapsValid(psstrmOut); + + m_pzpst->m_nMaxChunk = max(m_pzpst->m_nMaxChunk, psstrmIn->LastNextChunkLength()); +} + +/*---------------------------------------------------------------------------------------------- + This method is something of a kludge. During the course of running the rules we try to + keep track of the seg-min location, but sometimes that gets confused due to insertions + and deletions at the segment boundaries. +----------------------------------------------------------------------------------------------*/ +void GrSubPass::DoCleanUpSegMin(GrTableManager * ptman, + GrSlotStream * psstrmIn, int islotInitReadPos, GrSlotStream * psstrmOut) +{ + int islotSegMinIn = psstrmIn->SegMin(); + if (islotSegMinIn == -1) + return; // input doesn't even know it + if (islotInitReadPos > islotSegMinIn) + return; // should already have figured it out + + // Otherwise this batch of processing likely set the seg-min on the output stream, + // so check it out. + + // First, if the seg-min of the input stream is zero, it should be zero on the output. + if (islotSegMinIn == 0) + { + psstrmOut->SetSegMin(0, true); + return; + } + + // If there is an initial line-break, the seg-min should be just before it. + int islot; + if (ptman->State()->HasInitialLB()) + { + gid16 chwLB = ptman->LBGlyphID(); + + // Unfortunately, the seg-min from the previous segment can get off, too. :-( + // Fix it, while we're at it. + if (!psstrmIn->SlotAt(islotSegMinIn)->IsInitialLineBreak(chwLB)) + { + for (islot = 0; islot < psstrmIn->ReadPos(); islot++) + if (psstrmIn->SlotAt(islot)->IsInitialLineBreak(chwLB)) + { + psstrmIn->SetSegMin(islot, true); + break; + } + } + if (psstrmOut->SegMin() > -1 + && psstrmOut->SlotAt(psstrmOut->SegMin())->IsInitialLineBreak(chwLB)) + { + return; // already okay + } + for (islot = 0; islot < psstrmOut->WritePos(); islot++) + if (psstrmOut->SlotAt(islot)->IsInitialLineBreak(chwLB)) + { + psstrmOut->SetSegMin(islot, true); + return; + } + Assert(false); // couldn't find it + } + + // Otherwise, figure it out using the associations. First observe that the seg-min + // will be in the corresponding chunk to the seg-min of the previous pass. + int islotChunkMinIn = psstrmIn->ChunkInNextMin(islotSegMinIn); + int islotChunkLimIn = psstrmIn->ChunkInNextLim(islotSegMinIn); + if (islotChunkMinIn == -1) islotChunkMinIn = 0; + if (islotChunkLimIn == -1) islotChunkLimIn = 1; + + int islotChunkMinOut = psstrmIn->ChunkInNext(islotChunkMinIn); + int islotChunkLimOut = psstrmIn->ChunkInNext(islotChunkLimIn); + if (islotChunkMinOut == -1) islotChunkMinOut = 0; + if (islotChunkLimOut == -1) islotChunkLimOut = 1; + + int islotSegMinOut = psstrmOut->SegMin(); + if (islotSegMinOut == -1) + { + // No reasonable guess; try to figure it out. + for (islot = islotChunkMinOut; islot < islotChunkLimOut; islot++) + { +// GrSlotState * pslotOut = psstrmOut->SlotAt(islot); + if (psstrmOut->SlotAt(islot)->BeforeAssoc() == 0) + { + islotSegMinOut = islot; + break; + } + } + if (islotSegMinOut == -1) + { + // Ick, couldn't figure it out. Let's hope we set it to something reasonable earlier. + Assert(psstrmOut->SegMin() > -1); + return; + } + } + // else we have a reasonable guess + + // islotSegMinOut is the best or at leastfirst slot in the chunk that maps into the segment. + // But if there is an adjacent slot inserted before it and it is in the chunk, + // we want to include that also. + while (islotSegMinOut > islotChunkMinOut + && psstrmOut->SlotAt(islotSegMinOut-1)->BeforeAssoc() >= 0) + { + islotSegMinOut--; + } + psstrmOut->SetSegMin(islotSegMinOut, true); +} + +/*---------------------------------------------------------------------------------------------- + If we are in a state where we are supposed to skip some of the initial output, + do so. The purpose of this is to resync when restarting for a segment other than the + first. Caller should ensure that there are enough slots to skip. +----------------------------------------------------------------------------------------------*/ +int PassState::DoResyncSkip(GrSlotStream * psstrmOutput) +{ + if (m_fDidResyncSkip) + return 0; + + if (m_cslotSkipToResync == 0) + { + m_fDidResyncSkip = true; + return 0; + } + + if (!CanResyncSkip(psstrmOutput)) + { + Assert(false); // caller makes sure this doesn't happen + return (m_cslotSkipToResync - psstrmOutput->WritePos()); + } + + psstrmOutput->ResyncSkip(m_cslotSkipToResync); + m_fDidResyncSkip = true; + + return 0; +} + +/*---------------------------------------------------------------------------------------------- + Run a constraint for a single rule. + + @param ruln - rule to test + @param psstrmIn - input stream + @param psstrmOut - for accessing items in the pre-context + @param cslotPreModContext - the number of items that need to be tested prior to the + current stream position + @param cslotMatched - the number of items matched after the current stream position +----------------------------------------------------------------------------------------------*/ +bool GrPass::RunConstraint(GrTableManager * ptman, int ruln, + GrSlotStream * psstrmIn, GrSlotStream * psstrmOut, + int cslotPreModContext, int cslotMatched) +{ + int nRet = 0; + + if (m_prgibConstraintStart == NULL) + return true; + + int biStart = int(m_prgibConstraintStart[ruln]); + if (biStart == 0) + return true; // no constraints + + for (int islot = -cslotPreModContext; islot < cslotMatched; islot++) + { + nRet = RunCommandCode(ptman, m_prgbConstraintBlock + biStart, true, + psstrmIn, psstrmOut, islot); + + if (nRet == 0) // one slot failed + return false; + } + + return (nRet != 0); +} + +/*---------------------------------------------------------------------------------------------- + Check that we can interpret all the commands in the rule. If not, return -1 indicating + that we don't want to run a rule after all. This can happen when the version of the compiler + that generated the font is later than this engine. +----------------------------------------------------------------------------------------------*/ +int GrPass::CheckRuleValidity(int ruln) +{ + if (ruln == -1) + return -1; + + if (m_prgfRuleOkay[ruln]) + return ruln; + + int biStart = m_prgibActionStart[ruln]; + byte * pbNext = m_prgbActionBlock + biStart; + + // General purpose variables: +// int arg1, arg2, arg3, arg4; + int nSlotRef; + int nInputClass; + int nOutputClass; + int c; + int islotArg; + int nIndex; + int nGlyphAttr; + int nAttLevel; + int nFeat; + int nPState; +// SlotAttrName slat; + + int i; + +// int nRet = 0; + + while (true) // exit by encountering PopRet or RetTrue or RetZero + { + ActionCommand op = ActionCommand(*pbNext++); + + switch (op) + { + case kopNop: + break; + + case kopPushByte: + pbNext++; + break; + case kopPushByteU: + pbNext++; + break; + case kopPushShort: + pbNext++; + pbNext++; + break; + case kopPushShortU: + pbNext++; + pbNext++; + break; + case kopPushLong: + pbNext++; + pbNext++; + pbNext++; + pbNext++; + break; + + case kopNeg: + case kopTrunc8: case kopTrunc16: + case kopNot: + break; + + case kopAdd: case kopSub: + case kopMul: case kopDiv: + case kopMin: case kopMax: + case kopAnd: case kopOr: + case kopEqual: case kopNotEq: + case kopLess: case kopGtr: + case kopLessEq: case kopGtrEq: + break; + + case kopCond: + break; + + case kopNext: + break; + case kopNextN: + c = *pbNext; pbNext++; // count + break; + case kopPutGlyph8bitObs: + nOutputClass = *pbNext++; + break; + case kopPutGlyph: + pbNext += 2; + break; + case kopPutCopy: + nSlotRef = *pbNext++; + break; + case kopPutSubs8bitObs: + nSlotRef = *pbNext++; + nInputClass = *pbNext++; + nOutputClass = *pbNext++; + break; + case kopPutSubs: + nSlotRef = *pbNext++; + pbNext += 4; // 2 bytes each for classes + break; + case kopCopyNext: + break; + case kopInsert: + break; + case kopDelete: + break; + case kopAssoc: + c = *pbNext; pbNext++; + for (i = 0; i < c; i++) + pbNext++; + break; + case kopCntxtItem: + islotArg = *pbNext++; + c = *pbNext++; + break; + + case kopAttrSet: + case kopAttrAdd: + case kopAttrSub: + case kopAttrSetSlot: + pbNext++; // slot attribute ID + break; + case kopIAttrSet: + case kopIAttrAdd: + case kopIAttrSub: + case kopIAttrSetSlot: + pbNext++; // slot attribute ID + nIndex = *pbNext++; // index; eg, global ID for component + break; + + case kopPushSlotAttr: + pbNext++; + nSlotRef = *pbNext++; + break; + case kopPushISlotAttr: + pbNext++; + nSlotRef = *pbNext++; + nIndex = *pbNext++; + break; + + case kopPushGlyphAttrObs: + case kopPushAttToGAttrObs: + nGlyphAttr = *pbNext++; + nSlotRef = *pbNext++; + break; + case kopPushGlyphAttr: + case kopPushAttToGlyphAttr: + *pbNext += 3; + break; + case kopPushGlyphMetric: + case kopPushAttToGlyphMetric: + nGlyphAttr = *pbNext++; + nSlotRef = *pbNext++; + nAttLevel = *pbNext++; + break; + case kopPushFeat: + nFeat = *pbNext++; + nSlotRef = *pbNext++; + break; + + case kopPushProcState: + nPState = *pbNext++; + break; + case kopPushVersion: + break; + + case kopPopRet: + case kopRetZero: + case kopRetTrue: + m_prgfRuleOkay[ruln] = true; + return ruln; + + default: + // Uninterpretable command: + return -1; + } + } + + return ruln; // to keep compiler from griping +} + +/*---------------------------------------------------------------------------------------------- + Runs the rule, if any, otherwise just passes one character through. + The rule does nothing but set the line-break weights for the slots. + + @param ptman - the table manager that allocates slots + @param ruln - number of rule to run; -1 if none applies + @param psstrmIn/Out - input/output stream +----------------------------------------------------------------------------------------------*/ +void GrLineBreakPass::RunRule(GrTableManager * ptman, int ruln, + GrSlotStream * psstrmIn, GrSlotStream * psstrmOut) +{ + // Remember the beginnings of the new chunk: + int islotIn = psstrmIn->ReadPos(); + int islotOut = psstrmOut->WritePos(); + int cslotReprocessed = psstrmIn->SlotsToReprocess(); + + if (ruln == -1) + { +#ifdef OLD_TEST_STUFF + if (RunTestRules(ptman, ruln, psstrmIn, psstrmOut)) +#else + if (false) +#endif // OLD_TEST_STUFF + {} + else + // Just pass one glyph through. + { + psstrmOut->CopyOneSlotFrom(psstrmIn); + psstrmOut->SetPosForNextRule(0, psstrmIn, false); + } + } + else + { + // Run the rule. +#ifdef OLD_TEST_STUFF + if (RunTestRules(ptman, ruln, psstrmIn, psstrmOut)) +#else + if (false) +#endif // OLD_TEST_STUFF + {} + else + { + int biStart = m_prgibActionStart[ruln]; + int iResult = RunCommandCode(ptman, m_prgbActionBlock + biStart, false, + psstrmIn, psstrmOut, 0); + psstrmOut->SetPosForNextRule(iResult, psstrmIn, false); + } + } + + CheckInputProgress(psstrmIn, psstrmOut, islotIn); + + MapChunks(psstrmIn, psstrmOut, islotIn, islotOut, cslotReprocessed); +} + +/*---------------------------------------------------------------------------------------------- + Runs the rule, if any, otherwise just passes one character through. + Keeps track of the association information in the slots and chunk + mappings for backtracking. + + @param ptman - the table manager that allocates slots + @param ruln - number of rule to run; -1 if none applies + @param psstrmIn/Out - input / output streams +----------------------------------------------------------------------------------------------*/ +void GrSubPass::RunRule(GrTableManager * ptman, int ruln, + GrSlotStream * psstrmIn, GrSlotStream * psstrmOut) +{ + // Remember the beginnings of the new chunk: + int islotIn = psstrmIn->ReadPosForNextGet(); + int islotOut = psstrmOut->WritePos(); + int cslotReprocessed = psstrmIn->SlotsToReprocess(); + + if (ruln == -1) + { +#ifdef OLD_TEST_STUFF + if (RunTestRules(ptman, ruln, psstrmIn, psstrmOut)) +#else + if (false) +#endif // OLD_TEST_STUFF + {} + else + // Just pass one glyph through. + { + psstrmOut->CopyOneSlotFrom(psstrmIn); + psstrmOut->SetPosForNextRule(0, psstrmIn, false); + } + } + else + { + // Run the rule. +#ifdef OLD_TEST_STUFF + if (RunTestRules(ptman, ruln, psstrmIn, psstrmOut)) +#else + if (false) +#endif // OLD_TEST_STUFF + {} + else + /****************************/ + { + int biStart = m_prgibActionStart[ruln]; + int iResult = RunCommandCode(ptman, m_prgbActionBlock + biStart, false, + psstrmIn, psstrmOut, 0); + psstrmOut->SetPosForNextRule(iResult, psstrmIn, false); + + // Restore if we need line-boundary contextualization based on physical locations + ////psstrmOut->SetLBContextFlag(ptman, islotOut); + } + } + + CheckInputProgress(psstrmIn, psstrmOut, islotIn); + + MapChunks(psstrmIn, psstrmOut, islotIn, islotOut, cslotReprocessed); +} + +/*---------------------------------------------------------------------------------------------- + Runs the rule, if any, otherwise just positions one character in the standard way. + Keeps track of (among other things) the overall physical size of the output + and chunk mappings. + + @param ptman - the table manager that allocates slots + @param ruln - number of rule to run; -1 if none applies + @param psstrmIn / Out - input / output streams +----------------------------------------------------------------------------------------------*/ +void GrPosPass::RunRule(GrTableManager * ptman, int ruln, + GrSlotStream * psstrmIn, GrSlotStream * psstrmOut) +{ + // Remember the beginnings of the new chunk: + int islotIn = psstrmIn->ReadPosForNextGet(); + int islotOut = psstrmOut->WritePos(); + int cslotReprocessed = psstrmIn->SlotsToReprocess(); + + int iResult = 0; + + Assert(psstrmIn->GotIndexOffset()); + // Don't do any positioning in anything being processed from the previous line. + if (psstrmIn->ReadPosForNextGet() < psstrmIn->IndexOffset()) + ruln = -1; + + if (ruln == -1) + { + // Just position one glyph in the standard way. + psstrmOut->CopyOneSlotFrom(psstrmIn); + iResult = 0; + } + else + { + // Run the rule. +#ifdef OLD_TEST_STUFF + if (RunTestRules(ptman, ruln, psstrmIn, psstrmOut)) +#else + if (false) +#endif // OLD_TEST_STUFF + {} + else + { + int biStart = m_prgibActionStart[ruln]; + iResult = RunCommandCode(ptman, m_prgbActionBlock + biStart, false, + psstrmIn, psstrmOut, 0); + } + } + + // Make sure an entire cluster is present in the output stream. Actually there might be + // interleaved clusters, which is the purpose for the loop. + int dislotClusterMax = 0; + do { + dislotClusterMax = psstrmIn->MaxClusterSlot(islotIn, psstrmIn->ReadPos()); + Assert(dislotClusterMax >= 0); + for (int islot = 0; islot < dislotClusterMax; islot++) + psstrmOut->CopyOneSlotFrom(psstrmIn); + iResult -= dislotClusterMax; + } while (dislotClusterMax > 0); + + psstrmOut->SetPosForNextRule(iResult, psstrmIn, true); + + // Restore if we need line-boundary contextualization based on physical locations + ////psstrmOut->SetLBContextFlag(ptman, islotOut); + + if (ruln > -1) + { + psstrmOut->CalcIndexOffset(ptman); + // Update the attachment trees and cluster metrics for slots modified by this rule. + // Note that this loop assumes that the reprocess buffer points to the identical + // slot objects as the output stream (not copies of the slots). + for (int islot = islotIn - psstrmIn->SlotsSkippedToResync(); + islot < psstrmOut->WritePos() + psstrmIn->SlotsToReprocess(); + islot++) + { + psstrmOut->SlotAt(islot)->HandleModifiedPosition(ptman, psstrmIn, psstrmOut, islot); + } + } + + CheckInputProgress(psstrmIn, psstrmOut, islotIn); + + MapChunks(psstrmIn, psstrmOut, islotIn, islotOut, cslotReprocessed); + + psstrmOut->AssertStreamIndicesValid(psstrmIn); +} + +/*---------------------------------------------------------------------------------------------- + Perform the reversal for internal bidirectionality. + Caller is responsible for updating the read- and write-positions. + Private. + + OBSOLETE + + @param nCurrDirection - direction level of range of reverse + @param psstrmIn, psstrmOut - streams + @param islotInMin, islotInLim - range to reverse + @param islotOutMin,islotOutLim - corresponding output positions; note that islotOutMin + will be greater than islotOutLim if we are reversing + reversing the slots (direction is odd) +----------------------------------------------------------------------------------------------*/ +#if 0 +void GrBidiPass::OldReverse(int nCurrDirection, + GrSlotStream * psstrmIn, int islotInMin, int islotInLim, + GrSlotStream * psstrmOut, int islotOutMin, int islotOutLim) +{ + int oInc = (islotOutLim > islotOutMin)? 1: -1; // which direction output is moving in + // buffer (if dir is even, oInc == 1; + // if odd, oInc == -1) + + int islotITmp = islotInMin; + int islotOTmp = islotOutMin; + + while (islotITmp != islotInLim) { + // Find end of sub-range at current level to reverse. + int islotSubLim = psstrmIn->OldDirLevelRange(NULL, islotITmp, nCurrDirection); + Assert(islotSubLim >= 0); + + if (islotSubLim == islotITmp) { + // Current slot is at this level--just copy it. + psstrmOut->SimpleCopyFrom(psstrmIn, islotITmp, islotOTmp); + islotOTmp += oInc; + islotITmp++; + } + else { + // There is a sub-range that needs to be reversed. + int islotONext = islotOTmp + ((islotSubLim-islotITmp) * oInc); + OldReverse(nCurrDirection+1, + psstrmIn, islotITmp, islotSubLim, + psstrmOut, islotONext - oInc, islotOTmp - oInc); + islotOTmp = islotONext; + islotITmp = islotSubLim; + } + } +} +#endif + +/*---------------------------------------------------------------------------------------------- + Perform the reversal for internal bidirectionality. + Caller is responsible for updating the read- and write-positions. + Return the number of slots NOT copied because they were markers. + Private. + + @param psstrmIn, psstrmOut - streams + @param vislotStarts, vislotStops - indices of ranges to reverse, from inner to outer +----------------------------------------------------------------------------------------------*/ +int GrBidiPass::Reverse(GrTableManager * ptman, + GrSlotStream * psstrmIn, GrSlotStream * psstrmOut, + std::vector<int> & vislotStarts, std::vector<int> & vislotStops) +{ + Assert(vislotStarts.size() == vislotStops.size()); + + std::vector<int> vislotMap; // list of slot indices in the order they should be copied + // into the output + + int islotOuterStart = vislotStarts.back(); + int islotOuterStop = vislotStops.back(); + + // Initialize map as if nothing is reversed; eg for [2, 9]: (2 3 4 5 6 7 8 9) + int islot; + for (islot = 0; islot <= (islotOuterStop - islotOuterStart); islot++) + vislotMap.push_back(islot + islotOuterStart); + + // Do the inner reversals first, followed by the outer reversals. + size_t iislot; + for (iislot = 0; iislot < vislotStarts.size(); iislot++) + { + // Reverse the run. + int islotStart = vislotStarts[iislot] - islotOuterStart; + int islotStop = vislotStops[iislot] - islotOuterStart; + int islot1, islot2; + for (islot1 = islotStart, islot2 = islotStop; islot1 < islot2; islot1++, islot2--) + { + int islotTmp = vislotMap[islot1]; + vislotMap[islot1] = vislotMap[islot2]; + vislotMap[islot2] = islotTmp; + } + } + + // With vislotStarts = [7, 5, 2] and vislotStops = [8, 8, 9], we get + // first: (2 3 4 5 6 8 7 9) + // then: (2 3 4 7 8 5 6 9) + // and finally: (9 6 5 8 7 4 3 2) + + // Now copy the slots. + + int islotOutStart = psstrmOut->WritePos(); + int cslotNotCopied = 0; + for (iislot = 0; iislot < vislotMap.size(); iislot++) + { + GrSlotState * pslot = psstrmIn->SlotAt(vislotMap[iislot]); + // Don't copy bidi markers unless they have been set to use a specific glyph. + if (pslot->IsBidiMarker() && pslot->ActualGlyphForOutput(ptman) == 0) + cslotNotCopied++; + else + psstrmOut->SimpleCopyFrom(psstrmIn, vislotMap[iislot], + iislot + islotOutStart - cslotNotCopied); + } + return cslotNotCopied; +} + +/*---------------------------------------------------------------------------------------------- + Unwind the output for this pass, given that a change was assumed to have occurred + in the input at the given position. Return the position to which we unwound. +----------------------------------------------------------------------------------------------*/ +int GrPass::Unwind(GrTableManager * ptman, + int islotChanged, GrSlotStream * psstrmIn, GrSlotStream * psstrmOut, + bool fFirst) +{ + // Back up the number of slots required for the longest rule context, + // but if we land in the middle of a chunk, go forward to its boundary. + int islotIn = max(islotChanged - m_pzpst->MaxChunk(), 0); + if (!psstrmIn->NoReproc()) + islotIn = min(islotIn, psstrmIn->ReprocMin()); + psstrmIn->ClearReprocBuffer(); + int islotOut; + if (islotIn < psstrmIn->SlotsSkippedToResync() || + islotIn == 0 || + psstrmIn->ReadPos() == 0) + { + // The next-chunk-map is invalid. The beginning of the chunk is 0. + islotIn = 0; + islotOut = 0; + } + else if (islotIn >= psstrmIn->ReadPos()) + { + // No need to unwind. + Assert(psstrmIn->NoReproc()); + return psstrmOut->WritePos(); + } + else + { + Assert(islotIn < psstrmIn->ReadPos()); + islotIn = min(islotIn, psstrmIn->ReadPos() - 1); + while (islotIn < psstrmIn->ReadPos() && + (islotOut = psstrmIn->ChunkInNext(islotIn)) == -1) + { + ++islotIn; + } + if (islotIn == psstrmIn->ReadPos()) + islotOut = psstrmOut->WritePos(); + } + + Assert(islotOut != -2); // because the chunk size must be <= than the max-chunk, + // so we should never hit the end of the output stream + + // Now we've found a chunk boundary. + +// if (fFirst) +// Unattach(psstrmIn, islotIn, psstrmOut, islotOut, islotChanged); +// else +// Unattach(psstrmIn, islotIn, psstrmOut, islotOut, -1); + + psstrmIn->UnwindInput(islotIn, PreBidiPass()); + psstrmOut->UnwindOutput(islotOut, IsPosPass()); + + if (psstrmIn->ReadPos() < psstrmIn->SlotsSkippedToResync()) + { + ptman->Pass(m_ipass - 1)->UndoResyncSkip(); + psstrmIn->ClearSlotsSkippedToResync(); + } + if (psstrmOut->WritePos() < psstrmOut->SlotsSkippedToResync()) + { + Assert(psstrmOut->SlotsSkippedToResync() == m_pzpst->m_cslotSkipToResync); + UndoResyncSkip(); + psstrmOut->ClearSlotsSkippedToResync(); + } + + if (ptman->LoggingTransduction()) + m_pzpst->UnwindLogInfo(islotIn, islotOut); + + return islotOut; +} + +/*---------------------------------------------------------------------------------------------- + For a GrBidiPass, just unwind to the beginning of the reordered chunk. +----------------------------------------------------------------------------------------------*/ +int GrBidiPass::Unwind(GrTableManager * ptman, + int islotChanged, GrSlotStream * psstrmIn, GrSlotStream * psstrmOut, + bool fFirst) +{ + int islotIn; + int islotOut; + + if (islotChanged == 0) + { + islotIn = 0; + islotOut = 0; + } + else { + islotIn = min(islotChanged, psstrmIn->ReadPos()); + islotIn = max(islotIn - 1, 0); + islotOut = 0; + while (islotIn > 0 && (islotOut = psstrmIn->ChunkInNext(islotIn)) == -1) + --islotIn; + } + + if (islotOut == -1 || islotOut == -2) + islotOut = 0; + + // Now we've found a chunk boundary; islotI and islotO are the positions in the + // input and output streams, respectively. + + psstrmIn->UnwindInput(islotIn, false); + psstrmOut->UnwindOutput(islotOut, false); + + if (psstrmOut->WritePos() < m_pzpst->m_cslotSkipToResync) + { + Assert(false); // shouldn't be any of this mess for the Bidi pass + m_pzpst->m_fDidResyncSkip = false; + } + + if (ptman->LoggingTransduction()) + m_pzpst->UnwindLogInfo(islotIn, islotOut); + + return islotOut; +} + +/*---------------------------------------------------------------------------------------------- + Reinitialize part of the transduction logging information when we unwind the pass. +----------------------------------------------------------------------------------------------*/ +void PassState::UnwindLogInfo(int islotIn, int islotOut) +{ + while (m_crulrec > 0 && m_rgrulrec[m_crulrec-1].m_islot >= islotIn) + { + m_crulrec--; + m_rgrulrec[m_crulrec].m_islot = 0; + m_rgrulrec[m_crulrec].m_irul = 0; + m_rgrulrec[m_crulrec].m_fFired = false; + } + + for (int islot = islotOut; islot < 128; islot++) + { + m_rgcslotDeletions[islot] = 0; + m_rgfInsertion[islot] = false; + } +} + +/*---------------------------------------------------------------------------------------------- + Undo the side effects of attachments, ie, fix up the attachment trees as we unwind. + + @param islotLB the index of the line-break that was just inserted, or -1 if this is + not the first pass to include line-breaks; we want to skip this slot + in the processing in this method + + OBSOLETE +----------------------------------------------------------------------------------------------*/ +//:Ignore +void GrPosPass::Unattach(GrSlotStream * psstrmIn, int islotIn, + GrSlotStream * psstrmOut, int islotOut, int islotLB) +{ + // Because this is a positioning pass, there is a one-to-one correspondence between + // the slots in the input and the slots in the output. Thus we can make simplifying + // assumptions. Specifically, the chunk sizes are equal, except for the possiblity + // of a LB slot that we just inserted in the input stream. +#if 0 + +#ifdef _DEBUG + int islotDiff = (psstrmIn->ReadPos() - islotIn) - (psstrmOut->WritePos() - islotOut); + if (islotLB == -1) + Assert(islotDiff == 0); + else + Assert(islotDiff == 1); +#endif // _DEBUG + + int islotInLp = psstrmIn->ReadPos(); + int islotOutLp = psstrmOut->WritePos(); + for ( ; islotInLp-- > islotIn && islotOutLp-- > islotOut; ) + { + if (islotLB == islotInLp) + { + GrSlotState * pslotTmp = psstrmIn->SlotAt(islotInLp); + islotInLp--; + } + + GrSlotState * pslotIn = psstrmIn->SlotAt(islotInLp); + GrSlotState * pslotOut = psstrmOut->SlotAt(islotOutLp); + + if (pslotIn != pslotOut) + { + GrSlotState * pslotInRoot = pslotIn->AttachRoot(); + GrSlotState * pslotOutRoot = pslotOut->AttachRoot(); + + if (pslotOutRoot) + pslotOutRoot->RemoveLeaf(pslotOut); + if (pslotInRoot) + pslotInRoot->AddLeaf(pslotIn); + } + } +#endif // 0 +} +//:End Ignore + + +/*---------------------------------------------------------------------------------------------- + Record the fact that the given rule's constraint failed, for the purpose of logging + the transduction process. +----------------------------------------------------------------------------------------------*/ +void GrPass::RecordRuleFailed(int islot, int irul) +{ + m_pzpst->RecordRule(islot, irul, false); +} + + +/*---------------------------------------------------------------------------------------------- + Record the fact that the given rule fired, for the purpose of logging + the transduction process. +----------------------------------------------------------------------------------------------*/ +void GrPass::RecordRuleFired(int islot, int irul) +{ + m_pzpst->RecordRule(islot, irul, true); +} + + +/*---------------------------------------------------------------------------------------------- + Record the fact that we forcibly advanced due to the max-rule-loop. +----------------------------------------------------------------------------------------------*/ +void GrPass::RecordHitMaxRuleLoop(int islot) +{ + m_pzpst->RecordRule(islot, PassState::kHitMaxRuleLoop); +} + + +/*---------------------------------------------------------------------------------------------- + Record the fact that we forcibly advanced due to the max-backup. + + In theory it should be possible to call this, but in reality, it is pretty hard to be + able to say that there is a rule that would have fired except it was prevent by + MaxBackup. So currently this method is not called. +----------------------------------------------------------------------------------------------*/ +void GrPass::RecordHitMaxBackup(int islot) +{ + m_pzpst->RecordRule(islot, PassState::kHitMaxBackup); +} + + +/*---------------------------------------------------------------------------------------------- + Record some rule-related activity on a slot. +----------------------------------------------------------------------------------------------*/ +void PassState::RecordRule(int islot, int irul, bool fFired) +{ + if (m_crulrec >= 128) + return; + + m_rgrulrec[m_crulrec].m_irul = irul; + m_rgrulrec[m_crulrec].m_islot = islot; + m_rgrulrec[m_crulrec].m_fFired = true; + m_rgrulrec[m_crulrec].m_fFired = fFired; + m_crulrec++; +} + + +/*---------------------------------------------------------------------------------------------- + Record the fact that a slot was deleted before the given slot (in the output). +----------------------------------------------------------------------------------------------*/ +void PassState::RecordDeletionBefore(int islot) +{ + if (islot >= 128) + return; + + (m_rgcslotDeletions[islot])++; +} + +/*---------------------------------------------------------------------------------------------- + Record the fact that the given slot was inserted into the output. +----------------------------------------------------------------------------------------------*/ +void PassState::RecordInsertionAt(int islot) +{ + if (islot >= 128) + return; + + m_rgfInsertion[islot] = true; +} + +} // namespace gr diff --git a/Build/source/libs/graphite-engine/src/segment/GrPass.h b/Build/source/libs/graphite-engine/src/segment/GrPass.h new file mode 100644 index 00000000000..9afaf8ac518 --- /dev/null +++ b/Build/source/libs/graphite-engine/src/segment/GrPass.h @@ -0,0 +1,717 @@ +/*--------------------------------------------------------------------*//*:Ignore this sentence. +Copyright (C) 1999, 2001 SIL International. All rights reserved. + +Distributable under the terms of either the Common Public License or the +GNU Lesser General Public License, as specified in the LICENSING.txt file. + +File: GrPass.h +Responsibility: Sharon Correll +Last reviewed: Not yet. + +Description: + The GrPass class and subclasses. +----------------------------------------------------------------------------------------------*/ +#ifdef _MSC_VER +#pragma once +#endif +#ifndef PASS_INCLUDED +#define PASS_INCLUDED + +//:End Ignore + +namespace gr +{ + +/*---------------------------------------------------------------------------------------------- + This class stores the state of processing in a single pass. + + Hungarian: zpst +----------------------------------------------------------------------------------------------*/ + +class PassState { + friend class GrPass; + friend class GrGlyphGenPass; + friend class GrLineBreakPass; + friend class GrSubPass; + friend class GrBidiPass; + friend class GrPosPass; + +public: + PassState() + { + } + + void InitForNewSegment(int ipass, int nMaxChunk); + void InitializeLogInfo(); + + int MaxChunk() + { + return m_nMaxChunk; + } + + void SetResyncSkip(int c) + { + m_cslotSkipToResync = c; + m_fDidResyncSkip = false; + } + int NeededToResync() + { + if (m_fDidResyncSkip) + return 0; + return m_cslotSkipToResync; + } + bool DidResyncSkip() + { + return m_fDidResyncSkip; + } + bool CanResyncSkip(GrSlotStream * psstrmOutput) + { + if (m_fDidResyncSkip) + return true; // already did it + // Are we in the position to do the resync-skip? + return m_cslotSkipToResync <= psstrmOutput->WritePos(); + } + int DoResyncSkip(GrSlotStream * psstrmOutput); + void UndoResyncSkip() + { + m_fDidResyncSkip = false; + } + + void UnwindLogInfo(int islotIn, int islotOut); + void RecordRule(int islot, int irul, bool fFired = false); + void RecordDeletionBefore(int islot); + void RecordInsertionAt(int islot); +#ifdef TRACING + void LogRulesFiredAndFailed(std::ostream & strmOut, GrSlotStream * psstrmIn); + void LogInsertionsAndDeletions(std::ostream & strmOut, GrSlotStream * psstrmOut); +#endif // TRACING + +protected: + int m_ipass; + + // Used to avoid infinite loops. If m_nRulesSinceAdvance > m_nMaxRuleLoop, + // and we have not passed the stream's ReadPosMax, forcibly advance. + int m_nRulesSinceAdvance; + + // Maximum length of chunk, used when unwinding the streams during backtracking. This + // must be initialized to m_nMaxRuleContext, but that is not always adequate, because + // when there is reprocessing going on the chunks may be longer. + int m_nMaxChunk; + + // Indicates how much of the pass's initial output should be automatically + // skipped over in order to resync with a chunk boundary. This handles the situation + // where we are reprocessing a little of the previous segment in order to handle + // cross-line-boundary contextuals. The goal is to skip over the output until we hit + // what we know (from processing the previous segment) is a chunk boundary. + // + // In most cases (and always when this is the first segment of the + // writing system) this number is 0. + int m_cslotSkipToResync; + bool m_fDidResyncSkip; + + enum { + kHitMaxBackup = -1, + kHitMaxRuleLoop = -2 + }; + + // For logging transduction process: record of one rule matched or fired + struct RuleRecord + { + int m_irul; // rule index, or kHitMaxBackup or kHitMaxRuleLoop + int m_islot; // slot index of input stream + bool m_fFired; + }; + RuleRecord m_rgrulrec[128]; + int m_crulrec; + + int m_rgcslotDeletions[128]; // number of deletions before slot i in output + bool m_rgfInsertion[128]; // true if slot i in output was inserted +}; + +/*---------------------------------------------------------------------------------------------- + This class handles finding and applying rules from the pass and storing + the results in the appropriate stream. + + Hungarian: pass +----------------------------------------------------------------------------------------------*/ +class GrPass { + +protected: + // Action command codes; these MUST match the corresponding definitions in the compiler: + enum ActionCommand { + kopNop = 0, + + kopPushByte, kopPushByteU, kopPushShort, kopPushShortU, kopPushLong, + + kopAdd, kopSub, kopMul, kopDiv, + kopMin, kopMax, + kopNeg, + kopTrunc8, kopTrunc16, + + kopCond, + + kopAnd, kopOr, kopNot, + kopEqual, kopNotEq, + kopLess, kopGtr, kopLessEq, kopGtrEq, + + kopNext, kopNextN, kopCopyNext, + kopPutGlyph8bitObs, kopPutSubs8bitObs, kopPutCopy, + kopInsert, kopDelete, + kopAssoc, + kopCntxtItem, + + kopAttrSet, kopAttrAdd, kopAttrSub, + kopAttrSetSlot, + kopIAttrSetSlot, + kopPushSlotAttr, kopPushGlyphAttrObs,kopPushGlyphMetric, kopPushFeat, + kopPushAttToGAttrObs, kopPushAttToGlyphMetric, + kopPushISlotAttr, + + kopPushIGlyphAttr, // not implemented + + kopPopRet, kopRetZero, kopRetTrue, + kopIAttrSet, kopIAttrAdd, kopIAttrSub, + kopPushProcState, kopPushVersion, + kopPutSubs, kopPutSubs2, kopPutSubs3, + kopPutGlyph, kopPushGlyphAttr, kopPushAttToGlyphAttr + + }; + + enum StackMachineFlag { + ksmfDone = 0, + ksmfContinue, + ksmfUnderflow, + ksmfStackNotEmptied + }; + +public: + // Constructor: + GrPass(int i); + // Destructor: + virtual ~GrPass(); + + bool ReadFromFont(GrIStream & grstrm, int fxdSilfVersion, int fxdRuleVersion, int nOffset); + void InitializeWithNoRules(); + + virtual void SetTopDirLevel(int n) + { // only GrBidiPass does anything interesting + } + + void SetPassState(PassState * pzpst) + { + m_pzpst = pzpst; + } + + virtual void ExtendOutput(GrTableManager *, + GrSlotStream * psstrmInput, GrSlotStream * psstrmOutput, int nslotToGet, + TrWsHandling twsh, + int * pnRet, int * pcslotGot, int * pislotFinalBreak); + + int ExtendGlyphIDOutput(GrTableManager *, GrCharStream *, + GrSlotStream *, int ichSegLim, int cchwPostXlbContext, + LineBrk lb, int cslotToGet, bool fNeedFinalBreak, + TrWsHandling twsh, int * pislotFinalBreak); + + int ExtendFinalOutput(GrTableManager *, GrSlotStream * psstrmInput, + GrSlotStream * psstrmOutput, + float xsSpaceAllotted, bool fWidthIsCharCount, bool fInfiniteWidth, + bool fHaveLineBreak, bool fMustBacktrack, + LineBrk lbMax, TrWsHandling twsh, + int * pislotLB, float * pxsWidth); + + int RemoveTrailingWhiteSpace(GrTableManager * ptman, GrSlotStream * psstrmOut, + TrWsHandling twsh, int * pislotFinalBreak); + + virtual int Unwind(GrTableManager * ptman, + int islotChanged, GrSlotStream *psstrmInput, GrSlotStream * psstrmOutput, + bool fFirst); + + int MaxBackup() + { + return m_nMaxBackup; + } + + bool DidResyncSkip() + { + return m_pzpst->DidResyncSkip(); + } + int DoResyncSkip(GrSlotStream * psstrmOutput) + { + return m_pzpst->DoResyncSkip(psstrmOutput); + } + + void UndoResyncSkip() + { + m_pzpst->UndoResyncSkip(); + } + void SetResyncSkip(int n) + { + m_pzpst->SetResyncSkip(n); + } + + virtual void DoCleanUpSegMin(GrTableManager * ptman, + GrSlotStream * psstrmIn, int islotInitReadPos, GrSlotStream * psstrmOut) + { + } + + virtual bool IsPosPass() + { + return false; + } + + virtual bool PreBidiPass() = 0; + + bool RunConstraint(GrTableManager *, int ruln, + GrSlotStream * psstrmIn, GrSlotStream * psstrmOut, + int cslotPreModContext, int cslotMatched); + + int RunCommandCode(GrTableManager * ptman, + byte * pbStart, bool fConstraints, + GrSlotStream * psstrmIn, GrSlotStream * psstrmOut, int islot); + +protected: + int RunOneCommand(GrTableManager * ptman, bool fConstraints, + ActionCommand op, byte ** ppbArg, bool * pfMustGet, bool * pfInserting, + GrSlotStream * psstrmIn, GrSlotStream * psstrmOut, int islot, + std::vector<int> & vnStack, StackMachineFlag * psmf); + void DoStackArithmetic2Args(ActionCommand op, std::vector<int> & vnStack, + StackMachineFlag * psmf); + void DoStackArithmetic1Arg(ActionCommand op, std::vector<int> & vnStack, + StackMachineFlag * psmf); + void DoConditional(std::vector<int> & vnStack, StackMachineFlag * psmf); + StackMachineFlag CheckStack(std::vector<int> & vnStack, int cn); + void DoNext(GrTableManager * ptman, + int cslot, GrSlotStream * psstrmIn, GrSlotStream * psstrmOut); + void DoPutGlyph(GrTableManager * ptman, bool fInserting, int nReplacementClass, + GrSlotStream * psstrmIn, GrSlotStream * psstrmOut); + void DoPutCopy(GrTableManager * ptman, bool fInserting, int cslotCopyFrom, + GrSlotStream * psstrmIn, GrSlotStream * psstrmOut); + void DoPutSubs(GrTableManager * ptman, bool fInserting, + int cslotSel, int nSelClass, int nReplacementClass, + GrSlotStream * psstrmIn, GrSlotStream * psstrmOut); + void DoPutSubs2(GrTableManager * ptman, bool fInserting, + int cslotSel1, int nSelClass1, int cslotSel2, int nSelClass2, int nReplacementClass, + GrSlotStream * psstrmIn, GrSlotStream * psstrmOut); + void DoPutSubs3(GrTableManager * ptman, bool fInserting, + int cslotSel1, int nSelClass1, int cslotSel2, int nSelClass2, int cslotSel3, int nSelClass3, + int nReplacementClass, GrSlotStream * psstrmIn, GrSlotStream * psstrmOut); + void DoPutSubsInit(GrSlotStream * psstrmIn, GrSlotStream * psstrmOut, bool fInserting, + GrSlotState ** ppslotNextPut, bool * pfAtSegMin, bool * pfAtSegLim); + void DoPutSubsAux(GrTableManager * ptman, bool fInserting, gid16 nGlyphReplacement, + GrSlotStream * psstrmIn, GrSlotStream * psstrmOut, GrSlotState * pslotNextInput, + bool fAtSegMin, bool fAtSegLim); + void SetNeutralAssocs(GrSlotState * pslotNew, GrSlotStream * psstrmIn); + void DoDelete(GrTableManager * ptman, + GrSlotStream * psstrmIn, GrSlotStream * psstrmOut); + void DoAssoc(int cnAssocs, std::vector<int> & vnAssocs, bool fInserting, + GrSlotStream * psstrmIn, GrSlotStream * psstrmOut); + void DoSetAttr(GrTableManager * ptman, + ActionCommand op, bool fInserrting, + SlotAttrName slat, int slati, std::vector<int> & vnStack, + GrSlotStream * psstrmIn, GrSlotStream * psstrmOut); + void DoPushSlotAttr(GrTableManager * ptman, + int nSlotRef, bool fInserting, + SlotAttrName slat, int slati, std::vector<int> & vnStack, + GrSlotStream * psstrmIn, GrSlotStream * psstrmOut); + void DoPushGlyphAttr(GrTableManager * ptman, int nSlotRef, bool fInserting, + int nGlyphAttr, std::vector<int> & vnStack, + GrSlotStream * psstrmIn, GrSlotStream * psstrmOut); + void DoPushAttToGlyphAttr(GrTableManager * ptman, int nSlotRef, bool fInserting, + int nGlyphAttr, std::vector<int> & vnStack, + GrSlotStream * psstrmIn, GrSlotStream * psstrmOut); + void DoPushGlyphMetric(GrTableManager * ptman, int nSlotRef, bool fInserting, + int nGlyphAttr, int nAttLevel, std::vector<int> & vnStack, + GrSlotStream * psstrmIn, GrSlotStream * psstrmOut); + void DoPushAttToGlyphMetric(GrTableManager * ptman, int nSlotRef, bool fInserting, + int nGlyphAttr, int nAttLevel, std::vector<int> & vnStack, + GrSlotStream * psstrmIn, GrSlotStream * psstrmOut); + void DoPushFeatValue(GrTableManager * ptman, int islot, bool fInsering, + int nFeat, std::vector<int> & vnStack, + GrSlotStream * psstrmIn, GrSlotStream * psstrmOut); + void DoPushProcState(GrTableManager * ptman, int nPState, std::vector<int> & vnStack); +protected: + void DoPushGlyphMetricAux(GrTableManager * ptman, + GrSlotState * pslot, int nGlyphAttr, int nAttLevel, + std::vector<int> & vnStack, GrSlotStream * psstrmIn); +public: + int SortKeyForRule(int ruln) + { + Assert(ruln < m_crul); + return m_prgchwRuleSortKeys[ruln]; + } + int PreModContextForRule(int ruln) + { + Assert(ruln < m_crul); + return m_prgcritRulePreModContext[ruln]; + } + + int MaxRulePreContext() + { + if (m_pfsm) + return m_pfsm->MaxRulePreContext(); + else + return 0; + } + + void RecordRuleFailed(int islot, int irul); + void RecordRuleFired(int islot, int irul); + void RecordHitMaxRuleLoop(int islot); + void RecordHitMaxBackup(int islot); +#ifdef TRACING + void LogRulesFiredAndFailed(std::ostream & strmOut, GrSlotStream * psstrmIn); + void LogInsertionsAndDeletions(std::ostream & strmOut, GrSlotStream * psstrmOut); +#endif // TRACING + +protected: + int CheckRuleValidity(int ruln); + + virtual void RunRule(GrTableManager *, int ruln, + GrSlotStream * psstrmInput, GrSlotStream * psstrmOutput) = 0; + + void CheckInputProgress(GrSlotStream* input, GrSlotStream* psstrmOutput, + int islotOrigInput); + + void MapChunks(GrSlotStream * psstrmIn, GrSlotStream * psstrmOut, + int islotChunkI, int islotChunkO, int cslotReprocessed); + + virtual void Unattach(GrSlotStream * psstrmIn, int islotIn, // GrPosPass overrides + GrSlotStream * psstrmOut, int islotOut, int islotLB) + { + } + + friend class EngineState; // let it call the method below + virtual int MaxRuleContext() // GrBidiPass overrides + { + return m_nMaxRuleContext; + } + +protected: + // Instance variables: + + int m_ipass; // index of pass + + int m_fxdVersion; + + std::vector<int> vnStack; + // number of items required from previous pass; don't access directly, use the getter + // method, because GrBidiPass overrides to always use 1. + int m_nMaxRuleContext; + + GrFSM * m_pfsm; // Finite State Machine to give next rule + + int m_nMaxRuleLoop; // maximum number of rules to process before forcibly + // advancing input position + + int m_nMaxBackup; + + int m_crul; // number of rules + + // rule sort keys, indicating precedence of rules; m_crul of these + data16 * m_prgchwRuleSortKeys; + + // for each rule, the number of items in the context before the first modified item + // that the constraints need to be tested on + byte * m_prgcritRulePreModContext; + + // offset for pass-level constraints; just 1 of these + data16 m_cbPassConstraint; + // offsets for constraint and action code; m_crul+1 of each of these (the last + // indicates the total byte count) + data16 * m_prgibConstraintStart; + data16 * m_prgibActionStart; + + // blocks of constraint and action code + byte * m_prgbPConstraintBlock; // pass-level constraints + byte * m_prgbConstraintBlock; // rule constraints + byte * m_prgbActionBlock; + + bool m_fHasDebugStrings; + data16 * m_prgibConstraintDebug; // m_crul+1 of these + data16 * m_prgibRuleDebug; // m_crul+1 of these + + bool m_fCheckRules; + bool * m_prgfRuleOkay; + + // state of process for this pass + PassState * m_pzpst; + +public: +#ifdef OLD_TEST_STUFF + // For test procedures: + StrAnsi m_staBehavior; + + virtual void SetUpTestData(); + + // overridden on appropriate subclasses: + virtual void SetUpReverseNumbersTest() { Assert(false); } + virtual void SetUpBidiNumbersTest() { Assert(false); } + + virtual void SetUpCrossLineContextTest() { Assert(false); } + virtual void SetUpReprocessTest() { Assert(false); } + virtual void SetUpLineEdgeContextTest(int ipass) { Assert(false); } + virtual void SetUpBidiAlgorithmTest() { Assert(false); } + virtual void SetUpPseudoGlyphsTest() { Assert(false); } + virtual void SetUpSimpleFSMTest() { Assert(false); } + virtual void SetUpRuleActionTest() { Assert(false); } + virtual void SetUpRuleAction2Test() { Assert(false); } + virtual void SetUpAssocTest() { Assert(false); } + virtual void SetUpAssoc2Test() { Assert(false); } + virtual void SetUpDefaultAssocTest() { Assert(false); } + virtual void SetUpFeatureTest() { Assert(false); } + virtual void SetUpLigatureTest() { Assert(false); } + virtual void SetUpLigature2Test() { Assert(false); } +#endif // OLD_TEST_STUFF + +}; // end of class GrPass + + +/*---------------------------------------------------------------------------------------------- + The initial pass that generates glyph IDs from Unicode input. + + Hungarian: pass +----------------------------------------------------------------------------------------------*/ +class GrGlyphGenPass : public GrPass { +public: + GrGlyphGenPass(int ipass) : GrPass(ipass) + { + } + + virtual bool PreBidiPass() + { + return true; + } + +protected: + // Irrelevant when generating glyphs. + virtual void RunRule(GrTableManager *, int ruln, + GrSlotStream * psstrmInput, GrSlotStream * psstrmOutput) + { + Assert(false); + } + +}; // end of class GrGlyphGenPass + + +/*---------------------------------------------------------------------------------------------- + A pass containing rules that set the break weight values. + + Hungarian: pass +----------------------------------------------------------------------------------------------*/ +class GrLineBreakPass : public GrPass { +public: + GrLineBreakPass(int ipass) : GrPass(ipass) + { + } + + virtual bool PreBidiPass() + { + return true; + } + +protected: + virtual void RunRule(GrTableManager *, int ruln, + GrSlotStream * psstrmInput, GrSlotStream * psstrmOutput); + +public: +#ifdef OLD_TEST_STUFF + // For test procedures: + bool RunTestRules(GrTableManager *, GrSlotStream * psstrmIn, GrSlotStream * psstrmOut); + bool RunTestRules(GrTableManager *, int ruln, GrSlotStream * psstrmIn, + GrSlotStream * psstrmOut); + + virtual void SetUpCrossLineContextTest(); + bool RunCrossLineContextTest(GrTableManager*, GrSlotStream* psstrmIn, + GrSlotStream* psstrmOut); + + virtual void SetUpReprocessTest(); + bool RunReprocessTest(GrTableManager *, GrSlotStream * psstrmIn, GrSlotStream * psstrmOut); + + virtual void SetUpAssocTest(); + virtual void SetUpLigatureTest(); +#endif // OLD_TEST_STUFF + +}; // end of class GrLineBreakPass + + +/*---------------------------------------------------------------------------------------------- + A pass containing substitution rules. + + Hungarian: pass +----------------------------------------------------------------------------------------------*/ +class GrSubPass : public GrPass { +public: + GrSubPass(int ipass) : GrPass(ipass) + { + } + + virtual bool PreBidiPass() + { + return true; + } + +protected: + virtual void RunRule(GrTableManager *, int ruln, + GrSlotStream * psstrmInput, GrSlotStream * psstrmOutput); + + virtual void DoCleanUpSegMin(GrTableManager * ptman, + GrSlotStream * psstrmIn, int islotInitReadPos, GrSlotStream * psstrmOut); + +public: +#ifdef OLD_TEST_STUFF + // For test procedures: + bool RunTestRules(GrTableManager *, GrSlotStream * psstrmIn, + GrSlotStream * psstrmOut); + bool RunTestRules(GrTableManager *, int ruln, GrSlotStream * psstrmIn, + GrSlotStream * psstrmOut); + + virtual void SetUpReverseNumbersTest(); + bool RunReverseNumbersTest(GrTableManager*, GrSlotStream* psstrmIn, + GrSlotStream* psstrmOut); + + virtual void SetUpBidiNumbersTest(); + bool RunBidiNumbersTest(GrTableManager*, GrSlotStream* psstrmIn, + GrSlotStream* psstrmOut); + + virtual void SetUpCrossLineContextTest(); + bool RunCrossLineContextTest(GrTableManager*, GrSlotStream* psstrmIn, + GrSlotStream* psstrmOut); + + virtual void SetUpReprocessTest(); + bool RunReprocessTest(GrTableManager *, GrSlotStream * psstrmIn, + GrSlotStream * psstrmOut); + + virtual void SetUpLineEdgeContextTest(int ipass); + bool RunLineEdgeContextTest(GrTableManager *, GrSlotStream * psstrmIn, + GrSlotStream * psstrmOut); + + virtual void SetUpBidiAlgorithmTest(); + bool RunBidiAlgorithmTest(GrTableManager *, GrSlotStream * psstrmIn, + GrSlotStream * psstrmOut); + + virtual void SetUpPseudoGlyphsTest(); + bool RunPseudoGlyphsTest(GrTableManager *, GrSlotStream * psstrmIn, + GrSlotStream * psstrmOut); + + virtual void SetUpSimpleFSMTest(); + bool RunSimpleFSMTest(GrTableManager *, int ruln, GrSlotStream * psstrmIn, + GrSlotStream * psstrmOut); + virtual void SetUpRuleActionTest(); + virtual void SetUpRuleAction2Test(); + virtual void SetUpAssocTest(); + virtual void SetUpAssoc2Test(); + virtual void SetUpDefaultAssocTest(); + virtual void SetUpFeatureTest(); + virtual void SetUpLigatureTest(); + virtual void SetUpLigature2Test(); +#endif // OLD_TEST_STUFF + +}; // end of class GrSubPass + + +/*---------------------------------------------------------------------------------------------- + This class is the one the knows how to handle bidi reordering, including backtracking. + + Hungarian: pass +----------------------------------------------------------------------------------------------*/ +class GrBidiPass : public GrSubPass +{ +public: + // Constructor: + GrBidiPass(int ipass) + : GrSubPass(ipass), + m_nTopDirection(0) + { + } + + virtual void SetTopDirLevel(int n) + { + m_nTopDirection = n; + } + + virtual bool PreBidiPass() + { + return false; + } + + virtual void ExtendOutput(GrTableManager *, + GrSlotStream * psstrmI, GrSlotStream * psstrmO, int nslotToGet, + TrWsHandling twsh, + int * pnRet, int * pcslotGot, int * pislotFinalBreak); + + virtual int Unwind(GrTableManager * ptman, + int islotChanged, GrSlotStream * psstrmIn, GrSlotStream * psstrmOut, + bool fFirst); +protected: + virtual int MaxRuleContext() + { + return 1; + } + +private: + //void OldReverse(int nCurrDirection, + // GrSlotStream * psstrmI, int islotI1, int islotI2, + // GrSlotStream * psstrmO, int islotO1, int islotO2); + + int Reverse(GrTableManager * ptman, + GrSlotStream * psstrmIn, GrSlotStream * psstrmOut, + std::vector<int> & vislotStarts, std::vector<int> & vislotStops); + +protected: + // Instance variables: + int m_nTopDirection; // 0 for LTR, 1 for RTL -- CURRENTLY NOT USED; need to + // initialize it when we set the writing system direction + +#ifdef OLD_TEST_STUFF +public: + // For test procedures: + ////virtual void SetUpTestData(); + virtual void SetUpBidiNumbersTest(); + virtual void SetUpBidiAlgorithmTest(); +#endif // OLD_TEST_STUFF + +}; // end of class GrBidiPass + + +/*---------------------------------------------------------------------------------------------- + A pass containing positioning rules. + + Hungarian: pass +----------------------------------------------------------------------------------------------*/ +class GrPosPass : public GrPass { +public: + GrPosPass(int ipass) : GrPass(ipass) + { + } + + virtual bool IsPosPass() + { + return true; + } + + virtual bool PreBidiPass() + { + return false; + } + +protected: + virtual void RunRule(GrTableManager *, int ruln, + GrSlotStream * psstrmInput, GrSlotStream * psstrmOutput); + + virtual void Unattach(GrSlotStream * psstrmIn, int islotIn, + GrSlotStream * psstrmOut, int islotOut, int islotLB); + +public: +#ifdef OLD_TEST_STUFF + // For test procedures: + bool RunTestRules(GrTableManager *, GrSlotStream * psstrmIn, + GrSlotStream * psstrmOut); + bool RunTestRules(GrTableManager *, int ruln, GrSlotStream * psstrmIn, + GrSlotStream * psstrmOut); +#endif // OLD_TEST_STUFF + +}; // end of class GrPosPass + +} // namespace gr + + +#endif // PASS_INCLUDED diff --git a/Build/source/libs/graphite-engine/src/segment/GrPassActionCode.cpp b/Build/source/libs/graphite-engine/src/segment/GrPassActionCode.cpp new file mode 100644 index 00000000000..7c85aabf398 --- /dev/null +++ b/Build/source/libs/graphite-engine/src/segment/GrPassActionCode.cpp @@ -0,0 +1,1489 @@ +/*--------------------------------------------------------------------*//*:Ignore this sentence. +Copyright (C) 1999, 2001 SIL International. All rights reserved. + +Distributable under the terms of either the Common Public License or the +GNU Lesser General Public License, as specified in the LICENSING.txt file. + +File: GrPassActionCode.cpp +Responsibility: Sharon Correll +Last reviewed: Not yet. + +Description: + A continution of the GrPass file, including functions to run the action commands. +----------------------------------------------------------------------------------------------*/ + +//:>******************************************************************************************** +//:> Include files +//:>******************************************************************************************** +#include "Main.h" + +#ifdef _MSC_VER +#pragma hdrstop +#endif +#undef THIS_FILE +DEFINE_THIS_FILE + +//:End Ignore + +//:>******************************************************************************************** +//:> Forward declarations +//:>******************************************************************************************** + +//:>******************************************************************************************** +//:> Local type definitions +//:>******************************************************************************************** + +typedef signed char Int8; // signed value +typedef unsigned char Uint8; // unsigned value + + +namespace gr +{ + +//:>******************************************************************************************** +//:> Methods +//:>******************************************************************************************** + +/*---------------------------------------------------------------------------------------------- + The main function to run a block of action or constraint code and return a result. + + @param ptman - table manager, for generating new slots; NULL when + running constraints + @param pbStart - address of the first command + @param fConstraints - true if we are running constraint tests; + false if we are running rule actions + @param psstrmIn - input stream + @param psstrmOut - output stream; when running constraints, used to access items + in the precontext + @param islot - slot being processed relative to the start of the rule; + used only for constraints; must be 0 for rule actions +----------------------------------------------------------------------------------------------*/ +int GrPass::RunCommandCode(GrTableManager * ptman, + byte * pbStart, bool fConstraints, + GrSlotStream * psstrmIn, GrSlotStream * psstrmOut, int islot) +{ + byte * pbNext = pbStart; + + std::vector<int> vnStack; +// vnStack.EnsureSpace(128); // make it nice and big + + gAssert(fConstraints || islot == 0); + + // There are two states: fMustGet = true means we need to get a slot to work on from + // the input; fMustGet = false means we have a slot and we need to do a Next before + // we can get the next one. + bool fMustGet = !fConstraints; + + bool fInserting = false; // set to true by an Insert command + + int nRet = 0; + + while (true) + { + ActionCommand op = ActionCommand(*pbNext++); + StackMachineFlag smf; + nRet = RunOneCommand(ptman, fConstraints, op, + &pbNext, &fMustGet, &fInserting, + psstrmIn, psstrmOut, islot, + vnStack, &smf); + + if (smf == ksmfDone) + return nRet; + + if (smf == ksmfUnderflow) + { + Warn("Underflow in Graphite stack machine"); + gAssert(false); + FontException fexptn; + fexptn.version = -1; + fexptn.subVersion = -1; + fexptn.errorCode = kferrUnknown; + throw fexptn; // disastrous error in rendering; fall back to dumb rendering + } + } + return nRet; // not needed, but to avoid compiler warning +} + +/*---------------------------------------------------------------------------------------------- + Perform a single command. Specifically, read the arguments from the byte buffer and the + run the appropriate functions. + + @param ptman - table manager, for generating new slots; or NULL + @param fConstraints - true if we are running constraints; false if we are running + rule actions + @param op - command operator + @param ppbArg - pointer to first argument + @param pfMustGet - state: getting input or processing it; when running constraints, + always false + @param pfInserting - true if current item is to be inserted, or was inserted + @param psstrmIn - input stream + @param psstrmOut - output stream + @param islot - the slot being processed relative to the beginning of the rule; + only relevant for constraint testing, must be 0 for rule actions + @param vnStack - the stack of values being manipulated + @param psmf - error code, done flag, or continue flag +----------------------------------------------------------------------------------------------*/ +int GrPass::RunOneCommand(GrTableManager * ptman, bool fConstraints, + ActionCommand op, byte ** ppbArg, bool * pfMustGet, bool * pfInserting, + GrSlotStream * psstrmIn, GrSlotStream * psstrmOut, int islot, + std::vector<int> & vnStack, StackMachineFlag * psmf) +{ + *psmf = ksmfContinue; + + // General purpose variables: + int arg1, arg2, arg3, arg4; + int nSlotRef; + int nInputClass; + int nOutputClass; + int c; + int islotArg; + int nIndex; + int nGlyphAttr; + int nAttLevel; + int nFeat; + int nPState; + SlotAttrName slat; + + int i; + std::vector<int> vnTmp; + + byte * pbNext = *ppbArg; + + int nRet = 0; + + switch (op) + { + case kopNop: + break; + + case kopPushByte: + arg1 = Int8(*pbNext++); + vnStack.push_back(arg1); + break; + case kopPushByteU: + arg1 = Uint8(*pbNext++); + vnStack.push_back(arg1); + break; + case kopPushShort: + arg1 = Int8(*pbNext++); + arg2 = Uint8(*pbNext++); + vnStack.push_back((arg1 << 8) + arg2); + break; + case kopPushShortU: + arg1 = Uint8(*pbNext++); + arg2 = Uint8(*pbNext++); + vnStack.push_back((arg1 << 8) + arg2); + break; + case kopPushLong: + arg1 = Int8(*pbNext++); + arg2 = Uint8(*pbNext++); + arg3 = Uint8(*pbNext++); + arg4 = Uint8(*pbNext++); + vnStack.push_back((arg1 << 24) + (arg2 << 16) + (arg3 << 8) + arg4); + break; + + case kopNeg: + case kopTrunc8: case kopTrunc16: + case kopNot: + DoStackArithmetic1Arg(op, vnStack, psmf); + if (*psmf != ksmfContinue) + return 0; + break; + + case kopAdd: case kopSub: + case kopMul: case kopDiv: + case kopMin: case kopMax: + case kopAnd: case kopOr: + case kopEqual: case kopNotEq: + case kopLess: case kopGtr: + case kopLessEq: case kopGtrEq: + DoStackArithmetic2Args(op, vnStack, psmf); + if (*psmf != ksmfContinue) + return 0; + break; + + case kopCond: + DoConditional(vnStack, psmf); + if (*psmf != ksmfContinue) + return 0; + break; + + case kopNext: + if (fConstraints) + { + gAssert(false); + } + else + { + gAssert(!*pfMustGet); + DoNext(ptman, 1, psstrmIn, psstrmOut); + *pfMustGet = true; + *pfInserting = false; + } + break; + case kopNextN: + c = Int8(*pbNext++); + if (fConstraints) + { + gAssert(false); + } + else + { + gAssert(false); // for now we don't allow anything other than moving one slot + gAssert(!*pfMustGet); // forward at a time + DoNext(ptman, c, psstrmIn, psstrmOut); + *pfMustGet = true; + } + break; + case kopPutGlyph8bitObs: + case kopPutGlyph: + if (op == kopPutGlyph8bitObs) + nOutputClass = Uint8(*pbNext++); + else + { + nOutputClass = (Uint8(*pbNext++) << 8); + nOutputClass += Uint8(*pbNext++); + } + if (fConstraints) + { + gAssert(false); + } + else + { + gAssert(*pfMustGet); + DoPutGlyph(ptman, *pfInserting, nOutputClass, psstrmIn, psstrmOut); + *pfMustGet = false; + } + break; + case kopPutCopy: + nSlotRef = Int8(*pbNext++); + if (fConstraints) + { + gAssert(false); + } + else + { + gAssert(*pfMustGet); + DoPutCopy(ptman, *pfInserting, nSlotRef, psstrmIn, psstrmOut); + *pfMustGet = false; + } + break; + case kopPutSubs8bitObs: + case kopPutSubs: + nSlotRef = Int8(*pbNext++); + if (op == kopPutSubs8bitObs) + { + nInputClass = Uint8(*pbNext++); + nOutputClass = Uint8(*pbNext++); + } + else + { + nInputClass = (Uint8(*pbNext++) << 8); + nInputClass += Uint8(*pbNext++); + nOutputClass = (Uint8(*pbNext++) << 8); + nOutputClass += Uint8(*pbNext++); + } + if (fConstraints) + { + gAssert(false); + } + else + { + gAssert(*pfMustGet); + DoPutSubs(ptman, *pfInserting, nSlotRef, nInputClass, nOutputClass, + psstrmIn, psstrmOut); + *pfMustGet = false; + } + break; + case kopCopyNext: + if (fConstraints) + { + gAssert(false); + } + else + { + gAssert(*pfMustGet); + gAssert(!*pfInserting); // 0 means copy this slot + DoPutCopy(ptman, *pfInserting, 0, psstrmIn, psstrmOut); + DoNext(ptman, 1, psstrmIn, psstrmOut); + // *pfMustGet still = true + } + break; + case kopInsert: + if (fConstraints) + { + gAssert(false); + } + else + { + gAssert(*pfMustGet); + *pfInserting = true; + // *pfMustGet still = true + + if (ptman->LoggingTransduction()) + m_pzpst->RecordInsertionAt(psstrmOut->WritePos()); + } + break; + case kopDelete: + if (fConstraints) + { + gAssert(false); + } + else + { + gAssert(*pfMustGet); + DoDelete(ptman, psstrmIn, psstrmOut); + *pfMustGet = false; + } + break; + case kopAssoc: + c = Uint8(*pbNext++); + // std::vector<int> vnTmp; + for (i = 0; i < c; i++) + vnTmp.push_back(Int8(*pbNext++)); + if (fConstraints) + { + gAssert(false); + } + else + { + gAssert(!*pfMustGet); + DoAssoc(c, vnTmp, *pfInserting, psstrmIn, psstrmOut); + } + break; + case kopCntxtItem: + islotArg = Int8(*pbNext++); + c = Uint8(*pbNext++); + if (fConstraints) + { + // Old approach: + // If this is NOT the relevant item, push TRUE, which causes the subsequent tests + // (which are eventually ORed with it) to become irrelevant. + //vnStack.Push((int)(islotArg != islot)); + + // If this is not the relevant item, skip the specified number of bytes. + if (islotArg != islot) + { + pbNext += c; + vnStack.push_back(1); + } + } + else + { + gAssert(false); + } + break; + + case kopAttrSet: + case kopAttrAdd: + case kopAttrSub: + case kopAttrSetSlot: + slat = SlotAttrName(Uint8(*pbNext++)); // slot attribute ID + if (fConstraints) + { + gAssert(false); + } + else + { + gAssert(!*pfMustGet); + DoSetAttr(ptman, op, *pfInserting, slat, -1, vnStack, psstrmIn, psstrmOut); + if (*psmf != ksmfContinue) + return 0; + } + break; + case kopIAttrSet: + case kopIAttrAdd: + case kopIAttrSub: + case kopIAttrSetSlot: + slat = SlotAttrName(Uint8(*pbNext++)); // slot attribute ID + nIndex = Uint8(*pbNext++); // index; eg, global ID for component + if (fConstraints) + { + gAssert(false); + } + else + { + gAssert(!*pfMustGet); + DoSetAttr(ptman, op, *pfInserting, slat, nIndex, vnStack, + psstrmIn, psstrmOut); + if (*psmf != ksmfContinue) + return 0; + } + break; + + case kopPushSlotAttr: + slat = SlotAttrName(Uint8(*pbNext++)); + nSlotRef = Int8(*pbNext++); + if (fConstraints) + nSlotRef += islot + 1; // +1 to peek ahead to a slot we haven't "got" yet + DoPushSlotAttr(ptman, nSlotRef, *pfInserting, slat, -1, vnStack, psstrmIn, psstrmOut); + if (*psmf != ksmfContinue) + return 0; + break; + case kopPushISlotAttr: + slat = SlotAttrName(Uint8(*pbNext++)); + nSlotRef = Int8(*pbNext++); + nIndex = Uint8(*pbNext++); + if (fConstraints) + nSlotRef += islot + 1; // +1 to peek ahead to a slot we haven't "got" yet + DoPushSlotAttr(ptman, nSlotRef, *pfInserting, slat, nIndex, vnStack, psstrmIn, psstrmOut); + if (*psmf != ksmfContinue) + return 0; + break; + + case kopPushGlyphAttrObs: + case kopPushAttToGAttrObs: + case kopPushGlyphAttr: + case kopPushAttToGlyphAttr: + if (op == kopPushGlyphAttrObs || op == kopPushAttToGAttrObs) + nGlyphAttr = Uint8(*pbNext++); + else { + nGlyphAttr = (Uint8(*pbNext++) << 8); + nGlyphAttr += Uint8(*pbNext++); + } + nSlotRef = Int8(*pbNext++); + if (fConstraints) + nSlotRef += islot + 1; // +1 to peek ahead to a slot we haven't "got" yet + if (op == kopPushAttToGlyphAttr || op == kopPushAttToGAttrObs) + DoPushAttToGlyphAttr(ptman, nSlotRef, *pfInserting, nGlyphAttr, + vnStack, psstrmIn, psstrmOut); + else + DoPushGlyphAttr(ptman, nSlotRef, *pfInserting, nGlyphAttr, vnStack, + psstrmIn, psstrmOut); + if (*psmf != ksmfContinue) + return 0; + break; + case kopPushGlyphMetric: + case kopPushAttToGlyphMetric: + nGlyphAttr = Uint8(*pbNext++); + nSlotRef = Int8(*pbNext++); + nAttLevel = Int8(*pbNext++); + if (fConstraints) + nSlotRef += islot + 1; // +1 to peek ahead to a slot we haven't "got" yet + if (op == kopPushAttToGlyphMetric) + DoPushAttToGlyphMetric(ptman, nSlotRef, *pfInserting, nGlyphAttr, nAttLevel, + vnStack, psstrmIn, psstrmOut); + else + DoPushGlyphMetric(ptman, nSlotRef, *pfInserting, nGlyphAttr, nAttLevel, + vnStack, psstrmIn, psstrmOut); + if (*psmf != ksmfContinue) + return 0; + break; + case kopPushFeat: + nFeat = Uint8(*pbNext++); + nSlotRef = Int8(*pbNext++); + if (fConstraints) + nSlotRef += islot + 1; // +1 to peek ahead to a slot we haven't "got" yet +// else +// nSlotRef = 0; + DoPushFeatValue(ptman, nSlotRef, *pfInserting, nFeat, vnStack, psstrmIn, psstrmOut); + if (*psmf != ksmfContinue) + return 0; + break; + case kopPushProcState: + nPState = Uint8(*pbNext++); + gAssert(fConstraints); + DoPushProcState(ptman, nPState, vnStack); + break; + case kopPushVersion: + vnStack.push_back(kRuleVersion); + break; + case kopPopRet: + if ((*psmf = CheckStack(vnStack, 1)) != ksmfContinue) + return 0; + nRet = vnStack.back(); + vnStack.pop_back(); + if (vnStack.size() != 0) + { + gAssert(false); + *psmf = ksmfStackNotEmptied; + } + else + *psmf = ksmfDone; + break; + case kopRetZero: + nRet = 0; + if (vnStack.size() != 0) + { + gAssert(false); + *psmf = ksmfStackNotEmptied; + } + else + *psmf = ksmfDone; + break; + case kopRetTrue: + nRet = 1; + if (vnStack.size() != 0) + { + gAssert(false); + *psmf = ksmfStackNotEmptied; + } + else + *psmf = ksmfDone; + break; + + default: + gAssert(false); + } + + *ppbArg = pbNext; + return nRet; +} + +/*---------------------------------------------------------------------------------------------- + Perform arithmetic functions that take two arguments. +----------------------------------------------------------------------------------------------*/ +void GrPass::DoStackArithmetic2Args(ActionCommand op, std::vector<int> & vnStack, + StackMachineFlag * psmf) +{ + if ((*psmf = CheckStack(vnStack, 2)) != ksmfContinue) + return; + + int nArg2 = vnStack.back(); + vnStack.pop_back(); + int nArg1 = vnStack.back(); + vnStack.pop_back(); + + int nResult; + + switch (op) + { + case kopAdd: nResult = nArg1 + nArg2; break; + case kopSub: nResult = nArg1 - nArg2; break; + case kopMul: nResult = nArg1 * nArg2; break; + case kopDiv: nResult = nArg1 / nArg2; break; + case kopMin: nResult = min(nArg1, nArg2); break; + case kopMax: nResult = max(nArg1, nArg2); break; + case kopAnd: nResult = (nArg1 != 0 && nArg2 != 0)? 1: 0; break; + case kopOr: nResult = (nArg1 != 0 || nArg2 != 0)? 1: 0; break; + case kopEqual: nResult = (nArg1 == nArg2)? 1: 0; break; + case kopNotEq: nResult = (nArg1 != nArg2)? 1: 0; break; + case kopLess: nResult = (nArg1 < nArg2)? 1: 0; break; + case kopLessEq: nResult = (nArg1 <= nArg2)? 1: 0; break; + case kopGtr: nResult = (nArg1 > nArg2)? 1: 0; break; + case kopGtrEq: nResult = (nArg1 >= nArg2)? 1: 0; break; + default: + Assert(false); + } + + vnStack.push_back(nResult); +} + +/*---------------------------------------------------------------------------------------------- + Perform arithmetic functions that take one argument. +----------------------------------------------------------------------------------------------*/ +void GrPass::DoStackArithmetic1Arg(ActionCommand op, std::vector<int> & vnStack, + StackMachineFlag * psmf) +{ + if ((*psmf = CheckStack(vnStack, 1)) != ksmfContinue) + return; + + int nArg = vnStack.back(); + vnStack.pop_back(); + int nResult; + + switch (op) + { + case kopNeg: nResult = nArg * -1; break; + case kopTrunc8: nResult = nArg & 0xFF; break; + case kopTrunc16: nResult = nArg & 0xFFFF; break; + case kopNot: nResult = (nArg == 0)? 1: 0; break; + default: + Assert(false); + } + + vnStack.push_back(nResult); +} + +/*---------------------------------------------------------------------------------------------- + Perform the conditional statement. +----------------------------------------------------------------------------------------------*/ +void GrPass::DoConditional(std::vector<int> & vnStack, StackMachineFlag * psmf) +{ + if ((*psmf = CheckStack(vnStack, 3)) != ksmfContinue) + return; + + int nArg3 = vnStack.back(); + vnStack.pop_back(); + int nArg2 = vnStack.back(); + vnStack.pop_back(); + int nArg1 = vnStack.back(); + vnStack.pop_back(); + + if (nArg1 == 0) + vnStack.push_back(nArg3); + else + vnStack.push_back(nArg2); +} + +/*---------------------------------------------------------------------------------------------- + Check to make sure the stack has the appropriate number of items on it; if not, + return a stack error flag. +----------------------------------------------------------------------------------------------*/ +GrPass::StackMachineFlag GrPass::CheckStack(std::vector<int> & vnStack, int cn) +{ + if (signed(vnStack.size()) < cn) + return ksmfUnderflow; + else + return ksmfContinue; +} + +/*---------------------------------------------------------------------------------------------- + We are finished processing a slot; go on to the next slot, or possibly go backwards. +----------------------------------------------------------------------------------------------*/ +void GrPass::DoNext(GrTableManager * ptman, + int cslot, GrSlotStream * psstrmIn, GrSlotStream * psstrmOut) +{ + gAssert(cslot == 1); // for now anyway +} + +/*---------------------------------------------------------------------------------------------- + Set the next glyph to be the given class. There should only be one member of the class, + but if there happens to be more than one, just use the first member. + All the slot attributes remain unchanged from the current input slot; associations are + neutralized. + + @param ptman - table manager, for generating new slots + @param fInserting - true if this slot is being inserted, not copied + @param nReplacementClass - class from which to take replacement glyph (corresponding to + selector slot's glyph's index in selector class) + @param psstrmIn / Out - input/output streams +----------------------------------------------------------------------------------------------*/ +void GrPass::DoPutGlyph(GrTableManager * ptman, bool fInserting, int nReplacementClass, + GrSlotStream * psstrmIn, GrSlotStream * psstrmOut) +{ + EngineState * pengst = ptman->State(); + + // If we are exactly at the segment boundary, pass the information on to the output stream. + // Note that inserted items always go outside the segment, if they are at the boundary + // (you DON'T want them between the segment boundary and the LB slot!). + bool fSetSegMin = psstrmIn->AtSegMin() && !fInserting; + bool fSetSegLim = psstrmIn->AtSegLim(); + + // Slot to copy features and text properties from: + GrSlotState * pslotNextInput; + if (psstrmIn->AtEndOfContext()) + { + gAssert(fInserting); + pslotNextInput = psstrmIn->RuleInputSlot(0, psstrmOut); + } + else + pslotNextInput = (fInserting)? psstrmIn->Peek(): psstrmIn->NextGet(); + + gid16 nGlyphReplacement = ptman->GetClassGlyphIDAt(nReplacementClass, 0); + + GrSlotState * pslotNew; + if (fInserting) + { + pengst->NewSlot(nGlyphReplacement, pslotNextInput, m_ipass, &pslotNew); + // leave associations empty; eventually they will be "neutralized" + } + else + { + pengst->NewSlotCopy(pslotNextInput, m_ipass, &pslotNew); + pslotNew->SetGlyphID(nGlyphReplacement); + ptman->SetSlotAttrsFromGlyphAttrs(pslotNew); + } + + if (fSetSegMin) + psstrmOut->SetSegMinToWritePos(false); + if (fSetSegLim) + psstrmOut->SetSegLimToWritePos(false); + psstrmOut->NextPut(pslotNew); +} + +/*---------------------------------------------------------------------------------------------- + Copy a glyph from the given slot in the input to the output. All the associations and slot + attributes are copied from the specified slot as well. Consume a slot from the input + (which may or may not be the slot we're copying). + + @param ptman - table manager, for generating new slots + @param fInserting - true if this slot is being inserted, not copied + @param cslotCopyFrom - slot to copy from + @param psstrmIn / Out - input/output streams +----------------------------------------------------------------------------------------------*/ +void GrPass::DoPutCopy(GrTableManager * ptman, bool fInserting, int cslotCopyFrom, + GrSlotStream * psstrmIn, GrSlotStream * psstrmOut) +{ + EngineState * pengst = ptman->State(); + + // If we are exactly at the segment boundary, pass the information on to the output stream. + // Note that inserted items always go outside the segment, if they are at the boundary. + bool fSetSegMin = psstrmIn->AtSegMin() && !fInserting; + bool fSetSegLim = psstrmIn->AtSegLim(); + + if (!fInserting) + { + gAssert(!psstrmIn->AtEndOfContext()); + // Absorb next slot + psstrmIn->NextGet(); + } + + GrSlotState * pslotCopyFrom = psstrmIn->RuleInputSlot(cslotCopyFrom, psstrmOut); + + GrSlotState * pslotNew; + pengst->NewSlotCopy(pslotCopyFrom, m_ipass, &pslotNew); + + if (fSetSegMin) + psstrmOut->SetSegMinToWritePos(false); + if (fSetSegLim) + psstrmOut->SetSegLimToWritePos(false); + psstrmOut->NextPut(pslotNew); +} + +/*---------------------------------------------------------------------------------------------- + Copy the current slot from the input to the output, substituting the corresponding glyph + in the output class. All the associations and slot attributes remain unchanged from the + current input slot. + + @param ptman - table manager, for generating new slots + @param fInserting - true if this slot is being inserted, not copied + @param cslotSel - selector slot, relative to current slot + @param nSelectorClass - class of selector slot + @param nReplacementClass - class from which to take replacement glyph (corresponding to + selector slot's glyph's index in selector class) + @param psstrmIn / Out - input/output streams +----------------------------------------------------------------------------------------------*/ +void GrPass::DoPutSubs(GrTableManager * ptman, bool fInserting, + int cslotSel, int nSelClass, int nReplacementClass, + GrSlotStream * psstrmIn, GrSlotStream * psstrmOut) +{ + bool fAtSegMin, fAtSegLim; + GrSlotState * pslotNextInput; + DoPutSubsInit(psstrmIn, psstrmOut, fInserting, &pslotNextInput, &fAtSegMin, &fAtSegLim); + + GrSlotState * pslotInSelector = psstrmIn->RuleInputSlot(cslotSel, psstrmOut); + + gid16 gidSelector = pslotInSelector->GlyphID(); + int nSelIndex = ptman->GetIndexInGlyphClass(nSelClass, gidSelector); + gAssert(nSelIndex != -1); + gid16 gidReplacement = (nSelIndex == -1)? + gidSelector: + ptman->GetClassGlyphIDAt(nReplacementClass, nSelIndex); + + DoPutSubsAux(ptman, fInserting, gidReplacement, psstrmIn, psstrmOut, pslotNextInput, + fAtSegMin, fAtSegLim); +} + +/*---------------------------------------------------------------------------------------------- + Copy the current slot from the input to the output, substituting the corresponding glyph + in the output class based on 2 input classes. All the associations and slot attributes + remain unchanged from the current input slot. + + @param ptman - table manager, for generating new slots + @param fInserting - true if this slot is being inserted, not copied + @param cslotSel - selector slot, relative to current slot + @param nSelectorClass - class of selector slot + @param nReplacementClass - class from which to take replacement glyph (corresponding to + selector slot's glyph's index in selector class) + @param psstrmIn / Out - input/output streams +----------------------------------------------------------------------------------------------*/ +void GrPass::DoPutSubs2(GrTableManager * ptman, bool fInserting, + int cslotSel1, int nSelClass1, int cslotSel2, int nSelClass2, int nReplacementClass, + GrSlotStream * psstrmIn, GrSlotStream * psstrmOut) +{ + bool fAtSegMin, fAtSegLim; + GrSlotState * pslotNextInput; + DoPutSubsInit(psstrmIn, psstrmOut, fInserting, &pslotNextInput, &fAtSegMin, &fAtSegLim); + + GrSlotState * pslotInSelector1 = psstrmIn->RuleInputSlot(cslotSel1, psstrmOut); + gid16 gidSelector1 = pslotInSelector1->GlyphID(); + int nSelIndex1 = ptman->GetIndexInGlyphClass(nSelClass1, gidSelector1); + gAssert(nSelIndex1 != -1); + //size_t cClassLen1 = ptman->NumberOfGlyphsInClass(nSelClass1); + + GrSlotState * pslotInSelector2 = psstrmIn->RuleInputSlot(cslotSel2, psstrmOut); + gid16 gidSelector2 = pslotInSelector2->GlyphID(); + int nSelIndex2 = ptman->GetIndexInGlyphClass(nSelClass2, gidSelector2); + gAssert(nSelIndex2 != -1); + size_t cClassLen2 = ptman->NumberOfGlyphsInClass(nSelClass2); + + int nSelIndex = (nSelIndex1 == -1 || nSelIndex2 == -1) ? + -1 : + (nSelIndex1 * static_cast<int>(cClassLen2)) + nSelIndex2; + + gid16 gidReplacement = (nSelIndex == -1)? + gidSelector1: + ptman->GetClassGlyphIDAt(nReplacementClass, nSelIndex); + + DoPutSubsAux(ptman, fInserting, gidReplacement, psstrmIn, psstrmOut, pslotNextInput, + fAtSegMin, fAtSegLim); +} + +/*---------------------------------------------------------------------------------------------- + Copy the current slot from the input to the output, substituting the corresponding glyph + in the output class based on 2 input classes. All the associations and slot attributes + remain unchanged from the current input slot. + + @param ptman - table manager, for generating new slots + @param fInserting - true if this slot is being inserted, not copied + @param cslotSel - selector slot, relative to current slot + @param nSelectorClass - class of selector slot + @param nReplacementClass - class from which to take replacement glyph (corresponding to + selector slot's glyph's index in selector class) + @param psstrmIn / Out - input/output streams +----------------------------------------------------------------------------------------------*/ +void GrPass::DoPutSubs3(GrTableManager * ptman, bool fInserting, + int cslotSel1, int nSelClass1, int cslotSel2, int nSelClass2, int cslotSel3, int nSelClass3, + int nReplacementClass, + GrSlotStream * psstrmIn, GrSlotStream * psstrmOut) +{ + bool fAtSegMin, fAtSegLim; + GrSlotState * pslotNextInput; + DoPutSubsInit(psstrmIn, psstrmOut, fInserting, &pslotNextInput, &fAtSegMin, &fAtSegLim); + + GrSlotState * pslotInSelector1 = psstrmIn->RuleInputSlot(cslotSel1, psstrmOut); + gid16 gidSelector1 = pslotInSelector1->GlyphID(); + int nSelIndex1 = ptman->GetIndexInGlyphClass(nSelClass1, gidSelector1); + gAssert(nSelIndex1 != -1); + //size_t cClassLen1 = ptman->NumberOfGlyphsInClass(nSelClass1); + + GrSlotState * pslotInSelector2 = psstrmIn->RuleInputSlot(cslotSel2, psstrmOut); + gid16 gidSelector2 = pslotInSelector2->GlyphID(); + int nSelIndex2 = ptman->GetIndexInGlyphClass(nSelClass2, gidSelector2); + gAssert(nSelIndex2 != -1); + size_t cClassLen2 = ptman->NumberOfGlyphsInClass(nSelClass2); + +// GrSlotState * pslotInSelector3 = psstrmIn->RuleInputSlot(cslotSel3, psstrmOut); +// gid16 gidSelector3 = pslotInSelector3->GlyphID(); + int nSelIndex3 = ptman->GetIndexInGlyphClass(nSelClass3, gidSelector2); + gAssert(nSelIndex3 != -1); + size_t cClassLen3 = ptman->NumberOfGlyphsInClass(nSelClass3); + + int nSelIndex = (nSelIndex1 == -1 || nSelIndex2 == -1 || nSelIndex3 == -1) ? + -1 : + (((nSelIndex1 * static_cast<int>(cClassLen2)) + nSelIndex2) * static_cast<int>(cClassLen3)) + nSelIndex3; + + gid16 gidReplacement = (nSelIndex == -1)? + gidSelector1: + ptman->GetClassGlyphIDAt(nReplacementClass, nSelIndex); + + DoPutSubsAux(ptman, fInserting, gidReplacement, psstrmIn, psstrmOut, pslotNextInput, + fAtSegMin, fAtSegLim); +} + + +/*---------------------------------------------------------------------------------------------- + Initial common part of all the DoPutSubs... methods. +----------------------------------------------------------------------------------------------*/ +void GrPass::DoPutSubsInit(GrSlotStream * psstrmIn, GrSlotStream * psstrmOut, bool fInserting, + GrSlotState ** ppslotNextInput, bool * pfAtSegMin, bool * pfAtSegLim) +{ + // Do this before reading from stream. + *pfAtSegMin = psstrmIn->AtSegMin(); + *pfAtSegLim = psstrmIn->AtSegLim(); + + // Slot to copy features and text properties from: + if (psstrmIn->AtEndOfContext()) + { + gAssert(fInserting); + *ppslotNextInput = psstrmIn->RuleInputSlot(0, psstrmOut); + } + else + *ppslotNextInput = (fInserting)? psstrmIn->Peek(): psstrmIn->NextGet(); +} + +/*---------------------------------------------------------------------------------------------- + Common part of all the DoPutSubs... methods. +----------------------------------------------------------------------------------------------*/ +void GrPass::DoPutSubsAux(GrTableManager * ptman, bool fInserting, gid16 nGlyphReplacement, + GrSlotStream * psstrmIn, GrSlotStream * psstrmOut, GrSlotState * pslotNextInput, + bool fAtSegMin, bool fAtSegLim) +{ + EngineState * pengst = ptman->State(); + + // If we are exactly at the segment boundary, pass the information on to the output stream. + // Note that inserted items always go outside the segment, if they are at the boundary. + bool fSetSegMin = fAtSegMin && !fInserting; + bool fSetSegLim = fAtSegLim; + + GrSlotState * pslotNew; + if (fInserting) + { + pengst->NewSlot(nGlyphReplacement, pslotNextInput, m_ipass, &pslotNew); + // leave associations empty; eventually they will be "neutralized" + } + else + { + pengst->NewSlotCopy(pslotNextInput, m_ipass, &pslotNew); + pslotNew->SetGlyphID(nGlyphReplacement); + ptman->SetSlotAttrsFromGlyphAttrs(pslotNew); + } + + if (fSetSegMin) + psstrmOut->SetSegMinToWritePos(false); + if (fSetSegLim) + psstrmOut->SetSegLimToWritePos(false); + psstrmOut->NextPut(pslotNew); +} + +/*---------------------------------------------------------------------------------------------- + A slot has just been inserted. Clear all the associations; eventually (unless we go on + to set the associations explicitly) we will set its before-assoc to the slot after it + and its after-assoc to the slot before it. This makes it basically unselectable. + OBSOLETE - handled by slot initialization code +----------------------------------------------------------------------------------------------*/ +void GrPass::SetNeutralAssocs(GrSlotState * pslotNew, GrSlotStream * psstrmIn) +{ + pslotNew->ClearAssocs(); +} + +/*---------------------------------------------------------------------------------------------- + Delete the next slot in the input. + + @param psstrmIn - input stream + @param psstrmOut - output stream +----------------------------------------------------------------------------------------------*/ +void GrPass::DoDelete(GrTableManager * ptman, GrSlotStream * psstrmIn, GrSlotStream * psstrmOut) +{ + if (psstrmIn->AtSegMin()) + psstrmOut->SetSegMinToWritePos(); + if (psstrmIn->AtSegLim()) + psstrmOut->SetSegLimToWritePos(); + + GrSlotState * pslot = psstrmIn->NextGet(); + pslot->MarkDeleted(); + + if (ptman->LoggingTransduction()) + m_pzpst->RecordDeletionBefore(psstrmOut->WritePos()); +} + +/*---------------------------------------------------------------------------------------------- + Set the associations for the current slot. Any previous associations will be overwritten. + + @param cn - number of associations + @param vnAssoc - list of associations + @param fInserting - whether current slot was inserted + @param psstrmIn / Out - input/output streams +----------------------------------------------------------------------------------------------*/ +void GrPass::DoAssoc(int cnAssocs, std::vector<int> & vnAssocs, bool fInserting, + GrSlotStream * psstrmIn, GrSlotStream * psstrmOut) +{ + gAssert((unsigned)cnAssocs == vnAssocs.size()); + + // Sort the list of associations. It's okay to use a simple bubble sort + // since we expect the list to be very short. Ideally we should remove duplicates + // too but I don't think duplicates actually hurt anything. + for (int i1 = 0; i1 < cnAssocs - 1; i1++) + { + for (int i2 = i1 + 1; i2 < cnAssocs; i2++) + { + if (vnAssocs[i2] < vnAssocs[i1]) + { + int nTmp = vnAssocs[i2]; + vnAssocs[i2] = vnAssocs[i1]; + vnAssocs[i1] = nTmp; + } + } + } + + std::vector<GrSlotState *> vpslotAssocs; + vpslotAssocs.resize(cnAssocs); + for (int i = 0; i < cnAssocs; i++) + vpslotAssocs[i] = psstrmIn->RuleInputSlot(vnAssocs[i], psstrmOut); + + GrSlotState * pslot = psstrmOut->RuleOutputSlot(); + pslot->Associate(vpslotAssocs); +} + +/*---------------------------------------------------------------------------------------------- + Set a slot attribute for the current slot; the value is on the stack. + + @param op - command + @param fInserting - whether current slot was inserted + @param slat - slot attribute to set + @param slati - slot attribute index, or -1 for non-indexed attribute + @param vnStack - stack to read value from + @param psstrmIn / Out - input/output streams +----------------------------------------------------------------------------------------------*/ +void GrPass::DoSetAttr(GrTableManager * ptman, ActionCommand op, bool fInserting, + SlotAttrName slat, int slati, std::vector<int> & vnStack, + GrSlotStream * psstrmIn, GrSlotStream * psstrmOut) +{ + int nVal = vnStack.back(); + vnStack.pop_back(); + + if (slat == kslatUserDefnV1) + slat = kslatUserDefn; + + if (slati != -1 && slat != kslatCompRef && slat != kslatUserDefn) + { + // Invalid slot attribute index. + gAssert(false); + slati = -1; + } + + if (slati == -1 && (slat == kslatCompRef || slat == kslatUserDefn)) + { + // Missing slot attribute index. + gAssert(false); + slati = 0; + } + + if (slat == kslatPosX || slat == kslatPosY) + { + // Doesn't make sense to set the pos attribute. + gAssert(false); + return; + } + + GrSlotState * pslotIn = psstrmIn->RuleInputSlot(0, psstrmOut); + + GrSlotState * pslotComp; + + int nOldVal; + + if (op == kopAttrAdd || op == kopAttrSub || op == kopIAttrAdd || op == kopIAttrSub) + { + // Incrementing or decrementing. + + gAssert(!fInserting); + + switch (slat) + { + case kslatAdvX: nOldVal = pslotIn->AdvanceX(ptman); break; + case kslatAdvY: nOldVal = pslotIn->AdvanceY(ptman); break; + case kslatShiftX: nOldVal = pslotIn->ShiftX(); break; + case kslatShiftY: nOldVal = pslotIn->ShiftY(); break; + + case kslatAttAtX: nOldVal = pslotIn->AttachAtX(ptman, psstrmIn); break; + case kslatAttAtY: nOldVal = pslotIn->AttachAtY(); break; + case kslatAttAtXoff: nOldVal = pslotIn->AttachAtXOffset(); break; + case kslatAttAtYoff: nOldVal = pslotIn->AttachAtYOffset(); break; + + case kslatAttWithX: nOldVal = pslotIn->AttachWithX(ptman, psstrmIn); break; + case kslatAttWithY: nOldVal = pslotIn->AttachWithY(); break; + case kslatAttWithXoff: nOldVal = pslotIn->AttachWithXOffset(); break; + case kslatAttWithYoff: nOldVal = pslotIn->AttachWithYOffset(); break; + + case kslatUserDefn: nOldVal = pslotIn->UserDefn(slati); break; + + case kslatMeasureSol: nOldVal = pslotIn->MeasureSol(); break; + case kslatMeasureEol: nOldVal = pslotIn->MeasureEol(); break; + + case kslatJStretch: nOldVal = pslotIn->JStretch(); break; + case kslatJShrink: nOldVal = pslotIn->JShrink(); break; + case kslatJStep: nOldVal = pslotIn->JStep(); break; + case kslatJWeight: nOldVal = pslotIn->JWeight(); break; + case kslatJWidth: nOldVal = pslotIn->JWidth(); break; + + // These are kind of odd, but maybe. + case kslatAttLevel: nOldVal = pslotIn->AttachLevel(); break; + case kslatBreak: nOldVal = pslotIn->BreakWeight(); break; + case kslatDir: nOldVal = pslotIn->Directionality(); break; + + default: + // Kind of attribute that it makes no sense to increment. + gAssert(false); + return; + } + + if (op == kopAttrAdd || op == kopIAttrAdd) + nVal = nOldVal + nVal; + else + { + gAssert(op == kopAttrSub || op == kopIAttrSub); + nVal = nOldVal - nVal; + } + } + + GrSlotState * pslotOut = psstrmOut->RuleOutputSlot(); + + if (pslotOut->IsLineBreak(ptman->LBGlyphID())) + { + switch (slat) + { + case kslatAdvX: + case kslatAdvY: + case kslatShiftX: + case kslatShiftY: + + case kslatAttTo: + case kslatAttLevel: + case kslatAttAtX: + case kslatAttAtY: + case kslatAttAtGpt: + case kslatAttAtXoff: + case kslatAttAtYoff: + case kslatAttWithX: + case kslatAttWithY: + case kslatAttWithGpt: + case kslatAttWithXoff: + case kslatAttWithYoff: + + case kslatMeasureSol: + case kslatMeasureEol: + + case kslatJStretch: + case kslatJShrink: + case kslatJStep: + case kslatJWeight: + case kslatJWidth: + slat = kslatNoEffect; + break; + default: + ; // okay, do nothing + } + } + + nVal = GrGlyphSubTable::ConvertValueForVersion(nVal, slat, -1, m_fxdVersion); + + switch (slat) + { + case kslatAdvX: pslotOut->SetAdvanceX(nVal); break; + case kslatAdvY: pslotOut->SetAdvanceY(nVal); break; + case kslatShiftX: pslotOut->SetShiftX(nVal); break; + case kslatShiftY: pslotOut->SetShiftY(nVal); break; + + case kslatAttTo: pslotOut->SetAttachTo(nVal); break; + case kslatAttLevel: pslotOut->SetAttachLevel(nVal); break; + case kslatAttAtX: pslotOut->SetAttachAtX(nVal); break; + case kslatAttAtY: pslotOut->SetAttachAtY(nVal); break; + case kslatAttAtGpt: pslotOut->SetAttachAtGpoint(nVal); break; + case kslatAttAtXoff: pslotOut->SetAttachAtXOffset(nVal); break; + case kslatAttAtYoff: pslotOut->SetAttachAtYOffset(nVal); break; + case kslatAttWithX: pslotOut->SetAttachWithX(nVal); break; + case kslatAttWithY: pslotOut->SetAttachWithY(nVal); break; + case kslatAttWithGpt: pslotOut->SetAttachWithGpoint(nVal); break; + case kslatAttWithXoff: pslotOut->SetAttachWithXOffset(nVal); break; + case kslatAttWithYoff: pslotOut->SetAttachWithYOffset(nVal); break; + + case kslatUserDefn: pslotOut->SetUserDefn(slati, nVal); break; + + case kslatCompRef: + gAssert(nVal - 1 >= psstrmIn->RuleStartReadPos() - psstrmIn->ReadPosForNextGet()); + pslotComp = psstrmIn->RuleInputSlot(nVal, psstrmOut); + pslotOut->SetCompRefSlot(ptman, slati, pslotComp); + break; + + case kslatMeasureSol: pslotOut->SetMeasureSol(nVal); break; + case kslatMeasureEol: pslotOut->SetMeasureEol(nVal); break; + + case kslatJStretch: pslotOut->SetJStretch(nVal); break; + case kslatJShrink: pslotOut->SetJShrink(nVal); break; + case kslatJStep: pslotOut->SetJStep(nVal); break; + case kslatJWeight: pslotOut->SetJWeight(nVal); break; + case kslatJWidth: pslotOut->SetJWidth(nVal); break; + + case kslatBreak: pslotOut->SetBreakWeight(nVal); break; + case kslatDir: pslotOut->SetDirectionality(DirCode(nVal)); break; + case kslatInsert: pslotOut->SetInsertBefore(bool(nVal)); break; + + case kslatNoEffect: + // Do nothing. + break; + + default: + // Kind of attribute that it makes no sense to set. + gAssert(false); + return; + } +} + +/*---------------------------------------------------------------------------------------------- + Push value of a slot attribute onto the stack. + + @param nSlotRef - slot whose attribute we are interested in + @param fInserting - whether current slot was inserted + @param slat - slot attribute to get + @param slati - slot attribute index, or -1 for non-indexed attribute + @param vnStack - stack to push onto + @param psstrmIn - input stream +----------------------------------------------------------------------------------------------*/ +void GrPass::DoPushSlotAttr(GrTableManager * ptman, + int nSlotRef, bool fInserting, + SlotAttrName slat, int slati, std::vector<int> & vnStack, + GrSlotStream * psstrmIn, GrSlotStream * psstrmOut) +{ + GrSlotState * pslot = psstrmIn->RuleInputSlot(nSlotRef, psstrmOut, true); + if (pslot == NULL) + { + // Non-existent pre-context item. + vnStack.push_back(0); + return; + } + + int nVal; + int xyBogus; + + if (slat == kslatUserDefnV1) + slat = kslatUserDefn; + + if (slati != -1 && slat != kslatCompRef && slat != kslatUserDefn) + { + // Invalid slot attribute index. + gAssert(false); + slati = -1; + } + + if (slati == -1 && (slat == kslatCompRef || slat == kslatUserDefn)) + { + // Missing slot attribute index. + gAssert(false); + slati = 0; + } + + switch (slat) + { + case kslatAdvX: nVal = pslot->AdvanceX(ptman); break; + case kslatAdvY: nVal = pslot->AdvanceY(ptman); break; + case kslatShiftX: nVal = pslot->ShiftX(); break; + case kslatShiftY: nVal = pslot->ShiftY(); break; + + case kslatPosX: pslot->Position(ptman, psstrmIn, &nVal, &xyBogus); break; + case kslatPosY: pslot->Position(ptman, psstrmIn, &xyBogus, &nVal); break; + + case kslatAttTo: nVal = pslot->AttachTo(); break; + case kslatAttLevel: nVal = pslot->AttachLevel(); break; + case kslatAttAtX: nVal = pslot->AttachAtX(ptman, psstrmIn); break; + case kslatAttAtY: nVal = pslot->AttachAtY(); break; + case kslatAttAtGpt: nVal = pslot->AttachAtGpoint(); break; + case kslatAttAtXoff: nVal = pslot->AttachAtXOffset(); break; + case kslatAttAtYoff: nVal = pslot->AttachAtYOffset(); break; + case kslatAttWithX: nVal = pslot->AttachWithX(ptman, psstrmIn); break; + case kslatAttWithY: nVal = pslot->AttachWithY(); break; + case kslatAttWithGpt: nVal = pslot->AttachWithGpoint(); break; + case kslatAttWithXoff: nVal = pslot->AttachWithXOffset(); break; + case kslatAttWithYoff: nVal = pslot->AttachWithYOffset(); break; + + case kslatMeasureSol: nVal = pslot->MeasureSol(); break; + case kslatMeasureEol: nVal = pslot->MeasureEol(); break; + + case kslatJStretch: nVal = pslot->JStretch(); break; + case kslatJShrink: nVal = pslot->JShrink(); break; + case kslatJStep: nVal = pslot->JStep(); break; + case kslatJWeight: nVal = pslot->JWeight(); break; + case kslatJWidth: nVal = pslot->JWidth(); break; + + case kslatBreak: nVal = pslot->BreakWeight(); break; + case kslatDir: nVal = pslot->Directionality(); break; + case kslatInsert: nVal = pslot->InsertBefore(); break; + + case kslatUserDefn: nVal = pslot->UserDefn(slati); break; + + // Currently no way to look up component.X.ref. + + default: + // Invalid attribute. + gAssert(false); + nVal = 0; + } + + vnStack.push_back(nVal); +} + +/*---------------------------------------------------------------------------------------------- + Push value of a glyph attribute onto the stack. + + @param nSlotRef - slot whose attribute we are interested in + @param fInserting - whether current slot was inserted + @param nGlyphAttr - glyph attribute to get + @param vnStack - stack to push onto + @param psstrmIn - input stream +----------------------------------------------------------------------------------------------*/ +void GrPass::DoPushGlyphAttr(GrTableManager * ptman, int nSlotRef, bool fInserting, + int nGlyphAttr, + std::vector<int> & vnStack, GrSlotStream * psstrmIn, GrSlotStream * psstrmOut) +{ + GrSlotState * pslot = psstrmIn->RuleInputSlot(nSlotRef, psstrmOut, true); + if (pslot == NULL) + { + // Non-existent pre-context item. + vnStack.push_back(0); + return; + } + int nVal = pslot->GlyphAttrValueEmUnits(ptman, nGlyphAttr); + vnStack.push_back(nVal); +} + +/*---------------------------------------------------------------------------------------------- + Push value of a glyph attribute onto the stack. The slot of interest is the slot to + which the current slot is attached. + + @param nSlotRef - leaf slot + @param fInserting - whether current slot was inserted + @param nGlyphAttr - glyph attribute to get + @param vnStack - stack to push onto + @param psstrmIn - input stream +----------------------------------------------------------------------------------------------*/ +void GrPass::DoPushAttToGlyphAttr(GrTableManager * ptman, int nSlotRef, bool fInserting, + int nGlyphAttr, std::vector<int> & vnStack, + GrSlotStream * psstrmIn, GrSlotStream * psstrmOut) +{ + // Note that it is the slot in the output stream that has + // the relevant attach.to attribute set. + GrSlotState * pslotLeaf = psstrmOut->RuleOutputSlot(0); + int srAttTo = pslotLeaf->AttachTo(); + if (srAttTo == 0) + { + gAssert(false); + vnStack.push_back(0); + return; + } + + GrSlotState * pslotRoot = psstrmIn->RuleInputSlot(nSlotRef + srAttTo, psstrmOut); + int nVal = pslotRoot->GlyphAttrValueEmUnits(ptman, nGlyphAttr); + vnStack.push_back(nVal); +} + +/*---------------------------------------------------------------------------------------------- + Push value of a glyph metric onto the stack. + + @param nSlotRef - slot whose attribute we are interested in + @param fInserting - whether current slot was inserted + @param nGlyphAttr - glyph attribute to get + @param nAttLevel - for accessing composite metrics + @param vnStack - stack to push onto + @param psstrmIn - input stream +----------------------------------------------------------------------------------------------*/ +void GrPass::DoPushGlyphMetric(GrTableManager * ptman, int nSlotRef, bool fInserting, + int nGlyphAttr, int nAttLevel, + std::vector<int> & vnStack, GrSlotStream * psstrmIn, GrSlotStream * psstrmOut) +{ + GrSlotState * pslot = psstrmIn->RuleInputSlot(nSlotRef, psstrmOut, true); + if (pslot == NULL) + { + // Non-existent pre-context item. + vnStack.push_back(0); + return; + } + + DoPushGlyphMetricAux(ptman, pslot, nGlyphAttr, nAttLevel, vnStack, psstrmIn); +} + +/*---------------------------------------------------------------------------------------------- + Push value of a glyph metric onto the stack. The slot of interest is the slot to + which the current slot is attached. + + @param nSlotRef - slot whose attribute we are interested in + @param fInserting - whether current slot was inserted + @param nGlyphAttr - glyph attribute to get + @param nAttLevel - for accessing composite metrics + @param vnStack - stack to push onto + @param psstrmIn - input stream + @param psstrmOut - output stream +----------------------------------------------------------------------------------------------*/ +void GrPass::DoPushAttToGlyphMetric(GrTableManager * ptman, int nSlotRef, bool fInserting, + int nGlyphAttr, int nAttLevel, + std::vector<int> & vnStack, + GrSlotStream * psstrmIn, GrSlotStream * psstrmOut) +{ + // Note that it is the slot in the output stream that has + // the relevant attach.to attribute set. + GrSlotState * pslotLeaf = psstrmOut->RuleOutputSlot(0); + int srAttTo = pslotLeaf->AttachTo(); + if (srAttTo == 0) + { + gAssert(false); + vnStack.push_back(0); + return; + } + + GrSlotState * pslot = psstrmIn->RuleInputSlot(nSlotRef + srAttTo, psstrmOut); + + DoPushGlyphMetricAux(ptman, pslot, nGlyphAttr, nAttLevel, vnStack, psstrmIn); +} + +/*--------------------------------------------------------------------------------------------*/ + +void GrPass::DoPushGlyphMetricAux(GrTableManager * ptman, + GrSlotState * pslot, int nGlyphAttr, int nAttLevel, + std::vector<int> & vnStack, GrSlotStream * psstrmIn) +{ + int mVal; + GlyphMetric gmet = GlyphMetric(nGlyphAttr); + if (nAttLevel == 0 || gmet == kgmetAscent || gmet == kgmetDescent) + { + mVal = pslot->GlyphMetricEmUnits(ptman, nGlyphAttr); + } + else + { + pslot->CalcCompositeMetrics(ptman, psstrmIn, nAttLevel, true); + + float xy; + switch (gmet) + { + case kgmetLsb: xy = pslot->ClusterLsb(psstrmIn); break; + case kgmetRsb: xy = pslot->ClusterRsb(psstrmIn); break; + case kgmetBbTop: xy = pslot->ClusterBbTop(psstrmIn); break; + case kgmetBbBottom: xy = pslot->ClusterBbBottom(psstrmIn); break; + case kgmetBbLeft: xy = pslot->ClusterBbLeft(psstrmIn); break; + case kgmetBbRight: xy = pslot->ClusterBbRight(psstrmIn); break; + case kgmetBbHeight: xy = pslot->ClusterBbHeight(psstrmIn); break; + case kgmetBbWidth: xy = pslot->ClusterBbWidth(psstrmIn); break; +// case kgmetAdvHeight: xy = pslot->ClusterAdvHeight(psstrmIn); break; + case kgmetAdvWidth: xy = pslot->ClusterAdvWidth(psstrmIn); break; + default: + gAssert(false); + xy = 0; + } + mVal = ptman->LogToEmUnits(xy); + } + + vnStack.push_back(mVal); +} + +/*---------------------------------------------------------------------------------------------- + Push a feature value onto the stack. For rule actions, the slot of interest is the + current slot. + + @param nSlotRef - slot being examined + @param fInserting - whether current slot was inserted + @param nFeat - feature of interest + @param vnStack - stack to push onto + @param psstrmIn - input stream + @param psstrmOut - output stream +----------------------------------------------------------------------------------------------*/ +void GrPass::DoPushFeatValue(GrTableManager * ptman, int nSlotRef, bool fInserting, + int nFeat, std::vector<int> & vnStack, GrSlotStream * psstrmIn, GrSlotStream * psstrmOut) +{ + gAssert(!fInserting); + + GrSlotState * pslot = psstrmIn->RuleInputSlot(nSlotRef, psstrmOut, true); + if (pslot == NULL) + { + // Non-existent pre-context item. + vnStack.push_back(0); + return; + } + + int nVal = pslot->FeatureValue(nFeat); + vnStack.push_back(nVal); +} + +/*---------------------------------------------------------------------------------------------- + Push a processing-state value onto the stack. + + @param nFeat - feature of interest + @param vnStack - stack to push onto +----------------------------------------------------------------------------------------------*/ +void GrPass::DoPushProcState(GrTableManager * ptman, int nPState, std::vector<int> & vnStack) +{ + int nValue; + if (nPState == kpstatJustifyMode) + { + // Convert from internal modes to user modes. + int jmodi = ptman->InternalJustificationMode(); + switch (jmodi) + { + case kjmodiNormal: + case kjmodiCanShrink: + nValue = kjmoduNormal; + break; + case kjmodiMeasure: + nValue = kjmoduMeasure; + break; + case kjmodiJustify: + nValue = kjmoduJustify; + break; + default: + gAssert(false); + nValue = kjmoduNormal; + } + } + else if (nPState == kpstatJustifyLevel) + nValue = 1; // TODO: get justify level from ptman + vnStack.push_back(nValue); +} + +} // namespace gr diff --git a/Build/source/libs/graphite-engine/src/segment/GrPseudoMap.h b/Build/source/libs/graphite-engine/src/segment/GrPseudoMap.h new file mode 100644 index 00000000000..c6fec210808 --- /dev/null +++ b/Build/source/libs/graphite-engine/src/segment/GrPseudoMap.h @@ -0,0 +1,48 @@ +/*--------------------------------------------------------------------*//*:Ignore this sentence. +Copyright (C) 1999, 2001 SIL International. All rights reserved. + +Distributable under the terms of either the Common Public License or the +GNU Lesser General Public License, as specified in the LICENSING.txt file. + +File: GrPseudoMap.h +Responsibility: Sharon Correll +Last reviewed: Not yet. + +Description: + The GrPseudoMap class. +----------------------------------------------------------------------------------------------*/ +#ifdef _MSC_VER +#pragma once +#endif +#ifndef GR_PSEUDOMAP_INCLUDED +#define GR_PSEUDOMAP_INCLUDED + +//:End Ignore + +namespace gr +{ + +/*---------------------------------------------------------------------------------------------- + A mapping between a Unicode value and a pseudo-glyph. + + Hungarian: psd +----------------------------------------------------------------------------------------------*/ + +class GrPseudoMap +{ +public: + unsigned int Unicode() { return m_nUnicode; } + gid16 PseudoGlyph() { return m_chwPseudo; } + + void SetUnicode(int n) { m_nUnicode = n; } + void SetPseudoGlyph(gid16 chw) { m_chwPseudo = chw; } + +protected: + // Instance variables: + unsigned int m_nUnicode; + gid16 m_chwPseudo; +}; + +} // namespace gr + +#endif // !GR_PSEUDOMAP_INCLUDED diff --git a/Build/source/libs/graphite-engine/src/segment/GrSlotState.cpp b/Build/source/libs/graphite-engine/src/segment/GrSlotState.cpp new file mode 100644 index 00000000000..892318912e1 --- /dev/null +++ b/Build/source/libs/graphite-engine/src/segment/GrSlotState.cpp @@ -0,0 +1,1411 @@ +/*--------------------------------------------------------------------*//*:Ignore this sentence. +Copyright (C) 1999, 2001 SIL International. All rights reserved. + +Distributable under the terms of either the Common Public License or the +GNU Lesser General Public License, as specified in the LICENSING.txt file. + +File: GrSlotState.cpp +Responsibility: Sharon Correll +Last reviewed: Not yet. + +Description: + GrSlotState class implementation. +-------------------------------------------------------------------------------*//*:End Ignore*/ + +//:>******************************************************************************************** +//:> Include files +//:>******************************************************************************************** +#include "Main.h" + +#ifdef _MSC_VER +#pragma hdrstop +#endif +#undef THIS_FILE +DEFINE_THIS_FILE + +//:>******************************************************************************************** +//:> Forward declarations +//:>******************************************************************************************** + +//:>******************************************************************************************** +//:> Local Constants and static variables +//:>******************************************************************************************** + +namespace gr +{ + +//:>******************************************************************************************** +//:> Methods +//:>******************************************************************************************** + +/*---------------------------------------------------------------------------------------------- + Initialize slots. +----------------------------------------------------------------------------------------------*/ +// standard for pass 0 slots +void GrSlotState::Initialize(gid16 chw, GrEngine * pgreng, + GrFeatureValues fval, int ipass, int ichwSegOffset, int nUnicode) +{ + Assert(ipass == 0); + m_chwGlyphID = chw; + m_chwActual = kInvalidGlyph; + m_xysGlyphWidth = -1; + m_bStyleIndex = byte(fval.m_nStyleIndex); + u_intslot nullSlot; + nullSlot.pslot = NULL; + std::fill_n(PUserDefnBuf(), m_cnUserDefn, nullSlot); + std::fill_n(PCompRefBuf(), m_cnCompPerLig, nullSlot); + std::fill_n(PSlatiBuf(), m_cnCompPerLig, nullSlot); + u_intslot * pFeatBuf = PFeatureBuf(); + for (size_t i = 0; i < m_cnFeat; i++) + pFeatBuf[i].nValue = fval.m_rgnFValues[i]; + + m_ipassModified = ipass; + m_ichwSegOffset = ichwSegOffset; + m_nUnicode = nUnicode; + m_vpslotAssoc.clear(); + pgreng->InitSlot(this, nUnicode); + + switch (nUnicode) + { + case knLRM: m_spsl = kspslLRM; break; + case knRLM: m_spsl = kspslRLM; break; + case knLRO: m_spsl = kspslLRO; break; + case knRLO: m_spsl = kspslRLO; break; + case knLRE: m_spsl = kspslLRE; break; + case knRLE: m_spsl = kspslRLE; break; + case knPDF: m_spsl = kspslPDF; break; + default: + Assert(m_spsl == kspslNone); + m_spsl = kspslNone; + break; + } +} + +// line-break slots +void GrSlotState::Initialize(gid16 chw, GrEngine * pgreng, + GrSlotState * pslotFeat, int ipass, int ichwSegOffset) +{ + m_chwGlyphID = chw; + m_chwActual = kInvalidGlyph; + m_xysGlyphWidth = -1; + u_intslot nullSlot; + nullSlot.pslot = NULL; + std::fill_n(PUserDefnBuf(), m_cnUserDefn, nullSlot); + std::fill_n(PCompRefBuf(), m_cnCompPerLig, nullSlot); + std::fill_n(PSlatiBuf(), m_cnCompPerLig, nullSlot); + CopyFeaturesFrom(pslotFeat); + m_ipassModified = ipass; + m_ichwSegOffset = ichwSegOffset; + m_nUnicode = -1; + m_vpslotAssoc.clear(); + pgreng->InitSlot(this); + // Caller is responsible for setting m_spsl. +} + +// for inserting new slots after pass 0 (under-pos and unicode are irrelevant) +void GrSlotState::Initialize(gid16 chw, GrEngine * pgreng, + GrSlotState * pslotFeat, int ipass) +{ + m_chwGlyphID = chw; + m_chwActual = kInvalidGlyph; + m_xysGlyphWidth = -1; + u_intslot nullSlot; + nullSlot.pslot = NULL; + std::fill_n(PUserDefnBuf(), m_cnUserDefn, nullSlot); + std::fill_n(PCompRefBuf(), m_cnCompPerLig, nullSlot); + std::fill_n(PSlatiBuf(), m_cnCompPerLig, nullSlot); + CopyFeaturesFrom(pslotFeat); + m_ipassModified = ipass; + m_ichwSegOffset = kInvalid; + m_nUnicode = kInvalid; + m_vpslotAssoc.clear(); + pgreng->InitSlot(this); + m_spsl = kspslNone; +} + +/*---------------------------------------------------------------------------------------------- + The slot has been modified by the given pass and therefore is in a new state; + make a new SlotState initialized from the old one. +----------------------------------------------------------------------------------------------*/ +void GrSlotState::InitializeFrom(GrSlotState * pslotOld, int ipass) +{ + m_ipassModified = ipass; + m_pslotPrevState = pslotOld; + m_ichwSegOffset = kInvalid; + m_vpslotAssoc.clear(); + m_vpslotAssoc.push_back(pslotOld); + + m_dislotRootFixed = pslotOld->m_dislotRootFixed; + m_vdislotAttLeaves.resize(pslotOld->m_vdislotAttLeaves.size()); + for (size_t i = 0; i < pslotOld->m_vdislotAttLeaves.size(); i++) + m_vdislotAttLeaves[i] = pslotOld->m_vdislotAttLeaves[i]; + + GrSlotAbstract::CopyFrom(pslotOld); + + ////FixAttachmentTree(pslotOld); +} + +/*---------------------------------------------------------------------------------------------- + Copy the features and style information from the given slot. +----------------------------------------------------------------------------------------------*/ +void GrSlotState::CopyFeaturesFrom(GrSlotState * pslotSrc) +{ + m_bStyleIndex = pslotSrc->m_bStyleIndex; + Assert(m_cnFeat == pslotSrc->m_cnFeat); + std::copy(pslotSrc->PFeatureBuf(), pslotSrc->PFeatureBuf() + m_cnFeat, PFeatureBuf()); +} + +/*---------------------------------------------------------------------------------------------- + Copy the basic information. + Warning: this function will break if GrSlotState and subclasses are given virtual + methods. In that case, we will need to copy from the address of the first variable in + GrSlotAbstract. +----------------------------------------------------------------------------------------------*/ +void GrSlotAbstract::CopyFrom(GrSlotState * pslot) +{ + u_intslot * pnBufSave = m_prgnVarLenBuf; + *this = *pslot; + m_prgnVarLenBuf = pnBufSave; + Assert(m_prgnVarLenBuf); + std::copy(pslot->m_prgnVarLenBuf, pslot->m_prgnVarLenBuf + CExtraSpace(), m_prgnVarLenBuf); +} + +/*---------------------------------------------------------------------------------------------- + Return the ID of the actual glyph that will be used for output and metrics. This is the + same for most glyphs, but will be different for pseudo-glyphs. +----------------------------------------------------------------------------------------------*/ +gid16 GrSlotAbstract::ActualGlyphForOutput(GrTableManager * ptman) +{ + if (m_chwActual == kInvalidGlyph) + m_chwActual = ptman->ActualGlyphForOutput(m_chwGlyphID); + return m_chwActual; +} +/*---------------------------------------------------------------------------------------------- + We are replacing the old slot with the recipient. Replace the pointers in any attachment + root or attached leaf slots. + OBSOLETE +----------------------------------------------------------------------------------------------*/ +void GrSlotState::FixAttachmentTree(GrSlotState * pslotOld) +{ +#if 0 + pslotOld->m_vpslotAttLeaves.CopyTo(m_vpslotAttLeaves); + for (int islot = 0; islot < m_vpslotAttLeaves.Size(); islot++) + { + Assert(m_vpslotAttLeaves[islot]->m_pslotAttRoot == pslotOld); + m_vpslotAttLeaves[islot]->m_pslotAttRoot = this; + } + + m_pslotAttRoot = pslotOld->m_pslotAttRoot; + if (m_pslotAttRoot) + { + for (int islot = 0; islot < m_pslotAttRoot->m_vpslotAttLeaves.Size(); islot++) + { + if (m_pslotAttRoot->m_vpslotAttLeaves[islot] == pslotOld) + { + m_pslotAttRoot->m_vpslotAttLeaves.Delete(islot); + m_pslotAttRoot->m_vpslotAttLeaves.Push(this); + return; + } + } + Assert(false); // didn't find old slot in the list + } +#endif // 0 +} + +/*---------------------------------------------------------------------------------------------- + Make sure all the values are cached that are needed to be copied to the output slots +----------------------------------------------------------------------------------------------*/ +void GrSlotState::EnsureCacheForOutput(GrTableManager * ptman) +{ + // Make sure the actual glyph ID is set. + gid16 gidActual = ActualGlyphForOutput(ptman); + + // Make sure the glyph metrics are stored. + Font * pfont = ptman->State()->GetFont(); + if (IsLineBreak(ptman->LBGlyphID())) + { + GetGlyphMetric(pfont, kgmetAscent, 0); + GetGlyphMetric(pfont, kgmetDescent, 0); + m_xysGlyphX = 0; + m_xysGlyphY = 0; + m_xysGlyphHeight = 0; + m_xysGlyphWidth = 0; + m_xysAdvX = 0; + m_xysAdvY = 0; + m_fIsSpace = true; + } + else + { + //IsSpace(ptman); // cache this flag--doing bb-top below will do it + + GetGlyphMetric(pfont, kgmetAscent, gidActual); + GetGlyphMetric(pfont, kgmetDescent, gidActual); + GetGlyphMetric(pfont, kgmetBbTop, gidActual); + // call above will also cache all the values below + //GetGlyphMetric(pfont, kgmetBbBottom, gidActual); + //GetGlyphMetric(pfont, kgmetBbLeft, gidActual); + //GetGlyphMetric(pfont, kgmetBbRight, gidActual); + //GetGlyphMetric(pfont, kgmetAdvWidth, gidActual); + //GetGlyphMetric(pfont, kgmetAdvHeight, gidActual); + } +} + +/*---------------------------------------------------------------------------------------------- + Set the associations for the slot. +----------------------------------------------------------------------------------------------*/ +void GrSlotState::Associate(GrSlotState * pslot) +{ + m_vpslotAssoc.clear(); + m_vpslotAssoc.push_back(pslot); +} + +void GrSlotState::Associate(GrSlotState * pslotBefore, GrSlotState * pslotAfter) +{ + m_vpslotAssoc.clear(); + m_vpslotAssoc.push_back(pslotBefore); + m_vpslotAssoc.push_back(pslotAfter); +} + +void GrSlotState::Associate(std::vector<GrSlotState*> & vpslot) +{ + m_vpslotAssoc.clear(); + ///vpslot.CopyTo(m_vpslotAssoc); -- bug in CopyTo, so we do it ourselves: + for (size_t islot = 0; islot < vpslot.size(); ++islot) + { + m_vpslotAssoc.push_back(vpslot[islot]); + } + + // Set its character styles and features from the associated slot. + if (vpslot.size() > 0) // && !m_pslotPrevState + { + std::copy(m_vpslotAssoc[0]->PFeatureBuf(), + m_vpslotAssoc[0]->PFeatureBuf() + m_cnFeat, PFeatureBuf()); + } +} + +/*---------------------------------------------------------------------------------------------- + Clear the associations for the slot. +----------------------------------------------------------------------------------------------*/ +void GrSlotState::ClearAssocs() +{ + m_vpslotAssoc.clear(); +} + +/*---------------------------------------------------------------------------------------------- + Return a list of (ie, add into the vector) all the underlying associations, relative + to the official beginning of the segment. +----------------------------------------------------------------------------------------------*/ +void GrSlotState::AllAssocs(std::vector<int> & vichw) +{ + if (PassModified() == 0) + { + Assert(m_ichwSegOffset != kInvalid); + vichw.push_back(m_ichwSegOffset); + } + else + { + for (size_t i = 0; i < m_vpslotAssoc.size(); ++i) + m_vpslotAssoc[i]->AllAssocs(vichw); + } +} + +/*---------------------------------------------------------------------------------------------- + Return the underlying position of the before-association, relative to the official + beginning of the segment. May be -1, if this slot maps to a character in the previous + segment, or >= the length of the segment, if it maps to a character in the following + segment. +----------------------------------------------------------------------------------------------*/ +int GrSlotState::BeforeAssoc() +{ + GrSlotState * pslot = this; + while (pslot->PassModified() > 0) + { + pslot = pslot->RawBeforeAssocSlot(); + if (pslot == NULL) + { + return kPosInfinity; + } + } + Assert(pslot->m_ichwSegOffset != kInvalid); + return pslot->m_ichwSegOffset; +} + +/*---------------------------------------------------------------------------------------------- + Return the underlying position of the after-association, relative to the official + beginning of the segment. May be -1, if this slot maps to a character in the previous + segment, or >= the length of the segment, if it maps to a character in the following + segment. +----------------------------------------------------------------------------------------------*/ +int GrSlotState::AfterAssoc() +{ + GrSlotState * pslot = this; + while (pslot->PassModified() > 0) + { + pslot = pslot->RawAfterAssocSlot(); + if (pslot == NULL) + { + return kNegInfinity; + } + } + Assert(pslot->m_ichwSegOffset != kInvalid); + return pslot->m_ichwSegOffset; +} + +/*---------------------------------------------------------------------------------------------- + It is possible to get into a state where we are associated with an invalid state. + For instance, slot C may be associated with slots B1 and B2, but slot B1 is not associated + with any earlier slot, in which case slot C should remove the association with B1 and just + be associated with B2. +----------------------------------------------------------------------------------------------*/ +void GrSlotState::CleanUpAssocs() +{ + for (size_t i = 0; i < m_vpslotAssoc.size(); i++) + m_vpslotAssoc[i]->CleanUpAssocs(); + + GrSlotState * pslot; + + pslot = RawBeforeAssocSlot(); + while (pslot && BeforeAssoc() == kPosInfinity) + { + // The before association is bogus--delete it. + m_vpslotAssoc.erase(m_vpslotAssoc.begin()); + pslot = RawBeforeAssocSlot(); + } + + pslot = RawAfterAssocSlot(); + while (pslot && AfterAssoc() == kNegInfinity) + { + // The after association is bogus--delete it. + m_vpslotAssoc.pop_back(); + pslot = RawAfterAssocSlot(); + } +} + +/*---------------------------------------------------------------------------------------------- + Fill in the array of the given output slot with the underlying positions of the + ligature components, relative to the official beginning of the segment. + Positions may be < 0, if this slot maps to a character in the previous segment, + or >= the length of the segment, if it maps to a character in the following segment. +----------------------------------------------------------------------------------------------*/ +void GrSlotState::SetComponentRefsFor(GrSlotOutput * pslout, int slatiArg) +{ + if (PassModified() > 0) + { + GrSlotState * pslot; + int slati; + if (HasComponents()) + { + for (int iComponent = 0; iComponent < m_cnCompPerLig; iComponent++) + { + pslot = CompRefSlot(iComponent); + slati = Slati(iComponent); + if (pslot) + { + Assert(slati != -1); + Assert(PassModified() >= pslot->PassModified()); + pslot->SetComponentRefsFor(pslout, slati); + } + } + } + else + { + // Follow the chain back through the passes. + pslot = RawBeforeAssocSlot(); + if (pslot) + { + Assert(PassModified() >= pslot->PassModified()); + // Passing slati here is definitely needed for our Arabic font: for instance when + // you type something like "mla", so the l is transformed into a temporary glyph + // before creating the ligature. However, this seems to have broken something, + // which I will probably find eventually. :-( + pslot->SetComponentRefsFor(pslout, slatiArg); + //pslot->SetComponentRefsFor(pslout, -1); + } + } + } + else + { + Assert(m_ichwSegOffset != kInvalid); + pslout->AddComponentReference(m_ichwSegOffset, slatiArg); + } +} + +/*---------------------------------------------------------------------------------------------- + Fill in the vector with the underlying positions of the + ligature components, relative to the official beginning of the segment. + Positions may be < 0, if this slot maps to a character in the previous segment, + or >= the length of the segment, if it maps to a character in the following segment. + ENHANCE: merge with method above. +----------------------------------------------------------------------------------------------*/ +void GrSlotState::AllComponentRefs(std::vector<int> & vichw) +{ + if (PassModified() > 0) + { + GrSlotState * pslot; + if (HasComponents()) + { + for (int iComponent = 0; iComponent < m_cnCompPerLig; iComponent++) + { + pslot = CompRefSlot(iComponent); + if (pslot) + { + Assert(PassModified() >= pslot->PassModified()); + pslot->AllComponentRefs(vichw); + } + } + } + else + { + // ENHANCE: Strictly speaking we should probably have separate before- + // and after-lists for the components, but I think it would be pretty + // rare to have both deletion and ligatures overlapping, so I'm not + // going to bother with it for now. + //for (int islot = 0; islot < AssocsSize(); islot++) + for (int islot = 0; islot < 1; islot++) + { + m_vpslotAssoc[islot]->AllComponentRefs(vichw); + } + } + } + else + { + Assert(m_ichwSegOffset != kInvalid); + vichw.push_back(m_ichwSegOffset); + } +} + +/*---------------------------------------------------------------------------------------------- + Get the value of the component.???.ref attribute for the slot. + Note that 'i' is the local index for the component as defined for this glyph. +----------------------------------------------------------------------------------------------*/ +GrSlotState * GrSlotAbstract::CompRefSlot(int i) +{ + Assert(i < m_cnCompPerLig); + if (i < m_cnCompPerLig) + return CompRef(i); + else + return NULL; +} + +/*---------------------------------------------------------------------------------------------- + Set the value of the component.???.ref attribute for the slot. +----------------------------------------------------------------------------------------------*/ +void GrSlotState::SetCompRefSlot(GrTableManager * ptman, int slati, GrSlotState * pslotComp) +{ + // Convert the global identifer for the component to the index for this glyph. + int icomp = ptman->ComponentIndexForGlyph(GlyphID(), slati); + Assert(icomp != -1); + if (icomp == -1) + // Component not defined for this glyph--ignore. + return; + Assert(icomp < m_cnCompPerLig); + + if (m_fHasComponents == false) + { + // None have been set yet; initialize them. + for (int iLoop = 0; iLoop < m_cnCompPerLig; iLoop++) + { + SetCompRef(iLoop, NULL); + SetSlati(iLoop, -1); + } + } + + m_fHasComponents = true; + int iLoop; + for (iLoop = 0; iLoop < m_cnCompPerLig; iLoop++) + { + if (Slati(iLoop) == slati) + break; + if (Slati(iLoop) == -1) + break; + } + //if (icomp < m_cnCompPerLig) + if (iLoop < m_cnCompPerLig) + { + //SetCompRef(icomp, pslotComp); + SetCompRef(iLoop, pslotComp); + SetSlati(iLoop, slati); + } +} + +/*---------------------------------------------------------------------------------------------- + Return the underlying position for the slot, relative to the official beginning of the + segment. +----------------------------------------------------------------------------------------------*/ +int GrSlotState::SegOffset() +{ + if (m_ichwSegOffset == kInvalid) + { + Assert(m_pslotPrevState); + Assert(m_ipassModified > 0); + return m_pslotPrevState->SegOffset(); + } + else + { + Assert(m_pslotPrevState == NULL); + Assert(m_ipassModified == 0); + return m_ichwSegOffset; + } +} + +/*---------------------------------------------------------------------------------------------- + Return the value of the glyph attribute (in design units, if this is a measurement). +----------------------------------------------------------------------------------------------*/ +int GrSlotState::GlyphAttrValueEmUnits(GrTableManager * ptman, int nAttrID) +{ + return ptman->GlyphAttrValue(m_chwGlyphID, nAttrID); +} + +/*---------------------------------------------------------------------------------------------- + Return the value of the glyph metric, in design coordinates (ie, based on the font's + em-square). +----------------------------------------------------------------------------------------------*/ +int GrSlotState::GlyphMetricEmUnits(GrTableManager * ptman, int nMetricID) +{ + int mValue; + if (ptman->State()->GetFont()) + { + // Get the actual metric, possibly adjusted for hinting, then convert back to + // design units. + float xysValue = GlyphMetricLogUnits(ptman, nMetricID); + mValue = ptman->LogToEmUnits(xysValue); + } + else + // Ask the font directly. + ////mValue = ??? + mValue = 0; + + return mValue; +} + +/*---------------------------------------------------------------------------------------------- + Return the value of the glyph attribute, converted to source device coordinates. + The attribute is assumed to be one whose value is a measurement. +----------------------------------------------------------------------------------------------*/ +float GrSlotState::GlyphAttrValueLogUnits(GrTableManager * ptman, int nAttrID) +{ + int mValue = GlyphAttrValueEmUnits(ptman, nAttrID); + float xysRet = ptman->EmToLogUnits(mValue); + return xysRet; +} + +/*---------------------------------------------------------------------------------------------- + Return the value of the glyph metric, in the source device's logical units. +----------------------------------------------------------------------------------------------*/ +float GrSlotState::GlyphMetricLogUnits(GrTableManager * ptman, int nMetricID) +{ +#ifdef OLD_TEST_STUFF + if (ptman->GlyphTable() == NULL) + return 0; +#endif // OLD_TEST_STUFF + +#ifdef _DEBUG + if (IsLineBreak(ptman->LBGlyphID())) + { + Warn("Getting metrics of line-break slot"); + } +#endif // _DEBUG + if (IsLineBreak(ptman->LBGlyphID())) + { + return 0; + } + + return GetGlyphMetric(ptman->State()->GetFont(), nMetricID, + ActualGlyphForOutput(ptman)); +} + + +float GrSlotOutput::GlyphMetricLogUnits(Font * pfont, int nMetricID) +{ + Assert(m_chwActual != kInvalidGlyph); + if (m_chwActual == kInvalidGlyph) + return 0; + + return GetGlyphMetric(pfont, nMetricID, m_chwActual); +} + +float GrSlotOutput::GlyphMetricLogUnits(int gmet) +{ + // When the font is not passed as an argument, the values better be cached! + + switch (gmet) + { // There may be off by one errors below, depending on what width and height mean + case kgmetLsb: + return m_xysGlyphX; + case kgmetRsb: + return (m_xysAdvX - m_xysGlyphX - m_xysGlyphWidth); + case kgmetBbTop: + return m_xysGlyphY; + case kgmetBbBottom: + return (m_xysGlyphY - m_xysGlyphHeight); + case kgmetBbLeft: + return m_xysGlyphX; + case kgmetBbRight: + return (m_xysGlyphX + m_xysGlyphWidth); + case kgmetBbHeight: + return m_xysGlyphHeight; + case kgmetBbWidth: + return m_xysGlyphWidth; + case kgmetAdvWidth: + return m_xysAdvX; + case kgmetAdvHeight: + return m_xysAdvY; + default: + Warn("GetGlyphMetric was asked for an illegal metric."); + }; + + return 0; +} + + +float GrSlotAbstract::GetGlyphMetric(Font * pfont, int nMetricID, gid16 chwGlyphID) +{ + //GrResult res; + float xysRet = 0; + + GlyphMetric gmet = GlyphMetric(nMetricID); + + float yAscent, yDescent; + gr::Point ptAdvances; + gr::Rect rectBb; + + if (kgmetAscent == gmet) + { + if (m_xysFontAscent != -1) + return m_xysFontAscent; + //GrResult res = pgg->get_FontAscent(&xysRet); + //if (ResultFailed(res)) + //{ + // WARN(res); + // return 0; + //} + pfont->getFontMetrics(&yAscent); + m_xysFontAscent = xysRet; + if (pfont) + m_xysFontAscent = yAscent; + return xysRet; + } + + if (kgmetDescent == gmet) + { + if (m_xysFontDescent != -1) + return m_xysFontDescent; + //GrResult res = pgg->get_FontDescent(&xysRet); + //if (ResultFailed(res)) + //{ + // WARN(res); + // return 0; + // m_xysFontDescent = xysRet; + //} + pfont->getFontMetrics(NULL, &yDescent); + m_xysFontDescent = xysRet; + if (pfont) + m_xysFontDescent = yDescent; + return xysRet; + } + + if (m_xysGlyphWidth == -1) + { + //res = pgg->GetGlyphMetrics(chwGlyphID, &m_xysGlyphWidth, &m_xysGlyphHeight, + // &m_xysGlyphX, &m_xysGlyphY, &m_xysAdvX, &m_xysAdvY); + //if (ResultFailed(res)) + //{ + // //WARN(res); + // //return 0; + // ThrowHr(WarnHr(res)); + //} + + pfont->getGlyphMetrics(chwGlyphID, rectBb, ptAdvances); + + m_xysGlyphX = rectBb.left; + m_xysGlyphY = rectBb.top; + m_xysGlyphWidth = (rectBb.right - rectBb.left); + m_xysGlyphHeight = (rectBb.top - rectBb.bottom); + m_xysAdvX = ptAdvances.x; + m_xysAdvY = ptAdvances.y; + + m_fIsSpace = (0 == m_xysGlyphX && 0 == m_xysGlyphY); // should agree with test done in IsSpace() below + + if (m_fIsSpace) + { + // White space glyph - only case where nGlyphX == nGlyphY == 0 + // nGlyphWidth & nGlyphHeight are always set to 16 for unknown reasons, so correct. + m_xysGlyphWidth = m_xysGlyphHeight = 0; + } + } + + switch (gmet) + { // There may be off by one errors below, depending on what width and height mean + case kgmetLsb: + return m_xysGlyphX; + case kgmetRsb: + return (m_xysAdvX - m_xysGlyphX - m_xysGlyphWidth); + case kgmetBbTop: + return m_xysGlyphY; + case kgmetBbBottom: + return (m_xysGlyphY - m_xysGlyphHeight); + case kgmetBbLeft: + return m_xysGlyphX; + case kgmetBbRight: + return (m_xysGlyphX + m_xysGlyphWidth); + case kgmetBbHeight: + return m_xysGlyphHeight; + case kgmetBbWidth: + return m_xysGlyphWidth; + case kgmetAdvWidth: + return m_xysAdvX; + case kgmetAdvHeight: + return m_xysAdvY; + default: + Warn("GetGlyphMetric was asked for an illegal metric."); + }; + + return 0; +} + +/*---------------------------------------------------------------------------------------------- + Test the glyph id to see if it is a white space glyph. +----------------------------------------------------------------------------------------------*/ +int GrSlotState::IsSpace(GrTableManager * ptman) +{ + gid16 gidActual = ActualGlyphForOutput(ptman); + + ////if (m_xysGlyphWidth == -1) + ////{ + //// res = ptman->State()->GraphicsObject()->GetGlyphMetrics(gidActual, + //// &m_xysGlyphWidth, &m_xysGlyphHeight, + //// &m_xysGlyphX, &m_xysGlyphY, &m_xysAdvX, &m_xysAdvY); + //// if (ResultFailed(res)) + //// { + //// WARN(res); + //// return 2; // will test as true but can be distinguished by separate value + //// } + + //// gr::Point ptAdvances; + //// gr::Rect rectBb; + //// ptman->State()->Font()->getGlyphMetrics(gidActual, rectBb, ptAdvances); + ////} + + GetGlyphMetric(ptman->State()->GetFont(), kgmetBbLeft, gidActual); + // One call is enough to cache the information. + //GetGlyphMetric(ptman->State()->Font(), kgmetBbBottom, gidActual); + + // should agree with test done in GetGlyphMetric() above + //////m_fIsSpace = (0 == m_xysGlyphX && 0 == m_xysGlyphY); + + return m_fIsSpace; +} + +bool GrSlotOutput::IsSpace() +{ + return m_fIsSpace; +} + +/*---------------------------------------------------------------------------------------------- + If any of the attach attributes have been modified, fix things up. Set up the attachment + tree, and set any needed default positions. Zap the cached positions of any following + glyphs in the stream. + + @param psstrm - the stream in which the modifications were made + @param islotThis - the index of the attached (leaf) slot in the stream; + -1 if we don't know +----------------------------------------------------------------------------------------------*/ +void GrSlotState::HandleModifiedPosition(GrTableManager * ptman, + GrSlotStream * psstrmIn, GrSlotStream * psstrmOut, int islotThis) +{ + if (!m_fAttachMod && !m_fShiftMod) + return; + + if (islotThis == -1) + { + for (int islot = 0; islot < psstrmOut->WritePos(); islot++) + { + if (psstrmOut->SlotAt(islot) == this) + { + islotThis = islot; + break; + } + } + Assert(islotThis > -1); + } + + if (m_fAttachMod) + { + // Attachments. + + // Note that it doesn't make sense to attach a slot to itself, so if the value of + // the attach.to attribute is 0, that means clear the attachment. + GrSlotState * pslotNewRoot = AttachRoot(psstrmOut); + + AttachToRoot(ptman, psstrmOut, pslotNewRoot); + + if (pslotNewRoot) + { + // If this is an attachment with no positions, attach the two glyphs side by side + // in the appropriate order. + bool fRtl = ((pslotNewRoot->PostBidiDirLevel(ptman) % 2) != 0); + if ((fRtl && m_srAttachTo < 0) || (!fRtl && m_srAttachTo > 0)) + { + // The root is on the right, the leaf is on the left. + if (m_mAttachAtX == kNotYetSet && m_nAttachAtGpoint == kNotYetSet) + m_mAttachAtX = 0; + if (m_mAttachWithX == kNotYetSet && m_nAttachWithGpoint == kNotYetSet) + m_mAttachWithX = short(AdvanceX(ptman)); + } + else + { + // The root is on the left, the leaf is on the right. + if (m_mAttachAtX == kNotYetSet && m_nAttachAtGpoint == kNotYetSet) + m_mAttachAtX = short(pslotNewRoot->AdvanceX(ptman)); + if (m_mAttachWithX == kNotYetSet && m_nAttachWithGpoint == kNotYetSet) + m_mAttachWithX = 0; + } + } + } + else + { + // Shifting, or changing the advance width. + Assert(m_fShiftMod); + EnsureLocalAttachmentTree(ptman, psstrmIn, psstrmOut, islotThis); + ZapMetricsAndPositionDownToBase(psstrmOut); + ZapMetricsOfLeaves(psstrmOut); + } + + if (psstrmOut->m_ipass == ptman->NumberOfPasses() - 1) + ptman->InitPosCache(); // cached position is most likely no longer valid + + // Invalidate the positions of this and any following glyphs. + for (int islot = islotThis + 1; islot < psstrmOut->WritePos(); islot++) + psstrmOut->SlotAt(islot)->ZapPosition(); + + m_fAttachMod = false; + m_fShiftMod = false; +} + +/*---------------------------------------------------------------------------------------------- + The shift attribute of a slot has been modified. Make sure any slots that are part of the + same attachment cluster are local to this stream. The reason for this is so that the + position calculations stay consistent within the stream. +----------------------------------------------------------------------------------------------*/ +void GrSlotState::EnsureLocalAttachmentTree(GrTableManager * ptman, + GrSlotStream * psstrmIn, GrSlotStream * psstrmOut, int islotThis) +{ + if (m_dislotRootFixed) + { + GrSlotState * pslotRoot = SlotAtOffset(psstrmOut, m_dislotRootFixed); + psstrmOut->EnsureLocalCopy(ptman, pslotRoot, psstrmIn); + pslotRoot = SlotAtOffset(psstrmOut, m_dislotRootFixed); // get the new one! + pslotRoot->EnsureLocalAttachmentTree(ptman, psstrmIn, psstrmOut, islotThis + m_dislotRootFixed); + } + for (size_t islot = 0; islot < m_vdislotAttLeaves.size(); islot++) + { + GrSlotState * pslotLeaf = SlotAtOffset(psstrmOut, m_vdislotAttLeaves[islot]); + psstrmOut->EnsureLocalCopy(ptman, pslotLeaf, psstrmIn); + } +} + +/*---------------------------------------------------------------------------------------------- + The recipient slot is being attached to the argument slot. + NOTE: the caller is responsible to zap the cached positions of following glyphs + in the stream. +----------------------------------------------------------------------------------------------*/ +void GrSlotState::AttachToRoot(GrTableManager * ptman, GrSlotStream * psstrm, + GrSlotState * pslotNewRoot) +{ + GrSlotState * pslotOldRoot = (m_dislotRootFixed == 0) ? + NULL : + SlotAtOffset(psstrm, m_dislotRootFixed); + + if (pslotOldRoot) + { + if (pslotOldRoot != pslotNewRoot) + pslotOldRoot->RemoveLeaf(m_dislotRootFixed); + pslotOldRoot->ZapMetricsAndPositionDownToBase(psstrm); + pslotOldRoot->ZapMetricsOfLeaves(psstrm); + } + + ZapCompositeMetrics(); + + if (pslotNewRoot && pslotNewRoot != pslotOldRoot) + { + pslotNewRoot->AddLeaf(m_srAttachTo); + pslotNewRoot->ZapMetricsAndPositionDownToBase(psstrm); + pslotNewRoot->ZapMetricsOfLeaves(psstrm); + } + + m_dislotRootFixed = m_srAttachTo; +} + +/*---------------------------------------------------------------------------------------------- + Return the absolute position of the glyph, relative to the start of the segment, + in font design units. +----------------------------------------------------------------------------------------------*/ +void GrSlotState::Position(GrTableManager * ptman, + GrSlotStream * psstrmOut, int * pmXPos, int * pmYPos) +{ + Assert(!m_fAttachMod); // the attachment tree should be set up + Assert(!m_fShiftMod); + + float xsWidth, xsVisWidth; + if (m_xsPositionX == kNegInfFloat || m_ysPositionY == kNegInfFloat) + ptman->CalcPositionsUpTo(psstrmOut->m_ipass, this, &xsWidth, &xsVisWidth); + + *pmXPos = ptman->LogToEmUnits(m_xsPositionX); + *pmYPos = ptman->LogToEmUnits(m_ysPositionY); +} + +/*---------------------------------------------------------------------------------------------- + Recalculate the metrics for this slot, which has attachments on it (or possibly + did in the past). +----------------------------------------------------------------------------------------------*/ +void GrSlotState::AdjustRootMetrics(GrTableManager * ptman, GrSlotStream * psstrm) +{ + Assert(m_dislotRootFixed == m_srAttachTo); + GrSlotState * pslotRoot = AttachRoot(psstrm); + CalcRootMetrics(ptman, psstrm, kPosInfinity); + if (pslotRoot) + pslotRoot->AdjustRootMetrics(ptman, psstrm); +} + +/*---------------------------------------------------------------------------------------------- + Calculate the composite metrics for this slot. + + @param psstrm - stream for which we are calculating it + @param nLevel - attachment level we are asking for; kPosInifinity means all levels + @param fThorough - true: do a thorough recalculation; false: don't recalculate + metrics for leaves (are they assumed to be accurate???) + --currently not used +----------------------------------------------------------------------------------------------*/ +void GrSlotState::CalcCompositeMetrics(GrTableManager * ptman, GrSlotStream * psstrm, + int nLevel, bool fThorough) +{ + if (m_nCompositeLevel == nLevel) + return; + + if (fThorough) + { + Assert(m_dislotRootFixed == m_srAttachTo); + GrSlotState * pslotRoot = AttachRoot(psstrm); + + InitMetrics(ptman, pslotRoot); + + for (size_t islot = 0; islot < m_vdislotAttLeaves.size(); islot++) + { + GrSlotState * pslotLeaf = SlotAtOffset(psstrm, m_vdislotAttLeaves[islot]); + if (pslotLeaf->AttachLevel() <= nLevel) + pslotLeaf->CalcCompositeMetrics(ptman, psstrm, nLevel, fThorough); + else + // this slot will be ignored in the composite metrics + pslotLeaf->ZapRootMetrics(); + } + CalcRootMetrics(ptman, psstrm, nLevel); + + m_nCompositeLevel = nLevel; + } + else + { + Assert(false); // for now + + // Don't bother with the leaves. + InitRootMetrics(ptman); + } +} + +/*---------------------------------------------------------------------------------------------- + Calculate the metrics for this node and all its leaf nodes. +----------------------------------------------------------------------------------------------*/ +void GrSlotState::CalcRootMetrics(GrTableManager * ptman, GrSlotStream * psstrm, int nLevel) +{ + for (size_t idislot = 0; idislot < m_vdislotAttLeaves.size(); idislot++) + { + GrSlotState * pslot = SlotAtOffset(psstrm, m_vdislotAttLeaves[idislot]); + if (pslot->AttachLevel() > nLevel) + continue; + + m_xsClusterXOffset = min(m_xsClusterXOffset, pslot->m_xsClusterXOffset); + if (!pslot->m_fIgnoreAdvance) + { + m_xsClusterAdv = max( + m_xsClusterAdv, + pslot->m_xsClusterAdv + m_xsRootShiftX); + } + m_xsClusterBbLeft = min(m_xsClusterBbLeft, pslot->m_xsClusterBbLeft); + m_xsClusterBbRight = max(m_xsClusterBbRight, pslot->m_xsClusterBbRight); + m_ysClusterBbTop = max(m_ysClusterBbTop, pslot->m_ysClusterBbTop); + m_ysClusterBbBottom = min(m_ysClusterBbBottom, pslot->m_ysClusterBbBottom); + } +} + +/*---------------------------------------------------------------------------------------------- + Reset the cluster metrics of this slot. +----------------------------------------------------------------------------------------------*/ +void GrSlotState::InitMetrics(GrTableManager * ptman, GrSlotState * pslotRoot) +{ + InitLeafMetrics(ptman, pslotRoot); + InitRootMetrics(ptman); +} + +/*---------------------------------------------------------------------------------------------- + Initialize the variables that store the offsets of just this node (ignoring any of its + leaves) relative to the cluster base. +----------------------------------------------------------------------------------------------*/ +void GrSlotState::InitLeafMetrics(GrTableManager * ptman, GrSlotState * pslotRoot) +{ + float xsShiftX = ptman->EmToLogUnits(ShiftX()); + float ysShiftY = ptman->EmToLogUnits(ShiftY()); + + if (ptman->RightToLeft()) + xsShiftX *= -1; + + if (IsBase()) + { + // These two are actually meaningless numbers, for the base, but set them + // to something reasonable. + m_xsOffsetX = xsShiftX; + m_ysOffsetY = ysShiftY; + + m_xsRootShiftX = xsShiftX; + m_ysRootShiftY = ysShiftY; + Assert(!IsLineBreak(ptman->LBGlyphID()) + || (m_xsRootShiftX == 0 && m_ysRootShiftY == 0)); + return; + } + + Assert(!IsLineBreak(ptman->LBGlyphID())); + + // Hint-adjusted logical coordinates equivalent to attach.at and attach.with attributes. + // If attach.at or attach.with attributes were unset, the defaults for a side-by-side + // attachment should have been supplied in HandleModifiedPosition(). + float xsAttAtX, ysAttAtY, xsAttWithX, ysAttWithY; + AttachLogUnits(ptman, pslotRoot, &xsAttAtX, &ysAttAtY, &xsAttWithX, &ysAttWithY); + + m_xsOffsetX = xsAttAtX - xsAttWithX; + m_xsOffsetX += pslotRoot->m_xsOffsetX; + m_xsOffsetX += xsShiftX; + + m_ysOffsetY = ysAttAtY - ysAttWithY; + m_ysOffsetY += pslotRoot->m_ysOffsetY; + m_ysOffsetY += ysShiftY; + + // Cumulative effect of shifts on this and all base nodes: + m_xsRootShiftX = pslotRoot->m_xsRootShiftX + xsShiftX; + m_ysRootShiftY = pslotRoot->m_ysRootShiftY + ysShiftY; +} + +/*---------------------------------------------------------------------------------------------- + Initialize the variables that store the offsets of this node taking into account its + leaves; these are relative to the cluster base. +----------------------------------------------------------------------------------------------*/ +void GrSlotState::InitRootMetrics(GrTableManager * ptman) +{ + if (IsLineBreak(ptman->LBGlyphID())) + { + m_fIgnoreAdvance = true; + m_xsClusterXOffset = 0; + m_xsClusterAdv = 0; + m_xsClusterBbLeft = 0; + m_xsClusterBbRight = 0; + m_ysClusterBbTop = 0; + m_ysClusterBbBottom = 0; + return; + } + + float xsAdvanceX = ptman->EmToLogUnits(AdvanceX(ptman)); + + // If the glyph's advance width is zero, then we NEVER want it to have any affect, + // even if the glyph is attached way out to the right of its base's advance. + m_fIgnoreAdvance = (xsAdvanceX == 0); + + float xsBbLeft = GlyphMetricLogUnits(ptman, kgmetBbLeft); + float xsBbRight = GlyphMetricLogUnits(ptman, kgmetBbRight); + float ysBbTop = GlyphMetricLogUnits(ptman, kgmetBbTop); + float ysBbBottom = GlyphMetricLogUnits(ptman, kgmetBbBottom); + + // Any shifts should be ignored for the sake of calculating actual position or width, + // hence we subtract the cumulative effect of the shifts. + m_xsClusterXOffset = m_xsOffsetX - m_xsRootShiftX; + m_xsClusterAdv = m_xsOffsetX + xsAdvanceX - m_xsRootShiftX; + + m_xsClusterBbLeft = m_xsOffsetX + xsBbLeft; + m_xsClusterBbRight = m_xsOffsetX + xsBbRight; + m_ysClusterBbTop = m_ysOffsetY + ysBbTop; + m_ysClusterBbBottom = m_ysOffsetY + ysBbBottom; +} + +/*---------------------------------------------------------------------------------------------- + X-offset of a single glyph relative to the previous advance position. +----------------------------------------------------------------------------------------------*/ +float GrSlotState::GlyphXOffset(GrSlotStream * psstrm, float fakeItalicRatio) +{ + float xsRet = Base(psstrm)->ClusterRootOffset() + m_xsOffsetX; + + // fake an italic slant + xsRet += m_ysOffsetY * fakeItalicRatio; + + return xsRet; +} + +/*---------------------------------------------------------------------------------------------- + Y-offsets of a single glyph relative to the previous advance position. +----------------------------------------------------------------------------------------------*/ +float GrSlotState::GlyphYOffset(GrSlotStream * psstrm) +{ + return m_ysOffsetY; +} + +/*---------------------------------------------------------------------------------------------- + Return the offset of the last leaf of the cluster, relative to this slot, which is + the base. Return kNegInfinity if there are not enough slots in the stream to tell. +----------------------------------------------------------------------------------------------*/ +int GrSlotState::LastLeafOffset(GrSlotStream * psstrm) +{ + int islotRet = 0; + for (size_t idislot = 0; idislot < m_vdislotAttLeaves.size(); idislot++) + { + int dislot = m_vdislotAttLeaves[idislot]; + Assert(dislot != 0); + if (!psstrm->HasSlotAtPosPassIndex(PosPassIndex() + dislot)) + return kNegInfinity; + GrSlotState * pslotLeaf = SlotAtOffset(psstrm, dislot); + int islotTmp = pslotLeaf->LastLeafOffset(psstrm); + if (islotTmp == kNegInfinity) + return kNegInfinity; + islotRet = max(islotRet, (dislot + islotTmp)); + } + return islotRet; +} + +/*---------------------------------------------------------------------------------------------- + Return the attach positions in the device context's logical units, + adjusted for hinting if possible. + + Note that m_nAttachAt/WithGpoint = 0 is always an invalid value (resulting from a + glyph attribute that was defined in terms of x/y coordinates that didn't map to an + actual on-curve point). Therefore the x/y coordinates should be used to do the + attachment. In the case where we actually want point #0 on the curve, + m_nAttachAt/WithGpoint will have the special value 'kGpointZero' (the glyph attribute + is defined this way). +----------------------------------------------------------------------------------------------*/ +void GrSlotState::AttachLogUnits(GrTableManager * ptman, GrSlotState * pslotRoot, + float * pxsAttAtX, float * pysAttAtY, + float * pxsAttWithX, float * pysAttWithY) +{ + if (m_nAttachAtGpoint == kNotYetSet || m_nAttachAtGpoint == 0) + { + // Use x- and y-coordinates; no adjustment for hinting is done. + int mX = m_mAttachAtX + m_mAttachAtXOffset; + int mY = m_mAttachAtY + m_mAttachAtYOffset; + + *pxsAttAtX = ptman->EmToLogUnits(mX); + *pysAttAtY = ptman->EmToLogUnits(mY); + } + else + { + // Look up the actual on-curve point. + int nGpoint = m_nAttachAtGpoint; + if (nGpoint == kGpointZero) + nGpoint = 0; + bool fImpl = ptman->GPointToXY(pslotRoot->GlyphID(), nGpoint, pxsAttAtX, pysAttAtY); + + // Debuggers: + //int mXTmpAt = m_mAttachAtX + m_mAttachAtXOffset; + //int mYTmpAt = m_mAttachAtY + m_mAttachAtYOffset; + //int xsAttAtXTmp = ptman->EmToLogUnits(mXTmpAt); + //int ysAttAtYTmp = ptman->EmToLogUnits(mYTmpAt); + //fImpl = false; + + if (!fImpl) + { + // Fall back to using x- and y-coordinates; no adjustment for hinting. + int mX = m_mAttachAtX + m_mAttachAtXOffset; + int mY = m_mAttachAtY + m_mAttachAtYOffset; + *pxsAttAtX = ptman->EmToLogUnits(mX); + *pysAttAtY = ptman->EmToLogUnits(mY); + } + else + { + // Adjust by offsets. + *pxsAttAtX += ptman->EmToLogUnits(m_mAttachAtXOffset); + *pysAttAtY += ptman->EmToLogUnits(m_mAttachAtYOffset); + } + } + + if (m_nAttachWithGpoint == kNotYetSet || m_nAttachWithGpoint == 0) + { + // Use x- and y-coordinates; no adjustment for hinting is done. + int mX = m_mAttachWithX + m_mAttachWithXOffset; + int mY = m_mAttachWithY + m_mAttachWithYOffset; + + *pxsAttWithX = ptman->EmToLogUnits(mX); + *pysAttWithY = ptman->EmToLogUnits(mY); + } + else + { + // Look up the actual on-curve point. + int nGpoint = m_nAttachWithGpoint; + if (nGpoint == kGpointZero) + nGpoint = 0; + bool fImpl = ptman->GPointToXY(m_chwGlyphID, nGpoint, pxsAttWithX, pysAttWithY); + + // Debuggers: + //int mXTmpWith = m_mAttachWithX + m_mAttachWithXOffset; + //int mYTmpWith = m_mAttachWithY + m_mAttachWithYOffset; + //int xsAttWithXTmp = ptman->EmToLogUnits(mXTmpWith); + //int ysAttWithYTmp = ptman->EmToLogUnits(mYTmpWith); + //fImpl = false; + + if (!fImpl) + { + // Fall back to using x- and y-coordinates; no adjustment for hinting. + int mX = m_mAttachWithX + m_mAttachWithXOffset; + int mY = m_mAttachWithY + m_mAttachWithYOffset; + *pxsAttWithX = ptman->EmToLogUnits(mX); + *pysAttWithY = ptman->EmToLogUnits(mY); + } + else + { + // Adjust by offsets. + *pxsAttWithX += ptman->EmToLogUnits(m_mAttachWithXOffset); + *pysAttWithY += ptman->EmToLogUnits(m_mAttachWithYOffset); + } + } +} + +/*---------------------------------------------------------------------------------------------- + Return the slot that is 'dislot' slots away from this slot in the given stream. + Only valid for streams that are the input to or output of positioning passes. +----------------------------------------------------------------------------------------------*/ +GrSlotState * GrSlotState::SlotAtOffset(GrSlotStream * psstrm, int dislot) +{ + Assert(psstrm->m_fUsedByPosPass); + return psstrm->SlotAtPosPassIndex(PosPassIndex() + dislot); +} + +/*---------------------------------------------------------------------------------------------- + If the direction level has not been calculated at this point, assume it is the + top direction. This should only happen if there was no bidi pass to set it. + This method should only be called by the positioning passes; it assumes any bidi + pass has been run. +----------------------------------------------------------------------------------------------*/ +int GrSlotState::PostBidiDirLevel(GrTableManager * ptman) +{ + if (m_nDirLevel == -1) + { + Assert(!ptman->HasBidiPass()); + return ptman->TopDirectionLevel(); + } + return m_nDirLevel; +} + +/*---------------------------------------------------------------------------------------------- + Return true if this glyph represents a LRM code. +----------------------------------------------------------------------------------------------*/ +bool GrSlotState::IsLrm() +{ + if (PassModified() == 0) + return (m_nUnicode == knLRM); + return m_pslotPrevState->IsLrm(); +} + +/*---------------------------------------------------------------------------------------------- + Return true if this glyph represents a RLM code. +----------------------------------------------------------------------------------------------*/ +bool GrSlotState::IsRlm() +{ + if (PassModified() == 0) + return (m_nUnicode == knRLM); + return m_pslotPrevState->IsRlm(); +} + + +/*---------------------------------------------------------------------------------------------- + Used by GlyphInfo +----------------------------------------------------------------------------------------------*/ +//float GrSlotOutput::AdvanceX(Segment * pseg) +//{ +// return pseg->EmToLogUnits(m_mAdvanceX); +//} +//float GrSlotOutput::AdvanceY(Segment * pseg) +//{ +// return pseg->EmToLogUnits(m_mAdvanceY); +//} +float GrSlotOutput::MaxStretch(Segment * pseg, int level) +{ + Assert(level == 0); + return (level == 0) ? pseg->EmToLogUnits(m_mJStretch0) : 0; +} +float GrSlotOutput::MaxShrink(Segment * pseg, int level) +{ + Assert(level == 0); + return (level == 0) ? pseg->EmToLogUnits(m_mJShrink0) : 0; +} +float GrSlotOutput::StretchStep(Segment * pseg, int level) +{ + Assert(level == 0); + return (level == 0) ? pseg->EmToLogUnits(m_mJStep0) : 0; +} +int GrSlotOutput::JustWeight(int level) +{ + Assert(level == 0); + return (level == 0) ? m_nJWeight0 : 0; +} +float GrSlotOutput::JustWidth(Segment * pseg, int level) +{ + Assert(level == 0); + return (level == 0) ? pseg->EmToLogUnits(m_mJWidth0) : 0; +} +float GrSlotOutput::MeasureSolLogUnits(Segment * pseg) +{ + return pseg->EmToLogUnits(m_mMeasureSol); +} +float GrSlotOutput::MeasureEolLogUnits(Segment * pseg) +{ + return pseg->EmToLogUnits(m_mMeasureEol); +} + +/*---------------------------------------------------------------------------------------------- + Make a copy of the GrSlotOutput. This is used in making an identical copy of the segment. + Warning: this function will break if GrSlotState and subclasses are given virtual + methods. In that case, we will need to copy from the address of the first variable in + GrSlotAbstract. +----------------------------------------------------------------------------------------------*/ +void GrSlotOutput::ExactCopyFrom(GrSlotOutput * pslout, u_intslot * pnVarLenBuf, int cnExtraPerSlot) +{ + // The chunk of the object from GrSlotAbstract can be copied exactly, + // except for the variable-length buffer. + *this = *pslout; + m_prgnVarLenBuf = pnVarLenBuf; + std::copy(pslout->m_prgnVarLenBuf, pslout->m_prgnVarLenBuf + cnExtraPerSlot, + m_prgnVarLenBuf); + + // Now copy the stuff specific to GrSlotOutput. + m_ichwBeforeAssoc = pslout->m_ichwBeforeAssoc; + m_ichwAfterAssoc = pslout->m_ichwAfterAssoc; + m_cComponents = pslout->m_cComponents; + + for (size_t iislout = 0; iislout < pslout->m_visloutClusterMembers.size(); iislout++) + m_visloutClusterMembers.push_back(pslout->m_visloutClusterMembers[iislout]); + + m_isloutClusterBase = pslout->m_isloutClusterBase; + m_xsClusterXOffset = pslout->m_xsClusterXOffset; + m_xsClusterAdvance = pslout->m_xsClusterAdvance; + m_igbb = pslout->m_igbb; + m_xsAdvanceX = pslout->m_xsAdvanceX; + m_ysAdvanceY = pslout->m_ysAdvanceY; + m_rectBB = pslout->m_rectBB; +} + +/*---------------------------------------------------------------------------------------------- + Shift the glyph to the opposite end of the segment. This is needed for white-space-only + segments whose direction is being changed. +----------------------------------------------------------------------------------------------*/ +void GrSlotOutput::ShiftForDirDepthChange(float dxsSegWidth) +{ + float dxsShift = dxsSegWidth - m_xsAdvanceX - (2 * m_xsPositionX); + int tmp; tmp = (int) dxsShift; + m_xsPositionX += dxsShift; + m_rectBB.left += dxsShift; + m_rectBB.right += dxsShift; +} + +} // namespace gr diff --git a/Build/source/libs/graphite-engine/src/segment/GrSlotState.h b/Build/source/libs/graphite-engine/src/segment/GrSlotState.h new file mode 100644 index 00000000000..f9110d7b68c --- /dev/null +++ b/Build/source/libs/graphite-engine/src/segment/GrSlotState.h @@ -0,0 +1,1222 @@ +/*--------------------------------------------------------------------*//*:Ignore this sentence. +Copyright (C) 1999, 2001 SIL International. All rights reserved. + +Distributable under the terms of either the Common Public License or the +GNU Lesser General Public License, as specified in the LICENSING.txt file. + +File: GrSlotState.h +Responsibility: Sharon Correll +Last reviewed: Not yet. + +Copyright (C) 1999 by SIL International. All rights reserved. + +Description: + Class GrSlotAbstract, GrSlotState, and GrSlotOutput +----------------------------------------------------------------------------------------------*/ +#ifdef _MSC_VER +#pragma once +#endif +#ifndef SLOTSTATE_INCLUDED +#define SLOTSTATE_INCLUDED + +namespace gr +{ + +class GrSlotAbstract; +class GrSlotState; +class GrSlotOutput; +class GrSlotStream; +class Font; + +//:End Ignore + + +/*---------------------------------------------------------------------------------------------- + Subsumes GrSlotState and GrSlotOutput, both of which represent a single glyph + and its slot attributes and features. + + Hungarian: slab +----------------------------------------------------------------------------------------------*/ +class GrSlotAbstract +{ + friend class GrSlotStream; + friend class GlyphInfo; + +public: + // Constructor: + GrSlotAbstract() + { + } + + ~GrSlotAbstract() + { + // the table manager is responsible for destroying the contents of m_prgnVarLenBuf + } + + void BasicInitialize(int cnUserDefn, int cnCompPerLig, int cnFeat, u_intslot * pnBuf) + { + m_dirc = kNotYetSet; + m_lb = kNotYetSet; + m_fInsertBefore = true; + m_nDirLevel = -1; + m_fHasComponents = false; + + m_xysGlyphWidth = -1; + m_xysFontAscent = -1; + m_xysFontDescent = -1; + + m_mAdvanceX = kNotYetSet; + m_mAdvanceY = kNotYetSet; + m_mShiftX = 0; + m_mShiftY = 0; + + m_srAttachTo = 0; + m_nAttachLevel = 0; + + m_mAttachAtX = kNotYetSet; + m_mAttachAtY = 0; + m_nAttachAtGpoint = kNotYetSet; + m_mAttachAtXOffset = 0; + m_mAttachAtYOffset = 0; + m_mAttachWithX = kNotYetSet; + m_mAttachWithY = 0; + m_nAttachWithGpoint = kNotYetSet; + m_mAttachWithXOffset = 0; + m_mAttachWithYOffset = 0; + + m_fInsertBefore = true; + + m_mMeasureSol = 0; + m_mMeasureEol = 0; + + m_mJStretch0 = 0; + m_mJShrink0 = 0; + m_mJStep0 = 0; + m_nJWeight0 = 0; + m_mJWidth0 = 0; + + m_islotPosPass = kNotYetSet; + + m_cnUserDefn = byte(cnUserDefn); + m_cnCompPerLig = byte(cnCompPerLig); + m_cnFeat = byte(cnFeat); + m_prgnVarLenBuf = pnBuf; + + m_fAdvXSet = false; // for transduction logging + m_fAdvYSet = false; + } + + void SetBufferPtr(u_intslot * pn) + { + m_prgnVarLenBuf = pn; + } + + gid16 GlyphID() { return m_chwGlyphID; } + gid16 RawActualGlyph() { return m_chwActual; } + float GetGlyphMetric(Font * pfont, int nGlyphMetricID, gid16 chwGlyphID); + + int StyleIndex() { return m_bStyleIndex; } + void GetFeatureValues(GrFeatureValues * pfval) + { + pfval->m_nStyleIndex = m_bStyleIndex; + std::fill(pfval->m_rgnFValues, pfval->m_rgnFValues + kMaxFeatures, 0); + for (size_t i = 0; i < m_cnFeat; i++) + pfval->m_rgnFValues[i] = PFeatureBuf()[i].nValue; + } + + int RawAdvanceX() { return m_mAdvanceX; } + int RawAdvanceY() { return m_mAdvanceY; } + int ShiftX() { return m_mShiftX; } + int ShiftY() { return m_mShiftY; } + + int AttachTo() { return m_srAttachTo; } + int AttachLevel() { return m_nAttachLevel; } + + int RawAttachAtX() { return m_mAttachAtX; } + int AttachAtY() { return m_mAttachAtY; } + int AttachAtGpoint() { return m_nAttachAtGpoint; } + int AttachAtXOffset() { return m_mAttachAtXOffset; } + int AttachAtYOffset() { return m_mAttachAtYOffset; } + + int RawAttachWithX() { return m_mAttachWithX; } + int AttachWithY() { return m_mAttachWithY; } + int AttachWithGpoint() { return m_nAttachWithGpoint; } + int AttachWithXOffset() { return m_mAttachWithXOffset; } + int AttachWithYOffset() { return m_mAttachWithYOffset; } + + bool HasComponents() { return m_fHasComponents; } + +// int CompRef(int i) +// { +// Assert(i < m_cnCompPerLig); +// if (i < m_cnCompPerLig) +// return m_rgsrCompRef[i]; +// else +// return kNotYetSet; +// } + + GrSlotState * CompRefSlot(int i); + + int BreakWeight() + { + return m_lb; + } + DirCode Directionality() + { + return DirCode(m_dirc); + } + int InsertBefore() + { + return m_fInsertBefore; + } + int MeasureSol() + { + return m_mMeasureSol; + } + int MeasureEol() + { + return m_mMeasureEol; + } + + int DirLevel() + { + return m_nDirLevel; + } + + int SpecialSlotFlag() + { + return m_spsl; + } + void SetSpecialSlotFlag(int spsl) + { + m_spsl = spsl; + } + + void CopyFrom(GrSlotState * pslot); + + enum { + kNotYetSet = 0x7FFF, + kInvalidGlyph = 0xFFFF + }; + + int PosPassIndex() + { + return m_islotPosPass; + } + void SetPosPassIndex(int islot, bool fInputToPosPass1) + { + // If we're resetting it, it should be to the same value as before: + Assert(fInputToPosPass1 || m_islotPosPass == kNotYetSet || m_islotPosPass == islot); + m_islotPosPass = islot; + } + void IncPosPassIndex() + { + m_islotPosPass++; + } + void ZapPosPassIndex() + { + m_islotPosPass = kNotYetSet; + } + + gid16 ActualGlyphForOutput(GrTableManager * ptman); + + // Variable-length buffer--includes four sub-buffers: + // * user-defined variables + // * component.???.ref assignments + // * mapping from components used to global attribute IDs for components + // * feature values + + int UserDefn(int slati) + { + Assert(slati < m_cnUserDefn); + return m_prgnVarLenBuf[slati].nValue; + } + void SetUserDefn(int slati, int nVal) + { + Assert(slati < m_cnUserDefn); + m_prgnVarLenBuf[slati].nValue = nVal; + } + u_intslot * PUserDefnBuf() + { + return m_prgnVarLenBuf; + } + + GrSlotState * CompRef(int slati) + { + Assert(slati < m_cnCompPerLig); + return m_prgnVarLenBuf[m_cnUserDefn + slati].pslot; + } + void SetCompRef(int slati, GrSlotState * pvSlot) + { + Assert(slati < m_cnCompPerLig); + m_prgnVarLenBuf[m_cnUserDefn + slati].pslot = pvSlot; + } + u_intslot * PCompRefBuf() + { + return m_prgnVarLenBuf + m_cnUserDefn; + } + + int Slati(int i) + { + Assert(i < m_cnCompPerLig); + return m_prgnVarLenBuf[m_cnUserDefn + m_cnCompPerLig + i].nValue; + } + void SetSlati(int i, int n) + { + Assert(i < m_cnCompPerLig); + m_prgnVarLenBuf[m_cnUserDefn + m_cnCompPerLig + i].nValue = n; + } + u_intslot * PSlatiBuf() + { + return m_prgnVarLenBuf + m_cnUserDefn + m_cnCompPerLig; + } + + int FeatureValue(int i) + { + Assert(i < m_cnFeat); + return m_prgnVarLenBuf[m_cnUserDefn + (m_cnCompPerLig * 2) + i].nValue; + } + + u_intslot * PFeatureBuf() + { + return m_prgnVarLenBuf + m_cnUserDefn + (m_cnCompPerLig * 2); + } + + int CExtraSpace() + { + return m_cnUserDefn + (m_cnCompPerLig * 2) + m_cnFeat; + } + + // Needed for GlyphInfo: + float XPosition() + { + return m_xsPositionX; + } + float YPosition() + { + return m_ysPositionY; // relative to baseline (positive is up) + } + +protected: + gid16 m_chwGlyphID; + gid16 m_chwActual; // actual glyph to output (which is a different glyph for pseudos) + + int m_islotPosPass; // index of slot in positioning streams, relative to first official + // slot in the segment (possibly the LB slot) + + int m_spsl; // special slot flag: LB, bidi marker + +// bool m_fInitialLB; // for LB slots: true if this is the initial LB; + // false if it is the terminating LB + // TODO: remove + + int m_dirc; // default = kNotYetSet (read from glyph attr) + int m_lb; // default = kNotYetSet (read from glyph attr) + + int m_nDirLevel; + + // raw glyph metrics (directly from font) + float m_xysFontAscent; + float m_xysFontDescent; + float m_xysGlyphWidth; + float m_xysGlyphHeight; + float m_xysGlyphX; + float m_xysGlyphY; + float m_xysAdvX; + float m_xysAdvY; + + // Slot attributes: + short m_mAdvanceX; + short m_mAdvanceY; + short m_mShiftX; + short m_mShiftY; + + short m_srAttachTo; + short m_nAttachLevel; + + short m_mAttachAtX; + short m_mAttachAtY; + short m_mAttachAtXOffset; + short m_mAttachAtYOffset; + short m_mAttachWithX; + short m_mAttachWithY; + short m_mAttachWithXOffset; + short m_mAttachWithYOffset; + + short m_nAttachAtGpoint; + short m_nAttachWithGpoint; + + short m_mMeasureSol; + short m_mMeasureEol; + + unsigned short m_mJStretch0; + unsigned short m_mJShrink0; + unsigned short m_mJStep0; + int m_mJWidth0; + byte m_nJWeight0; + + bool m_fInsertBefore; // default = true + + bool m_fHasComponents; // default = false + + bool m_fIsSpace; + + byte m_bStyleIndex; + + byte m_cnUserDefn; + byte m_cnCompPerLig; + byte m_cnFeat; + // There is a large block managed by either the GrTableManager (for GrSlotState) + // or the segment (for GrSlotOutput); this variable points at the sub-buffer for this + // particular slot: + u_intslot * m_prgnVarLenBuf; + + float m_xsPositionX; + float m_ysPositionY; + +public: // for transduction logging + bool m_fAdvXSet; + bool m_fAdvYSet; + +}; // end of class GrSlotAbstract + + +/*---------------------------------------------------------------------------------------------- + A GrSlotState represents one slot as modified by a pass in the table. + Each time a slot state is modified, a new instance is created, copying the relevant + information. + + Hungarian: slot + + Other hungarian: + m - integer indicating glyph design units ("em-units") + sr - integer indicating slot reference +----------------------------------------------------------------------------------------------*/ +class GrSlotState : public GrSlotAbstract +{ + + friend class GrSlotStream; + +public: + enum { kNeutral = 99 }; + + // Constructors: + GrSlotState() + : GrSlotAbstract(), + m_pslotPrevState(NULL) + { + m_vpslotAssoc.clear(); + //m_fInitialLB = false; // TODO: remove + m_spsl = kspslNone; + m_fNeutralAssocs = false; + m_dircProc = kdircUnknown; + m_fDirProcessed = false; + + m_vdislotAttLeaves.clear(); + m_fAttachMod = false; + m_fShiftMod = false; + m_dislotRootFixed = 0; + ZapCompositeMetrics(); + } + + ~GrSlotState() + { + } + + void Initialize(gid16 chw, GrEngine *, GrFeatureValues fval, + int ipass, int ichwSegOffset, int nUnicode = -1); + void Initialize(gid16 chw, GrEngine *, GrSlotState * pslotFeat, + int ipass, int ichwSegOffset); + void Initialize(gid16 chw, GrEngine *, GrSlotState * pslotFeat, + int ipass); + + void InitializeFrom(GrSlotState * pslot, int ipass); + void CopyFeaturesFrom(GrSlotState * pslotSrc); + void FixAttachmentTree(GrSlotState * pslotOld); + + // General: + int RawSegOffset() { return m_ichwSegOffset; } + + void SetGlyphID(gid16 chw) + { + m_chwGlyphID = chw; + m_chwActual = kInvalidGlyph; + m_xysGlyphWidth = -1; // indicate glyph metrics are invalid + } + void Associate(GrSlotState *); + void Associate(GrSlotState *, GrSlotState *); + void Associate(std::vector<GrSlotState*> &); + void ClearAssocs(); + + int AssocsSize() { return m_vpslotAssoc.size(); } + GrSlotState * RawBeforeAssocSlot() + { + if (m_vpslotAssoc.size() == 0) + return NULL; + return m_vpslotAssoc[0]; + } + GrSlotState * RawAfterAssocSlot() + { + if (m_vpslotAssoc.size() == 0) + return NULL; + return m_vpslotAssoc.back(); + } + GrSlotState * AssocSlot(int i) { return m_vpslotAssoc[i]; } + + void AllAssocs(std::vector<int> & vichw); + int BeforeAssoc(); + int AfterAssoc(); + + void CleanUpAssocs(); + + void SetComponentRefsFor(GrSlotOutput *, int iComp = -1); + void AllComponentRefs(std::vector<int> & vichw); + + int PassModified() { return m_ipassModified; } + int SegOffset(); + int UnderlyingPos(); + GrSlotState * PrevState() { return m_pslotPrevState; } + + void MarkDeleted() // for now, do nothing + { + } + + int IsSpace(GrTableManager * ptman); + + // TODO: remove argument from these methods; it is no longer needed. + bool IsLineBreak(gid16 chwLB) + { + return (IsInitialLineBreak(chwLB) || IsFinalLineBreak(chwLB)); + //return (m_chwGlyphID == chwLB); // TODO: remove + } + bool IsInitialLineBreak(gid16 chwLB) + { + return (m_spsl == kspslLbInitial); + //return (IsLineBreak(chwLB) && m_fInitialLB == true); // TODO: remove + } + bool IsFinalLineBreak(gid16 chwLB) + { + return (m_spsl == kspslLbFinal); + //return (IsLineBreak(chwLB) && m_fInitialLB == false); // TODO: remove + } + + bool IsBidiMarker() + { + switch (m_spsl) + { + case kspslLRM: + case kspslRLM: + case kspslLRO: + case kspslRLO: + case kspslLRE: + case kspslRLE: + case kspslPDF: + return true; + default: + return false; + } + return false; + } + + // Directionality as determined by the bidi algorithm + DirCode DirProcessed() // return the value + { + Assert(m_dirc != kNotYetSet); + if (m_dircProc == kdircUnknown) + m_dircProc = DirCode(m_dirc); + return m_dircProc; + } + void SetDirProcessed(DirCode dirc) // set the directionality + { + m_dircProc = dirc; + } + bool DirHasBeenProcessed() // has this slot been fully processed? + { + return m_fDirProcessed; + } + void MarkDirProcessed() // this slot has been fully processed + { + m_fDirProcessed = true; + } + + // Slot attributes that must be calculated: + + int AdvanceX(GrTableManager * ptman) + { + if (m_mAdvanceX == kNotYetSet) + // Initialize it from the glyph metric (adjusted for hinting). + m_mAdvanceX = short(GlyphMetricEmUnits(ptman, kgmetAdvWidth)); + return m_mAdvanceX; + } + + int AdvanceY(GrTableManager * ptman) + { + if (m_mAdvanceY == kNotYetSet) + // Initialize it from the glyph metric (adjusted for hinting). + m_mAdvanceY = short(GlyphMetricEmUnits(ptman, kgmetAdvHeight)); + return m_mAdvanceY; + } + + int AttachAtX(GrTableManager * ptman, GrSlotStream * psstrm) + { + if (m_mAttachAtX == kNotYetSet) + { + Assert(false); // Should have already been set in HandleModifiedPosition, + // but just in case... + if (m_srAttachTo == 0) + return 0; + else + m_mAttachAtX = short(AttachRoot(psstrm)->AdvanceX(ptman)); // attach on the right + } + return m_mAttachAtX; + } + + int AttachWithX(GrTableManager * ptman, GrSlotStream * psstrm) + { + if (m_mAttachAtX == kNotYetSet) + { + Assert(false); // Should have already been set in HandleModifiedPosition, + // but just in case. + if (!m_srAttachTo == 0) + return 0; + else + m_mAttachAtX = 0; // attach on the right + } + return m_mAttachAtX; + } + + int JStretch() + { + return m_mJStretch0; + } + int JShrink() + { + return m_mJShrink0; + } + int JStep() + { + return m_mJStep0; + } + int JWeight() + { + return m_nJWeight0; + } + int JWidth() + { + return m_mJWidth0; + } + + // Slot attribute setters: + + void SetAdvanceX(int mVal) + { + Assert(mVal < 0xFFFF); m_mAdvanceX = short(mVal & 0xFFFF); m_fShiftMod = true; + m_fAdvXSet = true; // for transduction logging + } + void SetAdvanceY(int mVal) + { + Assert(mVal < 0xFFFF); m_mAdvanceY = short(mVal & 0xFFFF); m_fShiftMod = true; + m_fAdvYSet = true; // for transduction logging + } + void SetShiftX(int mVal) { + Assert(mVal < 0xFFFF); m_mShiftX = short(mVal & 0xFFFF); m_fShiftMod = true; } + void SetShiftY(int mVal) { + Assert(mVal < 0xFFFF); m_mShiftY = short(mVal & 0xFFFF); m_fShiftMod = true; } + + void SetAttachTo(int srVal) + { + Assert(srVal < 0xFFFF); + m_srAttachTo = short(srVal & 0xFFFF); + m_fAttachMod = true; + } + void SetAttachLevel(int nVal) { + Assert(nVal < 0xFFFF); m_nAttachLevel = short(nVal & 0xFFFF); m_fAttachMod = true; } + + void SetAttachAtX(int mVal) + { + Assert(mVal < 0xFFFF); + m_mAttachAtX = short(mVal) & 0xFFFF; + m_fAttachMod = true; + } + void SetAttachAtY(int mVal) { + Assert(mVal < 0xFFFF); m_mAttachAtY = short(mVal & 0xFFFF); m_fAttachMod = true; } + void SetAttachAtGpoint(int nVal) { + m_nAttachAtGpoint = short(nVal); m_fAttachMod = true; } + void SetAttachAtXOffset(int mVal) { + Assert(mVal < 0xFFFF); m_mAttachAtXOffset = short(mVal & 0xFFFF); m_fAttachMod = true; } + void SetAttachAtYOffset(int mVal) { + Assert(mVal < 0xFFFF); m_mAttachAtYOffset = short(mVal & 0xFFFF); m_fAttachMod = true; } + + void SetAttachWithX(int mVal) { + Assert(mVal < 0xFFFF); m_mAttachWithX = short(mVal & 0xFFFF); m_fAttachMod = true; } + void SetAttachWithY(int mVal) { + Assert(mVal < 0xFFFF); m_mAttachWithY = short(mVal & 0xFFFF); m_fAttachMod = true; } + void SetAttachWithGpoint(int nVal) { + m_nAttachWithGpoint = short(nVal); m_fAttachMod = true; } + void SetAttachWithXOffset(int mVal) { + Assert(mVal < 0xFFFF); m_mAttachWithXOffset = short(mVal & 0xFFFF); m_fAttachMod = true; } + void SetAttachWithYOffset(int mVal) { + Assert(mVal < 0xFFFF); m_mAttachWithYOffset = short(mVal & 0xFFFF); m_fAttachMod = true; } + + void SetCompRefSlot(GrTableManager * ptman, int i, GrSlotState * pslotComp); + + void SetBreakWeight(int lb) { m_lb = lb; } + void SetInsertBefore(bool f) { m_fInsertBefore = f; } + + void SetDirectionality(DirCode dirc) { m_dirc = dirc; } + void SetDirLevel(int n) { m_nDirLevel = n; } + + void SetMeasureSol(int mVal) { m_mMeasureSol = short(mVal); } + void SetMeasureEol(int mVal) { m_mMeasureEol = short(mVal); } + + void SetJStretch(int mVal) { m_mJStretch0 = short(mVal); } + void SetJShrink(int mVal) { m_mJShrink0 = short(mVal); } + void SetJStep(int mVal) { m_mJStep0 = short(mVal); } + void SetJWeight(int nVal) { m_nJWeight0 = byte(nVal); } + void SetJWidth(int mVal) { m_mJWidth0 = mVal; } + void AddJWidthToAdvance(GrTableManager * ptman) + { + // Don't change m_fShiftMod. + m_mAdvanceX = short(m_mJWidth0 + AdvanceX(ptman)); // make sure it is calculated + m_mJWidth0 = 0; + m_fAdvXSet = true; // for transduction logging + } + + int PostBidiDirLevel(GrTableManager * ptman); + + bool BaseEarlierInStream(); + + void ZapDirLevel() + { + m_nDirLevel = -1; + m_dircProc = kdircUnknown; + m_fDirProcessed = false; + } + + int GlyphAttrValueEmUnits(GrTableManager * ptman, int nAttrID); + int GlyphMetricEmUnits(GrTableManager * ptman, int nGlyphMetricID); + + float GlyphAttrValueLogUnits(GrTableManager * ptman, int nAttrID); + float GlyphMetricLogUnits(GrTableManager * ptman, int nGlyphMetricID); + +// void HandleModifiedCluster(GrTableManager * ptman, +// GrSlotStream * psstrm, int islotThis); + void HandleModifiedPosition(GrTableManager * ptman, + GrSlotStream * psstrmIn, GrSlotStream * psstrmOut, int islotThis); + + void CalcCompositeMetrics(GrTableManager * ptman, GrSlotStream * psstrm, + int nLevel, bool fThorough = false); + + void Position(GrTableManager * ptman, + GrSlotStream * psstrmOut, int * pmXPos, int * pmYPos); + + void ZapPosition() + { + m_xsPositionX = kNegInfFloat; + m_ysPositionY = kNegInfFloat; + } + + void SetXPos(float xs) + { + m_xsPositionX = xs; + } + void SetYPos(float ys) + { + m_ysPositionY = ys; // relative to baseline (positive is up) + } + + float ClusterRootOffset() { return -m_xsClusterXOffset; } // the offset of the root + // relative to the whole cluster + + float ClusterAdvWidthFrom(float xs) { return xs + m_xsClusterAdv; } + float ClusterBbLeftFrom(float xs) { return xs + m_xsClusterBbLeft; } + float ClusterBbRightFrom(float xs) { return xs + m_xsClusterBbRight; } + + float ClusterLsb(GrSlotStream * psstrm, float xs) + { + return ClusterBbLeft(psstrm) + xs; + } + float ClusterRsb(GrSlotStream * psstrm, float xs) + { + return ClusterAdvWidthFrom(xs) - ClusterBbRightFrom(xs); + } + + float ClusterAdvWidth(GrSlotStream * psstrm) + { + return ClusterAdvWidthFrom(Base(psstrm)->ClusterRootOffset()); + } +// int ClusterAdvHeight(GrSlotStream * psstrm) +// { +// Assert(m_mAdvanceY != kNotYetSet); +// return EmUnitsToTwips(m_mAdvanceY); +// } + float ClusterBbLeft(GrSlotStream * psstrm) + { + return ClusterBbLeftFrom(Base(psstrm)->ClusterRootOffset()); + } + float ClusterBbRight(GrSlotStream * psstrm) + { + return ClusterBbRightFrom(Base(psstrm)->ClusterRootOffset()); + } + float ClusterBbTop(GrSlotStream * psstrm) + { + return m_ysClusterBbTop; + } + float ClusterBbBottom(GrSlotStream * psstrm) + { + return m_ysClusterBbBottom; + } + float ClusterBbWidth(GrSlotStream * psstrm) + { + return m_xsClusterBbRight - m_xsClusterBbLeft + 1; + } + float ClusterBbHeight(GrSlotStream * psstrm) + { + return m_ysClusterBbTop - m_ysClusterBbBottom + 1; + } + float ClusterLsb(GrSlotStream * psstrm) + { + return ClusterBbLeft(psstrm); + } + float ClusterRsb(GrSlotStream * psstrm) + { + return ClusterAdvWidth(psstrm) - ClusterBbRight(psstrm); + } + + float GlyphXOffset(GrSlotStream * psstrm, float fakeItalicRatio = 0); + float GlyphYOffset(GrSlotStream * psstrm = NULL); + + bool IsBase() + { + Assert(m_dislotRootFixed == m_srAttachTo); + return (m_dislotRootFixed == 0); + } + + bool HasClusterMembers() + { + return (m_vdislotAttLeaves.size() > 0); + } + + GrSlotState * AttachRoot(GrSlotStream * psstrm) + { + if (m_srAttachTo == 0) + return NULL; + else + return SlotAtOffset(psstrm, m_srAttachTo); + } + + int AttachRootPosPassIndex() + { + return PosPassIndex() + m_srAttachTo; + } + + GrSlotState * Base(GrSlotStream * psstrm) + { + GrSlotState * pslotRoot = AttachRoot(psstrm); + if (!pslotRoot) + return this; + else + return pslotRoot->Base(psstrm); + } + + int LastLeafOffset(GrSlotStream * psstrm); + + void AddLeaf(int dislot) + { + m_vdislotAttLeaves.push_back(dislot * -1); + } + void RemoveLeaf(int dislot) + { + for (size_t iislot = 0; iislot < m_vdislotAttLeaves.size(); iislot++) + { + if (m_vdislotAttLeaves[iislot] == dislot * -1) + { + m_vdislotAttLeaves.erase(m_vdislotAttLeaves.begin() + iislot); + return; + } + } + Assert(false); + } + + bool HasAsRoot(GrSlotStream * psstrm, GrSlotState * pslot) + { + GrSlotState * pslotRoot = AttachRoot(psstrm); + if (pslotRoot == pslot) + return true; + else if (pslotRoot == NULL) + return false; + else + return pslotRoot->HasAsRoot(psstrm, pslot); + } + + bool HasAsPreviousState(GrSlotState * pslot) + { + if (this == pslot) + return true; + else + return m_pslotPrevState->HasAsPreviousState(pslot); + } + + void EnsureCacheForOutput(GrTableManager * ptman); + + bool IsLrm(); + bool IsRlm(); + + // For transduction logging: +#ifdef TRACING + void SlotAttrsModified(bool * rgfMods, bool fPreJust, int * pccomp, int * pcassoc); + void LogSlotAttribute(GrTableManager *, std::ostream &, int ipass, int slat, int icomp, + bool fPreJust, bool fPostJust); + void LogAssociation(GrTableManager * ptman, + std::ostream & strmOut, int ipass, int iassoc, bool fBoth, bool fAfter); + int m_islotTmpIn; // for use by transduction log; index of slot in input stream + int m_islotTmpOut; // ditto; index of slot in output stream +#endif // TRACING + +protected: + + // Instance variables: + + int m_ipassModified; // pass in which this slot was modified + + GrSlotState * m_pslotPrevState; + + int m_ichwSegOffset; // for original (pass 0) slot states: position in + // underlying text relative to the official + // beginning of the segment; + // should == kInvalid for other slot states + int m_nUnicode;; // for debugging + + std::vector<GrSlotState*> m_vpslotAssoc; // association mappings + + bool m_fNeutralAssocs; // true if we've set the associations to some neutral + // default, rather than them being set explicitly + // within a rule + + DirCode m_dircProc; // directionality as processed in bidi algorithm + bool m_fDirProcessed; + + + // Attachment and metrics + + // This is a flag that is set whenever we change the value of any of the attach + // slot attributes. Then when we want to find out something about the attachments, + // if it is set, we have some work to do in updating the pointers and metrics. + bool m_fAttachMod; + + // This is a flag that is set whenever we change the value of any of the shift or + // advance slot attributes. It forces us to zap the metrics. + bool m_fShiftMod; + + // This glyph (and all its leaves, if any) have a zero advance width; + // never allow it to affect the advance width of a cluster it is part of. + bool m_fIgnoreAdvance; + + int m_dislotRootFixed; // the offset of the slot (relative to this one) that considers + // this slot to be one of its leaves + std::vector<int> m_vdislotAttLeaves; + + // The following are used by the CalcCompositeMetrics() method and depend on + // the cluster level that was passed as an argument. + + int m_nCompositeLevel; // cluster level last used to calculate composite metrics; + // kNegInfinity if uncalculated + + // offsets for this node only, relative to cluster base + float m_xsOffsetX; + float m_ysOffsetY; // relative to baseline (positive is up) + + // offsets for this node and its leaves, relative to cluster base (y-coords are + // relative to baseline) + float m_xsClusterXOffset; + float m_xsClusterAdv; + float m_xsClusterBbLeft; + float m_xsClusterBbRight; + float m_ysClusterBbTop; + float m_ysClusterBbBottom; + + // cumulative total of shifts for this node and roots; advance needs to ignore these + float m_xsRootShiftX; + float m_ysRootShiftY; + + + // Private methods: + + void AdjustRootMetrics(GrTableManager * ptman, GrSlotStream *); + void InitMetrics(GrTableManager * ptman, GrSlotState * pslotRoot); + void InitLeafMetrics(GrTableManager * ptman, GrSlotState * pslotRoot); + void InitRootMetrics(GrTableManager * ptman); + void CalcRootMetrics(GrTableManager * ptman, GrSlotStream *, int nLevel); + void AttachToRoot(GrTableManager * ptman, GrSlotStream *, GrSlotState * pslotNewRoot); + void AttachLogUnits(GrTableManager * ptman, + GrSlotState * pslotRoot, + float * pxsAttAtX, float * pysAttAtY, + float * pxsAttWithX, float * pysAttWithY); + + GrSlotState * SlotAtOffset(GrSlotStream * psstrm, int dislot); // ENHANCE SharonC: inline? + + void EnsureLocalAttachmentTree(GrTableManager * ptman, + GrSlotStream * psstrmIn, GrSlotStream * psstrmOut, int islotThis); + + void ZapCompositeMetrics() + { + m_nCompositeLevel = kNegInfinity; + m_xsPositionX = kNegInfFloat; + m_ysPositionY = kNegInfFloat; + m_xsOffsetX = 0; + m_ysOffsetY = 0; + m_xsRootShiftX = 0; + m_ysRootShiftY = 0; + ZapRootMetrics(); + } + + void ZapRootMetrics() + { + m_xsClusterXOffset = 0; + m_xsClusterAdv = 0; + m_xsClusterBbLeft = 0; + m_xsClusterBbRight = 0; + m_ysClusterBbTop = 0; + m_ysClusterBbBottom = 0; + m_fIgnoreAdvance = false; + } + + void ZapMetricsAndPositionDownToBase(GrSlotStream * psstrm) + { + ZapCompositeMetrics(); + if (m_dislotRootFixed) + SlotAtOffset(psstrm, m_dislotRootFixed)->ZapMetricsAndPositionDownToBase(psstrm); + } + + void ZapMetricsOfLeaves(GrSlotStream * psstrm, bool fThis = false) + { + if (fThis) + ZapCompositeMetrics(); + for (size_t islot = 0; islot < m_vdislotAttLeaves.size(); islot++) + { + SlotAtOffset(psstrm, m_vdislotAttLeaves[islot])->ZapMetricsOfLeaves(psstrm, true); + } + } + +}; // end of class GrSlotState + + +/*---------------------------------------------------------------------------------------------- + A GrSlotOutput represents one slot as the final output of the final pass. These + are recorded in the segment. + + Hungarian: slout +----------------------------------------------------------------------------------------------*/ +class GrSlotOutput : public GrSlotAbstract +{ + friend class GlyphInfo; + friend class Segment; + +public: + GrSlotOutput() + { + m_cComponents = 0; + m_isloutClusterBase = -1; // not part of any cluster + m_igbb = -1; + m_visloutClusterMembers.clear(); + } + + void ExactCopyFrom(GrSlotOutput * pslout, u_intslot * pnVarLenBuf, int cnExtraPerSlot); + + int BeforeAssoc() + { + return m_ichwBeforeAssoc; // relative to the official beginning of the segment + } + int AfterAssoc() + { + return m_ichwAfterAssoc; // relative to the official beginning of the segment + } + + void SetBeforeAssoc(int ichw) + { + m_ichwBeforeAssoc = ichw; + } + void SetAfterAssoc(int ichw) + { + m_ichwAfterAssoc = ichw; + } + void AddComponentReference(int ichw, int slati) + { + if (m_cComponents >= m_cnCompPerLig) + { + Assert(false); // ignore the requested component ref + } + else + { + // Must be kept consistent with UserDefn() and CompRef() methods inherited + // from GrSlotAbstract. The component indices come after the above variable- + // length buffers. + m_prgnVarLenBuf[m_cnUserDefn + (m_cnCompPerLig * 2) + m_cnFeat + m_cComponents].nValue + = ichw; + + // OBSOLETE comment: + // Maps the used components to the defined components. Normally this will be + // one-to-one and the buffer will hold [0,1,2...]. But possibly we may + // have defined components a, b, and c, but only mapped a and c to actual + // characters. This buffer will then hold [0,2]. + + m_prgnVarLenBuf[m_cnUserDefn + (m_cnCompPerLig * 2) + m_cnFeat + + m_cnCompPerLig + m_cComponents].nValue + = slati; + +// Assert(iComp >= m_cComponents); // because we process them in order they are defined + // in, but we could have skipped some that are + // defined + m_cComponents++; + } + } + int NumberOfComponents() // the number used by the rules + { + return m_cComponents; + } + // Index of ligature components, relative to the beginning of the segment. + // iComp is index of components USED in this glyph. + int UnderlyingComponent(int iComp) + { + Assert(iComp < m_cnCompPerLig); + // Must be kept consistent with UserDefn() and CompRef() methods inherited + // from GrSlotAbstract. The component indices come after the above variable- + // length buffers. + return m_prgnVarLenBuf[m_cnUserDefn + (m_cnCompPerLig * 2) + m_cnFeat + iComp].nValue; + } + int ComponentId(int iComp) + { + return m_prgnVarLenBuf[m_cnUserDefn + (m_cnCompPerLig * 2) + m_cnFeat + + m_cnCompPerLig + iComp].nValue; + } + + // Given the index of the component used by the rules, indicate the + // corresponding component as defined by the glyph attributes. +// int DefinedComponent(int iCompUsed) +// { +// return m_prgnVarLenBuf[m_cnUserDefn + m_cnCompPerLig + m_cnFeat +// + m_cnUserDefn + iCompUsed]; +// } + + void SetClusterBase(int islout) + { + m_isloutClusterBase = islout; + } + + int ClusterBase() + { + return m_isloutClusterBase; + } + + bool IsPartOfCluster() + { + return m_isloutClusterBase > -1; + } + + int NumClusterMembers() + { + return m_visloutClusterMembers.size(); + } + + int ClusterMember(int iislout) + { + return m_visloutClusterMembers[iislout]; + } + + void AddClusterMember(int islout) + { + m_visloutClusterMembers.push_back(islout); + } + + float ClusterXOffset() { return m_xsClusterXOffset; } + float ClusterAdvance() { return m_xsClusterAdvance; } + void SetClusterXOffset(float xs) { m_xsClusterXOffset = xs; } + void SetClusterAdvance(float xs) { m_xsClusterAdvance = xs; } + + float GlyphMetricLogUnits(Font * pfont, int nGlyphMetric); + float GlyphMetricLogUnits(int nMetricID); + + int GlyphBbIndex() { return m_igbb; } + void SetGlyphBbIndex (int i) { m_igbb = i; } + + bool IsLineBreak() + { + return (IsInitialLineBreak() || IsFinalLineBreak()); + } + bool IsInitialLineBreak() + { + return (m_spsl == kspslLbInitial); + } + bool IsFinalLineBreak() + { + return (m_spsl == kspslLbFinal); + } + + float AdvanceXMetric() + { + return m_xysAdvX; + } + bool IsSpace(); + + int CExtraSpaceSlout() + { + return m_cnUserDefn + (m_cnCompPerLig * 2) + m_cnFeat + (m_cnCompPerLig * 2); + } + + void AdjustPosXBy(float dxs) + { + m_xsPositionX += dxs; + m_rectBB.left += dxs; + m_rectBB.right += dxs; + } + + void ShiftForDirDepthChange(float dxsSegWidth); + + // Used by GlyphInfo + int IndexAttachedTo(); + //inline float AdvanceX(Segment * pseg); + //inline float AdvanceY(Segment * pseg); + float MaxStretch(Segment * pseg, int level); + float MaxShrink(Segment * pseg, int level); + float StretchStep(Segment * pseg, int level); + int JustWeight(int level); + float JustWidth(Segment * pseg, int level); + float MeasureSolLogUnits(Segment * pseg); + float MeasureEolLogUnits(Segment * pseg); + Rect ComponentRect(Segment * pseg, int icomp); + +protected: + // Instance variables: + + int m_ichwBeforeAssoc; // index of associated character in the string + int m_ichwAfterAssoc; // (relative to the official beginning of the segment) + // char might possibly not be officially in this segment + + int m_cComponents; + + std::vector<int> m_visloutClusterMembers; // indices (logical surface position) + // of other slots that have this cluster as + // its base (absolute root), not necessarily sorted; + // does not include self + + int m_isloutClusterBase; // the index of the slot that serves as the base for the + // cluster this slot is a part of; -1 if not part of cluster + + // Measurements for highlighting an entire cluster, relative to origin of this slot, + // which is the cluster base. + float m_xsClusterXOffset; + float m_xsClusterAdvance; // for single non-cluster glyphs, advance width of positioned glyph + + // Index into m_prggbb in Segment; -1 indicates a line break slot that is not rendered: + int m_igbb; + + float m_xsAdvanceX; + float m_ysAdvanceY; + Rect m_rectBB; + +}; // end of class GrSlotOutput + +} // namespace gr + + +#endif // !SLOTSTATE_INCLUDED diff --git a/Build/source/libs/graphite-engine/src/segment/GrSlotStream.cpp b/Build/source/libs/graphite-engine/src/segment/GrSlotStream.cpp new file mode 100644 index 00000000000..b6ca2e599b4 --- /dev/null +++ b/Build/source/libs/graphite-engine/src/segment/GrSlotStream.cpp @@ -0,0 +1,2368 @@ +/*--------------------------------------------------------------------*//*:Ignore this sentence. +Copyright (C) 1999, 2001 SIL International. All rights reserved. + +Distributable under the terms of either the Common Public License or the +GNU Lesser General Public License, as specified in the LICENSING.txt file. + +File: GrSlotStream.cpp +Responsibility: Sharon Correll +Last reviewed: Not yet. + +Description: + GrSlotStream class implementation. +----------------------------------------------------------------------------------------------*/ + +//:>******************************************************************************************** +//:> Include files +//:>******************************************************************************************** +#include "Main.h" + +#include "GrConstants.h" + +#ifdef _MSC_VER +#pragma hdrstop +#endif +#undef THIS_FILE +DEFINE_THIS_FILE + +//:End Ignore + +//:>******************************************************************************************** +//:> Forward declarations +//:>******************************************************************************************** + +//:>******************************************************************************************** +//:> Local Constants and static variables +//:>******************************************************************************************** + +namespace gr +{ + +//:>******************************************************************************************** +//:> Methods +//:>******************************************************************************************** + +/*---------------------------------------------------------------------------------------------- + Answer true if there is nothing valid available to read from the stream. +----------------------------------------------------------------------------------------------*/ +bool GrSlotStream::AtEnd() +{ + Assert(AssertValid()); + + if (m_islotReprocPos > -1 && m_islotReprocPos < signed(m_vpslotReproc.size())) + return false; + + if (m_islotSegLim > -1 && m_islotReadPos >= m_islotSegLim) + // We've determined the segment limit, and we're at or past it. + return true; + + return (m_islotReadPos == m_islotWritePos); +} + +/*---------------------------------------------------------------------------------------------- + Answer true if there is nothing at all available to read from the stream. + For substitution passes, we need to consider slots that may be beyond what we know + will be the official end of the segment. +----------------------------------------------------------------------------------------------*/ +bool GrSlotStream::AtEndOfContext() +{ + if (m_fUsedByPosPass) + return AtEnd(); + + if (m_islotReprocPos > -1 && m_islotReprocPos < signed(m_vpslotReproc.size())) + return false; + + return (m_islotReadPos == m_islotWritePos); +} + +/*---------------------------------------------------------------------------------------------- + Append a slot to the end of the stream. +----------------------------------------------------------------------------------------------*/ +void GrSlotStream::NextPut(GrSlotState * pslot) +{ + Assert(AssertValid()); + + if (m_islotWritePos < signed(m_vpslot.size())) + { + m_vpslot[m_islotWritePos] = pslot; + } + else + { + m_vpslot.push_back(pslot); + m_vislotPrevChunkMap.push_back(-1); + m_vislotNextChunkMap.push_back(-1); + } + + if (m_fUsedByPosPass && GotIndexOffset()) + pslot->SetPosPassIndex(m_islotWritePos - m_cslotPreSeg, m_fInputToPosPass1); + + m_islotWritePos++; +} + +/*---------------------------------------------------------------------------------------------- + Get the next slot from the stream. +----------------------------------------------------------------------------------------------*/ +GrSlotState * GrSlotStream::NextGet() +{ + Assert(AssertValid()); + Assert(!AtEndOfContext()); + + GrSlotState * pslotRet; + + if (m_islotReprocPos > -1) + { + if (m_islotReprocPos >= signed(m_vpslotReproc.size())) + { + // Finished reprocessing. + Assert((unsigned)m_islotReprocPos == m_vpslotReproc.size()); + m_islotReprocPos = -1; + // But leave the m_vpslotReproc array in place so that earlier slots can + // be accessed by the rules. + pslotRet = m_vpslot[m_islotReadPos]; + m_islotReadPos++; + } + else + { + // Read from the reprocess buffer. + pslotRet = m_vpslotReproc[m_islotReprocPos]; + m_islotReprocPos++; + } + } + else + { + // Read normally. + pslotRet = m_vpslot[m_islotReadPos]; + m_islotReadPos++; + } + + Assert(!m_fUsedByPosPass || pslotRet->PosPassIndex() != GrSlotAbstract::kNotYetSet); + + return pslotRet; +} + +/*--------------------------------------------------------------------------------------------- + Peek at the next slot from the stream (dislot = 0 means the very next slot). + Only used for peeking forward. RuleInputSlot is used for accessing any possible + slot in the rule's context. +----------------------------------------------------------------------------------------------*/ +GrSlotState * GrSlotStream::Peek(int dislot) +{ + Assert(AssertValid()); + Assert(!AtEndOfContext()); + Assert(dislot >= 0); + + GrSlotState * pslotRet; + + if (m_islotReprocPos > -1) + { + int cslotReproc = m_vpslotReproc.size() - m_islotReprocPos; // num left in reproc buffer + if (dislot >= cslotReproc) + { + // Not quite enough slots in the reprocess buffer. + pslotRet = m_vpslot[m_islotReadPos + dislot - cslotReproc]; + } + else if (m_islotReprocPos + dislot < 0) + { + // Reading prior to the reprocess buffer + pslotRet = m_vpslot[m_islotReadPos + dislot - cslotReproc]; + } + else + { + // Read from the reprocess buffer. + pslotRet = m_vpslotReproc[m_islotReprocPos + dislot]; + } + } + else + { + // Read normally. + Assert(m_islotWritePos - m_islotReadPos > dislot); + pslotRet = m_vpslot[m_islotReadPos + dislot]; + } + + return pslotRet; +} + +/*--------------------------------------------------------------------------------------------- + Treat the stream, which is really the output stream, as an input stream, + retrieving a recently output slot. This happens when we are reading before the + current stream position when the rule is being matched, or before the original + stream position when the rule is being run. + + @param dislot - how far back to peek before the write position + when the rule started; a negative number + (NOTE: the current write position is irrelevant) + @param fNullOkay - true if it's okay to return NULL in the situation where we're asking + for something before the beginning of the stream +----------------------------------------------------------------------------------------------*/ +GrSlotState * GrSlotStream::PeekBack(int dislot, bool fNullOkay) +{ + Assert(dislot < 0); + if (dislot < m_islotRuleStartWrite * -1) + { + Assert(fNullOkay); + return NULL; + } + GrSlotState * pslotRet; + if (m_islotReprocPos > -1) + { + if (dislot < m_islotReprocLim - m_islotRuleStartWrite && + dislot >= m_islotReprocLim - m_islotRuleStartWrite - signed(m_vpslotReproc.size())) + { + // Read from the reprocess buffer. + // Review: this is weird; can it ever happen? + pslotRet = m_vpslotReproc[m_vpslotReproc.size() + - (m_islotReprocLim - m_islotRuleStartWrite) + dislot]; + } + else + pslotRet = m_vpslot[m_islotRuleStartWrite + dislot]; + } + else + pslotRet = m_vpslot[m_islotRuleStartWrite + dislot]; + + return pslotRet; +} + +/*--------------------------------------------------------------------------------------------- + Skip the given number of slots in the stream. + ENHANCE: implement more efficiently +----------------------------------------------------------------------------------------------*/ +void GrSlotStream::Skip(int dislot) +{ + for (int islot = 0; islot < dislot; ++islot) + NextGet(); +} + +/*---------------------------------------------------------------------------------------------- + Perform assertions to ensure that the stream is in a valid state. +----------------------------------------------------------------------------------------------*/ +bool GrSlotStream::AssertValid() +{ + Assert(m_islotWritePos <= signed(m_vpslot.size())); + Assert(m_islotReadPos <= m_islotWritePos); + // Streams used by the positioning passes (input or output) should not be working + // with slots beyond the segment lim + Assert(m_islotSegLim == -1 || !m_fUsedByPosPass || m_islotReadPos <= m_islotSegLim); + Assert(m_vpslot.size() == m_vislotPrevChunkMap.size()); + Assert(m_vpslot.size() == m_vislotNextChunkMap.size()); + return true; +} + +/*---------------------------------------------------------------------------------------------- + Return the number of slots that have been completely output by the previous pass but not + yet processed by the following pass. ("Completely" output means that they do not need + to be reprocessed by the same pass, only then are they available as valid input.) + Include any slots that are present by virtue of needing to be reprocessed by the + corresponding output stream. +----------------------------------------------------------------------------------------------*/ +int GrSlotStream::SlotsPending() +{ + Assert(AssertValid()); + if (m_islotSegLim > -1) + return (m_islotSegLim - m_islotReadPos + SlotsToReprocess()); + else + return (m_islotWritePos - m_islotReadPos + SlotsToReprocess()); +} + +int GrSlotStream::SlotsPendingInContext() +{ + Assert(AssertValid()); + if (m_fUsedByPosPass) + return SlotsPending(); + else + return (m_islotWritePos - m_islotReadPos + SlotsToReprocess()); +} + +int GrSlotStream::TotalSlotsPending() +{ + int cslot = SlotsPendingInContext(); + if (m_fUsedByPosPass && m_islotSegLim > -1) + { + int ctmp = m_islotWritePos - m_islotReadPos + SlotsToReprocess(); + if (ctmp > cslot) + { + int x; x = 3; + } + cslot = max(cslot, ctmp); + } + return cslot; +} + +/*---------------------------------------------------------------------------------------------- + Return true if this pass is input or output to a positioning pass and we're past the end + of the segment. So this stream is virtually finished. +----------------------------------------------------------------------------------------------*/ +bool GrSlotStream::PastEndOfPositioning(bool fOutput) +{ + if (m_fUsedByPosPass && m_islotSegLim > -1) + { + if (fOutput) // this stream is functioning as an output stream + return (m_islotWritePos >= m_islotSegLim); + else // this stream is functioning as an input stream + return (m_islotReadPos >= m_islotSegLim); + } + return false; +} + +/*---------------------------------------------------------------------------------------------- + Mark the stream as fully written, so that the output pass doesn't keep trying to ask + for more input. +----------------------------------------------------------------------------------------------*/ +void GrSlotStream::MarkFullyWritten() +{ + m_fFullyWritten = true; +} + +/*---------------------------------------------------------------------------------------------- + Return the position of the first glyph at or following the given position + that is in the top direction, or -1 if we do not have a complete range. + + @param ptman + @param islotStart + @nTopDirLevel - top direction: 0 = left-to-right, 1 = right-to-left +----------------------------------------------------------------------------------------------*/ +int GrSlotStream::OldDirLevelRange(EngineState * pengst, int islotStart, int nTopDirLevel) +{ + Assert(m_islotReadPos <= islotStart); + Assert(islotStart < m_islotWritePos); + + int islot = islotStart; + int nSlotDirLevel = GetSlotDirLevel(pengst, islot, nTopDirLevel, nTopDirLevel, + kdircUnknown, kdircNeutral); + if (nSlotDirLevel == -1) + return -1; + while (nSlotDirLevel > nTopDirLevel) + { + islot++; + if (islot >= m_islotWritePos) + { + if (m_fFullyWritten) + return islot; + else + return -1; + } + else if (m_islotSegLim > -1 && islot >= m_islotSegLim) + { + return islot; + } + + nSlotDirLevel = GetSlotDirLevel(pengst, islot, nTopDirLevel, nTopDirLevel, + kdircUnknown, kdircNeutral); + if (nSlotDirLevel == -1) + return -1; + } + return islot; +} + +/*---------------------------------------------------------------------------------------------- + Return the position of the first glyph at or following the given position + that is in the top direction, or -1 if we do not have a complete range. + + @param ptman + @param islotStart + @nTopDirLevel - top direction: 0 = left-to-right, 1 = right-to-left +----------------------------------------------------------------------------------------------*/ +int GrSlotStream::DirLevelRange(EngineState * pengst, int islotStart, int nTopDirLevel, + std::vector<int> & vislotStarts, std::vector<int> & vislotStops) +{ + Assert(m_islotReadPos <= islotStart); + Assert(islotStart < m_islotWritePos); + + std::vector<int> vislotStartStack; // indices of starts of ranges to reverse + + // Stack corresponding to embedding and override markers: + std::vector<DirCode> vdircMarkerStack; + std::vector<DirCode> vdircOverrideStack; + std::vector<int> vnLevelStack; + + vislotStarts.clear(); + vislotStops.clear(); + + //vislotStartStack.Push(islot); + + int islotSegLim = (FullyWritten() || m_islotSegLim > -1) ? + FinalSegLim() : + WritePos() + 100; // arbitrarily large number + + if (islotStart > islotSegLim) + { + // Don't do anything fancy with stuff beyond the end of the segment. + return 0; + } + + DirCode dircMarker = kdircNeutral; // none so far + int nCurrLevel = nTopDirLevel; + int nMarkerLevel = nTopDirLevel; + //bool fHitMark = false; + DirCode dircOverride = kdircNeutral; + int islot = islotStart; + Assert(islot <= m_islotWritePos); + + while (true) + { + GrSlotState * pslot = m_vpslot[islot]; + DirCode dirc = pslot->DirProcessed(); + bool fIncludeLast = false; + int nSlotDirLevel; + if (dirc == kdircLRO || dirc == kdircLRE) // steps X1, X3 in the Unicode bidi algorithm + { + vdircMarkerStack.push_back(dircMarker); + vdircOverrideStack.push_back(dircOverride); + vnLevelStack.push_back(nCurrLevel); + dircMarker = dirc; + dircOverride = (dirc == kdircLRO) ? kdircL : kdircNeutral; + nSlotDirLevel = (nCurrLevel % 2) ? + nCurrLevel + 1 : nCurrLevel + 2; // next highest LTR level + nMarkerLevel = nSlotDirLevel; + + pslot->SetDirProcessed(dircMarker); + pslot->SetDirLevel(nMarkerLevel); + pslot->MarkDirProcessed(); + } + else if (dirc == kdircRLO || dirc == kdircRLE) // steps X2, X4 + { + vdircMarkerStack.push_back(dircMarker); + vdircOverrideStack.push_back(dircOverride); + vnLevelStack.push_back(nCurrLevel); + dircMarker = dirc; + dircOverride = (dirc == kdircRLO) ? kdircR : kdircNeutral; + nSlotDirLevel = (nCurrLevel % 2) ? + nCurrLevel + 2 : nCurrLevel + 1; // next highest RTL level + nMarkerLevel = nSlotDirLevel; + + pslot->SetDirProcessed(dircMarker); + pslot->SetDirLevel(nMarkerLevel); + pslot->MarkDirProcessed(); + } + else if (dirc == kdircPDF) + { + if (dircMarker == kdircNeutral) + pslot->SetDirProcessed((nCurrLevel % 2) ? kdircR : kdircL); + else + pslot->SetDirProcessed(RightToLeftDir(dircMarker) ? kdircPdfR : kdircPdfL); + pslot->SetDirLevel(nMarkerLevel); + pslot->MarkDirProcessed(); + + if (vnLevelStack.size()) + { + dircMarker = vdircMarkerStack.back(); + vdircMarkerStack.pop_back(); + dircOverride = vdircOverrideStack.back(); + vdircOverrideStack.pop_back(); + nMarkerLevel = vnLevelStack.back(); + vnLevelStack.pop_back(); + nSlotDirLevel = nMarkerLevel; + fIncludeLast = true; + } + else // ignore + nSlotDirLevel = nCurrLevel; + } + else + { + nSlotDirLevel = GetSlotDirLevel(pengst, islot, nTopDirLevel, nMarkerLevel, + dircMarker, dircOverride); + if (nSlotDirLevel == -1) + return -1; // not enough slots + } + + Assert(pengst->WhiteSpaceOnly() || nCurrLevel >= nTopDirLevel); + while (nSlotDirLevel > nCurrLevel) + { + vislotStartStack.push_back(islot); + nCurrLevel++; + } + // otherwise... + while (nTopDirLevel <= nCurrLevel && nSlotDirLevel < nCurrLevel) + { + if (vislotStartStack.size()) + { + int islotStartTmp = vislotStartStack.back(); + vislotStartStack.pop_back(); + vislotStarts.push_back(islotStartTmp); + vislotStops.push_back(islot - 1); // previous character was the end of the range + } + else + Assert(pengst->WhiteSpaceOnly()); // the space is higher than the segment itself + nCurrLevel--; + } + // In the case of white-space-only segments, the space may be bumped up to a level + // higher than the segment itself. + Assert(pengst->WhiteSpaceOnly() || vislotStartStack.size() + nTopDirLevel == (unsigned)nCurrLevel); + Assert(pengst->WhiteSpaceOnly() || vnLevelStack.size() == vdircMarkerStack.size()); + Assert(pengst->WhiteSpaceOnly() || vdircOverrideStack.size() == vdircMarkerStack.size()); + + if (nCurrLevel <= nTopDirLevel) + { + if (fIncludeLast) + { + // Include in the range to reverse the PDF or the char after the LRM/RLM. + // (Note: don't use a size_t below: we are decrementing below zero.) + for (int i = signed(vislotStops.size()) - 1; i >= 0 && vislotStops[i] == islot-1; i--) + { + vislotStops[i] = islot; + } + islot++; + } + break; // reached end of reversible range + } + + islot++; + if (islot >= m_islotWritePos) + { + if (m_fFullyWritten) + break; // end of input + else + return -1; // not enough slots + } + else if (islot >= islotSegLim) + { + break; // end of input + } + } + + // Reached the end of a reversable range. + while (vislotStartStack.size() > 0) + { + int islotStartTmp = vislotStartStack.back(); + vislotStartStack.pop_back(); + vislotStarts.push_back(islotStartTmp); + //vislotStops.push_back(min(islot, ReadPos() - 1)); + //vislotStops.push_back(min(islot, max(ReadPos() - 1, islotStartTmp))); + vislotStops.push_back(min(islot - 1, islotSegLim - 1)); + nCurrLevel--; + } + Assert(pengst->WhiteSpaceOnly() || nCurrLevel == nTopDirLevel); + Assert(vislotStarts.size() == vislotStops.size()); + + // The start- and stop-lists were generated in the order that we hit the stops, in other + // words, from inner to outer. Which is the order in which we need to do the reversals, too. + + return islot; +} + + +/*---------------------------------------------------------------------------------------------- + Return the direction level of the glyph at the given position, according to the + Unicode bidi algorithm. Return -1 if there aren't enough slots in the stream to + figure it out. + + ???? How should this handle the reprocessing sub-stream???? + + @param ptman + @param islot - slot for which information is requested + @param nOuterLevel - 0 = left-to-right, 1 = right-to-left + @param nCurrLevel + @param dircMarker - most recent override or embedding marker, if any + (for interpreting PDF codes) + @param dircOverride - override imposed by LRO or RLO +----------------------------------------------------------------------------------------------*/ +int GrSlotStream::GetSlotDirLevel(EngineState * pengst, int islot, + int nOuterLevel, int nCurrLevel, DirCode dircMarker, DirCode dircOverride) +{ +// Assert(m_psstrmReprocess == NULL); + + GrSlotState * pslot = m_vpslot[islot]; + + int nDirLevel = pslot->DirLevel(); + if (nDirLevel > -1) + // already figured out + return nDirLevel; + + if (pengst->WhiteSpaceOnly()) + { + // White-space-only segments should not have any reordering. + nDirLevel = pengst->ParaRightToLeft(); + pslot->SetDirLevel(nDirLevel); + pslot->MarkDirProcessed(); + return nDirLevel; + } + + DirCode dirc = pslot->DirProcessed(); + + if (!pslot->DirHasBeenProcessed() && (WeakDir(dirc) || NeutralDir(dirc)) ) + { + // Resolve weak and neutral types; process an entire run of weak types at once, + // in several passes. + + int islotWeakRunLim = islot + 1; + while (true) + { + if (islotWeakRunLim >= m_islotWritePos) + { + if (!m_fFullyWritten) + return -1; // get more slots + else + break; + } + else if (m_islotSegLim > -1 && islotWeakRunLim >= SegLimIfKnown()) + { + break; + } + if (StrongDir(m_vpslot[islotWeakRunLim]->DirProcessed())) + // ignore the line-break; we need the next strong dir after that + //// && !m_vpslot[islotWeakRunLim]->IsFinalLineBreak(ptman->LBGlyphID())) + { + break; + } + if (m_vpslot[islotWeakRunLim]->DirProcessed() == kdircPDF && + dircMarker != kdircUnknown) + { + // A valid PDF is a strong code. TODO: replace kdircPDF with kdircPdfL/R + break; + } + + islotWeakRunLim++; + } + + int islotLp; + + // X6. Handle directional overrides: LRO, RLO. + if (dircOverride != kdircNeutral) + { + for (islotLp = islot; islotLp < islotWeakRunLim; islotLp++) + { + m_vpslot[islotLp]->SetDirProcessed(dircOverride); + m_vpslot[islotLp]->MarkDirProcessed(); + } + goto LSetDir; + } + + DirCode dircLp; + //DirCode dircTmp; + + // W1. Non-spacing marks get the type of the previous slot. + for (islotLp = islot; islotLp < islotWeakRunLim; islotLp++) + { + Assert(!m_vpslot[islotLp]->DirHasBeenProcessed()); + + if (m_vpslot[islotLp]->DirProcessed() == kdircNSM) + { +#ifdef _DEBUG + if (islotLp <= 0) + { + Warn("Pathological case in bidi algorithm"); + } +#endif // _DEBUG + dircLp = AdjacentNonBndNeutralCode(pengst, islotLp - 1, -1, dircMarker); + + m_vpslot[islotLp]->SetDirProcessed(dircLp); + } + } + + // W2. European numbers are changed to Arabic numbers if the previous strong type + // was an Arabic letter. + DirCode dircPrevStrong = AdjacentStrongCode(pengst, islot - 1, -1, dircMarker); + if (dircPrevStrong == kdircRArab) + { + for (islotLp = islot; islotLp < islotWeakRunLim; islotLp++) + { + dircLp = m_vpslot[islotLp]->DirProcessed(); + if (dircLp == kdircEuroNum) + m_vpslot[islotLp]->SetDirProcessed(kdircArabNum); + } + } + + // W4. A single European separator between two Latin numbers changes to a Latin number. + // A single common separator between two numbers of the same type changes to that + // type. + for (islotLp = islot; islotLp < islotWeakRunLim; islotLp++) + { + dircLp = m_vpslot[islotLp]->DirProcessed(); + if ((dircLp == kdircEuroSep || dircLp == kdircComSep) && + islotLp + 1 < m_islotWritePos) // don't test last slot in stream + { + DirCode dircPrev = AdjacentNonBndNeutralCode(pengst, islotLp - 1, -1, dircMarker); + DirCode dircNext = AdjacentNonBndNeutralCode(pengst, islotLp + 1, 1, dircMarker); + if (dircPrev == dircNext) + { + if (dircLp == kdircEuroSep && dircPrev == kdircEuroNum) + { + m_vpslot[islotLp]->SetDirProcessed(kdircEuroNum); + } + else if (dircLp == kdircComSep && + (dircPrev == kdircEuroNum || dircPrev == kdircArabNum)) + { + m_vpslot[islotLp]->SetDirProcessed(dircPrev); + } + } + } + } + + // W5, W6. A sequence of European number terminators adjacent to Latin numbers changes + // to Latin numbers. + // This appears to be an ambiguity in the official algorithm: do we search backward + // first, or forward? For our purposes, it is easier to search backward first. + for (islotLp = islot; islotLp < islotWeakRunLim; islotLp++) + { + dircLp = m_vpslot[islotLp]->DirProcessed(); + if (dircLp == kdircEuroTerm || dircLp == kdircBndNeutral) + { + if (TerminatorSequence(pengst, islotLp - 1, -1, dircMarker) == kdircEuroNum) + dircLp = kdircEuroNum; + else + { + if (TerminatorSequence(pengst, islotLp + 1, 1, dircMarker) == kdircEuroNum) + dircLp = kdircEuroNum; + // else could possibly be unknown if we are at the end of the stream + } + } + + // W6. Separators and terminators change to neutral. (Don't change + // boundary neutrals at this point, because that would confuse step Pre-L1.) + if (dircLp == kdircEuroSep || dircLp == kdircEuroTerm || dircLp == kdircComSep) + { + dircLp = kdircNeutral; + } + m_vpslot[islotLp]->SetDirProcessed(dircLp); + } + + // W7. European numbers are changed to plain left-to-right if the previous strong type + // was left-to-right. + dircPrevStrong = AdjacentStrongCode(pengst, islot - 1, -1, dircMarker); + if (dircPrevStrong == kdircL) + { + for (islotLp = islot; islotLp < islotWeakRunLim; islotLp++) + { + dircLp = (m_vpslot[islotLp]->DirProcessed()); + if (dircLp == kdircEuroNum) + m_vpslot[islotLp]->SetDirProcessed(kdircL); + } + } + + // Pre-L1. Make a list of all the trailing whitespace characters that need to take + // on the direction of the final line break (ie, the top direction). We can't actually + // change them yet, because that would confuse the loop below. But we have to + // record them now because they might get changed in the loop below. + std::vector<int> vislotTrailingWS; + int islotFinalLB = -1; + for (islotLp = islotWeakRunLim; islotLp-- > islot; ) + { + if (m_vpslot[islotLp]->IsFinalLineBreak(pengst->TableManager()->LBGlyphID())) + { + islotFinalLB = islotLp; + for (int islotLp2 = islotLp; islotLp2-- > islot; ) + { + dircLp = m_vpslot[islotLp2]->DirProcessed(); + if (dircLp == kdircWhiteSpace || dircLp == kdircBndNeutral) + vislotTrailingWS.push_back(islotLp2); + else + break; // hit something other than whitespace or neutral + } + break; + } + } + + // N1. A sequence of neutrals takes the direction of the strong surrounding text if + // the text on both sides has the same direction. Latin and Arabic numbers are treated + // as if they are right-to-left. + // (If they are not the same, leave the type as neutral.) + for (islotLp = islot; islotLp < islotWeakRunLim; islotLp++) + { + dircLp = m_vpslot[islotLp]->DirProcessed(); + + // Change Boundary Neutrals to plain neutrals (must happen after Pre-L1). + if (dircLp == kdircBndNeutral) + { + m_vpslot[islotLp]->SetDirProcessed(kdircNeutral); + dircLp = kdircNeutral; + } + + if (NeutralDir(dircLp)) + { + // First, look for either a strong code or a number. If we get at least + // one strong code and a number with the same direction, use that strong + // code. + DirCode dircPrev = AdjacentStrongCode(pengst, islotLp - 1, -1, dircMarker, true); + DirCode dircNext = AdjacentStrongCode(pengst, islotLp + 1, 1, dircMarker, true); + + if (dircNext == kdircUnknown) + return -1; + + if (dircPrev == kdircUnknown) + dircPrev = dircNext; + + DirCode dircResult = kdircUnknown; + if (dircNext == kdircNeutral) + {} + else if (RightToLeftDir(dircPrev) == RightToLeftDir(dircNext) && + (StrongDir(dircNext) || StrongDir(dircPrev))) + { + // Got a strong matching direction. + dircResult = (StrongDir(dircPrev)) ? dircPrev : dircNext; + } + else if (dircNext == kdircPdfL || dircNext == kdircPdfR) + { + // A following PDF flag wins, because it represents the edge of a run. + dircResult = dircNext; + } + else if (StrongDir(dircNext) && StrongDir(dircPrev)) + { // We must have text in different directions. + } + else + { + // Try again, insisting on strong codes, no numbers. + if (!StrongDir(dircPrev)) + dircPrev = AdjacentStrongCode(pengst, islotLp - 1, -1, dircMarker, false); + if (!StrongDir(dircNext)) + dircNext = AdjacentStrongCode(pengst, islotLp + 1, 1, dircMarker, false); + if (dircPrev == kdircUnknown) + dircPrev = dircNext; + + if (RightToLeftDir(dircPrev) == RightToLeftDir(dircNext) && + (StrongDir(dircNext) || StrongDir(dircPrev))) + { + // Got a strong matching direction. + dircResult = (StrongDir(dircPrev)) ? dircPrev : dircNext; + } + } + + if (dircResult != kdircUnknown) + { + dircResult = (RightToLeftDir(dircResult)) ? kdircR : kdircL; + m_vpslot[islotLp]->SetDirProcessed(dircResult); + } + // else leave as neutral + } + } + + // L1. Now change trailing neutrals recorded above to the directionality of + // the immediately following line-break, if any. + if (islotFinalLB != -1) + { + DirCode dircLB = m_vpslot[islotFinalLB]->DirProcessed(); + dircLB = (dircLB == kdircLlb) ? kdircL : kdircR; + for (int iislot = 0; iislot < signed(vislotTrailingWS.size()); iislot++) + { + islot = vislotTrailingWS[iislot]; + m_vpslot[islot]->SetDirProcessed(dircLB); + } + } + + // Mark all the slots processed. + for (islotLp = islot; islotLp < islotWeakRunLim; islotLp++) + m_vpslot[islotLp]->MarkDirProcessed(); + } + else + m_vpslot[islot]->MarkDirProcessed(); + +LSetDir: + + // Final resolution. + + dirc = pslot->DirProcessed(); + + if (dirc != kdircLlb && dirc != kdircRlb && dircOverride != kdircNeutral) + { + // Enforce whatever is imposed by the override. + m_vpslot[islot]->SetDirProcessed(dircOverride); + nDirLevel = nCurrLevel; + } + else if ((nCurrLevel % 2) == 0) // current level is even (left-to-right) + { + switch (dirc) + { + case kdircNeutral: + case kdircWhiteSpace: + nDirLevel = nCurrLevel; + break; + + case kdircL: + nDirLevel = nCurrLevel; + break; + + case kdircLlb: + case kdircRlb: // all line-breaks have the direction of the paragraph + nDirLevel = nOuterLevel; + break; + + case kdircR: + case kdircRArab: + nDirLevel = nCurrLevel + 1; + break; + + case kdircEuroNum: + case kdircArabNum: + nDirLevel = nCurrLevel + 2; + break; + + default: + Assert(false); + nDirLevel = nCurrLevel; + } + } + else // current level is odd (right-to-left) + { + switch (dirc) + { + case kdircNeutral: + case kdircWhiteSpace: + nDirLevel = nCurrLevel; + break; + + case kdircR: + //case kdircRlb: + case kdircRArab: + nDirLevel = nCurrLevel; + break; + + case kdircL: + //case kdircLlb: + case kdircEuroNum: + case kdircArabNum: + nDirLevel = nCurrLevel + 1; + break; + + case kdircLlb: + case kdircRlb: // all line-breaks have the direction of the paragraph + nDirLevel = nOuterLevel; + break; + + default: + Assert(false); + nDirLevel = nCurrLevel; + } + } + pslot->SetDirLevel(nDirLevel); + + return nDirLevel; +} + +/*---------------------------------------------------------------------------------------------- + Return the next or previous strong code, kdircNeutral if we don't find one, + or kdircUnknown if we need more slots to process. + + @param islot - index of slot of interest + @param nInc - +1 to search forward, -1 to search backward + @param fNumbersAreStrong - true if we want to treat numbers as having strong + directionality + @param dircPDF - how to interpret PDF codes (from corresponding RLO, etc) +----------------------------------------------------------------------------------------------*/ +DirCode GrSlotStream::AdjacentStrongCode(EngineState * pengst, int islot, int nInc, + DirCode dircPDF, bool fNumbersAreStrong) +{ + if (islot < 0) + return pengst->InitialStrongDir(); + + else if (islot >= m_islotWritePos) + { + if (m_fFullyWritten || (m_islotSegLim > -1 && islot >= SegLimIfKnown())) + return kdircNeutral; + else + return kdircUnknown; + } + + DirCode dirc = m_vpslot[islot]->DirProcessed(); + + if (dirc == kdircPDF && StrongDir(dircPDF)) + return (RightToLeftDir(dircPDF)) ? kdircPdfR : kdircPdfL; + if (StrongDir(dirc)) + return dirc; + if (fNumbersAreStrong && (dirc == kdircEuroNum || dirc == kdircArabNum)) + return dirc; + + return AdjacentStrongCode(pengst, islot + nInc, nInc, dircPDF, fNumbersAreStrong); +} + +/*---------------------------------------------------------------------------------------------- + Return kdircEuroNum if there is a terminator sequence starting or ending with a + European number. Return kdircUnknown if we need more slots to process. + Return kdircNeutral otherwise. + + @param islot - index of slot of interest + @param nInc - +1 to search forward, -1 to search backward + @param dircPDF - how to interpret PDF codes (from corresponding RLO, etc) +----------------------------------------------------------------------------------------------*/ +DirCode GrSlotStream::TerminatorSequence(EngineState * pengst, int islot, int nInc, + DirCode dircPDF) +{ + if (islot < 0) + return pengst->InitialTermDir(); + + else if (islot >= m_islotWritePos) + { + if (m_fFullyWritten) + return kdircNeutral; + else + return kdircUnknown; + } + + DirCode dirc = m_vpslot[islot]->DirProcessed(); + + if (dirc == kdircPDF) + return (RightToLeftDir(dircPDF)) ? kdircPdfR : kdircPdfL; + else if (dirc == kdircEuroNum) + // number found at end of terminator sequence + return kdircEuroNum; + else if (dirc == kdircEuroTerm) + // yes, this slot is a terminator; are there more? + return TerminatorSequence(pengst, islot + nInc, nInc, dircPDF); + else if (dirc == kdircLlb || dirc == kdircRlb) + // line-break slot--ignore and keep going + return TerminatorSequence(pengst, islot + nInc, nInc, dircPDF); + else if (dirc == kdircBndNeutral) + // boundary neutral--ignore and keep going + return TerminatorSequence(pengst, islot + nInc, nInc, dircPDF); + else + // no number found + return kdircNeutral; +} + +/*---------------------------------------------------------------------------------------------- + Return the previous code that is not a Boundary Neutral. Return Other Neutral if there + is no such. + + @param islot - index of slot of interest + @param nInc - +1 to search forward, -1 to search backward + @param dircPDF - how to interpret PDF codes (from corresponding RLO, etc) +----------------------------------------------------------------------------------------------*/ +DirCode GrSlotStream::AdjacentNonBndNeutralCode(EngineState * pengst, int islot, int nInc, + DirCode dircPDF) +{ + Assert(islot < m_islotWritePos); + if (islot < 0) + return kdircNeutral; // or should we use: ptman->InitialStrongDir() ?? + + else if (islot >= m_islotWritePos) + { + if (m_fFullyWritten) + return kdircNeutral; + else + return kdircUnknown; + } + + DirCode dirc = m_vpslot[islot]->DirProcessed(); + + if (dirc == kdircBndNeutral) + return AdjacentNonBndNeutralCode(pengst, islot + nInc, nInc, dircPDF); + else if (dirc == kdircPDF) + return (RightToLeftDir(dircPDF)) ? kdircPdfR : kdircPdfL; + else + return dirc; +} + +/*---------------------------------------------------------------------------------------------- + Directionality type functions. +----------------------------------------------------------------------------------------------*/ +bool StrongDir(DirCode dirc) +{ + return (dirc == kdircL || dirc == kdircR || dirc == kdircRArab || + dirc == kdircLRO || dirc == kdircRLO || dirc == kdircLRE || dirc == kdircRLE || + dirc == kdircPdfL || dirc == kdircPdfR); +} + +bool WeakDir(DirCode dirc) +{ + return (dirc == kdircEuroNum || dirc == kdircEuroSep || + dirc == kdircEuroTerm || dirc == kdircArabNum || + dirc == kdircComSep || dirc == kdircBndNeutral || dirc == kdircNSM); +} + +bool NeutralDir(DirCode dirc) +{ + return (dirc == kdircWhiteSpace || dirc == kdircNeutral); +} + +/*---------------------------------------------------------------------------------------------- + Special values used by the bidi algorithm. +----------------------------------------------------------------------------------------------*/ +bool BidiCode(int nUnicode) +{ + switch (nUnicode) + { + case knLRM: + case knRLM: + case knLRO: + case knRLO: + case knLRE: + case knRLE: + case knPDF: + return true; + default: + break; + } + return false; +} + +/*---------------------------------------------------------------------------------------------- + Return true if the directionality should be treated as right-to-left. + Used for the step of resolving neutrals (N1). +----------------------------------------------------------------------------------------------*/ +bool RightToLeftDir(DirCode dirc) +{ + switch (dirc) + { + case kdircL: + case kdircLRO: + case kdircLRE: + case kdircPdfL: + return false; + + case kdircR: + case kdircRArab: + case kdircArabNum: + case kdircEuroNum: + case kdircRLO: + case kdircRLE: + case kdircPdfR: + return true; + + case kdircNeutral: + return false; + + default: + Assert(false); + return false; + } +} + +/*---------------------------------------------------------------------------------------------- + Copy a slot from prevStream, incrementing the positions. +----------------------------------------------------------------------------------------------*/ +void GrSlotStream::CopyOneSlotFrom(GrSlotStream * psstrmPrev) +{ +#ifdef _DEBUG + gid16 chw; chw = psstrmPrev->Peek()->GlyphID(); +#endif + + // If we are exactly at the segment boundary, pass the information on to this stream. + if (psstrmPrev->AtSegMin()) + SetSegMinToWritePos(); + if (psstrmPrev->AtSegLim()) + SetSegLimToWritePos(); + + NextPut(psstrmPrev->NextGet()); + + AssertValid(); + psstrmPrev->AssertValid(); +} + +/*---------------------------------------------------------------------------------------------- + Copy one slot from the input stream to the recipient, which is the output stream. + Used when reversing ranges of glyphs; caller is responsible for adjusting the read- + and write-positions of the streams. + + REVIEW: How should this handle the reprocessing sub-stream? Currently this is not a problem + because this function is only used by the bidi pass, which never has intra-pass + reprocessing. +----------------------------------------------------------------------------------------------*/ +void GrSlotStream::SimpleCopyFrom(GrSlotStream * psstrmI, int islotInput, int islotOutput) +{ + if (signed(m_vpslot.size()) < islotOutput + 1) + { + m_vpslot.resize(islotOutput + 1); + m_vislotPrevChunkMap.resize(islotOutput + 1); + m_vislotNextChunkMap.resize(islotOutput + 1); + } + + m_vpslot[islotOutput] = psstrmI->m_vpslot[islotInput]; + m_vislotPrevChunkMap[islotOutput] = -1; + m_vislotNextChunkMap[islotOutput] = -1; + + Assert(m_fUsedByPosPass); + if (m_fUsedByPosPass && GotIndexOffset()) + m_vpslot[islotOutput]->SetPosPassIndex(islotOutput - m_cslotPreSeg, m_fInputToPosPass1); + +} + +/*---------------------------------------------------------------------------------------------- + Return true if there is physical space to add more glyphs, false if the positioning + has caused them to use all the alotted space. Recipient is assumed to be output + stream from the final pass. + + @param ptman - table manager, for handling the positioning + @param pgg - graphics device + @param xsSpaceAllotted - how much space is available + @param fWidthIsCharCount - kludge for test procedures + @param fIgnoreTrailingWS - true if we are ignoring trailing white-space (currently + always true) + @param twsh - how the segment is handling trailing white-space + @param pxsWidth - return width of stuff so far +----------------------------------------------------------------------------------------------*/ +bool GrSlotStream::MoreSpace(GrTableManager * ptman, + float xsSpaceAllotted, bool fWidthIsCharCount, + bool fIgnoreTrailingWS, TrWsHandling twsh, + float * pxsWidth) +{ + Assert(ptman->NumberOfPasses() - 1 == m_ipass); + + if (fWidthIsCharCount) + { + // Used in test procedures + *pxsWidth = (float)m_islotWritePos; + return (m_islotWritePos < xsSpaceAllotted); + } + else + { + *pxsWidth = -1; + if (!GotIndexOffset()) + return true; + if (WritePos() <= IndexOffset()) + return true; + int dislot = MaxClusterSlot(WritePos()); + if (dislot == kNegInfinity || dislot > 0) + // We're in the middle of processing a cluster--we have to keep processing, + // so for now assume there is more space. + return true; + + float xsWidth, xsVisWidth; + ptman->CalcPositionsUpTo(m_ipass, NULL, &xsWidth, &xsVisWidth); + + *pxsWidth = (fIgnoreTrailingWS || twsh == ktwshOnlyWs) ? xsVisWidth : xsWidth; + return (*pxsWidth < xsSpaceAllotted); + } +} + +/*---------------------------------------------------------------------------------------------- + Work backwards from the prevLineBreak position (or if none, the readPos) to find the + previous line-break of the given weight or less. Return the position of the inserted + line-break slot, or -1 if no appropriate break point was found. Recipient should be + the output of the final line break pass, or if none, the output of the + glyph-generation pass. + + @param ptman - table manager, for supplying new slots + @param islotPrevBreak - position of previous inserted line break + @param fInsertedLB - did we actually insert a line-break glyph before? + @param islotStartTry - where to start looking, or -1 for the end of the stream + @param lb - (max) break weight to allow + @param twsh - how we are handling trailing white-space + @param islotMin - first slot that is officially part of the segment (after initial LB) + @param plbNextToTry - the best we found +----------------------------------------------------------------------------------------------*/ +int GrSlotStream::InsertLineBreak(GrTableManager * ptman, + int islotPrevBreak, bool fInsertedLB, int islotStartTry, + LineBrk lb, TrWsHandling twsh, int islotMin, + LineBrk * plbNextToTry) +{ + EngineState * pengst = ptman->State(); + + Assert(!fInsertedLB || islotPrevBreak > -1); + + int ichwSegOffset; + int islot; + LineBrk lbFound; + if (!FindSegmentEnd(ptman, islotStartTry, lb, twsh, islotMin, + &islot, &ichwSegOffset, &lbFound, plbNextToTry)) + { + return -1; // didn't find a legal break + } + + GrSlotState * pslotCopyFeat = pengst->AnAdjacentSlot(m_ipass, islot + 1); + Assert(pslotCopyFeat); + + if (islotPrevBreak > -1 && fInsertedLB) + { + //GrSlotState * pslotOld = m_vpslot[islotPrevBreak]; + + m_vpslot.erase(m_vpslot.begin() + islotPrevBreak); + m_vislotPrevChunkMap.erase(m_vislotPrevChunkMap.begin() + islotPrevBreak); + m_vislotNextChunkMap.erase(m_vislotNextChunkMap.begin() + islotPrevBreak); + AdjustPrevStreamNextChunkMap(ptman, islotPrevBreak + 1, -1); + } + else + { + m_islotReadPos++; + m_islotWritePos++; + } + + GrSlotState * pslotNew; + pengst->NewSlot(ptman->LBGlyphID(), pslotCopyFeat, 0, ichwSegOffset, &pslotNew); + + pslotNew->SetSpecialSlotFlag(kspslLbFinal); + pslotNew->SetBreakWeight(lbFound); + pslotNew->SetDirectionality(ptman->RightToLeft() ? kdircRlb : kdircLlb); + + int islotLB = islot + 1; + m_vpslot.insert(m_vpslot.begin() + islotLB, pslotNew); + // Inserting -1 makes it part of a chunk with the previous char/glyph + m_vislotPrevChunkMap.insert(m_vislotPrevChunkMap.begin() + islotLB, -1); + m_vislotNextChunkMap.insert(m_vislotNextChunkMap.begin() + islotLB, -1); + AdjustPrevStreamNextChunkMap(ptman, islotLB, 1); + // We don't need to adjust the following stream because we are going to unwind it anyway. + + if (m_fUsedByPosPass && GotIndexOffset()) + { + pslotNew->SetPosPassIndex(islot - m_cslotPreSeg, m_fInputToPosPass1); + // Increment the stream index for the following slots. + for (int islotTmp = islotLB; + islotTmp < ((islotPrevBreak == -1) ? m_islotWritePos : islotPrevBreak + 1); + islotTmp++) + { + SlotAt(islotTmp)->IncPosPassIndex(); + } + } + + m_islotSegLim = islotLB + 1; // after the line-break glyph + + return islotLB; +} + +/*---------------------------------------------------------------------------------------------- + Make a segment break that does not correspond to a line break; ie, don't insert + a line-break glyph. + + Work backwards from the prevLineBreak position (or if none, the readPos) to find the + previous line-break of the given weight or less. Return the position of the final slot + in the segment, or -1 if no appropriate break point was found. Recipient should be + the output of the final line break pass, if any, or the glyph-generation pass. + + @param ptman - table manager, for supplying new slots + @param islotPrevBreak - position of previous inserted line break + @param fInsertedLB - did we actually insert a line-break glyph previously; currently + ignored + @param islotStartTry - where to start looking, or -1 for the end of the stream + @param lb - (max) break weight to allow + @param twsh - how we are handling trailing white-space + @param islotMin - first slot that is officially part of the segment (after initial LB) +----------------------------------------------------------------------------------------------*/ +int GrSlotStream::MakeSegmentBreak(GrTableManager * ptman, + int islotPrevBreak, bool fInsertedLB, int islotStartTry, + LineBrk lb, TrWsHandling twsh, int islotMin, + LineBrk * plbNextToTry) +{ + int ichwSegOffset; + int islot; + LineBrk lbFound; + if (!FindSegmentEnd(ptman, islotStartTry, lb, twsh, islotMin, + &islot, &ichwSegOffset, &lbFound, plbNextToTry)) + { + return -1; // didn't find a legal break + } + + // Review: do we need to delete any previously-inserted line-break glyph from the stream? + // Doesn't seem like this has been a problem so far. + + m_islotSegLim = islot + 1; // after the final glyph of this segment + + return islot; +} + +/*---------------------------------------------------------------------------------------------- + Work backwards from the prev line break position (or if none, the readPos) to find an + earlier line-break of the given weight or less. + Recipient should be the output of the final line break pass, if any, or the output + of the glyph-generation pass. + + @param ptman - table manager, for supplying new slots + @param islotStartTry - where to start looking, or -1 for the end of the stream + @param lb - (max) break weight to allow + @param twsh - how we are handling trailing white-space + @param islotMin - first slot that is officially part of the segment (after initial LB) + @param pislot - slot AFTER which the break will go (not the lim of the segment) + @param pichwSegOffset - offset relative to the segment + @param plbFound - weight of break to be created + @param plbNextToTry - best found, in case we didn't find one at the requested level + + @return Whether or not an appropriate break was found. +----------------------------------------------------------------------------------------------*/ +bool GrSlotStream::FindSegmentEnd(GrTableManager * ptman, + int islotStartTry, LineBrk lb, TrWsHandling twsh, int islotMin, + int * pislot, int * pichwSegOffset, LineBrk * plbFound, + LineBrk * plbNextToTry) +{ + Assert(AssertValid()); + Assert(m_ipass == ptman->NumberOfLbPasses()); + + if (islotStartTry < 0) + return false; + + *pislot = islotStartTry; + GrSlotState * pslot = m_vpslot[*pislot]; + *plbNextToTry = klbClipBreak; // worst break + + ptman->State()->SetRemovedTrWhiteSpace(false); + + while (twsh != ktwshOnlyWs) // in white-space only case, ignore lb values + { + if (*pislot < islotMin) + { + return false; + } + *plbFound = LineBrk(pslot->m_lb); + // Sanity check + if (abs(*plbFound) > klbClipBreak) + { +// Assert(false); + Warn("Unusually large breakweight"); + *plbFound = (LineBrk)((*plbFound < 0) ? -klbClipBreak : klbClipBreak); + } + *plbNextToTry = (LineBrk)(min(static_cast<int>(*plbNextToTry), abs(*plbFound))); + *pichwSegOffset = pslot->SegOffset(); + + if (int(*plbFound) >= 0 && *plbFound <= lb) + break; + + --(*pislot); + if (*pislot < 0) + { + return false; + } + pslot = m_vpslot[*pislot]; + + if (int(*plbFound) <= 0 && ((int)*plbFound * -1) <= lb) + { + *plbFound = LineBrk(int(*plbFound) * -1); + break; + } + } + + // Found a good break. + + if (twsh == ktwshOnlyWs) + { + // white-space-only segment + while (pslot->Directionality() != kdircWhiteSpace) + { + --(*pislot); + if (*pislot < 0) + return false; + pslot = m_vpslot[*pislot]; + *pichwSegOffset = pslot->SegOffset(); + } + } + else if (twsh == ktwshNoWs) + { + // no trailing white-space: remove it + while (pslot->Directionality() == kdircWhiteSpace) + { + --(*pislot); + if (*pislot < 0) + return false; + pslot = m_vpslot[*pislot]; + *pichwSegOffset = pslot->SegOffset(); + ptman->State()->SetRemovedTrWhiteSpace(true); + } + } + + return true; +} + +/*---------------------------------------------------------------------------------------------- + Return true if the stream (which should be the output of the last linebreak pass) has + a better break at some earlier point than the one we found. + + @param islotBreak - position of inserted or final break point in stream, or index + of last valid slot +----------------------------------------------------------------------------------------------*/ +bool GrSlotStream::HasEarlierBetterBreak(int islotBreak, LineBrk lbFound, gid16 chwLB) +{ + Assert(AssertValid()); + + int islot = islotBreak; + if (SlotAt(islot)->IsFinalLineBreak(chwLB)) + islot--; + + if (lbFound == klbNoBreak) + lbFound = (LineBrk)SlotAt(islot)->BreakWeight(); + + while (islot >= 0) + { + if (SlotAt(islot)->IsInitialLineBreak(chwLB)) + return false; + if (SlotAt(islot)->BreakWeight() < lbFound) + return true; + islot--; + } + + return false; +} + +/*---------------------------------------------------------------------------------------------- + Make sure the stream contains a local instance of the given slot, not a copy used by + a previous pass. +----------------------------------------------------------------------------------------------*/ +void GrSlotStream::EnsureLocalCopy(GrTableManager * ptman, GrSlotState * pslot, + GrSlotStream * psstrmIn) +{ + //Assert(m_vpslot[islot + m_cslotPreSeg] == pslot); + Assert(pslot->PassModified() <= m_ipass); + + if (pslot->PassModified() < m_ipass) + { + int islot = pslot->PosPassIndex(); + Assert(SlotAtPosPassIndex(islot) == pslot); + GrSlotState * pslotNew; + ptman->State()->NewSlotCopy(pslot, m_ipass, &pslotNew); + m_vpslot[islot + m_cslotPreSeg] = pslotNew; + + psstrmIn->ReplaceSlotInReprocessBuffer(pslot, pslotNew); + } +} + +/*---------------------------------------------------------------------------------------------- + A slot has been replace within the coorsponding output stream. Make sure the reprocess + buffer of this stream constains the same slot. +----------------------------------------------------------------------------------------------*/ +void GrSlotStream::ReplaceSlotInReprocessBuffer(GrSlotState * pslotOld, GrSlotState * pslotNew) +{ + if (m_islotReprocPos > -1) + { + for (size_t islot = 0; islot < m_vpslotReproc.size(); islot++) + { + if (m_vpslotReproc[islot] == pslotOld) + m_vpslotReproc[islot] = pslotNew; + } + } +} + +/*---------------------------------------------------------------------------------------------- + Inserting a line break can potentially alter the directionality of preceeding glyphs. + Zap the information that has been calculated. + + @param islotLB - index of the inserted final line-break glyph +----------------------------------------------------------------------------------------------*/ +void GrSlotStream::ZapCalculatedDirLevels(int islotLB) +{ + int islot; + for (islot = m_islotWritePos; islot-- > islotLB; ) + m_vpslot[islot]->ZapDirLevel(); + + for (islot = islotLB; islot-- > 0; ) + { + if (StrongDir(m_vpslot[islot]->Directionality())) + // We've found a strong direction code; don't need to zap beyond this point. + return; + + m_vpslot[islot]->ZapDirLevel(); + } +} + +/*---------------------------------------------------------------------------------------------- + Return the slot index of the last slot in the cluster for the just-processed chunk, + relative to the last slot in the chunk. + + Return kNegInfinity if there are not enough slots in the stream to tell. This can + happen while we are in the middle of processing a cluster. +----------------------------------------------------------------------------------------------*/ +int GrSlotStream::MaxClusterSlot(int islotChunkMin, int islotChunkLim) +{ + Assert(islotChunkLim > islotChunkMin); + + if (!m_fUsedByPosPass) + return 0; // no clusters yet + + int islotRet = SlotAt(islotChunkLim - 1)->PosPassIndex(); + + for (int islot = islotChunkMin; islot < islotChunkLim; islot++) + { + GrSlotState * pslot = SlotAt(islot); + + if (m_cslotPreSeg == -1) + return kNegInfinity; + if (!HasSlotAtPosPassIndex(pslot->AttachRootPosPassIndex())) + return kNegInfinity; + + GrSlotState * pslotBase = pslot->Base(this); + + int dislotOffset = pslotBase->LastLeafOffset(this); + if (dislotOffset == kNegInfinity) + return kNegInfinity; + islotRet = max(islotRet, pslotBase->PosPassIndex() + dislotOffset); + } + + // Make it relative to the last slot in the chunk. + islotRet -= SlotAt(islotChunkLim - 1)->PosPassIndex(); + + return islotRet; +} + +/*---------------------------------------------------------------------------------------------- + Return the break weight of the given slot, which should be a line-break. + OBSOLETE?? +----------------------------------------------------------------------------------------------*/ +LineBrk GrSlotStream::BreakWeightAt(gid16 chwLB, int islot) +{ + GrSlotState * pslot = GetSlotAt(islot); + Assert(pslot->IsLineBreak(chwLB)); + return LineBrk(pslot->m_lb); +} + +/*---------------------------------------------------------------------------------------------- + Append an initial or final line break of the given weight to the stream. + + @param ptman - table manager, for generating new slot + @param lb - break weight for the line-break glyph + @param dirc - directionality code for the line-break glyph + @param islotLB - where to insert, or -1 if at the end + @param fInitial - is this an initial line-break, or a final break? + @param ichwSegOffset - offset relative to the beginning of the segment +----------------------------------------------------------------------------------------------*/ +void GrSlotStream::AppendLineBreak(GrTableManager * ptman, GrCharStream * pchstrm, + LineBrk lb, DirCode dirc, int islotLB, bool fInitial, int ichwSegOffset) +{ + EngineState * pengst = ptman->State(); + + Assert(AssertValid()); +/// Assert(m_islotReprocPos == -1); + + Assert(islotLB == -1 || fInitial); + if (islotLB == -1) + islotLB = m_islotWritePos; + +// int islot = m_islotWritePos; + + GrSlotState * pslotCopyFeat = pengst->AnAdjacentSlot(m_ipass, islotLB); + GrSlotState * pslotNew; + if (pslotCopyFeat) + pengst->NewSlot(ptman->LBGlyphID(), pslotCopyFeat, 0, ichwSegOffset, &pslotNew); + else + { + GrFeatureValues fval; + pchstrm->CurrentFeatures(ptman, &fval); + pengst->NewSlot(ptman->LBGlyphID(), fval, 0, ichwSegOffset, -1, &pslotNew); + } + + pslotNew->m_lb = lb; + pslotNew->SetSpecialSlotFlag(fInitial ? kspslLbInitial : kspslLbFinal); + //pslotNew->m_fInitialLB = fInitial; // TODO: remove + pslotNew->SetDirectionality(dirc); + + m_vpslot.insert(m_vpslot.begin() + islotLB, pslotNew); + // Make it part of a chunk with previous char/glyph + m_vislotPrevChunkMap.insert(m_vislotPrevChunkMap.begin() + islotLB, -1); + m_vislotNextChunkMap.insert(m_vislotNextChunkMap.begin() + islotLB, -1); + if (m_ipass > 0) + AdjustPrevStreamNextChunkMap(ptman, islotLB, 1); + + m_islotWritePos++; + + if (m_fUsedByPosPass && GotIndexOffset()) + { + pslotNew->SetPosPassIndex(islotLB - m_cslotPreSeg, m_fInputToPosPass1); + // Increment the stream index for the following slots. + for (int islotTmp = islotLB + 1; islotTmp < m_islotWritePos; islotTmp++) + { + SlotAt(islotTmp)->IncPosPassIndex(); + } + } + + if (fInitial) + m_islotSegMin = islotLB; // just before the LB + else + m_islotSegLim = islotLB + 1; // just after the LB +} + +/*---------------------------------------------------------------------------------------------- + Return the index of the line break glyph within the specified range, or -1 if none. + Recipient is functioning as the output stream. + + @param chwLB - glyph ID being used for line-break glyphs + @param islotMin/Lim - range to search +----------------------------------------------------------------------------------------------*/ +int GrSlotStream::FindFinalLineBreak(gid16 chwLB, int islotMin, int islotLim) +{ + for (int islot = islotMin; islot < islotLim; ++islot) + { + if (m_vpslot[islot]->IsFinalLineBreak(chwLB)) + return islot; + } + return -1; +} + +/*---------------------------------------------------------------------------------------------- + A slot has been inserted (nInc == 1) or deleted (nInc == -1). Adjust the next-chunk map + of the previous stream to match. +----------------------------------------------------------------------------------------------*/ +void GrSlotStream::AdjustPrevStreamNextChunkMap(GrTableManager * ptman, int islotMod, int nInc) +{ + if (m_ipass == 0) + return; // no previous stream to adjust + + int islotTmp = max(islotMod - 5, 0); + while (islotTmp > 0 && m_vislotPrevChunkMap[islotTmp] == -1) + islotTmp--; + + ptman->InputStream(m_ipass)->AdjustNextChunkMap( + ((islotTmp <= 0) ? 0 : m_vislotPrevChunkMap[islotTmp]), + islotMod, nInc); +} + +/*---------------------------------------------------------------------------------------------- + An insertion or deletion has occurred at the given location in the following stream + (specifically of an line-break glyph). Adjust the next-chunk-map accordingly so + the indices still match. +----------------------------------------------------------------------------------------------*/ +void GrSlotStream::AdjustNextChunkMap(int islotMin, int islotInsOrDel, int nInc) +{ + for (int islot = islotMin; islot < m_islotWritePos; islot++) + { + if (m_vislotNextChunkMap[islot] != -1 && m_vislotNextChunkMap[islot] >= islotInsOrDel) + m_vislotNextChunkMap[islot] += nInc; + } +} + +/*---------------------------------------------------------------------------------------------- + Unwind the read position of the stream, so that the following pass will begin + reading from the new position. The recipient is functioning as the input stream. +----------------------------------------------------------------------------------------------*/ +void GrSlotStream::UnwindInput(int islotNewPos, bool fPreBidiPass) +{ + Assert(m_islotReprocPos == -1); + Assert(islotNewPos <= m_islotReadPos); + + int islot; + for (islot = islotNewPos; islot < m_islotReadPos; ++islot) + m_vislotNextChunkMap[islot] = -1; + + m_islotReadPos = islotNewPos; + m_islotReadPosMax = m_islotReadPos; + + Assert(m_islotReadPos <= m_islotWritePos); + if (fPreBidiPass) + { + for (islot = m_islotReadPos; islot < m_islotWritePos; ++islot) + SlotAt(islot)->ZapDirLevel(); + } +} + +/*---------------------------------------------------------------------------------------------- + Unwind the write position of the stream, so that the pass will begin writing + at the new position. The recipient is functioning as the output stream +----------------------------------------------------------------------------------------------*/ +void GrSlotStream::UnwindOutput(int islotNewPos, bool fOutputOfPosPass) +{ + Assert(islotNewPos <= m_islotWritePos); + + int islot; + for (islot = islotNewPos; islot < m_islotWritePos; ++islot) + { + m_vislotPrevChunkMap[islot] = -1; + + if (!fOutputOfPosPass && m_fUsedByPosPass) + // This stream is the input to the first positioning pass, ie, the output + // of the last sub pass or the bidi pass. Zap the stream indices, + // since unwinding this pass could cause them to be invalid due to a later + // reordering. + SlotAt(islot)->ZapPosPassIndex(); + } + + m_islotWritePos = islotNewPos; + + m_fFullyWritten = false; + + if (m_islotSegMin > m_islotWritePos) + m_islotSegMin = -1; + if (m_islotSegLim > m_islotWritePos) + m_islotSegLim = -1; +} + +/*---------------------------------------------------------------------------------------------- + We've just created a new chunk, with the recipient as the output stream. The beginning + of the chunk is passed as 'islotOutput' and the end of the chunk is assumed to be the + write-position of the recipient. Store the mappings into the corresponding + input position. + + @param islotInputMin - beginning of chunk in input stream + @param islotOutputMin - beginning of chunk in output stream (recipient) + @param islotInputLim - end of chunk in input stream (currently not used) + @param fSkipChunkStart - if true, either we have reprocessed part of a + previously created chunk, + or there was no input consumed; + in either case, don't record the beginning of the chunk, + just make this chunk be part of the previous + @param fBackingUp - this chunk results in the stream position moving backwards, + so clear anything we're backing over +----------------------------------------------------------------------------------------------*/ +void GrSlotStream::MapInputChunk(int islotInputMin, int islotOutputMin, int islotInputLim, + bool fSkipChunkStart, bool fBackingUp) +{ + Assert(AssertValid()); + Assert(islotOutputMin >= -1); + + if (!fSkipChunkStart) + { + Assert(islotOutputMin >= 0); + if (islotOutputMin >= 0) // just in case an invalid value was passed + m_vislotPrevChunkMap[islotOutputMin] = islotInputMin; + } + + int islot; + for (islot = max(0, islotOutputMin + 1); islot < m_islotWritePos; ++islot) + m_vislotPrevChunkMap[islot] = -1; // not a chunk boundary + +// if (fBackingUp && m_islotWritePos < m_vislotPrevChunkMap.Size()) +// m_vislotPrevChunkMap[m_islotWritePos] = -1; + + if (fBackingUp) + for (islot = m_islotWritePos; islot < signed(m_vislotPrevChunkMap.size()); islot++) + m_vislotPrevChunkMap[islot] = -1; + +// m_vislotPrevChunkMap[m_islotWritePos] = islotInputLim; +} + +/*---------------------------------------------------------------------------------------------- + Return the length of the final chunk, where the recipient is the input stream. +----------------------------------------------------------------------------------------------*/ +int GrSlotStream::LastNextChunkLength() +{ + int cslotRet = 1; + for (int islot = m_islotReadPos; islot-- > 0; ) + { + if (m_vislotNextChunkMap[islot] != -1) + return cslotRet; + cslotRet++; + } + return cslotRet + 1; +} + +/*---------------------------------------------------------------------------------------------- + We've just created a new chunk, with the recipient as the input stream. The beginning of + the chunk is passed as 'islotInputMin' and the end of the chunk is assumed to be the + read-position of the recipient. Store the mappings into the corresponding + output position. + + @param islotOutputMin - beginning of chunk in output stream + @param islotInputMin - beginning of chunk in input stream (recipient) + @param islotOutputLim - end of chunk in output stream (currently not used) + @param fSkipChunkStart - if true, either we have reprocessed part of a + previously created chunk, + or there was no output generated; + in either case, don't record the beginning of the chunk, + just make this chunk be part of the previous + @param fBackingUp - this chunk results in the stream position moving backwards, + so clear anything we're backing over +----------------------------------------------------------------------------------------------*/ +void GrSlotStream::MapOutputChunk(int islotOutputMin, int islotInputMin, int islotOutputLim, + bool fSkipChunkStart, int cslotReprocess, bool fBackingUp) +{ + Assert(AssertValid()); + + // Note: islotInputMin can be less than -1 if there is a large reprocess buffer near the + // beginning of the input stream. In which case, fSkipChunk start should be true and + // cslotReprocess large enough to keep any of the stuff below from having any problems. + //Assert(islotInputMin >= -1); + + if (!fSkipChunkStart) + { + Assert(islotInputMin >= 0); + if (islotInputMin >= 0) // just in case an invalid value was passed + m_vislotNextChunkMap[islotInputMin] = islotOutputMin; + } + + int islot; + for (islot = max(0, islotInputMin + 1 + cslotReprocess); islot < m_islotReadPos; ++islot) + m_vislotNextChunkMap[islot] = -1; // not a chunk boundary + +// if (fBackingUp && m_islotReadPos < m_vislotNextChunkMap.Size()) +// m_vislotNextChunkMap[m_islotReadPos] = -1; + + if (fBackingUp) + for (islot = m_islotReadPos; islot < signed(m_vislotNextChunkMap.size()); islot++) + m_vislotNextChunkMap[islot] = -1; + +// m_vislotNextChunkMap[m_islotReadPos] = islotOutputLim; +} + +/*---------------------------------------------------------------------------------------------- + Ensure that the chunk maps for a pair of streams match properly. The recipient is + the input stream. +----------------------------------------------------------------------------------------------*/ +void GrSlotStream::AssertChunkMapsValid(GrSlotStream * psstrmOut) +{ +#ifdef _DEBUG + GrSlotStream * psstrmIn = this; + + Assert(m_ipass == 0 || (m_ipass + 1 == psstrmOut->m_ipass)); + + int islotMapNext; + int islotMapPrev; + int islot; + for (islot = 0; islot < m_islotReadPos; ++islot) + { + islotMapNext = psstrmIn->ChunkInNext(islot); + Assert(islotMapNext != -2); + if (islotMapNext != -1) + { + islotMapPrev = psstrmOut->ChunkInPrev(islotMapNext); + if (islotMapPrev == -2) + { + Assert(islot == psstrmOut->WritePos()); + } + else + { + Assert(islot == islotMapPrev); + } + } + } + + for (islot = 0; islot < psstrmOut->m_islotWritePos; ++islot) + { + islotMapPrev = psstrmOut->ChunkInPrev(islot); + Assert(islotMapPrev != -2); + if (islotMapPrev != -1) + { + islotMapNext = psstrmIn->ChunkInNext(islotMapPrev); + if (islotMapNext == -2) + { + Assert(islot == psstrmIn->ReadPos()); + } + else + { + Assert(islot == islotMapNext); + } + } + } +#endif // _DEBUG +} + +/*---------------------------------------------------------------------------------------------- + Ensure that corresponding items in the streams of a positioning pass have matching + stream indices. The recipient is the output stream. +----------------------------------------------------------------------------------------------*/ +void GrSlotStream::AssertStreamIndicesValid(GrSlotStream * psstrmIn) +{ +#ifdef _DEBUG + if (!GotIndexOffset()) + return; + + Assert(m_fUsedByPosPass); + + for (int islot = 0; islot < WritePos(); islot++) + { + GrSlotState * pslotOut = SlotAt(islot); + GrSlotState * pslotIn = + psstrmIn->SlotAt(islot + psstrmIn->IndexOffset() - IndexOffset()); + Assert(pslotOut->PosPassIndex() == pslotIn->PosPassIndex()); + Assert(pslotOut->HasAsPreviousState(pslotIn)); + } +#endif // _DEBUG +} + +/*---------------------------------------------------------------------------------------------- + Ensure that the roots of all attachments made in this chunk are present + in the output stream. (Currently the compiler ensures this by making it an error + to write rules that don't do this.) +----------------------------------------------------------------------------------------------*/ +void GrSlotStream::AssertAttachmentsInOutput(int islotMin, int islotLim) +{ +#ifdef _DEBUG + for (int islot = islotMin; islot < islotLim; islot++) + { + GrSlotState * pslotOut = SlotAt(islot); + int dislot = pslotOut->AttachTo(); + Assert(islotMin <= islot + dislot); + Assert(islot + dislot < islotLim); + } +#endif // _DEBUG +} + +/*---------------------------------------------------------------------------------------------- + Answer the slot index corresponding to the start of the chunk (as mapped to the following + stream). + ENHANCE: make more robust. +----------------------------------------------------------------------------------------------*/ +int GrSlotStream::ChunkInNextMin(int islot) +{ + int islotRet = islot; + while (m_vislotNextChunkMap[islotRet] == -1 && islotRet > 0) + --islotRet; + return islotRet; +} + +/*---------------------------------------------------------------------------------------------- + Answer the slot index corresponding to the end of the chunk (as mapped to the following + stream). + ENHANCE: make more robust. +----------------------------------------------------------------------------------------------*/ +int GrSlotStream::ChunkInNextLim(int islot) +{ + int islotRet = islot + 1; + if (islotRet == m_islotReadPos) + return islot; + while (m_vislotNextChunkMap[islotRet] == -1 && islotRet < m_islotReadPos) + { + ++islotRet; + Assert(islotRet < signed(m_vislotNextChunkMap.size())); + } + return islotRet; +} + +/*---------------------------------------------------------------------------------------------- + Skip over the given number slots. This is used to resync the streams when we + are restarting a new segment. Specifically the output stream informs the + input stream of the number of slots to skip to get the boundary for a chunk that + the output finds interesting. +----------------------------------------------------------------------------------------------*/ +void GrSlotStream::ResyncSkip(int cslot) +{ + Assert(AssertValid()); + m_islotReadPos += cslot; + m_cslotSkippedForResync = cslot; + // Should never skip past the beginning of the line. + Assert(m_islotSegMin == -1 || m_islotReadPos <= m_islotSegMin); +} + +/*---------------------------------------------------------------------------------------------- + Record the number of slots in the stream that are previous to the official start of the + segment. +----------------------------------------------------------------------------------------------*/ +void GrSlotStream::CalcIndexOffset(GrTableManager * ptman) +{ + if (GotIndexOffset()) + return; // already figured it + + if (m_islotSegMin > -1) + { + m_cslotPreSeg = m_islotSegMin; + if (m_fUsedByPosPass) + { + for (int islotPre = 0; islotPre < WritePos(); islotPre++) + { + SlotAt(islotPre)->SetPosPassIndex(islotPre - m_cslotPreSeg, + m_fInputToPosPass1); + } + } + } + // else can't figure it out yet + + // Old code: +#if 0 + gid16 chwLB = ptman->LBGlyphID(); + + for (int islot = 0; islot < WritePos(); islot++) + { + if (SlotAt(islot)->IsLineBreak(chwLB)) + { + if (SlotAt(islot)->IsInitialLineBreak(chwLB)) + { + m_cslotPreSeg = islot; + } + else + { + // Otherwise, we hit the final line break, which is kind of strange, because + // in this case there is no initial line break, and we should have set + // m_cslotPreSeg to 0 immediately when it was initialized. + Assert(false); + m_cslotPreSeg = 0; + } + + if (m_fUsedByPosPass) + { + for (int islotPre = 0; islotPre < WritePos(); islotPre++) + { + SlotAt(islotPre)->SetPosPassIndex(islotPre - m_cslotPreSeg, + m_fInputToPosPass1); + } + } + return; + } + + } + // No line breaks yet, so no way to tell. +#endif +} + +/*---------------------------------------------------------------------------------------------- + Set the positions in the streams for the next rule. The recipient is the output stream. + If we are moving backwards, copy part of the output stream back to (a temporary buffer + in) the input stream, so that we can reprocess it. + + @param cslotArg - how far to skip forward or back; most commonly zero +----------------------------------------------------------------------------------------------*/ +void GrSlotStream::SetPosForNextRule(int cslotArg, GrSlotStream * psstrmIn, + bool fOutputOfPosPass) +{ + Assert(AssertValid()); + + int cslot = cslotArg; + // Can't reprocess what has already been read by the next pass. MaxBackup should allow + // the flexibility needed, but if not, we fix it here. + if (m_islotWritePos + cslot < m_islotReadPos) + cslot = m_islotReadPos - m_islotWritePos; + + Assert(psstrmIn->m_ipass == m_ipass-1); + + if (cslot >= 0) + { + // Skipping forward. + Assert(cslot <= psstrmIn->SlotsPendingInContext()); + for (int islot = 0; islot < cslot; islot++) + CopyOneSlotFrom(psstrmIn); + } + else + { + // Reprocessing. + + // Save corresponding positions before doing the back-up. + int islotReadPosInSave = psstrmIn->ReadPosForNextGet(); + int islotWritePosOutSave = this->WritePos(); + + std::vector<GrSlotState*> vpslotTmp; + int islot; + if (psstrmIn->SlotsToReprocess() > 0) + { + // Stick any slots still to reprocess in a temporary buffer. + for (islot = psstrmIn->m_islotReprocPos; + islot < signed(psstrmIn->m_vpslotReproc.size()); + islot++) + { + vpslotTmp.push_back(psstrmIn->m_vpslotReproc[islot]); + } + } + + psstrmIn->ClearReprocBuffer(); + psstrmIn->m_islotReprocLim = psstrmIn->m_islotReadPos; + + for (islot = cslot; islot < 0; islot++) + psstrmIn->m_vpslotReproc.push_back(m_vpslot[m_islotWritePos+islot]); + for (islot = 0; islot < signed(vpslotTmp.size()); islot++) + psstrmIn->m_vpslotReproc.push_back(vpslotTmp[islot]); + psstrmIn->m_islotReprocPos = 0; + + if (!fOutputOfPosPass && m_fUsedByPosPass) + { + // Last substitution pass; zap the stream indices, since they may + // now be invalid. + for (islot = 0; islot < signed(psstrmIn->m_vpslotReproc.size()); islot++) + psstrmIn->m_vpslotReproc[islot]->ZapPosPassIndex(); + } + + // If either the min or lim is in the middle of the reprocess buffer, + // adjust as necessary to match where it is in the output stream. + int islotSegMinIn = psstrmIn->SegMin(); + if (islotSegMinIn > -1 && + psstrmIn->ReadPosForNextGet() <= islotSegMinIn && + islotSegMinIn < islotReadPosInSave) + { + Assert(this->SegMin() > -1); + int dislotIn = islotReadPosInSave - islotSegMinIn; + int dislotOut = islotWritePosOutSave - SegMin(); + psstrmIn->SetSegMin(islotSegMinIn + dislotIn - dislotOut, true); + if (psstrmIn->m_cslotPreSeg > psstrmIn->SegMin()) + psstrmIn->m_cslotPreSeg = -1; + } + int islotSegLimIn = psstrmIn->SegLimIfKnown(); + if (islotSegLimIn > -1 && + psstrmIn->ReadPosForNextGet() <= islotSegLimIn && + islotSegLimIn < islotReadPosInSave) + { + int islotSegLimOut = this->SegLimIfKnown(); + Assert(islotSegLimOut > -1); + int dislotIn = islotReadPosInSave - islotSegLimIn; + int dislotOut = islotWritePosOutSave - islotSegLimOut; + psstrmIn->SetSegLim(islotSegLimIn + dislotIn - dislotOut); + } + + m_islotWritePos += cslot; + + if (m_islotSegMin >= m_islotWritePos) + m_islotSegMin = -1; + if (m_islotSegLim >= m_islotWritePos) + m_islotSegLim = -1; + } +} + +/*---------------------------------------------------------------------------------------------- + If a rule was run over a LB glyph, set the appropriate flag in the table manager. + CURRENTLY NOT USED +----------------------------------------------------------------------------------------------*/ +void GrSlotStream::SetLBContextFlag(GrTableManager * ptman, int islotStart) +{ + gid16 chwLB = ptman->LBGlyphID(); + for (int islot = islotStart; islot < WritePos(); islot++) + { + GrSlotState * pslot = SlotAt(islot); + if (pslot->IsInitialLineBreak(chwLB)) + ptman->State()->SetStartLineContext(true); + if (pslot->IsFinalLineBreak(chwLB)) + ptman->State()->SetEndLineContext(true); + } +} + +/*---------------------------------------------------------------------------------------------- + Return the number of slots that have been output by the current pass but must be + reprocessed by this same pass. The recipient is serving as the input stream. +----------------------------------------------------------------------------------------------*/ +int GrSlotStream::SlotsToReprocess() +{ + Assert(AssertValid()); + + if (m_islotReprocPos == -1) + return 0; + return (m_vpslotReproc.size() - m_islotReprocPos); +} + +/*---------------------------------------------------------------------------------------------- + Get the input at the given slot, that is, the original input, ignoring the issue of + reprocessing. +----------------------------------------------------------------------------------------------*/ +GrSlotState * GrSlotStream::GetSlotAt(int islot) +{ + return m_vpslot[islot]; +} + +/*---------------------------------------------------------------------------------------------- + Return the "current" input item from the rule's perspective, ie, the last slot read. + So dislotOffset = 0 means not the slot at the read position but one slot earlier. + + If dislotOffset is less the the original read position that was in effect at the + beginning of the rule, we need to read from the output stream, which has the + up-to-date values. See the class comment for more details. + + @param dislotOffset - offset from current stream position + @param fNullOkay - true if it's okay to return NULL in the situation where we're asking + for something before the beginning of the stream +----------------------------------------------------------------------------------------------*/ + +GrSlotState * GrSlotStream::RuleInputSlot(int dislotOffset, GrSlotStream * psstrmOutput, + bool fNullOkay) +{ + Assert(m_islotRuleStartRead <= m_islotReadPos); + + if (dislotOffset > 0) + return Peek(dislotOffset - 1); + + int cslotOffsetBack = dislotOffset * -1; + + if (m_islotReprocLim > -1) + { + // There is a reprocess buffer. + + // Position when starting the rule should never be earlier than the + // beginning of the reprocess buffer. + Assert(m_islotRuleStartRead >= m_islotReprocLim - signed(m_vpslotReproc.size())); + + // number of items following the reprocess buffer: + int cslotPostReproc = m_islotReadPos - m_islotReprocLim; + + if (cslotOffsetBack >= cslotPostReproc) + { + // number of items in the reprocess buffer that were valid when the rule + // was started: + int cslotValidReproc = m_islotReprocLim - m_islotRuleStartRead; + + if (cslotOffsetBack >= cslotPostReproc + cslotValidReproc) + { + // Read from the output stream. + int dislotTmp = dislotOffset - 1 + cslotPostReproc + cslotValidReproc; + Assert(dislotTmp < 0); + return psstrmOutput->PeekBack(dislotTmp); + } + else + { + if (m_islotReprocPos > -1) + { + // Current read pos is inside reprocess buffer. + Assert(cslotPostReproc == 0); + int islotStartReadReprocBuf = m_vpslotReproc.size() - cslotValidReproc; + Assert(islotStartReadReprocBuf >= 0); + int islotInReprocBuf = m_islotReprocPos - cslotOffsetBack - 1; + if (islotInReprocBuf < islotStartReadReprocBuf) + { + // Return a slot from before the reprocess buffer, from the + // output stream (which is serving as our input stream for precontext + // items). + return psstrmOutput->PeekBack(islotInReprocBuf - islotStartReadReprocBuf); + } + else if (islotInReprocBuf < 0) + { + Assert(false); + //int islotReprocMin = m_islotReprocLim - m_vpslotReproc.Size(); + //return m_vpslot[islotReprocMin + islotInReprocBuf]; + } + else + // Read from the reprocess buffer. + return m_vpslotReproc[islotInReprocBuf]; + } + else + // Looking backwards into the reprocess buffer. + return m_vpslotReproc[ + m_vpslotReproc.size() - cslotOffsetBack + cslotPostReproc - 1]; + } + } + } + + // There is no reprocess buffer, or it is not a factor. + + if (m_islotReadPos + dislotOffset - 1 < m_islotRuleStartRead) + { + // Read from the output stream. + return psstrmOutput->PeekBack(m_islotReadPos + dislotOffset - 1 - m_islotRuleStartRead, + fNullOkay); + } + else + { + // Read normally from the input stream. + return m_vpslot[m_islotReadPos - cslotOffsetBack - 1]; + } +} + +/*---------------------------------------------------------------------------------------------- + Return the "current" output item from the rule's perspective, ie, the last slot written. + So dislotOffset = 0 means not the slot at the write position but one slot earlier. +----------------------------------------------------------------------------------------------*/ + +GrSlotState * GrSlotStream::RuleOutputSlot(int dislotOffset) +{ + return m_vpslot[m_islotWritePos - 1 + dislotOffset]; +} + +/*---------------------------------------------------------------------------------------------- + For any slot in the final output that has no associations, set the "before" pointer to + the following slot and the "after" pointer to the preceding slot. The idea of this is + that it will make it impossible to select a the glyph separately. + + Note that if the slot is the first on the line, it will not be associated with + a slot in the previous segment. + + @param chwLB - the glyph IB for the line-break glyphs +----------------------------------------------------------------------------------------------*/ +void GrSlotStream::SetNeutralAssociations(gid16 chwLB) +{ + for (int islot = 0; islot < m_islotWritePos; ++islot) + { + GrSlotState * pslot = SlotAt(islot); + if (pslot->PassModified() > 0 && + (pslot->BeforeAssoc() == kPosInfinity || pslot->AfterAssoc() == kNegInfinity)) + { + pslot->CleanUpAssocs(); + if (pslot->BeforeAssoc() != kPosInfinity && pslot->AfterAssoc() != kNegInfinity) + continue; + + Assert(pslot->BeforeAssoc() == kPosInfinity && pslot->AfterAssoc() == kNegInfinity); + + GrSlotState * pslotBefore = FindAssociatedSlot(islot, 1, chwLB); + GrSlotState * pslotAfter = FindAssociatedSlot(islot, -1, chwLB); + + if (pslotBefore && pslotAfter) + pslot->Associate(pslotBefore, pslotAfter); + else if (pslotBefore) + pslot->Associate(pslotBefore); + else if (pslotAfter) + pslot->Associate(pslotAfter); + else + // Weird, but can happen with an empty segment. + Warn("No assocations"); + +// Assert(pslot->m_vpslotAssoc.Size() > 0); + pslot->m_fNeutralAssocs = true; + } + } +} + +/*---------------------------------------------------------------------------------------------- + Find the next or previous slot in the final output that has explicit associations, or + return NULL if none. + + @param islot - starting slot + @param nInc - +1 if we want to find the next slot, -1 if we want the previous slot + @param chwLB - the glyph IB for the line-break glyphs +----------------------------------------------------------------------------------------------*/ +GrSlotState * GrSlotStream::FindAssociatedSlot(int islot, int nInc, gid16 chwLB) +{ + int islotNext = islot + nInc; + + while (islotNext >= 0 && islotNext < m_islotWritePos) + { + GrSlotState * pslotRet = SlotAt(islotNext); + if (pslotRet->IsLineBreak(chwLB)) + { } + else if (pslotRet->PassModified() == 0 || + (pslotRet->m_vpslotAssoc.size() > 0 && pslotRet->m_fNeutralAssocs == false)) + { + return pslotRet; + } + islotNext += nInc; + } + return NULL; +} + +} // namespace gr diff --git a/Build/source/libs/graphite-engine/src/segment/GrSlotStream.h b/Build/source/libs/graphite-engine/src/segment/GrSlotStream.h new file mode 100644 index 00000000000..fd35d50c0d0 --- /dev/null +++ b/Build/source/libs/graphite-engine/src/segment/GrSlotStream.h @@ -0,0 +1,576 @@ +/*--------------------------------------------------------------------*//*:Ignore this sentence. +Copyright (C) 1999, 2001 SIL International. All rights reserved. + +Distributable under the terms of either the Common Public License or the +GNU Lesser General Public License, as specified in the LICENSING.txt file. + +File: GrSlotStream.h +Responsibility: Sharon Correll +Last reviewed: Not yet. + +Description: + Classes GrSlotStream and GrSlotState. +----------------------------------------------------------------------------------------------*/ +#ifdef _MSC_VER +#pragma once +#endif +#ifndef SLOTSTREAM_INCLUDED +#define SLOTSTREAM_INCLUDED + +//:End Ignore + +namespace gr +{ + +class EngineState; +class Font; + +/*---------------------------------------------------------------------------------------------- + Each GrSlotStream represents the output of one pass, which also serves as the input + of the following pass. Hence it has two positions, a write position for when it is + serving as an output stream, and a read position for when it is serving as an input + stream. The TableManager holds a list of the streams. + + The reading of slot streams is complicated by two factors. First, it may be necessary + to access the "pre-context" items of a rule, which are the slots before the current + stream position at the point when the rule is matched. These items will not be modified + by the rule, but may have been modified by a previous rule in the same pass, Therefore + we need to read these slots from the output stream, rather than the input stream, + because the output stream has the current slot attribute values that we want to work with. + In order make this happen, we keep track of the read and write positions of + the streams at the time the current rule was matched (m_islotRuleStartRead and + m_islotRuleStartWrite) and whenever we try to read a slot before m_islotRuleStartRead, + we read the corresponding slot from the output stream instead. + + Second, a rule may explicitly set the advance position so that some items are + actually reprocessed (and potentially modified) by the same pass. When this happens, + we actually copy the slots to reprocess from the output stream back + into a temporary buffer (m_vpslotReproc) in the input stream, and set up the stream so + that the next read operations will read from the buffer. + + It gets tricky because these two mechanisms can overlap with each other, so care + must be taken to read from the right place: the input stream in the normal way, + the reprocess buffer, or the output stream. See the RuleInputSlot method. + + Another messy bit of code has to do with maintaining chunks. These are used for + backtracking and unwinding after inserting a line-break. Adjacent streams have maps + showing how many slots were processed by a single rule; the maps must be consisent + between adjacent rules. The following is an example of a pair of valid chunk maps: + + Input stream Output stream + Pos next-map prev-map + 0 0 0 + 1 -1 -1 + 2 2 2 + 3 4 -1 + 4 -1 3 + 5 -1 -1 + 6 7 -1 + 7 -1 6 + 8 -1 -1 + + A -1 means that slot is not at the beginning of a chunk. Otherwise, there must be a + corresponding value in the parallel map of the adjacent stream--eg, the input stream's + next-map(3) == 4 and the output stream's prev-map(4) == 3. Assertions are run + regularly to ensure that the chunk maps stay valid. + + So above, the first chunk corresponds to slots 0-1 in the input and 0-1 in the output; + two slots were processed by the rule. The second chunk corresponds to slot 2 + in the input and slots 2-3 in the output (a slot was inserted). + The third corresponds to slots 3-5 in the input and 4-6 in the output (three slots + processed). The fourth chunk covers slots 6-8 in the input and 7-8 in the output + (one slot was deleted). + + ENHANCE SharonC: explain the effect of skipped slots on the chunk maps. + + Hungarian: sstrm +----------------------------------------------------------------------------------------------*/ +class GrSlotStream { + + friend class GrSlotState; + +public: + // Constructor: + GrSlotStream(int ipass) + { + m_ipass = ipass; + m_vpslot.clear(); + m_vislotPrevChunkMap.clear(); + m_vislotNextChunkMap.clear(); + m_vpslotReproc.clear(); + } + + // Destructor: + ~GrSlotStream() + { + ReleaseSlots(0, m_vpslot.size()); + } + + void ReleaseSlots(int islotMin, int islotLim) + { + // A slot stream is responsible for deleting the slot states that it created, + // that is, the ones whose modified tag equals this stream's pass index. + // NO LONGER TRUE - there is a master list of them managed by the GrTableManager. +// for (int islot = islotMin; islot < islotLim; ++islot) +// { +// if (m_vpslot[islot]->m_ipassModified == m_ipass) +// delete m_vpslot[islot]; +// } + } + + // Getters: + int WritePos() { return m_islotWritePos; } + int ReadPos() { return m_islotReadPos; } + bool FullyWritten() { return m_fFullyWritten; } + + int SlotsPresent() { return m_vpslot.size(); } + + // Setters: + void SetWritePos(int islot) + { + m_islotWritePos = islot; + } + void SetReadPos(int islot) + { + m_islotReadPos = islot; + } + + void Initialize(int ipassPos1, bool fAnythingPrevious) + { + m_islotWritePos = 0; + m_islotReadPos = 0; + m_islotReprocPos = -1; + m_islotReprocLim = -1; + m_islotRuleStartRead = 0; + m_islotRuleStartWrite = 0; + m_islotReadPosMax = 0; + m_cslotSkippedForResync = 0; + m_cslotPreSeg = (fAnythingPrevious) ? -1 : 0; + m_fFullyWritten = false; + m_islotSegMin = -1; + m_islotSegLim = -1; + m_fUsedByPosPass = (m_ipass + 1 >= ipassPos1); + m_fInputToPosPass1 = (m_ipass + 1 == ipassPos1); + m_vpslot.clear(); // not responsible for destroying the slots + m_vislotPrevChunkMap.clear(); + m_vislotNextChunkMap.clear(); + } + +public: + bool AtEnd(); + bool AtEndOfContext(); + void NextPut(GrSlotState* pslot); + GrSlotState * NextGet(); + GrSlotState * Peek(int dislot = 0); + GrSlotState * PeekBack(int dislot, bool fNullOkay = false); + void Skip(int = 1); + GrSlotState * SlotAt(int islot) + { + return m_vpslot[islot]; + } + + int OutputOfPass() + { + return m_ipass; + } + + // This function is intended to be used within positioning passes where there is a + // one-to-one correspondence beween the slots in consecutive streams. + GrSlotState * OutputSlotAt(int islot) + { + return Peek(islot - ReadPosForNextGet()); + } + + int ReadPosForNextGet() + { + return ReadPos() - SlotsToReprocess(); + } + + // Only valid for streams that are the input or output to a positioning pass. + GrSlotState * SlotAtPosPassIndex(int islot) + { + Assert(GotIndexOffset()); + Assert(m_fUsedByPosPass); + return m_vpslot[islot + m_cslotPreSeg]; + } + + // Only valid for streams that are the input or output to a positioning pass. + bool HasSlotAtPosPassIndex(int islot) + { + Assert(GotIndexOffset()); + Assert(m_fUsedByPosPass); + return (signed(m_vpslot.size()) > (islot + m_cslotPreSeg)); + } + + bool AssertValid(); + void AssertChunkMapsValid(GrSlotStream * psstrmOut); + void AssertStreamIndicesValid(GrSlotStream * psstrmIn); + void AssertAttachmentsInOutput(int islotMin, int islotLim); + bool NoReproc() + { + return (m_islotReprocPos == -1 || (m_islotReprocPos >= signed(m_vpslotReproc.size()))); + } + + int SlotsPending(); + int SlotsPendingInContext(); + int TotalSlotsPending(); + bool PastEndOfPositioning(bool fOutput); + + void ClearReprocBuffer() + { + m_islotReprocPos = -1; + m_islotReprocLim = -1; + m_vpslotReproc.clear(); + } + + int ReprocLim() + { + return m_islotReprocLim; + } + + int ReprocMin() + { + Assert(m_islotReprocPos > -1); + return m_islotReprocLim - m_vpslot.size(); + } + + int RuleStartReadPos() + { + return m_islotRuleStartRead; + } + + void SetRuleStartReadPos() + { + if (m_islotReprocPos > -1) + m_islotRuleStartRead = m_islotReprocLim - SlotsToReprocess(); + else + m_islotRuleStartRead = m_islotReadPos; + } + + int RuleStartWritePos() + { + return m_islotRuleStartWrite; + } + + void SetRuleStartWritePos() + { + m_islotRuleStartWrite = m_islotWritePos; + } + + void MarkFullyWritten(); + + void SetSegMin(int islot, bool fAdjusting = false) + { + Assert(fAdjusting || m_islotSegMin == -1 || m_islotSegMin == islot); + m_islotSegMin = islot; + } + void SetSegMinToWritePos(bool fMod = true) + { + if (m_islotSegMin == -1) + m_islotSegMin = m_islotWritePos; + else + Assert(m_islotSegMin <= m_islotWritePos); // eg, already set to before the initial LB + } + int SegMin() + { + return m_islotSegMin; + } + void SetSegLim(int islot) + { + m_islotSegLim = islot; + } + void SetSegLimToWritePos(bool fMod = true) + { + if (m_islotSegLim > -1 && !fMod) + { + Assert(m_islotSegLim <= m_islotWritePos); + return; + } + m_islotSegLim = m_islotWritePos; + } + int SegLimIfKnown() + { + return m_islotSegLim; + } + int FinalSegLim() + { + if (m_islotSegLim > -1) + return m_islotSegLim; + else + return m_islotWritePos; + } + + // Return true if we are exactly at the segment min. + bool AtSegMin() + { + if (m_islotSegMin == -1) + return false; + return (m_islotSegMin == ReadPosForNextGet()); + } + + // Return true if we are exactly at the segment lim. + bool AtSegLim() + { + if (m_islotSegLim == -1) + return false; + return (m_islotSegLim == ReadPosForNextGet()); + } + + int ReadPosMax() + { + return m_islotReadPosMax; + } + void SetReadPosMax(int islot) + { + m_islotReadPosMax = std::max(m_islotReadPosMax, islot); + } + + int OldDirLevelRange(EngineState * pengst, int islotStart, int nTopDirection); + int DirLevelRange(EngineState * pengst, int islotStart, int nTopDirection, + std::vector<int> & vislotStarts, std::vector<int> & vislotStops); + int GetSlotDirLevel(EngineState * pengst, int islot, + int nOuterLevel, int nCurrLevel, DirCode dircMarker, DirCode dircOverride); + DirCode AdjacentStrongCode(EngineState * pengst, int islot, int nInc, + DirCode dircPDF, bool fNumbersAreStrong = false); + DirCode TerminatorSequence(EngineState * pengst, int islot, int nInc, DirCode dircPDF); + DirCode AdjacentNonBndNeutralCode(EngineState * pengst, int islot, int nInc, + DirCode dircPDF); + + void CopyOneSlotFrom(GrSlotStream * psstrmPrev); + void SimpleCopyFrom(GrSlotStream * psstrmI, int islotInput, int islotOutput); + + bool MoreSpace(GrTableManager * ptman, + float xsSpaceAllotted, bool fWidthIsCharCount, + bool fIgnoreTrailingWS, TrWsHandling twsh, + float * pxsWidth); + + int InsertLineBreak(GrTableManager * ptman, + int islotPrevBreak, bool fInsertedLB, int islotStartTry, + LineBrk lb, TrWsHandling twsh, int islotMin, LineBrk * plbNextToTry); + int MakeSegmentBreak(GrTableManager * ptman, + int islotPrevBreak, bool fInsertedLB, int islotStartTry, + LineBrk lb, TrWsHandling twsh, int islotMin, LineBrk * plbNextToTry); + bool FindSegmentEnd(GrTableManager * ptman, + int islotStartTry, LineBrk lb, TrWsHandling twsh, int islotMin, + int * pislot, int * pichwSegOffset, + LineBrk * plbFound, LineBrk * plbNextToTry); + bool HasEarlierBetterBreak(int islotBreak, LineBrk lbFound, gid16 chwLB); + LineBrk BreakWeightAt(gid16 chwLB, int islot); + void AppendLineBreak(GrTableManager *, GrCharStream * pchstrm, + LineBrk, DirCode dirc, int islot, bool fInitial, int ichwSegOffset); + int FindFinalLineBreak(gid16 chwLB, int islotMin, int islotLim); + void AdjustPrevStreamNextChunkMap(GrTableManager * ptman, int islotMod, int nInc); + void AdjustNextChunkMap(int islotMin, int islotMod, int nInc); + + void EnsureLocalCopy(GrTableManager * ptman, GrSlotState * pslot, GrSlotStream * psstrmIn); + void ReplaceSlotInReprocessBuffer(GrSlotState * pslotOld, GrSlotState * pslotNew); + + void ZapCalculatedDirLevels(int islotLB); + + int MaxClusterSlot(int islotChunkLim) + { + return MaxClusterSlot(islotChunkLim - 1, islotChunkLim); + } + int MaxClusterSlot(int islotChunkMin, int islotChunkLim); + + void UnwindInput( int islot, bool fPreBidiPass); + void UnwindOutput(int islot, bool fOutputOfPosPass); + + void MapInputChunk( int islotIn, int islotOut, int islotInLim, + bool fReprocessing, bool fBackingUp); + void MapOutputChunk(int islotOut, int islotIn, int islotOutLim, + bool fReprocessing, int cslotReprocess, bool fBackingUp); + + int LastNextChunkLength(); + + int ChunkInPrev(int i) + { + Assert(i >= 0); + Assert(i < signed(m_vislotPrevChunkMap.size())); + Assert(i < m_islotWritePos); + return m_vislotPrevChunkMap[i]; + } + int ChunkInNext(int i) + { + Assert(i >= 0); + Assert(i < signed(m_vislotNextChunkMap.size())); + Assert(i < m_islotReadPos); + return m_vislotNextChunkMap[i]; + } + + int ChunkInNextMin(int islot); + int ChunkInNextLim(int islot); + + void ResyncSkip(int nslot); + int SlotsSkippedToResync() + { + return m_cslotSkippedForResync; + } + void ClearSlotsSkippedToResync() + { + m_cslotSkippedForResync = 0; + } + + int IndexOffset() + { + Assert(GotIndexOffset()); // should have been calculated + return m_cslotPreSeg; + } + void SetIndexOffset(int c) + { + m_cslotPreSeg = c; + } + bool GotIndexOffset() + { + return (m_cslotPreSeg >= 0); + } + void CalcIndexOffset(GrTableManager * ptman); + + void SetPosForNextRule(int nslot, GrSlotStream * psstrmInput, bool fOutputOfPosPass); + void SetLBContextFlag(GrTableManager * ptman, int islotStart); + int SlotsToReprocess(); + + void SetNeutralAssociations(gid16 chwLB); +protected: + GrSlotState * FindAssociatedSlot(int islot, int nInc, gid16 chwLB); + + GrSlotState * GetSlotAt(int islot); + +public: + // Used by the action code: + void BackupReadPos(int cslot = 1) + { + for (int islot = 0; islot < cslot; islot++) + { + if (m_islotReprocLim > -1 && m_islotReprocLim == m_islotReadPos) + { + if (m_islotReprocPos == -1) + m_islotReprocPos = m_vpslotReproc.size() - 1; + else + --m_islotReprocPos; + Assert(m_islotReprocPos >= 0); + } + else + --m_islotReadPos; + } + } + + GrSlotState * RuleInputSlot(int dislot = 0, GrSlotStream * psstrmOut = NULL, + bool fNullOkay = false); + GrSlotState * RuleOutputSlot(int dislot = 0); + + void PutSlotAt(GrSlotState * pslot, int islot) + { + // Currently only used for the final pass, when applying justify.width to + // the advance width. + Assert(signed(m_vpslot.size()) > islot); + Assert(WritePos() > islot); + Assert(ReadPos() <= islot); + Assert(SlotsToReprocess() == 0); + m_vpslot[islot] = pslot; + } + +protected: + // Instance variables: + int m_ipass; // which pass this stream is serving as OUTPUT of + + std::vector<GrSlotState*> m_vpslot; // the slots being processed + + // Chunking information: + std::vector<int> m_vislotPrevChunkMap; // chunk mapping into previous stream; + // -1 if not at a chunk boundary + // (this information is needed for + // reinitialization after inserting a line break) + + std::vector<int> m_vislotNextChunkMap; // chunk mapping into next stream, or -1 + // (this information is needed for backtracking) + + // Since most streams serve as both input and output, there are two positions. + // Note that readPos <= writePos always. + int m_islotWritePos; // where previous pass should put next output + int m_islotReadPos; // where following pass should start processing from + + // When we are reprocessing some of the output from the next stream: + std::vector<GrSlotState*> m_vpslotReproc; // temporary buffer + int m_islotReprocLim; // the read position corresponding to the end of the reprocess + // buffer; -1 if buffer is invalid + int m_islotReprocPos; // index into m_vpslotReproc from where we should read next; + // -1 if we are not reprocessing + + int m_islotRuleStartRead; // read position when the rule was started; any requests before + // this point should access the output stream, because it has + // the most up-to-date values. + int m_islotRuleStartWrite; // write position when the rule started + + bool m_fFullyWritten; // set to true when we can't get any more from the + // previous pass; initially false. + + int m_islotSegMin; // the official min of the segment; -1 if not yet set + int m_islotSegLim; // the official lim of the segment; -1 if not yet set + // (only set for the output of the final line-break pass + // and those beyond) + + int m_cslotSkippedForResync; // number of slots skipped to resync; + // specifically, the number of slots' worth of the + // next-chunk-map that are invalid, not mapped + // to any slots in the following stream + + int m_cslotPreSeg; // number of slots previous to the official beginning of the segment; + // -1 if we haven't figured it out yet. Used for keeping streams + // in the positioning pass in sync; not particularly useful for + // substitution passes, and will possibly be left uncalculated. + + bool m_fUsedByPosPass; // true if this is input to or output from a positioning pass + bool m_fInputToPosPass1; // true if this is the input to the first positioning pass + + // Max value of m_islotReadPos; used for recognizing infinite loops and forcibly + // advancing (see GrPass::CheckInputProgress). + int m_islotReadPosMax; + +public: + // For transduction logging: +#ifdef TRACING +// void LogSlotGlyphs(std::ofstream & strmOut, gid16 chwLB); +#endif // TRACING + + // For test procedures: + int NumberOfSlots() { return m_vpslot.size(); } + GrSlotState * LastSlotWritten() + { + if (m_islotWritePos == 0) + return NULL; + else + return m_vpslot[m_islotWritePos-1]; + } + GrSlotState * LastSlotRead() + { + if (m_islotReadPos == 0) + return NULL; + else + return m_vpslot[m_islotReadPos-1]; + } + + gid16 GlyphIDAt(int islot) + { + return m_vpslot[islot]->m_chwGlyphID; + } + + int PreChunk() // answer the number of prepended items that are not in any chunk; + // should be obsolete now + { + int islot; + for (islot = 0; islot < m_islotReadPos; ++islot) + { + if (m_vislotNextChunkMap[islot] != -1) + return islot; + } + Assert(false); + return islot; + } + +}; // end of class GrSlotStream + +} // namespace gr + + +#endif // !SLOTSTREAM_INCLUDED diff --git a/Build/source/libs/graphite-engine/src/segment/GrTableManager.cpp b/Build/source/libs/graphite-engine/src/segment/GrTableManager.cpp new file mode 100644 index 00000000000..1e5a1aa2846 --- /dev/null +++ b/Build/source/libs/graphite-engine/src/segment/GrTableManager.cpp @@ -0,0 +1,2686 @@ +/*--------------------------------------------------------------------*//*:Ignore this sentence. +Copyright (C) 1999, 2001 SIL International. All rights reserved. + +Distributable under the terms of either the Common Public License or the +GNU Lesser General Public License, as specified in the LICENSING.txt file. + +File: GrTableManager.cpp +Responsibility: Sharon Correll +Last reviewed: Not yet. + +Description: + GrTableManager, which handles the interactions between passes, including the demand-pull + algorithm. +-------------------------------------------------------------------------------*//*:End Ignore*/ + +//:>******************************************************************************************** +//:> Include files +//:>******************************************************************************************** +#include "Main.h" + +#ifdef _MSC_VER +#pragma hdrstop +#endif +#undef THIS_FILE +DEFINE_THIS_FILE +#ifndef _WIN32 +#include <stdlib.h> +#include <math.h> +#endif + +//:>******************************************************************************************** +//:> Forward declarations +//:>******************************************************************************************** + +//:>******************************************************************************************** +//:> Local Constants and static variables +//:>******************************************************************************************** + +namespace gr +{ + +//:>******************************************************************************************** +//:> Methods +//:>******************************************************************************************** + +/*---------------------------------------------------------------------------------------------- + Constructors and initializers +----------------------------------------------------------------------------------------------*/ +EngineState::EngineState() + : m_ipassJustCalled(-1), + m_fStartLineContext(false), + m_fEndLineContext(false), + ////m_pgg(NULL), + m_cslotPreSeg(0), + m_islotblkCurr(-1), + m_islotNext(kSlotBlockSize), + m_fInitialLB(false), + m_fFinalLB(false), + m_fInsertedLB(false), + m_fExceededSpace(false), + m_fHitHardBreak(false), + m_fRemovedWhtsp(false), + m_dxsShrinkPossible(GrSlotState::kNotYetSet), + m_islotPosNext(-1), + m_xsPosXNext(0), + m_ysPosYNext(0), + m_dxsTotWidthSoFar(0), + m_dxsVisWidthSoFar(0), + m_prgzpst(NULL), + m_prgpsstrm(NULL) +{ + m_vslotblk.clear(); + m_vprgnSlotVarLenBufs.clear(); +} + +void EngineState::Initialize(GrEngine * pgreng, GrTableManager * ptman) +{ + m_ptman = ptman; + m_cpass = ptman->NumberOfPasses(); // duplicate for convenience + + m_cUserDefn = pgreng->NumUserDefn(); + m_cFeat = pgreng->NumFeat(); + m_cCompPerLig = pgreng->NumCompPerLig(); + + if (m_prgzpst) + delete[] m_prgzpst; + m_prgzpst = new PassState[m_cpass]; + ptman->StorePassStates(m_prgzpst); +} + +/*---------------------------------------------------------------------------------------------- + Destructors +----------------------------------------------------------------------------------------------*/ +GrTableManager::~GrTableManager() +{ + for (int ipass = 0; ipass < m_cpass; ++ipass) + delete Pass(ipass); + + delete[] m_prgppass; +} + +EngineState::~EngineState() +{ + DestroySlotBlocks(); + + delete[] m_prgzpst; + + if (m_prgpsstrm) + { + for (int isstrm = 0; isstrm < m_cpass; ++isstrm) + delete OutputStream(isstrm); + + delete[] m_prgpsstrm; + } +} + +/*---------------------------------------------------------------------------------------------- + Delete all the slots. +----------------------------------------------------------------------------------------------*/ +void EngineState::DestroySlotBlocks() +{ + Assert(m_vslotblk.size() == m_vprgnSlotVarLenBufs.size()); + for (size_t islotblk = 0; islotblk < m_vslotblk.size(); ++islotblk) + { + delete[] m_vslotblk[islotblk]; + delete[] m_vprgnSlotVarLenBufs[islotblk]; + } + m_vslotblk.clear(); + m_vprgnSlotVarLenBufs.clear(); +} + +/*---------------------------------------------------------------------------------------------- + Create the passes, and them fill them in by reading from the file stream. +----------------------------------------------------------------------------------------------*/ +bool GrTableManager::CreateAndReadPasses(GrIStream & grstrm, + int fxdSilfVersion, int fxdRuleVersion, + int cpassFont, long lSubTableStart, int * rgnPassOffsets, + int ipassSub1Font, int ipassPos1Font, int ipassJust1Font, byte ipassPostBidiFont) +{ + Assert(ipassSub1Font <= ipassJust1Font); + Assert(ipassJust1Font <= ipassPos1Font); + Assert(ipassPos1Font <= cpassFont); + if (ipassSub1Font > ipassJust1Font || ipassJust1Font > ipassPos1Font + || ipassPos1Font > cpassFont) + { + return false; // bad table + } + + // Adjusted indices based on the fact that we have a glyph generation pass that is + // pass 0, and possibly a bidi pass: + m_cpass = cpassFont + 1; + int ipassLB1, ipassSub1, ipassBidi, ipassPos1, ipassJust1; + ipassLB1 = 1; + ipassSub1 = ipassSub1Font + 1; + if (ipassPostBidiFont == 0xFF) + { + // No bidi pass. + m_fBidi = false; + // Add 1 below to account for the glyph-generation pass. + ipassPos1 = ipassPos1Font + 1; + ipassJust1 = ipassJust1Font + 1; + ipassBidi = ipassJust1; + } + else + { + m_fBidi = true; + m_cpass++; + Assert(ipassPostBidiFont == ipassJust1Font); + ipassJust1 = ipassJust1Font + 2; + ipassPos1 = ipassPos1Font + 2; + ipassBidi = ipassJust1 - 1; + } + + // Always make at least one positioning pass, even if there are no rules. + if (ipassPos1 == m_cpass) + { + m_cpass++; + } + + m_prgppass = new GrPass*[m_cpass + 1]; + m_prgppass[0] = new GrGlyphGenPass(0); + + int ipass = 1; + int ipassFont = 0; + m_cpassLB = 0; + m_ipassJust1 = 1; + m_ipassPos1 = 1; + for ( ; ipass < m_cpass; ipass++, ipassFont++) + { + if (ipass < ipassSub1) + { + m_prgppass[ipass] = new GrLineBreakPass(ipass); + m_prgppass[ipass]->ReadFromFont(grstrm, fxdSilfVersion, fxdRuleVersion, + lSubTableStart + rgnPassOffsets[ipassFont]); + m_cpassLB++; + m_ipassJust1++; + m_ipassPos1++; + } + else if (ipassSub1 <= ipass && ipass < ipassBidi) + { + m_prgppass[ipass] = new GrSubPass(ipass); + m_prgppass[ipass]->ReadFromFont(grstrm, fxdSilfVersion, fxdRuleVersion, + lSubTableStart + rgnPassOffsets[ipassFont]); + m_ipassJust1++; + m_ipassPos1++; + } + else if (ipassBidi == ipass && ipass < ipassJust1) + { + m_prgppass[ipass] = new GrBidiPass(ipass); + m_prgppass[ipass]->SetTopDirLevel(TopDirectionLevel()); + ipassFont--; // no corresponding pass in font + m_ipassJust1++; + m_ipassPos1++; + } + else if (ipassJust1 <= ipass && ipass < ipassPos1) + { + // A justification pass is, in essence, a substitution pass. + m_prgppass[ipass] = new GrSubPass(ipass); + m_prgppass[ipass]->ReadFromFont(grstrm, fxdSilfVersion, fxdRuleVersion, + lSubTableStart + rgnPassOffsets[ipassFont]); + m_ipassPos1++; + } + else if (ipassPos1 <= ipass) + { + m_prgppass[ipass] = new GrPosPass(ipass); + if (ipassFont < cpassFont) + { + m_prgppass[ipass]->ReadFromFont(grstrm, fxdSilfVersion, fxdRuleVersion, + lSubTableStart + rgnPassOffsets[ipassFont]); + } + else + { + // No positioning pass in font: create a bogus one. + m_prgppass[ipass]->InitializeWithNoRules(); + } + } + else + { + Assert(false); + return false; // bad table + } + } + + return true; +} + +/*---------------------------------------------------------------------------------------------- + Create passes corresponding to an invalid font. +----------------------------------------------------------------------------------------------*/ +void GrTableManager::CreateEmpty() +{ + m_cpass = 2; + + m_prgppass = new GrPass*[2]; + m_prgppass[0] = new GrGlyphGenPass(0); + + m_prgppass[1] = new GrPosPass(1); + m_prgppass[1]->InitializeWithNoRules(); + + m_ipassPos1 = 1; + m_cpassLB = 0; + m_fBidi = false; + + m_engst.CreateEmpty(); +} + +void EngineState::CreateEmpty() +{ + m_fStartLineContext = false; + m_fEndLineContext = false; +} + +/*---------------------------------------------------------------------------------------------- + Create the slot streams corresponding to the passes. +----------------------------------------------------------------------------------------------*/ +void EngineState::CreateSlotStreams() +{ + if (m_prgpsstrm) + return; + + m_prgpsstrm = new GrSlotStream * [m_cpass]; + for (int ipass = 0; ipass < m_cpass; ++ipass) + { + m_prgpsstrm[ipass] = new GrSlotStream(ipass); + } +} + +/*---------------------------------------------------------------------------------------------- + Store information about the pass-states in the pass objects. +----------------------------------------------------------------------------------------------*/ +void GrTableManager::StorePassStates(PassState * rgzpst) +{ + for (int ipass = 0; ipass < m_cpass; ipass++) + Pass(ipass)->SetPassState(rgzpst + ipass); +} + +/*---------------------------------------------------------------------------------------------- + Demand-driven loop: with goal of filling up final slot stream, loop backward and forward + filling up slot streams until final stream has reached physical capacity. + + @param pgjus - justification agent; NULL if justification is of no interest + @param jmodi - (internal) justification mode + @param fStartLine, fEndLine + @param ichSegLim - known end of segment; -1 when using backtracking + @param dxWidthRequested - available width; when justifying, exact width desired + @param dxUnjustifiedWidth - when justifying, what the width would be normally + @param fNeedFinalBreak - true if the end of the segment needs to be a valid break point + @param fMoreText - true if char stream doesn't go up to text-stream lim + @param ichFontLim - end of the range that could be rendered by this font + @param fInfiniteWidth - used for "measured" segments + @param fWidthIsCharCount - kludge for test procedures: the width is a character count, + not a pixel count + @param lbPref - try for longest segment ending with this breakweight + - when justifying, lb to assume + @param lbMax - max (last resort) breakweight if no preferred break possible + @param ichwCallerBtLim - caller's backtrack lim; -1 if caller is not backtracking + @param twsh - how to handle trailing white-space + @param fParaRtl - overall paragraph direction + @param nDirDepth - direction depth of this segment +----------------------------------------------------------------------------------------------*/ +void GrTableManager::Run(Segment * psegNew, Font * pfont, + GrCharStream * pchstrm, IGrJustifier * pgjus, int jmodi, + LayoutEnvironment & layout, + int ichSegLim, + float dxWidthRequested, float dxUnjustifiedWidth, + bool fNeedFinalBreak, bool fMoreText, int ichFontLim, + bool fInfiniteWidth, bool fWidthIsCharCount, + int ichwCallerBtLim, + int nDirDepth, SegEnd estJ) +{ + bool fStartLine = layout.startOfLine(); + bool fEndLine = layout.endOfLine(); + LineBrk lbPref = layout.bestBreak(); + LineBrk lbMax = layout.worstBreak(); + TrWsHandling twsh = layout.trailingWs(); + bool fParaRtl = layout.rightToLeft(); + Segment * psegPrev = layout.prevSegment(); + Segment * psegInit = layout.segmentForInit(); + std::ostream * pstrmLog = layout.loggingStream(); + + m_engst.Initialize(Engine(), this); // do this after the tables have been fully read + m_engst.m_jmodi = jmodi; + m_engst.m_ipassJustCalled = -1; + m_engst.m_twsh = twsh; + m_engst.m_fParaRtl = fParaRtl; + m_engst.m_dxsShrinkPossible = GrSlotState::kNotYetSet; + m_fLogging = (pstrmLog != NULL); + + int cbPrev; + byte * pbPrevSegDat; + if (psegPrev) + cbPrev = psegPrev->NextSegInitBuffer(&pbPrevSegDat); + else if (psegInit) + cbPrev = psegInit->ThisSegInitBuffer(&pbPrevSegDat); + else + cbPrev = 0; + + if (pchstrm->IsEmpty()) + { + m_engst.m_lbPrevEnd = klbNoBreak; + InitSegmentAsEmpty(psegNew, pfont, pchstrm, fStartLine, fEndLine); + psegNew->SetWidths(0, 0); + psegNew->SetUpOutputArrays(pfont, this, NULL, 0, 0, 0, twsh, + fParaRtl, nDirDepth, true); + psegNew->SetLayout(layout); + return; + } + + int islotUnderBreak = -1; // position of inserted or final break point in underlying + // data; none so far (if an actual LB has been inserted, + // this is the index of that slot; otherwise it is the + // index of the last valid slot) + int islotSurfaceBreak = -1; // position of line break in surface stream; none so far + LineBrk lbBestToTry = lbPref; + LineBrk lbFound = klbNoBreak; + + float dxMaxWidth = dxWidthRequested; + + if (m_engst.m_jmodi == kjmodiJustify) + { + Assert(fInfiniteWidth); + lbBestToTry = klbClipBreak; + lbFound = lbPref; + } + + float dxWidth; + + std::vector<int> vnslotNeeded; + vnslotNeeded.resize(m_cpass); + + m_engst.InitializeStreams(this, pchstrm, cbPrev, pbPrevSegDat, fNeedFinalBreak, &islotUnderBreak); + + int ipassCurr; + int nNeedInput; // kNextPass: go on to the next pass + // kBacktrack: need to insert a line break + // > 0: need more input from previous pass + + int cchwPostXlbContext = m_pgreng->PostXlbContext(); + if (ichSegLim > -1) + { + // When we know exactly where the end of the segment will be, get as much as + // we need from the character stream, and then fill up the remaining streams. + // (3 below is an arbitrary number; we just want to get slightly more than + // we really need.) + vnslotNeeded[0] = ichSegLim - pchstrm->Min() + cchwPostXlbContext + 3; + for (int ipass = 1; ipass < m_cpass; ipass++) + vnslotNeeded[ipass] = 10000; + // Start at pass 0. + ipassCurr = 0; + } + else + { + // When we're doing line-breaking to find the end of the segment, use the + // demand-driven approach that tries to fill up the available space. + vnslotNeeded[m_cpass - 1] = 1; + + // Start at the last pass. (It might be more efficient to start at the first pass and + // generate a small arbitrary number of glyphs.) + ipassCurr = m_cpass - 1; + } + + while (ipassCurr < m_cpass) + { + if (ipassCurr == 0) + { + int nNeedNow = vnslotNeeded[0]; + // Zeroth pass: generate glyph IDs from the underlying input. + nNeedInput = Pass(0)->ExtendGlyphIDOutput(this, + pchstrm, OutputStream(0), ichSegLim, cchwPostXlbContext, lbPref, + nNeedNow, fNeedFinalBreak, m_engst.m_twsh, &islotUnderBreak); + Assert(nNeedInput == kNextPass || nNeedInput == kBacktrack); + vnslotNeeded[0] -= nNeedNow; + } + else if (ipassCurr < m_cpass - 1) + { + // Middle pass. + int cslotNeedNow = vnslotNeeded[ipassCurr]; + cslotNeedNow = max(1, cslotNeedNow); + int cslotGot; + Pass(ipassCurr)->ExtendOutput(this, + InputStream(ipassCurr), OutputStream(ipassCurr), cslotNeedNow, twsh, + &nNeedInput, &cslotGot, &islotUnderBreak); + vnslotNeeded[ipassCurr] -= cslotGot; + //if (nNeedInput != kNextPass && nNeedInput != kBacktrack) + // nNeedInput = max(nNeedInput, cslotNeedNow - cslotGot); + } + else // (ipassCurr == m_cpass - 1) + { + // Final pass: position and test allotted space. + float dxWidthWShrink = dxMaxWidth; + if (m_engst.m_dxsShrinkPossible != GrSlotState::kNotYetSet) + dxWidthWShrink += (int)m_engst.m_dxsShrinkPossible; + nNeedInput = Pass(ipassCurr)->ExtendFinalOutput(this, + InputStream(ipassCurr), OutputStream(ipassCurr), + dxWidthWShrink, fWidthIsCharCount, fInfiniteWidth, + (islotUnderBreak > -1), (fNeedFinalBreak && lbFound == klbNoBreak), + lbMax, twsh, + &islotSurfaceBreak, &dxWidth); + } + + if (m_engst.m_jmodi == kjmodiJustify) + { + if (ipassCurr < m_ipassJust1 && !OutputStream(ipassCurr)->FullyWritten()) + { + // When justifying, fill up all the streams up to the point where we need + // to interact with the justification routine. + // I think this code is obsolete. + int nStillNeed = 1000; + if ((ipassCurr == 1 && nNeedInput != kNextPass) || ipassCurr == 0) + nStillNeed = ichSegLim - (pchstrm->Pos() - pchstrm->Min()) + + cchwPostXlbContext + 3; + if (nStillNeed > 0) + { + if (nNeedInput == kNextPass) + ipassCurr++; // repeat this pass + nNeedInput = nStillNeed; + } + } + else if (nNeedInput == kNextPass && ipassCurr + 1 == m_ipassJust1) + { + CallJustifier(pgjus, ipassCurr, dxUnjustifiedWidth, dxWidthRequested, fEndLine); + } + } + DetermineShrink(pgjus, ipassCurr); + + if (nNeedInput == kBacktrack) + { + // Final output has overflowed its space--find (or adjust) the break point. + + bool fFoundBreak = Backtrack(&islotUnderBreak, + &lbBestToTry, lbMax, twsh, fMoreText, ichwCallerBtLim, &lbFound); + if (!fFoundBreak) + { + // Nothing will fit. Initialize the new segment just enough so that + // we can delete it. + InitSegmentToDelete(psegNew); + return; + } + } + else if (nNeedInput == kNextPass) + { + // This pass is sufficiently full--return to following pass. + ipassCurr++; + } + else + { + // Get more input from previous pass. + ipassCurr--; + vnslotNeeded[ipassCurr] = nNeedInput; + } + } + + // At this point we have fully transduced all the text for this segment. + + // Figure out why we broke the segment and what the caller is supposed to do about it. + SegEnd est; + if (m_engst.m_jmodi == kjmodiJustify) + { + // Don't change what was passed in. + est = estJ; + } + else if (m_engst.m_fRemovedWhtsp) // (islotUnderBreak > -1 && twsh == ktwshNoWs) + { + Assert(!m_engst.m_fInsertedLB); + est = kestMoreWhtsp; + } + + else if (m_engst.m_fHitHardBreak) + { + est = kestHardBreak; + } + + else if (!m_engst.m_fExceededSpace && !fMoreText) + // Rendered to the end of the original requested range. + est = kestNoMore; + +// else if (!m_fExceededSpace && !fNextIsSameWs && fMoreText) +// // Writing system break; may be more room on the line. But see tweak below. +// *pest = kestWsBreak; + + else if (m_engst.m_fExceededSpace) + { + // Exceeded available space, found a legal break. + if (twsh == ktwshNoWs && m_engst.m_fRemovedWhtsp) + est = kestMoreWhtsp; + else + est = kestMoreLines; + } + + else if (twsh == ktwshOnlyWs) + { + est = kestOkayBreak; // but see tweak below + } + + else + { + // Broke because of something like a font change. Determine whether this is a legal + // break or not. + // TODO SharonC: handle the situation where it is legal to break BEFORE the first + // character of the following segment. + Assert(fMoreText); + //Assert(fNextIsSameWs); + if (ichwCallerBtLim > -1) + { + Assert(m_engst.m_fInsertedLB); + } + else if (ichSegLim > -1) + { + // We stopped where caller said to. + Assert(fInfiniteWidth); + } + else + { + Assert(!m_engst.m_fInsertedLB); + Assert(islotUnderBreak == -1 || m_engst.m_fFinalLB); + } + int islotTmp = OutputStream(m_cpass - 1)->WritePos(); + GrSlotState * pslotTmp; + if (m_engst.m_fFinalLB || m_engst.m_fInsertedLB) + pslotTmp = OutputStream(m_cpass-1)->SlotAt(islotTmp - 2); + else + pslotTmp = OutputStream(m_cpass-1)->SlotAt(islotTmp - 1); + + if (abs(pslotTmp->BreakWeight()) <= lbPref) + est = kestOkayBreak; + + else if (abs(pslotTmp->BreakWeight()) > lbMax) + est = kestBadBreak; + + else if (OutputStream(m_cpassLB)->HasEarlierBetterBreak(islotUnderBreak, lbFound, + this->LBGlyphID())) + { + est = kestBadBreak; + } + else + est = kestOkayBreak; + + // We no longer have the ability to know whether or not the next segment will use + // the same writing system. + //if (est == kestBadBreak && m_engst.m_fExceededSpace && !fNextIsSameWs && fMoreText) + // est = kestWsBreak; + } + + // Make a segment out of it and return it. + int dichSegLen; + InitNewSegment(psegNew, pfont, pchstrm, pgjus, + islotUnderBreak, islotSurfaceBreak, fStartLine, fEndLine, ichFontLim, lbFound, est, + &dichSegLen); + if (psegNew->segmentTermination() == kestNothingFit) + { + return; + } + else if (psegNew->segmentTermination() == kestHardBreak && + psegNew->startCharacter() == psegNew->stopCharacter()) + { + // Empty segment caused by a hard-break. + m_engst.m_lbPrevEnd = klbNoBreak; + psegNew->SetWidths(0, 0); + psegNew->SetUpOutputArrays(pfont, this, NULL, 0, 0, 0, twsh, + fParaRtl, nDirDepth, true); + psegNew->SetLayout(layout); + return; + } + + psegNew->RecordInitializationForThisSeg(cbPrev, pbPrevSegDat); + + if ((est == kestNoMore || est == kestWsBreak) && twsh == ktwshOnlyWs && + dichSegLen < pchstrm->Lim() - pchstrm->Min()) + { + // If we are asking for white-space only, and we didn't use up everything in the font + // range, we could possibly have gotten an initial white-space only segment + // (as opposed to a writing system break). + est = kestOkayBreak; + psegNew->FixTermination(est); + } + + // Do this before fixing up the associations below. + //if (m_pgreng->LoggingTransduction()) + // WriteTransductionLog(pchstrm, *ppsegRet, + // cbPrev, pbPrevSegDat, pbNextSegDat, pcbNextSegDat); + WriteTransductionLog(pstrmLog, pchstrm, psegNew, cbPrev, pbPrevSegDat); + + RecordAssocsAndOutput(pfont, psegNew, fWidthIsCharCount, twsh, fParaRtl, nDirDepth); + + // Do this after fixing up the associations. + //if (m_pgreng->LoggingTransduction()) + // WriteAssociationLog(pchstrm, *ppsegRet); + WriteAssociationLog(pstrmLog, pchstrm, psegNew); + + psegNew->SetLayout(layout); +} + +/*---------------------------------------------------------------------------------------------- + Create a new segment to return. + + @param pchstrm - input stream + @param pgjus - justification agent, in case this segment needs to be + stretched or shrunk + @param islotLBStreamBreak - position of inserted line break slot (which should be equal + to the position in the glyph generation stream); + -1 if no line-break was inserted (we rendered to the + end of the range) + @param islotSurfaceBreak - position of line break slot in final (surface) stream + @param fStartLine - does this segment start a line? + @param fEndLine - does this segment end a line? + @param ichFontLim - end of range that could be rendered by this font + @param lbEndFound - line break found; possibly adjusted here + @param est - why did the segment terminate +----------------------------------------------------------------------------------------------*/ +void GrTableManager::InitNewSegment(Segment * psegNew, + Font * pfont, GrCharStream * pchstrm, IGrJustifier * pgjus, + int islotLBStreamBreak, int islotSurfaceBreak, bool fStartLine, bool fEndLine, int ichFontLim, + LineBrk lbEndFound, SegEnd est, int * pdichSegLen) +{ + LineBrk lbStart = m_engst.m_lbPrevEnd; + LineBrk lbEnd = lbEndFound; + if (est == kestBadBreak) + lbEnd = klbLetterBreak; + + int ichwMin = pchstrm->Min(); + int ichwLim; + +// int cslotPreSegStream0 = m_cslotPreSeg; +// if (m_fInitialLB && m_cpassLB > 0) +// { +// // Initial LB is included in m_cslotPreSeg, but is not in stream 0. +// // --Not really needed, because both islotLBStreamBreak and m_cslotPreSeg take +// // the initial LB into account if it is there. +// cslotPreSegStream0--; +// } + + // No longer have to subtract this, because the chunk map takes it into + // consideration. + //int cslotPreInitialLB = m_cslotPreSeg - (m_fInitialLB ? 1 : 0); + + //int ichwOldTmp; + + if (m_engst.m_fInsertedLB) + { + Assert(islotLBStreamBreak > -1); + int ichwSegLim = m_engst.LbSlotToSegLim(islotLBStreamBreak, pchstrm, m_cpassLB); + ichwLim = ichwSegLim + pchstrm->Min(); // - cslotPreInitialLB; + + // Old result, for debugging: + //ichwOldTmp = islotLBStreamBreak + pchstrm->Min() - m_cslotPreSeg; + } + else if (m_engst.m_fFinalLB || islotLBStreamBreak == -1) + ichwLim = pchstrm->Lim(); + else + { + int ichwSegLim = m_engst.LbSlotToSegLim(islotLBStreamBreak, pchstrm, m_cpassLB); + ichwLim = ichwSegLim + pchstrm->Min(); // - cslotPreInitialLB; + + // Old result, for debugging: + // islotLBStreamBreak is the last slot in the segment, not the LB glyph; hence +1. + //ichwOldTmp = islotLBStreamBreak + pchstrm->Min() - m_cslotPreSeg + 1; + } + + *pdichSegLen = ichwLim - ichwMin; + + if (ichwMin >= ichwLim) + { + if (est == kestHardBreak) + { + // Empty segment cause by a hard-break. + InitSegmentAsEmpty(psegNew, pfont, pchstrm, fStartLine, fEndLine); + psegNew->FixTermination(est); + } + else + { + // Invalid empty segment. + Assert(ichwMin == ichwLim); + InitSegmentToDelete(psegNew); + } + return; + } + + Assert(psegNew); + + psegNew->Initialize(pchstrm->String(), + ichwMin, ichwLim, + lbStart, lbEnd, est, fStartLine, fEndLine, m_pgreng->RightToLeft()); + + psegNew->SetEngine(m_pgreng); + psegNew->SetFont(pfont); + psegNew->SetJustifier(pgjus); + psegNew->SetFaceName(m_pgreng->FaceName(), m_pgreng->BaseFaceName()); + + bool fNextSegNeedsContext = + !(est == kestNoMore || est == kestWsBreak + || ichwLim >= pchstrm->Lim() || ichwLim >= ichFontLim); + + InitializeForNextSeg(psegNew, islotLBStreamBreak, islotSurfaceBreak, lbEnd, + fNextSegNeedsContext, pchstrm); + + psegNew->SetPreContext(0 - m_pgreng->PreXlbContext()); +} + +/*---------------------------------------------------------------------------------------------- + The newly created segment is invalid, for instance, because we were backtracking and + couldn't find a valid break point. Initialize it just enough so that it can be + deleted by the client. +----------------------------------------------------------------------------------------------*/ +void GrTableManager::InitSegmentToDelete(Segment * psegNew) +{ + psegNew->Initialize(NULL, 0, 0, klbClipBreak, klbClipBreak, kestNothingFit, + false, false, false); +} + +/*---------------------------------------------------------------------------------------------- + Map from the given slot (which is generally the final LB slot) to the end of the segment, + taking into account the fact that a single slot may represent two 16-bit surrogates. + + There are several approaches that seem reasonable but do NOT work. (I tried them!) + You can't calculate it directly from the length of the streams, because that doesn't take + into account the possible presence of surrogate pairs in the character stream. You can't + use the chunk map at the point of the LB, because if a rule spanned a LB the chunk won't + have ended there and so there won't be any valid information. + + What seems to work is to figure out the slot(s) in the zeroth stream using the associations + and then use the segment offset for this slot. This should be reliable because there is + always a one-to-one correspondence between glyphs in the line-break passes (ie, no + insertions or deletions), so the associations should be dependable and straightforward. +----------------------------------------------------------------------------------------------*/ +int EngineState::LbSlotToSegLim(int islotLB, GrCharStream * pchstrm, int cpassLB) +{ + Assert(islotLB < OutputStream(cpassLB)->WritePos()); + + // Figure out the corresponding characters in the zeroth stream. These should be straight- + // forward to figure out because no substitutions are permitted in the LB passes. + // I do the slots before and after the LB, if possible, as a sanity check. + GrSlotStream * psstrmLB = OutputStream(cpassLB); + GrSlotState * pslotPreLB = (m_fFinalLB || m_fInsertedLB) ? + psstrmLB->SlotAt(islotLB - 1) : + psstrmLB->SlotAt(islotLB); + GrSlotState * pslotPostLB = (psstrmLB->WritePos() > islotLB + 1) ? + psstrmLB->SlotAt(islotLB + 1) : + NULL; + + int ichwPreLB = pslotPreLB->AfterAssoc(); + + int ichwSegLim; + if (pslotPostLB) + { + ichwSegLim = pslotPostLB->BeforeAssoc(); + Assert(ichwPreLB + 1 == ichwSegLim + || ichwPreLB + 2 == ichwSegLim); // in case of a surrogate + } + else + { + ichwSegLim = ichwPreLB + 1; + while (!pchstrm->AtUnicodeCharBoundary(ichwSegLim)) + ichwSegLim++; + Assert(pchstrm->AtUnicodeCharBoundary(ichwSegLim)); + } + + // Old buggy routine that doesn't handle the case where a rule (and hence the chunk map) did + // not end at the LB character: + // Map backwards through the LB pass streams, if any. + //int islotAdjusted = islotLB + 1; // after the LB + //for (int ipass = m_cpassLB; ipass > 0; ipass--) + //{ + // islotAdjusted = ChunkInPrev(ipass, islotAdjusted, pchstrm); + //} + // + //int ichwSegLim = ChunkInPrev(0, islotAdjusted, pchstrm); + + // Mapping from 32-bit to 16-bit should only increase the index. Except that there + // may be a LB in the slot stream that is not in the character stream, so it could be + // one less. + Assert(ichwSegLim + m_cslotPreSeg >= islotLB - 1); + + return ichwSegLim; +} + +/*---------------------------------------------------------------------------------------------- + Return the chunk mapping into the previous stream, taking into account the fact that it + is not recorded for the write-position. +----------------------------------------------------------------------------------------------*/ +int GrTableManager::ChunkInPrev(int ipass, int islot, GrCharStream * pchstrm) +{ + GrSlotStream * psstrmOut = OutputStream(ipass); + GrSlotStream * psstrmIn = (ipass == 0) ? NULL : InputStream(ipass); + + int islotTmp = islot; + int islotAdjusted; + do { + if (islot >= psstrmOut->WritePos()) + { + Assert(islot == psstrmOut->WritePos()); + islotAdjusted = (ipass == 0) ? pchstrm->SegOffset() : psstrmIn->ReadPos(); + Assert(islotAdjusted > -1); + } + else + islotAdjusted = psstrmOut->ChunkInPrev(islotTmp); + islotTmp--; + } while (islotAdjusted == -1); + + return islotAdjusted; +} + +/*---------------------------------------------------------------------------------------------- + Create a new segment corresponding to an empty string. + + @param pchstrm - input stream + @param fStartLine - does this segment start a line? + @param fEndLine - does this segment end a line? +----------------------------------------------------------------------------------------------*/ +void GrTableManager::InitSegmentAsEmpty(Segment * psegNew, Font * pfont, + GrCharStream * pchstrm, bool fStartLine, bool fEndLine) +{ + LineBrk lbStart = m_engst.m_lbPrevEnd; + LineBrk lbEnd = klbNoBreak; + + int ichwMin = 0; + int ichwLim = 0; + + Assert(psegNew); + + psegNew->Initialize(pchstrm->String(), ichwMin, ichwLim, + lbStart, lbEnd, kestNoMore, fStartLine, fEndLine, m_pgreng->RightToLeft()); + + psegNew->SetEngine(m_pgreng); + psegNew->SetFont(pfont); + psegNew->SetJustifier(NULL); // can't justify an empty segment + psegNew->SetFaceName(m_pgreng->FaceName(), m_pgreng->BaseFaceName()); + + byte pbNextSegDat[256]; + int cbNextSegDat; + int * pcbNextSegDat = &cbNextSegDat; + + // Initialization for (theoretical) next segment. + if (pbNextSegDat) + { + byte * pb = pbNextSegDat; + *pb++ = byte(lbEnd); + *pb++ = kdircNeutral; + *pb++ = kdircNeutral; + *pb++ = 0; + for (int ipass = 0; ipass < m_cpass; ipass++) + *pb++ = 0; + *pcbNextSegDat = 0; + psegNew->RecordInitializationForNextSeg(*pcbNextSegDat, pbNextSegDat); + } + + psegNew->SetPreContext(0); +} + +/*---------------------------------------------------------------------------------------------- + Find a line-break and unwind the passes. Return true if we were able to + backtrack successfully (which may have meant using a less-than-optimal line-break). + Return false if no line-break could be found (because maxBreakWeight did not allow + letter breaks or clipping--generally because there was already something on this + line). + + @param pislotPrevBreak - position of previously-created break, -1 if none; + this is the index of the line-break glyph if any, + or the index of the last slot in the segment; + adjusted to contain the position of the newly + found break + @param plbMin - minimum (best) line break weight to try + @param lbMax - maximum (worst possible) line break weight + @param twsh - how to handle trailing white-space + @param fMoreText - true if char stream doesn't go up to original lim + @param ichwCallerBtLim - caller's backtrack lim; -1 if caller is not backtracking + @param plbFound - kind of line-break created +----------------------------------------------------------------------------------------------*/ +bool GrTableManager::Backtrack(int * pislotPrevBreak, + LineBrk * plbMin, LineBrk lbMax, TrWsHandling twsh, bool fMoreText, + int ichwCallerBtLim, + LineBrk * plbFound) +{ + int islotStartTry; + if (*pislotPrevBreak == ichwCallerBtLim - 1) + { + islotStartTry = *pislotPrevBreak; + } + else if (*pislotPrevBreak == -1) + { + // If no line break has been found so far, figure out where to start trying based + // on where the final stream choked. + GrSlotStream * psstrmFinal = OutputStream(m_cpass-1); + if ((islotStartTry + = m_engst.TraceStreamZeroPos(psstrmFinal->WritePos()-1, TopDirectionLevel())) + == -1) + { + // Just start looking at the end of what's been generated. + islotStartTry = OutputStream(m_cpassLB)->ReadPos() - 1; + } + } + else + { + // Start just before previous line break. + if (m_engst.m_fInsertedLB || m_engst.m_fFinalLB) + islotStartTry = *pislotPrevBreak - 2; // skip inserted LB and previous slot + else + islotStartTry = *pislotPrevBreak - 1; // skip previous slot + } + + // Determine if we want to insert an actual line-break "glyph". Don't do that if + // we are omitting trailing white space or we are at a writing-system or font break. + bool fInsertLB; + if (twsh == ktwshNoWs) + // omitting trailing white-space + fInsertLB = false; + else if (twsh == ktwshOnlyWs) + // trailing white-space segment + fInsertLB = true; + else if (ichwCallerBtLim > -1) + // backtracking + fInsertLB = true; + else if (*pislotPrevBreak > -1) + // no longer at the edge of the writing system or font + fInsertLB = true; + else if (!fMoreText) + // at final edge of total range to render + fInsertLB = true; + else + fInsertLB = false; + + // Try to insert a line-break in the output of the (final) line-break pass (if any, or + // the output of the glyph-generation pass). First try the preferred (strictest) + // break weight and gradually relax. + LineBrk lb = *plbMin; + Assert(*plbMin <= lbMax); + GrSlotStream * psstrmLB = OutputStream(m_cpassLB); + islotStartTry = min(islotStartTry, psstrmLB->WritePos() - 1); + int islotNewBreak; + while (lb <= lbMax) + { + LineBrk lbNextToTry; + if (fInsertLB) + { + islotNewBreak = psstrmLB->InsertLineBreak(this, + *pislotPrevBreak, m_engst.m_fInsertedLB, islotStartTry, + lb, twsh, m_engst.m_cslotPreSeg, &lbNextToTry); + } + else + { + islotNewBreak = psstrmLB->MakeSegmentBreak(this, + *pislotPrevBreak, m_engst.m_fInsertedLB, islotStartTry, + lb, twsh, m_engst.m_cslotPreSeg, &lbNextToTry); + } + if (islotNewBreak > -1) + break; + if (lb >= lbMax) + break; + lb = IncLineBreak(lb); // relax the break weight + } + + if (islotNewBreak == -1) + { + return false; + } + + // We've successfully inserted a line break. + + if (fInsertLB) + m_engst.m_fInsertedLB = true; + + m_engst.m_fFinalLB = false; + + UnwindAndReinit(islotNewBreak); + + *pislotPrevBreak = islotNewBreak; + *plbMin = lb; // return the best break we were able to find, so we don't keep trying + // for a better one when we know now that we can't find it + *plbFound = lb; + + return true; +} + +/*---------------------------------------------------------------------------------------------- + This method is called after backtracking or removing trailing white-space. + Unwind the following slot streams as necessary depending on where the change was made. + + @param islotNewBreak - in output of (final) line-break pass +----------------------------------------------------------------------------------------------*/ +void GrTableManager::UnwindAndReinit(int islotNewBreak) +{ + OutputStream(m_cpassLB)->ZapCalculatedDirLevels(islotNewBreak); + + // Mark the passes before the line-break pass as fully written, so that if there + // isn't a line-break glyph to intercept, we don't keep trying to get more input + // from them. + int ipass; + for (ipass = 1; ipass < m_cpassLB + 1; ++ipass) + { + InputStream(ipass)->MarkFullyWritten(); + } + + // Unwind the passes to the changed positions. + // Note that we don't need to unwind pass 0, since all it does is generate glyph IDs. + // Also we don't need to unwind the line-break passes, because conceptually they happen + // before the break is inserted. + + int islotChgPos = islotNewBreak; + bool fFirst = true; + for (ipass = m_cpassLB + 1 ; ipass < m_cpass ; ++ipass) + { + islotChgPos = + Pass(ipass)->Unwind(this, islotChgPos, InputStream(ipass), OutputStream(ipass), + fFirst); + fFirst = false; + } + // For anything that may have been skipped in the final output: + OutputStream(m_cpass - 1)->SetReadPos(0); + OutputStream(m_cpass - 1)->SetReadPosMax(0); + Pass(m_cpass - 1)->UndoResyncSkip(); + OutputStream(m_cpass - 1)->ClearSlotsSkippedToResync(); + + m_engst.InitPosCache(); + + m_engst.m_dxsShrinkPossible = GrSlotState::kNotYetSet; // recalculate it +} + +/*---------------------------------------------------------------------------------------------- + Store information in the newly created segment that will allow us to reinitalize + the streams in order to process the next segment, appropriately handling any + cross-line-boundary contextuals. + + The information required is the number of glyphs in the first stream that must + be reprocessed, and a skip-offset between each stream indicating the beginning + of chunk boundaries. + + In most cases, where there are no cross-line-boundary contextuals, all these numbers + will be zero. + + The buffer generated is of the following format (all are 1 byte, unsigned): + end breakweight + previous strong directionality code + previous terminator dir code + restart backup + skip offsets for each pass + This format must match what is generated by InitializeStreams. + + @param pseg - newly created segment + @param islotUnderBreak - position of line break inserted in underlying stream (output + of glyph-generation pass--stream 0); -1 if we rendered to + the end of the range + @param islotSurfaceBreak - position of line break in surface stream; + -1 if we rendered to the end of the range + @param lbEnd - kind of line-break at the end of this segment + @param fNextSegNeedsContext - true if we need to remember the context for the next seg + @param pchstrm - character stream input for this segment + @param cbNextMax - amount of space available in the buffer + @param pbNextSegDat - buffer for initializing next segment + @param pcbNextSegDat - amount of space used in buffer +----------------------------------------------------------------------------------------------*/ +void GrTableManager::InitializeForNextSeg(Segment * pseg, + int islotUnderBreak, int islotSurfaceBreak, LineBrk lbEnd, + bool fNextSegNeedsContext, GrCharStream * pchstrm) +{ + byte pbNextSegDat[256]; + int cbNextMax = 256; + int cbNextSegDat; + int * pcbNextSegDat = &cbNextSegDat; + + std::vector<int> vcslotSkipOffsets; + vcslotSkipOffsets.resize(m_cpass); + + if (cbNextMax == 0 || !pbNextSegDat) + { + // Caller is not interested in cross-line-boundary contextualization. + Assert(cbNextMax == 0); + Assert(!pbNextSegDat); + return; + } + + if (cbNextMax < m_cpass + 4) + THROW(kresInvalidArg); + + byte * pb = pbNextSegDat; + + if (!fNextSegNeedsContext) + { + // No contextualization between this segment and the next, whatever kind of renderer + // it might use. + *pcbNextSegDat = 0; + return; + } + + gid16 chwLB = LBGlyphID(); + + int islotUnderLim = islotUnderBreak; + if (!m_engst.m_fInsertedLB && !m_engst.m_fFinalLB) + // Then islotUnderBreak is the position of the last slot in the segment; increment + // to get the lim in stream zero. + islotUnderLim++; + + int islotSurfaceLim = islotSurfaceBreak; + if (islotSurfaceLim == -1) + islotSurfaceLim = OutputStream(m_cpass - 1)->FinalSegLim(); + + *pb++ = byte(lbEnd); + + // Find previous strong and terminator directionality codes. + + GrSlotStream * psstrm = OutputStream(m_cpass - 1); + DirCode dircStrong = kdircNeutral; + DirCode dircTerm = kdircNeutral; + int islot; + for (islot = islotSurfaceLim; islot-- > 0; ) + { + GrSlotState * pslot = psstrm->SlotAt(islot); + DirCode dirc = pslot->Directionality(); + if (dircTerm == kdircNeutral && dirc == kdircEuroTerm) + dircTerm = dirc; + if (StrongDir(dirc)) + { + dircStrong = dirc; + break; + } + } + *pb++ = byte(dircStrong); + *pb++ = byte(dircTerm); + + // Record how much of each pass to redo in the next segment. + // The basic algorithm is described in the "WR Data Transform Engine" document. + // But here we are doing a variation on what is described there. We are calculating + // the backup locations for BOTH the rule start locations and the necessary pre-contexts. + // It is the precontext values that are recorded for use by the following segment. But we + // also calculate the positions for the rule-start locations to make sure there is enough + // pre-context at each pass. + + int islotRS = islotSurfaceLim; // rule-start positions + int islotPC = islotRS; // to handle pre-context positions + + // If line-breaks are irrelevant to this font, we don't need to bother. + bool fGiveUp = !m_pgreng->LineBreakFlag(); + + int ipass; + for (ipass = m_cpass - 1; ipass >= 0 && !fGiveUp; --ipass) + { + GrSlotStream * psstrmIn = (ipass == 0) ? NULL : InputStream(ipass); + GrSlotStream * psstrmOut = OutputStream(ipass); + int islotBackupMin = (ipass == 0) ? 0 : psstrmOut->SegMin(); + if (ipass >= m_cpassLB && m_engst.m_fInitialLB) + islotBackupMin++; // how far we can back up--not before the start of this segment + + int islotPrevRS; // corresponding slot in the previous stream for rule-start position + int islotPrevPC; // same for pre-context position + + // Initially we have to include at least this many prior slots: + int cslotSkipOffset = 0; + + if (islotRS == psstrmOut->WritePos()) + { + islotPrevRS = (ipass == 0) ? pchstrm->Pos() : psstrmIn->ReadPos(); + } + else + { + // Ignore the final line-break, because it is explicitly inserted during + // the (final) line-break pass. +// if (ipass == m_cpassLB && psstrm->SlotAt(islotRS)->IsFinalLineBreak(chwLB)) +// { +// Assert(islotRS > 0); +// islotRS--; +// } + // Ignore the chunk in the pass that generated the line break that corresponds + // to the inserted line break being chunked with the previous glyph--we don't + // want to treat that as a cross-line-boundary contextual! + if (ipass == m_cpassLB && psstrmOut->SlotAt(islotRS)->IsFinalLineBreak(chwLB)) + { +// Assert(psstrm->ChunkInPrev(islotRS) == -1); + if (islotRS + 1 == psstrmOut->WritePos()) + islotPrevRS = (ipass == 0) ? pchstrm->SegOffset() : psstrmIn->ReadPos(); + else + islotPrevRS = psstrmOut->ChunkInPrev(islotRS + 1); + } + else + { + // Back up to the beginning of a chunk. + while (islotRS >= islotBackupMin && + (islotPrevRS = psstrmOut->ChunkInPrev(islotRS)) == -1) + { + --islotRS; + //++cslotSkipOffset; + } + } + } + if (islotPrevRS == -1 || islotRS < islotBackupMin) // hit the start of the segment + { + // In order to get enough context to be sure to run the same rules again, + // we'd have to back up into the previous segment. Too complicated, so give up. + fGiveUp = true; + break; + } + + int cslotPreContext = Pass(ipass)->MaxRulePreContext(); +LBackupPC: + if (islotPC == psstrmOut->WritePos()) + { + islotPrevPC = (ipass == 0) ? pchstrm->Pos() : psstrmIn->ReadPos(); + } + else + { + if (ipass == m_cpassLB && psstrmOut->SlotAt(islotPC)->IsFinalLineBreak(chwLB)) + { + if (islotPC+1 == psstrmOut->WritePos()) + islotPrevPC = (ipass == 0) ? pchstrm->SegOffset() : psstrmIn->ReadPos(); + else + islotPrevPC = psstrmOut->ChunkInPrev(islotPC+1); + } + else + { + // Back up to the beginning of a chunk; also make sure there are enough slots + // to handle the required pre-context for this pass. + while (islotPC >= islotBackupMin && + ((islotPrevPC = psstrmOut->ChunkInPrev(islotPC)) == -1 || + islotPrevRS - islotPrevPC < cslotPreContext)) + { + --islotPC; + ++cslotSkipOffset; + } + } + } + if (islotPC >= islotBackupMin && islotPrevRS - islotPrevPC < cslotPreContext) + { + --islotPC; + ++cslotSkipOffset; + goto LBackupPC; + } + if (islotPrevPC == -1 || islotPC < islotBackupMin) // hit the start of the segment + { + fGiveUp = true; + break; + } + + vcslotSkipOffsets[ipass] = cslotSkipOffset; + + islotRS = islotPrevRS; + islotPC = islotPrevPC; + + if (ipass == 0) + { + // Convert from underlying chars to stream-zero slots. + islotRS += m_engst.m_cslotPreSeg; + islotPC += m_engst.m_cslotPreSeg; + } + } + + // The following should be the case unless they tried to position contextually across + // the line break boundary, which is a very strange thing to do! - no longer true, + // because the pre-context is now included in this count, and it normal to have + // a pre-context in the final positioning pass. + //Assert(vcslotSkipOffsets[m_cpass-1] == 0); + + // Also no adjustments are made by the glyph-generation pass, so this should always + // be true: + Assert(vcslotSkipOffsets[0] == 0); + + // The number of slots in the underlying input previous to the line-break + // that must be reprocessed: + int cslotPreLB = islotUnderLim - islotPC; + + if (fGiveUp) + { + // We can't reasonably provide enough context, so don't try to do it at all. + cslotPreLB = 0; + for (ipass = 0; ipass < m_cpass; ipass++) + vcslotSkipOffsets[ipass] = 0; + } + + Assert(cslotPreLB >= 0); + Assert(cslotPreLB < 0xFF); + + *pb++ = byte(cslotPreLB); + for (ipass = 0; ipass < m_cpass; ipass++) + *pb++ = byte(vcslotSkipOffsets[ipass]); + + *pcbNextSegDat = 4 + m_cpass; + + pseg->RecordInitializationForNextSeg(*pcbNextSegDat, pbNextSegDat); +} + +/*---------------------------------------------------------------------------------------------- + Calculate the associations, and record the output slots in the segment. +----------------------------------------------------------------------------------------------*/ +void GrTableManager::RecordAssocsAndOutput(Font * pfont, + Segment * pseg, bool fWidthIsCharCount, + TrWsHandling twsh, bool fParaRtl, int nDirDepth) +{ + int cchwUnderlying = pseg->stopCharacter() - pseg->startCharacter(); + + GrSlotStream * psstrmFinal = OutputStream(m_cpass-1); + int csloutSurface = psstrmFinal->WritePos() - psstrmFinal->IndexOffset(); // # of output slots + + psstrmFinal->SetNeutralAssociations(LBGlyphID()); + + float xsTotalWidth, xsVisWidth; + +#ifdef OLD_TEST_STUFF + if (fWidthIsCharCount) + xsTotalWidth = xsVisWidth = 0; + else +#endif // OLD_TEST_STUFF + + // Make sure the final positions are set for every glyph. + CalcPositionsUpTo(m_cpass-1, reinterpret_cast<GrSlotState *>(NULL), + &xsTotalWidth, &xsVisWidth); + pseg->SetWidths(xsVisWidth, xsTotalWidth); + + pseg->SetUpOutputArrays(pfont, this, psstrmFinal, cchwUnderlying, csloutSurface, LBGlyphID(), + twsh, fParaRtl, nDirDepth); + + // Set underlying-to-surface assocations in the segment. + CalculateAssociations(pseg, csloutSurface); +} + +/*---------------------------------------------------------------------------------------------- + Calculate the underlying-to-surface associations and ligature mappings. + Assumes the arrays have been properly initialized. +----------------------------------------------------------------------------------------------*/ +void GrTableManager::CalculateAssociations(Segment * pseg, int csloutSurface) +{ + GrSlotStream * psstrmFinal = OutputStream(m_cpass-1); + + std::vector<int> vichwAssocs; + std::vector<int> vichwComponents; + + for (int islot = psstrmFinal->IndexOffset(); islot < psstrmFinal->WritePos(); islot++) + { + GrSlotState * pslot = psstrmFinal->SlotAt(islot); + int islout = islot - psstrmFinal->IndexOffset(); + if (!pslot->IsLineBreak(LBGlyphID())) + { + vichwAssocs.clear(); + pslot->AllAssocs(vichwAssocs); + + size_t iichw; + for (iichw = 0; iichw < vichwAssocs.size(); ++iichw) + { + pseg->RecordSurfaceAssoc(vichwAssocs[iichw], islout, 0); + } + + vichwComponents.clear(); + if (pslot->HasComponents()) + pslot->AllComponentRefs(vichwComponents); + + for (iichw = 0; iichw < vichwComponents.size(); iichw++) + { + pseg->RecordLigature(vichwComponents[iichw], islout, iichw); + } + } + } + + AdjustAssocsForOverlaps(pseg); + +// pseg->AdjustForOverlapsWithPrevSeg(); // for any characters officially in the + // previous segment but rendered in this one, + // or vice versa + + // TODO SharonC: for all characters that are right-to-left in the underlying stream, + // reverse the before and after flags in the segment. +} + +/*---------------------------------------------------------------------------------------------- + For any slots associated with characters in the new segment but not rendered in the + segment, adjust the underlying-to-surface mappings accordingly. Ie, set the before + mapping to kNegInfinity for slots at the beginning of the streams, and set the + after mapping to kPosInfinity for slots at the end of the streams. + + TODO SharonC: Rework this method more carefully to handle each slot exactly once. The current + approach could possibly, for instance, process a slot that is inserted in one pass + and deleted in the next. +----------------------------------------------------------------------------------------------*/ +void GrTableManager::AdjustAssocsForOverlaps(Segment * pseg) +{ + if (!m_engst.m_fInitialLB && !m_engst.m_fFinalLB && !m_engst.m_fInsertedLB) + // no cross-line contextualization possible + return; + + gid16 chwLB = LBGlyphID(); + std::vector<int> vichwAssocs; + + // Process any slots output by any substitution pass (or later) on either side of the + // line-breaks. These are the relevant slots that are rendered in the previous and + // following segments. + for (int ipass = m_cpass; ipass-- > m_cpassLB + 1; ) + { + GrSlotStream * psstrm = OutputStream(ipass); + int islotMin = (ipass == m_cpass - 1) ? psstrm->IndexOffset() : 0; + + if (m_engst.m_fInitialLB) + { + for (int islot = islotMin; ; islot++) + { + GrSlotState * pslot = psstrm->SlotAt(islot); + if (pslot->IsInitialLineBreak(chwLB)) + break; + Assert(!pslot->IsFinalLineBreak(chwLB)); + if (pslot->PassModified() != ipass) + continue; + + vichwAssocs.clear(); + pslot->AllAssocs(vichwAssocs); + for (size_t iichw = 0; iichw < vichwAssocs.size(); ++iichw) + pseg->MarkSlotInPrevSeg(vichwAssocs[iichw], islot); + } + } + + if (m_engst.m_fFinalLB || m_engst.m_fInsertedLB && ipass > m_cpassLB) + { + for (int islot = psstrm->WritePos(); islot-- > islotMin ; ) + { + GrSlotState * pslot = psstrm->SlotAt(islot); + if (pslot->IsFinalLineBreak(chwLB)) + break; + Assert(!pslot->IsInitialLineBreak(chwLB)); + if (pslot->PassModified() != ipass) + continue; + + vichwAssocs.clear(); + pslot->AllAssocs(vichwAssocs); + for (size_t iichw = 0; iichw < vichwAssocs.size(); ++iichw) + pseg->MarkSlotInNextSeg(vichwAssocs[iichw], islot); + } + } + } +} + +/*---------------------------------------------------------------------------------------------- + Initialize all the streams. In particular, process the information about the + skip-offsets to handle segments other than the first. + + @param cbPrev - number of bytes in the pbPrevSegDat buffer + @param pbPrevSegDat - buffer in the following format (all are 1 byte, unsigned): + prev seg's end breakweight + prev seg's previous strong directionality code + prev seg's previous terminator dir code + restart backup + skip offsets for each pass + This format must match what is generated by InitializeForNextSeg. +----------------------------------------------------------------------------------------------*/ +void EngineState::InitializeStreams(GrTableManager * ptman, + GrCharStream * pchstrm, + int cbPrev, byte * pbPrevSegDat, bool fNeedFinalBreak, int * pislotFinalBreak) +{ + int cpassLB = ptman->NumberOfLbPasses(); + int ipassPos1 = ptman->FirstPosPass(); + + CreateSlotStreams(); + + int cslotBackup = 0; + + InitForNewSegment(ptman); + + if (cbPrev == 0) + { + m_lbPrevEnd = klbNoBreak; + m_dircInitialStrongDir = kdircNeutral; + m_dircInitialTermDir = kdircNeutral; + for (int ipass = 0; ipass < m_cpass; ++ipass) + { + OutputStream(ipass)->Initialize(ipassPos1, false); + m_prgzpst[ipass].SetResyncSkip(0); + + if (ptman->LoggingTransduction()) + m_prgzpst[ipass].InitializeLogInfo(); + } + } + else + { + Assert(cbPrev >= 4); + byte * pb = pbPrevSegDat; + + m_lbPrevEnd = LineBrk(*pb++); + + // Directionality codes from the previous segment. + m_dircInitialStrongDir = DirCode((int)*pb++); + m_dircInitialTermDir = DirCode((int)*pb++); + + cslotBackup = (int)*pb++; + Assert((cbPrev == 4 || cslotBackup == 0) || cbPrev == m_cpass + 4); + + for (int ipass = 0; ipass < m_cpass; ++ipass) + { + OutputStream(ipass)->Initialize(ipassPos1, true); + if (cbPrev == 4) + m_prgzpst[ipass].SetResyncSkip(0); // different writing system + else + m_prgzpst[ipass].SetResyncSkip(*pb++); + + if (ptman->LoggingTransduction()) + m_prgzpst[ipass].InitializeLogInfo(); + } + + pchstrm->Backup(cslotBackup); + } + + if (cbPrev > 0 || pchstrm->StartLine()) + { + // Go ahead and run the initial line-break passes (if any), and then insert + // a line-break at the appropriate place where the new segment will begin. + int cslotToGet = cslotBackup; +LGetMore: + ptman->Pass(0)->ExtendGlyphIDOutput(ptman, pchstrm, OutputStream(0), + -1, 0, klbWordBreak, + cslotToGet, fNeedFinalBreak, m_twsh, pislotFinalBreak); + OutputStream(0)->SetSegMin(cslotBackup); + for (int ipass = 1; ipass <= cpassLB; ipass++) + { + if (cslotToGet > 0) + { + int nRet = kNextPass; + int cslotGot; + ptman->Pass(ipass)->ExtendOutput(ptman, + InputStream(ipass), OutputStream(ipass), + cslotToGet, m_twsh, &nRet, &cslotGot, pislotFinalBreak); + if (nRet != kNextPass) + { + cslotToGet = 1; + goto LGetMore; + } + } + OutputStream(ipass)->SetSegMin(cslotBackup); + } + Assert(OutputStream(cpassLB)->WritePos() >= cslotBackup); + m_cslotPreSeg = cslotBackup; + if (pchstrm->StartLine()) + { + OutputStream(cpassLB)->AppendLineBreak(ptman, pchstrm, m_lbPrevEnd, + (ptman->RightToLeft() ? kdircRlb : kdircLlb), cslotBackup, true, -1); + SetInitialLB(); + m_cslotPreSeg++; + if (cpassLB > 0 && *pislotFinalBreak > -1) + // See corresponding kludge in GrPass::ExtendGlyphIDOutput. + *pislotFinalBreak += 1; + } + else + { + OutputStream(cpassLB)->SetSegMin(cslotBackup); + } + OutputStream(cpassLB)->CalcIndexOffset(ptman); + } + + else + { + Assert(cslotBackup == 0); + OutputStream(0)->SetSegMin(0); + m_cslotPreSeg = 0; + } +} + +/*---------------------------------------------------------------------------------------------- + Reinitialize as we start to generate a new segment. +----------------------------------------------------------------------------------------------*/ +void EngineState::InitForNewSegment(GrTableManager * ptman) +{ + // Set up a fresh batch of slots. + DestroySlotBlocks(); + m_islotblkCurr = -1; + m_islotNext = kSlotBlockSize; + + m_fInitialLB = false; + m_fFinalLB = false; + m_fInsertedLB = false; + m_fExceededSpace = false; + m_fHitHardBreak = false; + m_fRemovedWhtsp = false; + InitPosCache(); + //////m_pgg = pgg; + + for (int ipass = 0; ipass < m_cpass; ipass++) + m_prgzpst[ipass].InitForNewSegment(ipass, ptman->Pass(ipass)->MaxRuleContext()); +} + +/*---------------------------------------------------------------------------------------------- + Calculate the position in stream zero of the given slot in the final stream, + based on the associations. (This will be the same as the position at the end of the + line-break passes, which is really what we want.) + + @param islotFinal - index of the final slot in the final stream +----------------------------------------------------------------------------------------------*/ +int EngineState::TraceStreamZeroPos(int islotFinal, int nTopDir) +{ + GrSlotStream * psstrmFinal = OutputStream(m_cpass - 1); + if (psstrmFinal->WritePos() == 0) + return -1; + + GrSlotState * pslot = psstrmFinal->SlotAt(islotFinal); + + // If we're in the middle of reordered bidi stuff, just use the end of the stream. + if (pslot->DirLevel() > nTopDir) + return -1; + + int islot = pslot->BeforeAssoc(); + if (islot == kPosInfinity || islot < 0) + return -1; + return islot + m_cslotPreSeg; +} + +/*---------------------------------------------------------------------------------------------- + Return the next higher (more relaxed, less desirable) line break weight. +----------------------------------------------------------------------------------------------*/ +LineBrk GrTableManager::IncLineBreak(LineBrk lb) +{ + Assert(lb != klbClipBreak); + return (LineBrk)((int)lb + 1); +} + +/*---------------------------------------------------------------------------------------------- + Return a pointer to a slot that is near the current position in the given stream. + Return NULL if no slots have been output at all. + This is used for initializing features of line-break slots. + @param ipassArg - pass at which to start looking + @param islot - slot to look for; -1 if considering the current position +----------------------------------------------------------------------------------------------*/ +GrSlotState * EngineState::AnAdjacentSlot(int ipassArg, int islot) +{ + int ipass = ipassArg; + while (ipass >= 0) + { + GrSlotStream * psstrm = OutputStream(ipass); + if (psstrm->NumberOfSlots() > 0) + { + if (islot == -1) + { + if (psstrm->AtEnd()) + return psstrm->SlotAt(psstrm->WritePos() - 1); + else + return psstrm->Peek(); + } + else + { + if (psstrm->WritePos() <= islot) + return psstrm->SlotAt(psstrm->WritePos() - 1); + else + return psstrm->SlotAt(islot); + } + } + ipass--; + } + return NULL; +} + +/*---------------------------------------------------------------------------------------------- + Simple access methods. +----------------------------------------------------------------------------------------------*/ +GrEngine * GrTableManager::Engine() +{ + return m_pgreng; +} + +EngineState * GrTableManager::State() +{ + return &m_engst; +} + +GrEngine * EngineState::Engine() +{ + return m_ptman->Engine(); +} + +GrTableManager * EngineState::TableManager() +{ + return m_ptman; +} + +/*---------------------------------------------------------------------------------------------- + Memory management for slots +----------------------------------------------------------------------------------------------*/ + +// standard for pass 0 slots +void EngineState::NewSlot( + gid16 gID, GrFeatureValues fval, int ipass, int ichwSegOffset, int nUnicode, + GrSlotState ** ppslotRet) +{ + NextSlot(ppslotRet); + (*ppslotRet)->Initialize(gID, Engine(), fval, ipass, ichwSegOffset, nUnicode); +} + +// line-break slots +void EngineState::NewSlot( + gid16 gID, GrSlotState * pslotFeat, int ipass, int ichwSegOffset, + GrSlotState ** ppslotRet) +{ + NextSlot(ppslotRet); + (*ppslotRet)->Initialize(gID, Engine(), pslotFeat, ipass, ichwSegOffset); +} + +// for inserting new slots after pass 0 (under-pos and unicode are irrelevant) +void EngineState::NewSlot( + gid16 gID, GrSlotState * pslotFeat, int ipass, + GrSlotState ** ppslotRet) +{ + NextSlot(ppslotRet); + (*ppslotRet)->Initialize(gID, Engine(), pslotFeat, ipass); +} + +// for making a new version of the slot initialized from the old +void EngineState::NewSlotCopy(GrSlotState * pslotCopyFrom, int ipass, + GrSlotState ** ppslotRet) +{ + NextSlot(ppslotRet); + (*ppslotRet)->InitializeFrom(pslotCopyFrom, ipass); +} + +void EngineState::NextSlot(GrSlotState ** ppslotRet) +{ + int cnExtraPerSlot = m_cUserDefn + (m_cCompPerLig * 2) + m_cFeat; + if (m_islotNext >= kSlotBlockSize) + { + // Allocate a new block of slots + GrSlotState * slotblkNew = new GrSlotState[kSlotBlockSize]; + u_intslot * prgnSlotBuf = new u_intslot[kSlotBlockSize * cnExtraPerSlot]; + + m_vslotblk.push_back(slotblkNew); + m_vprgnSlotVarLenBufs.push_back(prgnSlotBuf); + m_islotblkCurr++; + m_islotNext = 0; + Assert((unsigned)m_islotblkCurr == m_vslotblk.size()-1); + } + + *ppslotRet = m_vslotblk[m_islotblkCurr] + m_islotNext; + (*ppslotRet)->GrSlotAbstract::BasicInitialize(m_cUserDefn, m_cCompPerLig, m_cFeat, + (m_vprgnSlotVarLenBufs[m_islotblkCurr] + (m_islotNext * cnExtraPerSlot))); + m_islotNext++; +} + +/*---------------------------------------------------------------------------------------------- + Convert the number of font design units into source device logical units. +----------------------------------------------------------------------------------------------*/ +float GrTableManager::EmToLogUnits(int m) +{ + return m_engst.EmToLogUnits(m); +} + +float EngineState::EmToLogUnits(int m) +{ + if (m == 0) + return 0; + + float xysFontEmSquare; + m_pfont->getFontMetrics(NULL, NULL, &xysFontEmSquare); + + // mEmUnits should be equal to the design units in the font's em square + int mEmUnits = Engine()->GetFontEmUnits(); + if (mEmUnits <= 0) + { + Warn("Failed to obtain font em-units"); + return (float)m; + } + + return GrEngine::GrIFIMulDiv(m, xysFontEmSquare, mEmUnits); +} + +/*---------------------------------------------------------------------------------------------- + Convert the source device coordinates to font design units. +----------------------------------------------------------------------------------------------*/ +int GrTableManager::LogToEmUnits(float xys) +{ + return m_engst.LogToEmUnits(xys); +} + +int EngineState::LogToEmUnits(float xys) +{ + if (xys == 0) + return 0; + + float xysFontEmSquare; + m_pfont->getFontMetrics(NULL, NULL, &xysFontEmSquare); + + // mEmUnits should be equal to the design units in the font's em square + int mEmUnits = Engine()->GetFontEmUnits(); + if (mEmUnits < 0) + { + Warn("Failed to obtain font em-units"); + return (int)xys; + } + + return GrEngine::GrFIFMulDiv(xys, mEmUnits, xysFontEmSquare); +} + +/*---------------------------------------------------------------------------------------------- + Find the coordinates for a given glyph point. + Return true on success, false otherwise. +----------------------------------------------------------------------------------------------*/ +bool GrTableManager::GPointToXY(gid16 chwGlyphID, int nGPoint, float * pxs, float * pys) +{ + return m_engst.GPointToXY(chwGlyphID, nGPoint, pxs, pys); +} + +bool EngineState::GPointToXY(gid16 chwGlyphID, int nGPoint, float * pxs, float * pys) +{ + *pxs = INT_MIN; + *pys = INT_MIN; + Point pointRet; + m_pfont->getGlyphPoint(chwGlyphID, nGPoint, pointRet); + *pxs = pointRet.x; + *pys = pointRet.y; + + return true; +} + +/*---------------------------------------------------------------------------------------------- + Call the justification routines. +----------------------------------------------------------------------------------------------*/ +void GrTableManager::CallJustifier(IGrJustifier * pgjus, int ipassCurr, + float dxUnjustified, float dxJustified, bool fEndLine) +{ + // Indicates the stream the justification routines will read and modify: + m_engst.m_ipassJustCalled = ipassCurr; + + int iGlyphMin = OutputStream(ipassCurr)->SegMin(); + int iGlyphLim = OutputStream(ipassCurr)->SegLimIfKnown(); + if (iGlyphLim == -1) + iGlyphLim = OutputStream(ipassCurr)->WritePos(); + + GrSlotStream * psstrm = OutputStream(ipassCurr); + if (m_pgreng->BasicJustification() && fEndLine) + UnstretchTrailingWs(psstrm, iGlyphLim); + + pgjus->adjustGlyphWidths(m_pgreng, iGlyphMin, iGlyphLim, dxUnjustified, dxJustified); + + // After returning, reading and modifying slot attributes is not permitted: + m_engst.m_ipassJustCalled = -1; +} + +/*---------------------------------------------------------------------------------------------- + For the basic justification approach, remove stretch from the trailing whitespace. +----------------------------------------------------------------------------------------------*/ +void GrTableManager::UnstretchTrailingWs(GrSlotStream * psstrm, int iGlyphLim) +{ + int islot = iGlyphLim - 1; + while (islot >= 0) + { + GrSlotState * pslot = psstrm->SlotAt(islot); + if (pslot->IsLineBreak(LBGlyphID())) + { } // keep looking for the trailing space + else if (pslot->IsSpace(this)) + pslot->SetJStretch(0); // TODO: do this at level 0 + else + // something other than space--quit + return; + islot--; + } +} + +/*---------------------------------------------------------------------------------------------- + Determine how much shrink is permissible. + + This method is really used only for testing the shrink mechanism, since at this time + we don't allow low-end justification to do any shrinking. +----------------------------------------------------------------------------------------------*/ +void GrTableManager::DetermineShrink(IGrJustifier * pgjus, int ipassCurr) +{ + if (m_engst.m_dxsShrinkPossible != GrSlotState::kNotYetSet) + return; + + if (m_engst.m_jmodi != kjmodiCanShrink || pgjus == NULL) + { + m_engst.m_dxsShrinkPossible = 0; + return; + } + + if (ipassCurr != m_ipassJust1 - 1) + return; + + GrSlotStream * psstrm = OutputStream(ipassCurr); + if (!psstrm->FullyWritten()) + return; + + // Normally just answer 0. To test shrinking, turn on the code below. + m_engst.m_dxsShrinkPossible = 0; + +#if TEST_SHRINK + int mTotal = 0; + int mShrink = 0; + int islotLim = psstrm->SegLimIfKnown(); + if (islotLim == -1) + islotLim = psstrm->WritePos(); + for ( ; islotLim >= 0; islotLim--) + { + // Trailing white space is not shrinkable. + GrSlotState * pslot = psstrm->SlotAt(islotLim - 1); + if (pslot->IsLineBreak(LBGlyphID())) + continue; + if (!pslot->IsSpace(this)) + break; + } + int islot; + for (islot = psstrm->SegMin(); islot < islotLim; islot++) + { + GrSlotState * pslot = psstrm->SlotAt(islot); + if (pslot->IsLineBreak(LBGlyphID())) + continue; + mTotal += pslot->AdvanceX(this); + mShrink += pslot->JShrink(); + } + + // Say that we are allowed to shrink up to 10% of the width of the segment. + mShrink = min(mShrink, mTotal/10); + m_engst.m_dxsShrinkPossible = EmToLogUnits(mShrink); +#endif // TEST_SHRINK +} + +/*---------------------------------------------------------------------------------------------- + Return the attribute of the given slot, back to the justification routine. Return + logical units. +----------------------------------------------------------------------------------------------*/ +GrResult EngineState::GetGlyphAttrForJustification(int iGlyph, int jgat, int nLevel, + float * pValueRet) +{ + GrResult res; + int valueRetInt; + switch(jgat) + { + case kjgatWeight: + case kjgatStretchInSteps: + case kjgatBreak: + res = GetGlyphAttrForJustification(iGlyph, jgat, nLevel, &valueRetInt); + *pValueRet = (float)valueRetInt; + return res; + default: + break; + } + + if (m_ipassJustCalled == -1) + return kresUnexpected; + + if (nLevel != 1) + return kresInvalidArg; + + GrSlotStream * psstrm = OutputStream(m_ipassJustCalled); + + if (iGlyph < -1 || iGlyph >= psstrm->WritePos()) + return kresInvalidArg; + + GrSlotState * pslot = psstrm->SlotAt(iGlyph); + + // bool fConvertUnits = true; + + switch(jgat) + { + case kjgatStretch: + *pValueRet = EmToLogUnits(pslot->JStretch()); + break; + case kjgatShrink: + *pValueRet = EmToLogUnits(pslot->JShrink()); + break; + case kjgatStep: + *pValueRet = EmToLogUnits(pslot->JStep()); + break; + default: + return kresNotImpl; + } + return kresOk; +} + +GrResult EngineState::GetGlyphAttrForJustification(int iGlyph, int jgat, int nLevel, + int * pValueRet) +{ + GrResult res; + float valueRetFloat; + switch(jgat) + { + case kjgatStretch: + case kjgatShrink: + case kjgatStep: + res = GetGlyphAttrForJustification(iGlyph, jgat, nLevel, &valueRetFloat); + *pValueRet = GrEngine::RoundFloat(valueRetFloat); + return res; + default: + break; + } + + if (m_ipassJustCalled == -1) + return kresUnexpected; + + if (nLevel != 1) + return kresInvalidArg; + + GrSlotStream * psstrm = OutputStream(m_ipassJustCalled); + + if (iGlyph < -1 || iGlyph >= psstrm->WritePos()) + return kresInvalidArg; + + GrSlotState * pslot = psstrm->SlotAt(iGlyph); + + int mStretch, mStep; + //float xsStretch, xsStep; + + switch(jgat) + { + case kjgatWeight: + *pValueRet = pslot->JWeight(); + break; + case kjgatStretchInSteps: + // ???? should this be calculated in terms of logical units?? + // Probably doesn't matter, since the logical units are floating point, so the + // level of precision is about the same. + mStretch = pslot->JStretch(); + mStep = pslot->JStep(); + if (mStep == 0) + return kresUnexpected; + *pValueRet = int(mStretch / mStep); // round down + break; + //case kjgatStretchInSteps: + // xsStretch = EmToLogUnits(pslot->JStretch()); + // xsStep = EmToLogUnits(pslot->JStep()); + // if (xsStep == 0) + // return kresUnexpected; + // *pValueRet = int(xsStretch / xsStep); // round down + // break; + case kjgatBreak: + *pValueRet = pslot->BreakWeight(); + break; + default: + return kresNotImpl; + } + return kresOk; +} + +/*---------------------------------------------------------------------------------------------- + Set the attribute of the given slot, for the justification routine. +----------------------------------------------------------------------------------------------*/ +GrResult EngineState::SetGlyphAttrForJustification(int iGlyph, int jgat, int nLevel, + float value) +{ + GrResult res; + int valueInt; + switch(jgat) + { + case kjgatWeight: + case kjgatStretchInSteps: + case kjgatBreak: + valueInt = (int)value; + res = SetGlyphAttrForJustification(iGlyph, jgat, nLevel, valueInt); + return res; + default: + break; + } + + if (m_ipassJustCalled == -1) + return kresUnexpected; + + if (nLevel != 1) + return kresInvalidArg; + + GrSlotStream * psstrm = OutputStream(m_ipassJustCalled); + + if (iGlyph < -1 || iGlyph >= psstrm->WritePos()) + return kresInvalidArg; + + GrSlotState * pslot = psstrm->SlotAt(iGlyph); + + int mValue = LogToEmUnits(value); + mValue = min(mValue, 0xFFFF); // truncate to an unsigned short + + // Review: do we really want to allow them to set stretch/shrink/step/weight? The normal + // thing for them to do is set width. + switch(jgat) + { + case kjgatStretch: + pslot->SetJStretch(mValue); + break; + case kjgatShrink: + pslot->SetJShrink(mValue); + break; + case kjgatStep: + pslot->SetJStep(mValue); + break; + case kjgatWidth: + pslot->SetJWidth(mValue); + break; + default: + return kresNotImpl; + } + return kresOk; +} + +GrResult EngineState::SetGlyphAttrForJustification(int iGlyph, int jgat, int nLevel, + int value) +{ + GrResult res; + float valueFloat; + switch(jgat) + { + case kjgatStretch: + case kjgatShrink: + case kjgatStep: + case kjgatWidth: + valueFloat = (float)value; + res = SetGlyphAttrForJustification(iGlyph, jgat, nLevel, valueFloat); + return res; + default: + break; + } + + if (m_ipassJustCalled == -1) + return kresUnexpected; + + if (nLevel != 1) + return kresInvalidArg; + + GrSlotStream * psstrm = OutputStream(m_ipassJustCalled); + + if (iGlyph < -1 || iGlyph >= psstrm->WritePos()) + return kresInvalidArg; + + GrSlotState * pslot = psstrm->SlotAt(iGlyph); + + int mValue, cSteps; + switch(jgat) + { + case kjgatWeight: + pslot->SetJWeight(value); + break; + case kjgatWidthInSteps: + cSteps = value; + mValue = pslot->JStep(); + if (mValue == 0) + return kresUnexpected; + pslot->SetJWidth(mValue * cSteps); + break; + default: + return kresNotImpl; + } + return kresOk; +} + +//:>-------------------------------------------------------------------------------------------- +//:> Functions to forward to the engine. +//:>------------------------------------------------------------------------------------------*/ +gid16 GrTableManager::GetGlyphIDFromUnicode(int nUnicode) +{ + return m_pgreng->GetGlyphIDFromUnicode(nUnicode); +} + +/*--------------------------------------------------------------------------------------------*/ + +gid16 GrTableManager::ActualGlyphForOutput(utf16 chw) +{ + return m_pgreng->ActualGlyphForOutput(chw); +} + +/*--------------------------------------------------------------------------------------------*/ + +GrGlyphTable * GrTableManager::GlyphTable() +{ + return m_pgreng->GlyphTable(); +} + +/*--------------------------------------------------------------------------------------------*/ + +gid16 GrTableManager::LBGlyphID() +{ + return m_pgreng->LBGlyphID(); +} + +/*--------------------------------------------------------------------------------------------*/ + +gid16 GrTableManager::GetClassGlyphIDAt(int nClass, int nIndex) +{ + return m_pgreng->GetClassGlyphIDAt(nClass, nIndex); +} + +/*--------------------------------------------------------------------------------------------*/ + +int GrTableManager::GetIndexInGlyphClass(int nClass, gid16 chwGlyphID) +{ + return m_pgreng->GetIndexInGlyphClass(nClass, chwGlyphID); +} + +/*--------------------------------------------------------------------------------------------*/ + +size_t GrTableManager::NumberOfGlyphsInClass(int nClass) +{ + return m_pgreng->NumberOfGlyphsInClass(nClass); +} + +/*--------------------------------------------------------------------------------------------*/ + +void GrTableManager::SetSlotAttrsFromGlyphAttrs(GrSlotState * pslot) +{ + m_pgreng->SetSlotAttrsFromGlyphAttrs(pslot); +} + +/*--------------------------------------------------------------------------------------------*/ + +int GrTableManager::NumFeat() +{ + return m_pgreng->NumFeat(); +} + +/*--------------------------------------------------------------------------------------------*/ + +int GrTableManager::DefaultForFeatureAt(int ifeat) +{ + return m_pgreng->DefaultForFeatureAt(ifeat); +} + +/*--------------------------------------------------------------------------------------------*/ + +void GrTableManager::DefaultsForLanguage(isocode lgcode, + std::vector<featid> & vnFeats, std::vector<int> & vnValues) +{ + return m_pgreng->DefaultsForLanguage(lgcode, vnFeats, vnValues); +} + +/*--------------------------------------------------------------------------------------------*/ + +GrFeature * GrTableManager::FeatureWithID(featid nID, int * pifeat) +{ + return m_pgreng->FeatureWithID(nID, pifeat); +} + +/*--------------------------------------------------------------------------------------------*/ + +GrFeature * GrTableManager::Feature(int ifeat) +{ + return m_pgreng->Feature(ifeat); +} + +/*--------------------------------------------------------------------------------------------*/ + +bool GrTableManager::RightToLeft() +{ + if (m_engst.WhiteSpaceOnly()) + return m_engst.m_fParaRtl; + else + return m_pgreng->RightToLeft(); +} + +/*--------------------------------------------------------------------------------------------*/ + +int GrTableManager::TopDirectionLevel() +{ + return m_pgreng->TopDirectionLevel(); +} + +/*--------------------------------------------------------------------------------------------*/ + +float GrTableManager::VerticalOffset() +{ + return m_pgreng->VerticalOffset(); +} + +/*--------------------------------------------------------------------------------------------*/ + +int GrTableManager::NumUserDefn() +{ + return m_pgreng->NumUserDefn(); +} + +/*--------------------------------------------------------------------------------------------*/ + +int GrTableManager::NumCompPerLig() +{ + return m_pgreng->NumCompPerLig(); +} + +/*--------------------------------------------------------------------------------------------*/ + +int GrTableManager::ComponentIndexForGlyph(gid16 chwGlyphID, int nCompID) +{ + return m_pgreng->ComponentIndexForGlyph(chwGlyphID, nCompID); +} + +/*--------------------------------------------------------------------------------------------*/ + +int GrTableManager::GlyphAttrValue(gid16 chwGlyphID, int nAttrID) +{ + return m_pgreng->GlyphAttrValue(chwGlyphID, nAttrID); +} + +/*--------------------------------------------------------------------------------------------*/ + +bool GrTableManager::LoggingTransduction() +{ + return m_fLogging; + //return m_pgreng->LoggingTransduction(); +} + +/*---------------------------------------------------------------------------------------------- + Calculate the positions of the glyphs up to the given slot, within the output of the given + pass. If the slot is NULL, go all the way to the end of what has been generated. + + @param ipass - index of pass needing positioning; normally this will be the + final pass, but it could be another if positions are + requested by the rules themselves + @param pslotLast - last slot that needs to be positioned, or NULL + @param pxsWidth - return the total width used so far + @param psxVisibleWidth - return the visible width so far + + MOVE to EngineState +----------------------------------------------------------------------------------------------*/ +void GrTableManager::CalcPositionsUpTo(int ipass, GrSlotState * pslotLast, + float * pxsWidth, float * pxsVisibleWidth) +{ + Assert(ipass >= m_ipassPos1 - 1); + + int isstrm = ipass; + GrSlotStream * psstrm = OutputStream(isstrm); + Assert(psstrm->GotIndexOffset()); + if (psstrm->WritePos() <= psstrm->IndexOffset()) + { + Assert(psstrm->WritePos() == 0); + *pxsWidth = 0; + *pxsVisibleWidth = 0; + return; + } + if (!pslotLast) + { + pslotLast = psstrm->SlotAt(psstrm->WritePos() - 1); + } + Assert(pslotLast); + + // First set positions of all base slots. + + int islot = psstrm->IndexOffset(); + GrSlotState * pslot; + float xs = 0; + float ys = VerticalOffset(); + *pxsWidth = 0; + *pxsVisibleWidth = 0; + + bool fLast = false; + bool fLastBase = false; + + bool fFakeItalic = m_pgreng->FakeItalic(); + float fakeItalicRatio = 0; + if (fFakeItalic) + fakeItalicRatio = State()->GetFont()->fakeItalicRatio(); + bool fBasicJust = m_pgreng->BasicJustification(); + + // Figure out how to know when to stop. + // We need to calculate up to the base of the last leaf slot, if it happens + // to be later in the stream than the last actual slot passed in. + if (!psstrm->HasSlotAtPosPassIndex(pslotLast->AttachRootPosPassIndex())) + return; + GrSlotState * pslotLastBase = pslotLast->Base(psstrm); + + if (ipass == m_cpass - 1 && m_engst.m_islotPosNext > -1) + { + // For final pass, initialize from cache of previous calculations. + islot = m_engst.m_islotPosNext; + xs = m_engst.m_xsPosXNext; + ys = m_engst.m_ysPosYNext; + *pxsWidth = m_engst.m_dxsTotWidthSoFar; + *pxsVisibleWidth = m_engst.m_dxsVisWidthSoFar; + + if (psstrm->SlotsPresent() <= islot) + return; + + if (pslotLastBase->PosPassIndex() == GrSlotAbstract::kNotYetSet) + { + Assert(false); // I think this case is handled above + return; // can't position this slot yet; its base has not been processed + } + + if (pslotLastBase->PosPassIndex() + psstrm->IndexOffset() < islot) + { + fLastBase = true; + if (pslotLast == pslotLastBase) + fLast = true; + } + } + + std::vector<GrSlotState *> vpslotAttached; + + bool fRtl = RightToLeft(); + + while (!fLast || !fLastBase) + { + Assert(islot < psstrm->SlotsPresent()); + + pslot = (isstrm == ipass) ? psstrm->SlotAt(islot) : psstrm->OutputSlotAt(islot); + + if (!pslot->IsBase()) + { + // This slot is attached to another; it will be positioned strictly + // relative to that one. This happens in the loop below. + vpslotAttached.push_back(pslot); + } + else + { + // Base character. + + bool fLB = pslot->IsLineBreak(LBGlyphID()); + if (InternalJustificationMode() == kjmodiJustify && fBasicJust + && ipass == m_cpass - 1 && !fLB) + { + m_engst.AddJWidthToAdvance(psstrm, &pslot, islot, &pslotLast, &pslotLastBase); + } + + // Make sure the metrics are the complete ones. + pslot->CalcCompositeMetrics(this, psstrm, kPosInfinity, true); + + float xsInc = pslot->GlyphXOffset(psstrm, fakeItalicRatio); + float ysInc = pslot->GlyphYOffset(psstrm); + float xsAdvX = pslot->ClusterAdvWidth(psstrm); + float ysAdvY = (fLB) ? + 0 : + EmToLogUnits(pslot->AdvanceY(this)); + + if (fRtl) + { + xs -= xsAdvX; + ys -= ysAdvY; + pslot->SetXPos(xs + xsInc); + pslot->SetYPos(ys + ysInc); + } + else + { + pslot->SetXPos(xs + xsInc); + pslot->SetYPos(ys + ysInc); + xs += xsAdvX; + ys += ysAdvY; + } + + *pxsWidth = max(*pxsWidth, fabsf(xs)); + if (!IsWhiteSpace(pslot)) + *pxsVisibleWidth = *pxsWidth; + + if (isstrm == m_cpass - 1) + { + // For the final output pass, cache the results of the calculation so far. + // Only do this for slots that have actually been processed by the final + // pass, because others may have intermediate values that may be changed + // later. + m_engst.m_islotPosNext = pslotLastBase->PosPassIndex() + psstrm->IndexOffset() + 1; + m_engst.m_xsPosXNext = xs; + m_engst.m_ysPosYNext = ys; + m_engst.m_dxsTotWidthSoFar = *pxsWidth; + m_engst.m_dxsVisWidthSoFar = *pxsVisibleWidth; + } + } + + // Need to have calculated both the last leaf and the last base (if they happen to + // be different); if so, we're done. + if (pslot == pslotLast) + fLast = true; + if (pslot == pslotLastBase) + fLastBase = true; + + islot++; + } + + Assert(fLast); + Assert(fLastBase); + + // Now set positions of non-base slots, relative to their bases. + + for (size_t ipslot = 0; ipslot < vpslotAttached.size(); ipslot++) + { + GrSlotState * pslot = vpslotAttached[ipslot]; + GrSlotState * pslotBase = pslot->Base(psstrm); + if (pslotBase->XPosition() == kNegInfinity || pslotBase->YPosition() == kNegInfinity) + { + Assert(false); + continue; + } + float xsCluster = pslotBase->XPosition() - pslotBase->GlyphXOffset(psstrm, fakeItalicRatio); + float ysCluster = pslotBase->YPosition() - pslotBase->GlyphYOffset(psstrm); + float xsInc = pslot->GlyphXOffset(psstrm, fakeItalicRatio); + float ysInc = pslot->GlyphYOffset(psstrm); + pslot->SetXPos(xsCluster + xsInc); + pslot->SetYPos(ysCluster + ysInc); + + // My theory is that we don't need to adjust *pxsWidth here, because the width of + // any non-base slots should be factored into the advance width of their cluster + // base, which was handled above. + } +} + +/*---------------------------------------------------------------------------------------------- + Return true if the given slot is white space. +----------------------------------------------------------------------------------------------*/ +bool GrTableManager::IsWhiteSpace(GrSlotState * pslot) +{ + if (pslot->IsLineBreak(LBGlyphID())) + return true; + return pslot->IsSpace(this); +} + +/*---------------------------------------------------------------------------------------------- + When doing basic justification, apply the value of justify.width to the advance width. + This happens during the final positioning routine. +----------------------------------------------------------------------------------------------*/ +void EngineState::AddJWidthToAdvance(GrSlotStream * psstrm, GrSlotState ** ppslot, int islot, + GrSlotState ** ppslotLast, GrSlotState ** ppslotLastBase) +{ + Assert(psstrm->OutputOfPass() == m_cpass - 1); // only used for final stream + if ((*ppslot)->JWidth() > 0) + { + if ((*ppslot)->PassModified() != m_cpass - 1) + { + // Replace the slot with a new slot, so it gets marked properly with the + // pass in which it was modified. (Otherwise the debug log gets screwed up.) + GrSlotState * pslotNew; + NewSlotCopy((*ppslot), m_cpass - 1, &pslotNew); + psstrm->PutSlotAt(pslotNew, islot); + if (*ppslot == *ppslotLast) + *ppslotLast = pslotNew; + if (*ppslot == *ppslotLastBase) + *ppslotLastBase = pslotNew; + *ppslot = pslotNew; + } + (*ppslot)->AddJWidthToAdvance(TableManager()); + } +} + + +//:>******************************************************************************************** +//:> Debuggers and utilities +//:>******************************************************************************************** + +//:Ignore +#if 0 +void GrTableManager::TempDebug() +{ + for (int ipass = 0; ipass < m_cpass; ++ipass) + { + int wpos = OutputStream(ipass)->WritePos(); + int rpos = OutputStream(ipass)->ReadPos(); + GrSlotState * wslot = OutputStream(ipass)->LastSlotWritten(); + GrSlotState * rslot = OutputStream(ipass)->LastSlotRead(); + + } +} +#endif +/*---------------------------------------------------------------------------------------------- + Given a character index in the original string, locate the corresponding line-break slot + in the final surface stream. The character index must be one before which there is a + line-break (seg lim), or the beginning or the end of the string. + TODO SharonC: fix the bugs +----------------------------------------------------------------------------------------------*/ +int GrTableManager::SurfaceLineBreakSlot(int ichw, GrCharStream * pchstrm, bool fInitial) +{ + if (ichw == 0) + { + Assert(fInitial); + return -1; // no initial line break + } + if (ichw == pchstrm->Lim()) + { + Assert(!fInitial); + return -1; // no terminating line break + } + + int islotIn = pchstrm->SegOffset(ichw); + Assert(islotIn >= 0); + islotIn += (fInitial? m_engst.m_cslotPreSeg - 1: m_engst.m_cslotPreSeg); + + gid16 chwLBGlyphID = LBGlyphID(); + + // Now islotIn is the position in stream 0; + + for (int ipass = 1; ipass < m_cpass; ++ipass) + { + int islotChunkMin; + int islotChunkLim; + GrSlotStream * psstrmOut = OutputStream(ipass); + + GrSlotStream * psstrmIn = InputStream(ipass); + if (fInitial) + islotIn = max(islotIn, psstrmIn->PreChunk()); + int islotT = psstrmIn->ChunkInNextMin(islotIn); + islotChunkMin = psstrmIn->ChunkInNext(islotT); + islotChunkMin = (islotChunkMin == -1)? 0: islotChunkMin; + + islotT = psstrmIn->ChunkInNextLim(islotIn); + Assert(islotT <= psstrmIn->ReadPos()); + if (islotT == psstrmIn->ReadPos()) + islotChunkLim = psstrmOut->WritePos(); + else + islotChunkLim = psstrmIn->ChunkInNext(islotT); + + int islotOut; + for (islotOut = islotChunkMin; islotOut < islotChunkLim; ++islotOut) + { + gid16 chw = psstrmOut->GlyphIDAt(islotOut); + if (chw == chwLBGlyphID) + break; + } + Assert(psstrmOut->GlyphIDAt(islotOut) == chwLBGlyphID); + + islotIn = islotOut; + } + return islotIn; +} + +/*---------------------------------------------------------------------------------------------- + Generate a debugger string showing the chunk boundaries for the given stream. +----------------------------------------------------------------------------------------------*/ +std::wstring GrTableManager::ChunkDebugString(int ipass) +{ + GrSlotStream * psstrm = OutputStream(ipass); + std::wstring stuRet; + //utf16 chwN = '\\'; + //utf16 chwR = '^'; + + gid16 chwLBGlyphID = LBGlyphID(); + + for (int islot = 0; islot < psstrm->WritePos(); islot++) + { + if (psstrm->ChunkInPrev(islot) != -1) + { + if (psstrm->ChunkInNext(islot) != -1) + stuRet.append(L">"); + else + stuRet.append(L"\\"); + } + else if (psstrm->ChunkInNext(islot) != -1) + stuRet.append(L"/"); + else + stuRet.append(L" "); + +// if (islot == psstrm->ReadPos()) +// stuRet.Append(&chwR, 1); + + wchar_t chw = psstrm->SlotAt(islot)->GlyphID(); + if (chw == chwLBGlyphID) + stuRet.append(L"#"); + else + stuRet.append(&chw, 1); + } + return stuRet; +} + +} // namespace gr + +//:End Ignore diff --git a/Build/source/libs/graphite-engine/src/segment/GrTableManager.h b/Build/source/libs/graphite-engine/src/segment/GrTableManager.h new file mode 100644 index 00000000000..b19ca8deb5a --- /dev/null +++ b/Build/source/libs/graphite-engine/src/segment/GrTableManager.h @@ -0,0 +1,554 @@ +/*--------------------------------------------------------------------*//*:Ignore this sentence. +Copyright (C) 1999, 2001 SIL International. All rights reserved. + +Distributable under the terms of either the Common Public License or the +GNU Lesser General Public License, as specified in the LICENSING.txt file. + +File: GrTableManager.h +Responsibility: Sharon Correll +Last reviewed: Not yet. + +Description: + The GrTableManager class. +----------------------------------------------------------------------------------------------*/ +#ifdef _MSC_VER +#pragma once +#endif +#ifndef GR_TABLEMAN_INCLUDED +#define GR_TABLEMAN_INCLUDED + +#include <cassert> +//:End Ignore + +namespace gr +{ + +class Font; + +/*---------------------------------------------------------------------------------------------- + There is a single instance of Engine State that keeps track of the state of the processing + for a single segment generation. + + Hungarian: engst +----------------------------------------------------------------------------------------------*/ +class EngineState +{ + friend class GrTableManager; +public: + EngineState(); + void Initialize(GrEngine *, GrTableManager *); + + ~EngineState(); + void DestroySlotBlocks(); + + void InitForNewSegment(GrTableManager * ptman); + + void CreateEmpty(); + + GrEngine * Engine(); + GrTableManager * TableManager(); + + void CreateSlotStreams(); + + GrSlotStream * InputStream(int ipass) + { + Assert(ipass > 0); // the char stream serves as input to the zeroth pass + return m_prgpsstrm[ipass - 1]; + } + + // Return the stream that serves as output to the given pass. + GrSlotStream * OutputStream(int ipass) + { + Assert(ipass < m_cpass); + return m_prgpsstrm[ipass]; + } + + GrSlotState * AnAdjacentSlot(int ipassArg, int islotArg); + + GrResult GetGlyphAttrForJustification(int iGlyph, int jgat, int nLevel, float * pxysValueRet); + GrResult GetGlyphAttrForJustification(int iGlyph, int jgat, int nLevel, int * pxysValueRet); + GrResult SetGlyphAttrForJustification(int iGlyph, int jgat, int nLevel, float xysValue); + GrResult SetGlyphAttrForJustification(int iGlyph, int jgat, int nLevel, int xysValue); + + enum { kSlotBlockSize = 50 }; // number of slots to allocate in a block + + void InitPosCache() + { + m_islotPosNext = -1; + m_xsPosXNext = 0; + m_ysPosYNext = 0; + m_dxsTotWidthSoFar = 0; + m_dxsVisWidthSoFar = 0; + } + + void SetFont(Font * pfont) + { + m_pfont = pfont; + } + Font * GetFont() + { + return m_pfont; + } + + bool StartLineContext() + { + return m_fStartLineContext; + } + bool EndLineContext() + { + return m_fEndLineContext; + } + void SetStartLineContext(bool f) + { + m_fStartLineContext = f; + } + void SetEndLineContext(bool f) + { + m_fEndLineContext = f; + } + + void SetInitialLB(bool f = true) + { + m_fInitialLB = f; + } + + void SetFinalLB(bool f = true) + { + m_fFinalLB = f; + } + + void SetInsertedLB(bool f = true) + { + m_fInsertedLB = f; + } + + bool HasInitialLB() + { + return m_fInitialLB; + } + bool HasInsertedLB() + { + return m_fInsertedLB; + } + bool HasFinalLB() + { + return m_fFinalLB; + } + + void SetExceededSpace(bool f = true) + { + m_fExceededSpace = f; + } + + bool ExceededSpace() + { + return m_fExceededSpace; + } + + void SetHitHardBreak(bool f = true) + { + m_fHitHardBreak = f; + } + + bool HitHardBreak() + { + return m_fHitHardBreak; + } + + void SetRemovedTrWhiteSpace(bool f = true) + { + m_fRemovedWhtsp = f; + } + + bool RemovedTrWhiteSpace() + { + return m_fRemovedWhtsp; + } + + bool WhiteSpaceOnly() + { + return (m_twsh == ktwshOnlyWs); + } + + bool ParaRightToLeft() + { + return m_fParaRtl; + } + + //int PrevPassMaxBackup(int ipass) + //{ + // if (ipass == 0) + // return 0; + // return Pass(ipass - 1)->MaxBackup(); + //} + + DirCode InitialStrongDir() + { + return m_dircInitialStrongDir; + } + DirCode InitialTermDir() + { + return m_dircInitialTermDir; + } + + void InitializeStreams(GrTableManager * ptman, GrCharStream * pchstrm, + int cbPrev, byte * pbPrevSegDat, bool fNeedFinalBreak, int * pislotFinalBreak); + int LbSlotToSegLim(int islot, GrCharStream * pchstrm, int cpassLB); + int TraceStreamZeroPos(int islotFinal, int nTopDir); + + float EmToLogUnits(int m); + int LogToEmUnits(float xys); + bool GPointToXY(gid16 chwGlyphID, int nGPoint, float * xs, float * ys); + + void AddJWidthToAdvance(GrSlotStream * psstrm, GrSlotState ** ppslot, int islot, + GrSlotState ** ppslotLast, GrSlotState ** ppslotLastBase); + + // Memory management for slots: + void NewSlot(gid16 gID, GrFeatureValues fval, int ipass, int ichwSegOffset, int nUnicode, + GrSlotState **); + void NewSlot(gid16 gID, GrSlotState * pslotFeat, int ipass, int ichwSegOffset, + GrSlotState **); + void NewSlot(gid16 gID, GrSlotState * pslotFeat, int ipass, GrSlotState **); + void NewSlotCopy(GrSlotState * pslotCopyFrom, int ipass, + GrSlotState ** pslotRet); +protected: + void NextSlot(GrSlotState ** ppslot); + +protected: + GrTableManager * m_ptman; + + int m_cFeat; + int m_cCompPerLig; + int m_cUserDefn; + + int m_jmodi; // justification mode + int m_ipassJustCalled; // pass after which justification routine was called; + // -1 if we are not in the midst of interacting with the + // GrJustifier + + // Pointer to font + Font * m_pfont; + + bool m_fStartLineContext; // true if there was a rule that ran over the initial LB + bool m_fEndLineContext; // true if there was a rule that ran over the final LB + + // the number of slots inserted in the first stream before the official beginning + // of the segment, including any initial line break + int m_cslotPreSeg; + + // List of allocated GrSlotStates; hungarian slotblk = prgslot + std::vector<GrSlotState *> m_vslotblk; + std::vector<u_intslot *> m_vprgnSlotVarLenBufs; // corresponding var-length blocks + int m_islotblkCurr; + int m_islotNext; + + // Directionality codes from previous segment; these are used by the bidi algorithm + // at the initial line-break character. + DirCode m_dircInitialStrongDir; + DirCode m_dircInitialTermDir; + + LineBrk m_lbPrevEnd; + + // All of the following 3 are true only when there is an actual LB glyph in the stream + bool m_fInitialLB; // is there is an initial LB (an actual LB glyph--in all streams) + bool m_fFinalLB; // is there is a final LB (in all streams--true when we've hit + // the end of the input and end-of-line is true) + bool m_fInsertedLB; // is there is a final LB inserted in the output of LB pass + // (true when we've backtracked) + + bool m_fExceededSpace; // true if we backtracked due to exceeding space; false if we + // haven't backtracked, or we did so for some other reason + // (eg, omitting trailing whitespace) + + bool m_fHitHardBreak; // true if we encountered a hard-break in the input stream + // (before the natural end of the segment) + + bool m_fRemovedWhtsp; // true if we've hit trailing whitespace characters that we've + // removed + + TrWsHandling m_twsh; // white-space handling + bool m_fParaRtl; // paragraph direction + + float m_dxsShrinkPossible; + + // The final positioning pass is where we are going to do most of the positioning. + // Cache the intermediate results so we don't have to calculate from the beginning of + // the segment every time. + int m_islotPosNext; // where to continue calculating positions in final output stream + // (one past the last base that was calculated) + float m_xsPosXNext; + float m_ysPosYNext; + float m_dxsTotWidthSoFar; + float m_dxsVisWidthSoFar; + + int m_cpass; // number of passes + + // array of pass-state objects + PassState * m_prgzpst; + + // array of slot streams which are output of passes (input of zeroth + // pass is the character stream, not a slot stream): + GrSlotStream ** m_prgpsstrm; + // For instance, a simple set of passes might look like this: + // CharStream + // GlyphGenPass pass 0 + // SlotStream stream 0 + // SubPass pass 1 + // SlotStream stream 1 + // PosPass pass 2 + // SlotStream stream 2 + // + // m_cpass = 3 + +}; // end of class EngineState + +/*---------------------------------------------------------------------------------------------- + There is a single instance of GrTableManager that handle the interactions between + passes, including the demand-pull mechanism. + + Hungarian: tman +----------------------------------------------------------------------------------------------*/ + +class GrTableManager { + +public: + // Constructor & destructor: + GrTableManager(GrEngine * pgreng) + : m_cpass(0), + m_fBidi(false), + m_prgppass(NULL), + m_pgreng(pgreng) + { + Assert(pgreng); + } + + ~GrTableManager(); + + bool CreateAndReadPasses(GrIStream & grstrm, int fxdSilfVersion, int fxdRuleVersion, + int cpassFont, long lSubTableStart, int * rgnPassOffsets, + int ipassSub1Font, int ipassPos1Font, int ipassJust1Font, byte ipassPostBidiFont); + + void CreateEmpty(); + + GrEngine * Engine(); + EngineState * State(); + + void Run(Segment * psegNew, Font * pfont, GrCharStream * pchstrm, + IGrJustifier * pgjus, int jmodi, + LayoutEnvironment & layout, + int ichStop, float dxWidth, float dxUnjustified, + bool fNeedFinalBreak, bool fMoreText, int ichFontLim, + bool fInfiniteWidth, bool fWidthIsCharCount, + int ichwCallerBtLim, + int nDirDepth, SegEnd estJ); + + GrPass* Pass(int ipass) + { + Assert(ipass >= 0); + return m_prgppass[ipass]; + } + + int NumberOfPasses() + { + return m_cpass; + } + + int NumberOfLbPasses() + { + return m_cpassLB; + } + + bool HasBidiPass() + { + return m_fBidi; + } + + int FirstPosPass() + { + return m_ipassPos1; + } + + void StorePassStates(PassState * rgzpst); + + // Return the stream that serves as input to the given pass. + GrSlotStream * InputStream(int ipass) + { + return m_engst.InputStream(ipass); + } + GrSlotStream * OutputStream(int ipass) + { + return m_engst.OutputStream(ipass); + } + + void UnwindAndReinit(int islotNewBreak); + +public: + int InternalJustificationMode() + { + return m_engst.m_jmodi; + } + + bool ShouldLogJustification() + { + return (m_engst.m_jmodi != kjmodiNormal); + } + + int PrevPassMaxBackup(int ipass) + { + if (ipass == 0) + return 0; + return Pass(ipass - 1)->MaxBackup(); + } + + // Forward to the engine itself + gid16 GetGlyphIDFromUnicode(int nUnicode); + gid16 ActualGlyphForOutput(utf16 chwGlyphID); + GrGlyphTable * GlyphTable(); + + gid16 LBGlyphID(); + + gid16 GetClassGlyphIDAt(int nClass, int nIndex); + int GetIndexInGlyphClass(int nClass, gid16 chwGlyphID); + size_t NumberOfGlyphsInClass(int nClass); + + void SetSlotAttrsFromGlyphAttrs(GrSlotState * pslot); + + int NumFeat(); + int DefaultForFeatureAt(int ifeat); + GrFeature * FeatureWithID(featid nID, int * pifeat); + GrFeature * Feature(int ifeat); + void DefaultsForLanguage(isocode lgcode, + std::vector<featid> & vnFeats, std::vector<int> & vnValues); + + bool RightToLeft(); + int TopDirectionLevel(); + + float VerticalOffset(); + + int NumUserDefn(); + int NumCompPerLig(); + + int ComponentIndexForGlyph(gid16 chwGlyphID, int nCompID); + int GlyphAttrValue(gid16 chwGlyphID, int nAttrID); + + // --- + + bool LoggingTransduction(); + + float EmToLogUnits(int m); + int LogToEmUnits(float xys); + bool GPointToXY(gid16 chwGlyphID, int nGPoint, float * xs, float * ys); + + void CalcPositionsUpTo(int ipass, GrSlotState * pslotLast, + float * pxsWidth, float * pxsVisibleWidth); + + void InitPosCache() + { + m_engst.InitPosCache(); + } + + bool IsWhiteSpace(GrSlotState * pslot); + + // For transduction logging: + //bool WriteTransductionLog(GrCharStream * pchstrm, Segment * psegRet, + // int cbPrev, byte * pbPrevSegDat, byte * pbNextSegDat, int * pcbNextSegDat); + //bool WriteAssociationLog(GrCharStream * pchstrm, Segment * psegRet); + bool WriteTransductionLog(std::ostream * pstrmLog, + GrCharStream * pchstrm, Segment * psegRet, int cbPrevSetDat, byte * pbPrevSegDat); + bool WriteAssociationLog(std::ostream * pstrmLog, + GrCharStream * pchstrm, Segment * psegRet); +#ifdef TRACING + void WriteXductnLog(std::ostream & strmOut, GrCharStream * pchstrm, Segment * psegRet, + int cbPrevSegDat, byte * pbPrevSegDat); + //bool LogFileName(std::string & staFile); + void LogUnderlying(std::ostream & strmOut, GrCharStream * pchstrm, int cchwBackup); + void LogPass1Input(std::ostream & strmOut); + void LogPassOutput(std::ostream & strmOut, int ipass, int cslotSkipped); + void LogAttributes(std::ostream & strmOut, int ipass, bool fJustWidths = false); + void LogFinalPositions(std::ostream & strmOut); + void LogUnderlyingHeader(std::ostream & strmOut, int ichwMin, + int ichwLim, int cchwBackup, int * prgichw16bit); + void LogSlotHeader(std::ostream & strmOut, int cslot, + int cspPerSlot, int cspLeading, int islotMin = 0); + void LogSlotGlyphs(std::ostream & strmOut, GrSlotStream * psstrm); + void SlotAttrsModified(int ipass, bool * rgfMods, bool fPreJust, int * pccomp, int *pcassoc); + void LogInTable(std::ostream & strmOut, int n); + void LogInTable(std::ostream & strmOut, float n); + void LogHexInTable(std::ostream & strmOut, gid16 chw, bool fPlus = false); + void LogDirCodeInTable(std::ostream & strmOut, int dirc); + void LogBreakWeightInTable(std::ostream & strmOut, int lb); +#endif // TRACING + +protected: + void InitNewSegment(Segment * psegNew, Font * pfont, GrCharStream * pchstrm, IGrJustifier * pgjus, + int islotStream0Break, int islotSurfaceBreak, bool fStartLine, bool fEndLine, int ichFontLim, + LineBrk lbEnd, SegEnd est, int * dichSegLen); + + void InitSegmentAsEmpty(Segment * psegNew, Font * pfont, GrCharStream * pchstrm, + bool fStartLine, bool fEndLine); + + void InitSegmentToDelete(Segment * psegNew); + + int ChunkInPrev(int ipass, int islot, GrCharStream * pchstrm); + + bool Backtrack(int * islotPrevLB, + LineBrk * plbPref, LineBrk lbMax, TrWsHandling, bool fMoreText, + int ichwCallerBtLim, LineBrk * plbFound); + + LineBrk IncLineBreak(LineBrk lb); + + void InitializeForNextSeg(Segment* pseg, + int islotStream0Break, int islotSurfaceBreak, LineBrk lbEnd, + bool fNextSegNeedsContext, GrCharStream * pchstrm); + + void RecordAssocsAndOutput(Font * pfont, + Segment * pseg, bool fWidthIsCharCount, + TrWsHandling twsh, bool fParaRtl, int nDirDepth); + + void CalculateAssociations(Segment * pseg, int csloutSurface); + void AdjustAssocsForOverlaps(Segment * pseg); + + void UnstretchTrailingWs(GrSlotStream * psstrm, int iGlyphLim); + void CallJustifier(IGrJustifier * pgjus, int ipassCurr, float dxUnjustified, float dxJustified, + bool fEndLine); + + void DetermineShrink(IGrJustifier * pgjus, int ipass); + +protected: + // Instance variables: + + int m_cpass; // number of passes; zero-th pass is Unicode character -> glyph ID mapping; + // Note thatwe always have one positioning pass to measure things even + // if there are no tables to run. + + int m_cpassLB; // number of line-break passes + int m_ipassPos1; // first positioning pass + int m_ipassJust1; // first justification pass (== m_ipassPos1 if none) + + bool m_fBidi; // is there a bidi pass? + + // array of passes: + GrPass ** m_prgppass; + + // Pointer back to Graphite engine, to give access to global constants and tables. + GrEngine * m_pgreng; + + // State of processing. + EngineState m_engst; + + bool m_fLogging; + +public: + // For test procedures: + void SetUpTest(std::wstring); + +protected: + void TempDebug(); + int SurfaceLineBreakSlot(int ichw, GrCharStream * pchstrm, bool fInitial); + std::wstring ChunkDebugString(int ipass); +}; // end of class GrTableManager + +} // namespace gr + + +#endif // !GR_TABLEMAN_INCLUDED diff --git a/Build/source/libs/graphite-engine/src/segment/Main.h b/Build/source/libs/graphite-engine/src/segment/Main.h new file mode 100644 index 00000000000..def3d771c7b --- /dev/null +++ b/Build/source/libs/graphite-engine/src/segment/Main.h @@ -0,0 +1,175 @@ +/*--------------------------------------------------------------------*//*:Ignore this sentence. +Copyright (C) 1999, 2001 SIL International. All rights reserved. + +Distributable under the terms of either the Common Public License or the +GNU Lesser General Public License, as specified in the LICENSING.txt file. + +File: Main.h +Responsibility: Sharon Correll +Last reviewed: Not yet. + +Description: + Main.header file for the Graphite engine. +----------------------------------------------------------------------------------------------*/ +#ifdef _MSC_VER +#pragma once +#endif +#ifndef GRAPHITE_H +#define GRAPHITE_H 1 + +//:End Ignore + +#define NO_EXCEPTIONS 1 + +// It's okay to use functions that were declared deprecated by VS 2005: +#define _CRT_SECURE_NO_DEPRECATE +#pragma warning(disable: 4996) // warning: function was declared deprecated +#pragma warning(disable: 4702) // unreachable code + +#include "GrCommon.h" + +//:>******************************************************************************************** +//:> Interfaces. +//:>******************************************************************************************** +#include "GrData.h" + +//:>******************************************************************************************** +//:> Implementations. +//:>******************************************************************************************** + +#ifndef _WIN32 +#include "GrMstypes.h" +#endif +#include "GrDebug.h" + +// For reading the font (FieldWorks's approach) and transduction logging: +#include <fstream> +#include <iostream> + +#include <vector> +////#include <algorithm> +#include <string> + +// gAssert should be used for any kind of assertions that can be caused by a corrupted font, +// particularly those that won't be caught when loading the tables. +#define gAssert(x) Assert(x) +// When testing the error mechanism (because the process of bringing up the assertion dialog +// interferes with FW repainting the invalid window: +////#define gAssert(x) if (!(x)) Warn("corrupted font?") + +// Internal headers. +#include "FileInput.h" + +// Public headers. +#include "GrResult.h" +//////#include "IGrGraphics.h" +#include "ITextSource.h" +#include "IGrJustifier.h" +#include "IGrEngine.h" +#include "GrConstants.h" +#include "GrFeature.h" + +// External helper classes. +#include "GrExt.h" + + +// Forward declarations. +namespace gr +{ + class GrTableManager; + class Segment; + class GrEngine; +} + +// Define after GrExt.h to avoid conflict with FW Rect class. +namespace gr +{ +/* +struct Point +{ + float x; + float y; + + Point() + { + x = y = 0; + } + + Point(POINT & p) + { + x = (float)p.x; + y = (float)p.y; + } +}; + + +struct Rect +{ + float top; + float bottom; + float left; + float right; + + Rect() + { + top = bottom = left = right = 0; + }; + + Rect(RECT & r) + { + top = (float)r.top; + bottom = (float)r.bottom; + left = (float)r.left; + right = (float)r.right; + }; +}; +*/ +}; // namespace gr + +#include "GrFeatureValues.h" +#include "GrSlotState.h" + +#include "SegmentAux.h" + +// Internal headers +#include "GrCharStream.h" +#include "GrGlyphTable.h" +#include "GrClassTable.h" +#include "GrPseudoMap.h" +#include "GrSlotStream.h" +#include "GrFSM.h" +#include "GrPass.h" +#include "GrTableManager.h" +#include "FontCache.h" + + +// Public headers +#include "Font.h" +#include "GraphiteProcess.h" +#include "GrEngine.h" +#include "FontFace.h" +#include "Segment.h" +#include "SegmentPainter.h" +#ifdef _WIN32 +#include <hash_map> +#include "WinFont.h" +#include "WinSegmentPainter.h" +#endif + +// Internal headers +#include "TtfUtil.h" +//#include "GrUtil.h" + +#include "GrWrappers.h" + +//#include "IGrDebug.h" +//#include "GrSegmentDebug.h" +//#include "GrEngineDebug.h" + +/////<<<<<<< .mine +// clashes with non gr stuff +// using namespace gr; +///////>>>>>>> .r98 + +#endif // GRAPHITE_H + diff --git a/Build/source/libs/graphite-engine/src/segment/Makefile.am b/Build/source/libs/graphite-engine/src/segment/Makefile.am new file mode 100644 index 00000000000..ad5c107f668 --- /dev/null +++ b/Build/source/libs/graphite-engine/src/segment/Makefile.am @@ -0,0 +1,26 @@ +segmentdir = $(top_srcdir)/src/segment + +libgraphite_la_SOURCES += $(segmentdir)/FileInput.cpp +libgraphite_la_SOURCES += $(segmentdir)/FontCache.cpp +libgraphite_la_SOURCES += $(segmentdir)/FontFace.cpp +libgraphite_la_SOURCES += $(segmentdir)/GrCharStream.cpp +libgraphite_la_SOURCES += $(segmentdir)/GrClassTable.cpp +libgraphite_la_SOURCES += $(segmentdir)/GrEngine.cpp +libgraphite_la_SOURCES += $(segmentdir)/GrFeature.cpp +libgraphite_la_SOURCES += $(segmentdir)/GrFSM.cpp +libgraphite_la_SOURCES += $(segmentdir)/GrGlyphTable.cpp +libgraphite_la_SOURCES += $(segmentdir)/GrPassActionCode.cpp +libgraphite_la_SOURCES += $(segmentdir)/GrPass.cpp +libgraphite_la_SOURCES += $(segmentdir)/GrSlotState.cpp +libgraphite_la_SOURCES += $(segmentdir)/GrSlotStream.cpp +libgraphite_la_SOURCES += $(segmentdir)/GrTableManager.cpp +libgraphite_la_SOURCES += $(segmentdir)/Platform.cpp +libgraphite_la_SOURCES += $(segmentdir)/SegmentAux.cpp +libgraphite_la_SOURCES += $(segmentdir)/Segment.cpp +libgraphite_la_SOURCES += $(segmentdir)/TestFSM.cpp +libgraphite_la_SOURCES += $(segmentdir)/TestPasses.cpp +libgraphite_la_SOURCES += $(segmentdir)/TransductionLog.cpp + +EXTRA_DIST += $(segmentdir)/*.h +EXTRA_DIST += $(segmentdir)/explicit_instantiations.cpp + diff --git a/Build/source/libs/graphite-engine/src/segment/Platform.cpp b/Build/source/libs/graphite-engine/src/segment/Platform.cpp new file mode 100644 index 00000000000..485e0606c48 --- /dev/null +++ b/Build/source/libs/graphite-engine/src/segment/Platform.cpp @@ -0,0 +1,107 @@ +#include "GrPlatform.h" + +#ifdef _WIN32 +#include "windows.h" + +namespace gr +{ + +size_t Platform_UnicodeToANSI(const utf16 * prgchwSrc, size_t cchwSrc, char * prgchsDst, size_t cchsDst) +{ + return ::WideCharToMultiByte(CP_ACP, 0, (LPCWSTR)prgchwSrc, cchwSrc, prgchsDst, cchsDst, NULL, NULL); +} + +size_t Platform_AnsiToUnicode(const char * prgchsSrc, size_t cchsSrc, utf16 * prgchwDst, size_t cchwDst) +{ + return ::MultiByteToWideChar(CP_ACP, 0, prgchsSrc, cchsSrc, (LPWSTR)prgchwDst, cchwDst); +} + +size_t Platform_8bitToUnicode(int nCodePage, const char * prgchsSrc, int cchsSrc, + utf16 * prgchwDst, int cchwDst) +{ + return ::MultiByteToWideChar(nCodePage, 0, prgchsSrc, cchsSrc, (LPWSTR)prgchwDst, cchwDst); +} + +size_t utf8len(const char *s) +{ + return mbstowcs(NULL,s,0); +} + +utf16 *utf16cpy(utf16 *dest, const utf16 *src) +{ + return (utf16*)wcscpy((wchar_t*)dest, (const wchar_t*)src); +} + +utf16 *utf16ncpy(utf16 *dest, const utf16 *src, size_t n) +{ + return (utf16*)wcsncpy((wchar_t*)dest, (const wchar_t*)src, n); +} + +utf16 *utf16ncpy(utf16 *dest, const char *src, size_t n) +{ + #ifdef UTF16DEBUG + std::cerr << "utf16ncpy8: " << src << std::endl; + #endif + Platform_AnsiToUnicode(src, strlen(src), dest, n); + return dest; +} + + +size_t utf16len(const utf16 *s) +{ + return wcslen((const wchar_t*)s); +} + +int utf16cmp(const utf16 *s1, const utf16 *s2) +{ +return wcscmp((const wchar_t*)s1, (const wchar_t*)s2); +} + +int utf16ncmp(const utf16 *s1, const utf16 *s2, size_t n) +{ + return wcsncmp((const wchar_t*)s1, (const wchar_t*)s2, n); +} + +int utf16cmp(const utf16 *s1, const char *s2) +{ + while (*s1 && s2) + { + if (*s1 < *s2) + { + return -1; + } + if (*s1 > *s2) + { + return 1; + } + *s1++; + *s2++; + } + if (*s1) return -1; + else if (*s2) return 1; + return 0; +} + +} +#else // not _WIN32 + +#include <iostream> +#include <stdlib.h> +#include <string.h> + + +namespace gr +{ + +size_t utf16len(const utf16 *s) +{ + // assumes NULL terminated strings + const utf16 *start = s; + for (; *s; ++s); + + return s - start; +} + +} // namespace gr +#endif // _WIN32 + diff --git a/Build/source/libs/graphite-engine/src/segment/Segment.cpp b/Build/source/libs/graphite-engine/src/segment/Segment.cpp new file mode 100644 index 00000000000..319baef85b5 --- /dev/null +++ b/Build/source/libs/graphite-engine/src/segment/Segment.cpp @@ -0,0 +1,3406 @@ +/*--------------------------------------------------------------------*//*:Ignore this sentence. +Copyright (C) 1999, 2001 SIL International. All rights reserved. + +Distributable under the terms of either the Common Public License or the +GNU Lesser General Public License, as specified in the LICENSING.txt file. + +File: Segment.cpp +Responsibility: Sharon Correll +Last reviewed: Not yet. + +Description: + Implements a Graphite segment--a range of rendered text in one writing system, that can be + rendered with a single font, and that fits on a single line. +-------------------------------------------------------------------------------*//*:End Ignore*/ + +//:>******************************************************************************************** +//:> Include files +//:>******************************************************************************************** +#include "Main.h" +#ifdef _MSC_VER +#pragma hdrstop +#endif +// any other headers (not precompiled) +#include <math.h> +#ifndef _WIN32 +#include <stdlib.h> +#endif + +#undef THIS_FILE +DEFINE_THIS_FILE + +//:>******************************************************************************************** +//:> Forward declarations +//:>******************************************************************************************** + +//:>******************************************************************************************** +//:> Local Constants and static variables +//:>******************************************************************************************** +#if 0 // TODO remove +namespace +{ + +static bool g_fDrawing; + +} // namespace +#endif + + +static int g_csegTotal = 0; + +namespace gr +{ + +//:>******************************************************************************************** +//:> Segment methods +//:>******************************************************************************************** + + +int Segment::GetSegmentCount() /// TEMP +{ + return g_csegTotal; +} + + +/*---------------------------------------------------------------------------------------------- + Constructor: basic initialization. +----------------------------------------------------------------------------------------------*/ +Segment::Segment() : + m_pgts(NULL), + m_pfont(NULL), + m_preneng(NULL), + m_fErroneous(false), + m_pgjus(NULL), + m_prgbNextSegDat(NULL), + m_cbNextSegDat(0), + m_prgInitDat(NULL), + m_cbInitDat(0), + m_fUseSepBase(0), + //m_psegAltEndLine(NULL), + m_prgisloutBefore(NULL), + m_prgisloutAfter(NULL), + m_prgvisloutAssocs(NULL), + m_prgisloutLigature(NULL), + m_prgiComponent(NULL), + m_psstrm(NULL), + m_cslout(0), + m_prgslout(NULL), + m_prgnSlotVarLenBuf(NULL), + m_cnUserDefn(0), + m_cnCompPerLig(0), + m_cnFeat(0), + m_cginf(0), + m_prgginf(NULL) +{ + m_cref = 1; + + g_csegTotal++; + + m_stuFaceName.erase(); + m_stuBaseFaceName.erase(); + m_vnSkipOffsets.clear(); +} + +/*---------------------------------------------------------------------------------------------- + Reference counting. +----------------------------------------------------------------------------------------------*/ + +long Segment::IncRefCount(void) +{ + AssertPtr(this); + return InterlockedIncrement(&m_cref); +} + +long Segment::DecRefCount(void) +{ + AssertPtr(this); + long cref = InterlockedDecrement(&m_cref); + if (cref == 0) { + m_cref = 1; + delete this; + } + return cref; +} + +/*---------------------------------------------------------------------------------------------- + Constructor to fit as much of the text on the line as possible, + finding a reasonable break point if necessary. +----------------------------------------------------------------------------------------------*/ +LineFillSegment::LineFillSegment( + Font * pfont, + ITextSource * pts, + LayoutEnvironment * playoutArg, + toffset ichStart, + toffset ichStop, + float xsMaxWidth, + bool fBacktracking) + : Segment() +{ + if (!pfont) + throw; + if (!pts) + throw; + + LayoutEnvironment layoutDefault; + LayoutEnvironment * playout = (!playoutArg) ? &layoutDefault : playoutArg; + + pfont->RenderLineFillSegment(this, pts, *playout, ichStart, ichStop, xsMaxWidth, + fBacktracking); +} + +/*---------------------------------------------------------------------------------------------- + Constructor to create a segment representing the given range of characters. +----------------------------------------------------------------------------------------------*/ +RangeSegment::RangeSegment( + Font * pfont, + ITextSource * pts, + LayoutEnvironment * playoutArg, + toffset ichStart, + toffset ichStop, + Segment * psegInitLike) + : Segment() +{ + if (!pfont) + throw; + if (!pts) + throw; + + LayoutEnvironment layoutDefault; + LayoutEnvironment * playout = (!playoutArg) ? &layoutDefault : playoutArg; + playout->setSegmentForInit(psegInitLike); + + pfont->RenderRangeSegment(this, pts, *playout, ichStart, ichStop); + + playout->setSegmentForInit(NULL); +} + +/*---------------------------------------------------------------------------------------------- + Constructor to create a justified segment. Private. +----------------------------------------------------------------------------------------------*/ +JustSegmentAux::JustSegmentAux( + Font * pfont, + ITextSource * pts, + LayoutEnvironment * playoutArg, + toffset ichStart, + toffset ichStop, + float xsNaturalWidth, + float xsJustifiedWidth, + Segment * psegInitLike) + : Segment() +{ + if (!pfont) + throw; + if (!pts) + throw; + + LayoutEnvironment layoutDefault; + LayoutEnvironment * playout = (!playoutArg) ? &layoutDefault : playoutArg; + playout->setSegmentForInit(psegInitLike); + + pfont->RenderJustifiedSegment(this, pts, *playout, ichStart, ichStop, + xsNaturalWidth, xsJustifiedWidth); + + playout->setSegmentForInit(NULL); +} + +//Segment::Segment(ITextSource * pgts, int ichwMin, int ichwLim, +// LineBrk lbStart, LineBrk lbEnd, +// bool fStartLine, bool fEndLine, bool fWsRtl) +//{ +// m_cref = 1; +// Initialize(pgts, ichwMin, ichwLim, lbStart, lbEnd, fStartLine, fEndLine, fWsRtl); +//} + +void Segment::Initialize(ITextSource * pgts, int ichwMin, int ichwLim, + LineBrk lbStart, LineBrk lbEnd, SegEnd est, + bool fStartLine, bool fEndLine, bool fWsRtl) +{ + AssertPtrN(pgts); + Assert(ichwMin >= 0); + Assert(ichwLim >= 0); + Assert(ichwMin <= ichwLim); + + //pgts->TextSrcObject(&m_pgts); // create a permanent wrapper, if necessary + // m_pgts->AddRef(); no, smart pointer + m_pgts = pgts; + + m_ichwMin = ichwMin; + m_dichwLim = ichwLim - ichwMin; + m_lbStart = lbStart; + m_lbEnd = lbEnd; + m_est = est; + m_dxsStretch = 0; + m_dxsWidth = -1; + m_dysAscent = -1; // to properly init, ComputeDimensions must be called + m_dysHeight = -1; + m_dysXAscent = -1; + m_dysXDescent = -1; + m_dysAscentOverhang = -1; + m_dysDescentOverhang = -1; + m_dxsLeftOverhang = -1; + m_dxsRightOverhang = -1; + m_dysOffset = 0; + m_fStartLine = fStartLine; + m_fEndLine = fEndLine; + m_fWsRtl = fWsRtl; + + m_dxsVisibleWidth = -1; + m_dxsTotalWidth = -1; + + m_psstrm = NULL; + m_prgslout = NULL; + m_prgnSlotVarLenBuf = NULL; + m_prgisloutBefore = NULL; + m_prgisloutAfter = NULL; + m_prgvisloutAssocs = NULL; + m_prgisloutLigature = NULL; + m_prgiComponent = NULL; + + m_prgbNextSegDat = NULL; + m_cbNextSegDat = 0; + + //m_psegAltEndLine = NULL; + + m_stuFaceName.erase(); + m_stuBaseFaceName.erase(); + m_fUseSepBase = false; + + m_psegPrev = NULL; // obsolete + m_psegNext = NULL; + + //InitializePlatform(); +} + +/*---------------------------------------------------------------------------------------------- + Destructor. +----------------------------------------------------------------------------------------------*/ +Segment::~Segment() +{ + g_csegTotal--; + + DestroyContents(); +} + +void Segment::DestroyContents() +{ + delete m_pfont; + m_pfont = NULL; + + //if (m_pgts) + // // A specially-created wrapper object has to be deleted. + // m_pgts->DeleteTextSrcPtr(); + m_pgts = NULL; + + //if (m_pgjus) + // // A specially-created wrapper object has to be deleted. + // m_pgjus->DeleteJustifierPtr(); + m_pgjus = NULL; + + //if (m_psegAltEndLine) + //{ + // if (m_psegAltEndLine->m_psegAltEndLine == this) + // m_psegAltEndLine->m_psegAltEndLine = NULL; + // delete m_psegAltEndLine; + //} + + //for (int iginf = 0; iginf < m_cginf; iginf++) // DELETE + // delete m_prgginf[iginf].components; + + Assert((m_prgslout && m_prgnSlotVarLenBuf) || (!m_prgslout && !m_prgnSlotVarLenBuf)); + delete[] m_prgslout; + delete[] m_prgnSlotVarLenBuf; + delete[] m_prgisloutBefore; + delete[] m_prgisloutAfter; + delete[] m_prgvisloutAssocs; + delete[] m_prgisloutLigature; + delete[] m_prgiComponent; + delete[] m_prgbNextSegDat; + delete[] m_prgInitDat; + delete[] m_prgginf; + m_cginf = 0; + + //DestroyContentsPlatform(); + + // ReleaseObj(m_pgts); automatic because smart pointer +} + +/*---------------------------------------------------------------------------------------------- + Factory method to create a new version of an existing segment with different + line-boundary flags. +----------------------------------------------------------------------------------------------*/ +Segment * Segment::LineContextSegment(Segment & seg, bool fStartLine, bool fEndLine) +{ + if (!seg.hasLineBoundaryContext()) + { + // Make an identical segment and just change the flags. + Segment * psegNew = new Segment(seg); + psegNew->InitLineContextSegment(fStartLine, fEndLine); + return psegNew; + } + else + { + // Rerun the processing. + LayoutEnvironment layout = seg.Layout(); + layout.setStartOfLine(fStartLine); + layout.setEndOfLine(fEndLine); + ITextSource & gts = seg.getText(); + Font & font = seg.getFont(); + return new RangeSegment(&font, >s, &layout, + seg.startCharacter(), seg.stopCharacter(), &seg); + } +} + +void Segment::InitLineContextSegment(bool fStartLine, bool fEndLine) +{ + // For RTL end-of-line segments, trailing white space hangs off the left of + // the origin of the segment. So when changing that flag + // shift appropriately. + bool fShift = ((m_nDirDepth % 2) && (m_fEndLine != fEndLine)); + + m_fStartLine = fStartLine; + m_fEndLine = fEndLine; + + m_layout.setStartOfLine(fStartLine); + m_layout.setEndOfLine(fEndLine); + + if (fShift) + { + (m_fEndLine) ? + ShiftGlyphs(m_dxsVisibleWidth - m_dxsTotalWidth) : // shift left + ShiftGlyphs(m_dxsTotalWidth - m_dxsVisibleWidth); // shift right + } + + m_dxsWidth = -1; +} + +/*---------------------------------------------------------------------------------------------- + A factory method to create a justified version of an existing segment. +----------------------------------------------------------------------------------------------*/ +Segment * Segment::JustifiedSegment(Segment & seg, float xsNewWidth) +{ + LayoutEnvironment layout(seg.Layout()); + ITextSource & gts = seg.getText(); + + // Why do we have to do this? + layout.setJustifier(seg.Justifier()); + Font & font = seg.getFont(); + return new JustSegmentAux(&font, >s, &layout, + seg.startCharacter(), seg.stopCharacter(), + seg.advanceWidth(), xsNewWidth, &seg); +} + +/*---------------------------------------------------------------------------------------------- + A factory method to create a new version of an existing trailing white-space segment + with a different direction. +----------------------------------------------------------------------------------------------*/ +Segment * Segment::WhiteSpaceSegment(Segment & seg, int nNewDepth) +{ + Segment * psegNew = new Segment(seg); + psegNew->InitWhiteSpaceSegment(nNewDepth); + return psegNew; +} + +void Segment::InitWhiteSpaceSegment(int nNewDepth) +{ + if (nNewDepth == m_nDirDepth) + { + return; + } + else if ((nNewDepth % 2) == (m_nDirDepth % 2)) + { + // Same direction, not much to change. + m_nDirDepth = nNewDepth; + return; + } + else if (m_twsh != ktwshOnlyWs) + return; // couldn't change it, oh, well + + // Otherwise, do the hard stuff: reverse the positions of the glyphs. + for (int islout = 0; islout < m_cslout; islout++) + { + OutputSlot(islout)->ShiftForDirDepthChange(m_dxsTotalWidth); + } + m_nDirDepth = nNewDepth; + + //SetDirectionDepthPlatform(nNewDepth); +} + +/*---------------------------------------------------------------------------------------------- + Basic copy method. +----------------------------------------------------------------------------------------------*/ +Segment::Segment(Segment & seg) +{ + int islout; + + m_pgts = seg.m_pgts; + m_dichwLim = seg.m_dichwLim; + m_ichwMin = seg.m_ichwMin; + m_pfont = seg.m_pfont->copyThis(); + m_preneng = seg.m_preneng; + m_fErroneous = seg.m_fErroneous; + m_pgjus = seg.m_pgjus; + m_fWsRtl = seg.m_fWsRtl; + m_fParaRtl = seg.m_fParaRtl; + m_twsh = seg.m_twsh; + m_nDirDepth = seg.m_nDirDepth; + m_cbNextSegDat = seg.m_cbNextSegDat; + m_prgbNextSegDat = new byte[m_cbNextSegDat]; + std::copy(seg.m_prgbNextSegDat, seg.m_prgbNextSegDat + m_cbNextSegDat, m_prgbNextSegDat); + m_psegPrev = seg.m_psegPrev; + m_psegNext = seg.m_psegNext; + m_stuFaceName = seg.m_stuFaceName; + m_stuBaseFaceName = seg.m_stuBaseFaceName; + m_fUseSepBase = seg.m_fUseSepBase; + m_pixHeight = seg.m_pixHeight; + m_fBold = seg.m_fBold; + m_fItalic = seg.m_fItalic; + m_lbStart = seg.m_lbStart; + m_lbEnd = seg.m_lbEnd; + m_fStartLine = seg.m_fStartLine; + m_fEndLine = seg.m_fEndLine; + m_est = seg.m_est; + m_mFontEmUnits = seg.m_mFontEmUnits; + m_dysFontAscent = seg.m_dysFontAscent; + m_dysFontDescent = seg.m_dysFontDescent; + m_xysEmSquare = seg.m_xysEmSquare; + m_xsDPI = seg.m_xsDPI; + m_ysDPI = seg.m_ysDPI; + m_dxsStretch = seg.m_dxsStretch; + m_dxsWidth = seg.m_dxsWidth; + m_dysHeight = seg.m_dysHeight; + m_dysAscent = seg.m_dysAscent; + m_dysXAscent = seg.m_dysXAscent; + m_dysXDescent = seg.m_dysXDescent; + m_dysAscentOverhang = seg.m_dysAscentOverhang; + m_dysDescentOverhang = seg.m_dysDescentOverhang; + m_dxsLeftOverhang = seg.m_dxsLeftOverhang; + m_dxsRightOverhang = seg.m_dxsRightOverhang; + m_dxsVisibleWidth = seg.m_dxsVisibleWidth; + m_dxsTotalWidth = seg.m_dxsTotalWidth; + m_isloutVisLim = seg.m_isloutVisLim; + m_dysOffset = seg.m_dysOffset; + + m_ichwAssocsMin = seg.m_ichwAssocsMin; + m_ichwAssocsLim = seg.m_ichwAssocsLim; + + int dichw = m_ichwAssocsLim - m_ichwAssocsMin; + m_prgisloutBefore = new int[dichw]; + std::copy(seg.m_prgisloutBefore, seg.m_prgisloutBefore + dichw, m_prgisloutBefore); + m_prgisloutAfter = new int[dichw]; + std::copy(seg.m_prgisloutAfter, seg.m_prgisloutAfter + dichw, m_prgisloutAfter); + m_prgvisloutAssocs = new std::vector<int>[dichw]; + std::copy(seg.m_prgvisloutAssocs, seg.m_prgvisloutAssocs + dichw, + m_prgvisloutAssocs); + m_prgisloutLigature = new int[dichw]; + std::copy(seg.m_prgisloutLigature, seg.m_prgisloutLigature + dichw, m_prgisloutLigature); + m_prgiComponent = new int[dichw]; + std::copy(seg.m_prgiComponent, seg.m_prgiComponent + dichw, m_prgiComponent); + + // m_psstrm + + m_cslout = seg.m_cslout; + int cnExtraPerSlot = (m_cslout > 0) ? seg.m_prgslout[0].CExtraSpaceSlout() : 0; + m_prgnSlotVarLenBuf = new u_intslot[m_cslout * cnExtraPerSlot]; + m_prgslout = new GrSlotOutput[m_cslout]; + for (islout = 0; islout < m_cslout; islout++) + { + GrSlotOutput * psloutOrig = seg.m_prgslout + islout; + GrSlotOutput * psloutThis = m_prgslout + islout; + psloutThis->ExactCopyFrom(psloutOrig, + m_prgnSlotVarLenBuf + (islout * cnExtraPerSlot), // var-length buffer location + cnExtraPerSlot); + } + + m_cnUserDefn = seg.m_cnUserDefn; + m_cnCompPerLig = seg.m_cnCompPerLig; + m_cnFeat = seg.m_cnFeat; + m_cslotRestartBackup = seg.m_cslotRestartBackup; + std::copy(seg.m_prgnSlotVarLenBuf, seg.m_prgnSlotVarLenBuf + + (m_cslout * cnExtraPerSlot), m_prgnSlotVarLenBuf); + + m_cginf = seg.m_cginf; + m_isloutGinf0 = seg.m_isloutGinf0; + m_prgginf = new GlyphInfo[m_cginf]; + std::copy(seg.m_prgginf, seg.m_prgginf + m_cginf, m_prgginf); + for (int iginf = 0; iginf < m_cginf; iginf++) + { + m_prgginf[iginf].m_pseg = this; + m_prgginf[iginf].m_pslout = m_prgslout + m_prgginf[iginf].m_islout; + } + + // Platform-specific; remove eventually: + //m_cgstrm = seg.m_cgstrm; + //m_prggstrm = new GlyphStrm[m_cgstrm]; + //for (int igstrm = 0; igstrm < m_cgstrm; igstrm++) + //{ + // m_prggstrm[igstrm].gsk.ys = seg.m_prggstrm[igstrm].gsk.ys; + // m_prggstrm[igstrm].gsk.clrFore = seg.m_prggstrm[igstrm].gsk.clrFore; + // m_prggstrm[igstrm].gsk.clrBack = seg.m_prggstrm[igstrm].gsk.clrBack; + // m_prggstrm[igstrm].vchwGlyphId = seg.m_prggstrm[igstrm].vchwGlyphId; + // m_prggstrm[igstrm].vdxs = seg.m_prggstrm[igstrm].vdxs; + // m_prggstrm[igstrm].vigbb = seg.m_prggstrm[igstrm].vigbb; + // m_prggstrm[igstrm].xsStart = seg.m_prggstrm[igstrm].xsStart; + // m_prggstrm[igstrm].xsStartInt = seg.m_prggstrm[igstrm].xsStartInt; + //} + //m_cgbb = seg.m_cgbb; + //m_prggbb = new GlyphBb[m_cgbb]; + //memcpy(m_prggbb, seg.m_prggbb, m_cgbb * sizeof(GlyphBb)); + //------------------------------------- + + m_vnSkipOffsets = seg.m_vnSkipOffsets; + m_dircPrevStrong = seg.m_dircPrevStrong; + m_dircPrevTerm = seg.m_dircPrevTerm; + m_cbInitDat = seg.m_cbInitDat; + m_prgInitDat = new byte[m_cbInitDat]; + std::copy(seg.m_prgInitDat, seg.m_prgInitDat + m_cbInitDat, m_prgInitDat); +} + +/*---------------------------------------------------------------------------------------------- + Swap the guts of the two segments. + OBSOLETE +----------------------------------------------------------------------------------------------*/ +void Segment::SwapWith(Segment * pgrseg) +{ + int crefThis = m_cref; + int crefOther = pgrseg->m_cref; + + std::swap(*this, *pgrseg); + + m_cref = crefThis; + pgrseg->m_cref = crefOther; +} + +/*---------------------------------------------------------------------------------------------- + Delete the pointer to the alternate-end-of-line segment. +----------------------------------------------------------------------------------------------*/ +void Segment::ClearAltEndLineSeg() +{ + //if (m_psegAltEndLine) + //{ + // if (m_psegAltEndLine->m_psegAltEndLine == this) + // m_psegAltEndLine->m_psegAltEndLine = NULL; // so we don't get into a loop + // delete m_psegAltEndLine; + // m_psegAltEndLine = NULL; + //} +} + + +//:>******************************************************************************************** +//:> Interface methods +//:>******************************************************************************************** + +/*---------------------------------------------------------------------------------------------- + Return the font that was used to create this segment. + Note that in some implementations, the font may be invalid; eg, on Windows the font + may not contain a valid device context. +----------------------------------------------------------------------------------------------*/ +Font & Segment::getFont() +{ + return *m_pfont; +} + +/*---------------------------------------------------------------------------------------------- + Return the text-source that the segment is representing. +----------------------------------------------------------------------------------------------*/ +ITextSource & Segment::getText() +{ + return *m_pgts; +} + +/*---------------------------------------------------------------------------------------------- + The distance the drawing point should advance after drawing this segment. + Always positive, even for RtoL segments. +----------------------------------------------------------------------------------------------*/ +float Segment::advanceWidth() +{ + if (m_dxsWidth < 0) + { + ////SetUpGraphics(ichwBase, pgg, true); + ComputeDimensions(); + } + return m_dxsWidth; +} + +/*---------------------------------------------------------------------------------------------- + Compute the rectangle in destination coords which contains all the pixels drawn by + this segment. This should be a sufficient rectangle to invalidate if the segment is + about to be discarded. +----------------------------------------------------------------------------------------------*/ +Rect Segment::boundingRect() +{ + if (m_dxsWidth < 0) + { + ////SetUpGraphics(ichwBase, pgg, true); + ComputeDimensions(); + } + + Rect rectRet; + rectRet.top = m_dysAscent + m_dysAscentOverhang; + rectRet.bottom = (m_dysAscent - m_dysHeight) - m_dysDescentOverhang; + rectRet.left = m_dxsLeftOverhang; + rectRet.right = m_dxsVisibleWidth + m_dxsRightOverhang; + + return rectRet; +} + +/*---------------------------------------------------------------------------------------------- + Answer whether the primary direction of the segment is right-to-left. This is based + on the direction of the writing system, except for white-space-only segments, in + which case it is based on the main paragraph direction. +----------------------------------------------------------------------------------------------*/ +bool Segment::rightToLeft() +{ + if (m_twsh == ktwshOnlyWs) + { + // White-space-only segment: use main paragraph direction. + return (m_nDirDepth % 2); + } + + GrEngine * pgreng = EngineImpl(); + if (pgreng) + { + return pgreng->RightToLeft(); + } + return (m_pgts->getRightToLeft(m_ichwMin)); +} + +/*---------------------------------------------------------------------------------------------- + Get the depth of direction embedding used by this segment. It is presumably the same + for all runs of the segment, otherwise, some of them would use a different writing + system and therefore be part of a different segment. So just use the first. +----------------------------------------------------------------------------------------------*/ +int Segment::directionDepth(bool * pfWeak) +{ + if (pfWeak) + *pfWeak = (m_twsh == ktwshOnlyWs); + +// *pnDepth = m_pgts->getDirectionDepth(m_ichwLim); +// Assert(*pnDepth == m_nDirDepth); + + return m_nDirDepth; +} + +/*---------------------------------------------------------------------------------------------- + Change the direction of the segment. This is needed specifically for white-space-only + segments, which are initially created to be in the direction of the paragraph, but then + later are discovered to not be line-end after all, and need to be changed to use the + directionality of the writing system. + + @return kresFail for segments that do not have weak directionality and therefore + cannot be changed. +----------------------------------------------------------------------------------------------*/ +bool Segment::setDirectionDepth(int nNewDepth) +{ + Assert(false); + + if (nNewDepth == m_nDirDepth) + { + return true; + } + else if ((nNewDepth % 2) == (m_nDirDepth % 2)) + { + // Same direction, not much to change. + m_nDirDepth = nNewDepth; + return true; + } + else if (m_twsh != ktwshOnlyWs) + return false; + + // Otherwise, do the hard stuff: reverse the positions of the glyphs. + for (int islout = 0; islout < m_cslout; islout++) + { + OutputSlot(islout)->ShiftForDirDepthChange(m_dxsTotalWidth); + } + + //SetDirectionDepthPlatform(nNewDepth); + + return true; + +} + +/*---------------------------------------------------------------------------------------------- + The logical range of characters covered by the segment, relative to the beginning of + the segment. The stop character is the index of the first character beyond the end + of the segment. + + These values should be exact at a writing system or string boundary, + but may be somewhat fuzzy at a line-break, since characters may be re-ordered + across such boundaries. The renderer is free to apply any definition it likes of + where a line-break occurs. This should always be the same value obtained from the + renderer as pdichLimSeg. +----------------------------------------------------------------------------------------------*/ +int Segment::startCharacter() +{ + return m_ichwMin; +} + +int Segment::stopCharacter() +{ + return m_ichwMin + m_dichwLim; +} + +/*---------------------------------------------------------------------------------------------- + Returns an indication of why the segment ended. +----------------------------------------------------------------------------------------------*/ +bool Segment::startOfLine() +{ + return m_fStartLine; +} + +/*---------------------------------------------------------------------------------------------- + Returns an indication of why the segment ended. +----------------------------------------------------------------------------------------------*/ +bool Segment::endOfLine() +{ + return m_fEndLine; +} + +/*---------------------------------------------------------------------------------------------- + Returns true if this segment has something special happening at line boundaries. +----------------------------------------------------------------------------------------------*/ +bool Segment::hasLineBoundaryContext() +{ + return (EngineImpl()->LineBreakFlag()); +} + +/*---------------------------------------------------------------------------------------------- + Returns an indication of why the segment ended. +----------------------------------------------------------------------------------------------*/ +SegEnd Segment::segmentTermination() +{ + return m_est; +} + +/*---------------------------------------------------------------------------------------------- + Indicates the last character of interest to the segment, relative to the beginning + of the segment. The meaning of this is that no behavior of this segment will be + affected if characters beyond that change. This does not necessarily mean that + a different line break could not have been obtained by the renderer if characters + beyond that change, just that a segment with the boundaries of this one would + not behave differently. +----------------------------------------------------------------------------------------------*/ +//GrResult Segment::get_LimInterest(int ichwBase, int * pdichw) +//{ +// ChkGrOutPtr(pdichw); +// +// GrEngine * pgreng = EngineImpl(); +// Assert(pgreng); +// *pdichw = m_dichwLim + pgreng->PostXlbContext(); +// ReturnResult(kresOk); +//} + +/*---------------------------------------------------------------------------------------------- + Changes the end-of-line status of the segment. This is used after making the last segment + of a string if we want to attempt to put some more text after it (e.g., in another + writing system), or if we have reordered due to bidirectionality. + + ENHANCE: the current version handles switching back and forth when we are trying to + decide whether we can put more stuff on a line. So it assumes we are at the end of + the contextual run (ie, the next batch of stuff will be in a different ows). + This will not correctly handle the way this function could or possibly + should be used for bidirectional reordering. +----------------------------------------------------------------------------------------------*/ +/* +GrResult Segment::changeLineEnds(bool fNewStart, bool fNewEnd) +{ + if (m_fStartLine != fNewStart || m_fEndLine != fNewEnd) + { + if (EngineImpl()->LineBreakFlag()) + { + if (!m_psegAltEndLine) + { + int dichwLimSeg, dichwContext; + float dxWidth; + SegEnd est; + int cbNextSegDat; + byte rgbNextSegDat[256]; + OLECHAR rgchErrMsg[256]; + Segment * pseg; + + EngineImpl()->FindBreakPointAux(m_pfont, m_pgts, m_pgjus, + m_ichwMin, m_ichwMin + m_dichwLim, m_ichwMin + m_dichwLim, + false, fNewStart, fNewEnd, kPosInfFloat, false, + klbNoBreak, klbNoBreak, m_twsh, m_fParaRtl, + &pseg, + &dichwLimSeg, &dxWidth, &est, + m_cbInitDat, m_prgInitDat, 256, rgbNextSegDat, &cbNextSegDat, + &dichwContext, + NULL, + rgchErrMsg, 256); + // TODO: do something with the error message. This shouldn't really happen, + // since to get here we already created a segment with this font. + if (!pseg) + { + // Because we provide INT_MAX as the amount of space, there should + // always be enough room to generate the alternate segment. + Assert(false); + THROW(kresUnexpected); + } + Assert(cbNextSegDat == 0); + m_psegAltEndLine = pseg; + } + Assert(m_psegAltEndLine->m_fEndLine == fNewEnd); + Segment * pseg = m_psegAltEndLine; // save the value + SwapWith(m_psegAltEndLine); + m_psegAltEndLine = pseg; // reset it after swap + pseg->m_psegAltEndLine = this; + } + else + { + bool fShift = ((m_nDirDepth % 2) && (m_fEndLine != fNewEnd)); + m_fStartLine = fNewStart; + m_fEndLine = fNewEnd; + if (fShift) + { + (m_fEndLine) ? + ShiftGlyphs(m_dxsVisibleWidth - m_dxsTotalWidth) : // shift left + ShiftGlyphs(m_dxsTotalWidth - m_dxsVisibleWidth); // shift right + } + m_dxsWidth = -1; + } + } + + ReturnResult(kresOk); +} +*/ + +/*---------------------------------------------------------------------------------------------- + Get the type of break that occurs at the logical start of the segment. +----------------------------------------------------------------------------------------------*/ +LineBrk Segment::startBreakWeight() +{ + return m_lbStart; +} + +/*---------------------------------------------------------------------------------------------- + Get the type of break that occurs at the logical end of the segment. +----------------------------------------------------------------------------------------------*/ +LineBrk Segment::endBreakWeight() +{ + return m_lbEnd; +} + +/*---------------------------------------------------------------------------------------------- + Read the amount of stretch, that is, the difference between the actual and natural width. + If the segment has been shrunk, returns a negative number. + TODO: implement properly. +----------------------------------------------------------------------------------------------*/ +float Segment::stretch() +{ + return (float)m_dxsStretch; +} + +/*---------------------------------------------------------------------------------------------- + Return the maximum stretch possible. + TODO: implement properly. +----------------------------------------------------------------------------------------------*/ +float Segment::maxStretch() +{ + return 1000; +} + +/*---------------------------------------------------------------------------------------------- + Return the maximum shrink possible. + TODO: implement properly. +----------------------------------------------------------------------------------------------*/ +float Segment::maxShrink() +{ + return 0; +} + +/*---------------------------------------------------------------------------------------------- + Return iterators representing the complete range of glyphs in the segment. +----------------------------------------------------------------------------------------------*/ +std::pair<GlyphIterator, GlyphIterator> Segment::glyphs() +{ + return std::make_pair(GlyphIterator(*this, 0), GlyphIterator(*this, m_cginf)); +} + +/*---------------------------------------------------------------------------------------------- + Return iterators representing the set of glyphs for the given character. +----------------------------------------------------------------------------------------------*/ +std::pair<GlyphSetIterator, GlyphSetIterator> Segment::charToGlyphs(toffset ich) +{ + std::vector<int> * pvislout = UnderlyingToLogicalAssocs(ich); + + // UnderlyingToLogicalAssocs can return NULL for edge cases + if (pvislout == NULL) + return std::make_pair(GlyphSetIterator(), GlyphSetIterator()); + else + return std::make_pair(GlyphSetIterator(*this, 0, *pvislout), + GlyphSetIterator(*this, pvislout->size(), *pvislout)); +} + +/*---------------------------------------------------------------------------------------------- + Return the rendered glyphs and their x- and y-coordinates, in the order generated by + the final positioning pass, relative to the top-left of the segment. They are always + returned in (roughly) left-to-right order. Note: any of the arrays may be null. +----------------------------------------------------------------------------------------------*/ +/* +GrResult Segment::GetGlyphsAndPositions( + Rect rs, Rect rd, int cchMax, int * pcchRet, + utf16 * prgchGlyphs, float * prgxd, float * prgyd, float * prgdxdAdv) +{ + ChkGrOutPtr(pcchRet); + if (prgchGlyphs) + ChkGrArrayArg(prgchGlyphs, cchMax); + if (prgxd) + ChkGrArrayArg(prgxd, cchMax); + if (prgyd) + ChkGrArrayArg(prgyd, cchMax); + if (prgdxdAdv) + ChkGrArrayArg(prgdxdAdv, cchMax); + + GrResult res; + + //SetUpGraphics(ichwBase, pgg, true); + + Assert(m_dxsWidth >= 0); + Assert(m_dysAscent >= 0); + + *pcchRet = m_cginf; + if (cchMax < m_cginf) + { + res = (cchMax == 0) ? kresFalse : kresInvalidArg; + //RestoreFont(pgg); + ReturnResult(res); + } + + if (m_dxsWidth < 0) + ComputeDimensions(); + + float dysFontAscent = m_dysFontAscent; + //////res = GetFontAscentSourceUnits(pgg, &dysFontAscent); + //////if (ResultFailed(res)) + //////{ + ////// RestoreFont(pgg); + ////// ReturnResult(res); + //////} + + float xs = 0; + // Top of text that sits on the baseline, relative to top of segment: + float ys = m_dysAscent - dysFontAscent; + + float * prgxdLocal; + if (prgxd == NULL) + prgxdLocal = new float[cchMax]; + else + prgxdLocal = prgxd; + + res = GetGlyphsAndPositionsPlatform(xs, ys, rs, rd, + prgchGlyphs, prgxdLocal, prgyd, prgdxdAdv); + + if (!prgxd) + delete[] prgxdLocal; + + //RestoreFont(pgg); + + ReturnResult(res); +} +*/ + +/*---------------------------------------------------------------------------------------------- + For debugging. Return the characters in this segment. + OBSOLETE +----------------------------------------------------------------------------------------------*/ +GrResult Segment::GetCharData(int cchMax, utf16 * prgch, int * pcchRet) +{ + ChkGrArgPtr(prgch); + ChkGrOutPtr(pcchRet); + + int ichLimTmp = m_ichwMin + min(m_dichwLim, cchMax); + m_pgts->fetch(m_ichwMin, ichLimTmp - m_ichwMin, prgch); + *pcchRet = ichLimTmp - m_ichwMin; + ReturnResult(kresOk); +} + +/*---------------------------------------------------------------------------------------------- + Return either the first or last character that the the glyph corresponds to. The + index of the glyph matches what was returned by GetGlyphsAndPositions. + OBSOLETE +----------------------------------------------------------------------------------------------*/ +GrResult Segment::GlyphToChar(int iginf, bool fFirst, int * pich) +{ + Assert(m_dxsWidth >= 0); + Assert(m_dysAscent >= 0); + + *pich = PhysicalSurfaceToUnderlying(iginf, fFirst); + ReturnResult(kresOk); +} + +/*---------------------------------------------------------------------------------------------- + Return all the characters that the glyph corresponds to. Most commonly this will + be only one char, but ligatures, for instance, will have several. The index of the + glyph matches what was returned by GetGlyphsAndPositions. + OBSOLETE +----------------------------------------------------------------------------------------------*/ +GrResult Segment::GlyphToAllChars(int iginf, + int cichMax, int * prgich, int * pcichRet) +{ + Assert(m_dxsWidth >= 0); + Assert(m_dysAscent >= 0); + + std::vector<int> vich; + int ichFirst = PhysicalSurfaceToUnderlying(iginf, true); + int ichLast = PhysicalSurfaceToUnderlying(iginf, false); + Assert(ichFirst <= ichLast); + + if (ichFirst < ichLast) + { + // Loop over the range of characters that might be associated with this glyph + // and add any that actually are. + // TODO: we should probably use a method that gets us all the physical associations, + // but this should get us 99.5% of the way there for now. + for (int ich = ichFirst; ich <= ichLast; ich++) + { + int iginfFirst = UnderlyingToPhysicalSurface(ich, true); + int iginfLast = UnderlyingToPhysicalSurface(ich, false); + if (iginfFirst == iginf || iginfLast == iginf) + vich.push_back(ich); + } + } + else + vich.push_back(ichFirst); + + *pcichRet = vich.size(); + if (cichMax < signed(vich.size())) + { + GrResult res = (cichMax == 0 ? kresFalse : kresInvalidArg); + ReturnResult(res); + } + int * pi = prgich; + for (size_t i = 0; i < vich.size(); i++) + *pi++ = vich[i]; + + ReturnResult(kresOk); +} + +/*---------------------------------------------------------------------------------------------- + Return either the first or last glyph that corresponds to the character. The + index of the glyph matches what was returned by GetGlyphsAndPositions. + OBSOLETE +----------------------------------------------------------------------------------------------*/ +GrResult Segment::CharToGlyph(int ich, bool fFirst, int * pigbb) +{ + Assert(m_dxsWidth >= 0); + Assert(m_dysAscent >= 0); + + *pigbb = UnderlyingToPhysicalSurface(ich, fFirst); + ReturnResult(kresOk); +} + + +//:>******************************************************************************************** +//:> Non-FieldWorks interface methods +//:>******************************************************************************************** + +/*---------------------------------------------------------------------------------------------- + Return the breakweight for the given character. Returns the best value if the character is + mapped to glyphs with different values. +----------------------------------------------------------------------------------------------*/ +LineBrk Segment::getBreakWeight(int ich, bool fBreakBefore) +{ + LineBrk lbFirst, lbLast; + + int isloutFirst = UnderlyingToLogicalSurface(ich, true); + if (isloutFirst == kPosInfinity || isloutFirst == kNegInfinity) + return klbClipBreak; + GrSlotOutput * psloutFirst = m_prgslout + isloutFirst; + bool fNotFirst = false; + if (psloutFirst->HasComponents() + && psloutFirst->UnderlyingComponent(0) != ich) + { + // Not the first component of a ligature. + lbFirst = (LineBrk) ((int)klbClipBreak * -1); + fNotFirst = true; + } + else + { + lbFirst = (LineBrk) psloutFirst->BreakWeight(); + } + + int isloutLast = UnderlyingToLogicalSurface(ich, false); + if (isloutLast == kPosInfinity || isloutLast == kNegInfinity) + return klbClipBreak; + GrSlotOutput * psloutLast = m_prgslout + isloutLast; + bool fNotLast = false; + if (psloutLast->HasComponents() + && psloutLast->UnderlyingComponent(psloutLast->NumberOfComponents() - 1) != ich) + { + // Not the last component of a ligature. + lbLast = klbClipBreak; + fNotLast = true; + } + else + { + lbLast = (LineBrk) psloutLast->BreakWeight(); + } + + LineBrk lbRet; + if (fNotLast && fNotFirst) + // middle of a ligature + lbRet = klbClipBreak; + else if (fNotLast) + lbRet = (fBreakBefore) ? lbFirst : klbClipBreak; + else if (fNotFirst) + lbRet = (fBreakBefore) ? klbClipBreak : lbLast; + else + lbRet = (fBreakBefore) ? lbFirst : lbLast; + + return lbRet; +} + +/*---------------------------------------------------------------------------------------------- + Return the width of the range of characters. Line-boundary contextualization is handled + by the measure attributes on the glyphs. The width returned is equivalent to the sum of + the widths of any line-segments that would be needed to underline all the characters. + + Since this method is intended to be used on "measured" segments that include the entire + paragraph, it is not very smart about handling cross-line-boundary contextualization. + It basically ignores any glyphs that are rendered by this segment but not officially + part of the segment. +----------------------------------------------------------------------------------------------*/ +float Segment::getRangeWidth(int ichMin, int ichLim, + bool fStartLine, bool fEndLine, bool fSkipSpace) +{ + if (m_dxsWidth < 0) + { + ////SetUpGraphics(ichwBase, pgg, true); + ComputeDimensions(); + } + + Assert(m_dxsWidth >= 0); + Assert(m_dysAscent >= 0); + + //float xsSegRight = m_dxsTotalWidth; + + int ichMinRange = min(ichMin, ichLim); + int ichLimRange = max(ichMin, ichLim); + + //int ichMinSeg = max(ichMinRange, m_ichwMin + m_ichwAssocsMin); + //int ichLimSeg = min(ichLimRange, m_ichwMin + m_ichwAssocsLim); + int ichMinSeg = max(ichMinRange, m_ichwMin); + int ichLimSeg = min(ichLimRange, m_ichwMin + m_dichwLim); + + if (ichLimSeg < m_ichwMin) // not + m_ichwAssocsMin + return 0; + if (ichMinSeg >= m_ichwMin + m_dichwLim) // not m_ichwAssocsLim + return 0; + + if (fSkipSpace) + { + int islout = UnderlyingToLogicalSurface(ichLimSeg - 1, true); + GrSlotOutput * pslout = (islout == kNegInfinity || islout == kPosInfinity) ? + NULL : + OutputSlot(islout); + while (pslout && pslout->IsSpace()) + { + ichLimSeg--; + islout = UnderlyingToLogicalSurface(ichLimSeg - 1, true); +// pslout = OutputSlot(islout); + pslout = (islout == kNegInfinity || islout == kPosInfinity) ? + NULL : OutputSlot(islout); + } + } + + float xsWidth = 0; + SegmentNonPainter segp(this); // doesn't really need to paint + float rgxdLefts[100]; + float rgxdRights[100]; + size_t cxd = 0; + if (ichMinRange < ichLimRange) + cxd = segp.getUnderlinePlacement(ichMinSeg, ichLimSeg, fSkipSpace, + 100, rgxdLefts, rgxdRights, NULL); + for (size_t i = 0; i < cxd; i++) + xsWidth += rgxdRights[i] - rgxdLefts[i]; + + // Add in the line-boundary contextualization. + int isloutFirst = UnderlyingToLogicalSurface(ichMin, true); + int isloutLast = UnderlyingToLogicalSurface(ichLim - 1, false); + int mSol = 0; + int mEol = 0; + if (0 <= isloutFirst && isloutFirst < m_cslout) + mSol = m_prgslout[isloutFirst].MeasureSol(); + if (0 <= isloutLast && isloutLast < m_cslout) + mEol = m_prgslout[isloutLast].MeasureEol(); + float dxsSol = GrEngine::GrIFIMulDiv(mSol, m_xysEmSquare, m_mFontEmUnits); + float dxsEol = GrEngine::GrIFIMulDiv(mEol, m_xysEmSquare, m_mFontEmUnits); + xsWidth += dxsSol; + xsWidth += dxsEol; + + //RestoreFont(pgg); + + return xsWidth; +} + +/*---------------------------------------------------------------------------------------------- + Return the next reasonable breakpoint after ichStart resulting in range that fits in + the specified width. +----------------------------------------------------------------------------------------------*/ +int Segment::findNextBreakPoint(int ichStart, + LineBrk lbPref, LineBrk lbWorst, float dxMaxWidth, + float * pdxBreakWidth, bool fStartLine, bool fEndLine) +{ + ChkGrOutPtr(pdxBreakWidth); + + int ichBreak; + + // First make a rough estimation of how much will fit, using a binary chop approach. + int iginfStart = UnderlyingToPhysicalSurface(ichStart, !m_fWsRtl); + int iginfEnd = UnderlyingToPhysicalSurface(m_ichwMin + m_dichwLim - 1, m_fWsRtl); + int iginfLeft = min(iginfStart, iginfEnd); + int iginfRight= max(iginfStart, iginfEnd); + + int iginfMin = iginfLeft; + int iginfLim = iginfRight; + int ichFit; + if (m_fWsRtl) // right-to-left + { + float xRight = GlyphLeftEdge(iginfRight + 1); + float xLeftMost = xRight - dxMaxWidth; + while (GlyphLeftEdge(iginfLim) < xLeftMost) + { + if (iginfLim - iginfMin <= 1) + break; + int iginfMid = (iginfLim + iginfMin) >> 1; // divide by 2 + if (GlyphLeftEdge(iginfMid) < xLeftMost) + iginfLim = iginfMid; + else + iginfMin = iginfMid; + } + ichFit = PhysicalSurfaceToUnderlying(iginfLim, false); + } + else // left-to-right + { + float xLeft = GlyphLeftEdge(iginfLeft); + float xRightMost = dxMaxWidth - xLeft; // QUESTION: shouldn't this be +, not -? + iginfLim++; + while (GlyphLeftEdge(iginfLim) > xRightMost) + { + if (iginfLim - iginfMin <= 1) + break; + int iginfMid = (iginfLim + iginfMin) >> 1; // divide by 2 + if (GlyphLeftEdge(iginfMid) > xRightMost) + iginfLim = iginfMid; + else + iginfMin = iginfMid; + } + Assert(iginfLim < m_cginf); + if (iginfLim >= m_cginf) iginfLim = m_cginf - 1; + ichFit = PhysicalSurfaceToUnderlying(iginfLim, false); + } + + // Breaking after ichFit should pretty much fit in the space. But make sure. + float dxWidthFit; + while (ichFit > ichStart) + { + dxWidthFit = getRangeWidth(ichStart, ichFit, fStartLine, fEndLine); + if (dxWidthFit <= dxMaxWidth) + break; + ichFit--; + } + + if (ichFit <= ichStart) + { + // Nothing will fit. + return ichStart - 1; + } + + // Now look for a good break. First look forward until the range doesn't fit anymore. + int ichTry = ichFit; + int nExtra = 2; + LineBrk lbBest = klbClipBreak; + int ichBestBreak = -1; + while (nExtra > 0 && ichTry < m_ichwMin + m_dichwLim) + { + float dxWTmp; + dxWTmp = getRangeWidth(ichStart, ichTry, fStartLine, fEndLine); + if (dxWTmp <= dxMaxWidth) + { + LineBrk lbTmp; + lbTmp = getBreakWeight(ichTry - 1, false); + if (lbTmp > 0 && lbTmp < lbWorst && max(lbTmp, lbPref) <= lbBest) + { + ichBestBreak = ichTry; + lbBest = max(lbTmp, lbPref); // any value better than lbPref is insignificant + } + else + { + lbTmp = getBreakWeight(ichTry - 1, true); + if (lbTmp < 0) + { + lbTmp = LineBrk(max(lbTmp * -1L, long(lbPref))); + if (lbTmp < lbWorst && lbTmp <= lbBest) + { + ichBestBreak = ichTry - 1; + lbBest = lbTmp; + } + } + } + } + else + { + // Didn't fit. But it is theoretically possible that actually adding another + // character will! Try a few more. + nExtra--; + } + ichTry++; + } + + if (ichTry >= m_ichwMin + m_dichwLim) + { + // Break at the end of the segment. + ichBreak = m_ichwMin + m_dichwLim; + *pdxBreakWidth = getRangeWidth(ichStart, m_dichwLim, fStartLine, fEndLine); + return ichBreak; + } + + if (ichBestBreak > -1 && lbBest <= lbPref) + { + // Found a reasonable break by looking forward; return it. + ichBreak = ichBestBreak;// + m_ichwMin; + *pdxBreakWidth = getRangeWidth(ichStart, ichBestBreak, fStartLine, fEndLine); + return ichBreak; + } + + // Didn't find a really good break point looking forward from our rough guess. + // Now look backward. + + ichTry = ichFit; + while (ichTry > ichStart) + { + LineBrk lbTmp; + lbTmp = getBreakWeight(ichTry - 1, false); + if (lbTmp > 0 && lbTmp < lbWorst && max(lbTmp, lbPref) < lbBest) + { + ichBestBreak = ichTry; + lbBest = max(lbTmp, lbPref); // any value better than lbPref is insignificant + } + else + { + lbTmp = getBreakWeight(ichTry - 1, true); + if (lbTmp < 0) + { + lbTmp = LineBrk(max(lbTmp * -1L, long(lbPref))); + if (lbTmp < lbWorst && lbTmp < lbBest) + { + ichBestBreak = ichTry - 1; + lbBest = lbTmp; + } + } + } + if (lbBest <= lbPref) + break; + + ichTry--; + } + + if (ichBestBreak > -1 && ichBestBreak > ichStart) + { + // Found a reasonable break by looking backward; return it. + ichBreak = ichBestBreak;// + m_ichwMin; + *pdxBreakWidth = getRangeWidth(ichStart, ichBestBreak, fStartLine, fEndLine); + return ichBreak; + } + + // No reasonable break. + return ichStart - 1; +} + +/*---------------------------------------------------------------------------------------------- + Private; used by findNextBreakPoint(). +----------------------------------------------------------------------------------------------*/ +float Segment::GlyphLeftEdge(int iginf) +{ + if (iginf >= m_cginf) + return m_prgginf[m_cginf - 1].origin() + m_prgginf[m_cginf - 1].advanceWidth(); +// return m_prgginf[m_cginf].origin() + m_prgginf[m_cginf].advanceWidth(); + else + return m_prgginf[iginf].origin(); +} + + +//:>******************************************************************************************** +//:> Other methods +//:>******************************************************************************************** + +/*---------------------------------------------------------------------------------------------- + Get the embedded string. For temporary use; AddRef is not automatically called. + Do not release unless you AddRef. +----------------------------------------------------------------------------------------------*/ +ITextSource * Segment::GetString() +{ + return m_pgts; +} + +/*---------------------------------------------------------------------------------------------- + Compute the ascent, height, width, and overhangs of the segment. + The ascent is the ascent of the font (distance from the baseline to the top) + plus any extra ascent height specified by the font. + The descent is distance from the baseline to the bottom of font, plus any extra descent + specified by the font (ExtraDescent). + The ascent overhang is the distance from the top of the font to the top of the highest + glyph, or zero if all the glyphs fit within the ascent. + The descent overhang is the distance from the bottom of the font to the bottom of the lowest + glyph, or zero if all the glyphs fit within the descent. + The height is the sum of the ascent and descent. +----------------------------------------------------------------------------------------------*/ +void Segment::ComputeDimensions() +{ + if (m_fEndLine) + m_dxsWidth = m_dxsVisibleWidth; // don't include trailing white space. + else + m_dxsWidth = m_dxsTotalWidth; + + if (m_dxsWidth == -1) + { + for (int iginf = 0; iginf < m_cginf; iginf++) + { + GlyphInfo * pginf = m_prgginf + iginf; + if (pginf->isSpace()) + continue; + m_dxsWidth = max(m_dxsWidth, (pginf->origin() + pginf->advanceWidth())); + } + } + + int dysNegFontDescent = (int)m_dysFontDescent * -1; + + // if there were no glyphs in the segment then there is sometimes no EngineImpl + if (!EngineImpl()) + { + Assert(m_cslout == 0); + Assert(m_cginf == 0); + m_dxsWidth = 0; + m_dysAscent = 0; // max(0.0, m_dysFontAscent); + m_dysHeight = 0; // max(0.0, m_dysAscent - dysNegFontDescent); + m_dysAscentOverhang = 0; + m_dysDescentOverhang = 0; + m_dxsLeftOverhang = 0; + m_dxsRightOverhang = 0; + return; + } + + // Calculate the extra ascent and descent. + int mXAscent = EngineImpl()->ExtraAscent(); + int mXDescent = EngineImpl()->ExtraDescent(); + + m_dysXAscent = GrEngine::GrIFIMulDiv(mXAscent, m_xysEmSquare, m_mFontEmUnits); + m_dysXDescent = GrEngine::GrIFIMulDiv(mXDescent, m_xysEmSquare, m_mFontEmUnits); + + m_dysAscent = m_dysFontAscent + m_dysXAscent; + float dysNegDescent = dysNegFontDescent - m_dysXDescent; // dysNegDescent < 0 + m_dysHeight = m_dysAscent - dysNegDescent; + + // Calculate the overhangs. + float dysVisAscent = m_dysAscent; + float dysNegVisDescent = dysNegDescent; + float dxsVisLeft = 0; + float dxsVisRight = m_dxsTotalWidth; + ComputeOverhangs(&dysVisAscent, &dysNegVisDescent, &dxsVisLeft, &dxsVisRight); + m_dysAscentOverhang = max(float(0), (dysVisAscent - m_dysAscent)); + m_dysDescentOverhang = max(float(0), (dysNegDescent - dysNegVisDescent)); + m_dxsLeftOverhang = min(float(0), dxsVisLeft); + m_dxsRightOverhang = max(float(0), dxsVisRight - m_dxsTotalWidth); + + m_dysOffset = EngineImpl()->VerticalOffset(); +} + +/*---------------------------------------------------------------------------------------------- + Compute the visible ascent and descent, based on how much the bounding box of any + glyph may extend beyond the ascent or descent of the font. + Also compute the left- and right-overhangs. +----------------------------------------------------------------------------------------------*/ +void Segment::ComputeOverhangs(float * pdysVisAscent, float * pdysNegVisDescent, + float * pdxsVisLeft, float * pdxsVisRight) +{ + for (int iginf = 0; iginf < m_cginf; iginf++) + { + *pdysVisAscent = max(*pdysVisAscent, m_prgginf[iginf].bb().top); + *pdysNegVisDescent = min(*pdysNegVisDescent, m_prgginf[iginf].bb().bottom); + + *pdxsVisLeft = min(*pdxsVisLeft, m_prgginf[iginf].bb().left); + *pdxsVisRight = max(*pdxsVisRight, m_prgginf[iginf].bb().right); + } +} + +/*---------------------------------------------------------------------------------------------- + Create the arrays to store the final output and association information. +----------------------------------------------------------------------------------------------*/ +void Segment::SetUpOutputArrays(Font * pfont, GrTableManager * ptman, + GrSlotStream * psstrmFinal, + int cchwInThisSeg, int csloutSurface, gid16 chwLB, + TrWsHandling twsh, bool fParaRtl, int nDirDepth, bool fEmpty) +{ + m_mFontEmUnits = EngineImpl()->GetFontEmUnits(); + + pfont->getFontMetrics(&m_dysFontAscent, &m_dysFontDescent, &m_xysEmSquare); + m_xsDPI = (float)pfont->getDPIx(); + m_ysDPI = (float)pfont->getDPIy(); + + // Note that storing both of these values is redundant; they should be the same. + Assert(m_xysEmSquare == m_pixHeight); + + m_twsh = twsh; + m_fParaRtl = fParaRtl; + if (m_twsh == ktwshOnlyWs) + m_nDirDepth = (int)fParaRtl; + else if (fParaRtl && nDirDepth == 0) + m_nDirDepth = 2; + else + m_nDirDepth = nDirDepth; + + // Create association mappings and set up final versions of slots. + + Assert((psstrmFinal == NULL) == fEmpty); + + m_ichwAssocsMin = 0; + m_ichwAssocsLim = cchwInThisSeg; + + m_prgisloutBefore = new int[cchwInThisSeg]; + + m_prgisloutAfter = new int[cchwInThisSeg]; + + m_prgvisloutAssocs = new std::vector<int>[cchwInThisSeg]; + + m_prgisloutLigature = new int[cchwInThisSeg]; + + m_prgiComponent = new int[cchwInThisSeg]; + + int cslot = 0; + m_psstrm = psstrmFinal; // TODO: make a local variable + if (m_psstrm) + cslot = m_psstrm->FinalSegLim(); + else + Assert(fEmpty); + + float xsMin = 0; + int islot; + int islotMin = (m_psstrm) ? m_psstrm->IndexOffset() : 0; + for (islot = islotMin; islot < cslot; islot++) + xsMin = min(xsMin, m_psstrm->SlotAt(islot)->XPosition()); + + // For right-to-left segments, the draw origin is at the left side of the visible + // portion of the text. So if necessary, scoot everything left so that the invisible + // trailing white-space is to the left of the draw position. + Assert(m_dxsTotalWidth > -1); + Assert(m_dxsVisibleWidth > -1); + float dxsInvisible = (m_fEndLine) ? m_dxsTotalWidth - m_dxsVisibleWidth : 0; + for (islot = islotMin; ((m_nDirDepth % 2) && (islot < cslot)); islot++) + { + GrSlotState * pslot = m_psstrm->SlotAt(islot); + // int islout = islot - islotMin; + if (pslot->GlyphID() == chwLB) + continue; // skip over linebreak markers + if (m_nDirDepth % 2) // redundant test + { + // RTL will have descending neg values, adjust to be positive. Also make sure + // invisible trailing white space is to the left of the draw origin. + // Review: will changing this value have side affects? + pslot->SetXPos(pslot->XPosition() - xsMin - dxsInvisible); + } + } + + Assert(kPosInfinity > 0); + Assert(kNegInfinity < 0); + for (int ichw = 0; ichw < cchwInThisSeg; ++ichw) + { + m_prgisloutBefore[ichw] = kPosInfinity; + m_prgisloutAfter[ichw] = kNegInfinity; + m_prgvisloutAssocs[ichw].clear(); + m_prgisloutLigature[ichw] = kNegInfinity; + m_prgiComponent[ichw] = 0; + } + + m_cslout = csloutSurface; + m_cnUserDefn = ptman->NumUserDefn(); + m_cnCompPerLig = ptman->NumCompPerLig(); + m_cnFeat = ptman->NumFeat(); + // Normal buffers, plus underlying indices of ligature components, plus + // map from used components to defined components. + int cnExtraPerSlot = m_cnUserDefn + (m_cnCompPerLig * 2) + m_cnFeat + (m_cnCompPerLig * 2); + m_prgslout = new GrSlotOutput[m_cslout]; + m_prgnSlotVarLenBuf = new u_intslot[m_cslout * cnExtraPerSlot]; + + m_isloutVisLim = 0; + if (m_psstrm) + { + for (islot = islotMin; islot < psstrmFinal->FinalSegLim(); ++islot) + { + int isloutAdj = islot - islotMin; + + GrSlotState * pslot = psstrmFinal->SlotAt(islot); + pslot->SetPosPassIndex(isloutAdj, false); + pslot->EnsureCacheForOutput(ptman); + GrSlotOutput * pslout = OutputSlot(isloutAdj); + pslout->SetBufferPtr(m_prgnSlotVarLenBuf + (isloutAdj * cnExtraPerSlot)); + pslout->CopyFrom(pslot); + pslout->SetBeforeAssoc(pslot->BeforeAssoc()); + pslout->SetAfterAssoc(pslot->AfterAssoc()); + if (pslot->HasComponents()) + pslot->SetComponentRefsFor(pslout); // also sets it for pslout, since they share the buffer + //if (pslot->HasClusterMembers()) + //{ + //Assert(pslot->HasClusterMembers() || pslot->ClusterRootOffset() == 0); + pslout->SetClusterXOffset(pslot->ClusterRootOffset() * -1); + pslout->SetClusterAdvance(pslot->ClusterAdvWidthFrom(0)); + + //} + + if (!ptman->IsWhiteSpace(pslot)) + m_isloutVisLim = islot + 1; + } + } + + // Set up the clusters. + for (islot = islotMin; islot < m_cslout; islot++) + { + GrSlotState * pslot = psstrmFinal->SlotAt(islot); + int isloutAdj = islot - islotMin; + int isloutBaseIndex = pslot->Base(psstrmFinal)->PosPassIndex(); + if (!pslot->IsBase()) + { + OutputSlot(isloutBaseIndex)->AddClusterMember(pslot->PosPassIndex()); + OutputSlot(isloutAdj)->SetClusterBase(isloutBaseIndex); + } + else if (pslot->HasClusterMembers() && pslot->IsBase()) + { + Assert(isloutBaseIndex == islot - islotMin); + OutputSlot(isloutAdj)->SetClusterBase(isloutBaseIndex); + } + } + + ///AssertValidClusters(psstrmFinal); + +#ifdef OLD_TEST_STUFF + if (ptman->GlyphTable() == NULL) + return; // test procedures +#endif // OLD_TEST_STUFF + + // CalcPositionsUpTo() called on final pass already from Table Mgr + + // Final output for draw routines. + + SetUpGlyphInfo(ptman, chwLB, nDirDepth, islotMin, cslot); + + //SetUpOutputArraysPlatform(ptman, chwLB, nDirDepth, islotMin, cslot); + +} + +/*---------------------------------------------------------------------------------------------- + Set up the data structures that represent the actual rendered glyphs for the new segment. +----------------------------------------------------------------------------------------------*/ +void Segment::SetUpGlyphInfo(GrTableManager * ptman, gid16 chwLB, int nDirDepth, + int islotMin, int cslot) +{ + //int paraDirLevel = (ptman->State()->ParaRightToLeft()) ? 1 : 0; + + m_cginf = 0; + + int islot; + for (islot = islotMin; islot < cslot; islot++) + { + if (m_psstrm->SlotAt(islot)->GlyphID() != chwLB) + { + m_cginf++; + } + } + + // For right-to-left segments, the draw origin will be at the left side of the visible + // portion of the text. So if necessary, scoot everything left so that the invisible + // trailing white-space is to the left of the draw position. + Assert(m_dxsTotalWidth > -1); + Assert(m_dxsVisibleWidth > -1); + //float dxsInvisible = (m_fEndLine) ? m_dxsTotalWidth - m_dxsVisibleWidth : 0; + + m_prgginf = new GlyphInfo [m_cginf]; + + m_isloutGinf0 = -1; + int iginf = 0; + for (int islot = islotMin; islot < cslot; islot++) + { + GrSlotState * pslot = m_psstrm->SlotAt(islot); + + if (pslot->GlyphID() == chwLB) + { + continue; // skip over linebreak markers + } + + int islout = islot - islotMin; + GrSlotOutput * pslout = OutputSlot(islout); + + if (m_isloutGinf0 == -1) + m_isloutGinf0 = islout; + + m_prgginf[iginf].m_pslout = pslout; + m_prgginf[iginf].m_islout = islout; + m_prgginf[iginf].m_pseg = this; + + // Fill in stuff in the output slot that is needed by the GlyphInfo object. + pslout->m_xsAdvanceX = pslot->GlyphMetricLogUnits(ptman, kgmetAdvWidth); + pslout->m_ysAdvanceY = pslot->GlyphMetricLogUnits(ptman, kgmetAdvHeight); + pslout->m_rectBB.top + = pslot->YPosition() + pslot->GlyphMetricLogUnits(ptman, kgmetBbTop); + pslout->m_rectBB.bottom + = pslot->YPosition() + pslot->GlyphMetricLogUnits(ptman, kgmetBbBottom); + pslout->m_rectBB.left + = pslot->XPosition() + pslot->GlyphMetricLogUnits(ptman, kgmetBbLeft); + if (pslot->IsSpace(ptman)) + pslout->m_rectBB.right + = pslot->XPosition() + pslot->GlyphMetricLogUnits(ptman, kgmetAdvWidth); + else + pslout->m_rectBB.right + = pslot->XPosition() + pslot->GlyphMetricLogUnits(ptman, kgmetBbRight); + + iginf++; + } + + if (cslot - islotMin == 0) + m_isloutGinf0 = 0; + + Assert(m_isloutGinf0 == 0 || m_isloutGinf0 == 1); +} + +/* +// OLD VERSION +void Segment::SetUpGlyphInfo(GrTableManager * ptman, gid16 chwLB, int nDirDepth, + int islotMin, int cslot) +{ + int paraDirLevel = (ptman->State()->ParaRightToLeft()) ? 1 : 0; + + m_cginf = 0; + int islot; + for (islot = islotMin; islot < cslot; islot++) + { + if (m_psstrm->SlotAt(islot)->GlyphID() != chwLB) + m_cginf++; + } + + // For right-to-left segments, the draw origin will be at the left side of the visible + // portion of the text. So if necessary, scoot everything left so that the invisible + // trailing white-space is to the left of the draw position. + Assert(m_dxsTotalWidth > -1); + Assert(m_dxsVisibleWidth > -1); + float dxsInvisible = (m_fEndLine) ? m_dxsTotalWidth - m_dxsVisibleWidth : 0; + + m_prgginf = new GlyphInfo [m_cginf]; + + // Fill in glyph information structures. + m_isloutGinf0 = -1; + for (islot = islotMin; islot < cslot; islot++) + { + GrSlotState * pslot = m_psstrm->SlotAt(islot); + + int islout = islot - islotMin; + if (pslot->GlyphID() == chwLB) + { + continue; // skip over linebreak markers + } + if (m_isloutGinf0 == -1) + m_isloutGinf0 = islout; + + GrSlotOutput * pslout = OutputSlot(islout); + + int iginf = islout - m_isloutGinf0; + GlyphInfo & ginf = m_prgginf[iginf]; + + ginf.glyphID = pslot->ActualGlyphForOutput(ptman); + ginf.pseudoGlyphID = pslot->GlyphID(); + if (ginf.glyphID == ginf.pseudoGlyphID) + ginf.pseudoGlyphID = 0; + + ginf.isSpace = pslot->IsSpace(ptman); + + ginf.origin = pslot->XPosition(); + ginf.yOffset = pslot->YPosition(); + + ginf.bbLeft = pslot->XPosition() + pslot->GlyphMetricLogUnits(ptman, kgmetBbLeft); + ginf.bbTop = pslot->YPosition() + pslot->GlyphMetricLogUnits(ptman, kgmetBbTop); + if (ginf.isSpace) + { + ginf.bbRight = pslot->XPosition() + pslot->GlyphMetricLogUnits(ptman, kgmetAdvWidth); + } + else + { + ginf.bbRight = pslot->XPosition() + pslot->GlyphMetricLogUnits(ptman, kgmetBbRight); + } + ginf.bbBottom = pslot->YPosition() + pslot->GlyphMetricLogUnits(ptman, kgmetBbBottom); + + ginf.advanceWidth = pslot->GlyphMetricLogUnits(ptman, kgmetAdvWidth); + ginf.advanceHeight = 0; + + if (pslot->AttachTo() == 0) // not attached + ginf.attachedTo = -1; + else + ginf.attachedTo = pslot->AttachTo() + islot; + + ginf.directionality = byte(pslot->Directionality()); + if (pslot->DirLevel() == -1) + ginf.directionLevel = paraDirLevel; + else + ginf.directionLevel = byte(pslot->DirLevel()); + + //ginf.firstChar = LogicalSurfaceToUnderlying(islout, true); + //ginf.lastChar = LogicalSurfaceToUnderlying(islout, false); + + if (pslot->HasComponents()) + { + SetUpLigInfo(ptman, ginf, pslout); + } + else + { + ginf.numberOfComponents = 0; + ginf.components = NULL; + } + + ginf.insertBefore = pslot->InsertBefore(); + + ginf.maxStretch[0] = ptman->EmToLogUnits(pslot->JStretch()); + ginf.maxShrink[0] = ptman->EmToLogUnits(pslot->JShrink()); + ginf.stretchStep[0]= ptman->EmToLogUnits(pslot->JStep()); + ginf.justWidth[0] = ptman->EmToLogUnits(pslot->JWidth()); + ginf.justWeight[0] = pslot->JWeight(); + + ginf.measureStartOfLine = pslout->MeasureSolLogUnits(); + ginf.measureEndOfLine = pslout->MeasureEolLogUnits(); + } +} +*/ + +/*---------------------------------------------------------------------------------------------- + Set up the data structures that represent the components of a ligature glyph. +----------------------------------------------------------------------------------------------*/ +/* +void Segment::SetUpLigInfo(GrTableManager * ptman, GlyphInfo & ginf, GrSlotOutput * pslout) +{ + GrGlyphTable * pgtbl = ptman->GlyphTable(); + + int ccomp = pslout->NumberOfComponents(); + ginf.numberOfComponents = ccomp; + ginf.components = new ComponentBox[ccomp]; + for (int icomp = 0; icomp < ccomp; icomp++) + { + ComponentBox & cb = ginf.components[icomp]; + + // TODO: Rework ComponentIndexForGlyph to take slati rather than iForGlyph. + float xsLeft, xsRight, ysTop, ysBottom; + int slati = pslout->ComponentId(icomp); + int iForGlyph = pgtbl->ComponentIndexForGlyph(pslout->GlyphID(), slati); + pgtbl->ComponentBoxLogUnits(m_xysEmSquare, pslout->GlyphID(), iForGlyph, + m_mFontEmUnits, m_dysAscent, + &xsLeft, &ysTop, &xsRight, &ysBottom, false); + + cb.firstChar = pslout->UnderlyingComponent(icomp); + cb.lastChar = cb.firstChar; // TODO: is this supported? + cb.left = xsLeft; + cb.right = xsRight; + cb.top = ysTop; + cb.bottom = ysBottom; + } +} +*/ + + +// --------- OBSOLETE - replaced by GrTableManager::AdjustAssocsForOverlaps() --------------- +#if 0 + +/*---------------------------------------------------------------------------------------------- + Now that this segment is finished and has its associations set, handle any overlaps in + the associations between the previous segment and this one. + + Review: Think about whether we need to distinguish between before==kPosInfinity meaning + that the glyph is in the next segment vs. there is no association at all. + OBSOLETE +----------------------------------------------------------------------------------------------*/ +void Segment::AdjustForOverlapsWithPrevSeg() +{ + if (m_psegPrev) + { + AdjustBeforeArrayFromPrevSeg(); + m_psegPrev->AdjustAfterArrayFromNextSeg(); + } +} + +/*---------------------------------------------------------------------------------------------- + For any characters that are officially in this segment but are rendered in + the previous segment, initialize this segment's before array appropriately. Specifically, + set this segment's before value to something invalid--negative infinity, which will + never be overwritten. + OBSOLETE - replaced by AdjustAfterArrayFromNextSeg +----------------------------------------------------------------------------------------------*/ +void Segment::InitBeforeArrayFromPrevSeg() +{ + Assert(m_psegPrev); + + int ichwPrevMin = m_psegPrev->m_ichwMin; + int ichwPrevLim = m_psegPrev->m_ichwLim; +// int ichwPrevContextLim = +// m_psegPrev->m_ichwAssocsLim - m_psegPrev->m_ichwAssocsMin + m_psegPrev->m_ichwMin; + int cchwPrevAssocsMin = m_psegPrev->m_ichwAssocsMin; + int cchwPrevAssocsLim = m_psegPrev->m_ichwAssocsLim; + + // Loop over associations appended to the end of the previous segment; + // ichwPrev and ichwThis are relative to the start of their respective segments. + for (int ichwPrev = ichwPrevLim - ichwPrevMin; ichwPrev < cchwPrevAssocsLim; ++ichwPrev) + { + int ichwThis = ichwPrev - (ichwPrevLim - ichwPrevMin); // relative to start of this seg + if (m_psegPrev->m_prgisloutBefore[ichwPrev - cchwPrevAssocsMin] < kPosInfinity) + // The "before" value is in the previous segment, so make this segment's + // before value invalid. + m_prgisloutBefore[ichwThis - m_ichwAssocsMin] = kNegInfinity; + } +} + +/*---------------------------------------------------------------------------------------------- + For any associations that overlap between the previous segment and this, adjust + this segment's before array appropriately. Specifically, for any characters that + are rendered in both, set this segment's before value to something invalid. + OBSOLETE +----------------------------------------------------------------------------------------------*/ +void Segment::AdjustBeforeArrayFromPrevSeg() +{ + Assert(m_psegPrev); + + int ichwPrevMin = m_psegPrev->m_ichwMin; + int ichwPrevLim = m_psegPrev->m_ichwLim; + int ichwPrevAssocsMin = m_psegPrev->m_ichwAssocsMin; + int ichwPrevAssocsLim = m_psegPrev->m_ichwAssocsLim; + + // Loop over associations appended to the end of the previous segment + // (ie, rendered in the previous segment but officially in this one); + // ichwPrev and ichwThis are relative to the start of their respective segments. + for (int ichwPrev = ichwPrevLim - ichwPrevMin; ichwPrev < ichwPrevAssocsLim; ++ichwPrev) + { + int ichwThis = ichwPrev - (ichwPrevLim - ichwPrevMin); // relative to start of this seg + if (m_psegPrev->m_prgisloutBefore[ichwPrev - ichwPrevAssocsMin] < kPosInfinity) + // The "before" value is in the previous segment, so make this segment's + // before value invalid. + m_prgisloutBefore[ichwThis - m_ichwAssocsMin] = kNegInfinity; + } + // Loop over associations prepended to the start of this segment + // (ie, rendered in this segment but officially in the previous one); + // ichwPrev and ichwThis are relative to the start of their respective segments. + for (int ichwThis = m_ichwAssocsMin; ichwThis < 0; ++ichwThis) + { + int ichwPrev = ichwThis + (ichwPrevLim - ichwPrevMin); + if (m_psegPrev->m_prgisloutBefore[ichwPrev - ichwPrevAssocsMin] < kPosInfinity) + // The "before" value is in the previous segment, so make this segment's + // before value invalid. + m_prgisloutBefore[ichwThis - m_ichwAssocsMin] = kNegInfinity; + } +} + +/*---------------------------------------------------------------------------------------------- + For any associations that overlap between this segment and the next, adjust + this segment's after array appropriately. Specifically, for any characters that + are rendered in both, set this segment's after value to something invalid. + OBSOLETE +----------------------------------------------------------------------------------------------*/ +void Segment::AdjustAfterArrayFromNextSeg() +{ + Assert(m_psegNext); + + int ichwNextMin = m_psegNext->m_ichwMin; + int ichwNextContextMin = ichwNextMin + m_psegNext->m_ichwAssocsMin; + int ichwNextAssocsMin = m_psegNext->m_ichwAssocsMin; + + // Loop over associations prepended to the beginning of the following segment + // (ie, rendered in the next segment but officially in this one); + // ichwThis and ichwNext are relative to the start of their respective segments. + for (int ichwNext = cchwNextAssocsMin; ichwNext < 0; ++ichwNext) + { + int ichwThis = ichwNext + (m_ichwLim - m_ichwMin); + if (m_psegNext->m_prgisloutAfter[ichwNext - ichwNextAssocsMin] > kNegInfinity) + // The "after" value is in the next segment, so make this segment's + // after value invalid. + m_prgisloutAfter[ichwThis - m_ichwAssocsMin] = kPosInfinity; + } + + // Loop over associations appended to the end of this segment + // (ie, rendered in this segment but officially in the next one); + // ichwThis and ichwNext are relative to the start of their respective segments. + for (int ichwThis = m_ichwLim - m_ichwMin; ichwThis < m_cchwAssocsLim; ++ichwThis) + { + int ichwNext = ichwThis - (m_ichwLim - m_ichwMin); + if (m_psegNext->m_prgisloutAfter[ichwNext - ichwNextAssocsMin] > kNegInfinity) + // The "after" value is in the next segment, so make this segment's + // after value invalid. + m_prgisloutAfter[ichwThis - m_ichwAssocsMin] = kPosInfinity; + } +} + +#endif // -------------------------- END OF OBSOLETE CODE ---------------------------------- + +/*---------------------------------------------------------------------------------------------- + Record an underlying-to-surface association mapping, based on the fact that there is + a corresponding surface-to-underlying association in the streams. + + @param ichwUnder - character index relative to the official beginning of the segment + @param islotSurface - surface glyph it maps to +----------------------------------------------------------------------------------------------*/ +void Segment::RecordSurfaceAssoc(int ichwUnder, int islotSurface, int nDir) +{ + // If we are recording an association across the line boundary, make sure there + // is space for it. + EnsureSpaceAtLineBoundaries(ichwUnder); + + // For left-to-right chars, the before-value is the minimum of the previous + // and new values, and the after-value is the maximum. + if (nDir % 2 == 0) + { + // left-to-right + m_prgisloutBefore[ichwUnder - m_ichwAssocsMin] = + min(m_prgisloutBefore[ichwUnder - m_ichwAssocsMin], islotSurface); + m_prgisloutAfter[ ichwUnder - m_ichwAssocsMin] = + max(m_prgisloutAfter[ ichwUnder - m_ichwAssocsMin], islotSurface); + } + else + { + // right-to-left: the before-value is the max and the after-value is the min + m_prgisloutBefore[ichwUnder - m_ichwAssocsMin] = + max(m_prgisloutBefore[ichwUnder - m_ichwAssocsMin], islotSurface); + m_prgisloutAfter[ ichwUnder - m_ichwAssocsMin] = + min(m_prgisloutAfter[ ichwUnder - m_ichwAssocsMin], islotSurface); + } + + m_prgvisloutAssocs[ichwUnder - m_ichwAssocsMin].push_back(islotSurface); +} + +/*---------------------------------------------------------------------------------------------- + Record an underlying-to-surface ligature mapping, based on the fact that there is + a component.ref attribute in the surface stream. Note that if there was a previous + ligature value there, it will be overwritten. + + @param ichwUnder - character index relative to the official beginning of the segment + @param islotSurface - surface glyph it maps to + @param iComponent - which component this glyph represents +----------------------------------------------------------------------------------------------*/ +void Segment::RecordLigature(int ichwUnder, int islotSurface, int iComponent) +{ + // If we are recording a mapping across the line boundary, make sure there + // is space for it. + EnsureSpaceAtLineBoundaries(ichwUnder); + + Assert(m_prgisloutLigature[ichwUnder - m_ichwAssocsMin] == kNegInfinity); + + m_prgisloutLigature[ichwUnder - m_ichwAssocsMin] = islotSurface; + m_prgiComponent[ ichwUnder - m_ichwAssocsMin] = iComponent; +} + +/*---------------------------------------------------------------------------------------------- + The given slot is one that is rendered in the previous segment. + If it is associated with a character officially in this segment, mark the + before-association kNegInfinity. + + @param ichwUnder - character index relative to the official beginning of the segment + @param islot - processed glyph it maps to +----------------------------------------------------------------------------------------------*/ +void Segment::MarkSlotInPrevSeg(int ichwUnder, int islot) +{ + if (ichwUnder >= m_ichwAssocsMin) + m_prgisloutBefore[ichwUnder - m_ichwAssocsMin] = kNegInfinity; +} + +/*---------------------------------------------------------------------------------------------- + The given slot is one that is rendered in the following segment. + If it is associated with a character officially in this segment, mark the + after-association kPosInfinity. + + @param ichwUnder - character index relative to the official beginning of the segment + @param islot - processed glyph it maps to +----------------------------------------------------------------------------------------------*/ +void Segment::MarkSlotInNextSeg(int ichwUnder, int islot) +{ + if (ichwUnder < m_ichwAssocsLim) + m_prgisloutAfter[ichwUnder - m_ichwAssocsMin] = kPosInfinity; +} + +/*---------------------------------------------------------------------------------------------- + If we are recording an association across the line boundary (in either direction), + make sure there is space for it. This involves adding space to the beginning or + the end of the association arrays, and adjusting the min and lim indicators. +----------------------------------------------------------------------------------------------*/ +void Segment::EnsureSpaceAtLineBoundaries(int ichwUnder) +{ + int cchwNewMin = min(ichwUnder, m_ichwAssocsMin); + int cchwNewLim = max(ichwUnder+1, m_ichwAssocsLim); + if (cchwNewMin < m_ichwAssocsMin || cchwNewLim > m_ichwAssocsLim) + { + // Make space either at the beginning or the end of the arrays. + int cchwPreAdd = m_ichwAssocsMin - cchwNewMin; + int cchwPostAdd = cchwNewLim - m_ichwAssocsLim; + + int * prgisloutTmp = m_prgisloutBefore; + m_prgisloutBefore = new int[cchwNewLim - cchwNewMin]; + std::copy(prgisloutTmp, prgisloutTmp + (m_ichwAssocsLim - m_ichwAssocsMin), + m_prgisloutBefore + cchwPreAdd); + delete[] prgisloutTmp; + + prgisloutTmp = m_prgisloutAfter; + m_prgisloutAfter = new int[cchwNewLim - cchwNewMin]; + std::copy(prgisloutTmp, prgisloutTmp + (m_ichwAssocsLim - m_ichwAssocsMin), + m_prgisloutAfter + cchwPreAdd); + delete[] prgisloutTmp; + + std::vector<int> * pvisloutTmp = m_prgvisloutAssocs; + m_prgvisloutAssocs = new std::vector<int>[cchwNewLim - cchwNewMin]; + std::swap_ranges(m_prgvisloutAssocs + cchwPreAdd, + m_prgvisloutAssocs + cchwPreAdd + + (m_ichwAssocsLim - m_ichwAssocsMin), pvisloutTmp); + delete[] pvisloutTmp; + + prgisloutTmp = m_prgisloutLigature; + m_prgisloutLigature = new int[cchwNewLim - cchwNewMin]; + std::copy(prgisloutTmp, prgisloutTmp + (m_ichwAssocsLim - m_ichwAssocsMin), + m_prgisloutLigature + cchwPreAdd); + delete[] prgisloutTmp; + + prgisloutTmp = m_prgiComponent; + m_prgiComponent = new int[cchwNewLim - cchwNewMin]; + std::copy(prgisloutTmp, prgisloutTmp + (m_ichwAssocsLim - m_ichwAssocsMin), + m_prgiComponent + cchwPreAdd); + delete[] prgisloutTmp; + + // Initialize new slots. + int i; + for (i = 0; i < cchwPreAdd; ++i) + { + m_prgisloutBefore[i] = kPosInfinity; + m_prgisloutAfter[i] = kNegInfinity; + m_prgvisloutAssocs[i].clear(); + m_prgisloutLigature[i] = kNegInfinity; + m_prgiComponent[i] = 0; + } + for (i = m_ichwAssocsLim - m_ichwAssocsMin + cchwPreAdd; + i < m_ichwAssocsLim - m_ichwAssocsMin + cchwPreAdd + cchwPostAdd; + ++i) + { + m_prgisloutBefore[i] = kPosInfinity; + m_prgisloutAfter[i] = kNegInfinity; + m_prgvisloutAssocs[i].clear(); + m_prgisloutLigature[i] = kNegInfinity; + m_prgiComponent[i] = 0; + } + m_ichwAssocsMin = cchwNewMin; + m_ichwAssocsLim = cchwNewLim; + } +} + +/*---------------------------------------------------------------------------------------------- + Return the given output slot. +----------------------------------------------------------------------------------------------*/ +GrSlotOutput * Segment::OutputSlot(int islout) +{ + return m_prgslout + islout; +} + +/*---------------------------------------------------------------------------------------------- + Set the rendering engine to the current one. +----------------------------------------------------------------------------------------------*/ +void Segment::SetEngine(GrEngine * pgreng) +{ + //if (m_preneng) + // m_preneng->DecRefCount(); + m_preneng = pgreng; + //if (m_preneng) + // m_preneng->IncRefCount(); +} + +/*---------------------------------------------------------------------------------------------- + Set the font. +----------------------------------------------------------------------------------------------*/ +void Segment::SetFont(Font * pfont) +{ + m_pfont = pfont->copyThis(); + + m_fBold = pfont->bold(); + m_fItalic = pfont->italic(); + // Note that we store the character height (which does not include the internal leading), + // not the actual font height, ie, ascent + descent. This is what is used in the LOGFONT. + pfont->getFontMetrics(NULL, NULL, &m_pixHeight); +} + +/*---------------------------------------------------------------------------------------------- + Switch the font out. + ??? Do we need to change the member variables too? +----------------------------------------------------------------------------------------------*/ +void Segment::SwitchFont(Font * pfont) +{ + m_pfont = pfont; +} + +/*---------------------------------------------------------------------------------------------- + Return the engine implementation if it is a proper GrEngine, or NULL otherwise. +----------------------------------------------------------------------------------------------*/ +GrEngine * Segment::EngineImpl() +{ + return m_preneng; +} + +/*---------------------------------------------------------------------------------------------- + Set the justification agent, in case this segment will ever need to be stretched + or shrunk. +----------------------------------------------------------------------------------------------*/ +void Segment::SetJustifier(IGrJustifier * pgjus) +{ + m_pgjus = pgjus; + //if (pgjus) + // pgjus->JustifierObject(&m_pgjus); + //else + // m_pgjus = NULL; +} + +/*---------------------------------------------------------------------------------------------- + Set the graphics object to use the base font. +----------------------------------------------------------------------------------------------*/ +//void Segment::SetToBaseFont(IGrGraphics * pgg) +//{ +// if (m_fUseSepBase) +// EngineImpl()->SwitchGraphicsFont(pgg, true); +//} + +/*---------------------------------------------------------------------------------------------- + Restore the graphics object to use the font that it originally had when passed in to + the interface method. +----------------------------------------------------------------------------------------------*/ +//void Segment::RestoreFont(IGrGraphics * pgg) +//{ +// if (m_fUseSepBase) +// EngineImpl()->SwitchGraphicsFont(pgg, false); +//} + +/*---------------------------------------------------------------------------------------------- + Given a logical surface location, return the underlying position (relative to the + beginning of the string). NOTE that we are returning the index of the closest charater + (plus an indication of which side the click was on), not a position between characters. + + @param islout - output slot + @param xsOffset - x coordinate of physical click, relative to left of glyph; + kPosInfinity means all the way to the right; + kNegInfinity means all the way to the left + @param ysOffset - y coordinate of physical click, relative to TOP of segment (NOT + upwards from baseline); kPosInfinity means all the way at + the top; kNegInfinity means all the way at the bottom + @param dxsGlyphWidth - width of glyph clicked on, or 0 if irrelevant + @param dysGlyphHeight - actually line height -- CURRENTLY NOT USED + @param pfAfter - return true if they clicked on trailing side; possibly NULL +----------------------------------------------------------------------------------------------*/ +int Segment::LogicalSurfaceToUnderlying(int islout, float xsOffset, float ysClick, + float dxsGlyphWidth, float dysGlyphHeight, bool * pfAfter) +{ + Assert(islout >= 0); + Assert(islout < m_cslout); + + GrSlotOutput * pslout = OutputSlot(islout); + bool fGlyphRtl = SlotIsRightToLeft(pslout); + + int dichw; + bool fAfter, fRight; + + float ysForGlyph = pslout->YPosition(); + + float dysFontAscent = m_dysFontAscent; + ////GrResult res = GetFontAscentSourceUnits(pgg, &dysFontAscent); + ////if (ResultFailed(res)) + //// THROW(WARN(res)); + dysFontAscent += m_dysXAscent; + + float dysOffset = ysClick - (m_dysAscent - dysFontAscent); // relative to top of 0-baseline text + + // If the slot has components, see if the offset falls within one of them. + + if (pslout->HasComponents()) + { + GrEngine * pgreng = EngineImpl(); + if (!pgreng) + goto LNotLigature; + + GrGlyphTable * pgtbl = pgreng->GlyphTable(); + + for (int icomp = 0; icomp < pslout->NumberOfComponents(); icomp++) + { + float xsLeft, xsRight, ysTop, ysBottom; + + // TODO: Rework ComponentIndexForGlyph to take slati rather than iForGlyph. + int slati = pslout->ComponentId(icomp); + int iForGlyph = pgtbl->ComponentIndexForGlyph(pslout->GlyphID(), slati); + if (!pgtbl->ComponentBoxLogUnits(m_xysEmSquare, pslout->GlyphID(), iForGlyph, + m_mFontEmUnits, m_dysAscent, + &xsLeft, &ysTop, &xsRight, &ysBottom)) + { + continue; // component not defined + } + Assert(xsLeft <= xsRight); + Assert(ysTop <= ysBottom); + if (xsOffset < xsLeft || xsOffset > xsRight) + continue; + if ((dysOffset - ysForGlyph) < ysTop || (dysOffset - ysForGlyph) > ysBottom) + continue; + + // Click was within the component's box. + + dichw = pslout->UnderlyingComponent(icomp); + Assert(m_ichwMin + dichw >= 0); + Assert(dichw < m_dichwLim); + + fRight = (xsOffset - xsLeft > xsRight - xsOffset); + fAfter = (fGlyphRtl) ? !fRight : fRight; + if (pfAfter) + *pfAfter = fAfter; + + Assert(GrCharStream::AtUnicodeCharBoundary(m_pgts, m_ichwMin + dichw)); + + return m_ichwMin + dichw; + } + } + +LNotLigature: + + // No relevant ligature component. + // If the x-offset is less than half way across, + // answer the before-association; otherwise answer the after-association. + + if (xsOffset == kPosInfinity) + fRight = true; + else if (xsOffset == kNegInfinity) + fRight = false; + else + fRight = (xsOffset > (dxsGlyphWidth / 2)); + + fAfter = (fGlyphRtl) ? !fRight : fRight; + + int diCharAdj = 0; + if (fAfter) + { + dichw = pslout->AfterAssoc(); + if (pfAfter) + *pfAfter = true; + // The following should not be necessary because of the way associations are set up: + //while (!GrCharStream::AtUnicodeCharBoundary(m_pgts, ichwBase + dichw + diCharAdj)) + // diCharAdj++; + } + else + { + dichw = pslout->BeforeAssoc(); + if (pfAfter) + *pfAfter = false; + // The following should not be necessary because of the way associations are set up: + //while (!GrCharStream::AtUnicodeCharBoundary(m_pgts, ichwBase + dichw + diCharAdj)) + // diCharAdj--; + } + + // This should be true because of the way the associations are set up: + Assert(GrCharStream::AtUnicodeCharBoundary(m_pgts, m_ichwMin + dichw)); // + diCharAdj)); + + return m_ichwMin + dichw + diCharAdj; +} + +/*--------------------------------------------------------------------------------------------*/ + +int Segment::LogicalSurfaceToUnderlying(int islout, bool fBefore) +{ + if (fBefore) + return LogicalSurfaceToUnderlying(islout, kNegInfFloat, kNegInfFloat); + else + return LogicalSurfaceToUnderlying(islout, kPosInfFloat, kPosInfFloat); +} + +/*---------------------------------------------------------------------------------------------- + Given a physical surface location (an index into the glyph boxes), + return the underlying position, relative to the beginning of the string. + + @param igbb - index of glyph + @param xsOffset - relative to left side of glyph + @param ysOffset - downwards from top of segment (not upwards from baseline) +----------------------------------------------------------------------------------------------*/ +int Segment::PhysicalSurfaceToUnderlying( + int iginf, float xsOffset, float ysOffset, + float dxsGlyphWidth, float dysGlyphHeight, bool * pfAfter) +{ + int islout = iginf + m_isloutGinf0; + return LogicalSurfaceToUnderlying(islout, xsOffset, ysOffset, + dxsGlyphWidth, dysGlyphHeight, pfAfter); +} + +/*--------------------------------------------------------------------------------------------*/ + +int Segment::PhysicalSurfaceToUnderlying(int iginf, bool fBefore) +{ + int islout = iginf + m_isloutGinf0; + bool fGlyphRtl = SlotIsRightToLeft(OutputSlot(islout)); + if (fBefore == fGlyphRtl) + return PhysicalSurfaceToUnderlying(iginf, kPosInfFloat, kPosInfFloat); + else + return PhysicalSurfaceToUnderlying(iginf, kNegInfFloat, kNegInfFloat); +} + +/*---------------------------------------------------------------------------------------------- + Given an underlying position (relative to the beginning of the string), + return the logical surface location (ignoring right-to-left). + NOTE that ichw indicates a CHARACTER, not a location between characters. So ichw = 3, + fBefore = false indicates a selection between characters 3 and 4. + For surrogates, it indicates the FIRST of the pair of 16-bit chars. +----------------------------------------------------------------------------------------------*/ +int Segment::UnderlyingToLogicalSurface(int ichw, bool fBefore) +{ + int ichwSegOffset = ichw - m_ichwMin; + + if (ichwSegOffset < m_ichwAssocsMin) + return kNegInfinity; // probably rendered in previous segment + + + // if the buffers aren't even allocated, then it probably means there + // wasn't room for any glyphs, so the following segment is most likely +// else if (ichwSegOffset >= m_ichwAssocsLim) + else if (ichwSegOffset >= m_ichwAssocsLim || !m_prgisloutBefore || !m_prgisloutAfter) + return kPosInfinity; // probably rendered in following segment + + else if (fBefore) + { + int isloutRet; + int ichw = ichwSegOffset; + // If no association has been made, loop forward to the next slot + // we are before. As a last resort, answer kPosInfinity, meaning we + // aren't before anything. + do + { + isloutRet = m_prgisloutBefore[ichw - m_ichwAssocsMin]; + do { ++ichw; } + while (!GrCharStream::AtUnicodeCharBoundary(m_pgts, ichw)); + } while (isloutRet == kPosInfinity && ichw < m_ichwAssocsLim); + return isloutRet; + } + else + { + int isloutRet; + int ichw = ichwSegOffset; + // If no association has been made, loop backward to the previous slot + // we are after. As a last resort, answer kNegInfinity, meaning we + // aren't after anything. + do + { + isloutRet = m_prgisloutAfter[ichw - m_ichwAssocsMin]; + do { --ichw; } + while (!GrCharStream::AtUnicodeCharBoundary(m_pgts, ichw)); + } while (isloutRet == kNegInfinity && ichw >= 0); + return isloutRet; + } + Assert(false); // should never reach here +} + +/*---------------------------------------------------------------------------------------------- + Given an underlying position (relative to the beginning of the string), + return the physical surface location, the index into m_prgginf. +----------------------------------------------------------------------------------------------*/ +int Segment::UnderlyingToPhysicalSurface(int ichw, bool fBefore) +{ + int islout = UnderlyingToLogicalSurface(ichw, fBefore); + return LogicalToPhysicalSurface(islout); +} + +/*---------------------------------------------------------------------------------------------- + Given a logical position (index into output slots), return the physical position, + the index into the list of glyphs (m_prgginf). +----------------------------------------------------------------------------------------------*/ +int Segment::LogicalToPhysicalSurface(int islout) +{ + if (islout == kNegInfinity || islout == kPosInfinity) + return islout; + + Assert(m_prgginf[islout - m_isloutGinf0].logicalIndex() == (unsigned)(islout - m_isloutGinf0)); + + return islout - m_isloutGinf0; +} + +/*---------------------------------------------------------------------------------------------- + Given an underlying position (relative to the beginning of the string), + return the physical surface locations of all the associated surface glyphs. + OBSOLETE +----------------------------------------------------------------------------------------------*/ +void Segment::UnderlyingToPhysicalAssocs(int ichw, std::vector<int> & viginf) +{ + int ichwSegOffset = ichw - m_ichwMin; + + if (ichwSegOffset < m_ichwAssocsMin) + return; // possibly rendered in previous segment + + else if (ichwSegOffset >= m_ichwAssocsLim) + return; // possibly rendered in following segment + + else + { + std::vector<int> * pvisloutTmp = &(m_prgvisloutAssocs[ichwSegOffset - m_ichwAssocsMin]); + + for (size_t i = 0; i < pvisloutTmp->size(); i++) + { + int islout = (*pvisloutTmp)[i]; + Assert(islout != kNegInfinity); + Assert(islout != kPosInfinity); + viginf.push_back(LogicalToPhysicalSurface(islout)); + } + } +} + +/*---------------------------------------------------------------------------------------------- + Given an underlying character position (relative to the beginning of the string), + return a pointer to the vector containing the logical surface locations of + all the associated surface glyphs. + + WARNING: this method returns a pointer to the actual data, not a copy of it. + Do not modify the returned vector! +----------------------------------------------------------------------------------------------*/ +std::vector<int> * Segment::UnderlyingToLogicalAssocs(int ichw) +{ + int ichwSegOffset = ichw - m_ichwMin; + + if (ichwSegOffset < m_ichwAssocsMin) + return NULL; // probably rendered in previous segment + + else if (ichwSegOffset >= m_ichwAssocsLim) + return NULL; // probably rendered in following segment + + else + { + std::vector<int> * pvisloutRet = &(m_prgvisloutAssocs[ichwSegOffset - m_ichwAssocsMin]); + return pvisloutRet; + } +} + +/*---------------------------------------------------------------------------------------------- + Given an underlying character position (relative to the beginning of the string), + return an index of a slot in this segment that can used to test things like + InsertBefore. Don't return kNegInfinity or kPosInfinity except as a last resort. +----------------------------------------------------------------------------------------------*/ +int Segment::UnderlyingToLogicalInThisSeg(int ichw) +{ + int isloutTest = kNegInfinity; + std::vector<int> * pvislout = UnderlyingToLogicalAssocs(ichw); + if (pvislout) + { + for (size_t iislout = 0; iislout < pvislout->size(); iislout++) + { + isloutTest = (*pvislout)[iislout]; + if (isloutTest != kNegInfinity && isloutTest != kPosInfinity) + return isloutTest; + } + } + return isloutTest; +} + +/*---------------------------------------------------------------------------------------------- + Return true if the two characters map to exactly the same set of glyphs. +----------------------------------------------------------------------------------------------*/ +bool Segment::SameSurfaceGlyphs(int ichw1, int ichw2) +{ + std::vector<int> * pvislout1 = UnderlyingToLogicalAssocs(ichw1); + std::vector<int> * pvislout2 = UnderlyingToLogicalAssocs(ichw2); + + if (pvislout1 == NULL || pvislout1 == NULL) + return false; + + if (pvislout1->size() != pvislout2->size()) + return false; + for (size_t islout = 0; islout < pvislout1->size(); islout++) + { + if ((*pvislout1)[islout] != (*pvislout2)[islout]) + return false; + } + return true; +} + +/*---------------------------------------------------------------------------------------------- + Return the direction level of the given character. +----------------------------------------------------------------------------------------------*/ +int Segment::DirLevelOfChar(int ichw, bool fBefore) +{ + if (m_twsh == ktwshOnlyWs) + return m_nDirDepth; + int islot = UnderlyingToLogicalSurface(ichw, fBefore); + if (islot == kNegInfinity || islot == kPosInfinity) + return 0; + int nDir = OutputSlot(islot)->DirLevel(); + if (nDir == -1) + // Not calculated: assume to be the top direction level. + nDir = TopDirLevel(); + + return nDir; +} + +/*---------------------------------------------------------------------------------------------- + Return whether the given character is right-to-left. +----------------------------------------------------------------------------------------------*/ +int Segment::CharIsRightToLeft(int ichw, bool fBefore) +{ + int nDir = DirLevelOfChar(ichw, fBefore); + return ((nDir % 2) != 0); +} + +/*---------------------------------------------------------------------------------------------- + Return whether the given slot is right-to-left. +----------------------------------------------------------------------------------------------*/ +int Segment::SlotIsRightToLeft(GrSlotOutput * pslout) +{ + if (m_twsh == ktwshOnlyWs) + return (m_nDirDepth % 2); + int nDir = pslout->DirLevel(); + if (nDir == -1) + nDir = TopDirLevel(); + return (nDir % 2); +} + +/*---------------------------------------------------------------------------------------------- + Check to make sure that the attachments are valid. Specifically, for every glyph that + is part of a cluster, all glyphs between that glyph and its root must (ultimately) have + that root glyph as one of its roots. + OBSOLETE +----------------------------------------------------------------------------------------------*/ +void Segment::AssertValidClusters(GrSlotStream * psstrm) +{ +#ifdef _DEBUG + for (int islot = 0; islot < psstrm->WritePos(); islot++) + { + GrSlotState * pslotThis = psstrm->SlotAt(islot); + GrSlotState * pslotRoot = pslotThis->AttachRoot(psstrm); + if (!pslotRoot) + continue; + Assert(pslotRoot != pslotThis); + + int inc = (pslotThis->PosPassIndex() > pslotRoot->PosPassIndex()) ? -1 : 1; + + for (int islot2 = pslotThis->PosPassIndex() + inc; + islot2 != pslotRoot->PosPassIndex(); + islot2 += inc) + { + GrSlotState * pslotMid = psstrm->SlotAt(islot2); + Assert(pslotMid->HasAsRoot(psstrm, pslotRoot)); + } + } +#endif // _DEBUG +} + +/*---------------------------------------------------------------------------------------------- + Return information about (editing) clusters as appropriate for Uniscribe, ie, consistent + with the ScriptShape funciton. + A cluster includes all attachments, all clumps of characters created by insert = false, + and clumps of reordered glyphs. + + prgiginfFirstOfCluster returns the first glyph of the cluster for each CHARACTER + (glyph indices returned are zero-based and refer to physical order--left-to-right, etc.). + pfClusterStart returns a flag for each GLYPH indicating whether it is the first (left-most) + of a cluster. + Both arrays are optional. + CORRECTION: for now we use strictly logical order. It's not clear whether physical + order is needed for right-to-left text or not. See note below. + + TODO: convert from GlyphInfo objects which are RTL. +----------------------------------------------------------------------------------------------*/ +GrResult Segment::getUniscribeClusters( + int * prgiginfFirstOfCluster, int cchMax, int * pcch, + bool * pfClusterStart, int cfMax, int * pcf) +{ + ChkGrOutPtr(pcch); + if (prgiginfFirstOfCluster) + ChkGrArrayArg(prgiginfFirstOfCluster, cchMax); + ChkGrOutPtr(pcf); + if (pfClusterStart) + ChkGrArrayArg(pfClusterStart, cfMax); + + GrResult res = kresOk; + + //SetUpGraphics(ichwBase, pgg, true); + + if (m_dxsWidth == -1) + ComputeDimensions(); + + Assert(m_dxsWidth >= 0); + Assert(m_dysAscent >= 0); + + if (pcch) + *pcch = m_dichwLim; + if (pcf) + *pcf = m_cginf; + if (cchMax < m_dichwLim) + { + if (cchMax == 0 && cfMax == 0) + res = kresFalse; // just asking for size information + else if (prgiginfFirstOfCluster || cchMax > 0) + res = kresInvalidArg; // not enough space + } + if (cfMax < m_cginf) + { + if (cchMax == 0 && cfMax == 0) + res = kresFalse; + else if (pfClusterStart || cfMax > 0) + res = kresInvalidArg; + } + if (res != kresOk) + { + //RestoreFont(pgg); + ReturnResult(res); + } + + // Generate a pair of arrays, indicating the first and last (logical) slots + // of the cluster for each character. Initially clusters are based just on + // character-to-glyph associations, but they expand as we process + // insert=false settings, attachments, and reordering. + + std::vector<int> visloutBefore; + std::vector<int> visloutAfter; + visloutBefore.resize(m_dichwLim); + visloutAfter.resize(m_dichwLim); + + int ich; + for (ich = 0; ich < m_dichwLim; ich++) + { + visloutBefore[ich] = m_prgisloutBefore[ich - m_ichwAssocsMin]; + visloutAfter[ich] = m_prgisloutAfter[ich - m_ichwAssocsMin]; + } + + if (m_dichwLim == 1) + { + // Only one character, therefore only one cluster. + visloutBefore[0] = m_isloutGinf0; + visloutAfter[0] = max(m_cginf - 1, 0) + m_isloutGinf0; + } + else + { + // Make all glyphs attached to each other part of a cluster, or glyphs where + // insertion is not allowed. + for (ich = 0; ich < m_dichwLim; ich++) + { + std::vector<int> * pvislout = UnderlyingToLogicalAssocs(ich + m_ichwMin); + + if (!pvislout || pvislout->size() == 0) + { + // No glyphs represent this character. Merge it with the previous + // character. + visloutBefore[ich] = (ich == 0) ? visloutBefore[ich + 1] : visloutBefore[ich - 1]; + visloutAfter[ich] = (ich == 0) ? visloutAfter[ich + 1] : visloutAfter[ich - 1]; + continue; + } + + size_t iislout; + for (iislout = 0; iislout < pvislout->size(); iislout++) + { + int islout = (*pvislout)[iislout]; + if (!m_prgslout[islout].InsertBefore()) + { + // This glyph does not allow insertion before it; make its character part + // of the same cluster as the previous character. + MergeUniscribeCluster(visloutBefore, visloutAfter, ich - 1, ich); +/* ++ // Keith Stribley's fix: ++ // NOTE: this is NOT always the same as the previous character, so ++ // check whether the next character actually has a glyph before this ++ // one ++ if ((ich == m_dichwLim - 1) || ++ (std::min(visloutBefore[ich + 1], visloutAfter[ich + 1]) > ++ std::max(visloutBefore[ich], visloutAfter[ich]))) ++ { ++ MergeUniscribeCluster(visloutBefore, visloutAfter, ich - 1, ich); ++ } ++ else ++ MergeUniscribeCluster(visloutBefore, visloutAfter, ich, ich + 1); +*/ + } + else if (m_prgslout[islout].ClusterBase() != -1 + && m_prgslout[islout].ClusterBase() != islout) + { + // This glyph is attached to something; make them part of the same cluster. + int isloutBase = m_prgslout[islout].ClusterBase(); + int ichBase = LogicalSurfaceToUnderlying(isloutBase, false) - m_ichwMin; + MergeUniscribeCluster(visloutBefore, visloutAfter, + ich, ichBase); + int ichBase1 = LogicalSurfaceToUnderlying(isloutBase, false) - m_ichwMin; + if (ichBase1 != ichBase) + MergeUniscribeCluster(visloutBefore, visloutAfter, + ich, ichBase1); + } + } + } + + // Handle reordering. Whenever the islout values get out of order, we need to + // merge into a single cluster. + for (ich = 0; ich < m_dichwLim - 1; ich++) + { + Assert(visloutBefore[ich] <= visloutAfter[ich]); + const int b1 = visloutBefore[ich]; + const int a1 = visloutAfter[ich]; + const int b2 = visloutBefore[ich + 1]; + const int a2 = visloutAfter[ich + 1]; + if (b1 == b2 && a1 == a2) + continue; // already a cluster + + if (std::min(b2, a2) <= std::max(b1, a1)) + { + MergeUniscribeCluster(visloutBefore, visloutAfter, ich, ich + 1); + } + } + } + + if (pfClusterStart) + { + // Initialize. + int iginf; + for (iginf = 0; iginf < m_cginf; iginf++) + pfClusterStart[iginf] = false; + } + + // To skip line-break slots; remember that the number of output slots equals the + // number of gbbs plus initial and/or final line-break slots if any. + // In this case the beginning of the first cluster indicates the first real glyph. + int isloutFirstReal = (m_dichwLim) ? ((m_fWsRtl)? visloutAfter[0] : visloutBefore[0]) : 0; + + // Kludge to make assertions below easier: + if (m_dichwLim > 0) + { + visloutBefore.push_back(visloutAfter.back() + 1); + visloutAfter.push_back(visloutAfter.back() + 1); + } + + // Convert (logical) slots to actual glyphs. + for (ich = 0; ich < m_dichwLim; ich++) + { + // NOTE: to treat the first of the cluster as the left-most, use visloutAfter for + // right-to-left situations. + + if (prgiginfFirstOfCluster) + prgiginfFirstOfCluster[ich] + = LogicalToPhysicalSurface((m_fWsRtl) ? visloutAfter[ich] : visloutBefore[ich]); + int clusterStartPos = ((m_fWsRtl) ? visloutAfter[ich] : visloutBefore[ich]) - isloutFirstReal; + if (pfClusterStart) + pfClusterStart[clusterStartPos] = true; + + Assert((visloutBefore[ich] == visloutBefore[ich + 1] && + visloutAfter[ich] == visloutAfter[ich + 1]) + || (visloutBefore[ich] <= visloutAfter[ich] && + visloutAfter[ich] < visloutBefore[ich + 1])); + } + + //RestoreFont(pgg); + + return kresOk; +} + +/*---------------------------------------------------------------------------------------------- + Merge the given characters into the same Uniscribe cluster. This means merging any + intervening characters as well. +----------------------------------------------------------------------------------------------*/ +void Segment::MergeUniscribeCluster( + std::vector<int> & visloutBefore, std::vector<int> & visloutAfter, + int ich1, int ich2) +{ + int ichStartOrig = min(ich1, ich2); + int ichStopOrig = max(ich1, ich2); + + int isloutMin = visloutBefore[ichStopOrig]; + int isloutMax = visloutAfter[ichStartOrig]; + + int ichStart = ichStopOrig; + int ichStop = ichStartOrig; + // Find the beginning of the cluster. + while (ichStart > 0 && + (ichStart > ichStartOrig + || visloutBefore[ichStart - 1] >= visloutBefore[ichStopOrig] + || visloutBefore[ichStart - 1] == visloutBefore[ichStart])) + { + ichStart--; + isloutMin = std::min(isloutMin, visloutBefore[ichStart]); + isloutMax = std::max(isloutMax, visloutAfter[ichStart]); + } + // Find end of cluster. + while (ichStop < m_dichwLim - 1 && + (ichStop < ichStopOrig + || visloutAfter[ichStop + 1] <= visloutAfter[ichStartOrig] + || visloutAfter[ichStop + 1] == visloutAfter[ichStop])) + { + ichStop++; + isloutMin = std::min(isloutMin, visloutBefore[ichStop]); + isloutMax = std::max(isloutMax, visloutAfter[ichStop]); + } + + int ich; + for (ich = ichStart; ich <= ichStop; ich++) + { + visloutBefore[ich] = isloutMin; + visloutAfter[ich] = isloutMax; + } +} + +/*---------------------------------------------------------------------------------------------- + Return information about the positioning of glyphs as appropriate for Uniscribe. + The advance widths returned are the natural ones (what would be applied if no smart + rendering were happening), and the x-offsets are the difference between the + natural location of the glyph and what is produced by Graphite. The y-offset + is the same as GetGlyphsAndPositions. + + This is an experimental method. Its behavior may change signficantly in the future. +----------------------------------------------------------------------------------------------*/ +/* +GrResult Segment::GetUniscribePositions( + Rect rs, Rect rd, int cgidMax, int *pcgidRet, + float * prgxd, float * prgyd, float * prgdxdAdv) +{ + ChkGrOutPtr(pcgidRet); + ChkGrArrayArg(prgxd, cgidMax); + ChkGrArrayArg(prgyd, cgidMax); + ChkGrArrayArg(prgdxdAdv, cgidMax); + + GrResult res; + + //SetUpGraphics(ichBase, pgg, true); + + Assert(m_dxsWidth >= 0); + Assert(m_dysAscent >= 0); + + *pcgidRet = m_cginf; + if (cgidMax < m_cginf) + { + res = (cgidMax == 0) ? kresFalse : kresInvalidArg; + //RestoreFont(pgg); + ReturnResult(res); + } + + if (m_dxsWidth < 0) + ComputeDimensions(); + + float * rgxdTmp = new float[cgidMax]; + float * rgdxdAdvTmp = new float[cgidMax]; + res = GetGlyphsAndPositions(rs, rd, cgidMax, pcgidRet, + NULL, rgxdTmp, prgyd, rgdxdAdvTmp); + if (ResultFailed(res)) + { + delete[] rgxdTmp; + delete[] rgdxdAdvTmp; + //RestoreFont(pgg); + ReturnResult(res); + } + + float xdLeft = SegmentPainter::ScaleX(0, rs, rd); + float xdXPosNatural = xdLeft; + for (int iginf = 0; iginf < *pcgidRet; iginf++) + { + int islout = iginf + m_isloutGinf0; + GrSlotOutput * pslout = OutputSlot(islout); + float dxsAdvNatural = pslout->AdvanceXMetric(); + float dxdAdvNatural = SegmentPainter::ScaleX(dxsAdvNatural, rs, rd) - xdLeft; + prgdxdAdv[iginf] = dxdAdvNatural; + prgxd[iginf] = rgxdTmp[iginf] - xdXPosNatural; + + xdXPosNatural += dxdAdvNatural; + } + + delete[] rgxdTmp; + delete[] rgdxdAdvTmp; + + //RestoreFont(pgg); + + ReturnResult(res); + +} +*/ + +/*---------------------------------------------------------------------------------------------- + Return information about the positioning of glyphs as appropriate for Uniscribe. + The advance widths returned are the natural ones (what would be applied if no smart + rendering were happening), and the x-offsets are the difference between the + natural location of the glyph and what is produced by Graphite. The y-offset + is the same as GetGlyphsAndPositions. + + This is an experimental method. Its behavior may change signficantly in the future. +----------------------------------------------------------------------------------------------*/ +/* +GrResult Segment::GetUniscribeGlyphsAndPositions( + Rect rs, Rect rd, int cgidMax, int *pcgidRet, utf16 * prgchGlyphs, + float * prgxd, float * prgyd, float * prgdxdAdv) +{ + ChkGrOutPtr(pcgidRet); + ChkGrArrayArg(prgxd, cgidMax); + ChkGrArrayArg(prgyd, cgidMax); + ChkGrArrayArg(prgdxdAdv, cgidMax); + + GrResult res; + + //SetUpGraphics(ichBase, pgg, true); + + Assert(m_dxsWidth >= 0); + Assert(m_dysAscent >= 0); + + *pcgidRet = m_cginf; + if (cgidMax < m_cginf) + { + res = (cgidMax == 0) ? kresFalse : kresInvalidArg; + //RestoreFont(pgg); + ReturnResult(res); + } + + if (m_dxsWidth < 0) + ComputeDimensions(); + + float * rgxdTmp = new float[cgidMax]; + float * rgdxdAdvTmp = new float[cgidMax]; + res = GetGlyphsAndPositions(rs, rd, cgidMax, pcgidRet, + prgchGlyphs, rgxdTmp, prgyd, rgdxdAdvTmp); + if (ResultFailed(res)) + { + delete[] rgxdTmp; + delete[] rgdxdAdvTmp; + //RestoreFont(pgg); + ReturnResult(res); + } + + float xdLeft = SegmentPainter::ScaleX(0, rs, rd); + float xdXPosNatural = xdLeft; + for (int iginf = 0; iginf < *pcgidRet; iginf++) + { + int islout = iginf + m_isloutGinf0; + GrSlotOutput * pslout = OutputSlot(islout); + float dxsAdvNatural = pslout->AdvanceXMetric(); + float dxdAdvNatural = SegmentPainter::ScaleX(dxsAdvNatural, rs, rd) - xdLeft; + prgdxdAdv[iginf] = dxdAdvNatural; + prgxd[iginf] = rgxdTmp[iginf] - xdXPosNatural; + + xdXPosNatural += dxdAdvNatural; + } + + delete[] rgxdTmp; + delete[] rgdxdAdvTmp; + + //RestoreFont(pgg); + + ReturnResult(res); +} +*/ + +/*---------------------------------------------------------------------------------------------- + Return the visual box for the given ligature component. Return (0,0,0,0) if the component + is not defined. +----------------------------------------------------------------------------------------------*/ +Rect Segment::ComponentRect(GrSlotOutput * pslout, int icomp) +{ + Rect rectRet; + rectRet.top = rectRet.bottom = rectRet.left = rectRet.right = 0; + if (icomp < 0 || icomp > pslout->NumberOfComponents()) + return rectRet; + + GrEngine * pgreng = EngineImpl(); + if (!pgreng) + return rectRet; + GrGlyphTable * pgtbl = pgreng->GlyphTable(); + if (!pgtbl) + return rectRet; + + float xsLeft, xsRight, ysTop, ysBottom; + + // TODO: Rework ComponentIndexForGlyph to take slati rather than iForGlyph. + int slati = pslout->ComponentId(icomp); + int iForGlyph = pgtbl->ComponentIndexForGlyph(pslout->GlyphID(), slati); + if (!pgtbl->ComponentBoxLogUnits(m_xysEmSquare, pslout->GlyphID(), iForGlyph, + m_mFontEmUnits, m_dysAscent, + &xsLeft, &ysTop, &xsRight, &ysBottom, + false)) // origin is baseline + { + return rectRet; + } + rectRet.top = ysTop; + rectRet.bottom = ysBottom; + rectRet.left = xsLeft; + rectRet.right = xsRight; + + return rectRet; +} + +/*---------------------------------------------------------------------------------------------- + Shift the glyphs physically by the given amount. This happens when a right-to-left + segment is changed from being end-of-line to not, so we have to account for the fact + that the trailing white-space was previous invisible but is now visible. +----------------------------------------------------------------------------------------------*/ +void Segment::ShiftGlyphs(float dxsShift) +{ + for (int islout = 0; islout < m_cslout; islout++) + { + GrSlotOutput * pslout = OutputSlot(islout); + pslout->AdjustPosXBy(dxsShift); + } + + //ShiftGlyphsPlatform(dxsShift); +} + +/*---------------------------------------------------------------------------------------------- + Find the glyph that best matches the given click location. + + @param xsClick - relative to start of segment + @param ysClick - relative to segment baseline +----------------------------------------------------------------------------------------------*/ +int Segment::GlyphHit(float xsClick, float ysClick) +{ + float xsLeft = kPosInfFloat; + float xsRight = kNegInfFloat; + + int iginf; + for (iginf = 0; iginf < m_cginf; iginf++) + { + GlyphInfo * pginf = m_prgginf + iginf; + xsLeft = min(xsLeft, pginf->origin()); + xsRight = max(xsRight, pginf->origin() + pginf->advanceWidth()); + } + + if (xsClick < xsLeft) + return m_fWsRtl ? m_cginf - 1 : 0; + else if (xsClick > xsRight) + return m_fWsRtl ? 0 : m_cginf - 1; + + int iginfHit; + std::vector<int> viginfNear; + std::vector<int> viginfHit; + std::vector<int> viginfInside; + + // Find glyph which might contain click point. + + for (iginfHit = m_cginf; iginfHit-- > 0; ) + { + if (m_fWsRtl) + { + if (m_prgginf[iginfHit].bb().right >= xsClick) + break; + } + else + { + if (m_prgginf[iginfHit].bb().left <= xsClick) + break; + } + } + if (iginfHit < 0) // click within lsb of first glyph + { + return 0; + } + + // Find all BB that OVERLAP the click point (BbLeft < XClick < BbRight). + for (iginf = iginfHit; iginf >= 0; iginf--) + { + if (m_prgginf[iginf].bb().left <= xsClick && xsClick <= m_prgginf[iginf].bb().right) + { + viginfNear.push_back(iginf); + } + } + + // Find all BB that CONTAIN the click point (considering the click's Y coor too). + int iiginf; + for (iiginf = 0; iiginf < (int)viginfNear.size(); iiginf++) + { + Rect rectBB = m_prgginf[viginfNear[iiginf]].bb(); + if (rectBB.bottom <= ysClick && ysClick <= rectBB.top) + { + viginfHit.push_back(viginfNear[iiginf]); + } + } + + // Find all BB for which the click is within their advance width. + for (iiginf = 0; iiginf < (int)viginfHit.size(); iiginf++) + { + Rect rectBB = m_prgginf[viginfHit[iiginf]].bb(); + if (rectBB.left <= xsClick && xsClick <= rectBB.right) + { + viginfInside.push_back(viginfHit[iiginf]); + } + } + + // If no BB coincides in any way with the click point, take the BB whose leading edge + // is closest to the click point. + // There could be more than one BB with the same leading x coor. + if (viginfHit.size() == 0 && viginfNear.size() == 0 && viginfInside.size() == 0) + { + float xsLeading; + if (iginfHit + 1 == m_cginf) + { + viginfNear.push_back(iginfHit); + } + else if (!m_fWsRtl) + { + // Left-to-right: + xsLeading = m_prgginf[iginfHit + 1].bb().left; + for (iginf = iginfHit + 1; iginf < m_cginf; iginf++) + { + if (m_prgginf[iginf].bb().left == xsLeading) + viginfNear.push_back(iginf); + } + } + else + { + // Right-to-left: + xsLeading = m_prgginf[iginfHit].bb().right; + for (iginf = iginfHit; iginf < m_cginf; iginf++) + { + if (m_prgginf[iginf].bb().right == xsLeading) + viginfNear.push_back(iginf); + } + } + } + + Assert(viginfNear.size()); + + int iginfLoc; + + // Select the BB based on its leading edge, offset from baseline, position in final pass. + if (viginfInside.size()) + iginfLoc = SelectBb(viginfInside, m_fWsRtl); + else if (viginfHit.size()) + iginfLoc = SelectBb(viginfHit, m_fWsRtl); + else + iginfLoc = SelectBb(viginfNear, m_fWsRtl); + + return iginfLoc; +} + +/*---------------------------------------------------------------------------------------------- + Select the bounding box we will use for a mouse hit. + Select the BB with the left-most (LTR) or right-most (RTL) edge. + If more than one BB has same edge of interest, select the one that is closest to the + the baseline. + If that is a tie, select the one with the lowest islout. + + @param vigbb - list of indexes into m_prggbb + @param fRTL - writing system direction +----------------------------------------------------------------------------------------------*/ +int Segment::SelectBb(std::vector<int> & viginf, bool fRTL) +{ + if (!viginf.size()) + { + Assert(false); + return -1; + } + + if (viginf.size() == 1) + return viginf[0]; + + // Find appropriate x coor of leading edge. + float xsLeading; + if (!fRTL) + xsLeading = m_prgginf[viginf[0]].bb().left; + else + xsLeading = m_prgginf[viginf[0]].bb().right; + + size_t iiginf; + for (iiginf = 1; iiginf < viginf.size(); iiginf++) + { + if (!fRTL) + xsLeading = min(xsLeading, m_prgginf[viginf[iiginf]].bb().left); + else + xsLeading = max(xsLeading, m_prgginf[viginf[iiginf]].bb().right); + } + + // Find BBs with the leading edge. + std::vector<int> viginfSameEdge; + for (iiginf = 0; iiginf < viginf.size(); iiginf++) + { + if (!fRTL) + { + if (m_prgginf[viginf[iiginf]].bb().left == xsLeading) + viginfSameEdge.push_back(viginf[iiginf]); + } + else + { + if (m_prgginf[viginf[iiginf]].bb().right == xsLeading) + viginfSameEdge.push_back(viginf[iiginf]); + } + } + + if (viginfSameEdge.size() == 1) + return viginfSameEdge[0]; + + // Find minimum distance from baseline. + float ysMin = fabsf(m_prgginf[viginfSameEdge[0]].yOffset()); + for (iiginf = 1; iiginf < viginfSameEdge.size(); iiginf++) + { + float ysThis = fabsf(m_prgginf[viginfSameEdge[iiginf]].yOffset()); + ysMin = min(ysMin, ysThis); + } + + // Find BBs with minimum distance from baseline. + std::vector<int> viginfSameY; + for (iiginf = 0; iiginf < viginfSameEdge.size(); iiginf++) + { + if (fabsf(m_prgginf[viginfSameEdge[iiginf]].yOffset()) == ysMin) + viginfSameY.push_back(viginfSameEdge[iiginf]); + } + + if (viginfSameY.size() == 1) + return viginfSameY[0]; + + // Find minimum index of current candidates. + int islout = m_prgginf[viginfSameY[0]].logicalIndex(); + for (iiginf = 1; iiginf < viginfSameY.size(); iiginf++) + { + int isloutThis = m_prgginf[viginfSameY[iiginf]].logicalIndex(); + islout = min(islout, isloutThis); + } + + // Find BB with minimum islout - islout should be unique for all glyphs. + for (iiginf = 0; iiginf < viginfSameY.size(); iiginf++) + { + if (m_prgginf[viginfSameY[iiginf]].logicalIndex() == (unsigned)islout) + return viginfSameY[iiginf]; + } + + Assert(false); + return -1; // needed to stop compiler warning +} + +/*---------------------------------------------------------------------------------------------- + Return the right-most glyph on the physical surface. If there are ties, it will + prefer the one that is logically closest to the edge. +----------------------------------------------------------------------------------------------*/ +int Segment::RightMostGlyph() +{ + float xsRight = kNegInfFloat; + int iginfRet = -1; + for (int iginf = 0; iginf < m_cginf; iginf++) + { + float xsThis = m_prgginf[iginf].bb().right; + // For RTL, we prefer the glyph that is logically first; for LTR we prefer logically last. + if (xsThis > xsRight || (!m_fWsRtl && xsRight == xsThis)) + { + xsRight = xsThis; + iginfRet = iginf; + } + } + return iginfRet; +} + +/*---------------------------------------------------------------------------------------------- + Return the left-most glyph on the physical surface. If there are ties, it will + prefer the one that is logically closest to the edge. +----------------------------------------------------------------------------------------------*/ +int Segment::LeftMostGlyph() +{ + float xsLeft = kPosInfFloat; + int iginfRet = -1; + for (int iginf = 0; iginf < m_cginf; iginf++) + { + float xsThis = m_prgginf[iginf].bb().left; + // For RTL, we prefer the glyph that is logically last; for LTR we prefer logically first. + if (xsThis < xsLeft || (m_fWsRtl && xsLeft == xsThis)) + { + xsLeft = xsThis; + iginfRet = iginf; + } + } + return iginfRet; +} + +/*---------------------------------------------------------------------------------------------- + Convert from em-units to logical units. +----------------------------------------------------------------------------------------------*/ +float Segment::EmToLogUnits(int m) +{ + return GrEngine::GrIFIMulDiv(m, m_xysEmSquare, m_mFontEmUnits); +} + +/*---------------------------------------------------------------------------------------------- + Return the line-break pseudo glyph ID. +----------------------------------------------------------------------------------------------*/ +gid16 Segment::LBGlyphID() +{ + return m_preneng->LBGlyphID(); +} + +} // namespace gr diff --git a/Build/source/libs/graphite-engine/src/segment/SegmentAux.cpp b/Build/source/libs/graphite-engine/src/segment/SegmentAux.cpp new file mode 100644 index 00000000000..00dce43df65 --- /dev/null +++ b/Build/source/libs/graphite-engine/src/segment/SegmentAux.cpp @@ -0,0 +1,245 @@ +/*--------------------------------------------------------------------*//*:Ignore this sentence. +Copyright (C) 2005 SIL International. All rights reserved. + +Distributable under the terms of either the Common Public License or the +GNU Lesser General Public License, as specified in the LICENSING.txt file. + +File: SegmentAux.cpp +Responsibility: Sharon Correll +Last reviewed: Not yet. + +Description: + Implements auxiliary class that work with Segment: + - GlyphInfo + - GlyphIterator + - LayoutEnvironment +-------------------------------------------------------------------------------*//*:End Ignore*/ + +//:>******************************************************************************************** +//:> Include files +//:>******************************************************************************************** +#include "Main.h" +#ifdef _MSC_VER +#pragma hdrstop +#endif +// any other headers (not precompiled) +#ifndef _WIN32 +#include <stdlib.h> +#endif + +#undef THIS_FILE +DEFINE_THIS_FILE + +//:>******************************************************************************************** +//:> Forward declarations +//:>******************************************************************************************** + +//:>******************************************************************************************** +//:> Local Constants and static variables +//:>******************************************************************************************** + +namespace gr +{ + +//:>******************************************************************************************** +//:> GlyphInfo methods +//:>******************************************************************************************** + +gid16 GlyphInfo::glyphID() +{ + return m_pslout->m_chwActual; +} + +gid16 GlyphInfo::pseudoGlyphID() +{ + if (m_pslout->m_chwActual == m_pslout->m_chwGlyphID) + return 0; // not a pseudo-glyph + return m_pslout->m_chwGlyphID; +} + +bool GlyphInfo::isAttached() const throw() +{ + return m_pslout->IsPartOfCluster(); +} + +gr::GlyphIterator GlyphInfo::attachedClusterBase() const throw() +{ + const int islout = m_pslout->ClusterBase(); + + // Since we are not passing an array, the constructor expects a ginf index, not a slout index. + return GlyphIterator(*m_pseg, ((islout >= 0) ? islout : m_islout) - m_pseg->m_isloutGinf0); +} + +float GlyphInfo::attachedClusterAdvance() const throw() +{ + return m_pslout->ClusterAdvance(); +} + +std::pair<gr::GlyphSetIterator, gr::GlyphSetIterator> GlyphInfo::attachedClusterGlyphs() const +{ + return std::make_pair( + GlyphSetIterator(*m_pseg, 0, + m_pslout->m_visloutClusterMembers), + GlyphSetIterator(*m_pseg, m_pslout->m_visloutClusterMembers.size(), + m_pslout->m_visloutClusterMembers)); +} + +size_t GlyphInfo::logicalIndex() +{ + if (m_pseg->OutputSlot(0)->IsLineBreak()) + return m_islout - 1; + else + return m_islout; +} + +float GlyphInfo::origin() +{ + return m_pslout->XPosition(); +} + +float GlyphInfo::advanceWidth() // logical units +{ + return m_pslout->m_xsAdvanceX; +} + +float GlyphInfo::advanceHeight() // logical units; zero for horizontal fonts +{ + return m_pslout->m_xysAdvY; +} + +float GlyphInfo::yOffset() +{ + return m_pslout->YPosition(); +} + +Rect GlyphInfo::bb() // logical units +{ + return m_pslout->m_rectBB; +} + +bool GlyphInfo::isSpace() +{ + return m_pslout->IsSpace(); +} + +// first char associated with this glyph, relative to start of the text-source +toffset GlyphInfo::firstChar() +{ + return m_pseg->LogicalSurfaceToUnderlying(m_islout, true); +} + +// last char associated with this glyph, relative to start of the text-source +toffset GlyphInfo::lastChar() +{ + return m_pseg->LogicalSurfaceToUnderlying(m_islout, false); +} + +unsigned int GlyphInfo::directionality() +{ + return m_pslout->Directionality(); +} + +// Embedding depth +unsigned int GlyphInfo::directionLevel() +{ + return m_pslout->DirLevel(); +} + +bool GlyphInfo::insertBefore() +{ + return m_pslout->InsertBefore(); +} + +int GlyphInfo::breakweight() +{ + return m_pslout->BreakWeight(); +} + +float GlyphInfo::maxStretch(size_t level) +{ + return m_pslout->MaxStretch(m_pseg, (int)level); +} + +float GlyphInfo::maxShrink(size_t level) +{ + return m_pslout->MaxShrink(m_pseg, (int)level); +} + +float GlyphInfo::stretchStep(size_t level) +{ + return m_pslout->StretchStep(m_pseg, (int)level); +} + +byte GlyphInfo::justWeight(size_t level) +{ + return byte(m_pslout->JustWeight((int)level)); +} + +float GlyphInfo::justWidth(size_t level) +{ + return m_pslout->JustWidth(m_pseg, (int)level); +} + +float GlyphInfo::measureStartOfLine() +{ + return m_pslout->MeasureSolLogUnits(m_pseg); +} + +float GlyphInfo::measureEndOfLine() +{ + return m_pslout->MeasureEolLogUnits(m_pseg); +} + +size_t GlyphInfo::numberOfComponents() +{ + return m_pslout->NumberOfComponents(); +} + +Rect GlyphInfo::componentBox(size_t icomp) +{ + return m_pseg->ComponentRect(m_pslout, icomp); +} + +toffset GlyphInfo::componentFirstChar(size_t icomp) +{ + Assert((int)icomp < m_pslout->NumberOfComponents()); + return m_pslout->UnderlyingComponent(icomp) + m_pseg->startCharacter(); +} +toffset GlyphInfo::componentLastChar(size_t icomp) +{ + Assert((int)icomp < m_pslout->NumberOfComponents()); + return m_pslout->UnderlyingComponent(icomp) + m_pseg->startCharacter(); +} + +bool GlyphInfo::erroneous() +{ + return m_pseg->Erroneous(); +} + + +//:>******************************************************************************************** +//:> GlyphIterator methods +//:>******************************************************************************************** + + // Constructor +GlyphIterator::GlyphIterator(Segment & seg, size_t iginf) +: _itr(seg.m_prgginf + iginf) +{} + +GlyphIterator::GlyphIterator(const GlyphSetIterator &set_itr) +: _itr(set_itr->segment().m_prgginf + set_itr->logicalIndex()) +{} + +/*---------------------------------------------------------------------------------------------- + Dereference the iterator, returning a GlyphInfo object. +----------------------------------------------------------------------------------------------*/ + +GlyphSetIterator::reference GlyphSetIterator::operator*() const +{ + assert(_seg_ptr); + // in the case of a non-contiguous list + return _seg_ptr->m_prgginf[(*_itr) - _seg_ptr->m_isloutGinf0]; +} + + +} // namespace gr diff --git a/Build/source/libs/graphite-engine/src/segment/TestFSM.cpp b/Build/source/libs/graphite-engine/src/segment/TestFSM.cpp new file mode 100644 index 00000000000..6dad1dc9dc8 --- /dev/null +++ b/Build/source/libs/graphite-engine/src/segment/TestFSM.cpp @@ -0,0 +1,3416 @@ +/*--------------------------------------------------------------------*//*:Ignore this sentence. +Copyright (C) 1999, 2001 SIL International. All rights reserved. + +Distributable under the terms of either the Common Public License or the +GNU Lesser General Public License, as specified in the LICENSING.txt file. + +File: TestFSM.cpp +Responsibility: Sharon Correll +Last reviewed: Not yet. + +Description: + Hard-coded FSMs for test procedures. +-------------------------------------------------------------------------------*//*:End Ignore*/ + +//:Ignore + +//:>******************************************************************************************** +//:> Include files +//:>******************************************************************************************** +#include "Main.h" + +#ifdef _MSC_VER +#pragma hdrstop +#endif +#undef THIS_FILE +DEFINE_THIS_FILE + +#ifdef OLD_TEST_STUFF + +//:>******************************************************************************************** +//:> Forward declarations +//:>******************************************************************************************** + +//:>******************************************************************************************** +//:> Local Constants and static variables +//:>******************************************************************************************** + +namespace gr +{ + +//:>******************************************************************************************** +//:> Methods +//:>******************************************************************************************** + +#ifndef _DEBUG + +bool GrLineBreakPass::RunTestRules(GrTableManager * ptman, int ruln, + GrSlotStream * psstrmInput, GrSlotStream * psstrmOutput) +{ + return false; +} + +bool GrSubPass::RunTestRules(GrTableManager * ptman, int ruln, + GrSlotStream * psstrmInput, GrSlotStream * psstrmOutput) +{ + return false; +} + +bool GrPosPass::RunTestRules(GrTableManager * ptman, int ruln, + GrSlotStream * psstrmInput, GrSlotStream * psstrmOutput) +{ + return false; +} +#endif // !_DEBUG + + +#ifdef _DEBUG + +/*---------------------------------------------------------------------------------------------- + Call the appropriate test function. +----------------------------------------------------------------------------------------------*/ + +bool GrLineBreakPass::RunTestRules(GrTableManager * ptman, int ruln, + GrSlotStream * psstrmInput, GrSlotStream * psstrmOutput) +{ + return false; +} + +bool GrSubPass::RunTestRules(GrTableManager * ptman, int ruln, + GrSlotStream * psstrmInput, GrSlotStream * psstrmOutput) +{ + if (m_staBehavior == "SimpleFSM") + return RunSimpleFSMTest(ptman, ruln, psstrmInput, psstrmOutput); + else if (m_staBehavior == "RuleAction") + return false; + else if (m_staBehavior == "RuleAction2") + return false; + else if (m_staBehavior == "Assoc") + return false; + else + return false; +} + +bool GrPosPass::RunTestRules(GrTableManager * ptman, int ruln, + GrSlotStream * psstrmInput, GrSlotStream * psstrmOutput) +{ + return false; +} + + +/*---------------------------------------------------------------------------------------------- + A simple FSM. + + Rules: + r1. clsCons clsVowel clsDiacrAcute + r2. clsCons clsVowelAE clsDiacr + r3. clsCons clsVowelI + r4. clsCons clsVowelI clsDiacrGrave + r5. clsCons clsVowelI clsDiacr + + Machine classes: + mcCons: b,c,d / f,g,h / j,k,l,m,n / p,q,r,s,t / v,w,x,y,z + mcVae: a 97/ e 101 + mcVi: i 105 + mcVou: o 111 / u 117 + mcDa: acute '/' 47 + mcDg: grave '\' 92 + mcDct: other diacritics-circumflex '^' 94 / tilde '~' 126 + + + FSM: (T=transition, F=final, A=accepting, NA=non-accepting) + + | mcCons mcVae mcVi mcVou mcDa mcDg mcDct + --------------------------------------------------------------------------- + T NA s0 | s1 + | - - - - - - - + T NA s1 | s2 s4 s3 + | - - - - - - - + T NA s2 | s5 s6 s6 + | - - - - - - - + T NA s3 | s10 + | - - - - - - - + T A s4 | s7 s8 s9 + | - - - - - - - + F A s5 | + | - - - - - - - + F A s6 | + | - - - - - - - + F A s7 | + | - - - - - - - + F A s8 | + | - - - - - - - + F A s9 | + | - - - - - - - + F A s10 | + | - - - - - - - + + Rules Matched: + s4: r3 + s5: r1, r2 + s6: r2 + s7: r1, r5 + s8: r4, r5 + s9: r5 + s10: r1 + +----------------------------------------------------------------------------------------------*/ + +void GrSubPass::SetUpSimpleFSMTest() +{ + m_nMaxRuleContext = m_nMaxChunk = 3; + m_nMaxRuleLoop = 2; + m_staBehavior = "SimpleFSM"; + + m_pfsm = new GrFSM(); + Assert(m_pfsm); + m_pfsm->SetUpSimpleFSMTest(); +} + +void GrFSM::SetUpSimpleFSMTest() +{ + // Create machine class ranges. + m_cmcr = 14; + m_prgmcr = new GrFSMClassRange[14]; + m_prgmcr[0].m_chwFirst = 47; // forward slash (acute): mcDa + m_prgmcr[0].m_chwLast = 47; + m_prgmcr[0].m_col = 4; + + m_prgmcr[1].m_chwFirst = 92; // backslash: mcDg + m_prgmcr[1].m_chwLast = 92; + m_prgmcr[1].m_col = 5; + + m_prgmcr[2].m_chwFirst = 94; // caret: mcDct + m_prgmcr[2].m_chwLast = 94; + m_prgmcr[2].m_col = 6; + + m_prgmcr[3].m_chwFirst = 97; // a: mcVae + m_prgmcr[3].m_chwLast = 97; + m_prgmcr[3].m_col = 1; + + m_prgmcr[4].m_chwFirst = 98; // b - d: mcCons + m_prgmcr[4].m_chwLast = 100; + m_prgmcr[4].m_col = 0; + + m_prgmcr[5].m_chwFirst = 101; // e: mcVae + m_prgmcr[5].m_chwLast = 101; + m_prgmcr[5].m_col = 1; + + m_prgmcr[6].m_chwFirst = 102; // f - h: mcCons + m_prgmcr[6].m_chwLast = 104; + m_prgmcr[6].m_col = 0; + + m_prgmcr[7].m_chwFirst = 105; // i: mcVi + m_prgmcr[7].m_chwLast = 105; + m_prgmcr[7].m_col = 2; + + m_prgmcr[8].m_chwFirst = 106; // j - n: mcCons + m_prgmcr[8].m_chwLast = 110; + m_prgmcr[8].m_col = 0; + + m_prgmcr[9].m_chwFirst = 111; // o: mcVou + m_prgmcr[9].m_chwLast = 111; + m_prgmcr[9].m_col = 3; + + m_prgmcr[10].m_chwFirst = 112; // p - t: mcCons + m_prgmcr[10].m_chwLast = 116; + m_prgmcr[10].m_col = 0; + + m_prgmcr[11].m_chwFirst = 117; // u: mcVou + m_prgmcr[11].m_chwLast = 117; + m_prgmcr[11].m_col = 3; + + m_prgmcr[12].m_chwFirst = 118; // v - z: mcCons + m_prgmcr[12].m_chwLast = 122; + m_prgmcr[12].m_col = 0; + + m_prgmcr[13].m_chwFirst = 126; // tilde: mcDct + m_prgmcr[13].m_chwLast = 126; + m_prgmcr[13].m_col = 6; + + m_dimcrInit = 8; // (max power of 2 <= m_cmcr); + m_cLoop = 3; // log2(max power of 2 <= m_cmcr); + m_imcrStart = m_cmcr - m_dimcrInit; + + + m_crow = 11; + m_crowNonAcpt = 4; + m_crowFinal = 6; + m_rowFinalMin = m_crow - m_crowFinal; + m_ccol = 7; + + // Set up transition table. + m_prgrowTransitions = new short[35]; // 35 = (m_crow-m_crowFinal) * m_ccol + short * psn = m_prgrowTransitions; + *psn++ = 1; *psn++ = 0; *psn++ = 0; *psn++ = 0; *psn++ = 0; *psn++ = 0; *psn++ = 0; + *psn++ = 0; *psn++ = 2; *psn++ = 4; *psn++ = 3; *psn++ = 0; *psn++ = 0; *psn++ = 0; + *psn++ = 0; *psn++ = 0; *psn++ = 0; *psn++ = 0; *psn++ = 5; *psn++ = 6; *psn++ = 6; + *psn++ = 0; *psn++ = 0; *psn++ = 0; *psn++ = 0; *psn++ =10; *psn++ = 0; *psn++ = 0; + *psn++ = 0; *psn++ = 0; *psn++ = 0; *psn++ = 0; *psn++ = 7; *psn++ = 8; *psn++ = 9; + + // Set up matched-rules tables. + m_prgrulnMatched = new data16[10]; // 10 = sum of rules matched for each accepting state + m_prgirulnMin = new data16[7+1]; // 7 = m_crow - m_crowNonAcpt + + m_prgirulnMin[0] = 0; // s4: r3 + m_prgrulnMatched[0] = 3; + + m_prgirulnMin[1] = 1; // s5: r1, r2 + m_prgrulnMatched[1] = 1; + m_prgrulnMatched[2] = 2; + + m_prgirulnMin[2] = 3; // s6: r2 + m_prgrulnMatched[3] = 2; + + m_prgirulnMin[3] = 4; // s7: r1, r5 + m_prgrulnMatched[4] = 1; + m_prgrulnMatched[5] = 5; + + m_prgirulnMin[4] = 6; // s8: r4, r5 + m_prgrulnMatched[6] = 4; + m_prgrulnMatched[7] = 5; + + m_prgirulnMin[5] = 8; // s9: r5 + m_prgrulnMatched[8] = 5; + + m_prgirulnMin[6] = 9; // s10: r1 + m_prgrulnMatched[9] = 1; + + m_prgirulnMin[7] = 10; +} + + +/*---------------------------------------------------------------------------------------------- + The "rule" does something silly: just inserts *'s--the number equals the number of the + rule. +----------------------------------------------------------------------------------------------*/ +bool GrSubPass::RunSimpleFSMTest(GrTableManager * ptman, int ruln, GrSlotStream * psstrmInput, + GrSlotStream * psstrmOutput) +{ + int cslotMatched = (ruln == 3)? 2: 3; + + GrSlotState * pslot0 = psstrmInput->Peek(0); + + for (int islot = 0; islot < cslotMatched; islot++) + psstrmOutput->CopyOneSlotFrom(psstrmInput); + + for (islot = 0; islot < ruln; islot++) + { + GrSlotState * pslot; + ptman->NewSlot('*', pslot0, NULL, m_ipass, &pslot); + pslot->Associate(pslot0); + psstrmOutput->NextPut(pslot); + } + + return true; +} + + +/*---------------------------------------------------------------------------------------------- + A simple set of rules with actions. + + Rules: + r0. clsDigit gSlash clsDigit _ > @3 _ @1 gTilde; // tilde = 126 + r1. _ clsDigit gBackSlash clsDigit > gDollar @4 _ @2; // dollar sign = 36 + + Machine classes: + mcDigit: '0' - '9' 48 - 57 + mcSlash: '/' 47 + mcBSlash: '\' 92 + + + FSM: (T=transition, F=final, A=accepting, NA=non-accepting) + + | clsDig mcSlash mcBSlash + ------------------------------------------------------ + T NA s0 | s1 + | - - - + T NA s1 | s2 s3 + | - - - + T NA s2 | s4 + | - - - + T NA s3 | s5 + | - - - + F A s4 | + | - - - + F A s5 | + | - - - + + Rules Matched: + s4: r0 + s5: r1 + +----------------------------------------------------------------------------------------------*/ +void GrEngine::SetUpRuleActionTest() +{ + if (m_pctbl) + delete m_pctbl; + + m_pctbl = new GrClassTable(); + m_pctbl->SetUpRuleActionTest(); +} + +void GrClassTable::SetUpRuleActionTest() +{ + m_ccls = 2; // number of classes + m_cclsLinear = 2; // number of classes in linear format + + m_prgchwBIGGlyphList = new data16[100]; + + m_prgichwOffsets = new data16[2+1]; + + gid16 * pchw = m_prgchwBIGGlyphList; + + // Output class 0: tilde + m_prgichwOffsets[0] = 0; + *pchw++ = msbf(data16(126)); // '~' + + // Output class 1: dollar + m_prgichwOffsets[1] = 1; + *pchw++ = msbf(data16(36)); // '$' + + m_prgichwOffsets[2] = 2; +} + +void GrSubPass::SetUpRuleActionTest() +{ + m_nMaxRuleContext = m_nMaxChunk = 3; + m_nMaxRuleLoop = 2; + m_staBehavior = "RuleAction"; + + m_pfsm = new GrFSM(); + Assert(m_pfsm); + m_pfsm->SetUpRuleActionTest(); + + m_crul = 2; + + // Set up constraint code--succeed trivially. + m_prgbConstraintBlock = new byte[3]; + byte * pb = m_prgbConstraintBlock; + *pb++ = kopPushByte; *pb++ = 1; // Push true + *pb++ = kopPopRet; // PopAndRet + + m_prgibConstraintStart = new data16[m_crul]; + m_prgibConstraintStart[0] = 0; + m_prgibConstraintStart[1] = 0; + + // Set up rule action code. + m_prgbActionBlock = new byte[30]; + pb = m_prgbActionBlock; + + // Rule 1: clsDigit gSlash clsDigit _ > @3 _ @1 gTilde; + + *pb++ = kopPutCopy; *pb++ = 2; // PutCopy 2 + *pb++ = kopNext; // Next + *pb++ = kopDelete; // Delete + *pb++ = kopNext; // Next + *pb++ = kopPutCopy; *pb++ = -2; // PutCopy -2 + *pb++ = kopNext; // Next + *pb++ = kopInsert; // Insert + *pb++ = kopPutGlyph8bitObs; *pb++ = 0; // PutGlyph 0 ('~' = 126) + *pb++ = kopNext; // Next + *pb++ = kopPushByte; *pb++ = 0; // Push 0 + *pb++ = kopPopRet; // PopAndRet + + // Rule 2: _ clsDigit gBackSlash clsDigit > gDollar @4 _ @2; + + *pb++ = kopInsert; // Insert + *pb++ = kopPutGlyph8bitObs; *pb++ = 1; // PutGlyph 1 ('$' = 36) + *pb++ = kopNext; // Next + *pb++ = kopPutCopy; *pb++ = 2; // PutCopy 2 + *pb++ = kopNext; // Next + *pb++ = kopDelete; // Delete + *pb++ = kopNext; // Next + *pb++ = kopPutCopy; *pb++ = -2; // PutCopy -2 + *pb++ = kopNext; // Next + *pb++ = kopPushByte; *pb++ = 0; // Push 0 + *pb++ = kopPopRet; // PopAndRet + + m_prgibActionStart = new data16[m_crul]; + m_prgibActionStart[0] = 0; + m_prgibActionStart[1] = 15; +} + +void GrFSM::SetUpRuleActionTest() +{ + // Create machine class ranges. + m_cmcr = 3; + m_prgmcr = new GrFSMClassRange[3]; + m_prgmcr[0].m_chwFirst = 47; // forward slash: mcSlash + m_prgmcr[0].m_chwLast = 47; + m_prgmcr[0].m_col = 1; + + m_prgmcr[1].m_chwFirst = 48; // 0 - 9: mcDigit + m_prgmcr[1].m_chwLast = 57; + m_prgmcr[1].m_col = 0; + + m_prgmcr[2].m_chwFirst = 92; // backslash + m_prgmcr[2].m_chwLast = 92; + m_prgmcr[2].m_col = 2; + + + m_dimcrInit = 2; // (max power of 2 <= m_cmcr); + m_cLoop = 1; // log2(max power of 2 <= m_cmcr); + m_imcrStart = m_cmcr - m_dimcrInit; + + + m_crow = 6; + m_crowNonAcpt = 4; + m_crowFinal = 2; + m_rowFinalMin = m_crow - m_crowFinal; + m_ccol = 3; + + // Set up transition table. + m_prgrowTransitions = new short[12]; // 12 = (m_crow-m_crowFinal) * m_ccol + short * psn = m_prgrowTransitions; + *psn++ = 1; *psn++ = 0; *psn++ = 0; + *psn++ = 0; *psn++ = 2; *psn++ = 3; + *psn++ = 4; *psn++ = 0; *psn++ = 0; + *psn++ = 5; *psn++ = 0; *psn++ = 0; + + // Set up matched-rules tables. + m_prgrulnMatched = new data16[2]; // 2 = sum of rules matched for each accepting state + m_prgirulnMin = new data16[2+1]; // 2 = m_crow - m_crowNonAcpt + + m_prgirulnMin[0] = 0; // s4: r0 + m_prgrulnMatched[0] = 0; + + m_prgirulnMin[1] = 1; // s5: r1 + m_prgrulnMatched[1] = 1; + + m_prgirulnMin[2] = 2; +} + +/*---------------------------------------------------------------------------------------------- + A more complicated set of rules with actions. + + PASS 1: + r0. clsDigit > @1 {dir = DIR_RIGHT} / ^ _ {dir == DIR_LEFT}; + r1. clsSpecial > @2 {dir = DIR_ARABNUMBER} / clsDigit {dir != DIR_LEFT} ^ _; + r2. clsSpecial > @2 {dir = @1.dir} / clsSpecial ^ _; + + Pass 1 machine classes: + mcDigit: '0' - '9' 48 - 57 + mcSpecial: '-', '_', '=', '~' 45, 95, 61, 126 + + Pass 1 FSM: (T=transition, F=final, A=accepting, NA=non-accepting) + + | mcDigit mcSpecial + ------------------------------------------ + T NA s0 | s2 s1 + | - _ + T NA s1 | s4 + | - - + T A s2 | s3 + | - - + F A s3 | + | - - + F A s4 | + | - - + + Rules Matched: + s2: r0 + s3: r1 + s4: r2 + + PASS 2: + r0. clsVowel gCaret > clsCirumVowel _; + r1. clsVowel gSlash > _ clsAcuteVowel$1; + + Pass 2 machine classes: + mcVowel + mcCaret: '^' 94 + mcSlash: '/' 47 + + + Pass 2 FSM: + | mcVowel mcCaret mcSlash + ----------------------------------------------------- + T NA s0 | s1 + | - _ + T NA s1 | s2 s3 + | - - + F A s2 | + | - - + F A s3 | + | - - + + Rules matched: + s2: r0 + s3: r1 + + + PASS 3: + r0. clsLeft > clsRight {dir = @1.dir} / _ clsDir {dir==DIR_RIGHT}; + r1. clsRight > clsLeft {dir = @1.dir} / clsDir {dir==DIR_RIGHT} _; + + Pass 3 machine classes: + mcDir: (clsDigit, clsSpecial) + mcLeft: '(', '[', '{' 40, 91, 123 + mcRight: ')', ']', '}' 41, 93, 125 + + Pass 3 FSM: + | mcDir mcLeft mcRight + ----------------------------------------------------- + T NA s0 | s2 s1 + | - _ + T NA s1 | s3 + | - - + T NA s2 | s4 + | - - + F A s3 | + | - - + F A s4 | + | - - + + Rules matched: + s3: r0 + s4: r1 + + +----------------------------------------------------------------------------------------------*/ +void GrEngine::SetUpRuleAction2Test() +{ + if (m_pctbl) + delete m_pctbl; + + m_pctbl = new GrClassTable(); + m_pctbl->SetUpRuleAction2Test(); +} + +void GrClassTable::SetUpRuleAction2Test() +{ + m_ccls = 7; // number of classes + m_cclsLinear = 4; // number of classes in linear format + + m_prgchwBIGGlyphList = new data16[70]; + + m_prgichwOffsets = new data16[7+1]; + + data16 * pchw = m_prgchwBIGGlyphList; + + // Output class 0: clsCirumVowel + m_prgichwOffsets[0] = 0; + *pchw++ = msbf(data16(226)); // a + *pchw++ = msbf(data16(234)); // e + *pchw++ = msbf(data16(238)); // i + *pchw++ = msbf(data16(244)); // o + *pchw++ = msbf(data16(251)); // u + *pchw++ = msbf(data16(194)); // A + *pchw++ = msbf(data16(202)); // E + *pchw++ = msbf(data16(206)); // I + *pchw++ = msbf(data16(212)); // O + *pchw++ = msbf(data16(219)); // U + + // Output class 1: clsAcuteVowel + m_prgichwOffsets[1] = 10; + *pchw++ = msbf(data16(225)); // a + *pchw++ = msbf(data16(233)); // e + *pchw++ = msbf(data16(237)); // i + *pchw++ = msbf(data16(243)); // o + *pchw++ = msbf(data16(250)); // u + *pchw++ = msbf(data16(193)); // A + *pchw++ = msbf(data16(201)); // E + *pchw++ = msbf(data16(205)); // I + *pchw++ = msbf(data16(211)); // O + *pchw++ = msbf(data16(218)); // U + + // Output class 2: clsLeft + m_prgichwOffsets[2] = 10 + 10; + *pchw++ = msbf(data16(40)); // ( + *pchw++ = msbf(data16(91)); // [ + *pchw++ = msbf(data16(123)); // { + + // Output class 3: clsRight + m_prgichwOffsets[3] = 20 + 3; + *pchw++ = msbf(data16(41)); // ) + *pchw++ = msbf(data16(93)); // ] + *pchw++ = msbf(data16(125)); // } + + // Input class 4: clsVowel + m_prgichwOffsets[4] = 23 + 3; // 26 + *pchw++ = msbf(data16(10)); + *pchw++ = msbf(data16(8)); *pchw++ = msbf(data16(3)); *pchw++ = msbf(data16(10-8)); + *pchw++ = msbf(data16(65)); *pchw++ = msbf(data16(5)); // A + *pchw++ = msbf(data16(69)); *pchw++ = msbf(data16(6)); // E + *pchw++ = msbf(data16(73)); *pchw++ = msbf(data16(7)); // I + *pchw++ = msbf(data16(79)); *pchw++ = msbf(data16(8)); // O + *pchw++ = msbf(data16(85)); *pchw++ = msbf(data16(9)); // U + *pchw++ = msbf(data16(97)); *pchw++ = msbf(data16(0)); // a + *pchw++ = msbf(data16(101)); *pchw++ = msbf(data16(1)); // e + *pchw++ = msbf(data16(105)); *pchw++ = msbf(data16(2)); // i + *pchw++ = msbf(data16(111)); *pchw++ = msbf(data16(3)); // o + *pchw++ = msbf(data16(117)); *pchw++ = msbf(data16(4)); // u + + // Input class 5: clsLeft + m_prgichwOffsets[5] = 26 + 4 + 10*2; // 50 + *pchw++ = msbf(data16(3)); + *pchw++ = msbf(data16(2)); *pchw++ = msbf(data16(1)); *pchw++ = msbf(data16(3-2)); + *pchw++ = msbf(data16(40)); *pchw++ = msbf(data16(0)); // ( + *pchw++ = msbf(data16(91)); *pchw++ = msbf(data16(1)); // [ + *pchw++ = msbf(data16(123)); *pchw++ = msbf(data16(2)); // { + + // Input class 6: clsRight + m_prgichwOffsets[6] = 50 + 4 + 3*2; // 60 + *pchw++ = msbf(data16(3)); + *pchw++ = msbf(data16(2)); *pchw++ = msbf(data16(1)); *pchw++ = msbf(data16(3-2)); + *pchw++ = msbf(data16(41)); *pchw++ = msbf(data16(0)); // ) + *pchw++ = msbf(data16(93)); *pchw++ = msbf(data16(1)); // ] + *pchw++ = msbf(data16(125)); *pchw++ = msbf(data16(2)); // } + + m_prgichwOffsets[7] = 60 + 4 + 3*2; // 70 +} + +void GrSubPass::SetUpRuleAction2Test() +{ + if (m_ipass == 1) + { + m_nMaxRuleContext = m_nMaxChunk = 2; + m_nMaxRuleLoop = 5; + m_staBehavior = "RuleAction2"; + + m_pfsm = new GrFSM(); + Assert(m_pfsm); + m_pfsm->SetUpRuleAction2Test(m_ipass); + + m_crul = 3; + + // Set up constraint code. + m_prgbConstraintBlock = new byte[1 + 10 + 10]; + byte * pb = m_prgbConstraintBlock; + *pb++ = kopRetTrue; // RetTrue + + *pb++ = kopCntxtItem; *pb++ = 0; // ContextItem 0 + *pb++ = kopPushSlotAttr; *pb++= kslatDir; // PushSlotAttr dir 0 + *pb++ = 0; + *pb++ = kopPushByte; *pb++ = kdircL; // Push DIR_LEFT + *pb++ = kopEqual; // Equal + *pb++ = kopOr; // Or + *pb++ = kopPopRet; // PopAndRet + + *pb++ = kopCntxtItem; *pb++ = 0; // ContextItem 0 + *pb++ = kopPushSlotAttr; *pb++= kslatDir; // PushSlotAttr dir 0 + *pb++ = 0; + *pb++ = kopPushByte; *pb++ = kdircL; // Push DIR_LEFT + *pb++ = kopNotEq; // NotEqual + *pb++ = kopOr; // Or + *pb++ = kopPopRet; // PopAndRet + + m_prgibConstraintStart = new data16[m_crul]; + m_prgibConstraintStart[0] = 1; + m_prgibConstraintStart[1] = 11; + m_prgibConstraintStart[2] = 0; + + // Set up rule action code. + m_prgbActionBlock = new byte[10 + 11 + 12]; + pb = m_prgbActionBlock; + + // Rule 0: clsDigit > @1 {dir = DIR_RIGHT} / ^ _ {dir == DIR_LEFT}; + + *pb++ = kopPutCopy; *pb++ = 0; // PutCopy 0 + *pb++ = kopPushByte; *pb++ = kdircR; // PushByte DIR_RIGHT + *pb++ = kopAttrSet; *pb++ = kslatDir; // AttrSet dir + *pb++ = kopNext; // Next + *pb++ = kopPushByte; *pb++ = -1; // Push -1 + *pb++ = kopPopRet; // PopAndRet + + // Rule 1: clsSpecial > @2 {dir = DIR_ARABNUMBER} / clsDigit {dir != DIR_LEFT} ^ _; + + *pb++ = kopCopyNext; // CopyNext + *pb++ = kopPutCopy; *pb++ = 0; // PutCopy 0 + *pb++ = kopPushByte; *pb++ = kdircArabNum; // Push DIR_ARABNUMBER + *pb++ = kopAttrSet; *pb++ = kslatDir; // AttrSet dir + *pb++ = kopNext; // Next + *pb++ = kopPushByte; *pb++ = -1; // Push -1 + *pb++ = kopPopRet; // PopAndRet + + // Rule 2: clsSpecial > @2 {dir = @1.dir} / clsSpecial ^ _; + + *pb++ = kopCopyNext; // CopyNext + *pb++ = kopPutCopy; *pb++ = 0; // PutCopy 0 + *pb++ = kopPushSlotAttr;*pb++ = kslatDir; // PushSlotAttr dir -1 + *pb++ = -1; + *pb++ = kopAttrSet; *pb++ = kslatDir; // AttrSet dir + *pb++ = kopNext; // Next + *pb++ = kopPushByte; *pb++ = -1; // Push -1 + *pb++ = kopPopRet; // PopAndRet + + m_prgibActionStart = new data16[m_crul]; + m_prgibActionStart[0] = 0; + m_prgibActionStart[1] = 10; + m_prgibActionStart[2] = 10 + 11; + } + else if (m_ipass == 2) + { + m_nMaxRuleContext = m_nMaxChunk = 2; + m_nMaxRuleLoop = 3; + m_staBehavior = "RuleAction2"; + + m_pfsm = new GrFSM(); + Assert(m_pfsm); + m_pfsm->SetUpRuleAction2Test(m_ipass); + + m_crul = 2; + + // Set up constraint code--succeed trivially. + m_prgbConstraintBlock = new byte[1]; + byte * pb = m_prgbConstraintBlock; + *pb++ = kopRetTrue; // RetTrue + + m_prgibConstraintStart = new data16[m_crul]; + m_prgibConstraintStart[0] = 0; + m_prgibConstraintStart[1] = 0; + + // Set up rule action code. + m_prgbActionBlock = new byte[16]; + pb = m_prgbActionBlock; + + // Rule 0: clsVowel gCaret > clsCirumVowel _; + + *pb++ = kopPutSubs8bitObs; *pb++ = 0; // PutSubs 0 clsVowel clsCircumVowel + *pb++ = 4; *pb++ = 0; + *pb++ = kopNext; // Next + *pb++ = kopDelete; // Delete + *pb++ = kopNext; // Next + *pb++ = kopRetZero; // RetZero + + // Rule 1: clsVowel gSlash > _ clsAcuteVowel$1; + + *pb++ = kopDelete; // Delete + *pb++ = kopNext; // Next + *pb++ = kopPutSubs8bitObs; *pb++ = -1; // PutSubs -1 clsVowel clsAcuteVowel + *pb++ = 4; *pb++ = 1; + *pb++ = kopNext; // Next + *pb++ = kopRetZero; // RetZero + + m_prgibActionStart = new data16[m_crul]; + m_prgibActionStart[0] = 0; + m_prgibActionStart[1] = 8; + } + else if (m_ipass == 3) + { + m_nMaxRuleContext = m_nMaxChunk = 2; + m_nMaxRuleLoop = 3; + m_staBehavior = "RuleAction2"; + + m_pfsm = new GrFSM(); + Assert(m_pfsm); + m_pfsm->SetUpRuleAction2Test(m_ipass); + + m_crul = 2; + + // Set up constraint code. + m_prgbConstraintBlock = new byte[1 + 10 + 10]; + byte * pb = m_prgbConstraintBlock; + *pb++ = kopRetTrue; // RetTrue + + *pb++ = kopCntxtItem; *pb++ = 1; // ContextItem 1 + *pb++ = kopPushSlotAttr;*pb++ = kslatDir; // PushSlotAttr dir 0 + *pb++ = 0; + *pb++ = kopPushByte; *pb++ = kdircR; // Push DIR_RIGHT + *pb++ = kopEqual; // Equal + *pb++ = kopOr; // Or + *pb++ = kopPopRet; // PopAndRet + + *pb++ = kopCntxtItem; *pb++ = 0; // ContextItem 0 + *pb++ = kopPushSlotAttr;*pb++ = kslatDir; // PushSlotAttr dir 0 + *pb++ = 0; + *pb++ = kopPushByte; *pb++ = kdircR; // Push DIR_RIGHT + *pb++ = kopEqual; // Equal + *pb++ = kopOr; // Or + *pb++ = kopPopRet; // PopAndRet + + m_prgibConstraintStart = new data16[m_crul]; + m_prgibConstraintStart[0] = 1; + m_prgibConstraintStart[1] = 1 + 10; + + // Set up rule action code. + m_prgbActionBlock = new byte[23]; + pb = m_prgbActionBlock; + + // Rule 0: clsLeft > clsRight {dir = @1.dir} / _ clsDir {dir == DIR_RIGHT}; + + *pb++ = kopPutSubs8bitObs; *pb++ = 0; // PutSubs 0 clsLeft clsRight + *pb++ = 5; *pb++ = 3; + *pb++ = kopPushSlotAttr;*pb++ = kslatDir; // PushSlotAttr dir 1 + *pb++ = 1; + *pb++ = kopAttrSet; *pb++ = kslatDir; // AttrSet dir + *pb++ = kopNext; // Next + *pb++ = kopRetZero; // RetZero + + // Rule 1: clsRight > clsLeft {dir = @1.dir} / clsDir {dir == DIR_RIGHT} _; + + *pb++ = kopCopyNext; // CopyNext + *pb++ = kopPutSubs8bitObs; *pb++ = 0; // PutSubs 0 clsRight clsLeft + *pb++ = 6; *pb++ = 2; + *pb++ = kopPushSlotAttr;*pb++ = kslatDir; // PushSlotAttr dir -1 + *pb++ = -1; + *pb++ = kopAttrSet; *pb++ = kslatDir; // AttrSet dir + *pb++ = kopNext; // Next + *pb++ = kopRetZero; // RetZero + + m_prgibActionStart = new data16[m_crul]; + m_prgibActionStart[0] = 0; + m_prgibActionStart[1] = 11; + } +} + + +void GrFSM::SetUpRuleAction2Test(int ipass) +{ + if (ipass == 1) + { + // Create machine class ranges. + m_cmcr = 5; + m_prgmcr = new GrFSMClassRange[5]; + m_prgmcr[0].m_chwFirst = 45; // hyphen + m_prgmcr[0].m_chwLast = 45; + m_prgmcr[0].m_col = 1; + + m_prgmcr[1].m_chwFirst = 48; // 0 - 9: mcDigit + m_prgmcr[1].m_chwLast = 57; + m_prgmcr[1].m_col = 0; + + m_prgmcr[2].m_chwFirst = 61; // equals sign + m_prgmcr[2].m_chwLast = 61; + m_prgmcr[2].m_col = 1; + + m_prgmcr[3].m_chwFirst = 95; // underscore + m_prgmcr[3].m_chwLast = 95; + m_prgmcr[3].m_col = 1; + + m_prgmcr[4].m_chwFirst = 126; // tilde + m_prgmcr[4].m_chwLast = 126; + m_prgmcr[4].m_col = 1; + + m_dimcrInit = 4; // (max power of 2 <= m_cmcr); + m_cLoop = 2; // log2(max power of 2 <= m_cmcr); + m_imcrStart = m_cmcr - m_dimcrInit; + + + m_crow = 5; + m_crowNonAcpt = 2; + m_crowFinal = 2; + m_rowFinalMin = m_crow - m_crowFinal; + m_ccol = 2; + + // Set up transition table. + m_prgrowTransitions = new short[6]; // 6 = (m_crow-m_crowFinal) * m_ccol + short * psn = m_prgrowTransitions; + *psn++ = 2; *psn++ = 1; + *psn++ = 0; *psn++ = 4; + *psn++ = 0; *psn++ = 3; + + // Set up matched-rules tables. + m_prgrulnMatched = new data16[3]; // 3 = sum of rules matched for each accepting state + m_prgirulnMin = new data16[3+1]; // 3 = m_crow - m_crowNonAcpt + + m_prgirulnMin[0] = 0; // s2: r0 + m_prgrulnMatched[0] = 0; + + m_prgirulnMin[1] = 1; // s3: r1 + m_prgrulnMatched[1] = 1; + + m_prgirulnMin[2] = 2; // s3: r2 + m_prgrulnMatched[2] = 2; + + m_prgirulnMin[3] = 3; + } + else if (ipass == 2) + { + // Create machine class ranges. + m_cmcr = 12; + m_prgmcr = new GrFSMClassRange[12]; + m_prgmcr[0].m_chwFirst = 47; // slash + m_prgmcr[0].m_chwLast = 47; + m_prgmcr[0].m_col = 2; + + m_prgmcr[1].m_chwFirst = 65; // A + m_prgmcr[1].m_chwLast = 65; + m_prgmcr[1].m_col = 0; + + m_prgmcr[2].m_chwFirst = 69; // E + m_prgmcr[2].m_chwLast = 69; + m_prgmcr[2].m_col = 0; + + m_prgmcr[3].m_chwFirst = 73; // I + m_prgmcr[3].m_chwLast = 73; + m_prgmcr[3].m_col = 0; + + m_prgmcr[4].m_chwFirst = 79; // O + m_prgmcr[4].m_chwLast = 79; + m_prgmcr[4].m_col = 0; + + m_prgmcr[5].m_chwFirst = 85; // U + m_prgmcr[5].m_chwLast = 85; + m_prgmcr[5].m_col = 0; + + m_prgmcr[6].m_chwFirst = 94; // caret + m_prgmcr[6].m_chwLast = 94; + m_prgmcr[6].m_col = 1; + + m_prgmcr[7].m_chwFirst = 97; // a + m_prgmcr[7].m_chwLast = 97; + m_prgmcr[7].m_col = 0; + + m_prgmcr[8].m_chwFirst = 101; // e + m_prgmcr[8].m_chwLast = 101; + m_prgmcr[8].m_col = 0; + + m_prgmcr[9].m_chwFirst = 105; // i + m_prgmcr[9].m_chwLast = 105; + m_prgmcr[9].m_col = 0; + + m_prgmcr[10].m_chwFirst = 111; // o + m_prgmcr[10].m_chwLast = 111; + m_prgmcr[10].m_col = 0; + + m_prgmcr[11].m_chwFirst = 117; // u + m_prgmcr[11].m_chwLast = 117; + m_prgmcr[11].m_col = 0; + + m_dimcrInit = 8; // (max power of 2 <= m_cmcr); + m_cLoop = 3; // log2(max power of 2 <= m_cmcr); + m_imcrStart = m_cmcr - m_dimcrInit; + + + m_crow = 4; + m_crowNonAcpt = 2; + m_crowFinal = 2; + m_rowFinalMin = m_crow - m_crowFinal; + m_ccol = 3; + + // Set up transition table. + m_prgrowTransitions = new short[6]; // 6 = (m_crow-m_crowFinal) * m_ccol + short * psn = m_prgrowTransitions; + *psn++ = 1; *psn++ = 0; *psn++ = 0; + *psn++ = 0; *psn++ = 2; *psn++ = 3; + + // Set up matched-rules tables. + m_prgrulnMatched = new data16[2]; // 2 = sum of rules matched for each accepting state + m_prgirulnMin = new data16[2+1]; // 2 = m_crow - m_crowNonAcpt + + m_prgirulnMin[0] = 0; // s1: r0 + m_prgrulnMatched[0] = 0; + + m_prgirulnMin[1] = 1; // s2: r1 + m_prgrulnMatched[1] = 1; + + m_prgirulnMin[2] = 2; + } + else if (ipass == 3) + { + // Create machine class ranges. + m_cmcr = 11; + m_prgmcr = new GrFSMClassRange[11]; + m_prgmcr[0].m_chwFirst = 40; // ( + m_prgmcr[0].m_chwLast = 40; + m_prgmcr[0].m_col = 1; + + m_prgmcr[1].m_chwFirst = 41; // ) + m_prgmcr[1].m_chwLast = 41; + m_prgmcr[1].m_col = 2; + + m_prgmcr[2].m_chwFirst = 45; // hyphen + m_prgmcr[2].m_chwLast = 45; + m_prgmcr[2].m_col = 0; + + m_prgmcr[3].m_chwFirst = 48; // 0 - 9: mcDigit + m_prgmcr[3].m_chwLast = 57; + m_prgmcr[3].m_col = 0; + + m_prgmcr[4].m_chwFirst = 61; // = + m_prgmcr[4].m_chwLast = 61; + m_prgmcr[4].m_col = 0; + + m_prgmcr[5].m_chwFirst = 91; // [ + m_prgmcr[5].m_chwLast = 91; + m_prgmcr[5].m_col = 1; + + m_prgmcr[6].m_chwFirst = 93; // ] + m_prgmcr[6].m_chwLast = 93; + m_prgmcr[6].m_col = 2; + + m_prgmcr[7].m_chwFirst = 95; // underscore + m_prgmcr[7].m_chwLast = 95; + m_prgmcr[7].m_col = 0; + + m_prgmcr[8].m_chwFirst = 123; // { + m_prgmcr[8].m_chwLast = 123; + m_prgmcr[8].m_col = 1; + + m_prgmcr[9].m_chwFirst = 125; // } + m_prgmcr[9].m_chwLast = 125; + m_prgmcr[9].m_col = 2; + + m_prgmcr[10].m_chwFirst = 126; // tilde + m_prgmcr[10].m_chwLast = 126; + m_prgmcr[10].m_col = 0; + + m_dimcrInit = 8; // (max power of 2 <= m_cmcr); + m_cLoop = 3; // log2(max power of 2 <= m_cmcr); + m_imcrStart = m_cmcr - m_dimcrInit; + + + m_crow = 5; + m_crowNonAcpt = 3; + m_crowFinal = 2; + m_rowFinalMin = m_crow - m_crowFinal; + m_ccol = 3; + + // Set up transition table. + m_prgrowTransitions = new short[9]; // 9 = (m_crow-m_crowFinal) * m_ccol + short * psn = m_prgrowTransitions; + *psn++ = 2; *psn++ = 1; *psn++ = 0; + *psn++ = 3; *psn++ = 0; *psn++ = 0; + *psn++ = 0; *psn++ = 0; *psn++ = 4; + + // Set up matched-rules tables. + m_prgrulnMatched = new data16[2]; // 2 = sum of rules matched for each accepting state + m_prgirulnMin = new data16[2+1]; // 2 = m_crow - m_crowNonAcpt + + m_prgirulnMin[0] = 0; // s3: r0 + m_prgrulnMatched[0] = 0; + + m_prgirulnMin[1] = 1; // s4: r1 + m_prgrulnMatched[1] = 1; + + m_prgirulnMin[2] = 2; + } +} + + +/*---------------------------------------------------------------------------------------------- + Tests of various kinds of assocations. + + PASS 1 (line-break): + r0. gX / # _ ^ ; // do nothing + r1. gX {break = -2}; + + Pass 1 machine classes: + mcX: 'x' 120 + mcLB: 35 + + Pass 1 FSM: (T=transition, F=final, A=accepting, NA=non-accepting) + + | gX gLB + -------------------------------- + T NA s0 | s2 s1 + | + T NA s1 | s3 + | + F A s2 | + | + F A s3 | + + + Rules Matched: + s2: r1 + s3: r0 + + + + PASS 2: + r0. clsOA clsE > clsELig:(1 2) _; + r1. clsLC _ _ gX > @1 @5 @1 @5 / _ _ # {break==2} _ _ ; + r2. _ clsFraction _ > clsNumerator:2 gSlash:2 clsDenom:2; + + Pass 2 machine classes: + mcOA: O A 79 65 + mcOAlc: o a 111 97 + mcE E e 69 101 + mcFraction: 188 189 190 + mcX: x 120 + mcLCother + mcLB: 35 + + + Pass 2 FSM: mcLC + | mcOA mcOAlc mcE mcElc other mcX mcFractn mcLB + ----------------------------------------------------------------------------------------- + T NA s0 | s1 s2 s4 s3 s3 s6 + | - - - - - - - + T NA s1 | s7 + | - - - - - - - + T NA s2 | s7 s5 + | - - - - - - - + T NA s3 | s5 + | - - - - - - - + T NA s4 | s5 + | - - - - - - - + T NA s5 | s8 + | - - - - - - - + F A s6 | + | - - - - - - + F A s7 | + | - - - - - - + F A s8 | + | - - - - - - + + Rules matched: + s6: r2 + s7: r0 + s8: r1 + + + PASS 3: + r0. _ > gHyphen / _ # {break == 2} ^; + + Pass 3 machine classes: + mcLB: 35 + + Pass 3 FSM: + + | gLB + -------------------------------- + T NA s0 | s1 + | + F A s1 | + | + + Rules Matched: + s1: r0 +----------------------------------------------------------------------------------------------*/ +void GrEngine::SetUpAssocTest() +{ + if (m_pctbl) + delete m_pctbl; + + m_pctbl = new GrClassTable(); + m_pctbl->SetUpAssocTest(); + + m_fLineBreak = true; + m_cchwPreXlbContext = 1; + m_cchwPostXlbContext = 1; +} + +void GrClassTable::SetUpAssocTest() +{ + m_ccls = 11; // number of classes + m_cclsLinear = 5; // number of classes in linear format (output classes) + + m_prgchwBIGGlyphList = new data16[143]; + + m_prgichwOffsets = new data16[m_ccls+1]; + + data16 * pchw = m_prgchwBIGGlyphList; + + // Output class 0: clsELig + m_prgichwOffsets[0] = 0; + *pchw++ = msbf(data16(140)); // OE + *pchw++ = msbf(data16(198)); // AE + *pchw++ = msbf(data16(156)); // oe + *pchw++ = msbf(data16(230)); // ae + + // Output class 1: clsNumerator + m_prgichwOffsets[1] = 4; + *pchw++ = msbf(data16(49)); // 1 + *pchw++ = msbf(data16(49)); // 1 + *pchw++ = msbf(data16(51)); // 3 + + // Output class 2: clsDenom + m_prgichwOffsets[2] = 4 + 3; + *pchw++ = msbf(data16(52)); // 4 + *pchw++ = msbf(data16(50)); // 2 + *pchw++ = msbf(data16(52)); // 4 + + // Output class 3: gSlash + m_prgichwOffsets[3] = 7 + 3; + *pchw++ = msbf(data16(47)); // '/' + + // Output class 4: gHyphen + m_prgichwOffsets[4] = 10 + 1; + *pchw++ = msbf(data16(45)); // '-' + + // Input class 5: clsOA + m_prgichwOffsets[5] = 11 + 1; + *pchw++ = msbf(data16(4)); + *pchw++ = msbf(data16(4)); *pchw++ = msbf(data16(2)); *pchw++ = msbf(data16(0)); + *pchw++ = msbf(data16(65)); *pchw++ = msbf(data16(1)); // A + *pchw++ = msbf(data16(79)); *pchw++ = msbf(data16(0)); // O + *pchw++ = msbf(data16(97)); *pchw++ = msbf(data16(3)); // a + *pchw++ = msbf(data16(111)); *pchw++ = msbf(data16(2)); // o + + // Input class 6: clsE + m_prgichwOffsets[6] = 12 + 4 + 4*2; // 24 + *pchw++ = msbf(data16(3)); + *pchw++ = msbf(data16(2)); *pchw++ = msbf(data16(1)); *pchw++ = msbf(data16(3-2)); + *pchw++ = msbf(data16(40)); *pchw++ = msbf(data16(0)); // E + *pchw++ = msbf(data16(91)); *pchw++ = msbf(data16(1)); // e + + // Input class 7: clsLC + m_prgichwOffsets[7] = 24 + 4 + 2*2; // 32 + *pchw++ = msbf(data16(26)); + *pchw++ = msbf(data16(16)); *pchw++ = msbf(data16(4)); *pchw++ = msbf(data16(26-16)); + for (int i = 0; i < 26; ++i) + { + *pchw++ = msbf(data16(97+i)); *pchw++ = msbf(data16(i)); + } + + // Input class 8: gX + m_prgichwOffsets[8] = 32 + 4 + 26*2; // 88 + *pchw++ = msbf(data16(1)); + *pchw++ = msbf(data16(1)); *pchw++ = msbf(data16(0)); *pchw++ = msbf(data16(0)); + *pchw++ = msbf(data16(120)); *pchw++ = msbf(data16(0)); + + // Input class 9: clsFraction + m_prgichwOffsets[9] = 88 + 4 + 1*2; // 127 + *pchw++ = msbf(data16(3)); + *pchw++ = msbf(data16(2)); *pchw++ = msbf(data16(1)); *pchw++ = msbf(data16(3-2)); + *pchw++ = msbf(data16(188)); *pchw++ = msbf(data16(0)); // 1/4 + *pchw++ = msbf(data16(189)); *pchw++ = msbf(data16(1)); // 1/2 + *pchw++ = msbf(data16(190)); *pchw++ = msbf(data16(2)); // 3/4 + + // Input class 10: # + m_prgichwOffsets[10] = 127 + 4 + 3*2; // 137 + *pchw++ = msbf(data16(1)); + *pchw++ = msbf(data16(1)); *pchw++ = msbf(data16(0)); *pchw++ = msbf(data16(0)); + *pchw++ = msbf(data16(35)); *pchw++ = msbf(data16(0)); // 1/4 + + m_prgichwOffsets[11] = 137 + 4 + 1*2; // 143 +} + +void GrLineBreakPass::SetUpAssocTest() +{ + m_nMaxRuleContext = m_nMaxChunk = 1; + m_nMaxRuleLoop = 5; + m_staBehavior = "Assoc"; + + m_pfsm = new GrFSM(); + Assert(m_pfsm); + m_pfsm->SetUpAssocTest(m_ipass); + + m_crul = 2; + + // Set up constraint code--succeed trivially. + m_prgbConstraintBlock = new byte[1]; + byte * pb = m_prgbConstraintBlock; + *pb++ = kopRetTrue; // RetTrue + + m_prgibConstraintStart = new data16[m_crul]; + m_prgibConstraintStart[0] = 0; + m_prgibConstraintStart[1] = 0; + + // Set up rule action code. + m_prgbActionBlock = new byte[7 + 8]; + pb = m_prgbActionBlock; + + // Rule 0: gX / # _ ^ ; // do nothing + + *pb++ = kopPutCopy; *pb++ = 0; // PutCopy 0 + *pb++ = kopNext; // Next + *pb++ = kopPutCopy; *pb++ = 0; // PutCopy 0 + *pb++ = kopNext; // Next + *pb++ = kopRetZero; // RetZero + + // Rule 1: gX {break = -2}; + + *pb++ = kopPutCopy; *pb++ = 0; // PutCopy 0 + *pb++ = kopPushByte; *pb++ = -2; // PushByte -2 + *pb++ = kopAttrSet; *pb++ = kslatBreak; // AttrSet breakweight + *pb++ = kopNext; // Next + *pb++ = kopRetZero; // RetZero + + m_prgibActionStart = new data16[m_crul]; + m_prgibActionStart[0] = 0; + m_prgibActionStart[1] = 7; +} + +void GrSubPass::SetUpAssocTest() +{ + if (m_ipass == 2) + { + m_nMaxRuleContext = m_nMaxChunk = 3; + m_nMaxRuleLoop = 5; + m_staBehavior = "Assoc"; + + m_pfsm = new GrFSM(); + Assert(m_pfsm); + m_pfsm->SetUpAssocTest(m_ipass); + + m_crul = 3; + + // Set up constraint code. + m_prgbConstraintBlock = new byte[1 + 10]; + byte * pb = m_prgbConstraintBlock; + *pb++ = kopRetTrue; // RetTrue + + *pb++ = kopCntxtItem; *pb++ = 1; // ContextItem 1 + *pb++ = kopPushSlotAttr;*pb++ = kslatBreak; // PushSlotAttr break 0 + *pb++ = 0; + *pb++ = kopPushByte; *pb++ = 2; // Push 2 + *pb++ = kopEqual; // Equal + *pb++ = kopOr; // Or + *pb++ = kopPopRet; // PopAndRet + + m_prgibConstraintStart = new data16[m_crul]; + m_prgibConstraintStart[0] = 0; + m_prgibConstraintStart[1] = 1; + m_prgibConstraintStart[2] = 0; + + // Set up rule action code. + m_prgbActionBlock = new byte[12 + 12 + 22]; + pb = m_prgbActionBlock; + + // Rule 0: clsOA clsE > clsELig:(1 2) _ ; + + *pb++ = kopPutSubs8bitObs; *pb++ = 0; // PutSubs 0 clsOA clsELig + *pb++ = 5; *pb++ = 0; + *pb++ = kopAssoc; *pb++ = 2; // Assoc 2 0 1 + *pb++ = 0; *pb++ = 1; + *pb++ = kopNext; // Next + *pb++ = kopDelete; // Delete + *pb++ = kopNext; // Next + *pb++ = kopRetZero; // RetZero + + // Rule 1: clsLC _ _ gX > @1 @5 @1 @5 / _ _ # {break==2} _ _ ; + + *pb++ = kopCopyNext; // CopyNext + *pb++ = kopInsert; // Insert + *pb++ = kopPutCopy; *pb++ = 2; // PutCopy 2 + *pb++ = kopNext; // Next + *pb++ = kopCopyNext; // CopyNext - line break + *pb++ = kopInsert; // Insert + *pb++ = kopPutCopy; *pb++ = -1; // PutCopy -1 + *pb++ = kopNext; // Next + *pb++ = kopCopyNext; // CopyNext + *pb++ = kopRetZero; // RetZero + + // Rule 2: _ clsFraction _ > clsNumerator:2 gSlash:2 clsDenom:2; + + *pb++ = kopInsert; // Insert + *pb++ = kopPutSubs8bitObs; *pb++ = 1; // PutSubs 1 clsFraction clsNumerator + *pb++ = 9; *pb++ = 1; + *pb++ = kopAssoc; *pb++ = 1; // Assoc 1 1 + *pb++ = 1; + *pb++ = kopNext; // Next + *pb++ = kopPutGlyph8bitObs; *pb++ = 3; // PutGlyph gSlash + *pb++ = kopNext; // Next + *pb++ = kopInsert; // Insert + *pb++ = kopPutSubs8bitObs; *pb++ = 0; // PutSubs 0 clsFraction clsDenom + *pb++ = 9; *pb++ = 2; + *pb++ = kopAssoc; *pb++ = 1; // Assoc 1 0 + *pb++ = 0; + *pb++ = kopNext; // Next + *pb++ = kopRetZero; // RetZero + + m_prgibActionStart = new data16[m_crul]; + m_prgibActionStart[0] = 0; + m_prgibActionStart[1] = 12; + m_prgibActionStart[2] = 12 + 12; + } + else if (m_ipass == 3) + { + m_nMaxRuleContext = m_nMaxChunk = 1; + m_nMaxRuleLoop = 3; + m_staBehavior = "Assoc"; + + m_pfsm = new GrFSM(); + Assert(m_pfsm); + m_pfsm->SetUpAssocTest(m_ipass); + + m_crul = 1; + + // Set up constraint code. + m_prgbConstraintBlock = new byte[1 + 10]; + byte * pb = m_prgbConstraintBlock; + *pb++ = kopRetTrue; // RetTrue + + *pb++ = kopCntxtItem; *pb++ = 0; // ContextItem 0 + *pb++ = kopPushSlotAttr;*pb++ = kslatBreak; // PushSlotAttr break 0 + *pb++ = 0; + *pb++ = kopPushByte; *pb++ = 2; // Push 2 + *pb++ = kopEqual; // Equal + *pb++ = kopOr; // Or + *pb++ = kopPopRet; // PopAndRet + + m_prgibConstraintStart = new data16[m_crul]; + m_prgibConstraintStart[0] = 1; + + m_prgbActionBlock = new byte[7]; + pb = m_prgbActionBlock; + + // Rule 0: _ > gHyphen / _ # {break == 2} ^; + + *pb++ = kopInsert; // Insert + *pb++ = kopPutGlyph8bitObs; *pb++ = 4; // PutGlyph 4 ('-' = 45) + *pb++ = kopNext; // Next + *pb++ = kopPushByte; *pb++ = 1; // Push 1 + *pb++ = kopPopRet; // PopAndRet + + m_prgibActionStart = new data16[m_crul]; + m_prgibActionStart[0] = 0; + } + else + Assert(false); +} + + +void GrFSM::SetUpAssocTest(int ipass) +{ + if (ipass == 1) + { + // Create machine class ranges. + m_cmcr = 2; + m_prgmcr = new GrFSMClassRange[m_cmcr]; + m_prgmcr[0].m_chwFirst = 35; // # + m_prgmcr[0].m_chwLast = 35; + m_prgmcr[0].m_col = 1; + + m_prgmcr[1].m_chwFirst = 120; // x + m_prgmcr[1].m_chwLast = 120; + m_prgmcr[1].m_col = 0; + + m_dimcrInit = 2; // (max power of 2 <= m_cmcr); + m_cLoop = 1; // log2(max power of 2 <= m_cmcr); + m_imcrStart = m_cmcr - m_dimcrInit; + + + m_crow = 4; + m_crowNonAcpt = 2; + m_crowFinal = 2; + m_rowFinalMin = m_crow - m_crowFinal; + m_ccol = 2; + + // Set up transition table. + m_prgrowTransitions = new short[(m_crow-m_crowFinal) * m_ccol]; // 4 + short * psn = m_prgrowTransitions; + *psn++ = 2; *psn++ = 1; + *psn++ = 3; *psn++ = 0; + + // Set up matched-rules tables. + m_prgrulnMatched = new data16[2]; // 2 = sum of rules matched for each accepting state + m_prgirulnMin = new data16[m_crow - m_crowNonAcpt + 1]; + + m_prgirulnMin[0] = 0; // s2: r1 + m_prgrulnMatched[0] = 1; + + m_prgirulnMin[1] = 1; // s3: r0 + m_prgrulnMatched[1] = 0; + + m_prgirulnMin[2] = 2; + } + else if (ipass == 2) + { + // Create machine class ranges. + m_cmcr = 13; + m_prgmcr = new GrFSMClassRange[m_cmcr]; + m_prgmcr[0].m_chwFirst = 35; // # + m_prgmcr[0].m_chwLast = 35; + m_prgmcr[0].m_col = 7; + + m_prgmcr[1].m_chwFirst = 65; // A + m_prgmcr[1].m_chwLast = 65; + m_prgmcr[1].m_col = 0; + + m_prgmcr[2].m_chwFirst = 69; // E + m_prgmcr[2].m_chwLast = 69; + m_prgmcr[2].m_col = 2; + + m_prgmcr[3].m_chwFirst = 79; // O + m_prgmcr[3].m_chwLast = 79; + m_prgmcr[3].m_col = 0; + + m_prgmcr[4].m_chwFirst = 97; // a + m_prgmcr[4].m_chwLast = 97; + m_prgmcr[4].m_col = 1; + + m_prgmcr[5].m_chwFirst = 98; // b - d + m_prgmcr[5].m_chwLast = 100; + m_prgmcr[5].m_col = 4; + + m_prgmcr[6].m_chwFirst = 101; // e + m_prgmcr[6].m_chwLast = 101; + m_prgmcr[6].m_col = 3; + + m_prgmcr[7].m_chwFirst = 102; // f - n + m_prgmcr[7].m_chwLast = 110; + m_prgmcr[7].m_col = 4; + + m_prgmcr[8].m_chwFirst = 111; // o + m_prgmcr[8].m_chwLast = 111; + m_prgmcr[8].m_col = 1; + + m_prgmcr[9].m_chwFirst = 112; // p - w + m_prgmcr[9].m_chwLast = 119; + m_prgmcr[9].m_col = 4; + + m_prgmcr[10].m_chwFirst = 120; // x + m_prgmcr[10].m_chwLast = 120; + m_prgmcr[10].m_col = 5; + + m_prgmcr[11].m_chwFirst = 121; // y - z + m_prgmcr[11].m_chwLast = 122; + m_prgmcr[11].m_col = 4; + + m_prgmcr[12].m_chwFirst = 188; // 1/4, 1/2, 3/4 + m_prgmcr[12].m_chwLast = 190; + m_prgmcr[12].m_col = 6; + + + m_dimcrInit = 8; // (max power of 2 <= m_cmcr); + m_cLoop = 3; // log2(max power of 2 <= m_cmcr); + m_imcrStart = m_cmcr - m_dimcrInit; + + + m_crow = 9; + m_crowNonAcpt = 6; + m_crowFinal = 3; + m_rowFinalMin = m_crow - m_crowFinal; + m_ccol = 8; + + // Set up transition table. + m_prgrowTransitions = new short[(m_crow-m_crowFinal) * m_ccol]; // 48 + short * psn = m_prgrowTransitions; + *psn++ = 1; *psn++ = 2; *psn++ = 0; *psn++ = 4; *psn++ = 3; *psn++ = 3; *psn++ = 6; *psn++ = 0; + *psn++ = 0; *psn++ = 0; *psn++ = 7; *psn++ = 0; *psn++ = 0; *psn++ = 0; *psn++ = 0; *psn++ = 0; + *psn++ = 0; *psn++ = 0; *psn++ = 0; *psn++ = 7; *psn++ = 0; *psn++ = 0; *psn++ = 0; *psn++ = 5; + *psn++ = 0; *psn++ = 0; *psn++ = 0; *psn++ = 0; *psn++ = 0; *psn++ = 0; *psn++ = 0; *psn++ = 5; + *psn++ = 0; *psn++ = 0; *psn++ = 0; *psn++ = 0; *psn++ = 0; *psn++ = 0; *psn++ = 0; *psn++ = 5; + *psn++ = 0; *psn++ = 0; *psn++ = 0; *psn++ = 0; *psn++ = 0; *psn++ = 8; *psn++ = 0; *psn++ = 0; + + // Set up matched-rules tables. + m_prgrulnMatched = new data16[3]; // 3 = sum of rules matched for each accepting state + m_prgirulnMin = new data16[3+1]; // 3 = m_crow - m_crowNonAcpt + + m_prgirulnMin[0] = 0; // s6: r2 + m_prgrulnMatched[0] = 2; + + m_prgirulnMin[1] = 1; // s7: r0 + m_prgrulnMatched[1] = 0; + + m_prgirulnMin[2] = 2; // s8: r1 + m_prgrulnMatched[2] = 1; + + m_prgirulnMin[3] = 3; + } + else if (ipass == 3) + { + // Create machine class ranges. + m_cmcr = 1; + m_prgmcr = new GrFSMClassRange[m_cmcr]; + m_prgmcr[0].m_chwFirst = 35; // # + m_prgmcr[0].m_chwLast = 35; + m_prgmcr[0].m_col = 0; + + m_dimcrInit = 1; // (max power of 2 <= m_cmcr); + m_cLoop = 0; // log2(max power of 2 <= m_cmcr); + m_imcrStart = m_cmcr - m_dimcrInit; + + + m_crow = 2; + m_crowNonAcpt = 1; + m_crowFinal = 1; + m_rowFinalMin = m_crow - m_crowFinal; + m_ccol = 1; + + // Set up transition table. + m_prgrowTransitions = new short[(m_crow-m_crowFinal) * m_ccol]; // 1 + short * psn = m_prgrowTransitions; + *psn++ = 1; + + // Set up matched-rules tables. + m_prgrulnMatched = new data16[1]; // 1 = sum of rules matched for each accepting state + m_prgirulnMin = new data16[m_crow - m_crowNonAcpt + 1]; + + m_prgirulnMin[0] = 0; // s1: r0 + m_prgrulnMatched[0] = 0; + + m_prgirulnMin[1] = 1; + } +} + + +/*---------------------------------------------------------------------------------------------- + Second batch of association tests. + + PASS 1: + r0. clsSymbol _ > _ @1 / _ gNumber gHyphen # gNumber _; + r1. clsSymbol _ > _ @1 / _ gHyphen # _; + r2. _ clsSymbol > @6 _ / _ gNumber gHyphen # gNumber _; + r3. _ clsSymbol > @4 _ / _ gHyphen # _; + r4. clsLeft _ > @1 clsRight$1:1 / _ clsLetter gHyphen # _ gSpace; + r5. clsLeft _ > @1 clsRight$1:1 / _ clsLetter gHyphen # clsLetter _ gSpace; + r6. clsLeft _ > @1 clsRight$1:1 / _ clsLetter _ gSpace; + r7. clsLeft _ > @1 clsRight$1:1 / _ clsLetter clsLetter _ gSpace; + + Pass 1 machine classes: + mcSym: @, $, %, & 64, 36, 37, 38 + mcNum: 48-57 + mcHyphen: 45 + mcSpace: 32 + mcLet: 97-122 + mcLeft: ( [ { 40, 91, 123 + mcRight: ) ] } 41, 93, 125 + + Pass 1 FSM: (T=transition, F=final, A=accepting, NA=non-accepting) + + | mcSym mcNum mcHyph mcSpace mcLet mcLeft mcLB + ---------------------------------------------------------------------------------- + T NA s0 | s1 s2 s3 s4 + | + T NA s1 | s5 s6 + | + T NA s2 | s7 + | + T NA s3 | s8 + | + T NA s4 | s9 + | + T NA s5 | s10 + | + T NA s6 | s18 + | + T NA s7 | s11 + | + T NA s8 | s19 + | + T NA s9 | s12 s20 s13 + | + T NA s10 | s14 + | + T NA s11 | s15 + | + T NA s12 | s16 + | + T NA s13 | s21 + | + T NA s14 | s22 + | + T NA s15 | s23 + | + T NA s16 | s24 s17 + | + T NA s17 | s25 + | + F A s18 | + | + F A s19 | + | + F A s20 | + | + F A s21 | + | + F A s22 | + | + F A s23 | + | + F A s24 | + | + F A s25 | + + + Rules Matched: + s18: r1 + s19: r3 + s20: r6 + s21: r7 + s22: r0 + s23: r2 + s24: r4 + s25: r5 +----------------------------------------------------------------------------------------------*/ +void GrEngine::SetUpAssoc2Test() +{ + if (m_pctbl) + delete m_pctbl; + + m_pctbl = new GrClassTable(); + m_pctbl->SetUpAssoc2Test(); + + m_fLineBreak = true; + m_cchwPreXlbContext = 3; + m_cchwPostXlbContext = 2; +} + +void GrClassTable::SetUpAssoc2Test() +{ + m_ccls = 2; // number of classes + m_cclsLinear = 1; // number of classes in linear format (output classes) + + m_prgchwBIGGlyphList = new data16[13]; + + m_prgichwOffsets = new data16[m_ccls+1]; + + data16 * pchw = m_prgchwBIGGlyphList; + + // Output class 0: clsRight + m_prgichwOffsets[0] = 0; + *pchw++ = msbf(data16(41)); // ) + *pchw++ = msbf(data16(93)); // ] + *pchw++ = msbf(data16(125)); // } + + // Input class 1: clsLeft + m_prgichwOffsets[1] = 3; + *pchw++ = msbf(data16(3)); + *pchw++ = msbf(data16(2)); *pchw++ = msbf(data16(1)); *pchw++ = msbf(data16(3-2)); + *pchw++ = msbf(data16(40)); *pchw++ = msbf(data16(0)); // ( + *pchw++ = msbf(data16(91)); *pchw++ = msbf(data16(1)); // [ + *pchw++ = msbf(data16(123)); *pchw++ = msbf(data16(2)); // { + + m_prgichwOffsets[2] = 3 + 4 + 3*2; // 13 +} + +void GrSubPass::SetUpAssoc2Test() +{ + m_nMaxRuleContext = m_nMaxChunk = 6; + m_nMaxRuleLoop = 5; + m_staBehavior = "Assoc2"; + + m_pfsm = new GrFSM(); + Assert(m_pfsm); + m_pfsm->SetUpAssoc2Test(m_ipass); + + m_crul = 8; + + // Set up constraint code. + m_prgbConstraintBlock = new byte[1]; + byte * pb = m_prgbConstraintBlock; + *pb++ = kopRetTrue; // RetTrue + + m_prgibConstraintStart = new data16[m_crul]; + m_prgibConstraintStart[0] = 0; + m_prgibConstraintStart[1] = 0; + m_prgibConstraintStart[2] = 0; + m_prgibConstraintStart[3] = 0; + m_prgibConstraintStart[4] = 0; + m_prgibConstraintStart[5] = 0; + m_prgibConstraintStart[6] = 0; + m_prgibConstraintStart[7] = 0; + + // Set up rule action code. + m_prgbActionBlock = new byte[11 + 9 + 11 + 9 + 14 + 15 + 12 + 13]; + pb = m_prgbActionBlock; + + // Rule 0: clsSymbol _ > _ @1 / _ gNumber gHyphen # gNumber _; + + *pb++ = kopDelete; // Delete + *pb++ = kopNext; // Next + *pb++ = kopCopyNext; // CopyNext + *pb++ = kopCopyNext; // CopyNext + *pb++ = kopCopyNext; // CopyNext + *pb++ = kopCopyNext; // CopyNext + *pb++ = kopInsert; // Insert + *pb++ = kopPutCopy; *pb++ = -4; // PutCopy -4 + *pb++ = kopNext; // Next + *pb++ = kopRetZero; // RetZero + + // Rule 1: clsSymbol _ > _ @1 / _ gHyphen # _; + + *pb++ = kopDelete; // Delete + *pb++ = kopNext; // Next + *pb++ = kopCopyNext; // CopyNext + *pb++ = kopCopyNext; // CopyNext + *pb++ = kopInsert; // Insert + *pb++ = kopPutCopy; *pb++ = -2; // PutCopy -2 + *pb++ = kopNext; // Next + *pb++ = kopRetZero; // RetZero + + // Rule 2: _ clsSymbol > @6 _ / _ gNumber gHyphen # gNumber _; + + *pb++ = kopInsert; // Insert + *pb++ = kopPutCopy; *pb++ = 5; // PutCopy 5 + *pb++ = kopNext; // Next + *pb++ = kopCopyNext; // CopyNext + *pb++ = kopCopyNext; // CopyNext + *pb++ = kopCopyNext; // CopyNext + *pb++ = kopCopyNext; // CopyNext + *pb++ = kopDelete; // Delete + *pb++ = kopNext; // Next + *pb++ = kopRetZero; // RetZero + + // Rule 3: _ clsSymbol > @4 _ / _ gHyphen # _; + + *pb++ = kopInsert; // Insert + *pb++ = kopPutCopy; *pb++ = 3; // PutCopy 3 + *pb++ = kopNext; // Next + *pb++ = kopCopyNext; // CopyNext + *pb++ = kopCopyNext; // CopyNext + *pb++ = kopDelete; // Delete + *pb++ = kopNext; // Next + *pb++ = kopRetZero; // RetZero + + // Rule 4: clsLeft _ > @1 clsRight$1 / _ clsLetter gHyphen # _ gSpace; + + *pb++ = kopCopyNext; // CopyNext + *pb++ = kopCopyNext; // CopyNext + *pb++ = kopCopyNext; // CopyNext + *pb++ = kopCopyNext; // CopyNext + *pb++ = kopInsert; // Insert + *pb++ = kopPutSubs8bitObs; *pb++ = -3; // PutSubs -3 1 0 + *pb++ = 1; *pb++ = 0; + *pb++ = kopAssoc; *pb++ = 1; // Assoc 1 -3 + *pb++ = -3; + *pb++ = kopNext; // Next; + *pb++ = kopRetZero; // RetZero + + // Rule 5: clsLeft _ > @1 clsRight$1 / _ clsLetter gHyphen # clsLetter _ gSpace; + + *pb++ = kopCopyNext; // CopyNext + *pb++ = kopCopyNext; // CopyNext + *pb++ = kopCopyNext; // CopyNext + *pb++ = kopCopyNext; // CopyNext + *pb++ = kopCopyNext; // CopyNext + *pb++ = kopInsert; // Insert + *pb++ = kopPutSubs8bitObs; *pb++ = -4; // PutSubs -4 1 0 + *pb++ = 1; *pb++ = 0; + *pb++ = kopAssoc; *pb++ = 1; // Assoc 1 -4 + *pb++ = -4; + *pb++ = kopNext; // Next; + *pb++ = kopRetZero; // RetZero + + // Rule 6: clsLeft _ > @1 clsRight$1 / _ clsLetter _ gSpace; + + *pb++ = kopCopyNext; // CopyNext + *pb++ = kopCopyNext; // CopyNext + *pb++ = kopInsert; // Insert + *pb++ = kopPutSubs8bitObs; *pb++ = -1; // PutSubs -1 1 0 + *pb++ = 1; *pb++ = 0; + *pb++ = kopAssoc; *pb++ = 1; // Assoc 1 -1 + *pb++ = -1; + *pb++ = kopNext; // Next; + *pb++ = kopRetZero; // RetZero + + // Rule 7: clsLeft _ > @1 clsRight$1 / _ clsLetter clsLetter _ gSpace; + + *pb++ = kopCopyNext; // CopyNext + *pb++ = kopCopyNext; // CopyNext + *pb++ = kopCopyNext; // CopyNext + *pb++ = kopInsert; // Insert + *pb++ = kopPutSubs8bitObs; *pb++ = -2; // PutSubs -2 1 0 + *pb++ = 1; *pb++ = 0; + *pb++ = kopAssoc; *pb++ = 1; // Assoc 1 -2 + *pb++ = -2; + *pb++ = kopNext; // Next; + *pb++ = kopRetZero; // RetZero + + + m_prgibActionStart = new data16[m_crul]; + m_prgibActionStart[0] = 0; + m_prgibActionStart[1] = 11; + m_prgibActionStart[2] = 11 + 9; + m_prgibActionStart[3] = 20 + 11; + m_prgibActionStart[4] = 31 + 9; + m_prgibActionStart[5] = 40 + 14; + m_prgibActionStart[6] = 54 + 15; + m_prgibActionStart[7] = 69 + 12; +} + + +void GrFSM::SetUpAssoc2Test(int ipass) +{ + // Create machine class ranges. + m_cmcr = 10; + m_prgmcr = new GrFSMClassRange[m_cmcr]; + m_prgmcr[0].m_chwFirst = 32; // space + m_prgmcr[0].m_chwLast = 32; + m_prgmcr[0].m_col = 3; + + m_prgmcr[1].m_chwFirst = 35; // # + m_prgmcr[1].m_chwLast = 35; + m_prgmcr[1].m_col = 6; + + m_prgmcr[2].m_chwFirst = 36; // $, %, & + m_prgmcr[2].m_chwLast = 38; + m_prgmcr[2].m_col = 0; + + m_prgmcr[3].m_chwFirst = 40; // ( + m_prgmcr[3].m_chwLast = 40; + m_prgmcr[3].m_col = 5; + + m_prgmcr[4].m_chwFirst = 45; // - + m_prgmcr[4].m_chwLast = 45; + m_prgmcr[4].m_col = 2; + + m_prgmcr[5].m_chwFirst = 48; // 0 - 9 + m_prgmcr[5].m_chwLast = 57; + m_prgmcr[5].m_col = 1; + + m_prgmcr[6].m_chwFirst = 64; // @ + m_prgmcr[6].m_chwLast = 64; + m_prgmcr[6].m_col = 0; + + m_prgmcr[7].m_chwFirst = 91; // [ + m_prgmcr[7].m_chwLast = 91; + m_prgmcr[7].m_col = 5; + + m_prgmcr[8].m_chwFirst = 97; // a - z + m_prgmcr[8].m_chwLast = 122; + m_prgmcr[8].m_col = 4; + + m_prgmcr[9].m_chwFirst = 123; // { + m_prgmcr[9].m_chwLast = 123; + m_prgmcr[9].m_col = 5; + + + m_dimcrInit = 8; // (max power of 2 <= m_cmcr); + m_cLoop = 3; // log2(max power of 2 <= m_cmcr); + m_imcrStart = m_cmcr - m_dimcrInit; + + + m_crow = 26; + m_crowNonAcpt = 18; + m_crowFinal = 8; + m_rowFinalMin = m_crow - m_crowFinal; + m_ccol = 7; + + // Set up transition table. + m_prgrowTransitions = new short[(m_crow-m_crowFinal) * m_ccol]; // 126 + short * psn = m_prgrowTransitions; + *psn++ = 1; *psn++ = 2; *psn++ = 3; *psn++ = 0; *psn++ = 0; *psn++ = 4; *psn++ = 0; + *psn++ = 0; *psn++ = 5; *psn++ = 6; *psn++ = 0; *psn++ = 0; *psn++ = 0; *psn++ = 0; + *psn++ = 0; *psn++ = 0; *psn++ = 7; *psn++ = 0; *psn++ = 0; *psn++ = 0; *psn++ = 0; + *psn++ = 0; *psn++ = 0; *psn++ = 0; *psn++ = 0; *psn++ = 0; *psn++ = 0; *psn++ = 8; + *psn++ = 0; *psn++ = 0; *psn++ = 0; *psn++ = 0; *psn++ = 9; *psn++ = 0; *psn++ = 0; + *psn++ = 0; *psn++ = 0; *psn++ =10; *psn++ = 0; *psn++ = 0; *psn++ = 0; *psn++ = 0; + *psn++ = 0; *psn++ = 0; *psn++ = 0; *psn++ = 0; *psn++ = 0; *psn++ = 0; *psn++ =18; + *psn++ = 0; *psn++ = 0; *psn++ = 0; *psn++ = 0; *psn++ = 0; *psn++ = 0; *psn++ =11; + *psn++ =19; *psn++ = 0; *psn++ = 0; *psn++ = 0; *psn++ = 0; *psn++ = 0; *psn++ = 0; + *psn++ = 0; *psn++ = 0; *psn++ =12; *psn++ =20; *psn++ =13; *psn++ = 0; *psn++ = 0; + *psn++ = 0; *psn++ = 0; *psn++ = 0; *psn++ = 0; *psn++ = 0; *psn++ = 0; *psn++ =14; + *psn++ = 0; *psn++ =15; *psn++ = 0; *psn++ = 0; *psn++ = 0; *psn++ = 0; *psn++ = 0; + *psn++ = 0; *psn++ = 0; *psn++ = 0; *psn++ = 0; *psn++ = 0; *psn++ = 0; *psn++ =16; + *psn++ = 0; *psn++ = 0; *psn++ = 0; *psn++ =21; *psn++ = 0; *psn++ = 0; *psn++ = 0; + *psn++ = 0; *psn++ =22; *psn++ = 0; *psn++ = 0; *psn++ = 0; *psn++ = 0; *psn++ = 0; + *psn++ =23; *psn++ = 0; *psn++ = 0; *psn++ = 0; *psn++ = 0; *psn++ = 0; *psn++ = 0; + *psn++ = 0; *psn++ = 0; *psn++ = 0; *psn++ =24; *psn++ =17; *psn++ = 0; *psn++ = 0; + *psn++ = 0; *psn++ = 0; *psn++ = 0; *psn++ =25; *psn++ = 0; *psn++ = 0; *psn++ = 0; + + // Set up matched-rules tables. + m_prgrulnMatched = new data16[8]; // 3 = sum of rules matched for each accepting state + m_prgirulnMin = new data16[m_crow - m_crowNonAcpt + 1]; + + m_prgirulnMin[0] = 0; // s18: r1 + m_prgrulnMatched[0] = 1; + + m_prgirulnMin[1] = 1; // s19: r3 + m_prgrulnMatched[1] = 3; + + m_prgirulnMin[2] = 2; // s20: r6 + m_prgrulnMatched[2] = 6; + + m_prgirulnMin[3] = 3; // s21: r7 + m_prgrulnMatched[3] = 7; + + m_prgirulnMin[4] = 4; // s22: r0 + m_prgrulnMatched[4] = 0; + + m_prgirulnMin[5] = 5; // s23: r2 + m_prgrulnMatched[5] = 2; + + m_prgirulnMin[6] = 6; // s24: r4 + m_prgrulnMatched[6] = 4; + + m_prgirulnMin[7] = 7; // s25: r5 + m_prgrulnMatched[7] = 5; + + m_prgirulnMin[8] = 8; +} + +/*---------------------------------------------------------------------------------------------- + Test of default associations + + PASS 1: + r0. gDollar _ > @1 gEquals; + r1. gAtSign > _ ; + + Pass 1 machine classes: + mcDollar: $ 36 + mcAt: @ 64 + + Pass 1 FSM: (T=transition, F=final, A=accepting, NA=non-accepting) + + | mcDollar mcAt + -------------------------------------- + T NA s0 | s1 s2 + | + F A s1 | + | + F A s2 | + + + Rules Matched: + s1: r0 + s2: r1 +----------------------------------------------------------------------------------------------*/ +void GrEngine::SetUpDefaultAssocTest() +{ + if (m_pctbl) + delete m_pctbl; + + m_pctbl = new GrClassTable(); + m_pctbl->SetUpDefaultAssocTest(); +} + +void GrClassTable::SetUpDefaultAssocTest() +{ + m_ccls = 1; // number of classes + m_cclsLinear = 1; // number of classes in linear format + + m_prgchwBIGGlyphList = new data16[1]; + + m_prgichwOffsets = new data16[m_ccls+1]; + + data16 * pchw = m_prgchwBIGGlyphList; + + // Output class 0: gEquals + m_prgichwOffsets[0] = 0; + *pchw++ = msbf(data16(61)); + + m_prgichwOffsets[1] = 1; +} + +void GrSubPass::SetUpDefaultAssocTest() +{ + m_nMaxRuleContext = m_nMaxChunk = 1; + m_nMaxRuleLoop = 5; + m_staBehavior = "DefaultAssoc"; + + m_pfsm = new GrFSM(); + Assert(m_pfsm); + m_pfsm->SetUpDefaultAssocTest(); + + m_crul = 2; + + // Set up constraint code. + m_prgbConstraintBlock = new byte[1]; + byte * pb = m_prgbConstraintBlock; + *pb++ = kopRetTrue; + + m_prgibConstraintStart = new data16[m_crul]; + m_prgibConstraintStart[0] = 0; + m_prgibConstraintStart[1] = 0; + + // Set up rule action code. + m_prgbActionBlock = new byte[6 + 3]; + pb = m_prgbActionBlock; + + // Rule 0: gDollar _ > @1 gEquals; + + *pb++ = kopCopyNext; // CopyNext + *pb++ = kopInsert; // Insert + *pb++ = kopPutGlyph8bitObs; *pb++ = 0; // PutGlyph 0 + *pb++ = kopNext; // Next + *pb++ = kopRetZero; // RetZero + + // Rule 1: gAtSign > _ ; + + *pb++ = kopDelete; // Delete + *pb++ = kopNext; // Next + *pb++ = kopRetZero; // RetZero + + m_prgibActionStart = new data16[m_crul]; + m_prgibActionStart[0] = 0; + m_prgibActionStart[1] = 6; +} + + +void GrFSM::SetUpDefaultAssocTest() +{ + // Create machine class ranges. + m_cmcr = 2; + m_prgmcr = new GrFSMClassRange[m_cmcr]; + m_prgmcr[0].m_chwFirst = 36; // $ + m_prgmcr[0].m_chwLast = 36; + m_prgmcr[0].m_col = 0; + + m_prgmcr[1].m_chwFirst = 64; // @ + m_prgmcr[1].m_chwLast = 64; + m_prgmcr[1].m_col = 1; + + + m_dimcrInit = 2; // (max power of 2 <= m_cmcr); + m_cLoop = 1; // log2(max power of 2 <= m_cmcr); + m_imcrStart = m_cmcr - m_dimcrInit; + + + m_crow = 3; + m_crowNonAcpt = 1; + m_crowFinal = 2; + m_rowFinalMin = m_crow - m_crowFinal; + m_ccol = 2; + + // Set up transition table. + m_prgrowTransitions = new short[(m_crow-m_crowFinal) * m_ccol]; // 2 + short * psn = m_prgrowTransitions; + *psn++ = 1; *psn++ = 2; + + // Set up matched-rules tables. + m_prgrulnMatched = new data16[2]; // 2 = sum of rules matched for each accepting state + m_prgirulnMin = new data16[m_crow - m_crowNonAcpt + 1]; + + m_prgirulnMin[0] = 0; // s1: r0 + m_prgrulnMatched[0] = 0; + + m_prgirulnMin[1] = 1; // s2: r1 + m_prgrulnMatched[1] = 1; + + m_prgirulnMin[2] = 2; +} + +/*---------------------------------------------------------------------------------------------- + Test of features. The ID of "theFeature" == 234, its index is 0. The default value is 1. + + PASS 1: + if (theFeature == 0) // * -> $ + r0. gAsterisk > gDollar ; + else if (theFeature == 1) // * -> % + r1. gAsterisk > gPercent ; + else // * -> @ + r2. gAsterisk > gAt ; + + Pass 1 machine classes: + mcAsterisk: * 42 + + Pass 1 FSM: (T=transition, F=final, A=accepting, NA=non-accepting) + + | mcAsterisk + -------------------------------- + T NA s0 | s1 + | + F A s1 | + + + Rules Matched: + s1: r0, r1, r2 +----------------------------------------------------------------------------------------------*/ +void GrEngine::SetUpFeatureTest() +{ + AddFeature(234, 0, 1, 1); + + if (m_pctbl) + delete m_pctbl; + + m_pctbl = new GrClassTable(); + m_pctbl->SetUpFeatureTest(); +} + +void GrClassTable::SetUpFeatureTest() +{ + m_ccls = 4; // number of classes + m_cclsLinear = 4; // number of classes in linear format + + m_prgchwBIGGlyphList = new data16[4]; + + m_prgichwOffsets = new data16[m_ccls + 1]; + + data16 * pchw = m_prgchwBIGGlyphList; + + m_prgichwOffsets[0] = 0; + *pchw++ = msbf(data16(36)); // $ + + m_prgichwOffsets[1] = 1; + *pchw++ = msbf(data16(37)); // % + + m_prgichwOffsets[2] = 2; + *pchw++ = msbf(data16(64)); // @ + + m_prgichwOffsets[3] = 3; + *pchw++ = msbf(data16(42)); // * + + m_prgichwOffsets[4] = 4; +} + +void GrSubPass::SetUpFeatureTest() +{ + m_nMaxRuleContext = m_nMaxChunk = 1; + m_nMaxRuleLoop = 5; + m_staBehavior = "Feature"; + + m_pfsm = new GrFSM(); + Assert(m_pfsm); + m_pfsm->SetUpFeatureTest(); + + m_crul = 3; + + // Set up constraint code. + m_prgbConstraintBlock = new byte[1 + 6 + 12 + 12]; + byte * pb = m_prgbConstraintBlock; + *pb++ = kopRetTrue; + + // (theFeature == 0) + *pb++ = kopPushFeat; *pb++ = 0; // PushFeat 0 + *pb++ = kopPushByte; *pb++ = 0; // PushByte 0 + *pb++ = kopEqual; // Equal + *pb++ = kopPopRet; // PopAndRet + + // (theFeature != 0 && theFeature == 1) + *pb++ = kopPushFeat; *pb++ = 0; // PushFeat 0 + *pb++ = kopPushByte; *pb++ = 0; // PushByte 0 + *pb++ = kopNotEq; // NotEq + *pb++ = kopPushFeat; *pb++ = 0; // PushFeat 0 + *pb++ = kopPushByte; *pb++ = 1; // PushByte 1 + *pb++ = kopEqual; // Equal + *pb++ = kopAnd; // And + *pb++ = kopPopRet; // PopAndRet + + // (theFeature != 0 && theFeature != 1) + *pb++ = kopPushFeat; *pb++ = 0; // PushFeat 0 + *pb++ = kopPushByte; *pb++ = 0; // PushByte 0 + *pb++ = kopNotEq; // NotEq + *pb++ = kopPushFeat; *pb++ = 0; // PushFeat 0 + *pb++ = kopPushByte; *pb++ = 1; // PushByte 1 + *pb++ = kopNotEq; // NotEq + *pb++ = kopAnd; // And + *pb++ = kopPopRet; // PopAndRet + + m_prgibConstraintStart = new data16[m_crul]; + m_prgibConstraintStart[0] = 1; + m_prgibConstraintStart[1] = 1 + 6; + m_prgibConstraintStart[2] = 1 + 6 + 12; + + // Set up rule action code. + m_prgbActionBlock = new byte[4 + 4 + 4]; + pb = m_prgbActionBlock; + + // Rule 0: gAsterisk > gDollar ; + + *pb++ = kopPutGlyph8bitObs; *pb++ = 0; // PutGlyph 0 + *pb++ = kopNext; // Next + *pb++ = kopRetZero; // RetZero + + // Rule 1: gAsterisk > gPercent ; + + *pb++ = kopPutGlyph8bitObs; *pb++ = 1; // PutGlyph 1 + *pb++ = kopNext; // Next + *pb++ = kopRetZero; // RetZero + + // Rule 2: gAsterisk > gAt ; + + *pb++ = kopPutGlyph8bitObs; *pb++ = 2; // PutGlyph 2 + *pb++ = kopNext; // Next + *pb++ = kopRetZero; // RetZero + + m_prgibActionStart = new data16[m_crul]; + m_prgibActionStart[0] = 0; + m_prgibActionStart[1] = 4; + m_prgibActionStart[2] = 4 + 4; +} + + +void GrFSM::SetUpFeatureTest() +{ + // Create machine class ranges. + m_cmcr = 1; + m_prgmcr = new GrFSMClassRange[m_cmcr]; + m_prgmcr[0].m_chwFirst = 42; // * + m_prgmcr[0].m_chwLast = 42; + m_prgmcr[0].m_col = 0; + + + m_dimcrInit = 1; // (max power of 2 <= m_cmcr); + m_cLoop = 0; // log2(max power of 2 <= m_cmcr); + m_imcrStart = m_cmcr - m_dimcrInit; + + + m_crow = 2; + m_crowNonAcpt = 1; + m_crowFinal = 1; + m_rowFinalMin = m_crow - m_crowFinal; + m_ccol = 1; + + // Set up transition table. + m_prgrowTransitions = new short[(m_crow-m_crowFinal) * m_ccol]; // 1 + short * psn = m_prgrowTransitions; + *psn++ = 1; + + // Set up matched-rules tables. + m_prgrulnMatched = new data16[3]; // 3 = sum of rules matched for each accepting state + m_prgirulnMin = new data16[m_crow - m_crowNonAcpt + 1]; + + m_prgirulnMin[0] = 0; // s1: r0, r1, r2 + m_prgrulnMatched[0] = 0; + m_prgrulnMatched[1] = 1; + m_prgrulnMatched[2] = 2; + + m_prgirulnMin[1] = 3; +} + + +/*---------------------------------------------------------------------------------------------- + Tests of ligatures and components. + + PASS 1 (line-break): + r0. clsLetter { break = 2 } + + Pass 1 machine classes: + mcLetter: a - z 97 - 122 + + Pass 1 FSM: (T=transition, F=final, A=accepting, NA=non-accepting) + + | clsLetter + -------------------------------- + T NA s0 | s1 + | + F A s1 | + + + Rules Matched: + s1: r0 + + + PASS 2 (substitution): + r0. gX gY gZ > gCapX:(1 2 4) { comp {X.ref=@1; Y.ref=@2; Z.ref=@4 }} _ _ + / _ _ # {break==2} _; + r1. gX gY gZ > _ _ gCapZ:(1 3 4) { comp {X.ref=@1; Y.ref=@3; Z.ref=@4 }} + / _ # {break==2} _ _; + r2. gX gY gZ > _ gCapY:(1 2 3) { comp {X.ref=@1; Y.ref=@2; Z.ref=@3 }} gPlus; + + Pass 2 machine classes: + mcX: x 120 + mcY: y 121 + mcZ: z 122 + mcLB: 35 + + + Pass 2 FSM: + | mcX mcY mcZ mcLB + ---------------------------------------------------- + T NA s0 | s1 + | - - - + T NA s1 | s2 s3 + | - - - + T NA s2 | s6 s4 + | - - - + T NA s3 | s5 + | - - - + T NA s4 | s7 + | - - - + T NA s5 | s8 + | - - - + F A s6 | + | - - - + F A s7 | + | - - - + F A s8 | + | - - - + + Rules matched: + s6: r2 + s7: r0 + s8: r1 +----------------------------------------------------------------------------------------------*/ +void GrEngine::SetUpLigatureTest() +{ + if (m_pctbl) + delete m_pctbl; + + m_pctbl = new GrClassTable(); + m_pctbl->SetUpLigatureTest(); + + m_pgtbl = new GrGlyphTable(); + m_pgtbl->SetUpLigatureTest(); + + m_fLineBreak = true; + m_cchwPreXlbContext = 2; + m_cchwPostXlbContext = 2; +} + +void GrClassTable::SetUpLigatureTest() +{ + m_ccls = 7; // number of classes + m_cclsLinear = 7; // number of classes in linear format (all classes are single-item) + + m_prgchwBIGGlyphList = new data16[7]; + + m_prgichwOffsets = new data16[m_ccls+1]; + + data16 * pchw = m_prgchwBIGGlyphList; + + // Class 0: gCapX + m_prgichwOffsets[0] = 0; + *pchw++ = msbf(data16(88)); // X + + // Class 1: gCapY + m_prgichwOffsets[1] = 1; + *pchw++ = msbf(data16(89)); // Y + + // Class 2: gCapZ + m_prgichwOffsets[2] = 2; + *pchw++ = msbf(data16(90)); // Z + + // Class 3: gPlus + m_prgichwOffsets[3] = 3; + *pchw++ = msbf(data16(43)); // + + + // Class 4: gX + m_prgichwOffsets[4] = 4; + *pchw++ = msbf(data16(120)); // x + + // Class 5: gY + m_prgichwOffsets[5] = 5; + *pchw++ = msbf(data16(121)); // y + + // Class 6: gZ + m_prgichwOffsets[6] = 6; + *pchw++ = msbf(data16(122)); // z + + m_prgichwOffsets[7] = 7; +} + +void GrGlyphTable::SetUpLigatureTest() +{ + SetNumberOfGlyphs(128); + SetNumberOfStyles(1); + + GrGlyphSubTable * pgstbl = new GrGlyphSubTable(); + Assert(pgstbl); + + pgstbl->Initialize(1, 0, 128, 10, 4); + SetSubTable(0, pgstbl); + + SetNumberOfComponents(4); // comp.w, comp.x, comp.y, comp.z + + pgstbl->SetUpLigatureTest(); +} + +/*********************************************************************************************** + TODO: This method is BROKEN because m_prgibBIGAttrValues has been changed. It is no + longer a data16 *. The Gloc table can contain 16-bit or 32-bit entries and must be + accessed accordingly. +***********************************************************************************************/ +void GrGlyphSubTable::SetUpLigatureTest() +{ + m_nAttrIDLim = 20; + + m_pgatbl = new GrGlyphAttrTable(); + m_pgatbl->Initialize(0, 102); + + for (int i = 0; i < 88; i++) + m_prgibBIGAttrValues[i] = (byte)msbf(data16(0)); + + m_prgibBIGAttrValues[88] = (byte)msbf(data16(0)); + m_prgibBIGAttrValues[89] = (byte)msbf(data16(34)); + m_prgibBIGAttrValues[90] = (byte)msbf(data16(68)); + + for (i = 91; i < 128; i++) + m_prgibBIGAttrValues[i] = (byte)msbf(data16(102)); + + m_pgatbl->SetUpLigatureTest(); +} + +void GrGlyphAttrTable::SetUpLigatureTest() +{ + // Glyph 88 'X' (2 runs; offset = 0): + // 0 = 0 comp.w undefined + // 1 = 8 comp.x: (0,0, 25,100) + // 2 = 12 comp.y: (25,0, 75,100) + // 3 = 16 comp.z: (75,0, 100,100) + // 4-7 = 0 comp.w box corners + // 8 = 100 comp.x. top + // 9 = 0 bottom + // 10= 0 left + // 11= 25 right + // 12= 100 comp.y. top + // 13= 0 bottom + // 14= 25 left + // 15= 75 right + // 16= 100 comp.z. top + // 17= 0 bottom + // 18= 75 left + // 19= 100 right + // + // Glyph 89 (2 runs; offset = 34): + // 0 = 0 comp.w undefined + // 1 = 8 comp.x: (0,0, 33,100) + // 2 = 12 comp.y: (33,0, 67,100) + // 3 = 16 comp.z: (67,0, 100,100) + // 4-7 = 0 comp.w box corners + // 8 = 100 comp.x. top + // 9 = 0 bottom + // 10= 0 left + // 11= 33 right + // 12= 100 comp.y. top + // 13= 0 bottom + // 14= 33 left + // 15= 67 right + // 16= 100 comp.z. top + // 17= 0 bottom + // 18= 67 left + // 19= 100 right + // + // Glyph 90 (2 runs; offset = 68): + // 0 = 0 comp.w undefined + // 1 = 8 comp.x: (0,0, 10,100) + // 2 = 12 comp.y: (10,0, 90,100) + // 3 = 16 comp.z: (90,0, 100,100) + // 4-7 = 0 comp.w box corners + // 8 = 100 comp.x. top + // 9 = 0 bottom + // 10= 0 left + // 11= 10 right + // 12= 100 comp.y. top + // 13= 0 bottom + // 14= 10 left + // 15= 90 right + // 16= 100 comp.z. top + // 17= 0 bottom + // 18= 90 left + // 19= 100 right + + byte * pbBIG = m_prgbBIGEntries; + + GrGlyphAttrRun gatrun; + + // Glyph 88 'X' + gatrun.m_bMinAttrID = 1; + gatrun.m_cAttrs = 3; + gatrun.m_rgchwBIGValues[0] = msbf(data16(8)); + gatrun.m_rgchwBIGValues[1] = msbf(data16(12)); + gatrun.m_rgchwBIGValues[2] = msbf(data16(16)); + memcpy(pbBIG, &gatrun, 8); // 8 = 3*2 + 2 + pbBIG += 8; + gatrun.m_bMinAttrID = 8; // comp.x.top + gatrun.m_cAttrs = 12; + gatrun.m_rgchwBIGValues[0] = msbf(data16(100)); + gatrun.m_rgchwBIGValues[1] = msbf(data16(0)); + gatrun.m_rgchwBIGValues[2] = msbf(data16(0)); + gatrun.m_rgchwBIGValues[3] = msbf(data16(25)); + gatrun.m_rgchwBIGValues[4] = msbf(data16(100)); + gatrun.m_rgchwBIGValues[5] = msbf(data16(0)); + gatrun.m_rgchwBIGValues[6] = msbf(data16(25)); + gatrun.m_rgchwBIGValues[7] = msbf(data16(75)); + gatrun.m_rgchwBIGValues[8] = msbf(data16(100)); + gatrun.m_rgchwBIGValues[9] = msbf(data16(0)); + gatrun.m_rgchwBIGValues[10] = msbf(data16(75)); + gatrun.m_rgchwBIGValues[11] = msbf(data16(100)); + memcpy(pbBIG, &gatrun, 26); // 26 = 12*2 + 2 + pbBIG += 26; + + // Glyph 89 'Y' + gatrun.m_bMinAttrID = 1; + gatrun.m_cAttrs = 3; + gatrun.m_rgchwBIGValues[0] = msbf(data16(8)); + gatrun.m_rgchwBIGValues[1] = msbf(data16(12)); + gatrun.m_rgchwBIGValues[2] = msbf(data16(16)); + memcpy(pbBIG, &gatrun, 8); // 8 = 3*2 + 2 + pbBIG += 8; + gatrun.m_bMinAttrID = 8; // comp.x.top + gatrun.m_cAttrs = 12; + gatrun.m_rgchwBIGValues[0] = msbf(data16(100)); + gatrun.m_rgchwBIGValues[1] = msbf(data16(0)); + gatrun.m_rgchwBIGValues[2] = msbf(data16(0)); + gatrun.m_rgchwBIGValues[3] = msbf(data16(33)); + gatrun.m_rgchwBIGValues[4] = msbf(data16(100)); + gatrun.m_rgchwBIGValues[5] = msbf(data16(0)); + gatrun.m_rgchwBIGValues[6] = msbf(data16(33)); + gatrun.m_rgchwBIGValues[7] = msbf(data16(67)); + gatrun.m_rgchwBIGValues[8] = msbf(data16(100)); + gatrun.m_rgchwBIGValues[9] = msbf(data16(0)); + gatrun.m_rgchwBIGValues[10] = msbf(data16(67)); + gatrun.m_rgchwBIGValues[11] = msbf(data16(100)); + memcpy(pbBIG, &gatrun, 26); // 26 = 12*2 + 2 + pbBIG += 26; + + // Glyph 90 'Z' + gatrun.m_bMinAttrID = 1; + gatrun.m_cAttrs = 3; + gatrun.m_rgchwBIGValues[0] = msbf(data16(8)); + gatrun.m_rgchwBIGValues[1] = msbf(data16(12)); + gatrun.m_rgchwBIGValues[2] = msbf(data16(16)); + memcpy(pbBIG, &gatrun, 8); // 8 = 3*2 + 2 + pbBIG += 8; + gatrun.m_bMinAttrID = 8; // comp.x.top + gatrun.m_cAttrs = 12; + gatrun.m_rgchwBIGValues[0] = msbf(data16(100)); + gatrun.m_rgchwBIGValues[1] = msbf(data16(0)); + gatrun.m_rgchwBIGValues[2] = msbf(data16(0)); + gatrun.m_rgchwBIGValues[3] = msbf(data16(10)); + gatrun.m_rgchwBIGValues[4] = msbf(data16(100)); + gatrun.m_rgchwBIGValues[5] = msbf(data16(0)); + gatrun.m_rgchwBIGValues[6] = msbf(data16(10)); + gatrun.m_rgchwBIGValues[7] = msbf(data16(90)); + gatrun.m_rgchwBIGValues[8] = msbf(data16(100)); + gatrun.m_rgchwBIGValues[9] = msbf(data16(0)); + gatrun.m_rgchwBIGValues[10] = msbf(data16(90)); + gatrun.m_rgchwBIGValues[11] = msbf(data16(100)); + memcpy(pbBIG, &gatrun, 26); // 26 = 12*2 + 2 + pbBIG += 26; + + Assert(pbBIG == m_prgbBIGEntries + 102); +} + +void GrLineBreakPass::SetUpLigatureTest() +{ + m_nMaxRuleContext = m_nMaxChunk = 1; + m_nMaxRuleLoop = 5; + m_staBehavior = "Ligatures"; + + m_pfsm = new GrFSM(); + Assert(m_pfsm); + m_pfsm->SetUpLigatureTest(m_ipass); + + m_crul = 1; + + // Set up constraint code--succeed trivially. + m_prgbConstraintBlock = new byte[1]; + byte * pb = m_prgbConstraintBlock; + *pb++ = kopRetTrue; // RetTrue + + m_prgibConstraintStart = new data16[m_crul]; + m_prgibConstraintStart[0] = 0; + + // Set up rule action code. + m_prgbActionBlock = new byte[8]; + pb = m_prgbActionBlock; + + // Rule 0: clsLetter { break = 2 } + + *pb++ = kopPutCopy; *pb++ = 0; // PutCopy 0 + *pb++ = kopPushByte; *pb++ = 2; // PushByte 2 + *pb++ = kopAttrSet; *pb++ = kslatBreak; // AttrSet breakweight + *pb++ = kopNext; // Next + *pb++ = kopRetZero; // RetZero + + m_prgibActionStart = new data16[m_crul]; + m_prgibActionStart[0] = 0; +} + +void GrSubPass::SetUpLigatureTest() +{ + if (m_ipass == 2) + { + m_nMaxRuleContext = m_nMaxChunk = 4; + m_nMaxRuleLoop = 5; + m_staBehavior = "Ligatures"; + + m_pfsm = new GrFSM(); + Assert(m_pfsm); + m_pfsm->SetUpLigatureTest(m_ipass); + + m_crul = 3; + + // Set up constraint code. + m_prgbConstraintBlock = new byte[1 + 10 + 10]; + byte * pb = m_prgbConstraintBlock; + *pb++ = kopRetTrue; // RetTrue + + *pb++ = kopCntxtItem; *pb++ = 2; // ContextItem 2 + *pb++ = kopPushSlotAttr;*pb++ = kslatBreak; // PushSlotAttr break 0 + *pb++ = 0; + *pb++ = kopPushByte; *pb++ = 2; // Push 2 + *pb++ = kopEqual; // Equal + *pb++ = kopOr; // Or + *pb++ = kopPopRet; // PopAndRet + + *pb++ = kopCntxtItem; *pb++ = 1; // ContextItem 1 + *pb++ = kopPushSlotAttr;*pb++ = kslatBreak; // PushSlotAttr break 0 + *pb++ = 0; + *pb++ = kopPushByte; *pb++ = 2; // Push 2 + *pb++ = kopEqual; // Equal + *pb++ = kopOr; // Or + *pb++ = kopPopRet; // PopAndRet + + m_prgibConstraintStart = new data16[m_crul]; + m_prgibConstraintStart[0] = 1; + m_prgibConstraintStart[1] = 11; + m_prgibConstraintStart[2] = 0; + + // Set up rule action code. + m_prgbActionBlock = new byte[29 + 29 + 29]; + pb = m_prgbActionBlock; + + // Rule 0: gX gY gZ > gCapX:(1 2 4) { comp {X.ref=@1; Y.ref=@2; Z.ref=@4 }} _ _ + // / _ _ # {break==2} _; + + *pb++ = kopPutGlyph8bitObs; *pb++ = 0; // PutGlyph 0 (gCapX) + *pb++ = kopAssoc; *pb++ = 3; // Assoc 3 0 1 3 + *pb++ = 0; *pb++ = 1; *pb++ = 3; + *pb++ = kopPushByte; *pb++ = 0; // Push 0 + *pb++ = kopIAttrSetSlot; // IAttrSetSlot comp_ref x + *pb++ = kslatCompRef; *pb++ = 1; + *pb++ = kopPushByte; *pb++ = 1; // Push 1 + *pb++ = kopIAttrSetSlot; // IAttrSetSlot comp_ref y + *pb++ = kslatCompRef; *pb++ = 2; + *pb++ = kopPushByte; *pb++ = 3; // Push 3 + *pb++ = kopIAttrSetSlot; // IAttrSetSlot comp_ref z + *pb++ = kslatCompRef; *pb++ = 3; + *pb++ = kopNext; // Next + *pb++ = kopDelete; // Delete + *pb++ = kopNext; // Next + *pb++ = kopCopyNext; // CopyNext + *pb++ = kopDelete; // Delete + *pb++ = kopNext; // Next + *pb++ = kopRetZero; // RetZero + + // Rule 1: gX gY gZ > _ _ gCapZ:(1 3 4) { comp {X.ref=@1; Y.ref=@3; Z.ref=@4 }} + // / _ # {break==2} _ _; + + *pb++ = kopDelete; // Delete + *pb++ = kopNext; // Next + *pb++ = kopCopyNext; // CopyNext + *pb++ = kopDelete; // Delete + *pb++ = kopNext; // Next + *pb++ = kopPutGlyph8bitObs; *pb++ = 2; // PutGlyph 2 (gCapZ) + *pb++ = kopAssoc; *pb++ = 3; // Assoc 3 -3 -1 0 + *pb++ = -3; *pb++ = -1; *pb++ = 0; + *pb++ = kopPushByte; *pb++ = -3; // Push -3 + *pb++ = kopIAttrSetSlot; // IAttrSetSlot comp_ref x + *pb++ = kslatCompRef; *pb++ = 1; + *pb++ = kopPushByte; *pb++ = -1; // Push -1 + *pb++ = kopIAttrSetSlot; // IAttrSetSlot comp_ref y + *pb++ = kslatCompRef; *pb++ = 2; + *pb++ = kopPushByte; *pb++ = 0; // Push 0 + *pb++ = kopIAttrSetSlot; // IAttrSetSlot comp_ref z + *pb++ = kslatCompRef; *pb++ = 3; + *pb++ = kopNext; // Next + *pb++ = kopRetZero; // RetZero + + // Rule 2: gX gY gZ > _ gCapY:(1 2 3) { comp {X.ref=@1; Y.ref=@2; Z.ref=@3 }} gPlus; + + *pb++ = kopDelete; // Delete + *pb++ = kopNext; // Next + *pb++ = kopPutGlyph8bitObs; *pb++ = 1; // PutGlyph 1 (gCapY) + *pb++ = kopAssoc; *pb++ = 3; // Assoc 3 -1 0 1 + *pb++ = -1; *pb++ = 0; *pb++ = 1; + *pb++ = kopPushByte; *pb++ = -1; // Push -1 + *pb++ = kopIAttrSetSlot; // IAttrSetSlot comp_ref x + *pb++ = kslatCompRef; *pb++ = 1; + *pb++ = kopPushByte; *pb++ = 0; // Push 0 + *pb++ = kopIAttrSetSlot; // IAttrSetSlot comp_ref y + *pb++ = kslatCompRef; *pb++ = 2; + *pb++ = kopPushByte; *pb++ = 1; // Push 1 + *pb++ = kopIAttrSetSlot; // IAttrSetSlot comp_ref z + *pb++ = kslatCompRef; *pb++ = 3; + *pb++ = kopNext; // Next + *pb++ = kopPutGlyph8bitObs; *pb++ = 3; // PutGlyph 3 (gPlus) + *pb++ = kopNext; // Next + *pb++ = kopRetZero; // RetZero + + m_prgibActionStart = new data16[m_crul]; + m_prgibActionStart[0] = 0; + m_prgibActionStart[1] = 29; + m_prgibActionStart[2] = 29 + 29; + } + else + Assert(false); +} + + +void GrFSM::SetUpLigatureTest(int ipass) +{ + if (ipass == 1) + { + // Create machine class ranges. + m_cmcr = 1; + m_prgmcr = new GrFSMClassRange[m_cmcr]; + m_prgmcr[0].m_chwFirst = 97; // a - z + m_prgmcr[0].m_chwLast = 122; + m_prgmcr[0].m_col = 0; + + m_dimcrInit = 1; // (max power of 2 <= m_cmcr); + m_cLoop = 0; // log2(max power of 2 <= m_cmcr); + m_imcrStart = m_cmcr - m_dimcrInit; + + + m_crow = 2; + m_crowNonAcpt = 1; + m_crowFinal = 1; + m_rowFinalMin = m_crow - m_crowFinal; + m_ccol = 1; + + // Set up transition table. + m_prgrowTransitions = new short[(m_crow-m_crowFinal) * m_ccol]; // 1 + short * psn = m_prgrowTransitions; + *psn++ = 1; + + // Set up matched-rules tables. + m_prgrulnMatched = new data16[1]; // 1 = sum of rules matched for each accepting state + m_prgirulnMin = new data16[m_crow - m_crowNonAcpt + 1]; + + m_prgirulnMin[0] = 0; // s1: r0 + m_prgrulnMatched[0] = 0; + + m_prgirulnMin[1] = 1; + } + else if (ipass == 2) + { + // Create machine class ranges. + m_cmcr = 4; + m_prgmcr = new GrFSMClassRange[m_cmcr]; + m_prgmcr[0].m_chwFirst = 35; // # + m_prgmcr[0].m_chwLast = 35; + m_prgmcr[0].m_col = 3; + + m_prgmcr[1].m_chwFirst = 120; // x + m_prgmcr[1].m_chwLast = 120; + m_prgmcr[1].m_col = 0; + + m_prgmcr[2].m_chwFirst = 121; // y + m_prgmcr[2].m_chwLast = 121; + m_prgmcr[2].m_col = 1; + + m_prgmcr[3].m_chwFirst = 122; // z + m_prgmcr[3].m_chwLast = 123; + m_prgmcr[3].m_col = 2; + + + m_dimcrInit = 4; // (max power of 2 <= m_cmcr); + m_cLoop = 2; // log2(max power of 2 <= m_cmcr); + m_imcrStart = m_cmcr - m_dimcrInit; + + + m_crow = 9; + m_crowNonAcpt = 6; + m_crowFinal = 3; + m_rowFinalMin = m_crow - m_crowFinal; + m_ccol = 4; + + // Set up transition table. + m_prgrowTransitions = new short[(m_crow-m_crowFinal) * m_ccol]; // 24 + short * psn = m_prgrowTransitions; + *psn++ = 1; *psn++ = 0; *psn++ = 0; *psn++ = 0; + *psn++ = 0; *psn++ = 2; *psn++ = 0; *psn++ = 3; + *psn++ = 0; *psn++ = 0; *psn++ = 6; *psn++ = 4; + *psn++ = 0; *psn++ = 5; *psn++ = 0; *psn++ = 0; + *psn++ = 0; *psn++ = 0; *psn++ = 7; *psn++ = 0; + *psn++ = 0; *psn++ = 0; *psn++ = 8; *psn++ = 0; + + // Set up matched-rules tables. + m_prgrulnMatched = new data16[3]; // 3 = sum of rules matched for each accepting state + m_prgirulnMin = new data16[3+1]; // 3 = m_crow - m_crowNonAcpt + + m_prgirulnMin[0] = 0; // s6: r2 + m_prgrulnMatched[0] = 2; + + m_prgirulnMin[1] = 1; // s7: r0 + m_prgrulnMatched[1] = 0; + + m_prgirulnMin[2] = 2; // s8: r1 + m_prgrulnMatched[2] = 1; + + m_prgirulnMin[3] = 3; + } +} + + +/*---------------------------------------------------------------------------------------------- + Tests of ligatures and components where the components are modified glyphs. + + PASS 1: + r0. gX gI > @2 @1; + r1. clsPlainVowel gAcute > clsVowelAcute:(1 2) _; + r2. clsPlainVowel gGrave > clsVowelGrave:(1 2) _; + + Pass 1 machine classes: + mcX: x 120 + mcI: i 105 + mcPV: a e o u 97 101 111 117 + mcAcute: / 47 + mcGrave: \ 92 + + Pass 1 FSM: (T=transition, F=final, A=accepting, NA=non-accepting) + + | mcX mcPV mcI mcAcute mcGrave + --------------------------------------------------------------- + T NA s0 | s1 s2 s2 + | - - - - + T NA s1 | s3 + | - - - - + T NA s2 | s4 s5 + | - - - - + F A s3 | + | - - - - + F A s4 | + | - - - - + F A s5 | + | - - - - + + + Rules Matched: + s3: r0 + s4: r1 + s5: r2 + + + PASS 2: + r0. clsVowel gI > clsCapVowel:(1 2) { comp {v.ref = @1; i.ref = @2}} _; + + Pass 2 machine classes: + mcV: 97 101 111 117 + 224 225 232 233 236 237 242 243 249 250 + mcI: i 105 + + + Pass 2 FSM: + | mcV mcI + ---------------------------------------------------- + T NA s0 | s1 s1 + | - + T NA s1 | s2 + | - + F A s2 | + | - + + Rules matched: + s2: r0 +----------------------------------------------------------------------------------------------*/ +void GrEngine::SetUpLigature2Test() +{ + if (m_pctbl) + delete m_pctbl; + + m_pctbl = new GrClassTable(); + m_pctbl->SetUpLigature2Test(); + + m_pgtbl = new GrGlyphTable(); + m_pgtbl->SetUpLigature2Test(); +} + +void GrClassTable::SetUpLigature2Test() +{ + m_ccls = 5; // number of classes + m_cclsLinear = 3; // number of classes in linear format + + m_prgchwBIGGlyphList = new data16[73]; + + m_prgichwOffsets = new data16[m_ccls+1]; + + data16 * pchw = m_prgchwBIGGlyphList; + + // Output class 0: clsVowelAcute + m_prgichwOffsets[0] = 0; + *pchw++ = msbf(data16(225)); // a-acute + *pchw++ = msbf(data16(233)); // e-acute + *pchw++ = msbf(data16(237)); // i-acute + *pchw++ = msbf(data16(243)); // o-acute + *pchw++ = msbf(data16(250)); // u-acute + + // Output class 1: clsVowelGrave + m_prgichwOffsets[1] = 5; + *pchw++ = msbf(data16(224)); // a-grave + *pchw++ = msbf(data16(232)); // e-grave + *pchw++ = msbf(data16(236)); // i-grave + *pchw++ = msbf(data16(242)); // o-grave + *pchw++ = msbf(data16(249)); // u-grave + + // Output class 2: clsCapVowel + m_prgichwOffsets[2] = 5 + 5; // 10 + *pchw++ = msbf(data16(65)); // A + *pchw++ = msbf(data16(69)); // E + *pchw++ = msbf(data16(73)); // I + *pchw++ = msbf(data16(79)); // O + *pchw++ = msbf(data16(85)); // U + *pchw++ = msbf(data16(193)); // A-acute + *pchw++ = msbf(data16(201)); // E-acute + *pchw++ = msbf(data16(205)); // I-acute + *pchw++ = msbf(data16(211)); // O-acute + *pchw++ = msbf(data16(218)); // U-acute + *pchw++ = msbf(data16(192)); // A-grave + *pchw++ = msbf(data16(200)); // E-grave + *pchw++ = msbf(data16(204)); // I-grave + *pchw++ = msbf(data16(210)); // O-grave + *pchw++ = msbf(data16(217)); // U-grave + + // Input class 3: clsPlainVowel + m_prgichwOffsets[3] = 10 + 15; // 25 + *pchw++ = msbf(data16(5)); + *pchw++ = msbf(data16(4)); *pchw++ = msbf(data16(2)); *pchw++ = msbf(data16(5-4)); + *pchw++ = msbf(data16(97)); *pchw++ = msbf(data16(0)); // a + *pchw++ = msbf(data16(101)); *pchw++ = msbf(data16(1)); // e + *pchw++ = msbf(data16(105)); *pchw++ = msbf(data16(2)); // i + *pchw++ = msbf(data16(111)); *pchw++ = msbf(data16(3)); // o + *pchw++ = msbf(data16(117)); *pchw++ = msbf(data16(4)); // u + + // Input class 4: clsVowel + m_prgichwOffsets[4] = 25 + 4+(5*2); // 39 + *pchw++ = msbf(data16(15)); + *pchw++ = msbf(data16(8)); *pchw++ = msbf(data16(3)); *pchw++ = msbf(data16(15-8)); + *pchw++ = msbf(data16(97)); *pchw++ = msbf(data16(0)); // a + *pchw++ = msbf(data16(101)); *pchw++ = msbf(data16(1)); // e + *pchw++ = msbf(data16(105)); *pchw++ = msbf(data16(2)); // i + *pchw++ = msbf(data16(111)); *pchw++ = msbf(data16(3)); // o + *pchw++ = msbf(data16(117)); *pchw++ = msbf(data16(4)); // u + *pchw++ = msbf(data16(224)); *pchw++ = msbf(data16(10)); // a-grave + *pchw++ = msbf(data16(225)); *pchw++ = msbf(data16(5)); // a-acute + *pchw++ = msbf(data16(232)); *pchw++ = msbf(data16(11)); // e-grave + *pchw++ = msbf(data16(233)); *pchw++ = msbf(data16(6)); // e-acute + *pchw++ = msbf(data16(236)); *pchw++ = msbf(data16(12)); // i-grave + *pchw++ = msbf(data16(237)); *pchw++ = msbf(data16(7)); // i-acute + *pchw++ = msbf(data16(242)); *pchw++ = msbf(data16(13)); // o-grave + *pchw++ = msbf(data16(243)); *pchw++ = msbf(data16(8)); // o-acute + *pchw++ = msbf(data16(249)); *pchw++ = msbf(data16(14)); // u-grave + *pchw++ = msbf(data16(250)); *pchw++ = msbf(data16(9)); // u-acute + + m_prgichwOffsets[5] = 39 + 4+(15*2); // 73 +} + +void GrGlyphTable::SetUpLigature2Test() +{ + SetNumberOfGlyphs(256); + SetNumberOfStyles(1); + + GrGlyphSubTable * pgstbl = new GrGlyphSubTable(); + Assert(pgstbl); + + pgstbl->Initialize(1, 0, 256, 10, 4); + SetSubTable(0, pgstbl); + + SetNumberOfComponents(2); // comp.base, comp.i + + pgstbl->SetUpLigature2Test(); +} + +/*********************************************************************************************** + TODO: This method is BROKEN because m_prgibBIGAttrValues has been changed. It is no + longer a data16 *. The Gloc table can contain 16-bit or 32-bit entries and must be + accessed accordingly. +***********************************************************************************************/ +void GrGlyphSubTable::SetUpLigature2Test() +{ + m_nAttrIDLim = 10; + + m_pgatbl = new GrGlyphAttrTable(); + m_pgatbl->Initialize(0, 330); + + for (int i = 0; i <= 65; i++) // A + m_prgibBIGAttrValues[i] = (byte)msbf(data16(0)); + + for (i = 66; i <= 69; i++) // E + m_prgibBIGAttrValues[i] = (byte)msbf(data16(22)); + + for (i = 70; i <= 73; i++) // I + m_prgibBIGAttrValues[i] = (byte)msbf(data16(44)); + + for (i = 74; i <= 79; i++) // O + m_prgibBIGAttrValues[i] = (byte)msbf(data16(66)); + + for (i = 80; i <= 85; i++) // U + m_prgibBIGAttrValues[i] = (byte)msbf(data16(88)); + + for (i = 86; i <= 192; i++) // A-grave + m_prgibBIGAttrValues[i] = (byte)msbf(data16(110)); + + m_prgibBIGAttrValues[193] = (byte)msbf(data16(132)); // A-acute + + for (i = 194; i <= 200; i++) // E-grave + m_prgibBIGAttrValues[i] = (byte)msbf(data16(154)); + + m_prgibBIGAttrValues[201] = (byte)msbf(data16(176)); // E-acute + + for (i = 202; i <= 204; i++) // I-grave + m_prgibBIGAttrValues[i] = (byte)msbf(data16(198)); + + m_prgibBIGAttrValues[205] = (byte)msbf(data16(220)); // I-acute + + for (i = 206; i <= 210; i++) // O-grave + m_prgibBIGAttrValues[i] = (byte)msbf(data16(242)); + + m_prgibBIGAttrValues[211] = (byte)msbf(data16(264)); // O-acute + + for (i = 212; i <= 217; i++) // U-grave + m_prgibBIGAttrValues[i] = (byte)msbf(data16(286)); + + m_prgibBIGAttrValues[218] = (byte)msbf(data16(308)); // O-acute + + for (i = 219; i < 256; i++) + m_prgibBIGAttrValues[i] = (byte)msbf(data16(330)); + + m_pgatbl->SetUpLigature2Test(); +} + +void GrGlyphAttrTable::SetUpLigature2Test() +{ + // All ligatures have the following defined: + // + // 0 = 2 comp.base (0,30, 100,100) + // 1 = 6 comp.i (0,0, 100,30) + // 2= 100 comp.base.top + // 3= 30 bottom + // 4= 0 left + // 5= 100 right + // 6 = 30 comp.i.top + // 7 = 0 bottom + // 8= 0 left + // 9= 100 right + + byte * pbBIG = m_prgbBIGEntries; + + GrGlyphAttrRun gatrun; + + for (int i = 0; i < 15; i++) + { + gatrun.m_bMinAttrID = 0; + gatrun.m_cAttrs = 10; + gatrun.m_rgchwBIGValues[0] = msbf(data16(2)); + gatrun.m_rgchwBIGValues[1] = msbf(data16(6)); + gatrun.m_rgchwBIGValues[2] = msbf(data16(100)); + gatrun.m_rgchwBIGValues[3] = msbf(data16(30)); + gatrun.m_rgchwBIGValues[4] = msbf(data16(0)); + gatrun.m_rgchwBIGValues[5] = msbf(data16(100)); + gatrun.m_rgchwBIGValues[6] = msbf(data16(30)); + gatrun.m_rgchwBIGValues[7] = msbf(data16(0)); + gatrun.m_rgchwBIGValues[8] = msbf(data16(0)); + gatrun.m_rgchwBIGValues[9] = msbf(data16(100)); + memcpy(pbBIG, &gatrun, 22); // 22 = 10*2 + 2 + pbBIG += 22; + } + + Assert(pbBIG == m_prgbBIGEntries + (22 * 15)); +} + +void GrSubPass::SetUpLigature2Test() +{ + if (m_ipass == 1) + { + m_nMaxRuleContext = m_nMaxChunk = 2; + m_nMaxRuleLoop = 5; + m_staBehavior = "Ligatures2"; + + m_pfsm = new GrFSM(); + Assert(m_pfsm); + m_pfsm->SetUpLigature2Test(m_ipass); + + m_crul = 3; + + // Set up constraint code. + m_prgbConstraintBlock = new byte[1]; + byte * pb = m_prgbConstraintBlock; + *pb++ = kopRetTrue; // RetTrue + + m_prgibConstraintStart = new data16[m_crul]; + m_prgibConstraintStart[0] = 0; + m_prgibConstraintStart[1] = 0; + m_prgibConstraintStart[2] = 0; + + // Set up rule action code. + m_prgbActionBlock = new byte[7 + 12 + 12]; + pb = m_prgbActionBlock; + + // Rule 0: gX gI > @2 @1; + + *pb++ = kopPutCopy; *pb++ = 1; // PutCopy 1 + *pb++ = kopNext; // Next + *pb++ = kopPutCopy; *pb++ = -1; // PutCopy -1 + *pb++ = kopNext; // Next + *pb++ = kopRetZero; // RetZero + + // Rule 1: clsPlainVowel gAcute > clsVowelAcute:(1 2) _; + + *pb++ = kopPutSubs8bitObs; *pb++ = 0; // PutSubs 0 3 0 + *pb++ = 3; *pb++ = 0; + *pb++ = kopAssoc; *pb++ = 2; // Assoc 2 0 1 + *pb++ = 0; *pb++ = 1; + *pb++ = kopNext; // Next + *pb++ = kopDelete; // Delete + *pb++ = kopNext; // Next + *pb++ = kopRetZero; // RetZero + + // Rule 2: clsPlainVowel gGrave > clsVowelGrave:(1 2) _; + + *pb++ = kopPutSubs8bitObs; *pb++ = 0; // PutSubs 0 3 1 + *pb++ = 3; *pb++ = 1; + *pb++ = kopAssoc; *pb++ = 2; // Assoc 2 0 1 + *pb++ = 0; *pb++ = 1; + *pb++ = kopNext; // Next + *pb++ = kopDelete; // Delete + *pb++ = kopNext; // Next + *pb++ = kopRetZero; // RetZero + + m_prgibActionStart = new data16[m_crul]; + m_prgibActionStart[0] = 0; + m_prgibActionStart[1] = 7; + m_prgibActionStart[2] = 7 + 12; + } + else if (m_ipass == 2) + { + m_nMaxRuleContext = m_nMaxChunk = 2; + m_nMaxRuleLoop = 5; + m_staBehavior = "Ligatures2"; + + m_pfsm = new GrFSM(); + Assert(m_pfsm); + m_pfsm->SetUpLigature2Test(m_ipass); + + m_crul = 1; + + // Set up constraint code. + m_prgbConstraintBlock = new byte[1]; + byte * pb = m_prgbConstraintBlock; + *pb++ = kopRetTrue; // RetTrue + + m_prgibConstraintStart = new data16[m_crul]; + m_prgibConstraintStart[0] = 0; + + // Set up rule action code. + m_prgbActionBlock = new byte[7 + 12 + 12]; + pb = m_prgbActionBlock; + + // Rule 0: clsVowel gI > clsCapVowel:(1 2) { comp {v.ref = @1; i.ref = @2}} _; + + *pb++ = kopPutSubs8bitObs; *pb++ = 0; // PutSubs 0 4 2 + *pb++ = 4; *pb++ = 2; + *pb++ = kopAssoc; *pb++ = 2; // Assoc 2 0 1 + *pb++ = 0; *pb++ = 1; + *pb++ = kopPushByte; *pb++ = 0; // Push 0 + *pb++ = kopIAttrSetSlot; // IAttrSetSlot compRef v + *pb++ = kslatCompRef; *pb++ = 0; + *pb++ = kopPushByte; *pb++ = 1; // Push 1 + *pb++ = kopIAttrSetSlot; // IAttrSetSlot compRef i + *pb++ = kslatCompRef; *pb++ = 1; + *pb++ = kopNext; // Next + *pb++ = kopDelete; // Delete + *pb++ = kopNext; // Next + *pb++ = kopRetZero; // RetZero + + m_prgibActionStart = new data16[m_crul]; + m_prgibActionStart[0] = 0; + } + else + Assert(false); +} + + +void GrFSM::SetUpLigature2Test(int ipass) +{ + if (ipass == 1) + { + // Create machine class ranges. + m_cmcr = 8; + m_prgmcr = new GrFSMClassRange[m_cmcr]; + m_prgmcr[0].m_chwFirst = 47; // acute + m_prgmcr[0].m_chwLast = 47; + m_prgmcr[0].m_col = 3; + + m_prgmcr[1].m_chwFirst = 92; // grave + m_prgmcr[1].m_chwLast = 92; + m_prgmcr[1].m_col = 4; + + m_prgmcr[2].m_chwFirst = 97; // a + m_prgmcr[2].m_chwLast = 97; + m_prgmcr[2].m_col = 1; + + m_prgmcr[3].m_chwFirst = 101; // e + m_prgmcr[3].m_chwLast = 101; + m_prgmcr[3].m_col = 1; + + m_prgmcr[4].m_chwFirst = 105; // i + m_prgmcr[4].m_chwLast = 105; + m_prgmcr[4].m_col = 2; + + m_prgmcr[5].m_chwFirst = 111; // o + m_prgmcr[5].m_chwLast = 111; + m_prgmcr[5].m_col = 1; + + m_prgmcr[6].m_chwFirst = 117; // u + m_prgmcr[6].m_chwLast = 117; + m_prgmcr[6].m_col = 1; + + m_prgmcr[7].m_chwFirst = 120; // x + m_prgmcr[7].m_chwLast = 120; + m_prgmcr[7].m_col = 0; + + m_dimcrInit = 8; // (max power of 2 <= m_cmcr); + m_cLoop = 3; // log2(max power of 2 <= m_cmcr); + m_imcrStart = m_cmcr - m_dimcrInit; + + + m_crow = 6; + m_crowNonAcpt = 3; + m_crowFinal = 3; + m_rowFinalMin = m_crow - m_crowFinal; + m_ccol = 5; + + // Set up transition table. + m_prgrowTransitions = new short[(m_crow-m_crowFinal) * m_ccol]; // 15 + short * psn = m_prgrowTransitions; + *psn++ = 1; *psn++ = 2; *psn++ = 2; *psn++ = 0; *psn++ = 0; + *psn++ = 0; *psn++ = 0; *psn++ = 3; *psn++ = 0; *psn++ = 0; + *psn++ = 0; *psn++ = 0; *psn++ = 0; *psn++ = 4; *psn++ = 5; + + // Set up matched-rules tables. + m_prgrulnMatched = new data16[3]; // 3 = sum of rules matched for each accepting state + m_prgirulnMin = new data16[3+1]; // 3 = m_crow - m_crowNonAcpt + + m_prgirulnMin[0] = 0; // s3: r0 + m_prgrulnMatched[0] = 0; + + m_prgirulnMin[1] = 1; // s4: r1 + m_prgrulnMatched[1] = 1; + + m_prgirulnMin[2] = 2; // s5: r2 + m_prgrulnMatched[2] = 2; + + m_prgirulnMin[3] = 3; + } + else if (ipass == 2) + { + // Create machine class ranges. + m_cmcr = 10; + m_prgmcr = new GrFSMClassRange[m_cmcr]; + m_prgmcr[0].m_chwFirst = 97; // a + m_prgmcr[0].m_chwLast = 97; + m_prgmcr[0].m_col = 0; + + m_prgmcr[1].m_chwFirst = 101; // e + m_prgmcr[1].m_chwLast = 101; + m_prgmcr[1].m_col = 0; + + m_prgmcr[2].m_chwFirst = 105; // i + m_prgmcr[2].m_chwLast = 105; + m_prgmcr[2].m_col = 1; + + m_prgmcr[3].m_chwFirst = 111; // o + m_prgmcr[3].m_chwLast = 111; + m_prgmcr[3].m_col = 0; + + m_prgmcr[4].m_chwFirst = 117; // u + m_prgmcr[4].m_chwLast = 117; + m_prgmcr[4].m_col = 0; + + m_prgmcr[5].m_chwFirst = 224; // a-grave, a-acute + m_prgmcr[5].m_chwLast = 225; + m_prgmcr[5].m_col = 0; + + m_prgmcr[6].m_chwFirst = 232; // e-grave, e-acute + m_prgmcr[6].m_chwLast = 233; + m_prgmcr[6].m_col = 0; + + m_prgmcr[7].m_chwFirst = 236; // i-grave, i-acute + m_prgmcr[7].m_chwLast = 237; + m_prgmcr[7].m_col = 0; + + m_prgmcr[8].m_chwFirst = 242; // o-grave, o-acute + m_prgmcr[8].m_chwLast = 243; + m_prgmcr[8].m_col = 0; + + m_prgmcr[9].m_chwFirst = 249; // u-grave, u-acute + m_prgmcr[9].m_chwLast = 250; + m_prgmcr[9].m_col = 0; + + + m_dimcrInit = 8; // (max power of 2 <= m_cmcr); + m_cLoop = 3; // log2(max power of 2 <= m_cmcr); + m_imcrStart = m_cmcr - m_dimcrInit; + + + m_crow = 3; + m_crowNonAcpt = 2; + m_crowFinal = 1; + m_rowFinalMin = m_crow - m_crowFinal; + m_ccol = 2; + + // Set up transition table. + m_prgrowTransitions = new short[(m_crow-m_crowFinal) * m_ccol]; // 4 + short * psn = m_prgrowTransitions; + *psn++ = 1; *psn++ = 1; + *psn++ = 0; *psn++ = 2; + + // Set up matched-rules tables. + m_prgrulnMatched = new data16[1]; // 1 = sum of rules matched for each accepting state + m_prgirulnMin = new data16[1+1]; // 1 = m_crow - m_crowNonAcpt + + m_prgirulnMin[0] = 0; // s2: r0 + m_prgrulnMatched[0] = 0; + + m_prgirulnMin[1] = 1; + } +} + +#endif // _DEBUG + +} // namespace gr + +#endif // OLD_TEST_STUFF + +//:End Ignore diff --git a/Build/source/libs/graphite-engine/src/segment/TestPasses.cpp b/Build/source/libs/graphite-engine/src/segment/TestPasses.cpp new file mode 100644 index 00000000000..a85e6c56207 --- /dev/null +++ b/Build/source/libs/graphite-engine/src/segment/TestPasses.cpp @@ -0,0 +1,1005 @@ +/*--------------------------------------------------------------------*//*:Ignore this sentence. +Copyright (C) 1999, 2001 SIL International. All rights reserved. + +Distributable under the terms of either the Common Public License or the +GNU Lesser General Public License, as specified in the LICENSING.txt file. + +File: TestPasses.cpp +Responsibility: Sharon Correll +Last reviewed: Not yet. + +Description: + Hard-coded passes for test procedures. +-------------------------------------------------------------------------------*//*:End Ignore*/ + +//:Ignore + +//:>******************************************************************************************** +//:> Include files +//:>******************************************************************************************** +#include "Main.h" + +#ifdef _MSC_VER +#pragma hdrstop +#endif +#undef THIS_FILE +DEFINE_THIS_FILE + +#ifdef OLD_TEST_STUFF + +//:>******************************************************************************************** +//:> Forward declarations +//:>******************************************************************************************** + +//:>******************************************************************************************** +//:> Local Constants and static variables +//:>******************************************************************************************** + +namespace gr +{ + +//:>******************************************************************************************** +//:> Methods +//:>******************************************************************************************** + +/*---------------------------------------------------------------------------------------------- + For release version. +----------------------------------------------------------------------------------------------*/ +#ifndef _DEBUG + +bool GrLineBreakPass::RunTestRules(GrTableManager * ptman, + GrSlotStream * psstrmInput, GrSlotStream * psstrmOutput) +{ + return false; +} + +bool GrSubPass::RunTestRules(GrTableManager * ptman, + GrSlotStream * psstrmInput, GrSlotStream * psstrmOutput) +{ + return false; +} + +bool GrPosPass::RunTestRules(GrTableManager * ptman, + GrSlotStream * psstrmInput, GrSlotStream * psstrmOutput) +{ + return false; +} + +#endif // !_DEBUG + + +#ifdef _DEBUG + +/*---------------------------------------------------------------------------------------------- + Set up a table manager for testing. +----------------------------------------------------------------------------------------------*/ +void GrTableManager::SetUpTest(std::wstring stuRendBehavior) +{ + if (stuRendBehavior == L"NoRules") + { + m_cpass = 2; + m_cpassLB = 0; + m_ipassPos1 = 1; + m_fBidi = false; + + m_prgppass = new GrPass*[m_cpass]; + m_prgppass[0] = new GrGlyphGenPass(0); + m_prgppass[1] = new GrPosPass(1); + + m_prgppass[0]->SetUpTestData(); + m_prgppass[1]->SetUpTestData(); + } + else if (stuRendBehavior == L"JustReorder" || stuRendBehavior == "RightToLeftLayout") + { + m_cpass = 4; + m_cpassLB = 0; + m_ipassPos1 = 3; + m_fBidi = true; + + m_prgppass = new GrPass*[m_cpass]; + m_prgppass[0] = new GrGlyphGenPass(0); + m_prgppass[1] = new GrSubPass(1); + m_prgppass[2] = new GrBidiPass(2); + m_prgppass[3] = new GrPosPass(3); + + m_prgppass[0]->SetUpTestData(); + m_prgppass[1]->SetUpReverseNumbersTest(); + m_prgppass[2]->SetUpTestData(); + m_prgppass[3]->SetUpTestData(); + + m_prgppass[2]->SetTopDirLevel(TopDirectionLevel()); + } + else if (stuRendBehavior == L"CrossLineContext") + { + m_cpass = 4; + m_cpassLB = 1; + m_ipassPos1 = 3; + m_fBidi = false; + + m_prgppass = new GrPass*[m_cpass]; + m_prgppass[0] = new GrGlyphGenPass(0); + m_prgppass[1] = new GrLineBreakPass(1); + m_prgppass[2] = new GrSubPass(2); + m_prgppass[3] = new GrPosPass(3); + + m_prgppass[0]->SetUpTestData(); + m_prgppass[1]->SetUpCrossLineContextTest(); + m_prgppass[2]->SetUpCrossLineContextTest(); + m_prgppass[3]->SetUpTestData(); + } + else if (stuRendBehavior == L"Reprocess") + { + m_cpass = 6; + m_cpassLB = 1; + m_ipassPos1 = 5; + m_fBidi = true; + + m_prgppass = new GrPass*[m_cpass]; + m_prgppass[0] = new GrGlyphGenPass(0); + m_prgppass[1] = new GrLineBreakPass(1); + m_prgppass[2] = new GrSubPass(2); + m_prgppass[3] = new GrSubPass(3); + m_prgppass[4] = new GrBidiPass(4); + m_prgppass[5] = new GrPosPass(5); + + m_prgppass[0]->SetUpTestData(); // glyph-gen pass + m_prgppass[1]->SetUpReprocessTest(); // line-break pass + m_prgppass[2]->SetUpReverseNumbersTest(); // sub pass 1 + m_prgppass[3]->SetUpReprocessTest(); // sub pass 2 + m_prgppass[4]->SetUpTestData(); // bidi pass + m_prgppass[5]->SetUpTestData(); // pos pass + + m_prgppass[4]->SetTopDirLevel(TopDirectionLevel()); + } + else if (stuRendBehavior == L"LineEdgeContext") + { + m_cpass = 4; + m_cpassLB = 0; + m_ipassPos1 = 3; + m_fBidi = false; + + m_prgppass = new GrPass*[m_cpass]; + m_prgppass[0] = new GrGlyphGenPass(0); + m_prgppass[1] = new GrSubPass(1); + m_prgppass[2] = new GrSubPass(2); + m_prgppass[3] = new GrPosPass(3); + + m_prgppass[0]->SetUpTestData(); + m_prgppass[1]->SetUpLineEdgeContextTest(1); + m_prgppass[2]->SetUpLineEdgeContextTest(2); + m_prgppass[3]->SetUpTestData(); + } + else if (stuRendBehavior == L"BidiAlgorithm") + { + m_cpass = 4; + m_cpassLB = 0; + m_ipassPos1 = 3; + m_fBidi = true; + + m_prgppass = new GrPass*[m_cpass]; + m_prgppass[0] = new GrGlyphGenPass(0); + m_prgppass[1] = new GrSubPass(1); + m_prgppass[2] = new GrBidiPass(2); + m_prgppass[3] = new GrPosPass(3); + + m_prgppass[0]->SetUpTestData(); + m_prgppass[1]->SetUpBidiAlgorithmTest(); + m_prgppass[2]->SetUpBidiAlgorithmTest(); + m_prgppass[3]->SetUpTestData(); + + m_prgppass[2]->SetTopDirLevel(TopDirectionLevel()); + } + else if (stuRendBehavior == L"PseudoGlyphs") + { + m_cpass = 3; + m_cpassLB = 0; + m_ipassPos1 = 2; + m_fBidi = false; + + m_prgppass = new GrPass*[m_cpass]; + m_prgppass[0] = new GrGlyphGenPass(0); + m_prgppass[1] = new GrSubPass(1); + m_prgppass[2] = new GrPosPass(2); + + m_prgppass[0]->SetUpTestData(); + m_prgppass[1]->SetUpPseudoGlyphsTest(); + m_prgppass[2]->SetUpTestData(); + } + else if (stuRendBehavior == L"SimpleFSM") + { + m_cpass = 3; + m_cpassLB = 0; + m_ipassPos1 = 2; + m_fBidi = false; + + m_prgppass = new GrPass*[m_cpass]; + m_prgppass[0] = new GrGlyphGenPass(0); + m_prgppass[1] = new GrSubPass(1); + m_prgppass[2] = new GrPosPass(2); + + m_prgppass[0]->SetUpTestData(); + m_prgppass[1]->SetUpSimpleFSMTest(); + m_prgppass[2]->SetUpTestData(); + } + else if (stuRendBehavior == L"RuleAction") + { + m_cpass = 3; + m_cpassLB = 0; + m_ipassPos1 = 2; + m_fBidi = false; + + m_prgppass = new GrPass*[m_cpass]; + m_prgppass[0] = new GrGlyphGenPass(0); + m_prgppass[1] = new GrSubPass(1); + m_prgppass[2] = new GrPosPass(2); + + m_prgppass[0]->SetUpTestData(); + m_prgppass[1]->SetUpRuleActionTest(); + m_prgppass[2]->SetUpTestData(); + } + else if (stuRendBehavior == L"RuleAction2") + { + m_cpass = 6; + m_cpassLB = 0; + m_ipassPos1 = 5; + m_fBidi = true; + + m_prgppass = new GrPass*[m_cpass]; + m_prgppass[0] = new GrGlyphGenPass(0); + m_prgppass[1] = new GrSubPass(1); + m_prgppass[2] = new GrSubPass(2); + m_prgppass[3] = new GrSubPass(3); + m_prgppass[4] = new GrBidiPass(4); + m_prgppass[5] = new GrPosPass(5); + + m_prgppass[0]->SetUpTestData(); // glyph-gen pass + m_prgppass[1]->SetUpRuleAction2Test(); // sub pass 1 + m_prgppass[2]->SetUpRuleAction2Test(); // sub pass 2 + m_prgppass[3]->SetUpRuleAction2Test(); // sub pass 3 + m_prgppass[4]->SetUpTestData(); // bidi pass + m_prgppass[5]->SetUpTestData(); // pos pass + + m_prgppass[4]->SetTopDirLevel(TopDirectionLevel()); + } + else if (stuRendBehavior == L"Assoc") + { + m_cpass = 5; + m_cpassLB = 1; + m_ipassPos1 = 4; + m_fBidi = false; + + m_prgppass = new GrPass*[m_cpass]; + m_prgppass[0] = new GrGlyphGenPass(0); + m_prgppass[1] = new GrLineBreakPass(1); + m_prgppass[2] = new GrSubPass(2); + m_prgppass[3] = new GrSubPass(3); + m_prgppass[4] = new GrPosPass(4); + + m_prgppass[0]->SetUpTestData(); + m_prgppass[1]->SetUpAssocTest(); + m_prgppass[2]->SetUpAssocTest(); + m_prgppass[3]->SetUpAssocTest(); + m_prgppass[4]->SetUpTestData(); + } + else if (stuRendBehavior == L"Assoc2") + { + m_cpass = 3; + m_cpassLB = 0; + m_ipassPos1 = 2; + m_fBidi = false; + + m_prgppass = new GrPass*[m_cpass]; + m_prgppass[0] = new GrGlyphGenPass(0); + m_prgppass[1] = new GrSubPass(1); + m_prgppass[2] = new GrPosPass(2); + + m_prgppass[0]->SetUpTestData(); + m_prgppass[1]->SetUpAssoc2Test(); + m_prgppass[2]->SetUpTestData(); + } + else if (stuRendBehavior == L"DefaultAssoc") + { + m_cpass = 3; + m_cpassLB = 0; + m_ipassPos1 = 2; + m_fBidi = false; + + m_prgppass = new GrPass*[m_cpass]; + m_prgppass[0] = new GrGlyphGenPass(0); + m_prgppass[1] = new GrSubPass(1); + m_prgppass[2] = new GrPosPass(2); + + m_prgppass[0]->SetUpTestData(); + m_prgppass[1]->SetUpDefaultAssocTest(); + m_prgppass[2]->SetUpTestData(); + } + else if (stuRendBehavior == L"BidiNumbers") + { + m_cpass = 4; + m_cpassLB = 0; + m_ipassPos1 = 3; + m_fBidi = true; + + m_prgppass = new GrPass*[m_cpass]; + m_prgppass[0] = new GrGlyphGenPass(0); + m_prgppass[1] = new GrSubPass(1); + m_prgppass[2] = new GrBidiPass(2); + m_prgppass[3] = new GrPosPass(3); + + m_prgppass[0]->SetUpTestData(); + m_prgppass[1]->SetUpBidiNumbersTest(); + m_prgppass[2]->SetUpBidiNumbersTest(); + m_prgppass[3]->SetUpTestData(); + + m_prgppass[2]->SetTopDirLevel(TopDirectionLevel()); + } + else if (stuRendBehavior == L"Feature") + { + m_cpass = 3; + m_cpassLB = 0; + m_ipassPos1 = 2; + m_fBidi = false; + + m_prgppass = new GrPass*[m_cpass]; + m_prgppass[0] = new GrGlyphGenPass(0); + m_prgppass[1] = new GrSubPass(1); + m_prgppass[2] = new GrPosPass(2); + + m_prgppass[0]->SetUpTestData(); + m_prgppass[1]->SetUpFeatureTest(); + m_prgppass[2]->SetUpTestData(); + } + else if (stuRendBehavior == L"Ligatures") + { + m_cpass = 4; + m_cpassLB = 1; + m_ipassPos1 = 3; + m_fBidi = false; + + m_prgppass = new GrPass*[m_cpass]; + m_prgppass[0] = new GrGlyphGenPass(0); + m_prgppass[1] = new GrLineBreakPass(1); + m_prgppass[2] = new GrSubPass(2); + m_prgppass[3] = new GrPosPass(3); + + m_prgppass[0]->SetUpTestData(); + m_prgppass[1]->SetUpLigatureTest(); + m_prgppass[2]->SetUpLigatureTest(); + m_prgppass[3]->SetUpTestData(); + } + else if (stuRendBehavior == L"Ligatures2") + { + m_cpass = 4; + m_cpassLB = 1; + m_ipassPos1 = 3; + m_fBidi = false; + + m_prgppass = new GrPass*[m_cpass]; + m_prgppass[0] = new GrGlyphGenPass(0); + m_prgppass[1] = new GrSubPass(1); + m_prgppass[2] = new GrSubPass(2); + m_prgppass[3] = new GrPosPass(3); + + m_prgppass[0]->SetUpTestData(); + m_prgppass[1]->SetUpLigature2Test(); + m_prgppass[2]->SetUpLigature2Test(); + m_prgppass[3]->SetUpTestData(); + } + else + Assert(false); + + m_engst.m_prgpsstrm = new GrSlotStream*[m_cpass]; + for (int ipass = 0; ipass < m_cpass; ++ipass) + m_engst.m_prgpsstrm[ipass] = new GrSlotStream(ipass); +} + + +/*---------------------------------------------------------------------------------------------- + Set the pass variables that normally will be read from the ECF. +----------------------------------------------------------------------------------------------*/ + +void GrPass::SetUpTestData() +{ + m_nMaxRuleContext = m_nMaxChunk = 2; + m_nMaxRuleLoop = 2; +} + +//void GrBidiPass::SetUpTestData() +//{ +// m_nMaxRuleContext = m_nMaxChunk = 1; +// m_nMaxRuleLoop = 2; +//} + +/*---------------------------------------------------------------------------------------------- + Call the appropriate test function. +----------------------------------------------------------------------------------------------*/ + +bool GrLineBreakPass::RunTestRules(GrTableManager * ptman, + GrSlotStream * psstrmInput, GrSlotStream * psstrmOutput) +{ + if (m_staBehavior == "Reprocess") + return RunReprocessTest(ptman, psstrmInput, psstrmOutput); + else if (m_staBehavior == "CrossLineContext") + return RunCrossLineContextTest(ptman, psstrmInput, psstrmOutput); + else + return false; +} + +bool GrSubPass::RunTestRules(GrTableManager * ptman, + GrSlotStream * psstrmInput, GrSlotStream * psstrmOutput) +{ + if (m_staBehavior == "ReverseNumbers") + return RunReverseNumbersTest(ptman, psstrmInput, psstrmOutput); + else if (m_staBehavior == "CrossLineContext") + return RunCrossLineContextTest(ptman, psstrmInput, psstrmOutput); + else if (m_staBehavior == "Reprocess") + return RunReprocessTest(ptman, psstrmInput, psstrmOutput); + else if (m_staBehavior == "LineEdgeContext") + return RunLineEdgeContextTest(ptman, psstrmInput, psstrmOutput); + else if (m_staBehavior == "BidiAlgorithm") + return RunBidiAlgorithmTest(ptman, psstrmInput, psstrmOutput); + else if (m_staBehavior == "PseudoGlyphs") + return RunPseudoGlyphsTest(ptman, psstrmInput, psstrmOutput); + else if (m_staBehavior == "BidiNumbers") + return RunBidiNumbersTest(ptman, psstrmInput, psstrmOutput); + else + return false; +} + +bool GrPosPass::RunTestRules(GrTableManager * ptman, + GrSlotStream * psstrmInput, GrSlotStream * psstrmOutput) +{ + return false; +} + +/*---------------------------------------------------------------------------------------------- + A substitution pass that causes numbers to be reversed. Used by both the JustReorder test + and the Reprocess test. +----------------------------------------------------------------------------------------------*/ + +void GrSubPass::SetUpReverseNumbersTest() +{ + m_nMaxRuleContext = m_nMaxChunk = 1; + m_nMaxRuleLoop = 2; + m_staBehavior = "ReverseNumbers"; +} + +bool GrSubPass::RunReverseNumbersTest(GrTableManager * ptman, + GrSlotStream * psstrmInput, GrSlotStream * psstrmOutput) +{ + data16 chw0; + + // clsDigit { dir = DIR_RIGHT }; + if (psstrmInput->SlotsPending() >= 1) + { + bool fRtl = ptman->RightToLeft(); + chw0 = psstrmInput->Peek(0)->GlyphID(); + if ((chw0 >= '0') && (chw0 <= '9')) + { + GrSlotState * pslot0 = psstrmInput->NextGet(); +// GrFeatureValues fval0; +// pslot0->GetFeatureValues(&fval0); + + GrSlotState * pslotNew; + ptman->NewSlotCopy(pslot0, m_ipass, &pslotNew); + pslotNew->SetDirectionality(fRtl ? kdircArabNum : kdircR); + pslotNew->Associate(pslot0); + psstrmOutput->NextPut(pslotNew); + + return true; + } + else + { + GrSlotState * pslot0 = psstrmInput->NextGet(); +// GrFeatureValues fval0; +// pslot0->GetFeatureValues(&fval0); + + GrSlotState * pslotNew; + ptman->NewSlotCopy(pslot0, m_ipass, &pslotNew); + if (chw0 == 32) + pslotNew->SetDirectionality(kdircWhiteSpace); + else if (chw0 == '.') + pslotNew->SetDirectionality(kdircComSep); + else + pslotNew->SetDirectionality(fRtl ? kdircR : kdircL); + pslotNew->Associate(pslot0); + psstrmOutput->NextPut(pslotNew); + + return true; + } + } + else + return false; +} + +/*---------------------------------------------------------------------------------------------- + A substitution pass that will do some hard-coded actions resulting in cross-line-boundary + contextuals. +----------------------------------------------------------------------------------------------*/ +void GrEngine::SetUpCrossLineContextTest() +{ + m_fLineBreak = true; + m_cchwPreXlbContext = 2; + m_cchwPostXlbContext = 0; +} + +void GrLineBreakPass::SetUpCrossLineContextTest() +{ + m_nMaxRuleContext = m_nMaxChunk = 1; + m_nMaxRuleLoop = 2; + m_staBehavior = "CrossLineContext"; +} + +void GrSubPass::SetUpCrossLineContextTest() +{ + m_nMaxRuleContext = m_nMaxChunk = 3; + m_nMaxRuleLoop = 2; + m_staBehavior = "CrossLineContext"; +} + +bool GrLineBreakPass::RunCrossLineContextTest(GrTableManager * ptman, + GrSlotStream * psstrmInput, GrSlotStream * psstrmOutput) +{ + // '=' { break = intra }; + if ((psstrmInput->SlotsPending() >= 1) && + (psstrmInput->Peek(0)->GlyphID() == 61)) + { + GrSlotState * pslotNew0; + ptman->NewSlotCopy(psstrmInput->NextGet(), m_ipass, &pslotNew0); + pslotNew0->SetBreakWeight(int(klbHyphenBreak)); + psstrmOutput->NextPut(pslotNew0); + + psstrmOutput->SetPosForNextRule(0, psstrmInput, false); + + return true; + } + else + return false; +} + +bool GrSubPass::RunCrossLineContextTest(GrTableManager * ptman, + GrSlotStream * psstrmInput, GrSlotStream * psstrmOutput) +{ + // any '=' _ _ > any '=' '=':2 any:1 / _ _ # _ _ + if ((psstrmInput->SlotsPending() >= 3) && + (psstrmInput->Peek(1)->GlyphID() == 61) && + (psstrmInput->Peek(2)->GlyphID() == ptman->LBGlyphID())) + { + GrSlotState * pslot0 = psstrmInput->Peek(0); + GrSlotState * pslot1 = psstrmInput->Peek(1); + gid16 chw0 = pslot0->GlyphID(); +// GrFeatureValues fval0; +// pslot0->GetFeatureValues(&fval0); +// GrFeatureValues fval1; +// pslot1->GetFeatureValues(&fval1); + + // copy the first 3 slots + psstrmOutput->CopyOneSlotFrom(psstrmInput); + psstrmOutput->CopyOneSlotFrom(psstrmInput); + psstrmOutput->CopyOneSlotFrom(psstrmInput); + + // insert 2 more + GrSlotState * pslot3; + ptman->NewSlot(61, pslot1, NULL, m_ipass, &pslot3); + pslot3->Associate(pslot1); + psstrmOutput->NextPut(pslot3); + + GrSlotState * pslot4; + ptman->NewSlot(chw0, pslot0, NULL, m_ipass, &pslot4); + pslot4->Associate(pslot0); + psstrmOutput->NextPut(pslot4); + + return true; + } + else + return false; +} + +/*---------------------------------------------------------------------------------------------- + Line break and substitution passes that cause intra-pass reprocessing (ie, the ^ mechanism). +----------------------------------------------------------------------------------------------*/ + +void GrLineBreakPass::SetUpReprocessTest() +{ + m_nMaxRuleContext = m_nMaxChunk = 2; + m_nMaxRuleLoop = 2; + m_staBehavior = "Reprocess"; +} + +bool GrLineBreakPass::RunReprocessTest(GrTableManager * ptman, + GrSlotStream * psstrmInput, GrSlotStream * psstrmOutput) +{ + gid16 chw; + + // '^' {lb=2} / _ lc + if ((psstrmInput->SlotsPending() >=2) && + (psstrmInput->Peek(0)->GlyphID() == '^') && + (((chw = psstrmInput->Peek(1)->GlyphID()) >= 'a') && + (chw <= 'z'))) + { + GrSlotState * pslotNew0; + ptman->NewSlotCopy(psstrmInput->NextGet(), m_ipass, &pslotNew0); + pslotNew0->SetBreakWeight(int(klbHyphenBreak)); + psstrmOutput->NextPut(pslotNew0); + + psstrmOutput->SetPosForNextRule(0, psstrmInput, false); + + return true; + } + else + return false; +} + + +void GrSubPass::SetUpReprocessTest() +{ + m_nMaxRuleContext = m_nMaxChunk = 3; + m_nMaxRuleLoop = 2; + m_staBehavior = "Reprocess"; +} + +bool GrSubPass::RunReprocessTest(GrTableManager * ptman, + GrSlotStream * psstrmInput, GrSlotStream * psstrmOutput) +{ + gid16 chw; + gid16 chwLB = ptman->LBGlyphID(); + + // symbol > @2 {dir = DIR_ARABNUMBER} / any {dir != DIR_LEFT} ^ _ + if ((psstrmInput->SlotsPending() >= 2) && + (psstrmInput->Peek(0)->Directionality() != kdircL) && + (((chw = psstrmInput->Peek(1)->GlyphID()) == '@') || + (chw == '@') || (chw == '$') || (chw == '%') || + (chw == '^') || (chw == '&') || (chw == '*'))) + { + psstrmOutput->CopyOneSlotFrom(psstrmInput); + GrSlotState * pslotNew1; + ptman->NewSlotCopy(psstrmInput->NextGet(), m_ipass, &pslotNew1); + pslotNew1->SetDirectionality(kdircArabNum); + psstrmOutput->NextPut(pslotNew1); + + psstrmOutput->SetPosForNextRule(-1, psstrmInput, false); + + return true; + } + + // '^' lc > @1 uc + else if ((psstrmInput->SlotsPending() >= 2) && + (psstrmInput->Peek(0)->GlyphID() == '^') && + (((chw = psstrmInput->Peek(1)->GlyphID()) >= 'a') && + (chw <= 'z'))) + { + psstrmOutput->CopyOneSlotFrom(psstrmInput); + + gid16 chwNew = chw - 'a' + 'A'; + GrSlotState * pslotNew1; + ptman->NewSlotCopy(psstrmInput->NextGet(), m_ipass, &pslotNew1); + pslotNew1->SetGlyphID(chwNew); + psstrmOutput->NextPut(pslotNew1); + + psstrmOutput->SetPosForNextRule(0, psstrmInput, false); + + return true; + } + + // '^' lc > @1 uc / _ # _ + else if ((psstrmInput->SlotsPending() >= 3) && + (psstrmInput->Peek(0)->GlyphID() == '^') && + (psstrmInput->Peek(1)->IsLineBreak(chwLB)) && + (((chw = psstrmInput->Peek(2)->GlyphID()) >= 'a') && + (chw <= 'z'))) + { + psstrmOutput->CopyOneSlotFrom(psstrmInput); + psstrmOutput->CopyOneSlotFrom(psstrmInput); + + gid16 chwNew = chw - 'a' + 'A'; + GrSlotState * pslotNew1; + ptman->NewSlotCopy(psstrmInput->NextGet(), m_ipass, &pslotNew1); + pslotNew1->SetGlyphID(chwNew); + psstrmOutput->NextPut(pslotNew1); + + psstrmOutput->SetPosForNextRule(0, psstrmInput, false); + + return true; + } + + else + return false; +} + + +/*---------------------------------------------------------------------------------------------- + Substitution passes that create contextual forms at the line breaks; also a cross-line- + boundary contextual. +----------------------------------------------------------------------------------------------*/ + +void GrSubPass::SetUpLineEdgeContextTest(int ipass) +{ + m_nMaxRuleContext = (ipass == 2)? 5: 2; + m_nMaxChunk = m_nMaxRuleContext; + m_nMaxRuleLoop = 2; + m_staBehavior = "LineEdgeContext"; +} + +bool GrSubPass::RunLineEdgeContextTest(GrTableManager * ptman, + GrSlotStream * psstrmInput, GrSlotStream * psstrmOutput) +{ + gid16 chwLB = ptman->LBGlyphID(); + + // any _ _ > @1 '-' '-' / _ _ _ # + if ((m_ipass == 1) && + (psstrmInput->SlotsPending() >= 2) && + (psstrmInput->Peek(1)->IsLineBreak(chwLB))) + { + GrSlotState * pslot0 = psstrmInput->Peek(0); +// GrFeatureValues fval; +// pslot0->GetFeatureValues(&fval); + + psstrmOutput->CopyOneSlotFrom(psstrmInput); + + GrSlotState * pslotNew1; + ptman->NewSlot('-', pslot0, NULL, m_ipass, &pslotNew1); + pslotNew1->Associate(pslot0); + psstrmOutput->NextPut(pslotNew1); + + GrSlotState * pslotNew2; + ptman->NewSlot('-', pslot0, NULL, m_ipass, &pslotNew2); + pslotNew2->Associate(pslot0); + psstrmOutput->NextPut(pslotNew2); + + return true; + } + + // _ _ any > '+' '+' @4 / # _ _ _ + else if ((m_ipass == 1) && + (psstrmInput->SlotsPending() >= 2) && + (psstrmInput->Peek(0)->IsLineBreak(chwLB))) + { + GrSlotState * pslot2 = psstrmInput->Peek(1); +// GrFeatureValues fval; +// pslot2->GetFeatureValues(&fval); + + psstrmOutput->CopyOneSlotFrom(psstrmInput); + + GrSlotState * pslotNew0; + ptman->NewSlot('+', pslot2, NULL, m_ipass, &pslotNew0); + pslotNew0->Associate(pslot2); + psstrmOutput->NextPut(pslotNew0); + + GrSlotState * pslotNew1; + ptman->NewSlot('+', pslot2, NULL, m_ipass, &pslotNew1); + pslotNew1->Associate(pslot2); + psstrmOutput->NextPut(pslotNew1); + + psstrmOutput->CopyOneSlotFrom(psstrmInput); + + return true; + } + + // '-' '-' '+' '+' > @1 _ _ @4 / _ _ # _ _ + else if ((m_ipass == 2) && + (psstrmInput->SlotsPending() >= 5) && + (psstrmInput->Peek(0)->GlyphID() == '-') && + (psstrmInput->Peek(1)->GlyphID() == '-') && + (psstrmInput->Peek(2)->IsLineBreak(chwLB)) && + (psstrmInput->Peek(3)->GlyphID() == '+') && + (psstrmInput->Peek(4)->GlyphID() == '+')) + { + psstrmOutput->CopyOneSlotFrom(psstrmInput); + psstrmInput->Skip(1); + psstrmOutput->CopyOneSlotFrom(psstrmInput); + psstrmInput->Skip(1); + psstrmOutput->CopyOneSlotFrom(psstrmInput); + + return true; + } + + else + return false; +} + +/*---------------------------------------------------------------------------------------------- + Substitution passes that do something special with pseudo glyphs. +----------------------------------------------------------------------------------------------*/ + +void GrSubPass::SetUpPseudoGlyphsTest() +{ + m_nMaxRuleContext = m_nMaxChunk = 3; + m_nMaxRuleLoop = 2; + m_staBehavior = "PseudoGlyphs"; +} + +bool GrSubPass::RunPseudoGlyphsTest(GrTableManager * ptman, + GrSlotStream * psstrmInput, GrSlotStream * psstrmOutput) +{ + gid16 chwLB = ptman->LBGlyphID(); + + // pseudo > uppercase / _ ' ' + if ((psstrmInput->SlotsPending() >= 2) && + (psstrmInput->Peek(0)->GlyphID() > 1000) && // pseudo + (psstrmInput->Peek(1)->GlyphID() == 32)) + { + GrSlotState * pslot0 = psstrmInput->NextGet(); +// GrFeatureValues fval; +// pslot0->GetFeatureValues(&fval); + + GrSlotState * pslotNew; + ptman->NewSlotCopy(pslot0, m_ipass, &pslotNew); + pslotNew->SetGlyphID(pslot0->GlyphID() - 1000); + pslotNew->Associate(pslot0); + psstrmOutput->NextPut(pslotNew); + + psstrmOutput->SetPosForNextRule(0, psstrmInput, false); + + return true; + } + + // pseudo > uppercase / _ | + // pseudo > uppercase / _ # + else if ((psstrmInput->SlotsPending() <= 2) && + (psstrmInput->Peek(0)->GlyphID() > 1000) && // pseudo + (psstrmInput->SlotsPending() < 2 || psstrmInput->Peek(1)->GlyphID() == 35)) + { + GrSlotState * pslot0 = psstrmInput->NextGet(); +// GrFeatureValues fval; +// pslot0->GetFeatureValues(&fval); + + GrSlotState * pslotNew; + ptman->NewSlotCopy(pslot0, m_ipass, &pslotNew); + pslotNew->SetGlyphID(pslot0->GlyphID() - 1000); + pslotNew->Associate(pslot0); + psstrmOutput->NextPut(pslotNew); + + psstrmOutput->SetPosForNextRule(0, psstrmInput, false); + + return true; + } + + // pseudo any _ > lowercase:1 @2 @1 / _ _ ^ _; + else if ((psstrmInput->SlotsPending() >= 3) && + (psstrmInput->Peek(0)->GlyphID() > 1000)) // pseudo + { + GrSlotState * pslot0 = psstrmInput->NextGet(); +// GrFeatureValues fval; +// pslot0->GetFeatureValues(&fval); + + GrSlotState * pslotNew0; + ptman->NewSlot(pslot0->GlyphID()-1000-65+97, pslot0, NULL, m_ipass, &pslotNew0); + pslotNew0->Associate(pslot0); + psstrmOutput->NextPut(pslotNew0); + + psstrmOutput->CopyOneSlotFrom(psstrmInput); + + GrSlotState * pslotNew2; + ptman->NewSlot(pslot0->GlyphID(), pslot0, NULL, m_ipass, &pslotNew2); + pslotNew2->Associate(pslot0); + psstrmOutput->NextPut(pslotNew2); + + psstrmOutput->SetPosForNextRule(-1, psstrmInput, false); + + return true; + } + else + return false; +} + +/*---------------------------------------------------------------------------------------------- + Substitution pass that tests reversing numbers. +----------------------------------------------------------------------------------------------*/ +void GrSubPass::SetUpBidiNumbersTest() +{ + m_nMaxRuleContext = m_nMaxChunk = 1; + m_nMaxRuleLoop = 2; + m_staBehavior = "BidiNumbers"; +} + +bool GrSubPass::RunBidiNumbersTest(GrTableManager * ptman, + GrSlotStream * psstrmInput, GrSlotStream * psstrmOutput) +{ + gid16 chw0; + + // clsDigit > clsDigit { dir = DIR_ARABNUMBER }; + if ((psstrmInput->SlotsPending() >= 1) && + (((chw0 = psstrmInput->Peek(0)->GlyphID()) >= '0' && chw0 <= '9') || + (chw0 >= 'A' && chw0 <= 'Z') || (chw0 >= 'a' && chw0 <= 'z'))) + { + GrSlotState * pslot0 = psstrmInput->NextGet(); +// GrFeatureValues fval0; +// pslot0->GetFeatureValues(&fval0); + + GrSlotState * pslotNew; + ptman->NewSlot(chw0, pslot0, NULL, m_ipass, &pslotNew); + if (chw0 >= '0' && chw0 <= '9') + pslotNew->SetDirectionality(kdircArabNum); + else // a-z and A-Z + pslotNew->SetDirectionality(kdircR); + pslotNew->Associate(pslot0); + psstrmOutput->NextPut(pslotNew); + + return true; + } + else + return false; +} + +void GrBidiPass::SetUpBidiNumbersTest() +{ + m_nMaxRuleContext = m_nMaxChunk = 1; + m_nMaxRuleLoop = 2; + m_nTopDirection = 1; +} + +/*---------------------------------------------------------------------------------------------- + Substitution pass that tests the bidi algorithm thoroughly. +----------------------------------------------------------------------------------------------*/ +void GrSubPass::SetUpBidiAlgorithmTest() +{ + m_nMaxRuleContext = m_nMaxChunk = 1; + m_nMaxRuleLoop = 2; + m_staBehavior = "BidiAlgorithm"; +} + +bool GrSubPass::RunBidiAlgorithmTest(GrTableManager * ptman, + GrSlotStream * psstrmInput, GrSlotStream * psstrmOutput) +{ + gid16 chw0; + + if (psstrmInput->SlotsPending() >= 1) + { + chw0 = psstrmInput->Peek(0)->GlyphID(); + + DirCode dircNew; + + if (chw0 >= '0' && chw0 <= '9') + dircNew = kdircEuroNum; + else if (chw0 >= 'a' && chw0 <= 'z') + dircNew = kdircL; + else if (chw0 >= 'A' && chw0 <= 'M') + dircNew = kdircR; + else if (chw0 >= 'N' && chw0 <= 'T') + dircNew = kdircRArab; + else if (chw0 >= 'U' && chw0 <= 'Z') + dircNew = kdircArabNum; + else if (chw0 == '$' || chw0 == '%') + dircNew = kdircEuroTerm; + else if (chw0 == '.' || chw0 == '*' || chw0 == '+') + dircNew = kdircEuroSep; + else if (chw0 == '@') + dircNew = kdircComSep; + else if (chw0 == '_' || chw0 == '=') + dircNew = kdircNeutral; + else if (chw0 == ':') + dircNew = kdircNSM; + else + return false; + + GrSlotState * pslot0 = psstrmInput->NextGet(); +// GrFeatureValues fval0; +// pslot0->GetFeatureValues(&fval0); + + GrSlotState * pslotNew; + ptman->NewSlot(chw0, pslot0, NULL, m_ipass, &pslotNew); + pslotNew->SetDirectionality(dircNew); + pslotNew->Associate(pslot0); + psstrmOutput->NextPut(pslotNew); + + return true; + } + else + return false; +} + +void GrBidiPass::SetUpBidiAlgorithmTest() +{ + m_nMaxRuleContext = m_nMaxChunk = 1; + m_nMaxRuleLoop = 2; + m_nTopDirection = 0; +} + + +#endif // _DEBUG + +} // namespace gr + +#endif // OLD_TEST_STUFF + +//:End Ignore diff --git a/Build/source/libs/graphite-engine/src/segment/TransductionLog.cpp b/Build/source/libs/graphite-engine/src/segment/TransductionLog.cpp new file mode 100644 index 00000000000..658b2ec1044 --- /dev/null +++ b/Build/source/libs/graphite-engine/src/segment/TransductionLog.cpp @@ -0,0 +1,2102 @@ +/*--------------------------------------------------------------------*//*:Ignore this sentence. +Copyright (C) 2000, 2001 SIL International. All rights reserved. + +Distributable under the terms of either the Common Public License or the +GNU Lesser General Public License, as specified in the LICENSING.txt file. + +File: TransductionLog.cpp +Responsibility: Sharon Correll +Last reviewed: Not yet. + +Description: + Contains the functions for writing a log of the transduction process. +----------------------------------------------------------------------------------------------*/ + +//:>******************************************************************************************** +//:> Include files +//:>******************************************************************************************** +#include "Main.h" + +#ifdef _MSC_VER +#pragma hdrstop +#endif + +#include <math.h> + +#undef THIS_FILE +DEFINE_THIS_FILE + +//:End Ignore + +//:>******************************************************************************************** +//:> Forward declarations +//:>******************************************************************************************** + +//:>******************************************************************************************** +//:> Local Constants and static variables +//:>******************************************************************************************** + +namespace gr +{ + +//:>******************************************************************************************** +//:> Methods +//:>******************************************************************************************** + +#define SP_PER_SLOT 7 +#define LEADING_SP 15 +#define MAX_SLOTS 128 + +/*---------------------------------------------------------------------------------------------- + Output a file showing a log of the transduction process and the resulting segment. +----------------------------------------------------------------------------------------------*/ +//bool GrTableManager::WriteTransductionLog(GrCharStream * pchstrm, Segment * psegRet, +// int cbPrevSegDat, byte * pbPrevSegDat, byte * pbNextSegDat, int * pcbNextSegDat) +//{ +//#ifdef TRACING +// std::string staFile; +// if (!LogFileName(staFile)) +// return false; +// +// std::ofstream strmOut; +// if (cbPrevSegDat > 0) +// strmOut.open(staFile.c_str(), std::ios::app); // append +// else +// strmOut.open(staFile.c_str()); +// if (strmOut.fail()) +// return false; +// +// WriteXductnLog(strmOut, pchstrm, psegRet, cbPrevSegDat, pbPrevSegDat); +// +// strmOut.close(); +// return true; +//#else +// return false; +//#endif // TRACING +//} + +bool GrTableManager::WriteTransductionLog(std::ostream * pstrmLog, + GrCharStream * pchstrm, Segment * psegRet, int cbPrevSegDat, byte * pbPrevSegDat) +{ +#ifdef TRACING + if (!pstrmLog) + return false; + std::ostream & strmOut = *pstrmLog; + WriteXductnLog(strmOut, pchstrm, psegRet, cbPrevSegDat, pbPrevSegDat); + return true; +#else + return false; +#endif // TRACING +} + +/*---------------------------------------------------------------------------------------------- + Append to the file, showing the surface/underlying mappings. This must be done after + figuring out all the associations. + + Also write the final glyph positions, since they are now determined. +----------------------------------------------------------------------------------------------*/ +//bool GrTableManager::WriteAssociationLog(GrCharStream * pchstrm, Segment * psegRet) +//{ +//#ifdef TRACING +// std::string staFile; +// if (!LogFileName(staFile)) +// return false; +// +// std::ofstream strmOut; +// strmOut.open(staFile.c_str(), std::ios::app); // append +// if (strmOut.fail()) +// return false; +// +// LogFinalPositions(strmOut); +// +// strmOut << "\n\n- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -\n\n"; +// +// psegRet->LogUnderlyingToSurface(this, strmOut, pchstrm); +// psegRet->LogSurfaceToUnderlying(this, strmOut); +// +// strmOut << "\n\n=======================================================================\n\n"; +// +// strmOut.close(); +// return true; +//#else +// return false; +//#endif // TRACING +//} + + +bool GrTableManager::WriteAssociationLog(std::ostream * pstrmLog, + GrCharStream * pchstrm, Segment * psegRet) +{ +#ifdef TRACING + if (!pstrmLog) + return false; + + std::ostream & strmOut = *pstrmLog; + LogFinalPositions(strmOut); + + strmOut << "\n\n- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -\n\n"; + + psegRet->LogUnderlyingToSurface(this, strmOut, pchstrm); + psegRet->LogSurfaceToUnderlying(this, strmOut); + + strmOut << "\n\n=======================================================================\n\n"; + + return true; +#else + return false; +#endif // TRACING +} + +#ifdef TRACING + +/*---------------------------------------------------------------------------------------------- + Generate the name of the file where the log will be written. It goes in the directory + defined by either the TEMP or TMP environment variable. + + TODO: remove +----------------------------------------------------------------------------------------------*/ +//bool GrTableManager::LogFileName(std::string & staFile) +//{ +// char * pchTmpEnv = getenv("TEMP"); +// if (pchTmpEnv == 0) +// pchTmpEnv = getenv("TMP"); +// if (pchTmpEnv == 0) +// return false; +// +// staFile.assign(pchTmpEnv); +// if (staFile[staFile.size() - 1] != '\\') +// staFile.append("\\"); +// staFile.append("gr_xductn.log"); +// +// return true; +//} + +/*---------------------------------------------------------------------------------------------- + Output a file showing a log of the transduction process and the resulting segment. +----------------------------------------------------------------------------------------------*/ +void GrTableManager::WriteXductnLog(std::ostream & strmOut, + GrCharStream * pchstrm, Segment * psegRet, + int cbPrevSegDat, byte * pbPrevSegDat) +{ + if (cbPrevSegDat == 0) + LogUnderlying(strmOut, pchstrm, 0); + else + { + Assert(*(pbPrevSegDat + 4) == 0); // skip offset for first pass + LogUnderlying(strmOut, pchstrm, *(pbPrevSegDat + 3)); + } + + LogPass1Input(strmOut); + + for (int ipass = 1; ipass < m_cpass; ipass++) + { + if (cbPrevSegDat == 0) + LogPassOutput(strmOut, ipass, 0); + else + LogPassOutput(strmOut, ipass, *(pbPrevSegDat + 4 + ipass)); + } + + // Do this later, after outputting final positions: + //strmOut << "\n\n- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -\n\n"; +} + +/*---------------------------------------------------------------------------------------------- + Write out a log of the underlying input. +----------------------------------------------------------------------------------------------*/ +void GrTableManager::LogUnderlying(std::ostream & strmOut, GrCharStream * pchstrm, + int cchwBackup) +{ + strmOut << "UNDERLYING INPUT\n\n"; + + int rgnChars[MAX_SLOTS]; + bool rgfNewRun[MAX_SLOTS]; + std::fill_n(rgfNewRun, MAX_SLOTS, false); + GrFeatureValues rgfval[MAX_SLOTS]; + int cchwMaxRawChars; + + int cnUtf32 = pchstrm->GetLogData(this, rgnChars, rgfNewRun, rgfval, + cchwBackup, &cchwMaxRawChars); + cnUtf32 = min(cnUtf32, MAX_SLOTS); + + int ichw; + + // For UTF-8 and surrogate representation: + utf16 rgchwChars2[MAX_SLOTS]; + utf16 rgchwChars3[MAX_SLOTS]; + utf16 rgchwChars4[MAX_SLOTS]; + utf16 rgchwChars5[MAX_SLOTS]; + utf16 rgchwChars6[MAX_SLOTS]; + int rgcchwRaw[MAX_SLOTS]; + + if (cchwMaxRawChars > 1) + { + cchwMaxRawChars = min(cchwMaxRawChars, 6); // max of 6 raw (UTF-8 or UTF-16) chars per slot + pchstrm->GetLogDataRaw(this, cnUtf32, cchwBackup, cchwMaxRawChars, + rgnChars, rgchwChars2, rgchwChars3, rgchwChars4, rgchwChars5, rgchwChars6, + rgcchwRaw); + } + else + { + for (ichw = 0; ichw < cnUtf32; ichw++) + { + rgcchwRaw[ichw] = 1; + rgchwChars2[ichw] = 0; + rgchwChars3[ichw] = 0; + rgchwChars4[ichw] = 0; + rgchwChars5[ichw] = 0; + rgchwChars6[ichw] = 0; + } + } + + LogUnderlyingHeader(strmOut, pchstrm->Min(), (pchstrm->Min() + cnUtf32 - cchwBackup), + cchwBackup, rgcchwRaw); + + // Text + strmOut << "Text: "; // 15 spaces + for (ichw = 0; ichw < cnUtf32; ichw++) + { + if (rgnChars[ichw] < 0x0100 && rgchwChars2[ichw] == 0) // ANSI + strmOut << (char)rgnChars[ichw] << " "; // 6 spaces + else if (rgnChars[ichw] == knLRM) + strmOut << "<LRM> "; + else if (rgnChars[ichw] == knRLM) + strmOut << "<RLM> "; + else if (rgnChars[ichw] == knLRO) + strmOut << "<LRO> "; + else if (rgnChars[ichw] == knRLO) + strmOut << "<RLO> "; + else if (rgnChars[ichw] == knLRE) + strmOut << "<LRE> "; + else if (rgnChars[ichw] == knRLE) + strmOut << "<RLE> "; + else if (rgnChars[ichw] == knPDF) + strmOut << "<PDF> "; + else + strmOut << " "; + } + strmOut << "\n"; + + // Unicode + strmOut << "Unicode: "; + for (ichw = 0; ichw < cnUtf32; ichw++) + LogHexInTable(strmOut, utf16(rgnChars[ichw])); + strmOut << "\n"; + for (int icchRaw = 2; icchRaw <= cchwMaxRawChars; icchRaw++) + { + strmOut << " "; + for (ichw = 0; ichw < cnUtf32; ichw++) + { + utf16 chw; + switch (icchRaw) + { + case 2: chw = rgchwChars2[ichw]; break; + case 3: chw = rgchwChars3[ichw]; break; + case 4: chw = rgchwChars4[ichw]; break; + case 5: chw = rgchwChars5[ichw]; break; + case 6: chw = rgchwChars6[ichw]; break; + default: chw = 0; + } + if (chw == 0) + strmOut << " "; + else + LogHexInTable(strmOut, chw); + } + strmOut << "\n"; + } + + // Runs + strmOut << "Runs: "; + int crun = 0; + for (ichw = 0; ichw < cnUtf32; ichw++) + { + if (rgfNewRun[ichw]) + { + crun++; + strmOut << "|" << crun << ((crun < 10) ? " " : " "); + } + else + strmOut << " "; + } + strmOut << "\n"; + + // Features + strmOut << "Features and character properties:\n"; + crun = 0; + for (ichw = 0; ichw < cnUtf32; ichw++) + { + if (rgfNewRun[ichw]) + { + crun++; + strmOut << " Run " << crun << ": "; + rgfval[ichw].WriteXductnLog(this, strmOut); + + } + } +} + + +void GrFeatureValues::WriteXductnLog(GrTableManager * ptman, std::ostream & strmOut) +{ + bool fFirst = true; + for (int i = 0; i < kMaxFeatures; i++) + { + if (m_rgnFValues[i] != 0) + { + GrFeature * pfeat = ptman->Feature(i); + if (!fFirst) + strmOut << ","; + strmOut << pfeat->ID() << "=" << m_rgnFValues[i]; + fFirst = false; + } + } + if (fFirst) + strmOut << "all features=0"; + + strmOut << "\n\n"; +} + +/*---------------------------------------------------------------------------------------------- + Loop through the input stream of underlying characters, storing information in the + arrays for the log. + ENHANCE SharonC: do we need to show the underline properties? +----------------------------------------------------------------------------------------------*/ +int GrCharStream::GetLogData(GrTableManager * ptman, int * rgchl, bool * rgfNewRun, + GrFeatureValues * rgfval, int cchrBackup, int * pcchrMax) +{ + Assert(cchrBackup <= m_cchrBackedUp); + + int ichrPosToStop = m_ichrPos; + int ichrStart = m_ichrMin - max(cchrBackup, m_cchrBackedUp); + + *pcchrMax = 0; + + // Restart the stream. + m_ichrPos = ichrStart; + m_ichrRunMin = 0; + m_ichrRunLim = 0; + m_ichrRunOffset = kPosInfinity; + m_vislotNextChunkMap.clear(); + + int cchrSkipBackup = m_cchrBackedUp - cchrBackup; + + int c = 0; + int cchr = 0; + + while (m_ichrPos < ichrPosToStop) + { + if ((c < MAX_SLOTS) && (m_ichrPos >= m_ichrRunLim)) + { + rgfNewRun[c] = true; + } + + GrFeatureValues fval; + int ichrOffset, cchrThis; + int chl = NextGet(ptman, &fval, &ichrOffset, &cchrThis); + cchr += cchrThis; + if (cchr <= cchrSkipBackup) + {} // ignore - this is before the pre-segment stuff that we want to skip + else if (c < MAX_SLOTS) + { + rgchl[c] = chl; + *pcchrMax = max(*pcchrMax, cchrThis); + if (rgfNewRun[c]) + { + rgfval[c] = fval; + } + c++; + } + } + + ////return (m_ichwPos - (m_ichwMin - cchwBackup)); + return c; +} + +/*---------------------------------------------------------------------------------------------- + If any of the characters are comprised of surrogates or UTF-8, fill in the arrays with + the raw (UTF-16 or UTF-8) chars for display. To do this we get the raw characters + directly from the text source. +----------------------------------------------------------------------------------------------*/ +void GrCharStream::GetLogDataRaw(GrTableManager * ptman, int cchl, int cchrBackup, + int cchrMaxRaw, int * prgchl, + utf16 * prgchw2, utf16 * prgchw3, utf16 * prgchw4, utf16 * prgchw5, utf16 * prgchw6, + int * prgcchr) +{ + for (int i = 0; i < cchl; i++) + { + prgchw2[i] = 0; + } + + int ichrLim = m_ichrPos; + int ichrMin = m_ichrMin - cchrBackup; + + int cchrRange = ichrLim - ichrMin; + int ichr; + utf16 * prgchwRunText = new utf16[cchrRange]; + utf8 * prgchsRunText8 = NULL; + + UtfType utf = m_pgts->utfEncodingForm(); + + switch (utf) + { + case kutf8: + prgchsRunText8 = new utf8[cchrRange]; + m_pgts->fetch(ichrMin, cchrRange, prgchsRunText8); + for (int ichr = 0; ichr < cchrRange; ichr++) + prgchwRunText[ichr] = (utf16)prgchsRunText8[ichr]; // zero-extend into UTF-16 buffer + break; + case kutf16: + m_pgts->fetch(ichrMin, cchrRange, prgchwRunText); + break; + default: + case kutf32: // this method should never have been called + Assert(false); + for (int ichrLp = 0; ichrLp < cchrRange; ichrLp++) + { + prgcchr[ichrLp] = 1; + prgchw2[ichrLp] = 0; + prgchw3[ichrLp] = 0; + prgchw4[ichrLp] = 0; + prgchw5[ichrLp] = 0; + prgchw6[ichrLp] = 0; + } + return; + } + + ichr = ichrMin; + int ichl = 0; + while (ichr < ichrLim) + { + int cchrThis = 1; + // Replace 32-bit char with (first) 16-bit or 8-bit char. + prgchl[ichl] = (int)prgchwRunText[ichr - ichrMin]; + prgcchr[ichr] = cchrThis; + ichr++; + + while (!AtUnicodeCharBoundary(prgchwRunText, cchrRange, ichr - ichrMin, utf)) + { + cchrThis++; + switch (cchrThis) + { + case 2: prgchw2[ichl] = prgchwRunText[ichr - ichrMin]; break; + case 3: prgchw3[ichl] = prgchwRunText[ichr - ichrMin]; break; + case 4: prgchw4[ichl] = prgchwRunText[ichr - ichrMin]; break; + case 5: prgchw5[ichl] = prgchwRunText[ichr - ichrMin]; break; + case 6: prgchw6[ichl] = prgchwRunText[ichr - ichrMin]; break; + default: + break; + } + prgcchr[ichr] = cchrThis; + ichr++; + } + switch (cchrThis) + { + case 1: + prgchw2[ichl] = 0; + // fall through + case 2: + prgchw3[ichl] = 0; + // fall through + case 3: + prgchw4[ichl] = 0; + // fall through + case 4: + prgchw5[ichl] = 0; + // fall through + case 5: + prgchw6[ichl] = 0; + } + ichl++; + } + + delete[] prgchwRunText; + delete[] prgchsRunText8; +} + +/*---------------------------------------------------------------------------------------------- + Output the glyph IDs generated by the glyph generation pass (pass 0). +----------------------------------------------------------------------------------------------*/ +void GrTableManager::LogPass1Input(std::ostream & strmOut) +{ + strmOut << "INPUT TO PASS 1\n\n"; + + GrSlotStream * psstrm = OutputStream(0); + + LogSlotHeader(strmOut, psstrm->WritePos(), SP_PER_SLOT, LEADING_SP); + + LogSlotGlyphs(strmOut, psstrm); + + strmOut << "\n"; +} + +/*---------------------------------------------------------------------------------------------- + Output the the results of pass. +----------------------------------------------------------------------------------------------*/ +void GrTableManager::LogPassOutput(std::ostream & strmOut, int ipass, int cslotSkipped) +{ + strmOut << "\n"; + + GrPass * ppass = Pass(ipass); + GrSlotStream * psstrmIn = InputStream(ipass); + GrSlotStream * psstrmOut = OutputStream(ipass); + + int islot; + // Mark each slot with its index in the input and output streams. + for (islot = 0; islot < psstrmIn->ReadPos(); islot++) + psstrmIn->SlotAt(islot)->m_islotTmpIn = islot; + + for (islot = 0; islot < psstrmOut->WritePos(); islot++) + psstrmOut->SlotAt(islot)->m_islotTmpOut = islot; + + if (!dynamic_cast<GrBidiPass *>(ppass)) + ppass->LogRulesFiredAndFailed(strmOut, psstrmIn); + + strmOut << "\nOUTPUT OF PASS " << ipass; + if (dynamic_cast<GrBidiPass *>(ppass)) + strmOut << " (bidi)"; + else if (dynamic_cast<GrSubPass *>(ppass)) + { + if (ipass >= m_ipassJust1) + strmOut << " (justification)"; + else + strmOut << " (substitution)"; + } + else if (dynamic_cast<GrPosPass *>(ppass)) + strmOut << " (positioning)"; + else if (dynamic_cast<GrLineBreakPass *>(ppass)) + strmOut << " (linebreak)"; + strmOut << "\n"; + + ppass->LogInsertionsAndDeletions(strmOut, psstrmOut); + + LogSlotHeader(strmOut, psstrmOut->WritePos(), SP_PER_SLOT, LEADING_SP); + + LogSlotGlyphs(strmOut, psstrmOut); + + bool fAnyPseudos = false; + if (dynamic_cast<GrPosPass *>(ppass)) + { + for (islot = 0; islot < psstrmOut->WritePos(); islot++) + { + GrSlotState * pslotTmp = psstrmOut->SlotAt(islot); + if (pslotTmp->GlyphID() != pslotTmp->ActualGlyphForOutput(this)) + { + fAnyPseudos = true; + break; + } + } + } + if (fAnyPseudos) + { + strmOut << "Actual glyphs: "; + for (islot = 0; islot < psstrmOut->WritePos(); islot++) + { + GrSlotState * pslotTmp = psstrmOut->SlotAt(islot); + if (pslotTmp->GlyphID() != pslotTmp->ActualGlyphForOutput(this)) + LogHexInTable(strmOut, pslotTmp->ActualGlyphForOutput(this)); + else + strmOut << " "; + } + strmOut << "\n"; + } + + LogAttributes(strmOut, ipass); + + // Do this later, after we're sure the positions have been set: + //if (ipass == m_cpass - 1) + // LogFinalPositions(strmOut); + + if (cslotSkipped > 0) + { + strmOut << "\n "; + for (islot = 0; islot < cslotSkipped; islot++) + strmOut << "SKIP "; + strmOut << "\n"; + } + + // If this was the pass just before the justification routines get run, output a + // special line that just shows the results, ie, the values of justify.width. + // TODO: adjust this when measure mode get functioning. + if (ipass == m_ipassJust1 - 1 && ShouldLogJustification() + && m_engst.m_jmodi == kjmodiJustify) + { + strmOut << "\nJUSTIFICATION\n\n"; + LogSlotHeader(strmOut, psstrmOut->WritePos(), SP_PER_SLOT, LEADING_SP); + LogSlotGlyphs(strmOut, psstrmOut); + LogAttributes(strmOut, ipass, true); + } +} + +/*---------------------------------------------------------------------------------------------- + Write the list of rules fired and failed for a given pass. +----------------------------------------------------------------------------------------------*/ +void GrPass::LogRulesFiredAndFailed(std::ostream & strmOut, GrSlotStream * psstrmIn) +{ + m_pzpst->LogRulesFiredAndFailed(strmOut, psstrmIn); +} + +void PassState::LogRulesFiredAndFailed(std::ostream & strmOut, GrSlotStream * psstrmIn) +{ + + strmOut << "PASS " << m_ipass << "\n\n" << "Rules matched: "; + if (m_crulrec == 0) + strmOut << "none"; + strmOut << "\n"; + + for (int irulrec = 0; irulrec < m_crulrec; irulrec++) + { + if (m_rgrulrec[irulrec].m_fFired) + strmOut << " * "; + else + strmOut << " "; + + strmOut << m_rgrulrec[irulrec].m_islot << ". "; + + if (m_rgrulrec[irulrec].m_irul == PassState::kHitMaxRuleLoop) + strmOut << "hit MaxRuleLoop\n"; + else if (m_rgrulrec[irulrec].m_irul == PassState::kHitMaxBackup) + strmOut << "hit MaxBackup\n"; + else + { + strmOut << "rule " << m_ipass << "." << m_rgrulrec[irulrec].m_irul; + + if (m_rgrulrec[irulrec].m_fFired) + strmOut << " FIRED\n"; + else + strmOut << " failed\n"; + } + } +} + +/*---------------------------------------------------------------------------------------------- + Write out the marks on top of the slot table indicating insertions and deletions. +----------------------------------------------------------------------------------------------*/ +void GrPass::LogInsertionsAndDeletions(std::ostream & strmOut, GrSlotStream * psstrmOut) +{ + m_pzpst->LogInsertionsAndDeletions(strmOut, psstrmOut); +} + +void PassState::LogInsertionsAndDeletions(std::ostream & strmOut, GrSlotStream * psstrmOut) +{ + int cslotDel = m_rgcslotDeletions[0]; + bool fIns; + if (cslotDel > 1) + strmOut << "\n DEL-" << cslotDel; + else if (cslotDel == 1) + strmOut << "\n DEL "; + else + strmOut << "\n "; + + for (int islot = 0; islot < psstrmOut->WritePos(); islot++) + { + cslotDel = (islot + 1 >= MAX_SLOTS) ? 0 : m_rgcslotDeletions[islot + 1]; + fIns = (islot >= MAX_SLOTS) ? 0 : m_rgfInsertion[islot]; + + if (fIns) + { + strmOut << "INS"; + if (cslotDel > 0) + { + if (cslotDel > 1) + strmOut << "/D-" << cslotDel; + else + strmOut << "/DEL"; + } + else + strmOut << " "; + + } + else if (cslotDel > 0) + { + if (cslotDel >= 10) + strmOut << "DEL-" << cslotDel << " "; + else if (cslotDel > 1) + strmOut << " DEL-" << cslotDel << " "; + else + strmOut << " DEL "; + } + else + strmOut << " "; + } + + strmOut << "\n"; +} + +/*---------------------------------------------------------------------------------------------- + Write out a line for each slot attribute that changed during the pass. + + @param fJustWidths - a special pass that writes out just the effects of the justification + routines, ie, the values of justify.width +----------------------------------------------------------------------------------------------*/ +void GrTableManager::LogAttributes(std::ostream & strmOut, int ipass, + bool fJustWidths) +{ + // Figure out which slot attributes were modified for some slot during the pass. + bool * prgfMods = new bool[kslatMax + NumUserDefn() - 1]; + + bool fPreJust = (!fJustWidths && ipass == m_ipassJust1 - 1 && ShouldLogJustification()); + bool fPostJust = (fJustWidths || ipass == m_ipassJust1 && ShouldLogJustification()); + + int ccomp; // max number of components per slot + int cassoc; // max number of associations per slot + SlotAttrsModified(ipass, prgfMods, fPreJust, &ccomp, &cassoc); + + if (fPreJust) + { + // prgfMods[kslatJStretch] = true; + // prgfMods[kslatJShrink] = true; + // prgfMods[kslatJStep] = true; + // prgfMods[kslatWeight] = true; + prgfMods[kslatJWidth] = false; // output j.width in its own line (we call this + // method with fJustWidths == true) + } + else if (fPostJust) + prgfMods[kslatJWidth] = true; + + GrPass * ppass = Pass(ipass); + GrPass * ppassNext = (ipass < m_cpass - 1) ? Pass(ipass + 1) : NULL; + GrSlotStream * psstrm = OutputStream(ipass); + + if (fJustWidths) + {} + else if (dynamic_cast<GrBidiPass *>(ppass)) + { + // Special stuff for Bidi pass: + // Log value of directionality attribute. + strmOut << "directionality "; + int islot; + for (islot = 0; islot < psstrm->WritePos(); islot++) + { + GrSlotState * pslot = psstrm->SlotAt(islot); + LogDirCodeInTable(strmOut, pslot->DirProcessed()); + } + strmOut << "\n"; + + // Log final direction level for bidi pass. + strmOut << "dir level "; + for (islot = 0; islot < psstrm->WritePos(); islot++) + { + GrSlotState * pslot = psstrm->SlotAt(islot); + LogInTable(strmOut, pslot->DirLevel()); + } + strmOut << "\n"; + } + else if (ppassNext && dynamic_cast<GrBidiPass *>(ppassNext)) + { + // Next pass is Bidi: log input values of directionality attribute that are input for it. + strmOut << "directionality "; + for (int islot = 0; islot < psstrm->WritePos(); islot++) + { + GrSlotState * pslot = psstrm->SlotAt(islot); + LogDirCodeInTable(strmOut, pslot->Directionality()); + } + strmOut << "\n"; + } + + for (int slat = 0; slat < kslatMax + NumUserDefn() - 1; slat++) + { + int cIndexLim = 1; + if (slat == kslatCompRef) + cIndexLim = ccomp; + else if (slat == kslatUserDefn) + cIndexLim = 1; // kMaxUserDefinedSlotAttributes; + + if (fJustWidths && slat != kslatJWidth) + continue; + + bool fValidAttr = true; + for (int iIndex = 0; iIndex < cIndexLim; iIndex++) + { + if (prgfMods[slat]) + { + switch(slat) + { + case kslatAdvX: strmOut << "advance.x "; break; + case kslatAdvY: strmOut << "advance.y "; break; + case kslatAttTo: strmOut << "att.to "; break; + case kslatAttAtX: strmOut << "att.at.x "; break; + case kslatAttAtY: strmOut << "att.at.y "; break; + case kslatAttAtGpt: strmOut << "att.at.gpt "; break; + case kslatAttAtXoff: strmOut << "att.at.xoff "; break; + case kslatAttAtYoff: strmOut << "att.at.yoff "; break; + case kslatAttWithX: strmOut << "att.with.x "; break; + case kslatAttWithY: strmOut << "att.with.y "; break; + case kslatAttWithGpt: strmOut << "att.with.gpt "; break; + case kslatAttWithXoff: strmOut << "att.with.xoff "; break; + case kslatAttWithYoff: strmOut << "att.with.yoff "; break; + case kslatAttLevel: strmOut << "att.level "; break; + case kslatBreak: strmOut << "breakweight "; break; + case kslatCompRef: strmOut << "component " << iIndex + 1 // 1-based + << " "; break; + case kslatDir: strmOut << "dir "; break; + case kslatInsert: strmOut << "insert "; break; + case kslatMeasureSol: strmOut << "measure.sol "; break; + case kslatMeasureEol: strmOut << "measure.eol "; break; + case kslatJStretch: strmOut << "j.stretch "; break; + case kslatJShrink: strmOut << "j.shrink "; break; + case kslatJStep: strmOut << "j.step "; break; + case kslatJWeight: strmOut << "j.weight "; break; + case kslatJWidth: strmOut << "j.width "; break; + case kslatPosX: + case kslatPosY: + Assert(false); + break; + case kslatShiftX: strmOut << "shift.x "; break; + case kslatShiftY: strmOut << "shift.y "; break; + default: + if (kslatUserDefn <= slat && + slat < kslatUserDefn + NumUserDefn()) + { + strmOut << "user" << (slat - kslatUserDefn + 1) // 1-based + << ((iIndex >= 9) ? " " : " "); + } + else + { + // Invalid attribute: + Warn("bad slot attribute"); + fValidAttr = false; + break; + } + } + + if (!fValidAttr) + break; // out of iIndex loop + + for (int islot = 0; islot < psstrm->WritePos(); islot++) + { + GrSlotState * pslot = psstrm->SlotAt(islot); + pslot->LogSlotAttribute(this, strmOut, ipass, slat, iIndex, + fPreJust, fPostJust); + } + + strmOut << "\n"; + } + } + } + + if (fJustWidths) + goto LDone; + + for (int iassoc = 0; iassoc < cassoc; iassoc++) + { + // Log associations. Put them on one line it that will work; otherwise use several. + bool fBoth = (cassoc <= 2); + bool fAfter = (iassoc == (cassoc - 1)); + if (fBoth) + strmOut << "assocs "; + else if (iassoc == 0) + strmOut << "assocs-before "; + else if (fAfter) + strmOut << " -after "; + else + strmOut << " -other "; + + for (int islot = 0; islot < psstrm->WritePos(); islot++) + { + GrSlotState * pslot = psstrm->SlotAt(islot); + pslot->LogAssociation(this, strmOut, ipass, iassoc, fBoth, fAfter); + } + + strmOut << "\n"; + + if (fBoth) + break; + } + + if (cassoc == 0 && dynamic_cast<GrBidiPass *>(ppass)) + { + strmOut << "assocs "; + + // Log associations for all slots moved during the Bidi pass. + for (int islot = 0; islot < psstrm->WritePos(); islot++) + { + GrSlotState * pslot = psstrm->SlotAt(islot); + if (pslot->m_islotTmpIn != pslot->m_islotTmpOut) + LogInTable(strmOut, pslot->m_islotTmpIn); + else + strmOut << " "; + } + + strmOut << "\n"; + } + +LDone: + + delete[] prgfMods; +} + +/*---------------------------------------------------------------------------------------------- + Write out the final positions. +----------------------------------------------------------------------------------------------*/ +void GrTableManager::LogFinalPositions(std::ostream & strmOut) +{ + GrSlotStream * psstrm = OutputStream(m_cpass - 1); + + strmOut << "x position "; + for (int islot = 0; islot < psstrm->WritePos(); islot++) + { + GrSlotState * pslot = psstrm->SlotAt(islot); + if (pslot->IsLineBreak(LBGlyphID())) + { + strmOut << " "; + continue; + } + LogInTable(strmOut, pslot->XPosition()); + } + strmOut << "\n"; + + strmOut << "y position "; + for (int islot = 0; islot < psstrm->WritePos(); islot++) + { + GrSlotState * pslot = psstrm->SlotAt(islot); + if (pslot->IsLineBreak(LBGlyphID())) + { + strmOut << " "; + continue; + } + LogInTable(strmOut, pslot->YPosition()); + } + strmOut << "\n"; +} +#endif + +/*---------------------------------------------------------------------------------------------- + Write out the final underlying-to-surface associations. +----------------------------------------------------------------------------------------------*/ +void Segment::LogUnderlyingToSurface(GrTableManager * ptman, std::ostream & strmOut, + GrCharStream * pchstrm) +{ +#ifdef TRACING + strmOut << "\n\nUNDERLYING TO SURFACE MAPPINGS\n\n"; + + size_t cassocs = 0; + int fLigs = false; + int ichw; + for (ichw = 0; ichw < (m_ichwAssocsLim - m_ichwAssocsMin); ichw++) + { + cassocs = max(cassocs, m_prgvisloutAssocs[ichw].size()); + if (m_prgisloutLigature[ichw] != kNegInfinity) + fLigs = true; + } + +// ptman->LogSlotHeader(strmOut, m_ichwAssocsLim, SP_PER_SLOT, LEADING_SP, m_ichwAssocsMin); + ptman->LogUnderlyingHeader(strmOut, pchstrm->Min(), (pchstrm->Min() + m_ichwAssocsLim), + -m_ichwAssocsMin, NULL); + + int rgnChars[MAX_SLOTS]; + bool rgfNewRun[MAX_SLOTS]; + std::fill_n(rgfNewRun, MAX_SLOTS, false); + GrFeatureValues rgfval[MAX_SLOTS]; + int cchwMaxRawChars; + + int cchw = pchstrm->GetLogData(ptman, rgnChars, rgfNewRun, rgfval, + -m_ichwAssocsMin, &cchwMaxRawChars); + cchw = min(cchw, MAX_SLOTS); + + utf16 rgchwChars2[MAX_SLOTS]; + utf16 rgchwChars3[MAX_SLOTS]; + utf16 rgchwChars4[MAX_SLOTS]; + utf16 rgchwChars5[MAX_SLOTS]; + utf16 rgchwChars6[MAX_SLOTS]; + int rgcchwRaw[MAX_SLOTS]; + if (cchwMaxRawChars > 1) + { + cchwMaxRawChars = min(cchwMaxRawChars, 6); + pchstrm->GetLogDataRaw(ptman, cchw, -m_ichwAssocsMin, cchwMaxRawChars, + rgnChars, rgchwChars2, rgchwChars3, rgchwChars4, rgchwChars5, rgchwChars6, + rgcchwRaw); + } + else + { + for (ichw = 0 ; ichw <(m_ichwAssocsLim - m_ichwAssocsMin); ichw++) + { + rgcchwRaw[ichw] = 1; + rgchwChars2[ichw] = 0; + rgchwChars3[ichw] = 0; + rgchwChars4[ichw] = 0; + rgchwChars5[ichw] = 0; + rgchwChars6[ichw] = 0; + } + } + + // Text + strmOut << "Text: "; // 15 spaces + int inUtf32 = 0; + for (ichw = 0; ichw < (m_ichwAssocsLim - m_ichwAssocsMin); ichw++) + { + utf16 chw, chwNext; + switch (rgcchwRaw[ichw]) + { + default: + case 1: chw = utf16(rgnChars[inUtf32]); chwNext = rgchwChars2[inUtf32]; break; + case 2: chw = rgchwChars2[inUtf32]; chwNext = rgchwChars3[inUtf32]; break; + case 3: chw = rgchwChars3[inUtf32]; chwNext = rgchwChars4[inUtf32]; break; + case 4: chw = rgchwChars4[inUtf32]; chwNext = rgchwChars5[inUtf32]; break; + case 5: chw = rgchwChars5[inUtf32]; chwNext = rgchwChars6[inUtf32]; break; + case 6: chw = rgchwChars6[inUtf32]; chwNext = 0; break; + } + if (rgcchwRaw[ichw] == 1 && chwNext == 0 && chw < 0x0100) // ANSI + strmOut << (char)chw << " "; // 6 spaces + else + strmOut << " "; // 7 spaces + if (chwNext == 0) + inUtf32++; + } + strmOut << "\n"; + + // Unicode + strmOut << "Unicode: "; + inUtf32 = 0; + for (ichw = 0; ichw < (m_ichwAssocsLim - m_ichwAssocsMin); ichw++) + { + utf16 chw, chwNext; + switch (rgcchwRaw[ichw]) + { + default: + case 1: chw = utf16(rgnChars[inUtf32]); chwNext = rgchwChars2[inUtf32]; break; + case 2: chw = rgchwChars2[inUtf32]; chwNext = rgchwChars3[inUtf32]; break; + case 3: chw = rgchwChars3[inUtf32]; chwNext = rgchwChars4[inUtf32]; break; + case 4: chw = rgchwChars4[inUtf32]; chwNext = rgchwChars5[inUtf32]; break; + case 5: chw = rgchwChars5[inUtf32]; chwNext = rgchwChars6[inUtf32]; break; + case 6: chw = rgchwChars6[inUtf32]; chwNext = 0; break; + } + ptman->LogHexInTable(strmOut, chw, chwNext != 0); + if (chwNext == 0) + inUtf32++; + } + strmOut << "\n"; + + strmOut << "before "; + for (ichw = 0; ichw < (m_ichwAssocsLim - m_ichwAssocsMin); ichw++) + { + if (rgcchwRaw[ichw] > 1) + // continuation of Unicode codepoint + strmOut << " "; + else if (m_prgisloutBefore[ichw] == kNegInfinity) + strmOut << "<-- "; + else if (m_prgisloutBefore[ichw] == kPosInfinity) + strmOut << "--> "; + else + ptman->LogInTable(strmOut, m_prgisloutBefore[ichw]); + } + strmOut <<"\n"; + + for (size_t ix = 1; ix < cassocs - 1; ix++) //(cassocs > 2) + { + if (ix == 1) + strmOut << "other "; + else + strmOut << " "; + for (ichw = 0; ichw < (m_ichwAssocsLim - m_ichwAssocsMin); ichw++) + { + if (m_prgvisloutAssocs[ichw].size() <= ix) + strmOut << " "; + else if (m_prgvisloutAssocs[ichw][ix] != m_prgisloutAfter[ichw]) + ptman->LogInTable(strmOut, m_prgvisloutAssocs[ichw][ix]); + else + strmOut << " "; + } + strmOut << "\n"; + } + + strmOut << "after "; + for (ichw = 0; ichw < (m_ichwAssocsLim - m_ichwAssocsMin); ichw++) + { + if (rgcchwRaw[ichw] > 1) + // continuation of Unicode codepoint + strmOut << " "; + else if (m_prgisloutAfter[ichw] == kNegInfinity) + strmOut << "<-- "; + else if (m_prgisloutAfter[ichw] == kPosInfinity) + strmOut << "--> "; + else + ptman->LogInTable(strmOut, m_prgisloutAfter[ichw]); + } + strmOut <<"\n"; + + if (fLigs) + { + strmOut << "ligature "; + for (ichw = 0; ichw < (m_ichwAssocsLim - m_ichwAssocsMin); ichw++) + { + if (rgcchwRaw[ichw] > 1) + // continuation of Unicode codepoint + strmOut << " "; + else if (m_prgisloutLigature[ichw] != kNegInfinity) + ptman->LogInTable(strmOut, m_prgisloutLigature[ichw]); + else + strmOut << " "; + } + strmOut << "\n"; + + strmOut << "component "; + for (ichw = 0; ichw < (m_ichwAssocsLim - m_ichwAssocsMin); ichw++) + { + if (rgcchwRaw[ichw] > 1) + // continuation of Unicode codepoint + strmOut << " "; + else if (m_prgisloutLigature[ichw] != kNegInfinity) + ptman->LogInTable(strmOut, m_prgiComponent[ichw] + 1); // 1-based + else + strmOut << " "; + } + strmOut << "\n"; + } + + strmOut << "\n"; +#endif +} + +/*---------------------------------------------------------------------------------------------- + Write out the final surface-to-underlying associations. +----------------------------------------------------------------------------------------------*/ +void Segment::LogSurfaceToUnderlying(GrTableManager * ptman, std::ostream & strmOut) +{ +#ifdef TRACING + strmOut << "\nSURFACE TO UNDERLYING MAPPINGS\n\n"; + + ptman->LogSlotHeader(strmOut, m_cslout, SP_PER_SLOT, LEADING_SP); + + int ccomp = 0; + + strmOut << "Glyph IDs: "; + int islout; + for (islout = 0; islout < m_cslout; islout++) + { + GrSlotOutput * psloutTmp = m_prgslout + islout; + if (psloutTmp->SpecialSlotFlag() == kspslLbInitial || + psloutTmp->SpecialSlotFlag() == kspslLbFinal) + { + strmOut << "# "; + } + else + { + ptman->LogHexInTable(strmOut, psloutTmp->GlyphID()); + if (psloutTmp->HasComponents()) + ccomp = max(ccomp, psloutTmp->NumberOfComponents()); + } + } + strmOut << "\n"; + + bool fAnyPseudos = false; + for (islout = 0; islout < m_cslout; islout++) + { + GrSlotOutput * psloutTmp = m_prgslout + islout; + if (psloutTmp->GlyphID() != psloutTmp->ActualGlyphForOutput(ptman)) + { + fAnyPseudos = true; + break; + } + } + if (fAnyPseudos) + { + strmOut << "Actual glyphs: "; + for (int islout = 0; islout < m_cslout; islout++) + { + GrSlotOutput * psloutTmp = m_prgslout + islout; + if (psloutTmp->GlyphID() != psloutTmp->ActualGlyphForOutput(ptman)) + ptman->LogHexInTable(strmOut, psloutTmp->ActualGlyphForOutput(ptman)); + else + strmOut << " "; + } + strmOut << "\n"; + } + + strmOut << "before "; + for (islout = 0; islout < m_cslout; islout++) + { + GrSlotOutput * psloutTmp = m_prgslout + islout; + if (psloutTmp->SpecialSlotFlag() == kspslLbInitial || + psloutTmp->SpecialSlotFlag() == kspslLbFinal) + { + strmOut << " "; + } + else + ptman->LogInTable(strmOut, psloutTmp->BeforeAssoc()); + } + strmOut << "\n"; + + strmOut << "after "; + for (islout = 0; islout < m_cslout; islout++) + { + GrSlotOutput * psloutTmp = m_prgslout + islout; + if (psloutTmp->SpecialSlotFlag() == kspslLbInitial || + psloutTmp->SpecialSlotFlag() == kspslLbFinal) + { + strmOut << " "; + } + else + ptman->LogInTable(strmOut, psloutTmp->AfterAssoc()); + } + strmOut << "\n"; + + for (int icomp = 0; icomp < ccomp; icomp++) + { + strmOut << "component " << icomp + 1 // 1=based + << " "; + for (islout = 0; islout < m_cslout; islout++) + { + GrSlotOutput * psloutTmp = m_prgslout + islout; + if (psloutTmp->SpecialSlotFlag() == kspslLbInitial || + psloutTmp->SpecialSlotFlag() == kspslLbFinal) + { + strmOut << " "; + } + else if (icomp < psloutTmp->NumberOfComponents()) + ptman->LogInTable(strmOut, psloutTmp->UnderlyingComponent(icomp)); + else + strmOut << " "; + } + strmOut << "\n"; + } +#endif +} + +#ifdef TRACING +/*---------------------------------------------------------------------------------------------- + Write out the header lines for the underlying data. +----------------------------------------------------------------------------------------------*/ +void GrTableManager::LogUnderlyingHeader(std::ostream & strmOut, int iMin, + int iLim, int cBackup, int * prgichr) +{ + strmOut << "string "; + int i, iLabel; + int * pichr = prgichr; + for (i = iMin - cBackup, iLabel = i; i < iLim; i++, iLabel++, pichr++) + { + while (prgichr && *pichr > 1) + { + // continuation of upper-plane char + iLabel++; + pichr++; + } + LogInTable(strmOut, iLabel); + } + strmOut << "\n"; + + strmOut << "segment "; + pichr = prgichr; + for (i = 0 - cBackup, iLabel = i; i < (iLim - iMin); i++, iLabel++, pichr++) + { + while (prgichr && *pichr > 1) + { + // continuation of upper-plane char + iLabel++; + pichr++; + } + LogInTable(strmOut, iLabel); + } + strmOut << "\n\n"; +} + +/*---------------------------------------------------------------------------------------------- + Write out the header lines for the slot contents. +----------------------------------------------------------------------------------------------*/ +void GrTableManager::LogSlotHeader(std::ostream & strmOut, int islotLim, + int cspPerSlot, int cspLeading, int islotMin) +{ + islotLim = min(islotLim, MAX_SLOTS); + + int isp, islot; + + for (isp = 0; isp < cspLeading; isp++) + strmOut << " "; + + for (islot = islotMin; islot < islotLim; islot++) + LogInTable(strmOut, islot); + strmOut << "\n\n"; + +/**** + if (cslot >= 100) + { + for (isp = 0; isp < cspLeading; isp++) + strmOut << " "; + + for (islot = 0; islot < 100; islot++) + { + for (isp = 0; isp < cspPerSlot; isp++) + strmOut << " "; + } + + for (islot = 100; islot < cslot; islot++) + { + strmOut << "1"; + for (isp = 1; isp < cspPerSlot; isp++) + strmOut << " "; + } + } + + if (cslot > 10) + { + for (isp = 0; isp < cspLeading; isp++) + strmOut << " "; + + for (islot = 0; islot < 10; islot++) + { + for (isp = 0; isp < cspPerSlot; isp++) + strmOut << " "; + } + + for (islot = 10; islot < cslot; islot++) + { + if (true) // (islot % 10 == 0) + { + if (islot >= 100) + strmOut << ((islot - 100) / 10); + else + strmOut << (islot / 10); + } + else + strmOut << " "; + + for (isp = 1; isp < cspPerSlot; isp++) + strmOut << " "; + } + strmOut << "\n"; + } + + for (isp = 0; isp < cspLeading; isp++) + strmOut << " "; + + for (islot = 0; islot < cslot; islot++) + { + strmOut << (islot % 10); + for (isp = 1; isp < cspPerSlot; isp++) + strmOut << " "; + + } + strmOut << "\n"; +****/ +} + +/*---------------------------------------------------------------------------------------------- + Write out the glyph numbers for the given stream. +----------------------------------------------------------------------------------------------*/ +void GrTableManager::LogSlotGlyphs(std::ostream & strmOut, GrSlotStream * psstrm) +{ + strmOut << "Glyph IDs: "; + int islot; + for (islot = 0; islot < psstrm->WritePos(); islot++) + { + GrSlotState * pslotTmp = psstrm->SlotAt(islot); + if (pslotTmp->IsLineBreak(LBGlyphID())) + strmOut << "# "; + else + LogHexInTable(strmOut, pslotTmp->GlyphID()); + } + strmOut << "\n"; +} + +/*---------------------------------------------------------------------------------------------- + Return flags indicating which attributes were modified for some slot during the pass + and therefore need a line in the log file. + + The 'pfMods' array has one flag per slot attribute; the final group are the user- + definable attributes. 'pccomp' returns the maximum number of ligature components + in this pass. Associations are assumed to have changed if anything else for the slot did. +----------------------------------------------------------------------------------------------*/ +void GrTableManager::SlotAttrsModified(int ipass, bool * rgfMods, bool fPreJust, + int * pccomp, int * pcassoc) +{ + // Zero the flags + std::fill_n(rgfMods, kslatMax + NumUserDefn() - 1, false); + *pccomp = 0; + *pcassoc = 0; + + //GrSlotStream * psstrmIn = InputStream(ipass); + GrSlotStream * psstrmOut = OutputStream(ipass); + + for (int islot = 0; islot < psstrmOut->WritePos(); islot++) + { + GrSlotState * pslot = psstrmOut->SlotAt(islot); + Assert(pslot->PassModified() <= ipass); + if (pslot->PassModified() < ipass && !fPreJust) + continue; // not modified during this pass + + pslot->SlotAttrsModified(rgfMods, fPreJust, pccomp, pcassoc); + } +} + +void GrSlotState::SlotAttrsModified(bool * rgfMods, bool fPreJust, int * pccomp, int * pcassoc) +{ + // To handle reprocessing, in which case there may be a chain of slots modified + // in the same pass: + GrSlotState * pslotPrev = m_pslotPrevState; + while (pslotPrev && pslotPrev->PassModified() == PassModified()) + pslotPrev = pslotPrev->m_pslotPrevState; + + if (!pslotPrev) + { + // Inserted slot? + if (m_fAdvXSet) + rgfMods[kslatAdvX] = true; + if (m_fAdvYSet) + rgfMods[kslatAdvY] = true; + + if (m_srAttachTo != 0) + rgfMods[kslatAttTo] = true; + + if (m_mAttachAtX != kNotYetSet || m_mAttachAtY != 0) + { // always do these in pairs + rgfMods[kslatAttAtX] = true; + rgfMods[kslatAttAtY] = true; + } + if (m_nAttachAtGpoint != kNotYetSet) + rgfMods[kslatAttAtGpt] = true; + if (m_mAttachAtXOffset != 0 || m_mAttachAtYOffset != 0) + { // always do these in pairs + rgfMods[kslatAttAtXoff] = true; + rgfMods[kslatAttAtYoff] = true; + } + + if (m_mAttachWithX != kNotYetSet || m_mAttachWithY != 0) + { // always do these in pairs + rgfMods[kslatAttWithX] = true; + rgfMods[kslatAttWithY] = true; + } + if (m_nAttachWithGpoint != kNotYetSet) + rgfMods[kslatAttWithGpt] = true; + if (m_mAttachWithXOffset != 0 || m_mAttachWithYOffset != 0) + { // always do these in pairs + rgfMods[kslatAttWithXoff] = true; + rgfMods[kslatAttWithYoff] = true; + } + + if (m_nAttachLevel != 0) + rgfMods[kslatAttLevel] = true; + + if (m_lb != kNotYetSet) + rgfMods[kslatBreak] = true; + if (m_dirc != kNotYetSet) + rgfMods[kslatDir] = true; + if (m_fInsertBefore != true) + rgfMods[kslatInsert] = true; + + if (m_mMeasureSol != kNotYetSet && m_mMeasureSol != 0) + rgfMods[kslatMeasureSol] = true; + if (m_mMeasureEol != kNotYetSet && m_mMeasureEol != 0) + rgfMods[kslatMeasureEol] = true; + + if (m_mJStretch0 != kNotYetSet && m_mJStretch0 != 0) + rgfMods[kslatJStretch] = true; + if (m_mJShrink0 != kNotYetSet && m_mJShrink0 != 0) + rgfMods[kslatJShrink] = true; + if (m_mJStep0 != kNotYetSet && m_mJStep0 != 0) + rgfMods[kslatJStep] = true; + if (m_nJWeight0 != byte(kNotYetSet) && + m_nJWeight0 != 0 && m_nJWeight0 != 1) + rgfMods[kslatJWeight] = true; + if (m_mJWidth0 != kNotYetSet && m_mJWidth0 != 0) + rgfMods[kslatJWidth] = true; + + if (m_mShiftX != 0) + rgfMods[kslatShiftX] = true; + if (m_mShiftY != 0) + rgfMods[kslatShiftY] = true; + + int i; + for (i = 0; i < m_cnCompPerLig; i++) + { + if (CompRef(i) != NULL) + rgfMods[kslatCompRef] = true; + if (CompRef(i)) + *pccomp = max(*pccomp, i + 1); // max number of ligatures in this pass + } + + for (i = 0; i < m_cnUserDefn; i++) + { + if (UserDefn(i) != 0) + rgfMods[kslatUserDefn + i] = true; + } + } + else + { + if (m_fAdvXSet && m_mAdvanceX != pslotPrev->m_mAdvanceX) + rgfMods[kslatAdvX] = true; + if (m_fAdvYSet && m_mAdvanceY != pslotPrev->m_mAdvanceY) + rgfMods[kslatAdvY] = true; + + if (m_srAttachTo != pslotPrev->m_srAttachTo) + rgfMods[kslatAttTo] = true; + + if (m_mAttachAtX != pslotPrev->m_mAttachAtX || m_mAttachAtY != pslotPrev->m_mAttachAtY) + { + rgfMods[kslatAttAtX] = true; + rgfMods[kslatAttAtY] = true; + } + if (m_nAttachAtGpoint != pslotPrev->m_nAttachAtGpoint) + rgfMods[kslatAttAtGpt] = true; + if (m_mAttachAtXOffset != pslotPrev->m_mAttachAtXOffset || + m_mAttachAtYOffset != pslotPrev->m_mAttachAtYOffset) + { + rgfMods[kslatAttAtXoff] = true; + rgfMods[kslatAttAtYoff] = true; + } + + if (m_mAttachWithX != pslotPrev->m_mAttachWithX || + m_mAttachWithY != pslotPrev->m_mAttachWithY) + { + rgfMods[kslatAttWithX] = true; + rgfMods[kslatAttWithY] = true; + } + if (m_nAttachWithGpoint != pslotPrev->m_nAttachWithGpoint) + rgfMods[kslatAttWithGpt] = true; + if (m_mAttachWithXOffset != pslotPrev->m_mAttachWithXOffset || + m_mAttachWithYOffset != pslotPrev->m_mAttachWithYOffset) + { + rgfMods[kslatAttWithXoff] = true; + rgfMods[kslatAttWithYoff] = true; + } + + if (m_nAttachLevel != pslotPrev->m_nAttachLevel) + rgfMods[kslatAttLevel] = true; + + if (m_lb != pslotPrev->m_lb) + rgfMods[kslatBreak] = true; + if (m_dirc != pslotPrev->m_dirc) + rgfMods[kslatDir] = true; + if (m_fInsertBefore != pslotPrev->m_fInsertBefore) + rgfMods[kslatInsert] = true; + + if (m_mMeasureSol != pslotPrev->m_mMeasureSol) + rgfMods[kslatMeasureSol] = true; + if (m_mMeasureEol != pslotPrev->m_mMeasureEol) + rgfMods[kslatMeasureEol] = true; + + if (m_mJStretch0 != pslotPrev->m_mJStretch0 || (fPreJust && m_mJStretch0 != 0)) + rgfMods[kslatJStretch] = true; + if (m_mJShrink0 != pslotPrev->m_mJShrink0 || (fPreJust && m_mJShrink0 != 0)) + rgfMods[kslatJShrink] = true; + if (m_mJStep0 != pslotPrev->m_mJStep0 || (fPreJust && m_mJStep0 != 0)) + rgfMods[kslatJStep] = true; + if (m_nJWeight0 != pslotPrev->m_nJWeight0 || (fPreJust && m_nJWeight0 != 0)) + rgfMods[kslatJWeight] = true; + if (m_mJWidth0 != pslotPrev->m_mJWidth0) + rgfMods[kslatJWidth] = true; + + if (m_mShiftX != pslotPrev->m_mShiftX) + rgfMods[kslatShiftX] = true; + if (m_mShiftY != pslotPrev->m_mShiftY) + rgfMods[kslatShiftY] = true; + + int i; + for (i = 0; i < m_cnCompPerLig; i++) + { + if (CompRef(i) != pslotPrev->CompRef(i)) + rgfMods[kslatCompRef] = true; + if (CompRef(i)) + *pccomp = max(*pccomp, i + 1); // max number of ligatures in this pass + } + + for (i = 0; i < m_cnUserDefn; i++) + { + if (UserDefn(i) != pslotPrev->UserDefn(i)) + rgfMods[kslatUserDefn + i] = true; + } + } + + *pcassoc = max(*pcassoc, AssocsSize()); +} + +/*---------------------------------------------------------------------------------------------- + Log the value of the slot attribute for the given slot, if it changed. +----------------------------------------------------------------------------------------------*/ +void GrSlotState::LogSlotAttribute(GrTableManager * ptman, + std::ostream & strmOut, int ipass, int slat, int iIndex, + bool fPreJust, bool fPostJust) +{ + if (m_ipassModified != ipass && !fPreJust && !fPostJust) + { + strmOut << " "; + return; + } + + // To handle reprocessing, in which case there may be a chain of slots modified + // in the same pass: + GrSlotState * pslotPrev = m_pslotPrevState; + while (pslotPrev && pslotPrev->PassModified() == PassModified()) + pslotPrev = pslotPrev->m_pslotPrevState; + + // General-purpose variables for em-unit attributes: + int mThis; + int mPrev; + switch (slat) + { + case kslatShiftX: + mThis = m_mShiftX; + mPrev = (pslotPrev) ? pslotPrev->m_mShiftX : 0; + break; + case kslatShiftY: + mThis = m_mShiftY; + mPrev = (pslotPrev) ? pslotPrev->m_mShiftY : 0; + break; + case kslatMeasureSol: + mThis = m_mMeasureSol; + mPrev = (pslotPrev) ? pslotPrev->m_mMeasureSol : 0; + break; + case kslatMeasureEol: + mThis = m_mMeasureEol; + mPrev = (pslotPrev) ? pslotPrev->m_mMeasureEol : 0; + break; + case kslatJStretch: + mThis = m_mJStretch0; + mPrev = (pslotPrev) ? pslotPrev->m_mJStretch0 : 0; + mPrev = (fPreJust && mThis > 0) ? -1 : mPrev; // log it even if it didn't change + break; + case kslatJShrink: + mThis = m_mJShrink0; + mPrev = (pslotPrev) ? pslotPrev->m_mJShrink0 : 0; + mPrev = (fPreJust && mThis > 0) ? -1 : mPrev; // log it even if it didn't change + break; + case kslatJStep: + mThis = m_mJStep0; + mPrev = (pslotPrev) ? pslotPrev->m_mJStep0 : 0; + mPrev = (fPreJust && mThis > 1) ? -1 : mPrev; // log it even if it didn't change + break; + case kslatJWeight: + mThis = m_nJWeight0; + mPrev = (pslotPrev) ? pslotPrev->m_nJWeight0 : 0; + mPrev = (fPreJust && mThis > 1) ? -1 : mPrev; // log it even if it didn't change + break; + case kslatJWidth: + mThis = m_mJWidth0; + mPrev = (pslotPrev) ? pslotPrev->m_mJWidth0 : 0; + mPrev = (fPostJust && mThis > 0) ? -1 : mPrev; // log it even if it didn't change + break; + default: + // don't use these variables + break; + } + + switch (slat) + { + case kslatAdvX: + if (m_fAdvXSet && (!pslotPrev || m_mAdvanceX != pslotPrev->m_mAdvanceX)) + { + ptman->LogInTable(strmOut, m_mAdvanceX); + return; + } + break; + case kslatAdvY: + if (m_fAdvYSet && (!pslotPrev || m_mAdvanceY != pslotPrev->m_mAdvanceY)) + { + ptman->LogInTable(strmOut, m_mAdvanceY); + return; + } + break; + + case kslatAttTo: + if (m_srAttachTo != (pslotPrev ? pslotPrev->m_srAttachTo : 0)) + { + ptman->LogInTable(strmOut, m_srAttachTo); + return; + } + break; + + case kslatAttAtX: // always do these in pairs + case kslatAttAtY: + if (m_mAttachAtX != (pslotPrev ? pslotPrev->m_mAttachAtX : kNotYetSet) || + m_mAttachAtY != (pslotPrev ? pslotPrev->m_mAttachAtY : 0)) + { + ptman->LogInTable(strmOut, + ((slat == kslatAttAtX) ? m_mAttachAtX : m_mAttachAtY)); + return; + } + break; + case kslatAttAtGpt: + if (m_nAttachAtGpoint != (pslotPrev ? pslotPrev->m_nAttachAtGpoint : kNotYetSet)) + { + ptman->LogInTable(strmOut, + ((m_nAttachAtGpoint == kGpointZero) ? 0 : m_nAttachAtGpoint)); + return; + } + break; + case kslatAttAtXoff: // always do these in pairs + case kslatAttAtYoff: + if (m_mAttachAtXOffset != (pslotPrev ? pslotPrev->m_mAttachAtXOffset : 0) || + m_mAttachAtYOffset != (pslotPrev ? pslotPrev->m_mAttachAtYOffset : 0)) + { + ptman->LogInTable(strmOut, + ((slat == kslatAttAtXoff) ? m_mAttachAtXOffset : m_mAttachAtYOffset)); + return; + } + break; + + case kslatAttWithX: // always do these in pairs + case kslatAttWithY: + if (m_mAttachWithX != (pslotPrev ? pslotPrev->m_mAttachWithX : kNotYetSet) || + m_mAttachWithY != (pslotPrev ? pslotPrev->m_mAttachWithY : 0)) + { + ptman->LogInTable(strmOut, + ((slat == kslatAttWithX) ? m_mAttachWithX : m_mAttachWithY)); + return; + } + break; + case kslatAttWithGpt: + if (m_nAttachWithGpoint != (pslotPrev ? pslotPrev->m_nAttachWithGpoint : kNotYetSet)) + { + ptman->LogInTable(strmOut, + ((m_nAttachWithGpoint == kGpointZero) ? 0 : m_nAttachWithGpoint)); + return; + } + break; + case kslatAttWithXoff: // always do these in pairs + case kslatAttWithYoff: + if (m_mAttachWithXOffset != (pslotPrev ? pslotPrev->m_mAttachWithXOffset : 0) || + m_mAttachWithYOffset != (pslotPrev ? pslotPrev->m_mAttachWithYOffset : 0)) + { + ptman->LogInTable(strmOut, + ((slat == kslatAttWithXoff) ? m_mAttachWithXOffset : m_mAttachWithYOffset)); + return; + } + break; + + case kslatAttLevel: + if (m_nAttachLevel != (pslotPrev ? pslotPrev->m_nAttachLevel : 0)) + { + ptman->LogInTable(strmOut, m_nAttachLevel); + return; + } + break; + + case kslatBreak: + if (m_lb != (pslotPrev ? pslotPrev->m_lb : kNotYetSet)) + { + ptman->LogBreakWeightInTable(strmOut, m_lb); + return; + } + break; + case kslatDir: + if (m_dirc != (pslotPrev ? pslotPrev->m_dirc : kNotYetSet)) + { + ptman->LogDirCodeInTable(strmOut, m_dirc); + return; + } + break; + case kslatInsert: + if (m_fInsertBefore != (pslotPrev ? pslotPrev->m_fInsertBefore : true)) + { + if (m_fInsertBefore) + strmOut << "true "; + else + strmOut << "false "; + return; + } + break; + + case kslatJWeight: + if (m_mJStretch0 == 0 && m_mJShrink0 == 0) + mPrev = mThis; // don't log; weight is irrelevant + // fall through + case kslatShiftX: + case kslatShiftY: + case kslatMeasureSol: + case kslatMeasureEol: + case kslatJStretch: + case kslatJShrink: + case kslatJStep: + case kslatJWidth: + if (mThis != mPrev) + { + ptman->LogInTable(strmOut, mThis); + return; + } + break; + + case kslatCompRef: + if (CompRef(iIndex) != (pslotPrev ? pslotPrev->CompRef(iIndex) : 0)) + { + GrSlotState * pslotComp = reinterpret_cast<GrSlotState *>(CompRef(iIndex)); + ptman->LogInTable(strmOut, pslotComp->m_islotTmpIn); + return; + } + break; + + default: + if (kslatUserDefn <= slat && slat <= kslatUserDefn + m_cnUserDefn) + { + int iTmp = slat - kslatUserDefn; + if (UserDefn(iTmp) != (pslotPrev ? pslotPrev->UserDefn(iTmp) : 0)) + { + ptman->LogInTable(strmOut, UserDefn(iTmp)); + return; + } + } + else + gAssert(false); + } + + strmOut << " "; +} + +/*---------------------------------------------------------------------------------------------- + Log the value of the association, if the slot changed. +----------------------------------------------------------------------------------------------*/ +void GrSlotState::LogAssociation(GrTableManager * ptman, + std::ostream & strmOut, int ipass, int iassoc, bool fBoth, bool fAfter) +{ + if (m_ipassModified != ipass) + { + strmOut << " "; + return; + } + + if (fBoth) + { + GrSlotState * pslotBefore = (m_vpslotAssoc.size()) ? m_vpslotAssoc[0] : NULL; + GrSlotState * pslotAfter = (m_vpslotAssoc.size()) ? m_vpslotAssoc.back() : NULL; + // handle possible reprocessing + while (pslotBefore && pslotBefore->PassModified() == m_ipassModified) + pslotBefore = pslotBefore->m_pslotPrevState; + while (pslotAfter && pslotAfter->PassModified() == m_ipassModified) + pslotAfter = pslotAfter->m_pslotPrevState; + + int nBefore, nAfter; + + + int csp = 4; + if (pslotBefore) + { + nBefore = pslotBefore->m_islotTmpIn; + strmOut << nBefore; + if (nBefore > 99) csp--; + if (nBefore > 9) csp--; + } + else + { + strmOut << "??"; + csp--; + } + if (pslotAfter) + { + nAfter = pslotAfter->m_islotTmpIn; + strmOut << "/" << nAfter; + if (nAfter > 99) csp--; + if (nAfter > 9) csp--; + } + else + { + if (pslotBefore) + { + strmOut << "/" << "??"; + csp--; + } + else + csp = 5; + } + + for (int isp = 0; isp < csp; isp++) + strmOut << " "; + } + + else if (fAfter) + { + Assert(m_vpslotAssoc.size()); + GrSlotState * pslotAfter = m_vpslotAssoc.back(); + // handle possible reprocessing + while (pslotAfter && pslotAfter->PassModified() == m_ipassModified) + pslotAfter = pslotAfter->m_pslotPrevState; + + if (pslotAfter) + { + int nAfter = pslotAfter->m_islotTmpIn; + ptman->LogInTable(strmOut, nAfter); + } + else + strmOut << "?? "; + } + else if (iassoc < signed(m_vpslotAssoc.size())) + { + GrSlotState * pslot = m_vpslotAssoc[iassoc]; + // handle possible reprocessing + while (pslot && pslot->PassModified() == m_ipassModified) + pslot = pslot->m_pslotPrevState; + + if (pslot) + { + int n = pslot->m_islotTmpIn; + ptman->LogInTable(strmOut, n); + } + else + strmOut << "?? "; + } + else + strmOut << " "; +} + +/*---------------------------------------------------------------------------------------------- + Write a hex value (a glyphID or Unicode codepoint) into the table. +----------------------------------------------------------------------------------------------*/ +void GrTableManager::LogInTable(std::ostream & strmOut, int n) +{ + if (n == kNegInfinity) + { + strmOut << "-inf "; + return; + } + if (n == kPosInfinity) + { + strmOut << "+inf "; + return; + } + if (n > 999999) + { + strmOut << "****** "; + return; + } + if (n < -99999) + { + strmOut << "-***** "; + return; + } + + strmOut << n; + int csp = SP_PER_SLOT - 1; + + if (abs(n) > 99999) csp--; + if (abs(n) > 9999) csp--; + if (abs(n) > 999) csp--; + if (abs(n) > 99) csp--; + if (abs(n) > 9) csp--; + + if (n < 0) csp--; + + for (int isp = 0; isp < csp; isp++) + strmOut << " "; +} + +void GrTableManager::LogInTable(std::ostream & strmOut, float n) +{ + if (n == kNegInfFloat) + { + strmOut << "-inf "; + return; + } + if (n == kPosInfFloat) + { + strmOut << "+inf "; + return; + } + if (n > 9999) + { + strmOut << "****.* "; + return; + } + if (n < -999) + { + strmOut << "-***.* "; + return; + } + + int csp = SP_PER_SLOT - 4; + + int nInt = (int)(fabsf(n)); + int nTenths = (int)fabsf((fabsf(n) - float(nInt) + 0.05f) * 10); + if (nTenths >= 10) + { + nTenths = 0; + nInt++; + } + + if (nInt >= 1000) csp--; + if (nInt >= 100) csp--; + if (nInt >= 10) csp--; + + if (n < 0) csp--; + + for (int isp = 0; isp < csp; isp++) + strmOut << " "; + if (n < 0) + strmOut << "-"; + strmOut << nInt << "." << nTenths << " "; +} + +/*---------------------------------------------------------------------------------------------- + Write a hex value (a glyphID or Unicode codepoint) into the table. +----------------------------------------------------------------------------------------------*/ +void GrTableManager::LogHexInTable(std::ostream & strmOut, utf16 chw, bool fPlus) +{ + //char rgch[20]; + if (chw <= 0x0fff) strmOut << "0"; + if (chw <= 0x00ff) strmOut << "0"; + if (chw <= 0x000f) strmOut << "0"; + + strmOut << std::hex << chw << std::dec; + + for (int i = 4; i < SP_PER_SLOT - 2; i++) + strmOut << " "; + + if (fPlus) + strmOut << "+ "; + else + strmOut << " "; +} + +/*---------------------------------------------------------------------------------------------- + Write a directionality code to the table. +----------------------------------------------------------------------------------------------*/ +void GrTableManager::LogDirCodeInTable(std::ostream & strmOut, int dirc) +{ + switch (dirc) + { + case kdircUnknown: strmOut << "??? "; break; + case kdircNeutral: strmOut << "ON "; break; + case kdircL: strmOut << "L "; break; + case kdircR: strmOut << "R "; break; + case kdircRArab: strmOut << "AR "; break; + case kdircEuroNum: strmOut << "EN "; break; + case kdircEuroSep: strmOut << "ES "; break; + case kdircEuroTerm: strmOut << "ET "; break; + case kdircArabNum: strmOut << "AN "; break; + case kdircComSep: strmOut << "CS "; break; + case kdircWhiteSpace: strmOut << "WS "; break; + case kdircBndNeutral: strmOut << "BN "; break; + case kdircNSM: strmOut << "NSM "; break; + case kdircLRO: strmOut << "LRO "; break; + case kdircRLO: strmOut << "RLO "; break; + case kdircLRE: strmOut << "LRE "; break; + case kdircRLE: strmOut << "RLE "; break; + case kdircPDF: strmOut << "PDF "; break; + case kdircPdfL: strmOut << "PDF-L "; break; + case kdircPdfR: strmOut << "PDF-R "; break; + case kdircLlb: strmOut << "L "; break; + case kdircRlb: strmOut << "R "; break; + default: LogInTable(strmOut, dirc); break; + } +} + +/*---------------------------------------------------------------------------------------------- + Write a breakweight code to the table. +----------------------------------------------------------------------------------------------*/ +void GrTableManager::LogBreakWeightInTable(std::ostream & strmOut, int lb) +{ + if (lb < 0) + { + lb = lb * -1; + switch (lb) + { + case klbWsBreak: strmOut << "-ws "; break; + case klbWordBreak: strmOut << "-word "; break; + case klbHyphenBreak: strmOut << "-intra "; break; + case klbLetterBreak: strmOut << "-lettr "; break; + case klbClipBreak: strmOut << "-clip "; break; + default: LogInTable(strmOut, lb); break; + } + } + else + { + switch (lb) + { + case klbNoBreak: strmOut << "none "; break; + case klbWsBreak: strmOut << "ws "; break; + case klbWordBreak: strmOut << "word "; break; + case klbHyphenBreak: strmOut << "intra "; break; + case klbLetterBreak: strmOut << "letter "; break; + case klbClipBreak: strmOut << "clip "; break; + default: LogInTable(strmOut, lb); break; + } + } +} + +#endif // TRACING + +} // namespace gr diff --git a/Build/source/libs/graphite-engine/src/segment/explicit_instantiations.cpp b/Build/source/libs/graphite-engine/src/segment/explicit_instantiations.cpp new file mode 100644 index 00000000000..0aa2a40dddf --- /dev/null +++ b/Build/source/libs/graphite-engine/src/segment/explicit_instantiations.cpp @@ -0,0 +1,44 @@ +/*--------------------------------------------------------------------*//*:Ignore this sentence. +Copyright (C) 1999, 2001 SIL International. All rights reserved. + +Distributable under the terms of either the Common Public License or the +GNU Lesser General Public License, as specified in the LICENSING.txt file. + +File: explicit_instantiation.cpp +Responsibility: Sharon Correll +Last reviewed: Not yet. + +Description: + Explicitly instantiate HashMap and Vector types used in the GrEngine module. +-------------------------------------------------------------------------------*//*:End Ignore*/ + +// Standard includes for the module, gives the types we have to instantiate +#include "Main.h" +#pragma hdrstop + +// Standard includes for doing explicit instantiation: + +//////#include "Vector_i.cpp" + +namespace gr +{ + +// Types we use: +template class std::vector<int>; +// STL port doesn't like this +//template class std::vector<bool>; +template class std::vector<GrSlotState *>; +template class std::vector<GrGlyphSubTable *>; +template class std::vector<int *>; +/////template std::vector<std::wstring>; // remove +template class std::vector<OLECHAR>; +template class std::vector<byte>; +template class std::vector<DirCode>; + +// VS6.0 doesn't like these: +//template std::vector<RECT>; +//template std::vector<Segment::GlyphStrmKey>; +//template std::vector<Segment::LineSeg>; +//template std::vector<GrGlyphIndexPair>; + +} // namespace gr diff --git a/Build/source/libs/graphite-engine/src/segment/resource.h b/Build/source/libs/graphite-engine/src/segment/resource.h new file mode 100644 index 00000000000..775418f76bf --- /dev/null +++ b/Build/source/libs/graphite-engine/src/segment/resource.h @@ -0,0 +1,15 @@ +//{{NO_DEPENDENCIES}} +// Microsoft Developer Studio generated include file. +// Used by Graphite.rc +// + +// Next default values for new objects +// +#ifdef APSTUDIO_INVOKED +#ifndef APSTUDIO_READONLY_SYMBOLS +#define _APS_NEXT_RESOURCE_VALUE 101 +#define _APS_NEXT_COMMAND_VALUE 40001 +#define _APS_NEXT_CONTROL_VALUE 1000 +#define _APS_NEXT_SYMED_VALUE 101 +#endif +#endif |