You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
2626 lines
83 KiB
2626 lines
83 KiB
/******************************************************************************
|
|
|
|
Source File: Glyph Translation.CPP
|
|
|
|
This implements the classes which encode glyph mapping information.
|
|
|
|
Copyright (c) 1997 by Microsoft Corporation. All rights reserved.
|
|
|
|
A Pretty Penny Enterprises Production
|
|
|
|
Change History:
|
|
02-13-97 [email protected]
|
|
|
|
******************************************************************************/
|
|
|
|
#include "StdAfx.H"
|
|
#include "Resource.H"
|
|
|
|
// Psuedo definition of DESIGNVECTOR.
|
|
#if (_WIN32_WINNT < 0x0500)
|
|
typedef unsigned long DESIGNVECTOR;
|
|
#endif
|
|
|
|
#include "GTT.H"
|
|
#include <CodePage.H>
|
|
#include <wingdi.h>
|
|
#include <winddi.h>
|
|
#include <prntfont.h>
|
|
#include <uni16res.h>
|
|
#define _FMNEWFM_H_
|
|
#include "ProjRec.h"
|
|
#include "ChildFrm.H"
|
|
#include "GTTView.H"
|
|
#include "minidev.h"
|
|
|
|
extern "C"
|
|
BOOL
|
|
BConvertCTT2GTT(
|
|
IN HANDLE hHeap,
|
|
IN PTRANSTAB pCTTData,
|
|
IN DWORD dwCodePage,
|
|
IN WCHAR wchFirst,
|
|
IN WCHAR wchLast,
|
|
IN PBYTE pCPSel,
|
|
IN PBYTE pCPUnSel,
|
|
IN OUT PUNI_GLYPHSETDATA *ppGlyphSetData,
|
|
IN DWORD dwGlySize);
|
|
|
|
extern "C"
|
|
PUNI_GLYPHSETDATA
|
|
PGetDefaultGlyphset(
|
|
IN HANDLE hHeap,
|
|
IN WORD wFirstChar,
|
|
IN WORD wLastChar,
|
|
IN DWORD dwCodePage) ;
|
|
|
|
struct sRLE {
|
|
enum {Direct = 10, Paired, LengthOffset, LengthIndexOffset, Offset};
|
|
WORD m_wFormat;
|
|
WORD m_widRLE; // Must have unique "magic" value 0x78FE
|
|
DWORD m_dwcbThis; // Total size of the memory image.
|
|
WCHAR m_wcFirst, m_wcLast;
|
|
// Handle mapping data follows
|
|
DWORD m_dwcbImage; // Size of the handle mapping data only
|
|
DWORD m_dwFlag;
|
|
DWORD m_dwcGlyphs, m_dwcRuns;
|
|
};
|
|
|
|
union uencCTT {
|
|
WORD wOffset;
|
|
BYTE m_bDirect; // This member is used in GTT only!
|
|
BYTE abPaired[2];
|
|
};
|
|
|
|
struct sMapTableEntry {
|
|
enum {Composed = 1, Direct = 2, Paired = 4, Format = 7, SingleByte,
|
|
DoubleByte = 0x10, DBCS = 0x18, Replace = 0x20, Add = 0x40,
|
|
Disable = 0x80, PredefinedMask = 0xE0};
|
|
BYTE m_bCodePageIndex, m_bfType;
|
|
uencCTT m_uectt;
|
|
};
|
|
|
|
// Since I don't build the map table in memory, there is no need to declare
|
|
// the fact that the array of entries follows it
|
|
struct sMapTable {
|
|
DWORD m_dwcbImage, m_dwcEntries;
|
|
sMapTable(unsigned ucEntries) {
|
|
m_dwcbImage = sizeof *this + ucEntries * sizeof (sMapTableEntry);
|
|
m_dwcEntries = ucEntries; }
|
|
};
|
|
|
|
// Use a static for Code Page information- gets the max benefit from caching
|
|
|
|
static CCodePageInformation* pccpi = NULL ; // Use a static CCodePageInformation to derive more benefit from caching
|
|
|
|
/******************************************************************************
|
|
CInvocation class implementation
|
|
|
|
******************************************************************************/
|
|
|
|
IMPLEMENT_SERIAL(CInvocation, CObject, 0)
|
|
|
|
void CInvocation::Encode(BYTE c, CString& cs) const {
|
|
if (isprint(c))
|
|
if (c != _TEXT('\\'))
|
|
cs = c;
|
|
else
|
|
cs = _TEXT("\\\\");
|
|
else
|
|
cs.Format(_TEXT("\\x%2.2x"), c);
|
|
}
|
|
|
|
/******************************************************************************
|
|
|
|
CInvocation::Init
|
|
|
|
This copies a series of bytes into the invocation. Since the data structures
|
|
used to represent these lend themselves most readily to this, this is the
|
|
normal method used in reading info from a file.
|
|
|
|
******************************************************************************/
|
|
|
|
void CInvocation::Init(PBYTE pb, unsigned ucb) {
|
|
m_cbaEncoding.RemoveAll();
|
|
|
|
while (ucb--)
|
|
m_cbaEncoding.Add(*pb++);
|
|
}
|
|
|
|
void CInvocation::GetInvocation(CString& cs) const {
|
|
CString csWork;
|
|
|
|
cs.Empty();
|
|
for (int i = 0; i < m_cbaEncoding.GetSize(); i++) {
|
|
Encode(m_cbaEncoding[i], csWork);
|
|
cs += csWork;
|
|
}
|
|
}
|
|
|
|
// This member converts a C-Style encoding of an invocation into
|
|
// byte form and stores it.
|
|
|
|
void CInvocation::SetInvocation(LPCTSTR lpstrNew) {
|
|
|
|
CString csWork(lpstrNew);
|
|
|
|
m_cbaEncoding.RemoveAll();
|
|
|
|
while (!csWork.IsEmpty()) {
|
|
CString csClean = csWork.SpanExcluding("\\");
|
|
|
|
if (!csClean.IsEmpty()) {
|
|
for (int i = 0; i < csClean.GetLength(); i++)
|
|
m_cbaEncoding.Add((BYTE) csClean[i]);
|
|
csWork = csWork.Mid(csClean.GetLength());
|
|
continue;
|
|
}
|
|
|
|
// A backslash has been found. If the string ends with a backslash, we
|
|
// can't let the function assert in the switch statement so just return
|
|
// and ignore the terminating backslash. Yes, I know the string is
|
|
// invalid if it ends this way but, according to MS, this isn't a field
|
|
// they want validated.
|
|
|
|
if (csWork.GetLength() <= 1)
|
|
return ;
|
|
|
|
// OK, we have something to decode
|
|
|
|
switch (csWork[1]) {
|
|
|
|
case _TEXT('r'):
|
|
m_cbaEncoding.Add(13);
|
|
csWork = csWork.Mid(2);
|
|
continue;
|
|
|
|
case _TEXT('n'):
|
|
m_cbaEncoding.Add(10);
|
|
csWork = csWork.Mid(2);
|
|
continue;
|
|
|
|
case _TEXT('b'):
|
|
m_cbaEncoding.Add('\b');
|
|
csWork = csWork.Mid(2);
|
|
continue;
|
|
|
|
case _TEXT('\t'):
|
|
m_cbaEncoding.Add(9);
|
|
csWork = csWork.Mid(2);
|
|
continue;
|
|
|
|
case _TEXT('x'):
|
|
case _TEXT('X'):
|
|
{
|
|
|
|
CString csNumber = csWork.Mid(2,2).SpanIncluding(
|
|
_TEXT("1234567890abcdefABCDEF"));
|
|
|
|
csWork = csWork.Mid(2 + csNumber.GetLength());
|
|
unsigned u;
|
|
|
|
#if defined(UNICODE) || defined(_UNICODE)
|
|
#define _tsscanf swscanf
|
|
#else
|
|
#define _tsscanf sscanf
|
|
#endif
|
|
|
|
_tsscanf(csNumber, _TEXT("%x"), &u);
|
|
m_cbaEncoding.Add((BYTE)u);
|
|
continue;
|
|
}
|
|
|
|
// TODO: octal encodings are pretty common
|
|
|
|
default:
|
|
m_cbaEncoding.Add(
|
|
(BYTE) csWork[(int)(csWork.GetLength() != 1)]);
|
|
csWork = csWork.Mid(2);
|
|
continue;
|
|
}
|
|
}
|
|
|
|
// We've done it!
|
|
}
|
|
|
|
// This member function records the offset for its image (if any) and updates
|
|
// the given offset to reflect this
|
|
|
|
void CInvocation::NoteOffset(DWORD& dwOffset) {
|
|
m_dwOffset = Length() ? dwOffset : 0;
|
|
dwOffset += Length();
|
|
}
|
|
|
|
// I/O routines, both native and document form
|
|
|
|
void CInvocation::WriteSelf(CFile& cfTarget) const {
|
|
DWORD dwWork = Length();
|
|
cfTarget.Write(&dwWork, sizeof dwWork);
|
|
cfTarget.Write(&m_dwOffset, sizeof m_dwOffset);
|
|
}
|
|
|
|
void CInvocation::WriteEncoding(CFile& cfTarget, BOOL bWriteLength) const {
|
|
if (bWriteLength) {
|
|
WORD w = (WORD)Length();
|
|
cfTarget.Write(&w, sizeof w);
|
|
}
|
|
|
|
cfTarget.Write(m_cbaEncoding.GetData(), Length());
|
|
}
|
|
|
|
void CInvocation::Serialize(CArchive& car) {
|
|
CObject::Serialize(car);
|
|
m_cbaEncoding.Serialize(car);
|
|
}
|
|
|
|
/******************************************************************************
|
|
CGlyphHandle class implementation
|
|
|
|
******************************************************************************/
|
|
|
|
|
|
CGlyphHandle::CGlyphHandle()
|
|
{
|
|
m_wPredefined = m_wIndex = 0;
|
|
m_dwCodePage = m_dwidCodePage = m_dwOffset = 0;
|
|
|
|
// Allocate a CCodePageInformation class if needed.
|
|
|
|
if (pccpi == NULL)
|
|
pccpi = new CCodePageInformation ;
|
|
}
|
|
|
|
|
|
unsigned CGlyphHandle::CompactSize() const {
|
|
return (m_ciEncoding.Length() < 3) ? 0 : m_ciEncoding.Length();
|
|
}
|
|
|
|
/******************************************************************************
|
|
|
|
CGlyphHandle::operator ==
|
|
|
|
Returns true if the encoding, code point, and code page IDs (but maybe not
|
|
the indices) are the same.
|
|
|
|
******************************************************************************/
|
|
|
|
BOOL CGlyphHandle::operator ==(CGlyphHandle& cghRef) {
|
|
|
|
if (cghRef.m_wCodePoint != m_wCodePoint ||
|
|
cghRef.m_dwCodePage != m_dwCodePage ||
|
|
m_ciEncoding.Length() != cghRef.m_ciEncoding.Length())
|
|
return FALSE;
|
|
|
|
for (int i = 0; i < (int) m_ciEncoding.Length(); i++)
|
|
if (m_ciEncoding[i] != cghRef.m_ciEncoding[i])
|
|
return FALSE;
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
/******************************************************************************
|
|
|
|
CGlyphHandle::Init
|
|
|
|
This function has three overloads, for intializing from direc, paired, or
|
|
composed data.
|
|
|
|
******************************************************************************/
|
|
|
|
void CGlyphHandle::Init(BYTE b, WORD wIndex, WORD wCode) {
|
|
m_wIndex = wIndex;
|
|
m_wCodePoint = wCode;
|
|
m_ciEncoding.Init(&b, 1);
|
|
}
|
|
|
|
void CGlyphHandle::Init(BYTE ab[2], WORD wIndex, WORD wCode) {
|
|
m_wIndex = wIndex;
|
|
m_wCodePoint = wCode;
|
|
m_ciEncoding.Init(ab, 2);
|
|
}
|
|
|
|
void CGlyphHandle::Init(PBYTE pb, unsigned ucb, WORD wIndex, WORD wCode) {
|
|
m_wIndex = wIndex;
|
|
m_wCodePoint = wCode;
|
|
m_ciEncoding.Init(pb, ucb);
|
|
}
|
|
|
|
/******************************************************************************
|
|
|
|
CGlyphHandle::operator =
|
|
|
|
This is a copy (assignment) operator for the class.
|
|
|
|
******************************************************************************/
|
|
|
|
CGlyphHandle& CGlyphHandle::operator =(CGlyphHandle& cghTemplate) {
|
|
m_dwCodePage = cghTemplate.m_dwCodePage;
|
|
m_dwidCodePage = cghTemplate.m_dwidCodePage;
|
|
m_wCodePoint = cghTemplate.m_wCodePoint;
|
|
m_ciEncoding = cghTemplate.m_ciEncoding;
|
|
return *this;
|
|
}
|
|
|
|
// This member records the current offset for the data in RLE format, and
|
|
// then updates it to account for the length of any data that will go into
|
|
// the extra storage at the end of the file/
|
|
|
|
void CGlyphHandle::RLEOffset(DWORD& dwOffset, const BOOL bCompact) {
|
|
if (m_ciEncoding.Length() < 3)
|
|
return; // Don't need it, and don't use it!
|
|
|
|
m_dwOffset = dwOffset;
|
|
|
|
dwOffset += bCompact ? CompactSize() : MaximumSize();
|
|
}
|
|
|
|
/******************************************************************************
|
|
|
|
CGlyphHandle::GTTOffset
|
|
|
|
This member records the current offset for where our data will go, then adds
|
|
the length of the encoding string to it and updates the offset. It will only
|
|
be updated if the encoding must be composed data. The encoding length
|
|
includes a WORD length in the GTT world. Data of 1 byte, or of 2 if DBCS or
|
|
a Paired font, does not add any length.
|
|
|
|
******************************************************************************/
|
|
|
|
void CGlyphHandle::GTTOffset(DWORD& dwOffset, BOOL bPaired) {
|
|
if (m_ciEncoding.Length() >
|
|
(unsigned) 1 + (bPaired || pccpi->IsDBCS(m_dwCodePage))) {
|
|
m_dwOffset = dwOffset;
|
|
dwOffset += m_ciEncoding.Length() + sizeof m_wIndex;
|
|
}
|
|
else
|
|
m_dwOffset = 0;
|
|
}
|
|
|
|
// These members write our vital stuff to a given file.
|
|
|
|
void CGlyphHandle::WriteRLE(CFile& cfTarget, WORD wFormat) const {
|
|
// This is the RLE-specific glyph handle encoding
|
|
|
|
union {
|
|
DWORD dwOffset;
|
|
struct {
|
|
union {
|
|
struct {
|
|
BYTE bFirst, bSecond;
|
|
};
|
|
WORD wOffset;
|
|
};
|
|
union {
|
|
struct {
|
|
BYTE bIndexOrHiOffset, bLength;
|
|
};
|
|
WORD wIndex;
|
|
};
|
|
};
|
|
};
|
|
|
|
switch (wFormat) {
|
|
case sRLE::Direct:
|
|
case sRLE::Paired:
|
|
|
|
bFirst = m_ciEncoding[0];
|
|
bSecond = m_ciEncoding[1];
|
|
wIndex = m_wIndex;
|
|
break;
|
|
|
|
case sRLE::LengthIndexOffset:
|
|
|
|
if (!CompactSize()) { // Encode it in the first two bytes
|
|
bFirst = m_ciEncoding[0];
|
|
bSecond = m_ciEncoding[1];
|
|
}
|
|
else
|
|
wOffset = (WORD) m_dwOffset;
|
|
|
|
bIndexOrHiOffset = (BYTE) m_wIndex;
|
|
bLength = (BYTE)m_ciEncoding.Length();
|
|
break;
|
|
|
|
case sRLE::LengthOffset:
|
|
|
|
if (!CompactSize()) { // Encode it in the first two bytes
|
|
bFirst = m_ciEncoding[0];
|
|
bSecond = m_ciEncoding[1];
|
|
bIndexOrHiOffset = (BYTE) m_wIndex;
|
|
bLength = (BYTE)m_ciEncoding.Length();
|
|
break;
|
|
}
|
|
|
|
dwOffset = m_dwOffset;
|
|
bLength = (BYTE)m_ciEncoding.Length();
|
|
break;
|
|
|
|
case sRLE::Offset:
|
|
dwOffset = m_dwOffset;
|
|
break;
|
|
|
|
default:
|
|
_ASSERTE(FALSE);
|
|
// Should probably throw an exception...
|
|
}
|
|
|
|
cfTarget.Write(&dwOffset, sizeof dwOffset);
|
|
}
|
|
|
|
/******************************************************************************
|
|
|
|
CGlyphHandle::WriteGTT
|
|
|
|
This member function writes the GTT map table entry for this glyph in the
|
|
requested format.
|
|
|
|
******************************************************************************/
|
|
|
|
static BYTE abFlags[] = {sMapTableEntry::Replace, sMapTableEntry::Add,
|
|
sMapTableEntry::Disable};
|
|
|
|
void CGlyphHandle::WriteGTT(CFile& cfTarget, BOOL bPredefined) const {
|
|
sMapTableEntry smte;
|
|
|
|
smte.m_bCodePageIndex = (bPredefined && m_wPredefined == Removed) ?
|
|
0 : (BYTE) m_dwidCodePage;
|
|
|
|
// GTTOffset set m_dwOffset if Composed is needed. Otherwise we can tell
|
|
// the proper flags by looking at the length and whether it is DBCS or not
|
|
|
|
if (m_dwOffset) {
|
|
smte.m_uectt.wOffset = (WORD) m_dwOffset;
|
|
smte.m_bfType = sMapTableEntry::Composed;
|
|
}
|
|
else {
|
|
smte.m_bfType = pccpi->IsDBCS(m_dwCodePage) ?
|
|
((m_ciEncoding.Length() == 2) ?
|
|
sMapTableEntry::Paired : sMapTableEntry::Direct ) |
|
|
(pccpi->IsDBCS(m_dwCodePage, m_wCodePoint) ?
|
|
sMapTableEntry::DoubleByte : sMapTableEntry::SingleByte) :
|
|
(m_ciEncoding.Length() == 2) ?
|
|
sMapTableEntry::Paired : sMapTableEntry::Direct;
|
|
|
|
smte.m_uectt.abPaired[0] = m_ciEncoding[0];
|
|
smte.m_uectt.abPaired[1] = m_ciEncoding[1];
|
|
}
|
|
|
|
if (bPredefined)
|
|
smte.m_bfType |= abFlags[m_wPredefined];
|
|
|
|
// Just write it out!
|
|
cfTarget.Write(&smte, sizeof smte);
|
|
}
|
|
|
|
/******************************************************************************
|
|
|
|
CGlyphHandle::WriteEncoding
|
|
|
|
This method writes the encoding to to the file in the desired format. The
|
|
formats are:
|
|
|
|
GTT- write nothing if not composed. If composed, write the length, and then
|
|
the encoding.
|
|
|
|
RLESmall- just the encoding
|
|
RLEBig- the index and the encoding.
|
|
|
|
******************************************************************************/
|
|
|
|
void CGlyphHandle::WriteEncoding(CFile& cfTarget, WORD wfHow) const {
|
|
if (!m_dwOffset)
|
|
return; // Nothing to write
|
|
|
|
if (wfHow == RLEBig)
|
|
cfTarget.Write(&m_wIndex, sizeof m_wIndex);
|
|
|
|
m_ciEncoding.WriteEncoding(cfTarget, wfHow == GTT);
|
|
|
|
}
|
|
|
|
/******************************************************************************
|
|
|
|
CRunRecord class implementation
|
|
|
|
******************************************************************************/
|
|
|
|
CRunRecord::CRunRecord(CGlyphHandle *pcgh, CRunRecord *pcrrPrevious) {
|
|
m_wFirst = pcgh -> CodePoint();
|
|
m_wcGlyphs = 1;
|
|
m_dwOffset = 0;
|
|
m_cpaGlyphs.Add(pcgh);
|
|
|
|
// Maintain that old double chain!
|
|
|
|
m_pcrrPrevious = pcrrPrevious;
|
|
m_pcrrNext = m_pcrrPrevious -> m_pcrrNext;
|
|
m_pcrrPrevious -> m_pcrrNext = this;
|
|
if (m_pcrrNext)
|
|
m_pcrrNext -> m_pcrrPrevious = this;
|
|
}
|
|
|
|
/******************************************************************************
|
|
|
|
CRunRecord::CRunRecord(CRunRecord *pcrrPrevious, WORD wFirst)
|
|
|
|
This private constructor is the second tail record initializer. It is called
|
|
when a run is split due to a glyph deletion. In this case, we need to hook
|
|
into the chain, then fill in the details from our predecessor. wFirst tells
|
|
us where to begin extracting data from said predecessor.
|
|
|
|
******************************************************************************/
|
|
|
|
CRunRecord::CRunRecord(CRunRecord* pcrrPrevious, WORD wFirst) {
|
|
m_pcrrPrevious = pcrrPrevious;
|
|
m_pcrrNext = pcrrPrevious -> m_pcrrNext;
|
|
|
|
if (m_pcrrNext)
|
|
m_pcrrNext -> m_pcrrPrevious = this;
|
|
m_pcrrPrevious -> m_pcrrNext = this;
|
|
|
|
m_wFirst = m_wcGlyphs = 0;
|
|
m_dwOffset = 0;
|
|
|
|
// That's the normal empty initialization. Now, er fill ourselves from
|
|
// our predecessor
|
|
|
|
for (; wFirst < pcrrPrevious -> Glyphs(); wFirst++)
|
|
Add(&pcrrPrevious -> Glyph(wFirst));
|
|
}
|
|
|
|
/******************************************************************************
|
|
|
|
CRunRecord::CRunRecord(CRunRecord *pcrrPrevious)
|
|
|
|
This private constructor is the third and final tail record initializer. It
|
|
makes an exact duplicate of the previous record, then links itself into the
|
|
chain appropriately.
|
|
|
|
This constructor is necessary when a new code point is inserted ahead of the
|
|
earliest code point in the set of run records without extending the first
|
|
run.
|
|
|
|
******************************************************************************/
|
|
|
|
CRunRecord::CRunRecord(CRunRecord *pcrrPrevious) {
|
|
m_wFirst = pcrrPrevious -> m_wFirst;
|
|
m_wcGlyphs = pcrrPrevious -> m_wcGlyphs;
|
|
m_pcrrNext = pcrrPrevious -> m_pcrrNext;
|
|
m_pcrrPrevious = pcrrPrevious;
|
|
m_pcrrPrevious -> m_pcrrNext = this;
|
|
if (m_pcrrNext)
|
|
m_pcrrNext -> m_pcrrPrevious = this;
|
|
m_cpaGlyphs.Copy(pcrrPrevious -> m_cpaGlyphs);
|
|
}
|
|
|
|
// Initialize empty- this is used for the root record only
|
|
|
|
CRunRecord::CRunRecord() {
|
|
m_wFirst = m_wcGlyphs = 0;
|
|
m_dwOffset = 0;
|
|
m_pcrrNext = m_pcrrPrevious = NULL;
|
|
}
|
|
|
|
CRunRecord::~CRunRecord() {
|
|
if (m_pcrrNext)
|
|
delete m_pcrrNext;
|
|
}
|
|
|
|
unsigned CRunRecord::TotalGlyphs() const {
|
|
return m_pcrrNext ?
|
|
m_wcGlyphs + m_pcrrNext -> TotalGlyphs() : m_wcGlyphs;
|
|
}
|
|
|
|
BOOL CRunRecord::MustCompose() const {
|
|
for (unsigned u = 0; u < m_wcGlyphs; u++)
|
|
if (GlyphData(u).CompactSize())
|
|
return TRUE; // No need to look further
|
|
|
|
return m_pcrrNext ? m_pcrrNext -> MustCompose() : FALSE;
|
|
}
|
|
|
|
unsigned CRunRecord::ExtraNeeded(BOOL bCompact) {
|
|
unsigned uNeeded = 0;
|
|
|
|
for (unsigned u = 0; u < m_wcGlyphs; u++)
|
|
uNeeded += bCompact ? Glyph(u).CompactSize() : Glyph(u).MaximumSize();
|
|
|
|
return uNeeded + (m_pcrrNext ? m_pcrrNext -> ExtraNeeded() : 0);
|
|
}
|
|
|
|
/******************************************************************************
|
|
|
|
CRunRecord::GetGlyph()
|
|
|
|
This returns the nth handle in the run. We use recursion. This could get
|
|
bad enough in terms of performance (ony used to fill glyph map page in
|
|
editor) that we drop it, but I'll try it first.
|
|
|
|
******************************************************************************/
|
|
|
|
CGlyphHandle* CRunRecord::GetGlyph(unsigned u) const {
|
|
|
|
if (u < m_wcGlyphs)
|
|
return (CGlyphHandle *) m_cpaGlyphs[u];
|
|
return m_pcrrNext ? m_pcrrNext -> GetGlyph(u - m_wcGlyphs) : NULL;
|
|
}
|
|
|
|
/******************************************************************************
|
|
|
|
CRunRecord::Add
|
|
|
|
This member adds a glyph to the set of run records. This can mean adding an
|
|
additional record at the beginning or end of the set, extending an existing
|
|
record at either the beginning or end, and in those cases, orentiay merging
|
|
two records together.
|
|
|
|
******************************************************************************/
|
|
|
|
void CRunRecord::Add(CGlyphHandle *pcgh) {
|
|
WCHAR wcNew = pcgh -> CodePoint();
|
|
// If the glyph is already in the run, just update the info on it.
|
|
|
|
if (m_wcGlyphs && wcNew >= m_wFirst && wcNew < m_wFirst + m_wcGlyphs){
|
|
m_cpaGlyphs.SetAt(wcNew - m_wFirst, pcgh);
|
|
return;
|
|
}
|
|
|
|
// If this is the first record, and the glyph falls ahead of our first
|
|
// entry, we must clone ourselves, and become a one-glyph run. We cannot
|
|
// insert a record in front of oursleves as we are embedded in the
|
|
// glyph map structure directly.
|
|
|
|
if (m_wcGlyphs && wcNew < m_wFirst - 1) {
|
|
// This can only happen to the first record- otherwise the tail logic
|
|
// below would prevent this occurence
|
|
|
|
_ASSERTE(!m_pcrrPrevious);
|
|
|
|
// Clone us, using the copy contructor
|
|
|
|
CRunRecord *pcrr = new CRunRecord(this);
|
|
|
|
m_wFirst = pcgh -> CodePoint();
|
|
m_wcGlyphs = 1;
|
|
m_cpaGlyphs.RemoveAll();
|
|
m_cpaGlyphs.Add(pcgh);
|
|
return;
|
|
}
|
|
|
|
if (m_wcGlyphs && wcNew != m_wFirst + m_wcGlyphs &&
|
|
wcNew != m_wFirst - 1) {
|
|
|
|
// This belongs in some other record- pass it down the line, or
|
|
// append a new one.
|
|
|
|
if (m_pcrrNext)
|
|
// If this falls ahead of the next record, we must insert one now
|
|
|
|
if (wcNew < m_pcrrNext -> m_wFirst - 1)
|
|
m_pcrrNext = new CRunRecord(pcgh, this);
|
|
else
|
|
m_pcrrNext -> Add(pcgh);
|
|
else
|
|
m_pcrrNext = new CRunRecord(pcgh, this);
|
|
}
|
|
else {
|
|
// We're adding either at the front or the back, so do it right!
|
|
if (m_wFirst > wcNew) {
|
|
m_cpaGlyphs.InsertAt(0, pcgh);
|
|
m_wFirst = wcNew;
|
|
}
|
|
else
|
|
m_cpaGlyphs.Add(pcgh);
|
|
|
|
// This belonged here, so add it in- the root record begins with
|
|
// 0 glyphs, so if this is the first, keep track of it.
|
|
|
|
if (!m_wcGlyphs++)
|
|
m_wFirst = wcNew;
|
|
|
|
// If there is a following run, see if we need to merge it.
|
|
|
|
if (m_pcrrNext &&
|
|
m_pcrrNext -> m_wFirst == m_wFirst + m_wcGlyphs) {
|
|
// Merge the records.
|
|
|
|
m_cpaGlyphs.Append(m_pcrrNext -> m_cpaGlyphs);
|
|
m_wcGlyphs += m_pcrrNext -> m_wcGlyphs;
|
|
|
|
// Time to update the list. The class destructor removes the
|
|
// tail records, so that pointer must be set to NULL before the
|
|
// merged record is deleted.
|
|
|
|
CRunRecord *pcrrDead = m_pcrrNext;
|
|
|
|
m_pcrrNext = m_pcrrNext -> m_pcrrNext;
|
|
|
|
if (m_pcrrNext)
|
|
m_pcrrNext -> m_pcrrPrevious = this;
|
|
|
|
pcrrDead -> m_pcrrNext = NULL; // Avoid destructor overkill
|
|
pcrrDead -> m_wcGlyphs = 0; // Ditto
|
|
delete pcrrDead;
|
|
}
|
|
}
|
|
}
|
|
|
|
/******************************************************************************
|
|
|
|
CRunRecord::Delete
|
|
|
|
This member deletes a given glyph from the set of runs. Deleting an entry is
|
|
messy- it means splitting the record, unless we were so fortunate as to
|
|
merely lop off one of the ends.
|
|
|
|
******************************************************************************/
|
|
|
|
void CRunRecord::Delete(WORD wCodePoint) {
|
|
// If this isn't the right record, recurse or return as appropriate
|
|
|
|
if (wCodePoint < m_wFirst)
|
|
return;
|
|
|
|
if (wCodePoint >= m_wFirst + m_wcGlyphs) {
|
|
if (m_pcrrNext)
|
|
m_pcrrNext -> Delete(wCodePoint);
|
|
return;
|
|
}
|
|
|
|
WORD wIndex = wCodePoint - m_wFirst;
|
|
|
|
// Did we get lucky and hit the first or the last?
|
|
|
|
if (!wIndex || wIndex == -1 + m_wcGlyphs) {
|
|
// If there is only one entry in this run, kill it.
|
|
|
|
if (m_wcGlyphs == 1) {
|
|
if (m_pcrrPrevious) { // Not the first, then die!
|
|
m_pcrrPrevious -> m_pcrrNext = m_pcrrNext;
|
|
if (m_pcrrNext)
|
|
m_pcrrNext -> m_pcrrPrevious = m_pcrrPrevious;
|
|
m_pcrrNext = NULL; // We no longer have a follwing
|
|
delete this;
|
|
return; // It is finished
|
|
}
|
|
|
|
// We are the first. If there's someone after us, get their stuff
|
|
// and make it ours- otherwise, zero everything.
|
|
|
|
if (m_pcrrNext) {
|
|
m_cpaGlyphs.Copy(m_pcrrNext -> m_cpaGlyphs);
|
|
m_wFirst = m_pcrrNext -> m_wFirst;
|
|
m_wcGlyphs = m_pcrrNext -> m_wcGlyphs;
|
|
CRunRecord *pcrrVictim = m_pcrrNext;
|
|
m_pcrrNext = m_pcrrNext -> m_pcrrNext;
|
|
if (m_pcrrNext) // Raid 118880: BUG_BUG: m_pcrrNext become zero when deleted 1252 defalt with add code points
|
|
m_pcrrNext -> m_pcrrPrevious = this;
|
|
pcrrVictim -> m_pcrrNext = NULL;
|
|
delete pcrrVictim;
|
|
}
|
|
else {
|
|
m_cpaGlyphs.RemoveAll();
|
|
m_wFirst = m_wcGlyphs = 0;
|
|
}
|
|
m_dwOffset = 0;
|
|
return;
|
|
}
|
|
|
|
// OK, we can now kill the offending entry
|
|
|
|
m_cpaGlyphs.RemoveAt(wIndex);
|
|
m_wcGlyphs--;
|
|
|
|
// Yes, the following line is trick code. It's good for the soul...
|
|
m_wFirst += !wIndex;
|
|
return; // The glyph, she be toast.
|
|
}
|
|
|
|
// Alas, this means we must split the record.
|
|
|
|
// Since this means a new one must be made, let a new constructor do
|
|
// most of the dirty work for us.
|
|
|
|
m_pcrrNext = new CRunRecord(this, wIndex + 1);
|
|
|
|
_ASSERTE(m_pcrrNext); // We lose that, we might as well die...
|
|
|
|
// Delete everything after the offending member
|
|
|
|
m_cpaGlyphs.RemoveAt(wIndex, m_wcGlyphs - wIndex);
|
|
|
|
// Well, that about settles it, eh!
|
|
m_wcGlyphs = wIndex;
|
|
}
|
|
|
|
/******************************************************************************
|
|
|
|
CRunRecord::Empty
|
|
|
|
This method will be called if the glyph map is being re-initialized. We set
|
|
everything back to its initial state, and delete any tail records.
|
|
|
|
******************************************************************************/
|
|
|
|
void CRunRecord::Empty() {
|
|
|
|
if (m_pcrrNext)
|
|
delete m_pcrrNext;
|
|
|
|
m_pcrrNext = 0;
|
|
|
|
m_wFirst = m_wcGlyphs = 0;
|
|
|
|
m_cpaGlyphs.RemoveAll();
|
|
}
|
|
|
|
/******************************************************************************
|
|
|
|
CRunRecord::NoteOffset
|
|
|
|
This routine is given an offset which is to be managed in the run- the
|
|
management needed differs depending upon the file image being produced, so
|
|
we use a parameter to describe the tyoe being output.
|
|
|
|
In any event, the offset is passed by reference, and updated by each run
|
|
record in the set, in turn.
|
|
|
|
******************************************************************************/
|
|
|
|
void CRunRecord::NoteOffset(DWORD& dwOffset, BOOL bRLE, BOOL bPaired) {
|
|
if (bRLE) {
|
|
m_dwOffset = dwOffset;
|
|
dwOffset += m_wcGlyphs *
|
|
((CGlyphHandle *) m_cpaGlyphs[0]) -> RLESize();
|
|
}
|
|
else
|
|
for (unsigned u = 0; u < Glyphs(); u++)
|
|
Glyph(u).GTTOffset(dwOffset, bPaired);
|
|
|
|
// Recurse if there's more...
|
|
if (m_pcrrNext)
|
|
m_pcrrNext -> NoteOffset(dwOffset, bRLE, bPaired);
|
|
}
|
|
|
|
// This routine passes a DWORD to each glyph handle denoting where it can
|
|
// store its extra data. Each updates the offset, if necessary.
|
|
|
|
// We then recursively call each descendant to do the same thing.
|
|
|
|
void CRunRecord::NoteExtraOffset(DWORD &dwOffset, const BOOL bCompact) {
|
|
|
|
for (unsigned u = 0; u < m_wcGlyphs; u++)
|
|
Glyph(u).RLEOffset(dwOffset, bCompact);
|
|
|
|
if (m_pcrrNext)
|
|
m_pcrrNext -> NoteExtraOffset(dwOffset, bCompact);
|
|
}
|
|
|
|
// File output functions- These are all basically recursive. The callee does
|
|
// its thing, then passes it on down the chain. Since this is the order RLE
|
|
// and GTT are written in, everything is fine.
|
|
|
|
void CRunRecord::WriteSelf(CFile& cfTarget, BOOL bRLE) const {
|
|
cfTarget.Write(this, Size(bRLE));
|
|
if (m_pcrrNext)
|
|
m_pcrrNext -> WriteSelf(cfTarget, bRLE);
|
|
}
|
|
|
|
void CRunRecord::WriteHandles(CFile& cfTarget, WORD wFormat) const {
|
|
for (unsigned u = 0; u < m_wcGlyphs; u++)
|
|
GlyphData(u).WriteRLE(cfTarget, wFormat);
|
|
|
|
if (m_pcrrNext)
|
|
m_pcrrNext -> WriteHandles(cfTarget, wFormat);
|
|
}
|
|
|
|
// Member for writing the total set of GTT Map Table Entries
|
|
|
|
void CRunRecord::WriteMapTable(CFile& cfTarget, BOOL bPredefined) const {
|
|
for (unsigned u = 0; u < m_wcGlyphs; u++)
|
|
GlyphData(u).WriteGTT(cfTarget, bPredefined);
|
|
|
|
if (m_pcrrNext)
|
|
m_pcrrNext -> WriteMapTable(cfTarget, bPredefined);
|
|
}
|
|
|
|
/******************************************************************************
|
|
|
|
CRunRecord::WriteEncodings
|
|
|
|
This calls each glyph in the run record in ascending order to have it write
|
|
its encoding into the file in the given format. It then recursively calls
|
|
the next run record.
|
|
|
|
******************************************************************************/
|
|
|
|
void CRunRecord::WriteEncodings(CFile& cfTarget, WORD wfHow) const {
|
|
for (unsigned u = 0; u < m_wcGlyphs; u++)
|
|
GlyphData(u).WriteEncoding(cfTarget, wfHow);
|
|
|
|
if (m_pcrrNext)
|
|
m_pcrrNext -> WriteEncodings(cfTarget, wfHow);
|
|
}
|
|
|
|
/******************************************************************************
|
|
|
|
CCodePageData class implementation
|
|
|
|
******************************************************************************/
|
|
|
|
/******************************************************************************
|
|
|
|
CCodePageData::Invocation
|
|
|
|
This member function returns (in C-style string declaration form) the data to
|
|
send to the printer to perform the requested select/deselect of this code
|
|
page.
|
|
|
|
******************************************************************************/
|
|
|
|
void CCodePageData::Invocation(CString& csReturn, BOOL bSelect) const {
|
|
if (bSelect)
|
|
m_ciSelect.GetInvocation(csReturn);
|
|
else
|
|
m_ciDeselect.GetInvocation(csReturn);
|
|
}
|
|
|
|
/******************************************************************************
|
|
|
|
CCodePageData::SetInvocation(LPCTSTR lpstrInvoke, BOOL bSelect)
|
|
|
|
This member function sets the select or deselect sring using a string which
|
|
is decoded as a C-style string declaration.
|
|
|
|
******************************************************************************/
|
|
|
|
void CCodePageData::SetInvocation(LPCTSTR lpstrInvoke, BOOL bSelect) {
|
|
|
|
if (bSelect)
|
|
m_ciSelect.SetInvocation(lpstrInvoke);
|
|
else
|
|
m_ciDeselect.SetInvocation(lpstrInvoke);
|
|
}
|
|
|
|
/******************************************************************************
|
|
|
|
CCodePageData::SetInvocation(PBYTE pb, unsigned ucb, BOOL bSelect)
|
|
|
|
This member function initializes one of the two CInvocation members via its
|
|
Init function.
|
|
|
|
******************************************************************************/
|
|
|
|
void CCodePageData::SetInvocation(PBYTE pb, unsigned ucb, BOOL bSelect) {
|
|
if (bSelect)
|
|
m_ciSelect.Init(pb, ucb);
|
|
else
|
|
m_ciDeselect.Init(pb, ucb);
|
|
}
|
|
|
|
/******************************************************************************
|
|
|
|
CCodePageData::NoteOffsets
|
|
|
|
This member function is passed an offset at which it will record its
|
|
invocation strings. It simply funnels the call to each invocation member,
|
|
which updates the value as appropriate.
|
|
|
|
******************************************************************************/
|
|
|
|
void CCodePageData::NoteOffsets(DWORD& dwOffset) {
|
|
m_ciSelect.NoteOffset(dwOffset);
|
|
m_ciDeselect.NoteOffset(dwOffset);
|
|
}
|
|
|
|
// Write the id and invocation location information to the file
|
|
|
|
void CCodePageData::WriteSelf(CFile& cfTarget) {
|
|
cfTarget.Write(&m_dwid, sizeof m_dwid);
|
|
m_ciSelect.WriteSelf(cfTarget);
|
|
m_ciDeselect.WriteSelf(cfTarget);
|
|
}
|
|
|
|
// Write the invocation strings to a file.
|
|
|
|
void CCodePageData::WriteInvocation(CFile& cfTarget) {
|
|
m_ciSelect.WriteEncoding(cfTarget);
|
|
m_ciDeselect.WriteEncoding(cfTarget);
|
|
}
|
|
|
|
/******************************************************************************
|
|
|
|
CGlyphMap class implementation
|
|
|
|
******************************************************************************/
|
|
|
|
IMPLEMENT_SERIAL(CGlyphMap, CProjectNode, 0)
|
|
|
|
// The GTT header
|
|
|
|
struct sGTTHeader {
|
|
DWORD m_dwcbImage;
|
|
enum {Version1Point0 = 0x10000};
|
|
DWORD m_dwVersion;
|
|
DWORD m_dwfControl; // Any flags defined?
|
|
long m_lidPredefined;
|
|
DWORD m_dwcGlyphs;
|
|
DWORD m_dwcRuns;
|
|
DWORD m_dwofRuns;
|
|
DWORD m_dwcCodePages;
|
|
DWORD m_dwofCodePages;
|
|
DWORD m_dwofMapTable;
|
|
DWORD m_dwReserved[2];
|
|
sGTTHeader() {
|
|
memset(this, 0, sizeof *this);
|
|
m_dwVersion = Version1Point0;
|
|
m_lidPredefined = CGlyphMap::NoPredefined;
|
|
m_dwcbImage = sizeof *this;
|
|
}
|
|
};
|
|
|
|
CSafeMapWordToOb CGlyphMap::m_csmw2oPredefined;
|
|
|
|
/******************************************************************************
|
|
|
|
CGlyphMap::Public
|
|
|
|
This is a static member function which will return a pointer to one of the
|
|
predefined GTT files, after loading it if necessary.
|
|
|
|
******************************************************************************/
|
|
|
|
CGlyphMap* CGlyphMap::Public(WORD wID, WORD wCP/*= 0*/, DWORD dwDefCP/*= 0*/,
|
|
WORD wFirst/*= 0*/, WORD wLast/*= 255*/)
|
|
{
|
|
//TRACE("*** First char = %d\t\tLast char = %d\n", wFirst, wLast) ;
|
|
|
|
// If a GTT in this driver is needed, return NULL to force the
|
|
// program to go get it.
|
|
|
|
if (((short) wID) > 0)
|
|
return NULL ;
|
|
|
|
// If no GTT ID is set, use the code page that is passed in. If no code
|
|
// page is passed in, use the project's default code page. If there is
|
|
// no project, use 1252 as the default code page.
|
|
|
|
if (wID == 0)
|
|
if ((wID = wCP) == 0)
|
|
if ((wID = (WORD) dwDefCP) == 0)
|
|
wID = 1252 ;
|
|
|
|
// Check to see if wID is set to one of the Far East codepages that are
|
|
// built into the MDT. If that is the case, change it to the resource ID
|
|
// for that code page.
|
|
|
|
switch (wID) {
|
|
case 932:
|
|
wID = -17 ;
|
|
break ;
|
|
case 936:
|
|
wID = -16 ;
|
|
break ;
|
|
case 949:
|
|
wID = -18 ;
|
|
break ;
|
|
case 950:
|
|
wID = -10 ;
|
|
break ;
|
|
} ;
|
|
|
|
// If the ID is 1252, switch to the resource ID for CP 1252.
|
|
|
|
if (wID == 1252)
|
|
wID = -IDR_CP1252 ;
|
|
|
|
// The easy part comes if it is already loaded
|
|
|
|
CObject* pco ;
|
|
if (m_csmw2oPredefined.Lookup(wID, pco))
|
|
return (CGlyphMap*) pco ;
|
|
|
|
//// DEAD_BUG - The program can't load huge GTTs so just return NULL.
|
|
//
|
|
//if ((short) wID >= -18 || (short) wID <= -10)
|
|
// return NULL ;
|
|
|
|
// Manage loading a predefined code page or building a GTT based on a code
|
|
// page. Begin by declaring/initializing a class instance for it.
|
|
|
|
CGlyphMap *pcgm = new CGlyphMap ;
|
|
TCHAR acbuf[32] ;
|
|
if (FindResource(AfxGetResourceHandle(),
|
|
MAKEINTRESOURCE((((short) wID) < 0) ? -(short) wID : wID),
|
|
MAKEINTRESOURCE(IDR_GLYPHMAP)))
|
|
pcgm->m_csName.LoadString(IDS_DefaultPage + wID) ;
|
|
else {
|
|
strcpy(acbuf, _T("CP ")) ;
|
|
_itoa(wID, &acbuf[3], 10) ;
|
|
pcgm->m_csName = acbuf ;
|
|
} ;
|
|
pcgm->nSetRCID((int) wID) ;
|
|
pcgm->m_wFirstChar = wFirst ;
|
|
pcgm->m_wLastChar = wLast ;
|
|
pcgm->m_bResBldGTT = true ;
|
|
|
|
// Load/build the GTT. If this works, add it to the list of "predefined"
|
|
// GTTs and return a pointer to it.
|
|
|
|
if (pcgm->Load()) {
|
|
m_csmw2oPredefined[wID] = pcgm ;
|
|
return pcgm ;
|
|
} ;
|
|
|
|
// Clean up and return NULL if the load/build fails.
|
|
|
|
delete pcgm ;
|
|
return NULL ;
|
|
}
|
|
|
|
/******************************************************************************
|
|
|
|
CGlyphMap::MergePredefined
|
|
|
|
This merges in fresh glyph handles from the predefined GTT, then removes all
|
|
glyphs destined for the gallows.
|
|
|
|
******************************************************************************/
|
|
|
|
void CGlyphMap::MergePredefined() {
|
|
if (m_lidPredefined == NoPredefined)
|
|
return;
|
|
|
|
CWaitCursor cwc; // This takes a long time, I'll bet!
|
|
|
|
CGlyphMap *pcgm = Public((WORD) m_lidPredefined);
|
|
|
|
if (!pcgm)
|
|
AfxThrowNotSupportedException();
|
|
|
|
// First, add any new code pages in the predefined GTT
|
|
CMapWordToDWord cmw2dPageMap; // Map PDT code pages' indices to our own
|
|
|
|
for (unsigned u = 0; u < pcgm -> CodePages(); u++) {
|
|
for (unsigned u2 = 0; u2 < CodePages(); u2++)
|
|
if (PageID(u2) == pcgm -> PageID(u))
|
|
break;
|
|
|
|
if (u2 == CodePages())
|
|
AddCodePage(pcgm -> PageID(u));
|
|
|
|
cmw2dPageMap[(WORD)u] = u2;
|
|
}
|
|
|
|
CPtrArray cpaTemplate;
|
|
pcgm -> Collect(cpaTemplate);
|
|
|
|
for (int i = 0; i < cpaTemplate.GetSize(); i++) {
|
|
CGlyphHandle& cghTemplate = *(CGlyphHandle *) cpaTemplate[i];
|
|
CObject* pco;
|
|
|
|
if (!m_csmw2oEncodings.Lookup(cghTemplate.CodePoint(), pco)) {
|
|
// Add this one, and map the code page info.
|
|
CGlyphHandle* pcgh = new CGlyphHandle;
|
|
if (!pcgh)
|
|
AfxThrowMemoryException();
|
|
|
|
*pcgh = cghTemplate;
|
|
pcgh -> SetCodePage(cmw2dPageMap[(WORD)cghTemplate.CodePage()],
|
|
pcgm -> PageID(cghTemplate.CodePage()));
|
|
|
|
m_csmw2oEncodings[cghTemplate.CodePoint()] = pcgh;
|
|
m_crr.Add(pcgh);
|
|
}
|
|
}
|
|
|
|
// Now, all of the new pages have been added. We must remove all points
|
|
// listed as "Remove".
|
|
|
|
Collect(cpaTemplate); // Get all of the handles for ourselves
|
|
|
|
for (i = (int)cpaTemplate.GetSize(); i--; ) {
|
|
CGlyphHandle& cgh = *(CGlyphHandle *) cpaTemplate[i];
|
|
|
|
if (cgh.Predefined() == CGlyphHandle::Removed)
|
|
DeleteGlyph(cgh.CodePoint());
|
|
}
|
|
}
|
|
|
|
/******************************************************************************
|
|
|
|
CGlyphMap::UnmergePredefined
|
|
|
|
This is the harder of the two predfined handlers, if that is conceivable.
|
|
First (unless asked not to), glkyphs must be added to mark those missing from
|
|
the predefined GTT.
|
|
|
|
Then, the entire set is compared to the PDT, so they can be removed as
|
|
equivalent, or flagged as added or modified.
|
|
|
|
******************************************************************************/
|
|
|
|
void CGlyphMap::UnmergePredefined(BOOL bTrackRemovals) {
|
|
if (m_lidPredefined == NoPredefined)
|
|
return;
|
|
|
|
CWaitCursor cwc; // This takes a long time, I'll bet!
|
|
|
|
CGlyphMap *pcgm = Public((WORD) m_lidPredefined);
|
|
|
|
if (!pcgm)
|
|
AfxThrowNotSupportedException();
|
|
|
|
CPtrArray cpaPDT;
|
|
|
|
if (bTrackRemovals) {
|
|
pcgm -> Collect(cpaPDT);
|
|
|
|
for (int i = 0; i < cpaPDT.GetSize(); i++) {
|
|
CGlyphHandle& cgh = *(CGlyphHandle*) cpaPDT[i];
|
|
|
|
CObject* pco;
|
|
|
|
if (m_csmw2oEncodings.Lookup(cgh.CodePoint(), pco))
|
|
continue;
|
|
|
|
// This point was removed from the predefined set, so add it to
|
|
// ours, and mark it as such.
|
|
|
|
CGlyphHandle *pcghCorpse = new CGlyphHandle();
|
|
|
|
if (!pcghCorpse)
|
|
AfxThrowMemoryException();
|
|
|
|
*pcghCorpse = cgh;
|
|
|
|
pcghCorpse -> SetPredefined(CGlyphHandle::Removed);
|
|
|
|
m_csmw2oEncodings[cgh.CodePoint()] = pcghCorpse;
|
|
m_crr.Add(pcghCorpse);
|
|
}
|
|
}
|
|
|
|
// Mark all of the glyphs in our set, now. Also mark the code pages used
|
|
|
|
Collect(cpaPDT);
|
|
|
|
CMapWordToDWord cmw2dPages;
|
|
|
|
for (int i = (int)cpaPDT.GetSize(); i--; ) {
|
|
CGlyphHandle& cgh = *(CGlyphHandle*) cpaPDT[i];
|
|
|
|
union {
|
|
CObject *pco;
|
|
CGlyphHandle *pcgh;
|
|
};
|
|
|
|
if (pcgm -> m_csmw2oEncodings.Lookup(cgh.CodePoint(), pco))
|
|
if (*pcgh == cgh) {
|
|
if (cgh.Predefined() == CGlyphHandle::Removed)
|
|
continue; // Already accounted for
|
|
|
|
if (m_bPaired != pcgm -> m_bPaired && cgh.PairedRelevant())
|
|
cgh.SetPredefined(CGlyphHandle::Modified);
|
|
else {
|
|
DeleteGlyph(cgh.CodePoint()); // Unmodified
|
|
continue;
|
|
}
|
|
}
|
|
else
|
|
cgh.SetPredefined(CGlyphHandle::Modified);
|
|
else
|
|
cgh.SetPredefined(CGlyphHandle::Added);
|
|
|
|
cmw2dPages[(WORD)PageID(cgh.CodePage())]++; // Only track these pages
|
|
}
|
|
|
|
// Remove the unused code pages, unless they have selections
|
|
|
|
for (unsigned u = CodePages(); u--; )
|
|
if (!cmw2dPages[(WORD)PageID(u)])
|
|
if (CodePage(u).NoInvocation())
|
|
RemovePage(u, !u);
|
|
}
|
|
|
|
/******************************************************************************
|
|
|
|
CGlyphMap::GenerateRuns
|
|
|
|
This member will create the run records by iterating over the mapped glyph
|
|
handles.
|
|
|
|
******************************************************************************/
|
|
|
|
void CGlyphMap::GenerateRuns() {
|
|
if (m_crr.TotalGlyphs() == Glyphs())
|
|
return;
|
|
|
|
for (POSITION pos = m_csmw2oEncodings.GetStartPosition(); pos;) {
|
|
WORD wValue;
|
|
union {
|
|
CObject *pco;
|
|
CGlyphHandle *pcgh;
|
|
};
|
|
|
|
m_csmw2oEncodings.GetNextAssoc(pos, wValue, pco);
|
|
m_crr.Add(pcgh);
|
|
}
|
|
}
|
|
|
|
/******************************************************************************
|
|
|
|
CGlyphMap::Serialize
|
|
|
|
This member function serializes the Glyph map, i.e., loads or stores it into
|
|
a persistent object store. Only the project-level information is stored.
|
|
|
|
The file will be loaded using the project-level data.
|
|
|
|
Note: There was a second copy of the GTT's RC ID saved in the MDW file.
|
|
Because it could get out of sync with the one in the CGlyphMap's
|
|
CRCIDNode instance, the copy of the ID read/written by this routine
|
|
is no longer used. A bogus value is written and the number read is
|
|
discarded. This "ID" is still kept in the MDW file so that no new MDW
|
|
version is needed.
|
|
|
|
******************************************************************************/
|
|
|
|
void CGlyphMap::Serialize(CArchive& car)
|
|
{
|
|
WORD w = (WORD)0; // Used to read/write bogus RC ID in MDW file.
|
|
|
|
CProjectNode::Serialize(car) ;
|
|
|
|
if (car.IsLoading()) {
|
|
car >> w ;
|
|
}
|
|
else {
|
|
car << w ;
|
|
}
|
|
}
|
|
|
|
/******************************************************************************
|
|
|
|
CGlyphMap::CGlyphMap
|
|
|
|
The class constructor, in addition to setting some default values, builds an
|
|
array of IDs for the CProjectNode class from which it is derived to use in
|
|
building the context menu in the driver/project view tree.
|
|
|
|
It also allocates a single code page record for the current ANSI page, so we
|
|
always have a default page.
|
|
|
|
******************************************************************************/
|
|
|
|
CGlyphMap::CGlyphMap() {
|
|
m_cfn.SetExtension(_T(".GTT"));
|
|
m_bPaired = FALSE;
|
|
m_lidPredefined = NoPredefined;
|
|
|
|
// Build the context menu control
|
|
m_cwaMenuID.Add(ID_OpenItem);
|
|
m_cwaMenuID.Add(ID_CopyItem);
|
|
m_cwaMenuID.Add(ID_RenameItem);
|
|
m_cwaMenuID.Add(ID_DeleteItem);
|
|
m_cwaMenuID.Add(0);
|
|
m_cwaMenuID.Add(ID_ExpandBranch);
|
|
m_cwaMenuID.Add(ID_CollapseBranch);
|
|
//m_cwaMenuID.Add(0);
|
|
//m_cwaMenuID.Add(ID_GenerateOne);
|
|
|
|
// Initialy, let the default code page be the current ANSI page, if this
|
|
// is not a DBCS locale. Otherwise, use 1252, as no DBCS CTT file can be
|
|
// generated with UniTool
|
|
for (WORD w = 0; w < 256; w++)
|
|
if (IsDBCSLeadByte((BYTE) w))
|
|
break;
|
|
m_csoaCodePage.Add(new CCodePageData(w < 256 ? 1252 : GetACP()));
|
|
|
|
// Allocate a CCodePageInformation class if needed.
|
|
|
|
if (pccpi == NULL)
|
|
pccpi = new CCodePageInformation ;
|
|
|
|
// Assume that the GTT's data should be read from a file.
|
|
|
|
m_bResBldGTT = false ;
|
|
|
|
m_ctSaveTimeStamp = (time_t) 0 ; // The GTT has not been saved
|
|
}
|
|
|
|
/******************************************************************************
|
|
|
|
CGlyphMap::CodePages(CDWordArray &cdaReturn)
|
|
|
|
This overload fills a DWordArray with the code page IDs.
|
|
|
|
******************************************************************************/
|
|
|
|
void CGlyphMap::CodePages(CDWordArray &cdaReturn) const {
|
|
cdaReturn.RemoveAll();
|
|
for (unsigned u = 0; u < CodePages(); u++)
|
|
cdaReturn.Add(CodePage(u).Page());
|
|
}
|
|
|
|
/******************************************************************************
|
|
|
|
CGlyphMap::PageName
|
|
|
|
|
|
This member returns the name of a particular code page, by index.
|
|
|
|
******************************************************************************/
|
|
|
|
CString CGlyphMap::PageName(unsigned u) const {
|
|
return pccpi->Name(CodePage(u).Page());
|
|
}
|
|
|
|
/******************************************************************************
|
|
|
|
CGlyphMap::Invocation
|
|
|
|
This member returns (in C-style encoding) a string which is used to either
|
|
select or deselect a given code page.
|
|
|
|
******************************************************************************/
|
|
|
|
void CGlyphMap::Invocation(unsigned u, CString& csReturn,
|
|
BOOL bSelect) const {
|
|
CodePage(u).Invocation(csReturn, bSelect);
|
|
}
|
|
|
|
/******************************************************************************
|
|
|
|
CGlyphMap::UndefinedPoints
|
|
|
|
This member fills a map with all code points NOT in the current mapping, and
|
|
maps them to their related code pages. Thus only translatable points will be
|
|
passed back to the caller.
|
|
|
|
******************************************************************************/
|
|
|
|
void CGlyphMap::UndefinedPoints(CMapWordToDWord& cmw2dCollector) const {
|
|
|
|
cmw2dCollector.RemoveAll();
|
|
|
|
CWaitCursor cwc;
|
|
|
|
for (unsigned u = 0; u < CodePages(); u++) {
|
|
CWordArray cwaPage;
|
|
|
|
// Collect the code points in the code page
|
|
|
|
pccpi->Collect(PageID(u), cwaPage);
|
|
union {
|
|
CObject *pco;
|
|
DWORD dw;
|
|
};
|
|
|
|
// Check the entries- if they haven't been mapped yet, and
|
|
// some earlier code page hasn't claimed them, add them.
|
|
|
|
for (int i = 0; i < cwaPage.GetSize(); i++)
|
|
if (!m_csmw2oEncodings.Lookup(cwaPage[i], pco) &&
|
|
!cmw2dCollector.Lookup(cwaPage[i], dw))
|
|
cmw2dCollector[cwaPage[i]] = u;
|
|
}
|
|
}
|
|
|
|
/******************************************************************************
|
|
|
|
CGlyphMap::Load(CByteArray& cbaMap)
|
|
|
|
This loads an image of a GTT into safe memory, whether it is predefined or
|
|
a file.
|
|
|
|
******************************************************************************/
|
|
|
|
//void CGlyphMap::Load(CByteArray& cbaMap) const
|
|
void CGlyphMap::Load(CByteArray& cbaMap)
|
|
{
|
|
short xxx = (short) ((CProjectNode*) this)->nGetRCID() ; // The GTT's RC ID
|
|
short sid = (short) nGetRCID() ; // The GTT's RC ID
|
|
|
|
try {
|
|
// Try to load the GTT from a file when the GTT should not be loaded
|
|
// from a resource or built.
|
|
|
|
if (!m_bResBldGTT
|
|
&& (sid > 0 || (sid < Wansung && sid != -IDR_CP1252))) {
|
|
CFile cfGTT(m_cfn.FullName(),
|
|
CFile::modeRead | CFile::shareDenyWrite);
|
|
|
|
cbaMap.SetSize(cfGTT.GetLength());
|
|
cfGTT.Read(cbaMap.GetData(), (unsigned)cbaMap.GetSize());
|
|
return;
|
|
} ;
|
|
|
|
// If the GTT is a resource, load it from there.
|
|
// raid 441362
|
|
if(MAKEINTRESOURCE((sid < 0) ? -sid : sid) == NULL)
|
|
return;
|
|
|
|
HRSRC hrsrc = FindResource(AfxGetResourceHandle(),
|
|
MAKEINTRESOURCE((sid < 0) ? -sid : sid),
|
|
MAKEINTRESOURCE(IDR_GLYPHMAP));
|
|
if (hrsrc) {
|
|
HGLOBAL hg = LoadResource(AfxGetResourceHandle(), hrsrc) ;
|
|
if (!hg)
|
|
return ;
|
|
LPVOID lpv = LockResource(hg) ;
|
|
if (!lpv)
|
|
return ;
|
|
cbaMap.SetSize(SizeofResource(AfxGetResourceHandle(), hrsrc)) ;
|
|
memcpy(cbaMap.GetData(), lpv, (size_t)cbaMap.GetSize()) ;
|
|
return ;
|
|
} ;
|
|
|
|
//AfxMessageBox("GTT building code reached.") ;
|
|
|
|
// If all else fails, try to generate a GTT based on the code page ID
|
|
// that should be in m_wID if this point is reached.
|
|
|
|
HANDLE hheap ;
|
|
UNI_GLYPHSETDATA *pGTT ;
|
|
if (!(hheap = HeapCreate(HEAP_NO_SERIALIZE, 10 * 1024, 256 * 1024))) {
|
|
AfxMessageBox(IDS_HeapInGLoad) ;
|
|
return ;
|
|
} ;
|
|
pGTT = PGetDefaultGlyphset(hheap, m_wFirstChar, m_wLastChar,
|
|
(DWORD) sid) ;
|
|
if (pGTT == NULL) {
|
|
HeapDestroy(hheap) ; //raid 116600 Prefix
|
|
AfxMessageBox(IDS_PGetFailedInGLoad) ;
|
|
return ;
|
|
} ;
|
|
cbaMap.SetSize(pGTT->dwSize) ;
|
|
memcpy(cbaMap.GetData(), pGTT, (size_t)cbaMap.GetSize()) ;
|
|
HeapDestroy(hheap) ;
|
|
}
|
|
|
|
catch (CException *pce) {
|
|
pce -> ReportError();
|
|
pce -> Delete();
|
|
CString csMessage;
|
|
csMessage.Format(IDS_LoadFailure, Name());
|
|
AfxMessageBox(csMessage);
|
|
}
|
|
}
|
|
|
|
/******************************************************************************
|
|
|
|
CGlyphmap::SetSourceName
|
|
|
|
This takes and stores the source file name so we can load and convert later.
|
|
It also renames (or rather, sets the original name) for the GlyphMap using
|
|
the base file name.
|
|
|
|
******************************************************************************/
|
|
|
|
void CGlyphMap::SetSourceName(LPCTSTR lpstrNew) {
|
|
m_csSource = lpstrNew;
|
|
|
|
m_csName = m_csSource.Mid(m_csSource.ReverseFind(_T('\\')) + 1);
|
|
|
|
if (m_csName.Find(_T('.')) >= 0)
|
|
if (m_csName.Right(4).CompareNoCase(_T(".CTT"))) {
|
|
m_csName.SetAt(m_csName.Find(_T('.')), _T('_'));
|
|
CProjectNode::Rename(m_csName);
|
|
}
|
|
else
|
|
CProjectNode::Rename(m_csName.Left(m_csName.Find(_T('.'))));
|
|
else
|
|
CProjectNode::Rename(m_csName);
|
|
}
|
|
|
|
|
|
/******************************************************************************
|
|
|
|
CGlyphMap::SetFileName
|
|
|
|
This sets the new file name. It is done differently than in SetSourceName()
|
|
because the base file name must not be more than 8 characters long. (The
|
|
extra info is left in the node name by SetSourceName() because it is useful
|
|
there and it has no length limit.)
|
|
|
|
******************************************************************************/
|
|
|
|
BOOL CGlyphMap::SetFileName(LPCTSTR lpstrNew)
|
|
{
|
|
CString csnew ; // CString version of input parameter
|
|
|
|
csnew = lpstrNew ;
|
|
|
|
// If the input filespec contains an extension, remove it and pass the
|
|
// resulting string to the file node's rename routine. Otherwise, just
|
|
// pass the original string to the rename routine.
|
|
//
|
|
// This check is complicated by the fact that one of the path components
|
|
// might have a dot in it too. We need to check for the last dot and make
|
|
// sure it comes before a path separator.
|
|
|
|
if (csnew.ReverseFind(_T('.')) > csnew.ReverseFind(_T('\\')))
|
|
return m_cfn.Rename(csnew.Left(csnew.ReverseFind(_T('.')))) ;
|
|
else
|
|
return m_cfn.Rename(csnew) ;
|
|
}
|
|
|
|
|
|
/******************************************************************************
|
|
|
|
CGlyphMap::AddPoints
|
|
|
|
This member adds one or more code points to the glyph map using the given
|
|
list of points and associated pages.
|
|
|
|
******************************************************************************/
|
|
|
|
void CGlyphMap::AddPoints(CMapWordToDWord& cmw2dNew) {
|
|
WORD wKey;
|
|
DWORD dwixPage;
|
|
CWaitCursor cwc; // This could be slow!
|
|
|
|
for (POSITION pos = cmw2dNew.GetStartPosition(); pos; ) {
|
|
cmw2dNew.GetNextAssoc(pos, wKey, dwixPage);
|
|
|
|
// Get the MBCS encoding of the Unicode point as the initial
|
|
// glyph encoding.
|
|
|
|
CWordArray cwaIn;
|
|
CByteArray cbaOut;
|
|
|
|
cwaIn.Add(wKey);
|
|
pccpi->Convert(cbaOut, cwaIn, CodePage(dwixPage).Page());
|
|
|
|
// Create the glyph and add it to the map
|
|
|
|
CGlyphHandle *pcgh = new CGlyphHandle;
|
|
|
|
pcgh -> Init(cbaOut.GetData(), (unsigned) cbaOut.GetSize(), (WORD)Glyphs(),
|
|
wKey);
|
|
pcgh -> SetCodePage(dwixPage, CodePage(dwixPage).Page());
|
|
m_csmw2oEncodings[wKey] = pcgh;
|
|
m_crr.Add(pcgh);
|
|
}
|
|
Changed(); // Don't forget to tell the container!
|
|
}
|
|
|
|
/******************************************************************************
|
|
|
|
CGlyphMap::DeleteGlyph
|
|
|
|
This member function removes a glyph from the map. The most tricky part is
|
|
updating the run records, but that's not this class' responsibility, is it?
|
|
|
|
******************************************************************************/
|
|
|
|
void CGlyphMap::DeleteGlyph(WORD wCodePoint) {
|
|
if (!m_csmw2oEncodings.RemoveKey(wCodePoint))
|
|
return; // This glyph is already toast!
|
|
|
|
m_crr.Delete(wCodePoint);
|
|
Changed();
|
|
}
|
|
|
|
/******************************************************************************
|
|
|
|
CGlyphMap::RemovePage
|
|
|
|
This member function removes a code page from the list of available pages.
|
|
Glyphs that used this page will be remapped to a second specified page.
|
|
|
|
******************************************************************************/
|
|
|
|
BOOL CGlyphMap::RemovePage(unsigned uPage, unsigned uMapTo, bool bDelete /* bDelete = FALSE */) {
|
|
|
|
if (uPage >= CodePages() || uMapTo >= CodePages())
|
|
return FALSE;
|
|
|
|
// Pretty simple- walk the map- first replace any instances, then
|
|
// decrement any indices higher than uPage
|
|
|
|
WORD wKey;
|
|
|
|
union {
|
|
CObject* pco;
|
|
CGlyphHandle* pcgh;
|
|
};
|
|
|
|
for (POSITION pos = m_csmw2oEncodings.GetStartPosition(); pos; ) {
|
|
|
|
m_csmw2oEncodings.GetNextAssoc(pos, wKey, pco);
|
|
if (!bDelete){
|
|
if (pcgh -> CodePage() == uPage)
|
|
pcgh -> SetCodePage(uMapTo, CodePage(uMapTo).Page());
|
|
if (pcgh -> CodePage() > uPage)
|
|
pcgh -> SetCodePage(pcgh -> CodePage() - 1,
|
|
CodePage(pcgh -> CodePage()).Page());
|
|
}
|
|
else { // raid 118880
|
|
if (pcgh -> CodePage() == uPage)
|
|
DeleteGlyph(pcgh -> CodePoint() ) ; // delete m_csm2oEncodings, RunRecord
|
|
|
|
else if (pcgh -> CodePage() > uPage)
|
|
pcgh -> SetCodePage(pcgh -> CodePage() - 1,
|
|
CodePage(pcgh -> CodePage()).Page());
|
|
}
|
|
|
|
}
|
|
|
|
m_csoaCodePage.RemoveAt(uPage);
|
|
|
|
// Marke the GTT as dirty and then return success.
|
|
Changed();
|
|
return TRUE;
|
|
}
|
|
|
|
/******************************************************************************
|
|
|
|
CGlyphMap::ChangeCodePage
|
|
|
|
This member function changes the code page for one ore more glyphs to a
|
|
different page. At one time I thought remapping the code points would be
|
|
required, but currently, the Unicode stays intact. That seems a good feature
|
|
for demand-driven implementation.
|
|
|
|
******************************************************************************/
|
|
|
|
// This one changes the code page for one or more glyphs.
|
|
// For now, we will simply keep the Unicode intact. Eventually, a query
|
|
// should be made about the intent, so we can remap through the existing
|
|
// page, if that is what is desired.
|
|
|
|
void CGlyphMap::ChangeCodePage(CPtrArray& cpaGlyphs, DWORD dwidNewPage) {
|
|
for (unsigned u = 0; u < CodePages(); u++)
|
|
if (dwidNewPage == CodePage(u).Page())
|
|
break;
|
|
|
|
_ASSERTE(u < CodePages());
|
|
|
|
if (u >= CodePages())
|
|
return;
|
|
|
|
for (int i = 0; i < cpaGlyphs.GetSize(); i++)
|
|
((CGlyphHandle *) cpaGlyphs[i]) -> SetCodePage(u, dwidNewPage);
|
|
|
|
Changed();
|
|
}
|
|
|
|
/******************************************************************************
|
|
|
|
CGlyphMap::AddCodePage
|
|
|
|
This member function adds a new code page to the list of pages used in this
|
|
table.
|
|
|
|
******************************************************************************/
|
|
|
|
void CGlyphMap::AddCodePage(DWORD dwidNewPage) {
|
|
m_csoaCodePage.Add(new CCodePageData(dwidNewPage));
|
|
Changed();
|
|
}
|
|
|
|
/******************************************************************************
|
|
|
|
CGlyphMap::SetPredefinedID
|
|
|
|
This changes the predefined table used by the map. If it really is a change,
|
|
we must remove all non-modified points from any existing map, and then flesh
|
|
out using the new one.
|
|
|
|
******************************************************************************/
|
|
|
|
void CGlyphMap::UsePredefined(long lidNew) {
|
|
if (m_lidPredefined == lidNew)
|
|
return; // Didn't need to do this!
|
|
|
|
if (m_lidPredefined != NoPredefined)
|
|
UnmergePredefined(lidNew != NoPredefined);
|
|
|
|
m_lidPredefined = lidNew;
|
|
|
|
if (m_lidPredefined != NoPredefined)
|
|
MergePredefined();
|
|
|
|
Changed();
|
|
}
|
|
|
|
/******************************************************************************
|
|
|
|
CGlyphMap::SetInvocation
|
|
|
|
This member changes the invocation string for selecting or unselecting a
|
|
given code page.
|
|
|
|
******************************************************************************/
|
|
|
|
void CGlyphMap::SetInvocation(unsigned u, LPCTSTR lpstrInvoke,
|
|
BOOL bSelect) {
|
|
CodePage(u).SetInvocation(lpstrInvoke, bSelect);
|
|
Changed();
|
|
}
|
|
|
|
/******************************************************************************
|
|
|
|
CGlyphMap::ChangeEncoding
|
|
|
|
This member is called when the user changes the encoding string used to
|
|
invoke a given code point. This could be done via the glyph, but then the
|
|
containing document won't know of the change, and thus the change could be
|
|
inadvertently lost...
|
|
|
|
******************************************************************************/
|
|
|
|
void CGlyphMap::ChangeEncoding(WORD wCodePoint, LPCTSTR lpstrNewInvoke) {
|
|
|
|
union {
|
|
CObject *pco;
|
|
CGlyphHandle *pcgh;
|
|
};
|
|
|
|
if (!m_csmw2oEncodings.Lookup(wCodePoint, pco) || !lpstrNewInvoke||
|
|
!*lpstrNewInvoke)
|
|
return;
|
|
|
|
pcgh -> NewEncoding(lpstrNewInvoke);
|
|
|
|
Changed();
|
|
}
|
|
|
|
/******************************************************************************
|
|
|
|
CGlyphMap::ConvertCTT()
|
|
|
|
This member fuction initializes the glyphmap structure from a CTT file
|
|
|
|
******************************************************************************/
|
|
|
|
int CGlyphMap::ConvertCTT() {
|
|
|
|
struct sCTT {
|
|
enum {Composed, Direct, Paired};
|
|
WORD m_wFormat;
|
|
BYTE m_bFirstChar, m_bLastChar;
|
|
union {
|
|
uencCTT m_uectt[1];
|
|
BYTE m_bDirect[1];
|
|
};
|
|
};
|
|
|
|
// If this map isn't empty, empty it now- at least of the glyph data
|
|
|
|
m_csmw2oEncodings.RemoveAll();
|
|
m_crr.Empty();
|
|
|
|
CByteArray cbaImage;
|
|
|
|
try {
|
|
|
|
CFile cfCTT(m_csSource, CFile::modeRead | CFile::shareDenyWrite);
|
|
|
|
cbaImage.SetSize(cfCTT.GetLength());
|
|
|
|
cfCTT.Read(cbaImage.GetData(), cfCTT.GetLength());
|
|
}
|
|
|
|
catch (CException *pce) {
|
|
pce -> ReportError();
|
|
pce -> Delete();
|
|
return IDS_FileReadError ;
|
|
}
|
|
|
|
#if 0
|
|
union {
|
|
PBYTE pbCTT;
|
|
sCTT* psctt;
|
|
};
|
|
|
|
pbCTT = cbaImage.GetData();
|
|
BYTE bFirst = min(0x20, psctt -> m_bFirstChar),
|
|
bLast = 0xFF; // Since we use a byte this is the max!
|
|
unsigned ucGlyphs = 1 + bLast - bFirst;
|
|
|
|
|
|
// Convert the code points to Unicode
|
|
CByteArray cbaCode;
|
|
CWordArray cwaCode;
|
|
|
|
for (unsigned u = 0; u < ucGlyphs; u++)
|
|
cbaCode.Add(u + bFirst);
|
|
|
|
// Convert the data to Unicode using the selected code page. This uses
|
|
// data stored from MultiByteToWideChar, so it is similar, except we can
|
|
// do this with code pages which might not be installed on the user's
|
|
// system.
|
|
|
|
if (ucGlyphs != pccpi->Convert(cbaCode, cwaCode, CodePage(0).Page())) {
|
|
CString csWork;
|
|
csWork.Format(IDS_NoUnicodePoint, u + bFirst, CodePage(0).Page());
|
|
AfxMessageBox(csWork);
|
|
return IDS_UnicodeConvFailed ;
|
|
}
|
|
|
|
// Since we add phony glyphs to the table (why, one wonders), we must mark
|
|
// them so we can manufacture equally phony encodings for them.
|
|
for (u = 0; u < ucGlyphs; u++) {
|
|
|
|
// Now, let's record the Encoding
|
|
|
|
CGlyphHandle *pcghNew = new CGlyphHandle;
|
|
unsigned uToUse = u + bFirst - psctt -> m_bFirstChar;
|
|
|
|
switch (psctt -> m_wFormat) {
|
|
|
|
case sCTT::Direct:
|
|
if (u + bFirst < psctt -> m_bFirstChar)
|
|
pcghNew -> Init((BYTE) u + bFirst, u,cwaCode[u]);
|
|
else
|
|
pcghNew -> Init(psctt -> m_bDirect[uToUse], u, cwaCode[u]);
|
|
break;
|
|
|
|
case sCTT::Paired:
|
|
if (u + bFirst < psctt -> m_bFirstChar)
|
|
pcghNew -> Init((BYTE) u + bFirst, u, cwaCode[u]);
|
|
else
|
|
if (psctt -> m_uectt[uToUse].abPaired[1])
|
|
pcghNew -> Init(psctt -> m_uectt[uToUse].abPaired, u,
|
|
cwaCode[u]);
|
|
else
|
|
pcghNew -> Init(psctt -> m_uectt[uToUse].abPaired[0],
|
|
u, cwaCode[u]);
|
|
break;
|
|
|
|
case sCTT::Composed:
|
|
if (u + bFirst < psctt -> m_bFirstChar) {
|
|
BYTE bMe = u + bFirst;
|
|
pcghNew -> Init(&bMe, 1, u, cwaCode[u]);
|
|
}
|
|
else
|
|
pcghNew -> Init(pbCTT + psctt -> m_uectt[uToUse].wOffset,
|
|
psctt -> m_uectt[uToUse + 1].wOffset -
|
|
psctt -> m_uectt[uToUse].wOffset, u, cwaCode[u]);
|
|
break;
|
|
|
|
default: // Don't accept anything else!
|
|
AfxMessageBox(IDS_InvalidCTTFormat);
|
|
return IDS_Invalid2CTTFormat ;
|
|
} // One map entry coded
|
|
|
|
// Code page index inits OK, but must know the page
|
|
|
|
pcghNew -> SetCodePage(0, DefaultCodePage());
|
|
|
|
m_csmw2oEncodings[cwaCode[u]] = pcghNew;
|
|
m_crr.Add(pcghNew);
|
|
} // Loop of generating entries
|
|
|
|
m_bPaired = sCTT::Paired == psctt -> m_wFormat;
|
|
#else
|
|
{
|
|
HANDLE hheap;
|
|
PBYTE pbCTT;
|
|
UNI_GLYPHSETDATA *pGTT;
|
|
|
|
pbCTT = cbaImage.GetData();
|
|
if( !(hheap = HeapCreate(HEAP_NO_SERIALIZE, 10 * 1024, 256 * 1024 )))
|
|
{
|
|
char acMessage[256];
|
|
wsprintf(acMessage, "HeapCreate() fails in ctt2gtt" );
|
|
MessageBox(NULL, acMessage, NULL, MB_OK);
|
|
return IDS_HeapCFailed ;
|
|
}
|
|
|
|
//if (!BConvertCTT2GTT(hheap, (PTRANSTAB)pbCTT, DefaultCodePage(), 0x20,
|
|
ASSERT(m_pcdOwner != NULL) ;
|
|
DWORD dw = ((CProjectRecord*) m_pcdOwner)->GetDefaultCodePageNum() ;
|
|
if (!BConvertCTT2GTT(hheap, (PTRANSTAB)pbCTT, dw, 0x20, 0xff, NULL,
|
|
NULL, &pGTT, 0)){
|
|
HeapDestroy(hheap); // raid 116619 prefix
|
|
return IDS_ConvCTTFailed ;
|
|
}
|
|
try {
|
|
CFile cfGTT;
|
|
if (!cfGTT.Open(m_cfn.FullName(), CFile::modeCreate | CFile::modeWrite |
|
|
CFile::shareExclusive))
|
|
return IDS_FileWriteError;
|
|
cfGTT.Write(pGTT, pGTT->dwSize);
|
|
}
|
|
|
|
catch (CException *pce) {
|
|
pce -> ReportError();
|
|
pce -> Delete();
|
|
HeapDestroy(hheap);
|
|
return IDS_FileWriteError ;
|
|
}
|
|
|
|
HeapDestroy(hheap);
|
|
}
|
|
|
|
Load();
|
|
|
|
#endif
|
|
|
|
return 0 ;
|
|
}
|
|
|
|
/******************************************************************************
|
|
|
|
CGlyphMap::Load()
|
|
|
|
This initializes the glyphmap from a GTT format file. Since this is the
|
|
primary means of loading, it requires no parameters.
|
|
|
|
******************************************************************************/
|
|
|
|
BOOL CGlyphMap::Load(LPCTSTR lpstrName /*= NULL*/) {
|
|
|
|
// Note the correct name and path- the rename checks may fail, since the
|
|
// file is opened elsewhere (possibly with sharing conflicts), so disable
|
|
// them, for now. This code is a little sleazy- but the only time the
|
|
// file name isn't null is if the file's being opened.
|
|
|
|
if (FileTitle().IsEmpty() && lpstrName) {
|
|
m_cfn.EnableCreationCheck(FALSE);
|
|
SetFileName(lpstrName);
|
|
m_cfn.EnableCreationCheck();
|
|
}
|
|
|
|
if (Glyphs()) { // If we already have them, we're already loaded!
|
|
m_csoaCodePage.RemoveAll(); // Clean it all up, and reload.
|
|
m_csmw2oEncodings.RemoveAll();
|
|
m_crr.Empty();
|
|
}
|
|
|
|
CByteArray cbaGTT;
|
|
|
|
union {
|
|
PBYTE pbGTT;
|
|
sGTTHeader *psgtth;
|
|
};
|
|
|
|
Load(cbaGTT); // If this fails, it will post a reason why
|
|
|
|
if (!cbaGTT.GetSize())
|
|
return FALSE;
|
|
|
|
pbGTT = cbaGTT.GetData();
|
|
|
|
sMapTable* psmt = (sMapTable *) (pbGTT + psgtth -> m_dwofMapTable);
|
|
sMapTableEntry* psmte = (sMapTableEntry *)(psmt + 1);
|
|
|
|
// Before we go any further, let's do some validation
|
|
|
|
if (psgtth -> m_dwVersion != sGTTHeader::Version1Point0)
|
|
return FALSE;
|
|
|
|
m_bPaired = FALSE;
|
|
|
|
// First, let's snarf up the code page info
|
|
|
|
struct sInvocation {
|
|
DWORD m_dwSize, m_dwOffset;
|
|
};
|
|
|
|
struct sCodePageInfo {
|
|
DWORD m_dwPage;
|
|
sInvocation m_siSelect, m_siDeselect;
|
|
} *psci = (sCodePageInfo *)(pbGTT + psgtth -> m_dwofCodePages);
|
|
|
|
m_csoaCodePage.RemoveAll();
|
|
|
|
for (unsigned u = 0; u < psgtth -> m_dwcCodePages; u++, psci++) {
|
|
m_csoaCodePage.Add(new CCodePageData(psci -> m_dwPage));
|
|
if (!psci -> m_siSelect.m_dwSize != !psci -> m_siSelect.m_dwOffset ||
|
|
!psci -> m_siDeselect.m_dwSize !=
|
|
!psci -> m_siDeselect.m_dwOffset)
|
|
return FALSE; // The data is bogus!
|
|
|
|
CodePage(u).SetInvocation(((PBYTE) psci) + psci->m_siSelect.m_dwOffset,
|
|
psci -> m_siSelect.m_dwSize, TRUE);
|
|
CodePage(u).SetInvocation(((PBYTE) psci) + psci->m_siDeselect.m_dwOffset,
|
|
psci -> m_siDeselect.m_dwSize, FALSE);
|
|
//CodePage(u).SetInvocation(pbGTT + psci -> m_siSelect.m_dwOffset,
|
|
// psci -> m_siSelect.m_dwSize, TRUE);
|
|
//CodePage(u).SetInvocation(pbGTT + psci -> m_siDeselect.m_dwOffset,
|
|
// psci -> m_siDeselect.m_dwSize, FALSE);
|
|
}
|
|
|
|
// Next, we need to walk the glyph run tables to decipher and use the map
|
|
// table.
|
|
|
|
struct sGlyphRun {
|
|
WORD m_wFirst, m_wc;
|
|
} *psgr = (sGlyphRun *)(pbGTT + psgtth -> m_dwofRuns);
|
|
|
|
_ASSERTE(psgtth -> m_dwcGlyphs == psmt -> m_dwcEntries);
|
|
|
|
WORD wIndex = 0;
|
|
|
|
/*** Changes have been made so that the following code is unneeded.
|
|
The MDT can load predefined GTTs now. In addition, skipping
|
|
the complete loading of these GTTs causes some problems with
|
|
UFM width table loading. The cumulative count of entries in
|
|
the glyph run tables is used to determine the maximum size of
|
|
the UFMs width table. The data in the run tables is also used
|
|
to verify width table entries.
|
|
|
|
// Don't do the rest if a predefined GTT is being loaded. The code below
|
|
// can blow on large GTTs; eg, the DBCS ones. It may be better to find out
|
|
// why the code blows but this seems to work for now.
|
|
|
|
if ((short) m_wID >= CGlyphMap::Wansung
|
|
&& (short) m_wID <= CGlyphMap::CodePage437)
|
|
return TRUE ;
|
|
*/
|
|
|
|
for (unsigned uRun = 0; uRun < psgtth -> m_dwcRuns; uRun++, psgr++)
|
|
for (u = 0; u < psgr -> m_wc; u++, psmte++, wIndex++) {
|
|
CGlyphHandle* pcgh = new CGlyphHandle;
|
|
|
|
switch (psmte -> m_bfType & sMapTableEntry::Format) {
|
|
case sMapTableEntry::Direct:
|
|
|
|
pcgh -> Init((PBYTE) &psmte -> m_uectt, 1, wIndex,
|
|
psgr -> m_wFirst + u);
|
|
break;
|
|
|
|
case sMapTableEntry::Paired:
|
|
|
|
pcgh -> Init(psmte -> m_uectt.abPaired, wIndex,
|
|
psgr -> m_wFirst + u);
|
|
|
|
if (!(psmte -> m_bfType & sMapTableEntry::DBCS))
|
|
m_bPaired = TRUE;
|
|
|
|
break;
|
|
|
|
case sMapTableEntry::Composed:
|
|
|
|
pcgh -> Init(pbGTT + psgtth -> m_dwofMapTable +
|
|
psmte -> m_uectt.wOffset + sizeof wIndex,
|
|
*(PWORD) (pbGTT + psgtth -> m_dwofMapTable +
|
|
psmte -> m_uectt.wOffset), wIndex,
|
|
psgr -> m_wFirst + u);
|
|
break;
|
|
|
|
default: // Bad news- bad format
|
|
delete pcgh; // No orphans needed!
|
|
return FALSE;
|
|
}
|
|
|
|
// Don't forget the code page ID!
|
|
|
|
pcgh -> SetCodePage(psmte -> m_bCodePageIndex,
|
|
CodePage(psmte -> m_bCodePageIndex).Page());
|
|
|
|
// Mark this if it is to be disabled.
|
|
|
|
if (psmte -> m_bfType & sMapTableEntry::Disable)
|
|
pcgh -> SetPredefined(CGlyphHandle::Removed);
|
|
|
|
m_csmw2oEncodings[psgr -> m_wFirst + u] = pcgh;
|
|
m_crr.Add(pcgh);
|
|
}
|
|
|
|
// If we're predefined, Merge now.
|
|
|
|
m_lidPredefined = psgtth -> m_lidPredefined;
|
|
|
|
if (m_lidPredefined != NoPredefined)
|
|
MergePredefined();
|
|
|
|
return TRUE; // We actually did it!
|
|
}
|
|
|
|
/******************************************************************************
|
|
|
|
CGlyphMap::RLE
|
|
|
|
This generates an RLE-format file image of the glyph map.
|
|
|
|
******************************************************************************/
|
|
|
|
BOOL CGlyphMap::RLE(CFile& cfTarget) {
|
|
|
|
sRLE srle;
|
|
|
|
srle.m_widRLE = 0x78FE;
|
|
srle.m_wcFirst = m_crr.First();
|
|
srle.m_wcLast = m_crr.Last();
|
|
srle.m_dwFlag = 0;
|
|
srle.m_dwcGlyphs = m_csmw2oEncodings.GetCount();
|
|
srle.m_dwcRuns = m_crr.RunCount();
|
|
srle.m_dwcbImage = 4 * sizeof srle.m_dwcbImage + srle.m_dwcRuns *
|
|
m_crr.Size();
|
|
srle.m_dwcbThis = srle.m_dwcbImage + 3 * sizeof srle.m_dwcbThis +
|
|
srle.m_dwcGlyphs * sizeof srle.m_dwcGlyphs;
|
|
|
|
// Determine the correct format, and thus the RLE size
|
|
|
|
if (!m_crr.MustCompose())
|
|
srle.m_wFormat = m_bPaired ? sRLE::Paired : sRLE::Direct;
|
|
else
|
|
if (srle.m_dwcGlyphs < 256 &&
|
|
srle.m_dwcbThis + m_crr.ExtraNeeded() <= 0xffff) {
|
|
srle.m_dwcbThis += m_crr.ExtraNeeded();
|
|
srle.m_wFormat = sRLE::LengthIndexOffset;
|
|
}
|
|
else {
|
|
srle.m_dwcbThis += m_crr.ExtraNeeded(FALSE);
|
|
srle.m_wFormat = sRLE::LengthOffset;
|
|
}
|
|
|
|
// We now need to feed the offset information down to the lower level
|
|
// classes, so that they are prepared to render their information to
|
|
// the target file.
|
|
|
|
// The first items encoded are the runs, which immediately follow the RLE
|
|
// header.
|
|
|
|
DWORD dwOffset = sizeof srle + srle.m_dwcRuns * m_crr.Size();
|
|
|
|
m_crr.NoteOffset(dwOffset, TRUE, m_bPaired);
|
|
|
|
// If this requires extra data, it will be appearing after the FD_GLYPHSET
|
|
|
|
if (srle.m_wFormat == sRLE::LengthOffset ||
|
|
srle.m_wFormat == sRLE::LengthIndexOffset)
|
|
m_crr.NoteExtraOffset(dwOffset,
|
|
srle.m_wFormat == sRLE::LengthIndexOffset);
|
|
|
|
_ASSERTE(dwOffset == srle.m_dwcbThis);
|
|
|
|
// We've got our data, we've got our file, and we've got a job to do.
|
|
// Hop to it!
|
|
|
|
try {
|
|
cfTarget.Write(&srle, sizeof srle);
|
|
m_crr.WriteSelf(cfTarget);
|
|
m_crr.WriteHandles(cfTarget, srle.m_wFormat);
|
|
m_crr.WriteEncodings(cfTarget, srle.m_wFormat == sRLE::LengthOffset ?
|
|
CGlyphHandle::RLEBig : CGlyphHandle::RLESmall);
|
|
}
|
|
|
|
catch (CException *pce) {
|
|
pce -> ReportError();
|
|
pce -> Delete();
|
|
return FALSE;
|
|
}
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
/******************************************************************************
|
|
|
|
CGlyphMap::Glyph
|
|
|
|
I tried to do this in the header, file but that lets it be in-line, and I
|
|
don't want to export CRunRecord.
|
|
|
|
******************************************************************************/
|
|
|
|
CGlyphHandle* CGlyphMap::Glyph(unsigned u) {
|
|
return m_crr.GetGlyph(u);
|
|
}
|
|
|
|
/******************************************************************************
|
|
|
|
CGlyphMap::CreateEditor
|
|
|
|
This member function overrides the CProjectNode function to create a new
|
|
CGlyphMapContainer document embedding this Glyph Map. It then uses the
|
|
appropriate document template to open a view on this document.
|
|
|
|
******************************************************************************/
|
|
|
|
CMDIChildWnd *CGlyphMap::CreateEditor() {
|
|
CGlyphMapContainer* pcgmcMe= new CGlyphMapContainer(this, FileName());
|
|
|
|
// Make up a cool title
|
|
|
|
pcgmcMe -> SetTitle(m_pcbnWorkspace -> Name() + _TEXT(": ") + Name());
|
|
|
|
CMDIChildWnd *pcmcwNew = (CMDIChildWnd *) m_pcmdt ->
|
|
CreateNewFrame(pcgmcMe, NULL);
|
|
|
|
if (pcmcwNew) {
|
|
m_pcmdt -> InitialUpdateFrame(pcmcwNew, pcgmcMe, TRUE);
|
|
m_pcmdt -> AddDocument(pcgmcMe);
|
|
}
|
|
|
|
return pcmcwNew;
|
|
}
|
|
|
|
/******************************************************************************
|
|
|
|
CGlyphMap::Generate
|
|
|
|
This member function generates the GTT format image of the current data.
|
|
|
|
It returns a BOOL indicating success or failure.
|
|
|
|
******************************************************************************/
|
|
|
|
BOOL CGlyphMap::Generate(CFile& cfGTT) {
|
|
|
|
sGTTHeader sgtth;
|
|
|
|
// First, take care of any predefined stuff, if we have to
|
|
|
|
if (m_lidPredefined != NoPredefined)
|
|
UnmergePredefined(TRUE);
|
|
|
|
sgtth.m_dwcGlyphs = Glyphs();
|
|
sgtth.m_dwcRuns = m_crr.RunCount();
|
|
sgtth.m_dwcCodePages = CodePages();
|
|
sgtth.m_lidPredefined = m_lidPredefined;
|
|
|
|
// The run table is the first item after the header, so add in its size
|
|
|
|
sgtth.m_dwofRuns = sgtth.m_dwcbImage; // Runs are first item
|
|
sgtth.m_dwcbImage += sgtth.m_dwcRuns * m_crr.Size(FALSE);
|
|
sgtth.m_dwofCodePages = sgtth.m_dwcbImage; // Code pages are next
|
|
|
|
// Code page selection strings immediately follow the Code page structures
|
|
// The code page information size must be padded to a DWORD multiple
|
|
|
|
sgtth.m_dwcbImage += sgtth.m_dwcCodePages * CodePage(0).Size();
|
|
DWORD dwPadding ; // # of padding bytes needed to DWORD align map table
|
|
DWORD dwSelOffset ; // Offset from each CODEPAGEINFO to sel/desel strings
|
|
DWORD dwSelBytes ; // Total # of bytes used by sel/desel strings
|
|
dwSelOffset = sgtth.m_dwcbImage - sgtth.m_dwofCodePages ;
|
|
for (unsigned u = 0 ; u < CodePages() ; u++) {
|
|
CodePage(u).NoteOffsets(dwSelOffset) ;
|
|
dwSelOffset -= CodePage(0).Size() ;
|
|
} ;
|
|
|
|
// Save the amount of padding, as we'll write it later. It is also needed
|
|
// as part of the computation for the mapping table offset.
|
|
|
|
dwPadding = dwSelOffset + sgtth.m_dwcbImage ;
|
|
dwPadding = (sizeof(DWORD) -
|
|
(dwPadding & (sizeof(DWORD) - 1))) & (sizeof(DWORD) - 1) ;
|
|
|
|
// Compute the number of bytes used for the sel/desel strings and pad. Then
|
|
// add this count to the image count of bytes so that it can be used to set
|
|
// the mapping table offset.
|
|
|
|
dwSelBytes = dwSelOffset + dwPadding ;
|
|
sgtth.m_dwcbImage += dwSelBytes;
|
|
sgtth.m_dwofMapTable = sgtth.m_dwcbImage;
|
|
|
|
TRACE("***CGlyphMap::Generate() - dwPadding = %d, dwSelBytes = %d, m_dwofMapTable = 0x%x\n", dwPadding, dwSelBytes, sgtth.m_dwofMapTable) ;
|
|
|
|
// Map Table size determination
|
|
|
|
sMapTable smt(Glyphs());
|
|
|
|
// Fortunately for us, the following not only preps the data, it also
|
|
// updates the image size for us
|
|
if (m_crr.MustCompose())
|
|
m_crr.NoteOffset(smt.m_dwcbImage, FALSE, m_bPaired);
|
|
|
|
// Final Size calculation
|
|
sgtth.m_dwcbImage += smt.m_dwcbImage;
|
|
|
|
// Now, we just write it out
|
|
|
|
try {
|
|
cfGTT.Write(&sgtth, sizeof sgtth); // Header
|
|
|
|
ASSERT(sgtth.m_dwofRuns == cfGTT.GetPosition()) ;
|
|
m_crr.WriteSelf(cfGTT, FALSE); // Glyph Runs
|
|
|
|
ASSERT(sgtth.m_dwofCodePages == cfGTT.GetPosition()) ;
|
|
for (unsigned u = 0; u < CodePages(); u++)
|
|
CodePage(u).WriteSelf(cfGTT); // Code page structures
|
|
|
|
for (u = 0; u < CodePages(); u++)
|
|
CodePage(u).WriteInvocation(cfGTT); // Code page invocations
|
|
|
|
// Pad with 0's to DWORD align the mapping table
|
|
|
|
dwSelBytes = 0 ;
|
|
cfGTT.Write((LPSTR) &dwSelBytes, dwPadding) ;
|
|
|
|
// Do the map table, and we are finished!
|
|
|
|
ASSERT(sgtth.m_dwofMapTable == cfGTT.GetPosition()) ;
|
|
cfGTT.Write(&smt, sizeof smt);
|
|
m_crr.WriteMapTable(cfGTT, m_lidPredefined != NoPredefined);
|
|
m_crr.WriteEncodings(cfGTT, CGlyphHandle::GTT);
|
|
}
|
|
catch (CException * pce) {
|
|
// Take care of any predefined stuff, if we have to
|
|
|
|
if (m_lidPredefined != NoPredefined)
|
|
MergePredefined();
|
|
|
|
// Feedback- something broke when it shouldn't have
|
|
pce -> ReportError();
|
|
pce -> Delete();
|
|
return FALSE;
|
|
}
|
|
// Take care of any predefined stuff, if we have to
|
|
|
|
if (m_lidPredefined != NoPredefined)
|
|
MergePredefined();
|
|
|
|
Changed(FALSE);
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
/******************************************************************************
|
|
|
|
CGlyphMapContainer class - this encases the glyph table UI when it is either
|
|
embedded in the driver, or loaded directly from the GTT
|
|
|
|
******************************************************************************/
|
|
|
|
/******************************************************************************
|
|
|
|
CGlyphMapContainer::CGlyphMapContainer()
|
|
|
|
This default constructor is used whenever dynamic creation is used, which is
|
|
most MFC usages of the document system. It starts with an empty glyph map.
|
|
|
|
******************************************************************************/
|
|
|
|
IMPLEMENT_DYNCREATE(CGlyphMapContainer, CDocument)
|
|
|
|
CGlyphMapContainer::CGlyphMapContainer()
|
|
{
|
|
m_pcgm = new CGlyphMap;
|
|
m_pcgm -> NoteOwner(*this);
|
|
m_bSaveSuccessful = m_bEmbedded = FALSE;
|
|
}
|
|
|
|
/******************************************************************************
|
|
|
|
CGlyphMapContainer:CGlyphMapContainer(CGlyphMap *pvgm, CString csPath)
|
|
|
|
This constructor override is used when we create a CGlyphMapContainer
|
|
document from the driver/project level editor. In this case, a digested
|
|
map is passed, so no additional I/O us needed.
|
|
|
|
******************************************************************************/
|
|
|
|
CGlyphMapContainer::CGlyphMapContainer(CGlyphMap *pcgm, CString csPath)
|
|
{
|
|
m_pcgm = pcgm;
|
|
SetPathName(csPath, FALSE);
|
|
m_bEmbedded = TRUE;
|
|
m_bSaveSuccessful = FALSE;
|
|
m_pcgm -> NoteOwner(*this); // This is the document being edited!
|
|
}
|
|
|
|
BOOL CGlyphMapContainer::OnNewDocument() {
|
|
return CDocument::OnNewDocument();
|
|
}
|
|
|
|
CGlyphMapContainer::~CGlyphMapContainer() {
|
|
if (!m_bEmbedded && m_pcgm)
|
|
delete m_pcgm;
|
|
}
|
|
|
|
|
|
BEGIN_MESSAGE_MAP(CGlyphMapContainer, CDocument)
|
|
//{{AFX_MSG_MAP(CGlyphMapContainer)
|
|
// NOTE - the ClassWizard will add and remove mapping macros here.
|
|
//}}AFX_MSG_MAP
|
|
END_MESSAGE_MAP()
|
|
|
|
/////////////////////////////////////////////////////////////////////////////
|
|
// CGlyphMapContainer diagnostics
|
|
|
|
#ifdef _DEBUG
|
|
void CGlyphMapContainer::AssertValid() const {
|
|
CDocument::AssertValid();
|
|
}
|
|
|
|
void CGlyphMapContainer::Dump(CDumpContext& dc) const {
|
|
CDocument::Dump(dc);
|
|
}
|
|
#endif //_DEBUG
|
|
|
|
/////////////////////////////////////////////////////////////////////////////
|
|
// CGlyphMapContainer serialization
|
|
|
|
void CGlyphMapContainer::Serialize(CArchive& ar) {
|
|
if (ar.IsStoring()) {
|
|
// TODO: add storing code here
|
|
}
|
|
else {
|
|
// TODO: add loading code here
|
|
}
|
|
}
|
|
|
|
/////////////////////////////////////////////////////////////////////////////
|
|
// CGlyphMapContainer commands
|
|
|
|
BOOL CGlyphMapContainer::OnSaveDocument(LPCTSTR lpszPathName) {
|
|
|
|
// We save via the glyph map's Generate function.
|
|
|
|
CFile cfGTT;
|
|
if (!cfGTT.Open(lpszPathName, CFile::modeCreate | CFile::modeWrite |
|
|
CFile::shareExclusive))
|
|
return FALSE;
|
|
|
|
m_bSaveSuccessful = m_pcgm -> Generate(cfGTT) ;
|
|
|
|
// Update the save timestamp. This is done here so that other user of the
|
|
// Generate() function don't update the timestamp.
|
|
|
|
m_pcgm->m_ctSaveTimeStamp = CTime::GetCurrentTime() ;
|
|
|
|
// when open mutiple workspace, last of the pos is the workspace conating current GTT or UFM
|
|
CDriverResources * pcpr =(CDriverResources *) m_pcgm->GetWorkspace() ;
|
|
if (m_pcgm->ChngedCodePt() && pcpr ) {// && m_pcgm->Glyphs() <= 1000 ) {
|
|
pcpr->SyncUFMWidth() ;
|
|
}
|
|
return m_bSaveSuccessful ;
|
|
}
|
|
|
|
|
|
/******************************************************************************
|
|
|
|
CGlyphMapContainer::OnOpenDocument
|
|
|
|
This overrides the typical MFC open document action, which is to open the
|
|
document by serialization. Instead, we use the CGlyphMap Load override for
|
|
the GTT format to initialize the GlyphMap.
|
|
|
|
******************************************************************************/
|
|
|
|
BOOL CGlyphMapContainer::OnOpenDocument(LPCTSTR lpstrFile)
|
|
{
|
|
return m_pcgm->Load(lpstrFile) ;
|
|
}
|
|
|
|
|
|
/******************************************************************************
|
|
|
|
CGlyphMapContainer::SaveModified
|
|
|
|
The document is about to close. If the GTT was changed by the user but the
|
|
user does not want to save the changes, the user wants to close the GTT, and
|
|
the GTT was loaded from a workspace, then reload the GTT so that the changes
|
|
are removed from the in memory copy of the GTT. This keeps those "discarded"
|
|
changes from being displayed the next time the GTT is edited.
|
|
|
|
Return TRUE if it is ok for the doc to close. Otherwise, FALSE.
|
|
|
|
******************************************************************************/
|
|
|
|
BOOL CGlyphMapContainer::SaveModified()
|
|
{
|
|
// Get a pointer to the associate view class instance and use it to make
|
|
// sure the code page select/deselect strings are copied into the GTT.
|
|
|
|
POSITION pos = GetFirstViewPosition() ;
|
|
ASSERT(pos != NULL) ;
|
|
CGlyphMapView* pcgmv = (CGlyphMapView*) GetNextView(pos) ;
|
|
pcgmv->SaveBothSelAndDeselStrings() ;
|
|
|
|
// Find out if the document was modified and if the user wants to save it.
|
|
|
|
m_bSaveSuccessful = FALSE ;
|
|
BOOL bmodified = IsModified() ;
|
|
BOOL bcloseok = CDocument::SaveModified() ;
|
|
|
|
// If the GTT was loaded from a workspace, the GTT was changed, the user
|
|
// does NOT want to save the changes, and he does want to close the doc,
|
|
// then reload the GTT.
|
|
|
|
if (m_bEmbedded && bmodified && bcloseok && !m_bSaveSuccessful)
|
|
m_pcgm->Load() ;
|
|
|
|
// Return flag indicating if it is ok to close the doc.
|
|
|
|
return bcloseok ;
|
|
}
|
|
|
|
|