/****************************************************************************** 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 Bob_Kjelgaard@Prodigy.Net ******************************************************************************/ #include "StdAfx.H" #include "..\Resource.H" #include "GTT.H" #include 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 ccpi; /****************************************************************************** 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; } // 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(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 = 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 ******************************************************************************/ 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 || ccpi.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 = 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 = m_ciEncoding.Length(); break; } dwOffset = m_dwOffset; bLength = 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 = ccpi.IsDBCS(m_dwCodePage) ? ((m_ciEncoding.Length() == 2) ? sMapTableEntry::Paired : sMapTableEntry::Direct ) | (ccpi.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 && m_wFirst && 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; 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) { // The easy part comes if it is already loaded CObject* pco; if (m_csmw2oPredefined.Lookup(wID, pco)) return (CGlyphMap*) pco; // The hard part if (!FindResource(AfxGetResourceHandle(), MAKEINTRESOURCE(wID ? -(short) wID : IDR_CP1252), MAKEINTRESOURCE(IDR_GLYPHMAP))) return NULL; // No such predefined table CGlyphMap *pcgm = new CGlyphMap; pcgm -> m_csName.LoadString(IDS_DefaultPage + wID); pcgm -> SetID(wID); if (pcgm -> Load()) { m_csmw2oPredefined[wID] = pcgm; return pcgm; } delete pcgm; // It didn't work 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[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[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 = 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 = 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[PageID(cgh.CodePage())]++; // Only track these pages } // Remove the unused code pages, unless they have selections for (unsigned u = CodePages(); u--; ) if (!cmw2dPages[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. ******************************************************************************/ void CGlyphMap::Serialize(CArchive& car) { CProjectNode::Serialize(car); if (car.IsLoading()) { car >> m_wID; } else { car << m_wID; } } /****************************************************************************** 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_RenameItem); 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())); } /****************************************************************************** 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 ccpi.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 ccpi.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 { try { int i = (short) m_wID; if (i > 0 || i < Wansung) { // Not a predefined ID CFile cfGTT(m_cfn.FullName(), CFile::modeRead | CFile::shareDenyWrite); cbaMap.SetSize(cfGTT.GetLength()); cfGTT.Read(cbaMap.GetData(), cbaMap.GetSize()); return; } HRSRC hrsrc = FindResource(AfxGetResourceHandle(), MAKEINTRESOURCE(m_wID ? -(short) m_wID : IDR_CP1252), MAKEINTRESOURCE(IDR_GLYPHMAP)); if (!hrsrc) return; HGLOBAL hg = LoadResource(AfxGetResourceHandle(), hrsrc); if (!hg) return; LPVOID lpv = LockResource(hg); if (!lpv) return; cbaMap.SetSize(SizeofResource(AfxGetResourceHandle(), hrsrc)); memcpy(cbaMap.GetData(), lpv, cbaMap.GetSize()); } 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::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); ccpi.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(), 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) { 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 (pcgh -> CodePage() == uPage) pcgh -> SetCodePage(uMapTo, CodePage(uMapTo).Page()); if (pcgh -> CodePage() > uPage) pcgh -> SetCodePage(pcgh -> CodePage() - 1, CodePage(pcgh -> CodePage()).Page()); } m_csoaCodePage.RemoveAt(uPage); 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 ******************************************************************************/ BOOL 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 FALSE; } 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 != ccpi.Convert(cbaCode, cwaCode, CodePage(0).Page())) { CString csWork; csWork.Format(IDS_NoUnicodePoint, u + bFirst, CodePage(0).Page()); AfxMessageBox(csWork); return FALSE; } // 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 FALSE; } // 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; return TRUE; } /****************************************************************************** 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) { // 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(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; 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(); for (unsigned u = 0; u < CodePages(); u++) CodePage(u).NoteOffsets(sgtth.m_dwcbImage); // Save the amount of padding, as we'll write it later DWORD dwPadding = (sizeof (DWORD) - (sgtth.m_dwcbImage & (sizeof (DWORD) - 1))) & (sizeof (DWORD) - 1); sgtth.m_dwcbImage += dwPadding; sgtth.m_dwofMapTable = sgtth.m_dwcbImage; // 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 m_crr.WriteSelf(cfGTT, FALSE); // Glyph Runs 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 cfGTT.Write(((LPSTR) &dwPadding) + 1, dwPadding); // Pad with 0's // Do the map table, and we are finished! 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_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_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; return m_pcgm -> Generate(cfGTT); } /****************************************************************************** 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); }