summaryrefslogtreecommitdiff
path: root/Build/source/libs/graphite/src/segment
diff options
context:
space:
mode:
authorPeter Breitenlohner <peb@mppmu.mpg.de>2009-03-19 13:59:30 +0000
committerPeter Breitenlohner <peb@mppmu.mpg.de>2009-03-19 13:59:30 +0000
commit0b7b2c69ec83497d9accab064da0f4ad07662d42 (patch)
treee8f22a2acecbf3035850f56beb1c8161c53cdf2d /Build/source/libs/graphite/src/segment
parentee507bbd1fc483c58acb33cca25509a4dd81df9b (diff)
prepare for new build system
git-svn-id: svn://tug.org/texlive/trunk@12429 c570f23f-e606-0410-a88d-b1316a301751
Diffstat (limited to 'Build/source/libs/graphite/src/segment')
-rw-r--r--Build/source/libs/graphite/src/segment/FileInput.cpp266
-rw-r--r--Build/source/libs/graphite/src/segment/FileInput.h104
-rw-r--r--Build/source/libs/graphite/src/segment/FontCache.cpp308
-rw-r--r--Build/source/libs/graphite/src/segment/FontCache.h105
-rw-r--r--Build/source/libs/graphite/src/segment/FontFace.cpp598
-rw-r--r--Build/source/libs/graphite/src/segment/FontFace.h246
-rw-r--r--Build/source/libs/graphite/src/segment/GrCharStream.cpp652
-rw-r--r--Build/source/libs/graphite/src/segment/GrCharStream.h183
-rw-r--r--Build/source/libs/graphite/src/segment/GrClassTable.cpp382
-rw-r--r--Build/source/libs/graphite/src/segment/GrClassTable.h188
-rw-r--r--Build/source/libs/graphite/src/segment/GrEngine.cpp2030
-rw-r--r--Build/source/libs/graphite/src/segment/GrEngine.h622
-rw-r--r--Build/source/libs/graphite/src/segment/GrFSM.cpp526
-rw-r--r--Build/source/libs/graphite/src/segment/GrFSM.h245
-rw-r--r--Build/source/libs/graphite/src/segment/GrFeature.cpp321
-rw-r--r--Build/source/libs/graphite/src/segment/GrFeatureValues.h71
-rw-r--r--Build/source/libs/graphite/src/segment/GrGlyphTable.cpp650
-rw-r--r--Build/source/libs/graphite/src/segment/GrGlyphTable.h429
-rw-r--r--Build/source/libs/graphite/src/segment/GrPass.cpp1995
-rw-r--r--Build/source/libs/graphite/src/segment/GrPass.h748
-rw-r--r--Build/source/libs/graphite/src/segment/GrPassActionCode.cpp1492
-rw-r--r--Build/source/libs/graphite/src/segment/GrPseudoMap.h48
-rw-r--r--Build/source/libs/graphite/src/segment/GrSlotState.cpp1481
-rw-r--r--Build/source/libs/graphite/src/segment/GrSlotState.h1287
-rw-r--r--Build/source/libs/graphite/src/segment/GrSlotStream.cpp2368
-rw-r--r--Build/source/libs/graphite/src/segment/GrSlotStream.h577
-rw-r--r--Build/source/libs/graphite/src/segment/GrTableManager.cpp2691
-rw-r--r--Build/source/libs/graphite/src/segment/GrTableManager.h583
-rw-r--r--Build/source/libs/graphite/src/segment/Main.h181
-rw-r--r--Build/source/libs/graphite/src/segment/Makefile.am26
-rw-r--r--Build/source/libs/graphite/src/segment/MemoryUsage.cpp781
-rw-r--r--Build/source/libs/graphite/src/segment/Platform.cpp113
-rw-r--r--Build/source/libs/graphite/src/segment/Segment.cpp3547
-rw-r--r--Build/source/libs/graphite/src/segment/SegmentAux.cpp256
-rw-r--r--Build/source/libs/graphite/src/segment/TestFSM.cpp3416
-rw-r--r--Build/source/libs/graphite/src/segment/TestPasses.cpp1005
-rw-r--r--Build/source/libs/graphite/src/segment/TransductionLog.cpp2091
-rw-r--r--Build/source/libs/graphite/src/segment/XmlTransductionLog.cpp1583
-rw-r--r--Build/source/libs/graphite/src/segment/resource.h15
39 files changed, 34210 insertions, 0 deletions
diff --git a/Build/source/libs/graphite/src/segment/FileInput.cpp b/Build/source/libs/graphite/src/segment/FileInput.cpp
new file mode 100644
index 00000000000..52969532cd3
--- /dev/null
+++ b/Build/source/libs/graphite/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/src/segment/FileInput.h b/Build/source/libs/graphite/src/segment/FileInput.h
new file mode 100644
index 00000000000..f8c32b762eb
--- /dev/null
+++ b/Build/source/libs/graphite/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/src/segment/FontCache.cpp b/Build/source/libs/graphite/src/segment/FontCache.cpp
new file mode 100644
index 00000000000..f0540463e3b
--- /dev/null
+++ b/Build/source/libs/graphite/src/segment/FontCache.cpp
@@ -0,0 +1,308 @@
+/*--------------------------------------------------------------------*//*: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_flush == kflushAuto && fZapCache)
+ {
+ DeleteIfEmpty();
+ }
+
+ 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;
+}
+
+/*----------------------------------------------------------------------------------------------
+ Delete the cache if it is empty.
+----------------------------------------------------------------------------------------------*/
+void FontCache::DeleteIfEmpty()
+{
+ if (m_cfface <= 0)
+ // All items are NULL; delete this cache. Probably this is because
+ // the program is exiting.
+ FontFace::ZapFontCache();
+}
+
+/*----------------------------------------------------------------------------------------------
+ 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/src/segment/FontCache.h b/Build/source/libs/graphite/src/segment/FontCache.h
new file mode 100644
index 00000000000..56da5ab26b8
--- /dev/null
+++ b/Build/source/libs/graphite/src/segment/FontCache.h
@@ -0,0 +1,105 @@
+/*--------------------------------------------------------------------*//*: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;
+class FontMemoryUsage;
+
+/*----------------------------------------------------------------------------------------------
+ TODO: change from a sorted list to a hash table, if performance so requires.
+----------------------------------------------------------------------------------------------*/
+class FontCache {
+ friend class FontMemoryUsage;
+
+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 DeleteIfEmpty();
+ void AssertEmpty();
+
+ int GetFlushMode()
+ {
+ return m_flush;
+ }
+ void SetFlushMode(int);
+
+ // Debugging:
+ //bool DbgCheckFontCache();
+
+ void calculateMemoryUsage(FontMemoryUsage & fmu);
+
+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/src/segment/FontFace.cpp b/Build/source/libs/graphite/src/segment/FontFace.cpp
new file mode 100644
index 00000000000..45461fe63ac
--- /dev/null
+++ b/Build/source/libs/graphite/src/segment/FontFace.cpp
@@ -0,0 +1,598 @@
+/*--------------------------------------------------------------------*//*: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,
+ bool fDumbFallback)
+{
+ 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, fDumbFallback);
+ return pfface;
+}
+
+/*----------------------------------------------------------------------------------------------
+ Initialize the engine using the given Graphite font.
+----------------------------------------------------------------------------------------------*/
+GrResult FontFace::InitFontFace(Font * pfont,
+ std::wstring stuFaceName, bool fBold, bool fItalic,
+ bool fDumbFallback)
+{
+ 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
+
+ // Read directly from the font.
+ m_pgreng->DestroyContents();
+ GrResult res = kresUnexpected;
+ try {
+ res = m_pgreng->ReadFontTables(pfont, fItalic);
+ }
+ catch (...)
+ {
+ if (fDumbFallback && m_pgreng->DumbFallback() && !m_pgreng->BadFont())
+ {
+ // If we have a basically good font but can't do Graphite,
+ // just go with empty tables, which should be set up.
+ res = m_pgreng->m_resFontRead;
+ }
+ else
+ {
+ if (m_cfonts == 0)
+ {
+ //s_pFontCache->DeleteIfEmpty();
+ //Assert(s_pFontCache == 0);
+ delete this; // will also delete GrEngine
+ }
+ else
+ {
+ delete m_pgreng;
+ }
+ m_pgreng = NULL;
+ throw; // throw original exception
+ }
+ }
+
+ 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 or the basic font tables
+ (head, name, cmap) could not be loaded
+ - 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 * pSileTbl; const void * pSilfTbl;
+ const void * pFeatTbl; const void * pGlatTbl; const void * pGlocTbl; const void * pSillTbl;
+ //const void * pCmapTbl; const void * pNameTbl;
+ size_t cbHeadSz, cbSilfSz, cbFeatSz, cbGlatSz, cbGlocSz, cbSillSz;
+ //size_t cbCmapSz, cbSileSz, cbNameSz;
+
+ m_fFakeItalic = false;
+
+ bool fBasicTables = 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(pSileTbl, 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 and name
+ Assert(!m_fCmapTblCopy);
+ Assert(!m_fNameTblCopy);
+ fOk = SetCmapAndNameTables(pfont);
+ if (!fOk)
+ {
+ goto LUnexpected;
+ }
+
+ fBasicTables = true;
+
+ // If we have a bad base file, don't do Graphite stuff.
+ if (fBadBase || fMismatchedBase)
+ goto LUnexpected;
+
+ /****
+ 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 rgch1[50];
+// wchar_t rgch2[50];
+//#if defined(_WIN32)
+// // This version does not work in Windows, in spite of being documented:
+// // swprintf(rgch, 50, L"%d", (m_fxdBadVersion >> 16));
+// // Removing the size argument make it work.
+// swprintf(rgch1, L"%d", (m_fxdBadVersion >> 16));
+// swprintf(rgch2, L"%d", (m_fxdBadVersion & 0x0000FFFF));
+//#else
+// swprintf(rgch1, 50, L"%d", (m_fxdBadVersion >> 16));
+// swprintf(rgch2, 50, L"%d", (m_fxdBadVersion & 0x0000FFFF));
+//#endif
+ char rgch[50]; // more than enough space to print two 16-bit ints
+ char *pch = &rgch[0];
+ sprintf(rgch, "%d.%d", (m_fxdBadVersion >> 16), (m_fxdBadVersion & 0x0000FFFF));
+ std::wstring stu = L"unsupported version (";
+ //stu.append(rgch1);
+ //stu.append(L".");
+ //stu.append(rgch2);
+ while (*pch != 0)
+ stu.push_back((wchar_t)*pch++);
+ 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;
+ if (!fBasicTables)
+ m_resFontRead = kresFail; // bad font
+ else if (!fSilf)
+ m_resFontRead = kresFalse; // no Silf table--not a Graphite font
+ else
+ m_resFontRead = kresUnexpected; // couldn't read the Graphite tables
+
+ fexptn.errorCode = m_ferr;
+ fexptn.version = m_fxdBadVersion >> 16;
+ fexptn.subVersion = m_fxdBadVersion & 0x0000FFFF;
+ throw fexptn;
+
+ ReturnResult(m_resFontRead);
+}
+
+/*----------------------------------------------------------------------------------------------
+ Read the cmap and name tables.
+
+ This is called from two places. One is when we first initialize the engine. Also, if the
+ font tables have not been actually copied from the font, they may have been deleted
+ when the font was deleted. So when reusing the engine, set them to something valid based
+ on the current font.
+----------------------------------------------------------------------------------------------*/
+bool GrEngine::SetCmapAndNameTables(Font * pfont)
+{
+ GrResult res = kresOk;
+ bool fOk;
+ const void * pCmapTbl;
+ const void * pNameTbl;
+ size_t cbCmapSz, cbNameSz;
+
+ // cmap
+ if (!m_fCmapTblCopy)
+ {
+ 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;
+ return false;
+ }
+
+ if (pCmapTbl && cbCmapSz > 0)
+ {
+ // 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);
+ m_fCmapTblCopy = true;
+ m_cbCmapTbl = cbCmapSz;
+ }
+ else
+ {
+ m_pCmapTbl = const_cast<byte*>(reinterpret_cast<const byte*>(pCmapTbl));
+ m_fCmapTblCopy = false;
+ }
+
+ // 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;
+ return false;
+ }
+ if (!TtfUtil::CheckCmap31Subtable(m_pCmap_3_1))
+ {
+ m_stuInitError = L"checking cmap subtable failed";
+ m_ferr = kferrCheckCmapSubtable;
+ return false;
+ }
+ }
+ else
+ {
+ Assert(m_pCmapTbl);
+ }
+
+ // name table - eventually need feature label strings
+
+ // 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);
+
+ if (!m_fNameTblCopy)
+ {
+ 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;
+ return false;
+ }
+
+ if (pNameTbl && cbNameSz > 0)
+ {
+ // 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);
+ m_fNameTblCopy = true;
+ m_cbNameTbl = cbNameSz;
+ }
+ else
+ {
+ m_pNameTbl = const_cast<byte*>(reinterpret_cast<const byte*>(pNameTbl));
+ m_fNameTblCopy = false;
+ }
+ }
+ else
+ {
+ Assert(m_pNameTbl);
+ }
+
+ return true;
+}
+
+
+//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/src/segment/FontFace.h b/Build/source/libs/graphite/src/segment/FontFace.h
new file mode 100644
index 00000000000..7f2684a5908
--- /dev/null
+++ b/Build/source/libs/graphite/src/segment/FontFace.h
@@ -0,0 +1,246 @@
+/*--------------------------------------------------------------------
+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
+{
+ friend class gr::FontMemoryUsage;
+
+public:
+ FontFace()
+ {
+ m_cfonts = 0;
+ }
+
+ ~FontFace()
+ {
+ if (s_pFontCache)
+ 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 == NULL || s_pFontCache->GetFlushMode() == kflushAuto))
+ delete this;
+ }
+
+ bool NoFonts()
+ {
+ return (m_cfonts <= 0);
+ }
+
+ static FontFace * GetFontFace(Font * pfont,
+ std::wstring strFaceName, bool fBold, bool fItalic,
+ bool fDumbFallback = false);
+
+ GrResult InitFontFace(Font * pfont,
+ std::wstring stuFaceName, bool fBold, bool fItalic,
+ bool fDumbLayout);
+
+ 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);
+ }
+
+ bool BadFont(FontErrorCode * pferr = NULL)
+ {
+ return m_pgreng->BadFont(pferr);
+ }
+ bool DumbFallback(FontErrorCode * pferr = NULL)
+ {
+ return m_pgreng->DumbFallback(pferr);
+ }
+
+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();
+
+ static void calculateAllMemoryUsage(FontMemoryUsage & fmu);
+ void calculateMemoryUsage(FontMemoryUsage & fmu);
+
+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/src/segment/GrCharStream.cpp b/Build/source/libs/graphite/src/segment/GrCharStream.cpp
new file mode 100644
index 00000000000..20b5a46ab76
--- /dev/null
+++ b/Build/source/libs/graphite/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/src/segment/GrCharStream.h b/Build/source/libs/graphite/src/segment/GrCharStream.h
new file mode 100644
index 00000000000..892435d376e
--- /dev/null
+++ b/Build/source/libs/graphite/src/segment/GrCharStream.h
@@ -0,0 +1,183 @@
+/*--------------------------------------------------------------------*//*:Ignore this sentence.
+Copyright (C) 1999 - 2008 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 * TextSrc() { 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/src/segment/GrClassTable.cpp b/Build/source/libs/graphite/src/segment/GrClassTable.cpp
new file mode 100644
index 00000000000..43316fdc04a
--- /dev/null
+++ b/Build/source/libs/graphite/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/src/segment/GrClassTable.h b/Build/source/libs/graphite/src/segment/GrClassTable.h
new file mode 100644
index 00000000000..67d6940862c
--- /dev/null
+++ b/Build/source/libs/graphite/src/segment/GrClassTable.h
@@ -0,0 +1,188 @@
+/*--------------------------------------------------------------------*//*: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
+{
+ friend class FontMemoryUsage;
+
+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/src/segment/GrEngine.cpp b/Build/source/libs/graphite/src/segment/GrEngine.cpp
new file mode 100644
index 00000000000..0fda68df880
--- /dev/null
+++ b/Build/source/libs/graphite/src/segment/GrEngine.cpp
@@ -0,0 +1,2030 @@
+/*--------------------------------------------------------------------*//*: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>
+#include <cstring>
+#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_fCmapTblCopy = false;
+ m_fNameTblCopy = false;
+
+ 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;
+
+ if (m_fCmapTblCopy)
+ delete[] m_pCmapTbl;
+ if (m_fNameTblCopy)
+ delete[] m_pNameTbl;
+ m_pCmapTbl = NULL;
+ m_pNameTbl = NULL;
+ m_fCmapTblCopy = false;
+ m_fNameTblCopy = false;
+ }
+
+ 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);
+
+ SetCmapAndNameTables(pfont);
+
+ 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 = 0;
+ int ichFontLim = FindFontLim(pgts, ichMin, &nDirDepth);
+ int ichStrmLim = 0;
+ int ichSegLim = 0;
+ switch (segmode)
+ {
+ case ksegmodeBreak: // find break point
+ // Here ichLim should be the end of the text-source, unless we're backtracking.
+ ichStrmLim = min(ichFontLim, ichLim);
+ ichSegLim = -1; // unknown
+ break;
+ case ksegmodeJust: // stretching an existing segment to achieve justification
+ ichStrmLim = ichFontLim;
+ ichSegLim = ichLim;
+ break;
+ //case ksegmodeMms: // MakeMeasuredSegment
+ // ichLim = ichFontLim;
+ // ichStrmLim = ichFontLim;
+ // ichSegLim = -1; // process to the end of the stream
+ // break;
+ case ksegmodeRange: // segment from range
+ 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 = false;
+ int ichCallerBtLim = -1;
+ bool fInfiniteWidth = false;
+ 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 *pSilfTbl, int lSilfStart,
+ byte *pGlobTbl, int lGlocStart,
+ byte *pFeatTbl, int lFeatStart,
+ int * pfxdBadVersion)
+{
+ pgrstrm->OpenBuffer(pSilfTbl, isizeof(int));
+ pgrstrm->SetPositionInFont(lSilfStart);
+ *pfxdBadVersion = ReadVersion(*pgrstrm);
+ pgrstrm->CloseBuffer();
+ if (*pfxdBadVersion > kSilfVersion)
+ return false;
+
+ pgrstrm->OpenBuffer(pGlobTbl, lGlocStart + isizeof(int));
+ pgrstrm->SetPositionInFont(lGlocStart);
+ *pfxdBadVersion = ReadVersion(*pgrstrm);
+ pgrstrm->CloseBuffer();
+ if (*pfxdBadVersion > kGlocVersion)
+ return false;
+
+ pgrstrm->OpenBuffer(pFeatTbl, 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 and descent
+ 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--don't bother with these, I don't seem to be able to know what are reasonable values.
+ //if (m_chwPseudoAttr > 200 || m_chwBWAttr > 200 || m_chwDirAttr > 200)
+ // 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::kNotYetSet8)
+ {
+ switch (nUnicode)
+ {
+ case knSpace:
+ pslot->SetBreakWeight(klbWordBreak);
+ break;
+ case knHyphen:
+ pslot->SetBreakWeight(klbHyphenBreak);
+ break;
+ default:
+ pslot->SetBreakWeight(klbLetterBreak);
+ }
+ }
+
+ if ((int)pslot->Directionality() == GrSlotState::kNotYetSet8)
+ {
+ 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/src/segment/GrEngine.h b/Build/source/libs/graphite/src/segment/GrEngine.h
new file mode 100644
index 00000000000..81b54e805bc
--- /dev/null
+++ b/Build/source/libs/graphite/src/segment/GrEngine.h
@@ -0,0 +1,622 @@
+/*--------------------------------------------------------------------
+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;
+ friend class FontMemoryUsage;
+
+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 font could not be found or basic tables couldn't be read;
+ // kresFalse if the Silf table is not present;
+ // kresUnexpected if the Graphite tables could not be loaded
+
+ GrResult m_resFontValid; // kresInvalidArg if the engine is not yet initialized
+ // Originally:
+ // kresOk if Graphite font & dc font match cmaps
+ // kresUnexpected/kresFalse if dumb rendering
+ // kresFail if dumb rendering with no cmap
+ // But now it is apparently just = to m_resFontRead otherwise.
+ 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;
+ bool m_fCmapTblCopy;
+ int m_cbCmapTbl; // needed only for memory instrumentation
+
+ // 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_fNameTblCopy;
+ int m_cbNameTbl; // needed only for memory instrumentation
+
+ 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);
+ bool SetCmapAndNameTables(Font * pfont);
+
+ void CreateEmpty();
+
+ bool BadFont(FontErrorCode * pferr = NULL)
+ {
+ if (pferr)
+ *pferr = m_ferr;
+ return (m_resFontValid == kresFail || m_resFontRead == kresFail);
+ }
+ bool DumbFallback(FontErrorCode * pferr = NULL)
+ {
+ if (pferr)
+ *pferr = m_ferr;
+ return (m_resFontValid != kresOk || m_resFontRead != kresOk);
+ }
+
+ 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/src/segment/GrFSM.cpp b/Build/source/libs/graphite/src/segment/GrFSM.cpp
new file mode 100644
index 00000000000..53182edab0d
--- /dev/null
+++ b/Build/source/libs/graphite/src/segment/GrFSM.cpp
@@ -0,0 +1,526 @@
+/*--------------------------------------------------------------------*//*: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"
+#include <cstring>
+#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];
+ m_crulnMatched = 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(ppass, 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(GrPass * ppass, GrSlotStream * psstrmIn, GrSlotStream * psstrmOut,
+ int * prgrowAccepting, int * prgcslotMatched)
+{
+ int ipass = ppass->PassNumber();
+
+ // 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);
+
+ // Figure out what column the input glyph belong in.
+ // Since this is a time-critical routine, we cache the results for the current pass.
+ GrSlotState * pslot = (cslot < 0) ?
+ psstrmOut->PeekBack(cslot) :
+ psstrmIn->Peek(cslot);
+ int col;
+ if (pslot->PassNumberForColumn() == ipass)
+ {
+ col = pslot->FsmColumn();
+ }
+ else
+ {
+ gid16 chwGlyphID = (cslot < 0) ?
+ psstrmOut->PeekBack(cslot)->GlyphID() :
+ psstrmIn->Peek(cslot)->GlyphID();
+ col = FindColumn(chwGlyphID);
+ pslot->CacheFsmColumn(ipass, col);
+ }
+ 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 && chwGlyphID <= pmcrCurr->m_chwLast)
+ //nTest = 0;
+ // This is a tiny bit more efficient:
+ return pmcrCurr->m_col;
+ }
+
+ 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/src/segment/GrFSM.h b/Build/source/libs/graphite/src/segment/GrFSM.h
new file mode 100644
index 00000000000..b1909553daa
--- /dev/null
+++ b/Build/source/libs/graphite/src/segment/GrFSM.h
@@ -0,0 +1,245 @@
+/*--------------------------------------------------------------------*//*: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
+{
+ friend class FontMemoryUsage;
+
+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(GrPass * ppass, 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
+ int m_crulnMatched; // needed only for memory instrumentation
+
+ // 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/src/segment/GrFeature.cpp b/Build/source/libs/graphite/src/segment/GrFeature.cpp
new file mode 100644
index 00000000000..61970d56949
--- /dev/null
+++ b/Build/source/libs/graphite/src/segment/GrFeature.cpp
@@ -0,0 +1,321 @@
+/*--------------------------------------------------------------------*//*: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"
+#include <cstring>
+
+#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];
+ m_cfset = 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/src/segment/GrFeatureValues.h b/Build/source/libs/graphite/src/segment/GrFeatureValues.h
new file mode 100644
index 00000000000..de9eda0f03f
--- /dev/null
+++ b/Build/source/libs/graphite/src/segment/GrFeatureValues.h
@@ -0,0 +1,71 @@
+/*--------------------------------------------------------------------*//*:Ignore this sentence.
+Copyright (C) 1999 - 2008 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);
+ }
+
+ bool FeatureValue(size_t ifeat)
+ {
+ return m_rgnFValues[ifeat];
+ }
+
+ // 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/src/segment/GrGlyphTable.cpp b/Build/source/libs/graphite/src/segment/GrGlyphTable.cpp
new file mode 100644
index 00000000000..8643eae8cb8
--- /dev/null
+++ b/Build/source/libs/graphite/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/src/segment/GrGlyphTable.h b/Build/source/libs/graphite/src/segment/GrGlyphTable.h
new file mode 100644
index 00000000000..bec0e518dee
--- /dev/null
+++ b/Build/source/libs/graphite/src/segment/GrGlyphTable.h
@@ -0,0 +1,429 @@
+/*--------------------------------------------------------------------*//*: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 font.
+
+ Hungarian: gatbl
+----------------------------------------------------------------------------------------------*/
+
+class GrGlyphAttrTable
+{
+ friend class GrGlyphTable;
+ friend class GrGlyphSubTable;
+ friend class FontMemoryUsage;
+
+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
+ // font. 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; // needed only for memory instrumentation
+ 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 font.
+ 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-bit values for the offsets,
+ or just use a separate array pointer in this class. Which would be preferable?
+----------------------------------------------------------------------------------------------*/
+
+class GrGlyphSubTable
+{
+ friend class GrGlyphTable;
+ friend class FontMemoryUsage;
+
+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 - BIG endian
+ bool m_fGlocShort; // flag for Gloc table format
+ data16 * m_prgibBIGGlyphAttrDebug; // byte offsets for glyph attr debug strings - BIG endian
+
+ 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;
+ friend class FontMemoryUsage;
+
+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/src/segment/GrPass.cpp b/Build/source/libs/graphite/src/segment/GrPass.cpp
new file mode 100644
index 00000000000..26eb3a3deb6
--- /dev/null
+++ b/Build/source/libs/graphite/src/segment/GrPass.cpp
@@ -0,0 +1,1995 @@
+/*--------------------------------------------------------------------*//*: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),
+ m_fxdVersion(0),
+ m_nMaxRuleContext(0),
+ m_pfsm(NULL),
+ m_nMaxRuleLoop(0),
+ m_nMaxBackup(0),
+ m_crul(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_cbConstraints(0),
+ m_cbActions(0),
+ m_prgibConstraintDebug(NULL),
+ m_prgibRuleDebug(NULL),
+ m_fCheckRules(false),
+ m_prgfRuleOkay(NULL),
+ m_vnStack(128),
+ m_pzpst(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);
+ m_cbConstraints = cb;
+
+ cb = m_prgibConstraintStart[m_crul];
+ m_prgbConstraintBlock = new byte[cb];
+ grstrm.ReadBlockFromFont(m_prgbConstraintBlock, cb);
+ m_cbConstraints += cb;
+
+ cb = m_prgibActionStart[m_crul];
+ m_prgbActionBlock = new byte[cb];
+ grstrm.ReadBlockFromFont(m_prgbActionBlock, cb);
+ m_cbActions = 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();
+ int islotNoLbUpTo = 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(),
+ islotNoLbUpTo, psstrmOutput->WritePos());
+ islotNoLbUpTo = 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 = 0;
+ 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);
+ while (islotIn > 0 && !StrongDir(psstrmIn->SlotAt(islotIn)->Directionality()))
+ {
+ psstrmIn->SlotAt(islotIn)->ZapDirLevel();
+ islotIn--;
+ }
+ Assert(islotIn == 0 || psstrmIn->SlotAt(islotIn)->DirHasBeenProcessed());
+ 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/src/segment/GrPass.h b/Build/source/libs/graphite/src/segment/GrPass.h
new file mode 100644
index 00000000000..d4d6a16a9dc
--- /dev/null
+++ b/Build/source/libs/graphite/src/segment/GrPass.h
@@ -0,0 +1,748 @@
+/*--------------------------------------------------------------------*//*:Ignore this sentence.
+Copyright (C) 1999 - 2008 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;
+ friend class FontMemoryUsage;
+
+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);
+ void LogXmlRules(std::ostream & strmOut, GrTableManager *, GrSlotStream * psstrmIn, int nIndent);
+ void LogXmlGlyphs(std::ostream & strmOut, GrTableManager * ptman, GrSlotStream * psstrmOut,
+ int ipassJust1, bool fPreJust, bool fPostJust, bool fBidi, bool fBidiNext, int cslotsSkipped,
+ int nIndent);
+#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 {
+ friend class FontMemoryUsage;
+
+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();
+
+ int PassNumber() { return m_ipass; }
+
+ 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);
+ void LogXmlRules(std::ostream & strmOut, GrTableManager * ptman,
+ GrSlotStream * psstrmIn, int nIndent);
+ void LogXmlGlyphs(std::ostream & strmOut, GrTableManager * ptman, GrSlotStream * psstrmOut,
+ int ipassJust1, bool fPreJust, bool fPostJust, int cslotsSkipped, int nIndent);
+#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;
+
+ // 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;
+
+ int m_cbConstraints; // needed for memory instrumentation only
+ int m_cbActions; // needed for memory instrumentation only
+
+ 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;
+
+ std::vector<int> m_vnStack; // for stack machine processing (more efficient than creating the
+ // vector each time)
+
+ // 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
+{
+ friend class FontMemoryUsage;
+
+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
+{
+ friend class FontMemoryUsage;
+
+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
+{
+ friend class FontMemoryUsage;
+
+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
+{
+ friend class FontMemoryUsage;
+
+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
+{
+ friend class FontMemoryUsage;
+
+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/src/segment/GrPassActionCode.cpp b/Build/source/libs/graphite/src/segment/GrPassActionCode.cpp
new file mode 100644
index 00000000000..83c58b27e48
--- /dev/null
+++ b/Build/source/libs/graphite/src/segment/GrPassActionCode.cpp
@@ -0,0 +1,1492 @@
+/*--------------------------------------------------------------------*//*: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.
+
+This comment could create a merge conflict. It can be removed.
+----------------------------------------------------------------------------------------------*/
+
+//:>********************************************************************************************
+//:> 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
+ m_vnStack.clear();
+
+ 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,
+ m_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/src/segment/GrPseudoMap.h b/Build/source/libs/graphite/src/segment/GrPseudoMap.h
new file mode 100644
index 00000000000..c6fec210808
--- /dev/null
+++ b/Build/source/libs/graphite/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/src/segment/GrSlotState.cpp b/Build/source/libs/graphite/src/segment/GrSlotState.cpp
new file mode 100644
index 00000000000..f2d61e45eed
--- /dev/null
+++ b/Build/source/libs/graphite/src/segment/GrSlotState.cpp
@@ -0,0 +1,1481 @@
+/*--------------------------------------------------------------------*//*: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_ipassFsmCol = -1;
+ m_colFsm = -1;
+
+ 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.
+ m_ipassFsmCol = -1;
+ m_colFsm = -1;
+}
+
+// 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;
+ m_ipassFsmCol = -1;
+ m_colFsm = -1;
+}
+
+/*----------------------------------------------------------------------------------------------
+ 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)
+{
+ CopyFrom(pslotOld, false);
+
+ m_ipassModified = ipass;
+ m_pslotPrevState = pslotOld;
+ m_ichwSegOffset = kInvalid;
+ m_vpslotAssoc.clear();
+ m_vpslotAssoc.push_back(pslotOld);
+
+ m_dircProc = pslotOld->m_dircProc;
+ m_fDirProcessed = pslotOld->m_fDirProcessed;
+
+ // Since we're going on to a new pass, no point in copying these:
+ m_ipassFsmCol = -1;
+ m_colFsm = -1;
+
+ ////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: the functions below 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 GrSlotState::CopyFrom(GrSlotState * pslot, bool fCopyEverything)
+{
+ GrSlotAbstract::CopyAbstractFrom(pslot);
+ std::copy(pslot->m_prgnVarLenBuf, pslot->m_prgnVarLenBuf + CExtraSpace(), m_prgnVarLenBuf);
+
+ if (fCopyEverything)
+ {
+ Assert(false);
+ m_ipassModified = pslot->m_ipassModified;
+ m_pslotPrevState = pslot->m_pslotPrevState;
+ m_ichwSegOffset = pslot->m_ichwSegOffset;
+ m_colFsm = pslot->m_colFsm;
+ m_ipassFsmCol = pslot->m_ipassFsmCol;
+ // TODO: copy m_vpslotAssocs.
+ m_fNeutralAssocs = pslot->m_fNeutralAssocs;
+ }
+
+ m_dislotRootFixed = pslot->m_dislotRootFixed;
+
+ m_vdislotAttLeaves.resize(pslot->m_vdislotAttLeaves.size());
+ for (size_t i = 0; i < pslot->m_vdislotAttLeaves.size(); i++)
+ m_vdislotAttLeaves[i] = pslot->m_vdislotAttLeaves[i];
+
+ m_islotPosPass = pslot->m_islotPosPass;
+ m_nUnicode = pslot->m_nUnicode;
+ m_dircProc = pslot->m_dircProc;
+ m_fDirProcessed = pslot->m_fDirProcessed;
+ m_cnUserDefn = pslot->m_cnUserDefn;
+ m_cnFeat = pslot->m_cnFeat;
+ m_bStyleIndex = pslot->m_bStyleIndex;
+ m_mAdvanceX = pslot->m_mAdvanceX;
+ m_mAdvanceY = pslot->m_mAdvanceY;
+ m_mShiftX = pslot->m_mShiftX;
+ m_mShiftY = pslot->m_mShiftY;
+ m_srAttachTo = pslot->m_srAttachTo;
+ m_nAttachLevel = pslot->m_nAttachLevel;
+ m_mAttachAtX = pslot->m_mAttachAtX;
+ m_mAttachAtY = pslot->m_mAttachAtY;
+ m_mAttachAtXOffset = pslot->m_mAttachAtXOffset;
+ m_mAttachAtYOffset = pslot->m_mAttachAtYOffset;
+ m_mAttachWithX = pslot->m_mAttachWithX;
+ m_mAttachWithY = pslot->m_mAttachWithY;
+ m_mAttachWithXOffset = pslot->m_mAttachWithXOffset;
+ m_mAttachWithYOffset = pslot->m_mAttachWithYOffset;
+ m_nAttachAtGpoint = pslot->m_nAttachAtGpoint;
+ m_nAttachWithGpoint = pslot->m_nAttachWithGpoint;
+ m_fAttachMod = pslot->m_fAttachMod;
+ m_fShiftMod = pslot->m_fShiftMod;
+ m_fIgnoreAdvance = pslot->m_fIgnoreAdvance;
+
+ m_nCompositeLevel = kNegInfinity; // uncalculated, so don't need to copy the positions??
+}
+
+void GrSlotAbstract::CopyAbstractFrom(GrSlotState * pslot)
+{
+ u_intslot * pnBufSave = m_prgnVarLenBuf;
+ *this = *pslot;
+ m_prgnVarLenBuf = pnBufSave;
+ Assert(m_prgnVarLenBuf);
+}
+
+/*----------------------------------------------------------------------------------------------
+ Initialize the output slot from the one use within the passes, with 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 GrSlotOutput::InitializeOutputFrom(GrSlotState * pslot)
+{
+ CopyAbstractFrom(pslot);
+
+ // Copy just the component information, which is of length (m_cnCompPerLig * 2)
+ //std::copy(pslot->PCompRefBuf(),
+ // pslot->PCompRefBuf() + (m_cnCompPerLig * 2),
+ // this->PCompRefBufSlout());
+}
+
+/*----------------------------------------------------------------------------------------------
+ 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_bIsSpace = 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 * GrSlotState::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) -- obsolete: no longer caching these values
+//{
+// // 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)
+{
+ GlyphMetric gmet = GlyphMetric(nMetricID);
+
+ float yAscent, yDescent;
+ gr::Point ptAdvances;
+ gr::Rect rectBb;
+
+ if (kgmetAscent == gmet)
+ {
+ //if (m_xysFontAscent != -1)
+ // return m_xysFontAscent;
+ pfont->getFontMetrics(&yAscent);
+ //m_xysFontAscent = xysRet;
+ //if (pfont)
+ // m_xysFontAscent = yAscent;
+ return yAscent;
+ }
+
+ if (kgmetDescent == gmet)
+ {
+ //if (m_xysFontDescent != -1)
+ // return m_xysFontDescent;
+ pfont->getFontMetrics(NULL, &yDescent);
+ //m_xysFontDescent = xysRet;
+ //if (pfont)
+ // m_xysFontDescent = yDescent;
+ return yDescent;
+ }
+
+ float xysGlyphX, xysGlyphY, xysGlyphWidth, xysGlyphHeight, xysAdvX, xysAdvY;
+ //if (m_xysGlyphWidth == -1)
+ //{
+ pfont->getGlyphMetrics(chwGlyphID, rectBb, ptAdvances);
+
+ xysGlyphX = rectBb.left;
+ xysGlyphY = rectBb.top;
+ xysGlyphWidth = (rectBb.right - rectBb.left);
+ xysGlyphHeight = (rectBb.top - rectBb.bottom);
+ xysAdvX = ptAdvances.x;
+ xysAdvY = ptAdvances.y;
+
+ m_bIsSpace = (0 == xysGlyphX && 0 == xysGlyphY); // should agree with test done in IsSpace() below
+
+ if (m_bIsSpace == 1)
+ {
+ // White space glyph - only case where nGlyphX == nGlyphY == 0
+ // nGlyphWidth & nGlyphHeight are always set to 16 for unknown reasons, so correct.
+ xysGlyphWidth = xysGlyphHeight = 0;
+ }
+ //}
+
+ switch (gmet)
+ { // There may be off-by-one errors below, depending on what width and height mean
+ case kgmetLsb:
+ return xysGlyphX;
+ case kgmetRsb:
+ return (xysAdvX - xysGlyphX - xysGlyphWidth);
+ case kgmetBbTop:
+ return xysGlyphY;
+ case kgmetBbBottom:
+ return (xysGlyphY - xysGlyphHeight);
+ case kgmetBbLeft:
+ return xysGlyphX;
+ case kgmetBbRight:
+ return (xysGlyphX + xysGlyphWidth);
+ case kgmetBbHeight:
+ return xysGlyphHeight;
+ case kgmetBbWidth:
+ return xysGlyphWidth;
+ case kgmetAdvWidth:
+ return xysAdvX;
+ case kgmetAdvHeight:
+ return 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);
+ ////}
+
+ if (m_bIsSpace == -1)
+ 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_bIsSpace = (0 == m_xysGlyphX && 0 == m_xysGlyphY);
+
+ Assert(m_bIsSpace == 0 || m_bIsSpace == 1);
+
+ return m_bIsSpace;
+}
+
+bool GrSlotOutput::IsSpace()
+{
+ Assert(m_bIsSpace == 0 || m_bIsSpace == 1);
+ return m_bIsSpace;
+}
+
+/*----------------------------------------------------------------------------------------------
+ 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())
+ {
+ // The x-value below has to be zero because the shift is incorporated into
+ // m_xsRootShiftX. (m_ysRootShiftY, on the other hand, isn't used anywhere.)
+ m_xsOffsetX = 0;
+ 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;
+
+ m_isloutClusterBase = pslout->m_isloutClusterBase;
+ m_disloutCluster = pslout->m_disloutCluster;
+ 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;
+}
+
+/*----------------------------------------------------------------------------------------------
+ Return the indices of all the glyphs attached to this cluster, in logical order.
+ Return an empty vector if this glyph is not the base glyph or if there are no
+ attached glyphs.
+ This method return glyph indices, not slot indices.
+----------------------------------------------------------------------------------------------*/
+void GrSlotOutput::ClusterMembers(Segment * pseg, int isloutThis, std::vector<int> & visloutRet)
+{
+ if (m_isloutClusterBase == -1 || m_isloutClusterBase == isloutThis) // yes, a base
+ pseg->ClusterMembersForGlyph(isloutThis, m_disloutCluster, visloutRet);
+ else
+ {
+ Assert(visloutRet.size() == 0);
+ }
+}
+
+} // namespace gr
diff --git a/Build/source/libs/graphite/src/segment/GrSlotState.h b/Build/source/libs/graphite/src/segment/GrSlotState.h
new file mode 100644
index 00000000000..d000927d1a4
--- /dev/null
+++ b/Build/source/libs/graphite/src/segment/GrSlotState.h
@@ -0,0 +1,1287 @@
+/*--------------------------------------------------------------------*//*:Ignore this sentence.
+Copyright (C) 1999 - 2008 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:
+ Classes 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 BasicInitializeAbstract(int cnCompPerLig, u_intslot * pnBuf)
+ {
+ m_dirc = kNotYetSet8;
+ m_lb = kNotYetSet8;
+ m_fInsertBefore = true;
+ m_nDirLevel = -1;
+
+ m_bIsSpace = -1; // unknown
+
+ 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_cnCompPerLig = byte(cnCompPerLig);
+ m_prgnVarLenBuf = pnBuf;
+ }
+
+ void CopyAbstractFrom(GrSlotState * pslot);
+
+ 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);
+
+ //GrSlotState * CompRefSlot(int i);
+
+ int BreakWeight()
+ {
+ return (int)m_lb;
+ }
+ DirCode Directionality()
+ {
+ return DirCode(int(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 = sdata8(spsl);
+ }
+
+ enum {
+ kNotYetSet = 0x7FFF,
+ kNotYetSet8 = 0x7F,
+ kInvalidGlyph = 0xFFFF
+ };
+
+ gid16 ActualGlyphForOutput(GrTableManager * ptman);
+
+ // 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)
+
+ sdata8 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
+
+ sdata8 m_dirc; // default = kNotYetSet8 (read from glyph attr)
+ sdata8 m_lb; // default = kNotYetSet8 (read from glyph attr)
+
+ sdata8 m_nDirLevel;
+
+ // Slot attributes that are used by GrSlotOutput:
+ 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
+
+ sdata8 m_bIsSpace; // 0 = false, 1 = true, -1 = unknown
+
+ byte m_cnCompPerLig;
+ // 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;
+
+}; // 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;
+ friend class FontMemoryUsage;
+
+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 BasicInitialize(int cnUserDefn, int cnCompPerLig, int cnFeat, u_intslot * pnBuf)
+ {
+ BasicInitializeAbstract(cnCompPerLig, pnBuf);
+
+ m_xysGlyphWidth = -1;
+ m_xysFontAscent = -1;
+ m_xysFontDescent = -1;
+
+ m_mAdvanceX = kNotYetSet;
+ m_mAdvanceY = kNotYetSet;
+ m_mShiftX = 0;
+ m_mShiftY = 0;
+
+ m_fAdvXSet = false; // for transduction logging
+ m_fAdvYSet = false;
+
+ 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_islotPosPass = kNotYetSet;
+
+ m_cnUserDefn = byte(cnUserDefn);
+ m_cnFeat = byte(cnFeat);
+
+ m_fHasComponents = false;
+ }
+
+ 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);
+
+ void CopyFrom(GrSlotState * pslot, bool fCopyEverything = true);
+
+ // General:
+ int RawSegOffset() { return m_ichwSegOffset; }
+
+ void SetGlyphID(gid16 chw)
+ {
+ m_chwGlyphID = chw;
+ m_chwActual = kInvalidGlyph;
+ m_xysGlyphWidth = -1; // indicate glyph metrics are invalid
+ m_ipassFsmCol = -1;
+ m_colFsm = -1;
+ }
+
+ 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;
+ }
+
+ int StyleIndex() { return m_bStyleIndex; }
+
+ 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; }
+
+ 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]; }
+
+ GrSlotState * AssocSlot(int i)
+ {
+ if (i < 0)
+ return NULL;
+ if (i >= signed(m_vpslotAssoc.size()))
+ return NULL;
+
+ GrSlotState * pslotAssoc = m_vpslotAssoc[i];
+ // handle possible reprocessing
+ while (pslotAssoc && pslotAssoc->PassModified() == m_ipassModified)
+ pslotAssoc = pslotAssoc->m_pslotPrevState;
+ return pslotAssoc;
+ }
+
+ 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;
+ }
+
+ bool HasComponents() { return m_fHasComponents; }
+
+ // Directionality as determined by the bidi algorithm
+ DirCode DirProcessed() // return the value
+ {
+ Assert(m_dirc != kNotYetSet8);
+ 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;
+ }
+
+ int RawAdvanceX() { return m_mAdvanceX; }
+ int RawAdvanceY() { return m_mAdvanceY; }
+ int ShiftX() { return m_mShiftX; }
+ int ShiftY() { return m_mShiftY; }
+
+ // 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 = sdata8(lb); }
+ void SetInsertBefore(bool f) { m_fInsertBefore = f; }
+
+ void SetDirectionality(DirCode dirc) { m_dirc = sdata8(dirc); }
+ void SetDirLevel(int n) { m_nDirLevel = sdata8(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();
+
+ // Cache of FSM column information for the most recent pass:
+ int FsmColumn() { return m_colFsm; }
+ int PassNumberForColumn() { return m_ipassFsmCol; }
+ void CacheFsmColumn(int ipass, int col)
+ {
+ m_colFsm = col;
+ m_ipassFsmCol = ipass;
+ }
+
+ // 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
+
+ // user-defined slot attributes
+ 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;
+ }
+
+ // pointer to the associated slot which is the value of the comp.ref attribute
+ 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;
+ }
+
+ // global component identifier
+ 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;
+ }
+
+ // feature settings
+ 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;
+ }
+
+ GrSlotState * CompRefSlot(int i);
+
+ 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;
+ }
+
+ // For transduction logging:
+#ifdef TRACING
+ void SlotAttrsModified(bool * rgfMods, bool fPreJust, int * pccomp, int * pcassoc);
+ void LogSlotAttributeValue(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);
+ void LogXmlAttributes(std::ostream & strmOut, GrTableManager * ptman, GrSlotStream * psstrmOut,
+ int ipass, int islot,
+ bool fPreJust, bool fPostJust, bool fBidi, bool fBidiNext, int nIndent);
+ int GetSlotAttrValue(std::ostream & strmOut, GrTableManager * ptman,
+ int ipass, int slat, int iIndex, bool fPreJust, bool fPostJust);
+ 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_islotPosPass; // index of slot in positioning streams, relative to first official
+ // slot in the segment (possibly the LB slot)
+
+ int m_colFsm; // which FSM column this glyph corresponds to...
+ int m_ipassFsmCol; // ...for the most recent pass
+
+ 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
+
+ int m_nUnicode; // for debugging
+
+ DirCode m_dircProc; // directionality as processed in bidi algorithm
+ bool m_fDirProcessed;
+
+ // affects length of variable-length buffer
+ byte m_cnUserDefn;
+ byte m_cnFeat;
+
+ byte m_bStyleIndex;
+
+ // 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;
+
+ // 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;
+
+ // 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;
+
+ bool m_fHasComponents; // default = false
+
+ // Private methods:
+
+ void CopyAbstractFrom(GrSlotState * pslot);
+
+ 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);
+ }
+ }
+
+public: // for transduction logging
+ bool m_fAdvXSet;
+ bool m_fAdvYSet;
+
+}; // 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;
+ friend class SegmentMemoryUsage;
+
+public:
+ GrSlotOutput()
+ {
+ m_cComponents = 0;
+ m_isloutClusterBase = -1; // not part of any cluster
+ m_disloutCluster = 0;
+ m_igbb = -1;
+ }
+
+ void ExactCopyFrom(GrSlotOutput * pslout, u_intslot * pnVarLenBuf, int cnExtraPerSlot);
+
+ void InitializeOutputFrom(GrSlotState * pslot);
+
+ 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
+ {
+ m_prgnVarLenBuf[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_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);
+ return m_prgnVarLenBuf[iComp].nValue;
+ }
+ int ComponentId(int iComp)
+ {
+ return m_prgnVarLenBuf[m_cnCompPerLig + iComp].nValue;
+ }
+
+ //u_intslot * PCompRefBufSlout()
+ //{
+ // return m_prgnVarLenBuf;
+ //}
+
+ int CExtraSpaceSlout()
+ {
+ return (m_cnCompPerLig * 2);
+ }
+
+ 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 isloutThis, int isloutAttached)
+ {
+ m_disloutCluster = sdata8(max(int(m_disloutCluster), abs(isloutThis - isloutAttached)));
+ }
+
+ void ClusterMembers(Segment * pseg, int islout, std::vector<int> & visloutRet);
+
+ int ClusterRange()
+ {
+ return m_disloutCluster;
+ }
+
+ 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; }
+
+ Rect BoundingBox(Font & font)
+ {
+ Rect rectBB;
+ rectBB.left = m_xsPositionX + GlyphMetricLogUnits(&font, kgmetBbLeft);
+ if (IsSpace())
+ rectBB.right = m_xsPositionX + GlyphMetricLogUnits(&font, kgmetAdvWidth);
+ else
+ rectBB.right = m_xsPositionX + GlyphMetricLogUnits(&font, kgmetBbRight);
+ rectBB.top = m_ysPositionY + GlyphMetricLogUnits(&font, kgmetBbTop);
+ rectBB.bottom = m_ysPositionY + GlyphMetricLogUnits(&font, kgmetBbBottom);
+ return rectBB;
+ }
+
+ bool IsLineBreak()
+ {
+ return (IsInitialLineBreak() || IsFinalLineBreak());
+ }
+ bool IsInitialLineBreak()
+ {
+ return (m_spsl == kspslLbInitial);
+ }
+ bool IsFinalLineBreak()
+ {
+ return (m_spsl == kspslLbFinal);
+ }
+
+ //float AdvanceXMetric()
+ //{
+ // return m_xysAdvX;
+ //}
+
+ bool IsSpace();
+
+ 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:
+
+ sdata8 m_cComponents;
+
+ data8 m_disloutCluster; // how far to search on either side of this glyph to find
+ // other members of the cluster;
+ // 0 means there are no cluster members or this glyph is
+ // attached to some other base
+
+ 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
+
+ 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,
+ // in which case value is infinity
+
+ // 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; -- not used
+// Rect m_rectBB;
+
+}; // end of class GrSlotOutput
+
+} // namespace gr
+
+
+#endif // !SLOTSTATE_INCLUDED
diff --git a/Build/source/libs/graphite/src/segment/GrSlotStream.cpp b/Build/source/libs/graphite/src/segment/GrSlotStream.cpp
new file mode 100644
index 00000000000..db6a5c8af7e
--- /dev/null
+++ b/Build/source/libs/graphite/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++)
+ {
+ int islotTmp = vislotTrailingWS[iislot];
+ m_vpslot[islotTmp]->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 = sdata8(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/src/segment/GrSlotStream.h b/Build/source/libs/graphite/src/segment/GrSlotStream.h
new file mode 100644
index 00000000000..006191252dc
--- /dev/null
+++ b/Build/source/libs/graphite/src/segment/GrSlotStream.h
@@ -0,0 +1,577 @@
+/*--------------------------------------------------------------------*//*: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;
+ friend class FontMemoryUsage;
+
+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/src/segment/GrTableManager.cpp b/Build/source/libs/graphite/src/segment/GrTableManager.cpp
new file mode 100644
index 00000000000..fde364e7e2e
--- /dev/null
+++ b/Build/source/libs/graphite/src/segment/GrTableManager.cpp
@@ -0,0 +1,2691 @@
+/*--------------------------------------------------------------------*//*: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 = NULL;
+ 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, pfont, pchstrm);
+ 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);
+ }
+
+ SetFinalPositions(psegNew, fWidthIsCharCount);
+
+ // Do this before fixing up the associations below, so we can see the raw ones produced by the
+ // rules themselves.
+ //if (m_pgreng->LoggingTransduction())
+ // WriteTransductionLog(pchstrm, *ppsegRet,
+ // cbPrev, pbPrevSegDat, pbNextSegDat, pcbNextSegDat);
+ WriteTransductionLog(pstrmLog, pchstrm, psegNew, cbPrev, pbPrevSegDat);
+
+ RecordAssocsAndOutput(pfont, psegNew, 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, pfont, pchstrm);
+ }
+ return;
+ }
+
+ Assert(psegNew);
+
+ psegNew->Initialize(pchstrm->TextSrc(),
+ 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, Font * pfont,
+ GrCharStream * pchstrm)
+{
+ psegNew->Initialize(pchstrm->TextSrc(), 0, 0,
+ klbClipBreak, klbClipBreak, kestNothingFit, false, false,
+ 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());
+ psegNew->SetPreContext(0);
+}
+
+/*----------------------------------------------------------------------------------------------
+ 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->TextSrc(), 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.
+ 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
+ }
+ if (ichwCallerBtLim > -1 && islotStartTry > ichwCallerBtLim - 1)
+ {
+ islotStartTry = ichwCallerBtLim - 1;
+ }
+
+ // 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 = -1;
+ 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 cbNextSegDat;
+ int * pcbNextSegDat = &cbNextSegDat;
+
+ std::vector<int> vcslotSkipOffsets;
+ vcslotSkipOffsets.resize(m_cpass);
+
+ 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 = 0; // corresponding slot in the previous stream for rule-start position
+ int islotPrevPC = 0; // 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);
+}
+/*----------------------------------------------------------------------------------------------
+ Make sure the final positions are set for every glyph.
+----------------------------------------------------------------------------------------------*/
+void GrTableManager::SetFinalPositions(Segment * pseg, bool fWidthIsCharCount)
+{
+ 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);
+}
+
+/*----------------------------------------------------------------------------------------------
+ Calculate the associations, and record the output slots in the segment.
+----------------------------------------------------------------------------------------------*/
+void GrTableManager::RecordAssocsAndOutput(Font * pfont, Segment * pseg,
+ 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());
+
+ 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->CleanUpAssocsVectors();
+
+// 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)->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 = 0;
+ 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 = pslot->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/src/segment/GrTableManager.h b/Build/source/libs/graphite/src/segment/GrTableManager.h
new file mode 100644
index 00000000000..e0306c832c7
--- /dev/null
+++ b/Build/source/libs/graphite/src/segment/GrTableManager.h
@@ -0,0 +1,583 @@
+/*--------------------------------------------------------------------*//*:Ignore this sentence.
+Copyright (C) 1999 - 2008 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;
+ friend class FontMemoryUsage;
+
+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 {
+ friend class FontMemoryUsage;
+
+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(std::ostream * pstrmLog,
+ GrCharStream * pchstrm, Segment * psegRet, int cbPrevSetDat, byte * pbPrevSegDat);
+ bool WriteAssociationLog(std::ostream * pstrmLog,
+ GrCharStream * pchstrm, Segment * psegRet);
+ bool WriteXmlLog(std::ostream * pstrmLog,
+ GrCharStream * pchstrm, Segment * psegRet, int cbPrevSegDat, byte * pbPrevSegDat);
+ bool WriteXmlAssocLog(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);
+
+ void WriteXmlLogAux(std::ostream & strmOut,
+ GrCharStream * pchstrm, Segment * psegRet, int cbPrevSegDat, byte * pbPrevSegDat);
+ void LogXmlUnderlying(std::ostream & strmOut, GrCharStream * pchstrm, int cchwBackup, size_t nIndent);
+ void LogXmlUnderlyingAux(std::ostream & strmOut, GrCharStream * pchstrm,
+ int cch32Backup, int cch32Lim, size_t nIndent,
+ bool fLogText, bool fLogFeatures, bool fLogColor, bool fLogStrOff, bool fLogLig, bool fLogGlyphs);
+ void LogXmlPass(std::ostream & strmOut, int ipass, int cslotSkipped, int nIndent);
+
+ void LogXmlTagOpen(std::ostream & strmOut, std::string strTag, size_t nIndent, bool fContent);
+ void LogXmlTagPostAttrs(std::ostream & strmOut, bool fContent);
+ void LogXmlTagClose(std::ostream & strmOut, std::string strTag, size_t nIndent, bool fContent);
+ void LogXmlTagAttr(std::ostream & strmOut, std::string strAttr, int nValue, size_t nIndent = 0);
+ void LogXmlTagAttr(std::ostream & strmOut, std::string strAttr, const char * szValue, size_t nIndent = 0);
+ void LogXmlTagAttrHex(std::ostream & strmOut, std::string strAttr, int nValue, size_t nIndent = 0);
+ std::string HexString(int n1, int n2, int n3, int n4, int n5, int n6);
+ std::string HexString(std::vector<int>);
+ void LogXmlTagColor(std::ostream & strmOut, std::string strAttr, int clrValue, bool fBack,
+ size_t nIndent = 0);
+ void LogXmlDirCode(std::ostream & strmOut, std::string strAttr, int dircValue, size_t nIndent = 0);
+ void LogXmlBreakWeight(std::ostream & strmOut, std::string strAttr, int dircValue,
+ size_t nIndent = 0);
+ void LogXmlComment(std::ostream & strmOut, std::string strComment, size_t nIndent = 0);
+
+#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, Font * pfont, GrCharStream * pchstrm);
+
+ 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 SetFinalPositions(Segment * pseg, bool fWidthIsCharCount);
+ void RecordAssocsAndOutput(Font * pfont, Segment * pseg,
+ 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/src/segment/Main.h b/Build/source/libs/graphite/src/segment/Main.h
new file mode 100644
index 00000000000..da95af09ce8
--- /dev/null
+++ b/Build/source/libs/graphite/src/segment/Main.h
@@ -0,0 +1,181 @@
+/*--------------------------------------------------------------------*//*: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:
+#ifdef _MSC_VER
+#define _CRT_SECURE_NO_DEPRECATE
+#pragma warning(disable: 4996) // warning: function was declared deprecated
+#pragma warning(disable: 4702) // unreachable code
+#endif
+
+#include "GrCommon.h"
+
+//:>********************************************************************************************
+//:> Interfaces.
+//:>********************************************************************************************
+#include "GrData.h"
+
+//:>********************************************************************************************
+//:> Implementations.
+//:>********************************************************************************************
+
+#ifndef _WIN32
+#include "GrMstypes.h"
+#ifndef HAVE_FABSF
+float fabsf(float x);
+#endif
+#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/src/segment/Makefile.am b/Build/source/libs/graphite/src/segment/Makefile.am
new file mode 100644
index 00000000000..59113aa924b
--- /dev/null
+++ b/Build/source/libs/graphite/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)/MemoryUsage.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
+
diff --git a/Build/source/libs/graphite/src/segment/MemoryUsage.cpp b/Build/source/libs/graphite/src/segment/MemoryUsage.cpp
new file mode 100644
index 00000000000..1fb3fc25282
--- /dev/null
+++ b/Build/source/libs/graphite/src/segment/MemoryUsage.cpp
@@ -0,0 +1,781 @@
+/*--------------------------------------------------------------------*//*: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: MemoryUsage.cpp
+Responsibility: Sharon Correll
+Last reviewed: Not yet.
+
+Description:
+ Calculates memory usage for the engine and segments.
+----------------------------------------------------------------------------------------------*/
+
+#pragma warning(disable: 4244) // conversion from wchar_t to char
+#pragma warning(disable: 4702) // unreachable code
+
+//:>********************************************************************************************
+//:> Include files
+//:>********************************************************************************************
+
+#include "GrCommon.h"
+#include "GrData.h"
+#ifndef _WIN32
+#include "GrMstypes.h"
+#endif
+#include "GrDebug.h"
+//#include <fstream>
+#include <iostream>
+#include <cstring>
+//#include <string>
+// Forward declarations.
+
+#include "Main.h"
+
+//namespace gr
+//{
+// class GrTableManager;
+// class Segment;
+// class GrEngine;
+//}
+//
+//#include "GrTableManager.h"
+//#include "GrFeatureValues.h"
+//#include "GrSlotState.h"
+//#include "SegmentAux.h"
+//#include "Segment.h"
+#include "MemoryUsage.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
+//:>********************************************************************************************
+
+const int stringOverhead = (3 * sizeof(int)) + (sizeof(void *)) + (8 * sizeof(short));
+const int vectorOverhead = 3 * sizeof(void *);
+
+namespace gr
+{
+
+//:>********************************************************************************************
+//:> Font/engine
+//:>********************************************************************************************
+
+void FontMemoryUsage::initialize()
+{
+ font = 0;
+ fontCache = 0;
+ fontFace = 0;
+
+ eng_count = 0;
+ eng_overhead = 0;
+ eng_scalars = 0;
+ eng_strings = 0;
+ eng_pointers = 0;
+ eng_cmap = 0;
+ eng_nameTable = 0;
+
+ pseudoMap = 0;
+
+ clstbl_counters = 0;
+ clstbl_offsets = 0;
+ clstbl_glyphList = 0;
+
+ glftbl_general = 0;
+ glftbl_compDefns = 0;
+ glftbl_attrTable = 0;
+ glftbl_attrOffsets = 0;
+
+ lngtbl_general = 0;
+ lngtbl_entries = 0;
+ lngtbl_featureSets = 0;
+
+ tman_general = 0;
+
+ pass_count = 0;
+ pass_general = 0;
+ pass_fsm = 0;
+ pass_ruleExtras = 0;
+ pass_constraintOffsets = 0;
+ pass_constraintCode = 0;
+ pass_actionOffsets = 0;
+ pass_actionCode = 0;
+
+ engst_general = 0;
+ engst_passState = 0;
+
+ sstrm_count = 0;
+ sstrm_general = 0;
+ sstrm_chunkMapsUsed = 0;
+ sstrm_chunkMapsAlloc = 0;
+ sstrm_reprocBuf = 0;
+
+ slot_count = 0;
+ slot_general = 0;
+ slot_abstract = 0;
+ slot_varLenBuf = 0;
+ slot_assocsUsed = 0;
+ slot_assocsAlloc = 0;
+ slot_attachUsed = 0;
+ slot_attachAlloc = 0;
+}
+
+/*----------------------------------------------------------------------------------------------
+ Return the total byte count.
+----------------------------------------------------------------------------------------------*/
+int FontMemoryUsage::total()
+{
+ int grandTotal = 0;
+ grandTotal += font + fontCache + fontFace;
+ grandTotal += eng_overhead + eng_scalars + eng_strings + eng_pointers + eng_cmap + eng_nameTable;
+ grandTotal += pseudoMap;
+ grandTotal += clstbl_counters + clstbl_offsets + clstbl_glyphList;
+ grandTotal += glftbl_general + glftbl_compDefns + glftbl_attrTable + glftbl_attrOffsets;
+ grandTotal += lngtbl_general + lngtbl_entries + lngtbl_featureSets;
+ grandTotal += tman_general;
+ grandTotal += pass_general + pass_fsm + pass_ruleExtras
+ + pass_constraintOffsets + pass_constraintCode + pass_actionOffsets + pass_actionCode;
+ grandTotal += engst_general + engst_passState;
+ grandTotal += sstrm_general + sstrm_chunkMapsAlloc + sstrm_reprocBuf;
+ grandTotal += slot_general + slot_abstract + slot_varLenBuf + slot_assocsAlloc + slot_attachAlloc;
+
+ return grandTotal;
+}
+
+/*----------------------------------------------------------------------------------------------
+ Add the results of two runs together.
+----------------------------------------------------------------------------------------------*/
+void FontMemoryUsage::add(FontMemoryUsage & fmu)
+{
+ font += fmu.font;
+ fontCache += fmu.fontCache;
+ fontFace += fmu.fontFace;
+
+ eng_count += fmu.eng_count;
+ eng_overhead += fmu.eng_overhead;
+ eng_scalars += fmu.eng_scalars;
+ eng_strings += fmu.eng_strings;
+ eng_pointers += fmu.eng_pointers;
+ eng_cmap += fmu.eng_cmap;
+ eng_nameTable += fmu.eng_nameTable;
+
+ pseudoMap += fmu.pseudoMap;
+
+ clstbl_counters += fmu.clstbl_counters;
+ clstbl_offsets += fmu.clstbl_offsets;
+ clstbl_glyphList += fmu.clstbl_glyphList;
+
+ glftbl_general += fmu.glftbl_general;
+ glftbl_compDefns += fmu.glftbl_compDefns;
+ glftbl_attrTable += fmu.glftbl_attrTable;
+ glftbl_attrOffsets += fmu.glftbl_attrOffsets;
+
+ lngtbl_general += fmu.lngtbl_general;
+ lngtbl_entries += fmu.lngtbl_entries;
+ lngtbl_featureSets += fmu.lngtbl_featureSets;
+
+ tman_general += fmu.tman_general;
+
+ pass_count += fmu.pass_count;
+ pass_general += fmu.pass_general;
+ pass_fsm += fmu.pass_fsm;
+ pass_ruleExtras += fmu.pass_ruleExtras;
+ pass_constraintOffsets += fmu.pass_constraintOffsets;
+ pass_constraintCode += fmu.pass_constraintCode;
+ pass_actionOffsets += fmu.pass_actionOffsets;
+ pass_actionCode += fmu.pass_actionCode;
+
+ engst_general += fmu.engst_general;
+ engst_passState += fmu.engst_passState;
+
+ sstrm_count += fmu.sstrm_count;
+ sstrm_general += fmu.sstrm_general;
+ sstrm_chunkMapsUsed += fmu.sstrm_chunkMapsUsed;
+ sstrm_chunkMapsAlloc += fmu.sstrm_chunkMapsAlloc;
+ sstrm_reprocBuf += fmu.sstrm_reprocBuf;
+
+ slot_count += fmu.slot_count;
+ slot_general += fmu.slot_general;
+ slot_abstract += fmu.slot_abstract;
+ slot_varLenBuf += fmu.slot_varLenBuf;
+ slot_assocsUsed += fmu.slot_assocsUsed;
+ slot_assocsAlloc += fmu.slot_assocsAlloc;
+ slot_attachUsed += fmu.slot_attachUsed;
+ slot_attachAlloc += fmu.slot_attachAlloc;
+}
+
+/*----------------------------------------------------------------------------------------------
+ Add up the memory used by all the engines in the cache.
+----------------------------------------------------------------------------------------------*/
+FontMemoryUsage Font::calculateMemoryUsage()
+{
+ FontMemoryUsage fmu;
+ FontFace::calculateAllMemoryUsage(fmu);
+ return fmu;
+}
+
+void FontFace::calculateAllMemoryUsage(FontMemoryUsage & fmu)
+{
+ s_pFontCache->calculateMemoryUsage(fmu);
+}
+
+void FontCache::calculateMemoryUsage(FontMemoryUsage & fmuTotal)
+{
+ fmuTotal.fontCache += sizeof(int) * 4; // m_cfci, m_cfciMax, m_cfface, m_flush
+ fmuTotal.fontCache += sizeof(void*); // m_prgfci;
+
+ // Iterate through all the font faces in the cache.
+ for (int ifci = 0; ifci < m_cfci; ifci++)
+ {
+ CacheItem * pfci = m_prgfci + ifci;
+ char rgchFontName[32];
+ memset(rgchFontName, 0, 32 * sizeof(char));
+ std::copy(pfci->szFaceName, pfci->szFaceName + 32, rgchFontName);
+ std::string strFontName(rgchFontName);
+ fmuTotal.vstrFontNames.push_back(strFontName);
+ int cface = 0;
+ if (pfci->pffaceRegular)
+ {
+ FontMemoryUsage fmu;
+ pfci->pffaceRegular->calculateMemoryUsage(fmu);
+ fmuTotal.add(fmu);
+ fmuTotal.vFontTotalsReg.push_back(fmu.total());
+ cface++;
+ }
+ else
+ fmuTotal.vFontTotalsReg.push_back(0);
+
+ if (pfci->pffaceBold)
+ {
+ FontMemoryUsage fmu;
+ pfci->pffaceBold->calculateMemoryUsage(fmu);
+ fmuTotal.add(fmu);
+ fmuTotal.vFontTotalsBold.push_back(fmu.total());
+ cface++;
+ }
+ else
+ fmuTotal.vFontTotalsBold.push_back(0);
+
+ if (pfci->pffaceItalic)
+ {
+ FontMemoryUsage fmu;
+ pfci->pffaceItalic->calculateMemoryUsage(fmu);
+ fmuTotal.add(fmu);
+ fmuTotal.vFontTotalsItalic.push_back(fmu.total());
+ cface++;
+ }
+ else
+ fmuTotal.vFontTotalsItalic.push_back(0);
+
+ if (pfci->pffaceBI)
+ {
+ FontMemoryUsage fmu;
+ pfci->pffaceBI->calculateMemoryUsage(fmu);
+ fmuTotal.add(fmu);
+ fmuTotal.vFontTotalsBI.push_back(fmu.total());
+ cface++;
+ }
+ else
+ fmuTotal.vFontTotalsBI.push_back(0);
+
+ fmuTotal.vFaceCount.push_back(cface);
+ }
+}
+
+void FontFace::calculateMemoryUsage(FontMemoryUsage & fmu)
+{
+ fmu.fontFace += sizeof(int); // m_cfonts
+ fmu.fontFace += sizeof(void *) * 2; // s_pFontCache, m_pgreng
+
+ fmu.font += m_cfonts * sizeof(void*); // m_pfface
+
+ fmu.addEngine(m_pgreng);
+}
+
+void FontMemoryUsage::addEngine(GrEngine * pgreng)
+{
+ eng_count++;
+
+ eng_overhead += sizeof(long); // m_cref
+ eng_scalars += sizeof(bool) * 6; // m_fBold, m_fItalic, m_fSmart...
+ eng_scalars += sizeof(bool) * 2; // m_fFakeItalicCache, m_fFakeBICache;
+ eng_strings += stringOverhead * 4; // m_strCtrlFileReg/Bold/Italic/BI
+ eng_strings += sizeof(wchar_t) * pgreng->m_strCtrlFileReg.length();
+ eng_strings += sizeof(wchar_t) * pgreng->m_strCtrlFileBold.length();
+ eng_strings += sizeof(wchar_t) * pgreng->m_strCtrlFileItalic.length();
+ eng_strings += sizeof(wchar_t) * pgreng->m_strCtrlFileBI.length();
+ eng_scalars += sizeof(bool);
+ eng_strings += stringOverhead * 6; // m_stuCtrlFile etc.
+ eng_strings += sizeof(wchar_t) * pgreng->m_stuCtrlFile.length();
+ eng_strings += sizeof(wchar_t) * pgreng->m_stuInitError.length();
+ eng_strings += sizeof(wchar_t) * pgreng->m_stuFaceName.length();
+ eng_strings += sizeof(wchar_t) * pgreng->m_stuFeatures.length();
+ eng_strings += sizeof(wchar_t) * pgreng->m_stuBaseFaceName.length();
+ eng_scalars += sizeof(bool); // m_fUseSepBase
+ eng_scalars += sizeof(GrResult) * 2; // m_resFontRead, m_resFontValid
+ eng_scalars += sizeof(FontErrorCode);
+ eng_scalars += sizeof(int) * 3; // m_fxdBadVersion, m_nFontCheckSum, m_nScriptTag, m_grfsdc
+ eng_scalars += sizeof(bool); // m_fRightToLeft
+ eng_scalars += sizeof(int) * 2; // m_mXAscent, m_mXDescent
+ eng_scalars += sizeof(float); // m_dysOffset
+ eng_scalars += sizeof(bool); // m_fBasicJust
+ eng_scalars += sizeof(int); // m_cJLevels
+ eng_pointers += sizeof(void*) * 4; // m_ptman, m_pctbl, m_pgtbl, m_pfface
+ eng_scalars += sizeof(GrFeature) * kMaxFeatures;
+ eng_scalars += sizeof(int); // m_cfeat
+ eng_scalars += sizeof(size_t); // m_clcidFeatLabelLangs
+ eng_scalars += sizeof(short) * pgreng->m_clcidFeatLabelLangs;
+ // m_langtbl
+ eng_scalars += sizeof(bool); // m_fLineBreak
+ eng_scalars += sizeof(int) * 2; // m_cchwPreXlbContext, m_cchwPostXlbContext
+ eng_scalars += sizeof(data16) * 7; // m_chwPseudoAttr, etc.
+ eng_scalars += sizeof(gid16); // m_chwLBGlyphID
+ eng_scalars += sizeof(int) * 4; // m_cComponents, m_cnUserDefn, m_cnCompPerLig, m_mFontEmUnits
+
+ pseudoMap += sizeof(int) * 3;
+ pseudoMap += sizeof(GrPseudoMap) * pgreng->m_cpsd;
+
+ eng_cmap += sizeof(void*) * 2;
+ eng_cmap += sizeof(byte*);
+ eng_cmap += sizeof(bool);
+ if (pgreng->m_fCmapTblCopy)
+ eng_cmap += pgreng->m_cbCmapTbl;
+
+ eng_nameTable += sizeof(byte *);
+ eng_nameTable += sizeof(bool);
+ if (pgreng->m_fNameTblCopy)
+ eng_nameTable += pgreng->m_cbNameTbl;
+
+ eng_scalars += sizeof(bool) * 2; // m_fLogXductn, m_fInErrorState
+
+ clstbl_counters += sizeof(int) * 2; // m_ccls, m_cclsLinear
+ clstbl_offsets += sizeof(data16) * (pgreng->m_pctbl->m_ccls + 1);
+ clstbl_glyphList += sizeof(data16) * (pgreng->m_pctbl->m_prgichwOffsets[pgreng->m_pctbl->m_ccls]);
+
+ glftbl_general += sizeof(int) * 3; // m_cglf, m_cComponents, m_cgstbl
+ glftbl_general += vectorOverhead; // m_vpgstbl
+ GrGlyphSubTable * pgstbl = pgreng->m_pgtbl->m_vpgstbl[0];
+ glftbl_general += sizeof(int) * 4; // m_fxdSilfVersion, m_nAttrIDLim, m_cComponents, m_cnCompPerLig
+ glftbl_general += sizeof(bool) * 2; // m_fHasDebugStrings, m_fGlocShort
+ glftbl_general += sizeof(data16) * 3; // m_chw...Attr
+ glftbl_compDefns += sizeof(int) * (pgstbl->m_cnCompPerLig + 1) * pgreng->m_pgtbl->m_cglf;
+ glftbl_attrTable += sizeof(int) * 2; // m_fxdSilfVersion, m_cbEntryBufLen
+ glftbl_attrTable += sizeof(byte) * pgstbl->m_pgatbl->m_cbEntryBufLen;
+ glftbl_attrOffsets +=
+ (pgreng->m_pgtbl->m_cglf + 1) * ((pgstbl->m_fGlocShort) ? sizeof(data16) : sizeof(data32));
+
+ lngtbl_general += sizeof(size_t); // m_clang
+ lngtbl_general += sizeof(data16) * 3; // m_dilangInit, m_cLoop, m_ilangStart
+ lngtbl_general += sizeof(void*) * 2; // m_prglang, m_prgfset
+ lngtbl_general += sizeof(int); // m_cbOffset0
+ lngtbl_entries += sizeof(GrLangTable::LangEntry) * pgreng->m_langtbl.m_clang;
+ lngtbl_featureSets += sizeof(GrLangTable::FeatSet) * pgreng->m_langtbl.m_cfset;
+
+ GrTableManager * ptman = pgreng->m_ptman;
+ tman_general += sizeof(int) * 4; // m_cpass, m_cpassLB, m_ipassPos1, m_ipassJust1
+ tman_general += sizeof(bool); // m_fBidi
+ tman_general += sizeof(void*) * 2; // m_prgppass, m_pgreng
+ tman_general += sizeof(void*) * ptman->m_cpass; // m_prgppass
+ tman_general += sizeof(bool); // m_fLogging
+
+ pass_count += ptman->m_cpass;
+ for (int ipass = 0; ipass < ptman->m_cpass; ipass++)
+ {
+ GrPass * ppass = ptman->m_prgppass[ipass];
+ pass_general = sizeof(GrPass);
+
+ GrFSM * pfsm = ppass->m_pfsm;
+ if (pfsm)
+ {
+ pass_fsm += sizeof(int) * 5; // m_crow, m_crowFinal, m_rowFinalMin, m_crowNonAcpt, m_ccol
+ pass_fsm += sizeof(void*) * 3; // m_prgirulnMin, m_prgrulnMatched, m_prgrowTransitions
+ int crowSuccess = pfsm->m_crow - pfsm->m_crowNonAcpt;
+ pass_fsm += sizeof(data16) * (crowSuccess + 1); // m_prgirulnMin
+ pass_fsm += sizeof(data16) * (pfsm->m_crulnMatched); // m_prgrulnMatched
+ pass_fsm += sizeof(short) * ((pfsm->m_crow - pfsm->m_crowFinal) * pfsm->m_ccol);
+ pass_fsm += sizeof(data16); // m_dimcrInit, m_cLoop, m_imcrStart
+ pass_fsm += sizeof(int); // m_cmcr
+ pass_fsm += sizeof(void*); // m_prgmcr
+ pass_fsm += sizeof(GrFSMClassRange) * pfsm->m_cmcr;
+ pass_fsm += sizeof(int) * 2; // m_critMinRulePreContext, m_critMaxRulePreContext
+ pass_fsm += sizeof(void*); // m_prgrowStartStates
+ pass_fsm += sizeof(short) * (pfsm->m_critMaxRulePreContext - pfsm->m_critMinRulePreContext + 1);
+ }
+
+ pass_ruleExtras = sizeof(data16) * ppass->m_crul; // m_prgchwRuleSortKeys
+ pass_ruleExtras = sizeof(byte*) * ppass->m_crul; // m_prgcritRulePreModContext
+
+ pass_constraintOffsets += sizeof(data16) * (ppass->m_crul + 1);
+ pass_actionOffsets =+ sizeof(data16) * (ppass->m_crul + 1);
+
+ pass_constraintCode += sizeof(byte) * ppass->m_cbConstraints;
+ pass_actionCode += sizeof(byte) * ppass->m_cbActions;
+
+ pass_general += sizeof(bool) * ppass->m_crul; // m_prgfRuleOkay
+ pass_general += vectorOverhead; // m_vnStack
+ pass_general += sizeof(int) * ppass->m_vnStack.capacity();
+ }
+
+ engst_general += sizeof(EngineState);
+ engst_general += sizeof(void *) * ptman->m_engst.m_vslotblk.capacity();
+ engst_general += sizeof(void *) * ptman->m_engst.m_vprgnSlotVarLenBufs.capacity();
+ engst_general += sizeof(void *) * ptman->m_engst.m_cpass; // m_prgpsstrm
+
+ engst_passState += sizeof(PassState) * ptman->m_engst.m_cpass;
+
+ for (int isstrm = 0; isstrm < ptman->m_engst.m_cpass; isstrm++)
+ {
+ sstrm_count++;
+ GrSlotStream * psstrm = ptman->m_engst.m_prgpsstrm[isstrm];
+ sstrm_general += sizeof(GrSlotStream);
+ sstrm_chunkMapsUsed += psstrm->m_vislotPrevChunkMap.size();
+ sstrm_chunkMapsUsed += psstrm->m_vislotPrevChunkMap.size();
+ sstrm_chunkMapsAlloc += psstrm->m_vislotPrevChunkMap.capacity();
+ sstrm_chunkMapsAlloc += psstrm->m_vislotNextChunkMap.capacity();
+ sstrm_reprocBuf += psstrm->m_vpslotReproc.capacity();
+ sstrm_reprocBuf += sizeof(int) * 2;
+ sstrm_general -= sizeof(int) * 2;
+ }
+
+ slot_general += ptman->m_engst.m_vslotblk.size() * EngineState::kSlotBlockSize
+ * (sizeof(GrSlotState) - sizeof(GrSlotAbstract));
+ slot_abstract += ptman->m_engst.m_vslotblk.size() * EngineState::kSlotBlockSize
+ * sizeof(GrSlotAbstract);
+ int cnExtraPerSlot = ptman->m_engst.m_cUserDefn + (ptman->m_engst.m_cCompPerLig * 2)
+ + ptman->m_engst.m_cFeat;
+ slot_varLenBuf += EngineState::kSlotBlockSize * cnExtraPerSlot * sizeof(u_intslot);
+ for (size_t islotblk = 0; islotblk < ptman->m_engst.m_vslotblk.size(); islotblk++)
+ {
+ for (int islot = 0; islot < EngineState::kSlotBlockSize; islot++)
+ {
+ slot_count++;
+ GrSlotState * pslot = ptman->m_engst.m_vslotblk[islotblk] + islot;
+ slot_assocsUsed += pslot->m_vpslotAssoc.size();
+ slot_assocsAlloc += pslot->m_vpslotAssoc.capacity();
+ slot_attachUsed += pslot->m_vdislotAttLeaves.size();
+ slot_attachAlloc += pslot->m_vdislotAttLeaves.capacity();
+ }
+ }
+
+}
+
+/*----------------------------------------------------------------------------------------------
+ Write out the memory usage onto a stream.
+----------------------------------------------------------------------------------------------*/
+void FontMemoryUsage::prettyPrint(std::ostream & strm)
+{
+ int totalBytes = total();
+
+ strm << "Number of engines: " << eng_count << "\n";
+ strm << "Number of passes: " << pass_count << "\n\n";
+
+ strm << "Number of slot streams: " << sstrm_count << "\n";
+ strm << "Number of slots: " << slot_count << "\n\n";
+
+ int classTableTotal = clstbl_counters + clstbl_offsets + clstbl_glyphList;
+ int glyphTableTotal = glftbl_general + glftbl_compDefns + glftbl_attrTable + glftbl_attrOffsets;
+ int langTableTotal = lngtbl_general + lngtbl_entries + lngtbl_featureSets;
+ int passTotal = pass_general + pass_fsm + pass_ruleExtras + pass_constraintOffsets + pass_constraintCode
+ + pass_actionOffsets + pass_actionCode;
+ int engineTotal = eng_overhead + eng_scalars + eng_strings + eng_pointers + eng_cmap + eng_nameTable
+ + pseudoMap + classTableTotal + glyphTableTotal + langTableTotal + tman_general
+ + passTotal;
+
+ strm << "BYTE COUNT TOTALS\n";
+ strm << "Font: " << font << "\n";
+ strm << "Font cache: " << fontCache << "\n";
+ strm << "Font face: " << fontFace << "\n";
+ strm << "Engine " << engineTotal << "\n";
+ strm << " Overhead: " << eng_overhead << "\n";
+ strm << " Scalars: " << eng_scalars << "\n";
+ strm << " Strings: " << eng_strings << "\n";
+ strm << " Pointers: " << eng_pointers << "\n";
+ strm << " Cmap table: " << eng_cmap << "\n";
+ strm << " Name table: " << eng_nameTable << "\n";
+ strm << " Pseudo map: " << pseudoMap << "\n";
+ strm << " Class table: " << classTableTotal << "\n";
+ strm << " Counters: " << clstbl_counters << "\n";
+ strm << " Offsets: " << clstbl_offsets << "\n";
+ strm << " Glyph list: " << clstbl_glyphList << "\n";
+ strm << " Glyph table: " << glyphTableTotal << "\n";
+ strm << " General: " << glftbl_general << "\n";
+ strm << " Component defns: " << glftbl_compDefns << "\n";
+ strm << " Attr table: " << glftbl_attrTable << "\n";
+ strm << " Attr offsets: " << glftbl_attrOffsets << "\n";
+ strm << " Language table: " << langTableTotal << "\n";
+ strm << " General: " << lngtbl_general << "\n";
+ strm << " Entries: " << lngtbl_entries << "\n";
+ strm << " Feature sets: " << lngtbl_featureSets << "\n";
+ strm << " Table manager: " << tman_general << "\n";
+ strm << " Passes: " << passTotal << "\n";
+ strm << " General: " << pass_general << "\n";
+ strm << " FSM: " << pass_fsm << "\n";
+ strm << " Rule extras: " << pass_ruleExtras << "\n";
+ strm << " Constraint offsets: " << pass_constraintOffsets << "\n\n";
+ strm << " Constraint code: " << pass_constraintCode << "\n";
+ strm << " Action offsets: " << pass_actionOffsets << "\n";
+ strm << " Action code: " << pass_actionCode << "\n\n";
+
+ int slotTotal = slot_general + slot_abstract + slot_varLenBuf + slot_assocsAlloc + slot_attachAlloc;
+ int streamTotal = sstrm_general + sstrm_chunkMapsAlloc + sstrm_reprocBuf + slotTotal;
+ int engineStateTotal = engst_general + engst_passState + streamTotal;
+
+ strm << " Engine State: " << engineStateTotal << "\n";
+ strm << " General: " << engst_general << "\n";
+ strm << " Pass states: " << engst_passState << "\n";
+ strm << " Slot streams: " << streamTotal << "\n";
+ strm << " General: " << sstrm_general << "\n";
+ strm << " Chunk maps: " << sstrm_chunkMapsAlloc << " (" << sstrm_chunkMapsUsed << " used)\n";
+ strm << " Reprocess buffer: " << sstrm_reprocBuf << "\n";
+ strm << " Slots: " << slotTotal << "\n";
+ strm << " General: " << slot_general << "\n";
+ strm << " Abstract: " << slot_abstract << "\n";
+ strm << " Var-length buf: " << slot_varLenBuf << "\n";
+ strm << " Associations: " << slot_assocsAlloc << " (" << slot_assocsUsed << " used)\n";
+ strm << " Attachments: " << slot_attachAlloc << " (" << slot_attachUsed << " used)\n\n";
+
+ strm << "Total bytes used: " << totalBytes << "\n\n";
+
+ strm << "TOTALS PER FONT\n";
+ for (size_t ifont = 0; ifont < vstrFontNames.size(); ifont++)
+ {
+ strm << vstrFontNames[ifont].c_str() << "\n";
+ strm << " Regular: " << vFontTotalsReg[ifont] << "\n";
+ strm << " Bold: " << vFontTotalsBold[ifont] << "\n";
+ strm << " Italic: " << vFontTotalsItalic[ifont] << "\n";
+ strm << " Bold-italic: " << vFontTotalsBI[ifont] << "\n\n";
+ }
+}
+
+
+//:>********************************************************************************************
+//:> Segment
+//:>********************************************************************************************
+
+/*----------------------------------------------------------------------------------------------
+ Initialize the data structure.
+----------------------------------------------------------------------------------------------*/
+void SegmentMemoryUsage::initialize()
+{
+ seg_count = 0;
+ overhead = 0;
+ pointers = 0;
+ scalars = 0;
+ strings = 0;
+ metrics = 0;
+ associations = 0;
+ init = 0;
+ obsolete = 0;
+
+ slot_count = 0;
+ slot_abstract = 0;
+ slot_varLenBuf = 0;
+ slot_scalars = 0;
+ slot_clusterMembers = 0;
+
+ glyphInfo_count = 0;
+ glyphInfo = 0;
+
+ wastedVector = 0;
+}
+
+/*----------------------------------------------------------------------------------------------
+ Add the memory for the segemtn to the SegmentMemoryUsage data structure.
+----------------------------------------------------------------------------------------------*/
+void Segment::calculateMemoryUsage(SegmentMemoryUsage & smu)
+{
+ smu.addSegment(*this);
+}
+
+
+void SegmentMemoryUsage::addSegment(Segment & seg)
+{
+ seg_count++;
+
+ overhead += sizeof(long); // m_cref
+ pointers += sizeof(void*); // m_pgts
+ scalars += sizeof(int); // m_dichwLim
+ scalars += sizeof(int); // m_ichwMin
+ pointers += sizeof(void*); // m_pfont
+ pointers += sizeof(void*); // m_preneng
+ scalars += sizeof(bool); // m_fErroneous
+ pointers += sizeof(void*); // m_pgjus
+ scalars += sizeof(LayoutEnvironment);
+ scalars += sizeof(bool); // m_fWsRtl
+ scalars += sizeof(bool); // m_fParaRtl
+ scalars += sizeof(TrWsHandling);
+ scalars += sizeof(int); // m_nDirDepth
+ init += sizeof(byte*); // m_prgbNextSegDat
+ init += sizeof(int); // m_cbNextSegDat
+ init += sizeof(byte) * seg.m_cbNextSegDat;
+ init += sizeof(byte*); // m_prgInitDat
+ init += sizeof(int); // m_cbInitDat
+ init += sizeof(byte) * seg.m_cbInitDat;
+ init += sizeof(int); // m_dichPreContext
+// obsolete += sizeof(void *); // m_psegPrev
+// obsolete += sizeof(void *); // m_psegNext
+// strings += stringOverhead * 2;
+// strings += sizeof(wchar_t) * seg.m_stuFaceName.length();
+// strings += sizeof(wchar_t) * seg.m_stuBaseFaceName.length();
+// scalars += sizeof(bool); // m_fUseSepBase;
+// scalars += sizeof(float); // m_pixHeight
+// scalars += sizeof(bool); // m_fBold
+// scalars += sizeof(bool); // m_fItalic
+ scalars += sizeof(LineBrk); // m_lbStart
+ scalars += sizeof(LineBrk); // m_lbEnd
+ scalars += sizeof(bool); // m_fStartLine
+ scalars += sizeof(bool); // m_fEndLine
+ scalars += sizeof(SegEnd); // m_est
+ metrics += sizeof(int); // m_mFontEmUnits
+ metrics += sizeof(float) * 17; // was 19, removed m_x/ysDPI
+ associations += sizeof(int); // m_ichwAssocsMin
+ associations += sizeof(int); // m_ichwAssocsLim
+ int dichw = seg.m_ichwAssocsLim - seg.m_ichwAssocsMin;
+ associations += sizeof(int*); // m_prgisloutBefore
+ associations += sizeof(int) * dichw;
+ associations += sizeof(int*); // m_prgisloutAfter
+ associations += sizeof(int) * dichw;
+ associations += sizeof(void*); // m_prgpvisloutAssocs
+ associations += sizeof(void*) * dichw;
+ for (int idichw = 0; idichw < dichw; idichw++)
+ {
+ std::vector<int> * pvisloutAssocs = seg.m_prgpvisloutAssocs[idichw];
+ if (pvisloutAssocs)
+ {
+ associations += vectorOverhead;
+ associations += sizeof(int) * pvisloutAssocs->capacity();
+ wastedVector += sizeof(int) * (pvisloutAssocs->capacity() - pvisloutAssocs->size());
+ }
+ }
+ associations += sizeof(int*); // m_prgisloutLigature
+ associations += sizeof(int) * dichw;
+ associations += sizeof(sdata8*); // m_prgiComponent
+ associations += sizeof(sdata8) * dichw;
+// obsolete += sizeof(void*); // m_psstrm
+ scalars += sizeof(int); // m_cslout
+ slot_count += seg.m_cslout;
+ for (int islout = 0; islout < seg.m_cslout; islout++)
+ {
+ GrSlotOutput * pslout = seg.m_prgslout + islout;
+ slot_abstract += sizeof(gid16) * 2; // m_chwGlyphID, m_chwActual
+ slot_abstract += sizeof(sdata8) * 4; // m_spsl, m_dirc, m_lb, m_nDirLevel
+// slot_abstract += sizeof(float) * 8; // font/glyph metrics
+ slot_abstract += sizeof(short) * 5; // slot attributes - was 21
+ slot_abstract += sizeof(int); // m_mJWidth0;
+ slot_abstract += sizeof(byte); // m_nJWeight0;
+ slot_abstract += sizeof(bool); // m_fInsertBefore
+ slot_abstract += sizeof(sdata8); // m_bIsSpace
+ slot_abstract += sizeof(byte); // m_cnCompPerLig
+ slot_abstract += sizeof(void*); // m_prgnVarLenBuf
+ slot_varLenBuf += pslout->CExtraSpaceSlout() * sizeof(u_intslot);
+ slot_abstract += sizeof(float); // m_xsPositionX, m_ysPositionY
+// slot_abstract += sizeof(bool); // m_fAdvXSet, m_fAdvYSet
+ slot_scalars += sizeof(int) * 2; // m_ichwBefore/AfterAssoc
+ slot_scalars += sizeof(sdata8); // m_cComponents
+ slot_scalars += sizeof(int); // m_isloutClusterBase
+ slot_scalars += sizeof(sdata8); // m_disloutCluster
+ slot_scalars += sizeof(float) * 3; // m_xsClusterXOffset, m_xsClusterAdvance, m_xsAdvanceX
+ slot_scalars += sizeof(int); // m_igbb
+// slot_scalars += sizeof(Rect); // m_rectBB
+ }
+ scalars += sizeof(int); // m_cnUserDefn
+ scalars += sizeof(int); // m_cnCompPerLig
+ scalars += sizeof(int); // m_cnFeat
+ glyphInfo_count += seg.m_cginf;
+ scalars += sizeof(int); // m_cginf;
+ scalars += sizeof(int); // m_isloutGinf0
+ for (int iginf = 0; iginf < seg.m_cginf; iginf++)
+ {
+ ///GlyphInfo * pginf = seg.m_prgginf + iginf;
+ glyphInfo += sizeof(void *); // m_pseg
+ glyphInfo += sizeof(void *); // m_pslout
+ glyphInfo += sizeof(int); // m_islout
+ }
+ init += sizeof(int); // m_cslotRestartBackup
+ init += vectorOverhead;
+ init += sizeof(sdata8) * seg.m_vnSkipOffsets.capacity();
+ wastedVector += sizeof(sdata8) * (seg.m_vnSkipOffsets.capacity() - seg.m_vnSkipOffsets.size());
+ init += sizeof(DirCode); // m_dircPrevStrong
+ init += sizeof(DirCode); // m_dircPrevTerm
+
+ //int grandTotal;
+ //grandTotal = overhead + pointers + scalars + strings + metrics + associations
+ // + init + obsolete + slot_count + slot_abstract + slot_varLenBuf + slot_scalars
+ // + slot_clusterMembers + glyphInfo_count + glyphInfo;
+
+ //int totalPerSeg = grandTotal / seg_count;
+}
+
+/*----------------------------------------------------------------------------------------------
+ Write out the memory usage onto a stream.
+----------------------------------------------------------------------------------------------*/
+void SegmentMemoryUsage::prettyPrint(std::ostream & strm)
+{
+ int totalBytes = overhead + pointers + scalars + strings + metrics + associations
+ + init + obsolete + slot_count + slot_abstract + slot_varLenBuf + slot_scalars
+ + slot_clusterMembers + glyphInfo_count + glyphInfo;
+
+ int slotTotal = slot_abstract + slot_varLenBuf + slot_scalars + slot_clusterMembers;
+
+ strm << "Number of segments: " << seg_count << "\n\n";
+
+ strm << "TOTALS\n";
+ strm << "Overhead: " << overhead << "\n";
+ strm << "Pointers: " << pointers << "\n";
+ strm << "Scalars: " << scalars << "\n";
+ strm << "Strings: " << strings << "\n";
+ strm << "Metrics: " << metrics << "\n";
+ strm << "Associations: " << associations << "\n";
+ strm << "Obsolete: " << obsolete << "\n";
+ strm << "Slot data: " << slotTotal << "\n";
+ strm << " Abstract: " << slot_abstract << "\n";
+ strm << " Var-length buffer: " << slot_varLenBuf << "\n";
+ strm << " Scalars: " << slot_scalars << "\n";
+ strm << " Cluster members: " << slot_clusterMembers << "\n";
+ strm << "Glyph info: " << glyphInfo << "\n\n";
+
+ strm << "Wasted in vectors: " << wastedVector << "\n\n";
+
+ strm << "Total bytes used: " << totalBytes << "\n\n";
+
+ if (seg_count > 0)
+ {
+ strm << "AVERAGES\n";
+ strm << "Overhead: " << overhead/seg_count << "\n";
+ strm << "Pointers: " << pointers/seg_count << "\n";
+ strm << "Scalars: " << scalars/seg_count << "\n";
+ strm << "Strings: " << strings/seg_count << "\n";
+ strm << "Metrics: " << metrics/seg_count << "\n";
+ strm << "Associations: " << associations/seg_count << "\n";
+ strm << "Obsolete: " << obsolete/seg_count << "\n";
+ strm << "Slot data: " << slotTotal/seg_count << "\n";
+ strm << " Abstract: " << slot_abstract/seg_count << "\n";
+ strm << " Var-length buffer: " << slot_varLenBuf/seg_count << "\n";
+ strm << " Scalars: " << slot_scalars/seg_count << "\n";
+ strm << " Cluster members: " << slot_clusterMembers/seg_count << "\n";
+ strm << "Glyph info: " << glyphInfo/seg_count << "\n\n";
+
+ strm << "Avg. bytes per segment: " << totalBytes / seg_count << "\n\n";
+
+ strm << "Avg. # of slots per segment: " << slot_count / seg_count << "\n\n";
+ }
+}
+
+} //namespace gr
+
diff --git a/Build/source/libs/graphite/src/segment/Platform.cpp b/Build/source/libs/graphite/src/segment/Platform.cpp
new file mode 100644
index 00000000000..30ec3f4bf1f
--- /dev/null
+++ b/Build/source/libs/graphite/src/segment/Platform.cpp
@@ -0,0 +1,113 @@
+#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>
+
+#ifndef HAVE_FABSF
+float fabsf(float x)
+{
+ return (x < 0.0f) ? -x : x;
+}
+#endif
+
+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/src/segment/Segment.cpp b/Build/source/libs/graphite/src/segment/Segment.cpp
new file mode 100644
index 00000000000..b936b037a64
--- /dev/null
+++ b/Build/source/libs/graphite/src/segment/Segment.cpp
@@ -0,0 +1,3547 @@
+/*--------------------------------------------------------------------*//*: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_prgpvisloutAssocs(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_prgpvisloutAssocs = 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;
+ for (int ichw = 0; ichw < m_ichwAssocsLim - m_ichwAssocsMin; ichw++)
+ {
+ if (m_prgpvisloutAssocs && m_prgpvisloutAssocs[ichw])
+ delete m_prgpvisloutAssocs[ichw];
+ }
+ delete[] m_prgpvisloutAssocs;
+ 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, &gts, &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, &gts, &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_prgpvisloutAssocs = new std::vector<int> * [dichw];
+ for (int ivislout = 0; ivislout < dichw; ivislout++)
+ {
+ std::vector<int> * pvislout = seg.m_prgpvisloutAssocs[ivislout];
+ if (pvislout)
+ {
+ std::vector<int> * pvisloutNew = new std::vector<int>;
+ m_prgpvisloutAssocs[ivislout] = pvisloutNew;
+ *pvisloutNew = *pvislout; // copy the vector
+ }
+ }
+ m_prgisloutLigature = new int[dichw];
+ std::copy(seg.m_prgisloutLigature, seg.m_prgisloutLigature + dichw, m_prgisloutLigature);
+ m_prgiComponent = new sdata8[dichw];
+ std::copy(seg.m_prgiComponent, seg.m_prgiComponent + dichw, m_prgiComponent);
+
+ 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> vislout = UnderlyingToLogicalAssocs(ich);
+
+ if (vislout.size() == 0)
+ return std::make_pair(GlyphSetIterator(), GlyphSetIterator());
+ else
+ {
+ // Note that BOTH instances of GlyphSetIterator must use the same underlying vector,
+ // so that comparisons make sense.
+ RcVector * qvislout = new RcVector(vislout);
+ return std::make_pair(
+ GlyphSetIterator(*this, 0, qvislout),
+ GlyphSetIterator(*this, vislout.size(), qvislout));
+ }
+}
+
+/*----------------------------------------------------------------------------------------------
+ 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->NumberOfComponents() > 0)
+ && 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->NumberOfComponents() > 0)
+ && 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_prgpvisloutAssocs = new std::vector<int> * [cchwInThisSeg];
+
+ m_prgisloutLigature = new int[cchwInThisSeg];
+
+ m_prgiComponent = new sdata8[cchwInThisSeg];
+
+ int cslot = 0;
+ //m_psstrm = psstrmFinal; // TODO: make a local variable
+ if (psstrmFinal)
+ cslot = psstrmFinal->FinalSegLim();
+ else
+ Assert(fEmpty);
+
+ float xsMin = 0;
+ int islot;
+ int islotMin = (psstrmFinal) ? psstrmFinal->IndexOffset() : 0;
+ for (islot = islotMin; islot < cslot; islot++)
+ xsMin = min(xsMin, psstrmFinal->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 = psstrmFinal->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_prgpvisloutAssocs[ichw] = new std::vector<int>;
+ m_prgisloutLigature[ichw] = kNegInfinity;
+ m_prgiComponent[ichw] = 0;
+ }
+
+ m_cslout = csloutSurface;
+ if (ptman->NumUserDefn() > 0 && ptman->NumCompPerLig() > 0)
+ {
+ int x; x = 3;
+ }
+
+// 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);
+
+ // We don't need to store the user-defined slot attributes or the features in the segment itself.
+ // What we need is: (1) component.ref attr settings, (2) slot-attribute indices,
+ // (3) underlying indicates of ligature components, and (4) map from used components
+ // to defined components
+ int cnExtraPerSlot = m_cnCompPerLig * 4;
+ m_prgslout = new GrSlotOutput[m_cslout];
+ m_prgnSlotVarLenBuf = new u_intslot[m_cslout * cnExtraPerSlot];
+
+ m_isloutVisLim = 0;
+ if (psstrmFinal)
+ {
+ for (islot = islotMin; islot < psstrmFinal->FinalSegLim(); ++islot)
+ {
+ int isloutRel = islot - islotMin;
+
+ GrSlotState * pslot = psstrmFinal->SlotAt(islot);
+ pslot->SetPosPassIndex(isloutRel, false);
+ pslot->EnsureCacheForOutput(ptman);
+ GrSlotOutput * pslout = OutputSlot(isloutRel);
+ pslout->SetBufferPtr(m_prgnSlotVarLenBuf + (isloutRel * cnExtraPerSlot));
+ pslout->InitializeOutputFrom(pslot);
+ pslout->SetBeforeAssoc(pslot->BeforeAssoc());
+ pslout->SetAfterAssoc(pslot->AfterAssoc());
+ if (pslot->HasComponents())
+ pslot->SetComponentRefsFor(pslout);
+ Assert(pslot->HasComponents() == (pslout->NumberOfComponents() > 0));
+ //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(isloutBaseIndex, 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, psstrmFinal, 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, GrSlotStream * psstrmFinal,
+ 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 (psstrmFinal->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 = psstrmFinal->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;
+ if (m_isloutGinf0 == -1)
+ {
+ Assert(m_cginf == 0);
+ m_isloutGinf0 = ((OutputSlot(0)->IsInitialLineBreak()) ? 1 : 0);
+ }
+
+ Assert(m_isloutGinf0 == 0 || m_isloutGinf0 == 1);
+}
+
+/*
+// OLD VERSION
+void Segment::SetUpGlyphInfo(GrTableManager * ptman, GrSlotStream * psstrmFinal, 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 (psstrmFinal->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 = psstrmFinal->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 ----------------------------------
+
+
+/*----------------------------------------------------------------------------------------------
+ Generate a list of all the glyphs that are attached to the base with the given index.
+ Note that we want to generate glyph indices, not slot indices.
+
+ @param disloutCluster - indicates how far on either side of the base to look.
+----------------------------------------------------------------------------------------------*/
+void Segment::ClusterMembersForGlyph(int isloutBase, int disloutCluster,
+ std::vector<int> & visloutRet)
+{
+ for (int islout = max(0, isloutBase - disloutCluster);
+ islout < min(m_cslout, isloutBase + disloutCluster + 1);
+ islout++)
+ {
+ if (islout == isloutBase)
+ continue; // don't include the base itself
+ GrSlotOutput * pslout = m_prgslout + islout;
+ if (pslout->ClusterBase() == isloutBase)
+ {
+ visloutRet.push_back(islout);
+ }
+ }
+}
+
+/*----------------------------------------------------------------------------------------------
+ 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_prgpvisloutAssocs[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;
+ Assert(iComponent < kMaxComponentsPerGlyph);
+ m_prgiComponent[ ichwUnder - m_ichwAssocsMin] = sdata8(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> ** ppvisloutTmp = m_prgpvisloutAssocs;
+ m_prgpvisloutAssocs = new std::vector<int> * [cchwNewLim - cchwNewMin];
+ std::swap_ranges(m_prgpvisloutAssocs + cchwPreAdd,
+ m_prgpvisloutAssocs + cchwPreAdd +
+ (m_ichwAssocsLim - m_ichwAssocsMin), ppvisloutTmp);
+ delete[] ppvisloutTmp;
+
+ prgisloutTmp = m_prgisloutLigature;
+ m_prgisloutLigature = new int[cchwNewLim - cchwNewMin];
+ std::copy(prgisloutTmp, prgisloutTmp + (m_ichwAssocsLim - m_ichwAssocsMin),
+ m_prgisloutLigature + cchwPreAdd);
+ delete[] prgisloutTmp;
+
+ sdata8 * prgiCompTmp = m_prgiComponent;
+ m_prgiComponent = new sdata8[cchwNewLim - cchwNewMin];
+ std::copy(prgiCompTmp, prgiCompTmp + (m_ichwAssocsLim - m_ichwAssocsMin),
+ m_prgiComponent + cchwPreAdd);
+ delete[] prgiCompTmp;
+
+ // Initialize new slots.
+ int i;
+ for (i = 0; i < cchwPreAdd; ++i)
+ {
+ m_prgisloutBefore[i] = kPosInfinity;
+ m_prgisloutAfter[i] = kNegInfinity;
+ m_prgpvisloutAssocs[i] = new std::vector<int>;
+ 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_prgpvisloutAssocs[i] = new std::vector<int>;
+ 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_xysEmSquare); // m_xysEmSquare = 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->NumberOfComponents() > 0)
+ {
+ 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--;
+ }
+
+ if (dichw == kNegInfinity || dichw == kPosInfinity)
+ return dichw;
+
+ // 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
+----------------------------------------------------------------------------------------------*/
+#if 0
+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));
+ }
+ }
+}
+#endif
+
+/*----------------------------------------------------------------------------------------------
+ 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.
+
+ Returns an empty vector (not something containing infinities) if the character is
+ "invisible."
+----------------------------------------------------------------------------------------------*/
+std::vector<int> Segment::UnderlyingToLogicalAssocs(int ichw)
+{
+ std::vector<int> vnEmpty;
+ vnEmpty.clear();
+ Assert(vnEmpty.size() == 0);
+
+ int ichwSegOffset = ichw - m_ichwMin;
+
+ if (ichwSegOffset < m_ichwAssocsMin)
+ return vnEmpty; // probably rendered in previous segment
+
+ else if (ichwSegOffset >= m_ichwAssocsLim)
+ return vnEmpty; // probably rendered in following segment
+
+ else if (m_prgpvisloutAssocs[ichwSegOffset - m_ichwAssocsMin] == NULL)
+ {
+ // Create a vector using the before and after values.
+ std::vector<int> visloutRet;
+ int isloutBefore = m_prgisloutBefore[ichwSegOffset - m_ichwAssocsMin];
+ int isloutAfter = m_prgisloutAfter[ichwSegOffset - m_ichwAssocsMin];
+ if (isloutBefore != kPosInfinity && isloutBefore != kNegInfinity)
+ visloutRet.push_back(isloutBefore);
+ if (isloutAfter != kPosInfinity && isloutAfter != kNegInfinity && isloutBefore != isloutAfter)
+ {
+ visloutRet.push_back(isloutAfter);
+ }
+ return visloutRet;
+ }
+ else
+ {
+ std::vector<int> * pvisloutRet = m_prgpvisloutAssocs[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> vislout = UnderlyingToLogicalAssocs(ichw);
+ for (size_t iislout = 0; iislout < vislout.size(); iislout++)
+ {
+ isloutTest = vislout[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> vislout1 = UnderlyingToLogicalAssocs(ichw1);
+ std::vector<int> vislout2 = UnderlyingToLogicalAssocs(ichw2);
+
+ bool fRet = true;
+ if (vislout1.size() == 0 || vislout2.size() == 0)
+ fRet = false;
+ else if (vislout1.size() != vislout2.size())
+ fRet = false;
+ else
+ {
+ for (size_t islout = 0; islout < vislout1.size(); islout++)
+ {
+ if (vislout1[islout] != vislout2[islout])
+ {
+ fRet = false;
+ break;
+ }
+ }
+ }
+ return fRet;
+}
+
+/*----------------------------------------------------------------------------------------------
+ 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> vislout = UnderlyingToLogicalAssocs(ich + m_ichwMin);
+
+ if (vislout.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 < vislout.size(); iislout++)
+ {
+ int islout = vislout[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]);
+ Assert(visloutBefore[ich] != kPosInfinity);
+ Assert(visloutBefore[ich] != kNegInfinity);
+ Assert(visloutAfter[ich] != kPosInfinity);
+ Assert(visloutAfter[ich] != kNegInfinity);
+ 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;
+}
+
+
+// suppress GCC 4.3 warning for optimized min()/max() when called with (ich, ich+1) or similar
+#pragma GCC diagnostic ignored "-Wstrict-overflow"
+
+/*----------------------------------------------------------------------------------------------
+ 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 (bb.left < XClick < bb.right).
+ for (iginf = iginfHit; iginf >= 0; iginf--)
+ {
+ Rect rectBB = m_prgginf[iginf].bb();
+ if (rectBB.left <= xsClick && xsClick <= rectBB.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]);
+ }
+ }
+
+ if (viginfNear.size() > 2 && viginfHit.size() == 0)
+ {
+ // No hit along y-axis; find the closest thing.
+ float dy = float(10000000.0);
+ for (int iiginf2 = 0; iiginf2 < (int)viginfNear.size(); iiginf2++)
+ {
+ Rect rectBB = m_prgginf[viginfNear[iiginf2]].bb();
+ if (fabsf(rectBB.top - ysClick) < dy)
+ {
+ dy = fabsf(rectBB.top - ysClick);
+ viginfHit.clear();
+ viginfHit.push_back(viginfNear[iiginf2]);
+ }
+ if (fabsf(rectBB.bottom - ysClick) < dy)
+ {
+ dy = fabsf(rectBB.bottom - ysClick);
+ viginfHit.clear();
+ viginfHit.push_back(viginfNear[iiginf2]);
+ }
+ }
+ }
+
+ // Find all BB for which the click is within their advance width, if any.
+ for (iiginf = 0; iiginf < (int)viginfHit.size(); iiginf++)
+ {
+ //Rect rectBB = m_prgginf[viginfHit[iiginf]].bb(); -- ?? this tests the bounding box again :-/
+ //if (rectBB.left <= xsClick && xsClick <= rectBB.right)
+ gr::GlyphInfo * pginf = m_prgginf + viginfHit[iiginf];
+ if (pginf->advanceWidth() == 0 // advance width is irrelevant
+ || (pginf->origin() < xsClick && xsClick <= pginf->origin() + pginf->advanceWidth()))
+ {
+ 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.
+ First select any glyph with a significantly smaller bounding box.
+ Then, 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 a glyph that has a signficantly smaller bounding box.
+ // The idea is that it is going to be harder to hit than the bigger glyph.
+ float smallestArea = float(1000000000.0);
+ float largestArea = 0.0;
+ size_t iiginfSmallest = 0;
+ size_t iiginf;
+ for (iiginf = 0; iiginf < viginf.size(); iiginf++)
+ {
+ gr::GlyphInfo * pginf = m_prgginf + viginf[iiginf];
+ Rect rectBB = pginf->bb();
+ float thisArea = (rectBB.right - rectBB.left) * (rectBB.top - rectBB.bottom);
+ if (smallestArea > thisArea)
+ {
+ smallestArea = thisArea;
+ iiginfSmallest = iiginf;
+ }
+ largestArea = max(largestArea, thisArea);
+ }
+ if (smallestArea * 2.0 < largestArea)
+ return viginf[iiginfSmallest];
+
+ // 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;
+
+ for (iiginf = 1; iiginf < viginf.size(); iiginf++)
+ {
+ Rect rectBB = m_prgginf[viginf[iiginf]].bb();
+ if (!fRTL)
+ xsLeading = min(xsLeading, rectBB.left);
+ else
+ xsLeading = max(xsLeading, rectBB.right);
+ }
+
+ // Find BBs with the leading edge.
+ std::vector<int> viginfSameEdge;
+ for (iiginf = 0; iiginf < viginf.size(); iiginf++)
+ {
+ Rect rectBB = m_prgginf[viginf[iiginf]].bb();
+ if (!fRTL)
+ {
+ if (rectBB.left == xsLeading)
+ viginfSameEdge.push_back(viginf[iiginf]);
+ }
+ else
+ {
+ if (rectBB.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/src/segment/SegmentAux.cpp b/Build/source/libs/graphite/src/segment/SegmentAux.cpp
new file mode 100644
index 00000000000..9191e8724d5
--- /dev/null
+++ b/Build/source/libs/graphite/src/segment/SegmentAux.cpp
@@ -0,0 +1,256 @@
+/*--------------------------------------------------------------------*//*: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
+{
+ std::vector<int> visloutClusterMembers;
+ m_pslout->ClusterMembers(m_pseg, m_islout, visloutClusterMembers);
+ RcVector * qvislout = new RcVector(visloutClusterMembers);
+ return std::make_pair(
+ GlyphSetIterator(*m_pseg, 0, qvislout),
+ GlyphSetIterator(*m_pseg, visloutClusterMembers.size(), qvislout));
+}
+
+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
+{
+ Font & font = m_pseg->getFont();
+ return m_pslout->GlyphMetricLogUnits(&font, kgmetAdvHeight);
+}
+
+float GlyphInfo::yOffset()
+{
+ return m_pslout->YPosition();
+}
+
+Rect GlyphInfo::bb() // logical units
+{
+ return m_pslout->BoundingBox(m_pseg->getFont());
+}
+
+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)
+: m_pginf(seg.m_prgginf + iginf)
+{}
+
+// Copy constructor.
+// Check if the incoming iterator is null, as we cannot dereference
+// it to get the segment, and create a null GlyphIterator.
+// Technically this might better be an assert as converting a null
+// GlyphSetIterator into a null GlyphIterator is pointless and suggests
+// some kind of error.
+GlyphIterator::GlyphIterator(const GlyphSetIterator & sit)
+: m_pginf((sit == GlyphSetIterator())
+ ? 0
+ : sit->segment().m_prgginf + sit->logicalIndex())
+{}
+
+/*----------------------------------------------------------------------------------------------
+ Dereference the iterator, returning a GlyphInfo object.
+----------------------------------------------------------------------------------------------*/
+
+GlyphSetIterator::reference GlyphSetIterator::operator*() const
+{
+ assert(m_pseg != 0);
+ assert(m_vit != std::vector<int>::const_iterator());
+ // in the case of a non-contiguous list
+ return m_pseg->m_prgginf[(*m_vit) - m_pseg->m_isloutGinf0];
+}
+
+
+} // namespace gr
diff --git a/Build/source/libs/graphite/src/segment/TestFSM.cpp b/Build/source/libs/graphite/src/segment/TestFSM.cpp
new file mode 100644
index 00000000000..6dad1dc9dc8
--- /dev/null
+++ b/Build/source/libs/graphite/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/src/segment/TestPasses.cpp b/Build/source/libs/graphite/src/segment/TestPasses.cpp
new file mode 100644
index 00000000000..a85e6c56207
--- /dev/null
+++ b/Build/source/libs/graphite/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/src/segment/TransductionLog.cpp b/Build/source/libs/graphite/src/segment/TransductionLog.cpp
new file mode 100644
index 00000000000..73273ae3feb
--- /dev/null
+++ b/Build/source/libs/graphite/src/segment/TransductionLog.cpp
@@ -0,0 +1,2091 @@
+/*--------------------------------------------------------------------*//*:Ignore this sentence.
+Copyright (C) 2000 - 2008 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;
+
+ 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));
+ }
+
+ LogFinalPositions(strmOut);
+
+ 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->LogSlotAttributeValue(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 // TRACING
+
+/*----------------------------------------------------------------------------------------------
+ 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++)
+ {
+ if (m_prgpvisloutAssocs[ichw])
+ cassocs = max(cassocs, m_prgpvisloutAssocs[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 (int ix = 1; ix < signed(cassocs) - 1; ix++) //(cassocs > 2)
+ {
+ if (ix == 1)
+ strmOut << "other ";
+ else
+ strmOut << " ";
+ for (ichw = 0; ichw < (m_ichwAssocsLim - m_ichwAssocsMin); ichw++)
+ {
+ std::vector<int> * pvislout = m_prgpvisloutAssocs[ichw];
+ if (pvislout == NULL)
+ strmOut << " ";
+ else if (signed(pvislout->size()) <= ix)
+ strmOut << " ";
+ else if ((*pvislout)[ix] != m_prgisloutAfter[ichw])
+ ptman->LogInTable(strmOut, (*pvislout)[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());
+ 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 != kNotYetSet8)
+ rgfMods[kslatBreak] = true;
+ if (m_dirc != kNotYetSet8)
+ 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::LogSlotAttributeValue(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 = 0;
+ int mPrev = 0;
+ 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 : kNotYetSet8))
+ {
+ ptman->LogBreakWeightInTable(strmOut, m_lb);
+ return;
+ }
+ break;
+ case kslatDir:
+ if (m_dirc != (pslotPrev ? pslotPrev->m_dirc : kNotYetSet8))
+ {
+ 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 = AssocSlot(0);
+ GrSlotState * pslotAfter = AssocSlot(m_vpslotAssoc.size() - 1);
+
+ 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 = AssocSlot(m_vpslotAssoc.size() - 1);
+
+ if (pslotAfter)
+ {
+ int nAfter = pslotAfter->m_islotTmpIn;
+ ptman->LogInTable(strmOut, nAfter);
+ }
+ else
+ strmOut << "?? ";
+ }
+ else if (iassoc < signed(m_vpslotAssoc.size() - 1))
+ {
+ GrSlotState * pslot = AssocSlot(iassoc);
+
+ 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*-1); 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/src/segment/XmlTransductionLog.cpp b/Build/source/libs/graphite/src/segment/XmlTransductionLog.cpp
new file mode 100644
index 00000000000..d0dfcaafbe8
--- /dev/null
+++ b/Build/source/libs/graphite/src/segment/XmlTransductionLog.cpp
@@ -0,0 +1,1583 @@
+/*--------------------------------------------------------------------*//*:Ignore this sentence.
+Copyright (C) 2008 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 using an XML format.
+----------------------------------------------------------------------------------------------*/
+
+//:>********************************************************************************************
+//:> 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::WriteXmlLog(std::ostream * pstrmLog,
+ GrCharStream * pchstrm, Segment * psegRet, int cbPrevSegDat, byte * pbPrevSegDat)
+{
+#ifdef TRACING
+ if (!pstrmLog)
+ return false;
+ std::ostream & strmOut = *pstrmLog;
+ WriteXmlLogAux(strmOut, pchstrm, psegRet, cbPrevSegDat, pbPrevSegDat);
+ return true;
+#else
+ return false;
+#endif // TRACING
+}
+
+
+bool GrTableManager::WriteXmlAssocLog(std::ostream * pstrmLog,
+ GrCharStream * pchstrm, Segment * psegRet)
+{
+#ifdef TRACING
+ if (!pstrmLog)
+ return false;
+
+ std::ostream & strmOut = *pstrmLog;
+ LogFinalPositions(strmOut);
+
+ LogXmlTagOpen(strmOut, "Mappings", 1, true);
+ LogXmlTagPostAttrs(strmOut, true);
+
+ //psegRet->LogXmlUnderlyingToSurface(strmOut, this, pchstrm, 2);
+ //psegRet->LogXmlSurfaceToUnderlying(strmOut, this, 2);
+
+ LogXmlTagClose(strmOut, "Mappings", 1, true);
+
+ strmOut << "\n";
+ LogXmlTagClose(strmOut, "GraphiteTraceLog", 0, true);
+
+ return true;
+#else
+ return false;
+#endif // TRACING
+}
+
+#ifdef TRACING
+
+/*----------------------------------------------------------------------------------------------
+ Output a file showing a log of the transduction process and the resulting segment.
+----------------------------------------------------------------------------------------------*/
+void GrTableManager::WriteXmlLogAux(std::ostream & strmOut,
+ GrCharStream * pchstrm, Segment * psegRet,
+ int cbPrevSegDat, byte * pbPrevSegDat)
+{
+ strmOut
+ << "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n"
+ << "<!DOCTYPE GraphiteTraceLog SYSTEM \"GraphiteTraceLog.dtd\">\n"
+ << "\n";
+
+ LogXmlTagOpen(strmOut, "GraphiteTraceLog", 0, true);
+ LogXmlTagPostAttrs(strmOut, true);
+ strmOut << "\n";
+
+ LogXmlTagOpen(strmOut, "SegmentRun", 1, true);
+ LogXmlTagAttr(strmOut, "stringOffset", pchstrm->Min(), 0);
+ LogXmlTagPostAttrs(strmOut, true);
+
+ if (cbPrevSegDat == 0)
+ LogXmlUnderlying(strmOut, pchstrm, 0, 2);
+ else
+ {
+ Assert(*(pbPrevSegDat + 4) == 0); // skip offset for first pass
+ LogXmlUnderlying(strmOut, pchstrm, *(pbPrevSegDat + 3), 2);
+ }
+
+ //LogXmlPass1Input(strmOut);
+
+ for (int ipass = 0; ipass < m_cpass; ipass++)
+ {
+ if (cbPrevSegDat == 0)
+ LogXmlPass(strmOut, ipass, 0, 2);
+ else
+ LogXmlPass(strmOut, ipass, *(pbPrevSegDat + 4 + ipass), 2);
+ }
+
+ LogXmlTagClose(strmOut, "SegmentRun", 1, true);
+ strmOut << "\n";
+
+ // Don't do this until after we've logged the associations.
+ //LogXmlTagClose(strmOut, "GraphiteTraceLog", 0, true);
+}
+
+/*----------------------------------------------------------------------------------------------
+ Write out a log of the underlying input.
+----------------------------------------------------------------------------------------------*/
+void GrTableManager::LogXmlUnderlying(std::ostream & strmOut, GrCharStream * pchstrm,
+ int cchwBackup, size_t nIndent)
+{
+ LogXmlTagOpen(strmOut, "Input", nIndent, true);
+ LogXmlTagPostAttrs(strmOut, true);
+
+ LogXmlUnderlyingAux(strmOut, pchstrm, cchwBackup, -1, nIndent,
+ true, // text
+ true, // features
+ true, // color
+ false, // string offset
+ false, // ligature components
+ false); // glyph associations
+
+ LogXmlTagClose(strmOut, "Input", nIndent, true);
+}
+
+void GrTableManager::LogXmlUnderlyingAux(std::ostream & strmOut, GrCharStream * pchstrm,
+ int cch32Backup, int cch32Lim, size_t nIndent,
+ bool fLogText, bool fLogFeatures, bool fLogColor, bool fLogStrOff, bool fLogLig, bool fLogGlyphs)
+{
+ int rgnChars[MAX_SLOTS];
+ bool rgfNewRun[MAX_SLOTS];
+ std::fill_n(rgfNewRun, MAX_SLOTS, false);
+ GrFeatureValues rgfval[MAX_SLOTS];
+ int cchwMaxRawChars;
+
+ int cch32 = pchstrm->GetLogData(this, rgnChars, rgfNewRun, rgfval,
+ cch32Backup, &cchwMaxRawChars);
+ cch32 = min(cch32, MAX_SLOTS);
+ if (cch32Lim > -1)
+ cch32 = min(cch32Lim, cch32);
+
+ int ichw;
+
+ // For UTF-8 and surrogate representation:
+ int rgchwChars1[MAX_SLOTS];
+ utf16 rgchwChars2[MAX_SLOTS];
+ utf16 rgchwChars3[MAX_SLOTS];
+ utf16 rgchwChars4[MAX_SLOTS];
+ utf16 rgchwChars5[MAX_SLOTS];
+ utf16 rgchwChars6[MAX_SLOTS];
+ utf16 rgiRawString[MAX_SLOTS]; // indices from underlying slots to text-source chars (0-based)
+
+ int rgichwRaw[MAX_SLOTS]; // index of input char with in the given UTF32, 1-based
+
+ if (cchwMaxRawChars > 1)
+ {
+ cchwMaxRawChars = min(cchwMaxRawChars, 6); // max of 6 raw (UTF-8 or UTF-16) chars per slot
+ pchstrm->GetLogDataRaw(this, cch32, cch32Backup, cchwMaxRawChars,
+ rgchwChars1, rgchwChars2, rgchwChars3, rgchwChars4, rgchwChars5, rgchwChars6,
+ rgichwRaw);
+ }
+ else
+ {
+ for (ichw = 0; ichw < cch32; ichw++)
+ {
+ rgichwRaw[ichw] = 1;
+ rgchwChars1[ichw] = rgnChars[ichw];
+ rgchwChars2[ichw] = 0;
+ rgchwChars3[ichw] = 0;
+ rgchwChars4[ichw] = 0;
+ rgchwChars5[ichw] = 0;
+ rgchwChars6[ichw] = 0;
+ }
+ }
+
+ int ichUtf32 = 0;
+ ichw = 0;
+ while (ichUtf32 < cch32)
+ {
+ if (rgichwRaw[ichw] == 1)
+ {
+ rgiRawString[ichUtf32++] = (utf16)ichw;
+ }
+ ichw++;
+ }
+
+ int ichwFeat = 0;
+ for (ichw = 0; ichw < cch32; ichw++)
+ {
+ if (rgfNewRun[ichw])
+ ichwFeat = ichw; // because rgfval only saves features for first of run
+
+ // See if there are any features.
+ bool fFeatures = false;
+ size_t ifeat;
+ for (ifeat = 0; fLogFeatures && ifeat < kMaxFeatures; ifeat++)
+ {
+ if (rgfval[ichwFeat].FeatureValue(ifeat) != 0)
+ {
+ fFeatures = true;
+ break;
+ }
+ }
+
+ bool fContent = fFeatures;
+
+ LogXmlTagOpen(strmOut, "Char", nIndent+1, fContent);
+
+ LogXmlTagAttr(strmOut, "index", rgiRawString[ichw] - cch32Backup, 0);
+ LogXmlTagAttrHex(strmOut, "usv", rgnChars[ichw], 0);
+
+ if (fLogText)
+ {
+ char rgch[20];
+ rgch[0] = (char)rgnChars[ichw];
+ rgch[1] = 0;
+ if (rgnChars[ichw] < 0x0100 && rgchwChars2[ichw] == 0) // ANSI
+ LogXmlTagAttr(strmOut, "text", rgch);
+ else if (rgnChars[ichw] == knLRM)
+ LogXmlTagAttr(strmOut, "text", "<LRM>");
+ else if (rgnChars[ichw] == knRLM)
+ LogXmlTagAttr(strmOut, "text", "<RLM>");
+ else if (rgnChars[ichw] == knLRO)
+ LogXmlTagAttr(strmOut, "text", "<LRO>");
+ else if (rgnChars[ichw] == knRLO)
+ LogXmlTagAttr(strmOut, "text", "<RLO>");
+ else if (rgnChars[ichw] == knLRE)
+ LogXmlTagAttr(strmOut, "text", "<LRE>");
+ else if (rgnChars[ichw] == knRLE)
+ LogXmlTagAttr(strmOut, "text", "<RLE>");
+ else if (rgnChars[ichw] == knPDF)
+ LogXmlTagAttr(strmOut, "text", "<PDF>");
+ }
+
+ if (fLogStrOff)
+ LogXmlTagAttr(strmOut, "stringOffset", pchstrm->Min() + rgiRawString[ichw] - cch32Backup, 0);
+
+ if (rgchwChars2[ichw] != 0)
+ {
+ std::string strHex = HexString(rgchwChars1[ichw], rgchwChars2[ichw], rgchwChars3[ichw],
+ rgchwChars4[ichw], rgchwChars5[ichw], rgchwChars6[ichw]);
+ if (pchstrm->TextSrc()->utfEncodingForm() == kutf8)
+ LogXmlTagAttr(strmOut, "textSourceUtf8", strHex.c_str());
+ else
+ LogXmlTagAttr(strmOut, "textSourceUtf16", strHex.c_str());
+ }
+
+ if (fLogColor)
+ {
+ IColorTextSource * tscolor = dynamic_cast<IColorTextSource *>(pchstrm->TextSrc());
+ if (tscolor)
+ {
+ int clrFore, clrBack;
+ tscolor->getColors(ichw, &clrFore, &clrBack);
+ LogXmlTagColor(strmOut, "color", clrFore, false);
+ LogXmlTagColor(strmOut, "background", clrBack, true);
+ }
+ }
+
+ if (fLogLig)
+ {
+
+ }
+
+//if (fLogLig || fLogGlyphs)
+//{
+// size_t cassocs = 0;
+// int fLigs = false;
+// int ichw;
+// for (ichw = 0; ichw < (m_ichwAssocsLim - m_ichwAssocsMin); ichw++)
+// {
+// if (m_prgpvisloutAssocs[ichw])
+// cassocs = max(cassocs, m_prgpvisloutAssocs[ichw]->size());
+// if (m_prgisloutLigature[ichw] != kNegInfinity)
+// fLigs = true;
+// }
+//}
+
+ LogXmlTagPostAttrs(strmOut, fContent);
+
+ if (fLogFeatures)
+ {
+ for (ifeat = 0; ifeat < kMaxFeatures; ifeat++)
+ {
+ if (rgfval[ichwFeat].FeatureValue(ifeat) != 0)
+ {
+ GrFeature * pfeat = Feature(ifeat);
+
+ LogXmlTagOpen(strmOut, "Feature", nIndent+2, false);
+ LogXmlTagAttr(strmOut, "id", pfeat->ID());
+ LogXmlTagAttr(strmOut, "value", rgfval[ichwFeat].FeatureValue(ifeat));
+ //LogXmlTagPostAttrs(strmOut, false);
+ LogXmlTagClose(strmOut, "Feature", nIndent+1, false);
+ }
+ }
+ }
+
+ if (fLogGlyphs)
+ {
+ }
+
+ LogXmlTagClose(strmOut, "Char", nIndent+1, fContent);
+ }
+}
+
+/*----------------------------------------------------------------------------------------------
+ Output the the results of pass.
+----------------------------------------------------------------------------------------------*/
+void GrTableManager::LogXmlPass(std::ostream & strmOut, int ipass, int cslotSkipped, int nIndent)
+{
+ strmOut << "\n";
+
+ GrPass * ppass = Pass(ipass);
+ GrSlotStream * psstrmIn = (ipass == 0) ? NULL : InputStream(ipass);
+ GrSlotStream * psstrmOut = OutputStream(ipass);
+
+ int islot;
+ // Mark each slot with its index in the input and output streams.
+ for (islot = 0; ipass > 0 && islot < psstrmIn->ReadPos(); islot++)
+ psstrmIn->SlotAt(islot)->m_islotTmpIn = islot;
+
+ for (islot = 0; islot < psstrmOut->WritePos(); islot++)
+ psstrmOut->SlotAt(islot)->m_islotTmpOut = islot;
+
+ LogXmlTagOpen(strmOut, "Pass", nIndent, true);
+ LogXmlTagAttr(strmOut, "number", ipass);
+
+ std::string strType;
+ if (dynamic_cast<GrGlyphGenPass *>(ppass))
+ strType = "glyphGeneration";
+ else if (dynamic_cast<GrBidiPass *>(ppass))
+ strType = "bidi";
+ else if (dynamic_cast<GrSubPass *>(ppass))
+ {
+ if (ipass >= m_ipassJust1)
+ strType = "justification";
+ else
+ strType = "substitution";
+ }
+ else if (dynamic_cast<GrPosPass *>(ppass))
+ strType = "positioning";
+ else if (dynamic_cast<GrLineBreakPass *>(ppass))
+ strType = "linebreak";
+ LogXmlTagAttr(strmOut, "type", strType.c_str());
+
+ LogXmlTagPostAttrs(strmOut, true);
+
+ if (!dynamic_cast<GrBidiPass *>(ppass) && !dynamic_cast<GrGlyphGenPass *>(ppass))
+ {
+ LogXmlTagOpen(strmOut, "RulesMatched", nIndent+1, true);
+ LogXmlTagPostAttrs(strmOut, true);
+ ppass->LogXmlRules(strmOut, this, psstrmIn, nIndent+2);
+ LogXmlTagClose(strmOut, "RulesMatched", nIndent+1, true);
+ }
+
+ bool fJustWidths = false;
+ if (ipass == m_ipassJust1 - 1 && ShouldLogJustification()
+ && m_engst.m_jmodi == kjmodiJustify)
+ {
+ fJustWidths = true;
+ }
+
+ LogXmlTagOpen(strmOut, "Output", nIndent+1, true);
+ LogXmlTagPostAttrs(strmOut, true);
+ bool fPreJust = (!fJustWidths && ipass == m_ipassJust1 - 1 && ShouldLogJustification());
+ bool fPostJust = (fJustWidths || ipass == m_ipassJust1 && ShouldLogJustification());
+ ppass->LogXmlGlyphs(strmOut, this, psstrmOut, m_ipassJust1, fPreJust, fPostJust, cslotSkipped, nIndent+2);
+ LogXmlTagClose(strmOut, "Output", nIndent+1, true);
+
+ LogXmlTagClose(strmOut, "Pass", nIndent, true);
+
+#ifdef XMLOMIT
+
+ // 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);
+ }
+
+#endif // XMLOMIT
+
+}
+
+/*----------------------------------------------------------------------------------------------
+ Write the list of rules fired and failed for a given pass.
+----------------------------------------------------------------------------------------------*/
+void GrPass::LogXmlRules(std::ostream & strmOut, GrTableManager * ptman,
+ GrSlotStream * psstrmIn, int nIndent)
+{
+ m_pzpst->LogXmlRules(strmOut, ptman, psstrmIn, nIndent);
+}
+
+void PassState::LogXmlRules(std::ostream & strmOut, GrTableManager * ptman,
+ GrSlotStream * psstrmIn, int nIndent)
+{
+ if (m_crulrec == 0)
+ {
+ ptman->LogXmlComment(strmOut, "none", nIndent);
+ return;
+ }
+
+ for (int irulrec = 0; irulrec < m_crulrec; irulrec++)
+ {
+ bool fSourceAvailable = false;
+
+ ptman->LogXmlTagOpen(strmOut, "Rule", nIndent, fSourceAvailable);
+
+ ptman->LogXmlTagAttr(strmOut, "slot", m_rgrulrec[irulrec].m_islot);
+
+ if (m_rgrulrec[irulrec].m_irul == PassState::kHitMaxRuleLoop)
+ ptman->LogXmlTagAttr(strmOut, "hitMaxRuleLoop", "true");
+ else if (m_rgrulrec[irulrec].m_irul == PassState::kHitMaxBackup)
+ ptman->LogXmlTagAttr(strmOut, "hitMaxBackup", "true");
+ else
+ {
+ ptman->LogXmlTagAttr(strmOut, "debugNumber", m_rgrulrec[irulrec].m_irul);
+ ptman->LogXmlTagAttr(strmOut, "fired", (m_rgrulrec[irulrec].m_fFired) ? "true" : "false");
+ }
+
+ ptman->LogXmlTagPostAttrs(strmOut, fSourceAvailable);
+
+ // TODO: output Source
+
+ ptman->LogXmlTagClose(strmOut, "Rule", nIndent, fSourceAvailable);
+ }
+}
+
+void GrPass::LogXmlGlyphs(std::ostream & strmOut, GrTableManager * ptman, GrSlotStream * psstrmOut,
+ int ipassJust1, bool fPreJust, bool fPostJust, int cslotSkipped, int nIndent)
+{
+ GrBidiPass * ppassBidi = dynamic_cast<GrBidiPass *>(this);
+ GrBidiPass * ppassBidiNext = (m_ipass + 1 >= ptman->NumberOfPasses()) ?
+ NULL :
+ dynamic_cast<GrBidiPass *>(ptman->Pass(m_ipass + 1));
+ m_pzpst->LogXmlGlyphs(strmOut, ptman, psstrmOut, ipassJust1, fPreJust, fPostJust,
+ (ppassBidi != NULL), (ppassBidiNext != NULL), cslotSkipped, nIndent);
+}
+
+void PassState::LogXmlGlyphs(std::ostream & strmOut, GrTableManager * ptman, GrSlotStream * psstrmOut,
+ int ipassJust1, bool fPreJust, bool fPostJust, bool fBidi, bool fBidiNext, int cslotSkipped,
+ int nIndent)
+{
+ ptman->LogXmlTagOpen(strmOut, "Output", nIndent, true);
+ ptman->LogXmlTagPostAttrs(strmOut, true);
+
+ for (int islot = 0; islot < psstrmOut->WritePos() + 1; islot++) // +1 allows us to handle final deletion
+ {
+ if (islot > MAX_SLOTS)
+ break;
+
+ for (int islotDel = 0; islotDel < m_rgcslotDeletions[islot]; islotDel++)
+ {
+ ptman->LogXmlTagOpen(strmOut, "Glyph", nIndent+1, false);
+ ptman->LogXmlTagAttr(strmOut, "slot", "deleted");
+ ptman->LogXmlTagPostAttrs(strmOut, false);
+ ptman->LogXmlTagClose(strmOut, "Glyph", nIndent+1, false);
+ }
+ if (islot >= psstrmOut->WritePos())
+ break;
+
+ GrSlotState * pslot = psstrmOut->SlotAt(islot);
+
+ bool fMod = (pslot->PassModified() >= m_ipass && m_ipass > 0);
+ if (fBidi || fBidiNext)
+ fMod = true;
+
+ ptman->LogXmlTagOpen(strmOut, "Glyph", nIndent+1, fMod);
+
+ ptman->LogXmlTagAttr(strmOut, "slot", islot);
+ if (pslot->GlyphID() == ptman->LBGlyphID())
+ ptman->LogXmlTagAttr(strmOut, "gid", "linebreak");
+ else
+ ptman->LogXmlTagAttr(strmOut, "gid", pslot->GlyphID());
+ if (pslot->GlyphID() != pslot->ActualGlyphForOutput(ptman))
+ ptman->LogXmlTagAttr(strmOut, "actual", pslot->ActualGlyphForOutput(ptman));
+ if (m_rgfInsertion[islot])
+ ptman->LogXmlTagAttr(strmOut, "inserted", "true");
+ if (islot < cslotSkipped)
+ ptman->LogXmlTagAttr(strmOut, "nextPassSkips", "true");
+
+ ptman->LogXmlTagPostAttrs(strmOut, fMod);
+
+ if (fMod)
+ {
+ pslot->LogXmlAttributes(strmOut, ptman, psstrmOut, m_ipass, islot,
+ fPreJust, fPostJust, fBidi, fBidiNext, nIndent+2);
+ }
+
+ ptman->LogXmlTagClose(strmOut, "Glyph", nIndent+1, fMod);
+ }
+
+ ptman->LogXmlTagClose(strmOut, "Output", nIndent, true);
+}
+
+/*----------------------------------------------------------------------------------------------
+ Write out the attributes that have changes.
+----------------------------------------------------------------------------------------------*/
+void GrSlotState::LogXmlAttributes(std::ostream & strmOut, GrTableManager * ptman,
+ GrSlotStream * psstrmOut, int ipass,
+ int islot, bool fPreJust, bool fPostJust, bool fBidi, bool fBidiNext, int nIndent)
+{
+ // To handle reprocessing, in which case there may be a chain of slots modified
+ // in the same pass:
+ GrSlotState * pslotPrev = PrevState();
+ while (pslotPrev && pslotPrev->PassModified() == PassModified())
+ pslotPrev = pslotPrev->m_pslotPrevState;
+
+ bool * prgfMods = new bool[kslatMax + ptman->NumUserDefn() - 1];
+ memset(prgfMods, 0, (kslatMax + ptman->NumUserDefn() - 1) * sizeof(bool));
+ int ccomp;
+ int cassoc;
+ SlotAttrsModified(prgfMods, fPreJust, &ccomp, &cassoc); // just for this one slot
+
+ if (fPreJust)
+ prgfMods[kslatJWidth] = false;
+ else if (fPostJust)
+ prgfMods[kslatJWidth] = true;
+
+ //if (fJustWidths)
+ //{}
+
+ bool fLogAssocs = false;
+
+ for (int slat = 0; slat < kslatMax + ptman->NumUserDefn() - 1; slat++)
+ {
+ if (!prgfMods[slat] && (slat != kslatDir || (!fBidi && !fBidiNext)))
+ continue;
+
+ if (slat == kslatPosX)
+ continue;
+ if (slat == kslatPosY)
+ continue;
+
+ std::string strAttrName;
+ switch (slat)
+ {
+ case kslatAdvX: strAttrName = "advance.x"; break;
+ case kslatAdvY: strAttrName = "advance.y"; break;
+ case kslatAttTo: strAttrName = "att.to"; break;
+ case kslatAttAtX: strAttrName = "att.at.x"; break;
+ case kslatAttAtY: strAttrName = "att.at.y"; break;
+ case kslatAttAtGpt: strAttrName = "att.at.gpt"; break;
+ case kslatAttAtXoff: strAttrName = "att.at.xoff"; break;
+ case kslatAttAtYoff: strAttrName = "att.at.yoff"; break;
+ case kslatAttWithX: strAttrName = "att.with.x"; break;
+ case kslatAttWithY: strAttrName = "att.with.y"; break;
+ case kslatAttWithGpt: strAttrName = "att.with.gpt"; break;
+ case kslatAttWithXoff: strAttrName = "att.with.xoff"; break;
+ case kslatAttWithYoff: strAttrName = "att.with.yoff"; break;
+ case kslatAttLevel: strAttrName = "att.level"; break;
+ case kslatBreak: strAttrName = "breakweight"; break;
+ case kslatCompRef: strAttrName = "component"; break; // << iIndex + 1 - 1-based
+ case kslatDir: strAttrName = "dir"; break;
+ case kslatInsert: strAttrName = "insert"; break;
+ case kslatMeasureSol: strAttrName = "measure.sol"; break;
+ case kslatMeasureEol: strAttrName = "measure.eol"; break;
+ case kslatJStretch: strAttrName = "just.stretch"; break;
+ case kslatJShrink: strAttrName = "just.shrink"; break;
+ case kslatJStep: strAttrName = "just.step"; break;
+ case kslatJWeight: strAttrName = "just.weight"; break;
+ case kslatJWidth: strAttrName = "just.width"; break;
+ case kslatPosX:
+ case kslatPosY:
+ Assert(false);
+ break;
+ case kslatShiftX: strAttrName = "shift.x"; break;
+ case kslatShiftY: strAttrName = "shift.y"; break;
+ default:
+ if (kslatUserDefn <= slat &&
+ slat < kslatUserDefn + ptman->NumUserDefn())
+ {
+ strAttrName.assign("user");
+ char rgch[20];
+ itoa(slat - kslatUserDefn + 1, rgch, 10); // 1-based
+ strAttrName.append(rgch);
+ }
+ else
+ {
+ // Invalid attribute:
+ Warn("bad slot attribute");
+ break;
+ }
+ }
+
+ fLogAssocs = (fLogAssocs || prgfMods[slat]);
+
+ if (slat == kslatCompRef)
+ {
+ for (int icomp = 0; icomp < ccomp; icomp++)
+ {
+ ptman->LogXmlTagOpen(strmOut, "Attribute", nIndent, false);
+ ptman->LogXmlTagAttr(strmOut, "name", strAttrName.c_str());
+ ptman->LogXmlTagAttr(strmOut, "index", icomp+1);
+ int nValue = GetSlotAttrValue(strmOut, ptman, ipass, slat, icomp, fPreJust, fPostJust);
+ ptman->LogXmlTagAttr(strmOut, "newValue", nValue);
+ ptman->LogXmlTagPostAttrs(strmOut, false);
+ ptman->LogXmlTagClose(strmOut, "Attribute", nIndent, false);
+ }
+ }
+ else
+ {
+ std::string strValue = "newValue";
+ if ((!prgfMods[slat] && fBidiNext) || fBidi)
+ strValue = "value";
+
+ ptman->LogXmlTagOpen(strmOut, "Attribute", nIndent, false);
+ ptman->LogXmlTagAttr(strmOut, "name", strAttrName.c_str());
+ int nValue = GetSlotAttrValue(strmOut, ptman, ipass, slat, 0, fPreJust, fPostJust);
+ switch (slat)
+ {
+ case kslatDir: ptman->LogXmlDirCode(strmOut, strValue.c_str(), nValue);break;
+ case kslatBreak: ptman->LogXmlBreakWeight(strmOut, "newValue", nValue); break;
+ default: ptman->LogXmlTagAttr(strmOut, "newValue", nValue); break;
+ }
+ ptman->LogXmlTagPostAttrs(strmOut, false);
+ ptman->LogXmlTagClose(strmOut, "Attribute", nIndent, false);
+ }
+ }
+
+ if (fBidi)
+ {
+ // Output final directionality level
+ ptman->LogXmlTagOpen(strmOut, "Attribute", nIndent, false);
+ ptman->LogXmlTagAttr(strmOut, "name", "directionLevel");
+ ptman->LogXmlTagAttr(strmOut, "value", DirLevel());
+ ptman->LogXmlTagPostAttrs(strmOut, false);
+ ptman->LogXmlTagClose(strmOut, "Attribute", nIndent, false);
+ }
+
+ if (m_vpslotAssoc.size() > 1)
+ fLogAssocs = true; // multiple associations
+ else if (m_vpslotAssoc.size() == 1)
+ {
+ // association change from previous stream
+ GrSlotState * pslotAssoc = AssocSlot(0);
+ if (pslotAssoc == NULL || pslotAssoc->m_islotTmpIn != islot)
+ fLogAssocs = true;
+ }
+ if (fLogAssocs)
+ {
+ std::string strAssocs;
+ char rgch[20];
+ for (size_t iassoc = 0; iassoc < m_vpslotAssoc.size(); iassoc++)
+ {
+ GrSlotState * pslotAssoc = AssocSlot(iassoc);
+ if (pslotAssoc)
+ {
+ int n = pslotAssoc->m_islotTmpIn;
+ memset(rgch,0,20);
+ itoa(n,rgch,10);
+ if (iassoc > 0)
+ strAssocs.append(" ");
+ strAssocs.append(rgch);
+ }
+ }
+ if (strAssocs.length() == 0)
+ strAssocs.assign("??");
+ ptman->LogXmlTagOpen(strmOut, "Associations", nIndent, false);
+ ptman->LogXmlTagAttr(strmOut, "slots", strAssocs.c_str());
+ //ptman->LogXmlTagPostAttrs(strmOut, false);
+ ptman->LogXmlTagClose(strmOut, "Associations", nIndent, false);
+ }
+
+ delete prgfMods;
+}
+
+#if XMLOMIT
+/*----------------------------------------------------------------------------------------------
+ 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 // XMLOMIT
+
+/*----------------------------------------------------------------------------------------------
+ Log the value of the slot attribute for the given slot. Assume it has changed.
+----------------------------------------------------------------------------------------------*/
+int GrSlotState::GetSlotAttrValue(std::ostream & strmOut, GrTableManager * ptman,
+ int ipass, int slat, int iIndex, bool fPreJust, bool fPostJust)
+{
+ switch (slat)
+ {
+ case kslatAdvX:
+ return int(m_mAdvanceX);
+ case kslatAdvY:
+ return int(m_mAdvanceY);
+
+ case kslatAttTo:
+ return m_srAttachTo;
+
+ case kslatAttAtX: // always do these in pairs
+ return m_mAttachAtX;
+ case kslatAttAtY:
+ return m_mAttachAtY;
+ case kslatAttAtGpt:
+ return m_nAttachAtGpoint;
+
+ case kslatAttAtXoff: // always do these in pairs
+ return m_mAttachAtXOffset;
+ case kslatAttAtYoff:
+ return m_mAttachAtYOffset;
+
+ case kslatAttWithX: // always do these in pairs
+ return m_mAttachWithX;
+ case kslatAttWithY:
+ return m_mAttachWithY;
+
+ case kslatAttWithGpt:
+ return m_nAttachWithGpoint;
+
+ case kslatAttWithXoff: // always do these in pairs
+ return m_mAttachWithXOffset;
+ case kslatAttWithYoff:
+ return m_mAttachWithYOffset;
+
+ case kslatAttLevel:
+ return m_nAttachLevel;
+
+ case kslatBreak:
+ return m_lb;
+
+ case kslatDir:
+ return m_dirc;
+
+ case kslatInsert:
+ return m_fInsertBefore;
+
+ case kslatShiftX:
+ return m_mShiftX;
+ case kslatShiftY:
+ return m_mShiftY;
+ case kslatMeasureSol:
+ return m_mMeasureSol;
+ case kslatMeasureEol:
+ return m_mMeasureEol;
+ case kslatJStretch:
+ return m_mJStretch0;
+ case kslatJShrink:
+ return m_mJShrink0;
+ case kslatJStep:
+ return m_mJStep0;
+ case kslatJWeight:
+ return m_nJWeight0;
+ case kslatJWidth:
+ return m_mJWidth0;
+
+ case kslatCompRef:
+ {
+ GrSlotState * pslotComp = reinterpret_cast<GrSlotState *>(CompRef(iIndex));
+ return pslotComp->m_islotTmpIn;
+ }
+
+ default:
+ if (kslatUserDefn <= slat && slat <= kslatUserDefn + m_cnUserDefn)
+ {
+ int iTmp = slat - kslatUserDefn;
+ return UserDefn(iTmp);
+ }
+ else
+ gAssert(false);
+ }
+
+ return 0;
+}
+
+#endif // TRACING
+
+
+/*----------------------------------------------------------------------------------------------
+ Write out the final underlying-to-surface associations.
+----------------------------------------------------------------------------------------------*/
+void Segment::LogXmlUnderlyingToSurface(std::ostream & strmOut, GrTableManager * ptman,
+ GrCharStream * pchstrm, int nIndent)
+{
+#ifdef TRACING
+
+ ptman->LogXmlTagOpen(strmOut, "UnderlyingToSurface", nIndent, true);
+ ptman->LogXmlTagPostAttrs(strmOut, true);
+
+ ptman->LogXmlUnderlyingAux(strmOut, pchstrm, -m_ichwAssocsMin, (m_ichwAssocsLim - m_ichwAssocsMin),
+ nIndent+1,
+ true, // text
+ false, // features
+ false, // color
+ true, // string offset
+ true, // ligature components
+ true // glyph associations
+ );
+
+ ptman->LogXmlTagClose(strmOut, "UnderlyingToSurface", nIndent, true);
+
+
+#ifdef XMLOMIT
+ ptman->LogXmlTagOpen(strmOut, "UnderlyingToSurface", nIndent, true);
+
+ size_t cassocs = 0;
+ int fLigs = false;
+ int ichw;
+ for (ichw = 0; ichw < (m_ichwAssocsLim - m_ichwAssocsMin); ichw++)
+ {
+ if (m_prgpvisloutAssocs[ichw])
+ cassocs = max(cassocs, m_prgpvisloutAssocs[ichw]->size());
+ if (m_prgisloutLigature[ichw] != kNegInfinity)
+ fLigs = true;
+ }
+
+ 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 (int ix = 1; ix < signed(cassocs) - 1; ix++) //(cassocs > 2)
+ {
+ if (ix == 1)
+ strmOut << "other ";
+ else
+ strmOut << " ";
+ for (ichw = 0; ichw < (m_ichwAssocsLim - m_ichwAssocsMin); ichw++)
+ {
+ std::vector<int> * pvislout = m_prgpvisloutAssocs[ichw];
+ if (pvislout == NULL)
+ strmOut << " ";
+ else if (signed(pvislout->size()) <= ix)
+ strmOut << " ";
+ else if ((*pvislout)[ix] != m_prgisloutAfter[ichw])
+ ptman->LogInTable(strmOut, (*pvislout)[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 // XMLOMIT
+#endif // TRACING
+
+}
+
+#ifdef XMLOMIT
+
+/*----------------------------------------------------------------------------------------------
+ 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());
+ 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 // TRACING
+}
+
+#ifdef TRACING
+/*----------------------------------------------------------------------------------------------
+ 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 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
+
+#endif // XMLOMIT
+
+#ifdef TRACING
+/*----------------------------------------------------------------------------------------------
+ Write an XML tag, leaving it open to possibly add attributes.
+----------------------------------------------------------------------------------------------*/
+void GrTableManager::LogXmlTagOpen(std::ostream & strmOut, std::string strTag, size_t nIndent,
+ bool fContent)
+{
+ for (size_t i = 0; i < nIndent; i++)
+ strmOut << " ";
+
+ strmOut << "<" << strTag;
+}
+
+/*----------------------------------------------------------------------------------------------
+ Write the termination of the opening XML tag.
+----------------------------------------------------------------------------------------------*/
+void GrTableManager::LogXmlTagPostAttrs(std::ostream & strmOut,
+ bool fContent)
+{
+ if (fContent)
+ strmOut << ">\n";
+}
+
+/*----------------------------------------------------------------------------------------------
+ Write the closing form of the XML tag.
+----------------------------------------------------------------------------------------------*/
+void GrTableManager::LogXmlTagClose(std::ostream & strmOut, std::string strTag, size_t nIndent,
+ bool fContent)
+{
+ if (fContent)
+ {
+ for (size_t i = 0; i < nIndent; i++)
+ strmOut << " ";
+ strmOut << "</" << strTag << ">\n";
+ }
+ else
+ {
+ strmOut << " />\n";
+ }
+}
+
+/*----------------------------------------------------------------------------------------------
+ Write an attribute/value pair inside of a tag.
+----------------------------------------------------------------------------------------------*/
+void GrTableManager::LogXmlTagAttr(std::ostream & strmOut, std::string strAttr, int nValue,
+ size_t nIndent)
+{
+ char chBuf[20];
+ itoa(nValue, chBuf, 10);
+ LogXmlTagAttr(strmOut, strAttr, chBuf, nIndent);
+}
+
+void GrTableManager::LogXmlTagAttr(std::ostream & strmOut, std::string strAttr, const char * szValue,
+ size_t nIndent)
+{
+ if (nIndent > 0)
+ {
+ strmOut << "\n";
+ for (size_t i = 0; i < nIndent + 2; i++)
+ strmOut << " ";
+ }
+ else
+ strmOut << " ";
+
+ strmOut << strAttr << "=\"" << szValue << "\"";
+}
+
+void GrTableManager::LogXmlTagAttrHex(std::ostream & strmOut, std::string strAttr, int nValue,
+ size_t nIndent)
+{
+ char chBuf[20];
+ chBuf[0] = chBuf[1] = chBuf[2] = '0';
+ if (nValue < 0x10)
+ itoa(nValue, chBuf+3, 16); // prepend 3 zeros
+ else if (nValue < 0x100)
+ itoa(nValue, chBuf+2, 16); // prepend 2 zeros
+ else if (nValue < 0x1000)
+ itoa(nValue, chBuf+1, 16); // prepend 1 zero
+ else
+ itoa(nValue, chBuf, 16);
+ LogXmlTagAttr(strmOut, strAttr, chBuf, nIndent);
+}
+
+/*----------------------------------------------------------------------------------------------
+ Concatenate the hex form of the given numbers into a string.
+----------------------------------------------------------------------------------------------*/
+std::string GrTableManager::HexString(std::vector<int> vn)
+{
+ std::string strRet;
+ for (size_t in = 0; in < vn.size(); in++)
+ {
+ char rgch[20];
+ itoa(vn[in],rgch,16);
+ strRet.append(rgch);
+ if (in < vn.size() - 1)
+ strRet.append(" ");
+ }
+ return strRet;
+}
+
+std::string GrTableManager::HexString(int n1, int n2, int n3, int n4, int n5, int n6)
+{
+ std::vector<int> vn;
+ if (n1 > 0)
+ {
+ vn.push_back(n1);
+ if (n2 > 0)
+ {
+ vn.push_back(n2);
+ if (n3 > 0)
+ {
+ vn.push_back(n3);
+ if (n4 > 0)
+ {
+ vn.push_back(n4);
+ if (n5 > 0)
+ {
+ vn.push_back(n5);
+ if (n6 > 0)
+ vn.push_back(n6);
+ }
+ }
+ }
+ }
+ }
+ return HexString(vn);
+}
+
+/*----------------------------------------------------------------------------------------------
+ Write an attribute/value pair inside of a tag.
+----------------------------------------------------------------------------------------------*/
+void GrTableManager::LogXmlTagColor(std::ostream & strmOut, std::string strAttr, int clrValue,
+ bool fBack, size_t nIndent)
+{
+ // Ignore black foreground and white background.
+ if (!fBack && clrValue == kclrBlack)
+ return;
+ if (fBack && (clrValue == kclrWhite || clrValue == kclrTransparent))
+ return;
+
+ std::string strValue;
+ char rgch[20];
+ switch (clrValue)
+ {
+ case kclrBlack: strValue = "black"; break;
+ case kclrWhite: strValue = "white"; break;
+ case kclrRed: strValue = "red"; break;
+ case kclrGreen: strValue = "green"; break;
+ case kclrBlue: strValue = "blue"; break;
+ case 0x00ffff: strValue = "yellow"; break;
+ case 0xff00ff: strValue = "magenta"; break;
+ case 0xffff00: strValue = "cyan"; break;
+ default:
+ rgch[0] = '#';
+ itoa(clrValue, rgch+1, 16);
+ strValue.assign(rgch);
+ break;
+ }
+
+ LogXmlTagAttr(strmOut, strAttr, strValue.c_str(), nIndent);
+}
+
+/*----------------------------------------------------------------------------------------------
+ Write a directionality code to the table.
+----------------------------------------------------------------------------------------------*/
+void GrTableManager::LogXmlDirCode(std::ostream & strmOut, std::string strAttr, int dircValue,
+ size_t nIndent)
+{
+ std::string strValue;
+ char rgch[20];
+ switch (dircValue)
+ {
+ case kdircUnknown: strValue = "unknown"; break;
+ case kdircNeutral: strValue = "ON"; break;
+ case kdircL: strValue = "L"; break;
+ case kdircR: strValue = "R"; break;
+ case kdircRArab: strValue = "AR"; break;
+ case kdircEuroNum: strValue = "EN"; break;
+ case kdircEuroSep: strValue = "ES"; break;
+ case kdircEuroTerm: strValue = "ET"; break;
+ case kdircArabNum: strValue = "AN"; break;
+ case kdircComSep: strValue = "CS"; break;
+ case kdircWhiteSpace: strValue = "WS"; break;
+ case kdircBndNeutral: strValue = "BN"; break;
+ case kdircNSM: strValue = "NSM"; break;
+ case kdircLRO: strValue = "LRO"; break;
+ case kdircRLO: strValue = "RLO"; break;
+ case kdircLRE: strValue = "LRE"; break;
+ case kdircRLE: strValue = "RLE"; break;
+ case kdircPDF: strValue = "PDF"; break;
+ case kdircPdfL: strValue = "PDF-L"; break;
+ case kdircPdfR: strValue = "PDF-R"; break;
+ case kdircLlb: strValue = "L"; break;
+ case kdircRlb: strValue = "R"; break;
+ default:
+ itoa(dircValue, rgch, 10);
+ strValue.assign(rgch);
+ break;
+ }
+ LogXmlTagAttr(strmOut, strAttr, strValue.c_str(), nIndent);
+}
+
+/*----------------------------------------------------------------------------------------------
+ Write a breakweight code to the table.
+----------------------------------------------------------------------------------------------*/
+void GrTableManager::LogXmlBreakWeight(std::ostream & strmOut, std::string strAttr, int lbValue,
+ size_t nIndent)
+{
+ std::string strValue;
+ char rgch[20];
+ if (lbValue < 0)
+ {
+ lbValue = lbValue * -1;
+ switch (lbValue)
+ {
+ case klbWsBreak: strValue = "-ws"; break;
+ case klbWordBreak: strValue = "-word"; break;
+ case klbHyphenBreak: strValue = "-intra"; break;
+ case klbLetterBreak: strValue = "-letter"; break;
+ case klbClipBreak: strValue = "-clip"; break;
+ default:
+ itoa(lbValue * -1, rgch, 10);
+ strValue.assign(rgch);
+ break;
+ }
+ }
+ else
+ {
+ switch (lbValue)
+ {
+ case klbNoBreak: strValue = "none"; break;
+ case klbWsBreak: strValue = "ws"; break;
+ case klbWordBreak: strValue = "word"; break;
+ case klbHyphenBreak: strValue = "intra"; break;
+ case klbLetterBreak: strValue = "letter"; break;
+ case klbClipBreak: strValue = "clip"; break;
+ default:
+ itoa(lbValue, rgch, 10);
+ strValue.assign(rgch);
+ break;
+ }
+ }
+ LogXmlTagAttr(strmOut, strAttr, strValue.c_str(), nIndent);
+}
+
+/*----------------------------------------------------------------------------------------------
+ Add a comment to the log file.
+----------------------------------------------------------------------------------------------*/
+void GrTableManager::LogXmlComment(std::ostream & strmOut, std::string strComment,
+ size_t nIndent)
+{
+ for (size_t i = 0; i < nIndent; i++)
+ strmOut << " ";
+
+ strmOut << "<!-- " << strComment << " -->\n";
+}
+
+
+#endif // TRACING
+
+} // namespace gr
diff --git a/Build/source/libs/graphite/src/segment/resource.h b/Build/source/libs/graphite/src/segment/resource.h
new file mode 100644
index 00000000000..775418f76bf
--- /dev/null
+++ b/Build/source/libs/graphite/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