/****************************************************************************** Source File: Generic Font Information.CPP This implements the CFontInfo and all related classes, which describe printer fonts in all the detail necessary to satisfy all these different operating systems. Copyright (c) 1997 by Microsoft Corporation. All Rights Reserved. A Pretty Penny Enterprises Production Change History: 03-03-1997 Bob_Kjelgaard@Prodigy.Net Began work on this monster ******************************************************************************/ #include "StdAfx.H" #if defined(LONG_NAMES) #include "Glyph Translation.H" #include "Generic Font Information.H" #include #else #include "GTT.H" #include "FontInfo.H" #include "CodePage.H" #endif #include "..\Resource.H" // These are used to glue in UFM-specific stuff struct INVOCATION { DWORD dwcbCommand; // Byte size of string DWORD dwofCommand; // Offset in the file to the string }; // `Yes, this is a bit sleazy, but DirectDraw has nothing now or ever to // do with this tool, so why waste time tracking down more files? #define __DD_INCLUDED__ typedef DWORD PDD_DIRECTDRAW_GLOBAL, PDD_SURFACE_LOCAL, DESIGNVECTOR, DD_CALLBACKS, DD_HALINFO, DD_SURFACECALLBACKS, DD_PALETTECALLBACKS, VIDEOMEMORY; #include "winddi.h" #include "fmnewfm.h" #include // A handy constant for converting radians to 10's of a degree static const double dConvert = 900.0 / atan2(1.0, 0.0); // Use a static CCodePageInformation to derive more benefit from caching static CCodePageInformation ccpi; /****************************************************************************** CKern This class encapsulates the kerning pair structure. It's pretty trivial. The CFontInfo class maintains an array of these. ******************************************************************************/ class CKern : public CObject { FD_KERNINGPAIR m_fdkp; public: CKern() { m_fdkp.wcFirst = m_fdkp.wcSecond = m_fdkp.fwdKern = 0; } CKern(FD_KERNINGPAIR& fdkp) { m_fdkp = fdkp; } WCHAR First() const { return m_fdkp.wcFirst; } WCHAR Second() const { return m_fdkp.wcSecond; } short Amount() const { return m_fdkp.fwdKern; } void SetAmount(short sNew) { m_fdkp.fwdKern = sNew; } void Store(CFile& cf) { cf.Write(&m_fdkp, sizeof m_fdkp); } }; /****************************************************************************** CFontDifference class This class handles the requisite information content for the Font Difference structure involved with Font Simulation. ******************************************************************************/ /****************************************************************************** CFontDifference::CFontDifference(PBYTE pb) This constructor initializes the fields from a FONTDIFF structure in memory. ******************************************************************************/ CFontDifference::CFontDifference(PBYTE pb, CBasicNode *pcbn) { FONTDIFF *pfd = (FONTDIFF *) pb; m_pcbnOwner = pcbn; m_cwaMetrics.Add(pfd -> usWinWeight); m_cwaMetrics.Add(pfd -> fwdMaxCharInc); m_cwaMetrics.Add(pfd -> fwdAveCharWidth); m_cwaMetrics.Add((WORD) (dConvert * atan2((double) pfd -> ptlCaret.x, (double) pfd -> ptlCaret.y))); } /****************************************************************************** CFontDifference::SetMetric This function will modify one of the four metrics, if it is new, and it meets our criteria (Max >= Average, 0 <= Angle < 900, Weight <= 1000). Errors are reported via a public enum return code. ******************************************************************************/ WORD CFontDifference::SetMetric(unsigned u, WORD wNew) { if (wNew == m_cwaMetrics[u]) return OK; switch (u) { case Max: if (wNew < m_cwaMetrics[Average]) return Reversed; break; case Average: if (wNew > m_cwaMetrics[Max]) return Reversed; break; case Weight: if (wNew > 1000) return TooBig; break; default: // Angle if (wNew > 899) return TooBig; } m_cwaMetrics[u] = wNew; m_pcbnOwner -> Changed(); return OK; } /****************************************************************************** CFontDifference::Store(CFile& cf) This member creates a FONTDIFF structure, fills it, and writes it to the given file. The big calculation is the x and y components for the italic angle, if there is one. ******************************************************************************/ void CFontDifference::Store(CFile& cf, WORD wfSelection) { FONTDIFF fd = {0, 0, 0, 0, m_cwaMetrics[Weight], wfSelection, m_cwaMetrics[Average], m_cwaMetrics[Max]}; fd.bWeight = (m_cwaMetrics[Weight] >= FW_BOLD) ? PAN_WEIGHT_BOLD : (m_cwaMetrics[Weight] > FW_EXTRALIGHT) ? PAN_WEIGHT_MEDIUM : PAN_WEIGHT_LIGHT; fd.ptlCaret.x = !m_cwaMetrics[Angle] ? 0 : (long) (10000.0 * tan(((double) m_cwaMetrics[Angle]) / dConvert)); fd.ptlCaret.y = m_cwaMetrics[Angle] ? 10000 : 1; cf.Write(&fd, sizeof fd); } /****************************************************************************** CFontInfo class This class encapsulates all of the font knowledge this application needs. ******************************************************************************/ IMPLEMENT_SERIAL(CFontInfo, CProjectNode, 0) /****************************************************************************** CFontInfo::MapPFM This loads a PFM format file, if it isn't already loaded. ******************************************************************************/ BOOL CFontInfo::MapPFM() { if (m_cbaPFM.GetSize()) return TRUE; // Already has been loaded! try { CFile cfLoad(m_csSource, CFile::modeRead | CFile::shareDenyWrite); m_cbaPFM.SetSize(cfLoad.GetLength()); cfLoad.Read(m_cbaPFM.GetData(), cfLoad.GetLength()); } catch (CException *pce) { pce -> ReportError(); pce -> Delete(); m_cbaPFM.RemoveAll(); return FALSE; } return TRUE; } /****************************************************************************** CFontInfo::GetTranslation This loads a PFM format file and gets the default CTT ID from it. Nothing else is done. ******************************************************************************/ extern "C" int ICttID2GttID(long lPredefinedCTTID); BOOL CFontInfo::GetTranslation() { // PFM file structures- these are declared at this level to keep them off // the master class list for the project. #pragma pack(1) // The following is byte-aligned struct sPFMHeader { WORD m_wType, m_wPoints, m_wVertRes, m_wHorizRes, m_wAscent, m_wInternalLeading, m_wExternalLeading; BYTE m_bfItalic, m_bfUnderline, m_bfStrikeOut; WORD m_wWeight; BYTE m_bCharSet; WORD m_wPixWidth, m_wPixHeight; BYTE m_bfPitchAndFamily; WORD m_wAvgWidth, m_wMaxWidth; BYTE m_bFirstChar, m_bLastChar, m_bDefaultChar, m_bBreakChar; WORD m_wcbWidth; DWORD m_dwDevice, m_dwFace, m_dwBitsPointer, m_dwofBits; BYTE m_bReserved; }; struct sPFMExtension { WORD m_wcbRemaining; // From this point on DWORD m_dwofExtMetrics, m_dwofExtentTable, m_dwofOriginTable, m_dwofPairKernTable, m_dwofTrackKernTable, m_dwofDriverInfo, m_dwReserved; }; #pragma pack (2) // Everything else has word alignment struct sOldKernPair { union { BYTE m_abEach[2]; WORD m_wBoth; }; short m_sAmount; }; struct sKernTrack { short m_sDegree, m_sMinSize, m_sMinAmount, m_sMaxSize, m_sMaxAmount; }; struct sPFMDriverInfo { enum {CurrentVersion = 0x200}; enum {CannotItalicize = 1, CannotUnderline, SendCRAfterUsing = 4, CannotMakeBold = 8, CannotDoubleUnderline = 0x10, CannotStrikeThru = 0x20, BackspaceForPairs = 0x40}; WORD m_wcbThis, m_wVersion, m_wfCapabilities, m_widThis, m_wAdjustY, m_wYMovement, m_widCTT, m_wUnderlinePosition, m_wDoubleUnderlinePosition, m_wStrikeThruPosition; DWORD m_dwofSelect, m_dwofDeselect; WORD m_wPrivateData; /* Used in DeskJet driver for font enumerations */ short m_sShiftFromCenter; enum {HPIntelliFont, TrueType, PPDSScalable, CapsL, OEMType1, OEMType2}; WORD m_wFontType; }; #pragma pack() // We now return control to you if (!MapPFM()) return FALSE; // Now, map out the rest of the pieces of the structure. union { BYTE *pbPFM; // Base of the file for offsets! sPFMHeader *pspfmh; }; pbPFM = m_cbaPFM.GetData(); // Screen out evil files- part 1: is length sufficient? unsigned uSize = sizeof (sPFMHeader) + sizeof (sPFMExtension) + sizeof (sPFMDriverInfo); if ((unsigned) m_cbaPFM.GetSize() < uSize) return FALSE; // YA Sanity check if (pspfmh -> m_bLastChar < pspfmh -> m_bFirstChar) return FALSE; // Width table, if there is one. WORD *pwWidth = pspfmh -> m_wPixWidth ? NULL : (PWORD) (pspfmh + 1); uSize += !!pwWidth * sizeof (WORD) * (2 + pspfmh -> m_bLastChar - pspfmh -> m_bFirstChar); // Screen out evil files- part 2: is length still sufficient? if ((unsigned) m_cbaPFM.GetSize() < uSize) return FALSE; // PFMExtension follows width table, otherwise the header sPFMExtension *pspfme = pwWidth ? (sPFMExtension *) (pwWidth + 2 + pspfmh -> m_bLastChar - pspfmh -> m_bFirstChar) : (sPFMExtension *) (pspfmh + 1); // Penultimate sanity check- is the driver info offset real? if ((unsigned) m_cbaPFM.GetSize() < pspfme -> m_dwofDriverInfo + sizeof (sPFMDriverInfo)) return FALSE; // Text Metrics, DriverInfo and others are pointed at by PFM // Extension. sPFMDriverInfo *pspfmdi = (sPFMDriverInfo *) (pbPFM + pspfme -> m_dwofDriverInfo); // Final sanity check- is the driver info version real? if (pspfmdi -> m_wVersion > sPFMDriverInfo::CurrentVersion) return FALSE; m_widTranslation = (WORD) ICttID2GttID((long) - (short) pspfmdi -> m_widCTT); Changed(); return TRUE; } /****************************************************************************** CFontInfo::CalculateWidths() This member function is needed whenever a change is made to a variable pitch font's width table, or equally well, whenever an arbitrary table is picked up by a formerly fixed pitch font. It calculates the width using the approved algorithm (average means average of 26 lower-case plus the space, unless they don't exist, in which case it is of all non-zero widths). ******************************************************************************/ void CFontInfo::CalculateWidths() { // Assume the max width is now 0, then prove otherwise. Also collect the // raw information needed to correctly calculate the average width. m_wMaximumIncrement = 0; unsigned uPointsToAverage = 0, uOverallWidth = 0, uAverageWidth = 0, uZeroPoints = 0; for (unsigned u = 0; u < (unsigned) m_cpaGlyphs.GetSize(); u++) { WORD wWidth = m_cwaWidth[u];; m_wMaximumIncrement = max(m_wMaximumIncrement, wWidth); uOverallWidth += wWidth; if (!wWidth) uZeroPoints++; if (Glyph(u).CodePoint() == m_cwaSignificant[Break] || (Glyph(u).CodePoint() >= (WORD) 'a' && Glyph(u).CodePoint() <= (WORD) 'z')) { uAverageWidth += wWidth; uPointsToAverage++; } } // If we averaged 27 points, then this is the correct width. Otherwise, // We average all of the widths. cf the IFIMETRICS description in DDK m_wAverageWidth = (uPointsToAverage == 27) ? (WORD) (0.5 + ((double) uAverageWidth) / 27.0) : (WORD) (0.5 + (((double) uOverallWidth) / (double) (u - uZeroPoints))); } /****************************************************************************** CFontInfo::CFontInfo() This class constructor has a lot of work to do. Not only does it have to initialize 5 zillion fields, it has to build the context menu list, and a few other glorious items of that ilk. ******************************************************************************/ CFontInfo::CFontInfo() { m_pcmdt = NULL; m_pcgmTranslation = NULL; m_pcfdBold = m_pcfdItalic = m_pcfdBoth = NULL; m_cfn.SetExtension(_T(".UFM")); m_bCharacterSet = m_bPitchAndFamily = 0; m_wMaximumIncrement = m_wfStyle = m_wWeight = m_wAverageWidth = m_wHeight = m_widTranslation = 0; m_bLocation = m_bTechnology = m_bfGeneral = 0; m_bScalable = FALSE; m_wXResolution = m_wYResolution = m_wPrivateData = 0; m_sPreAdjustY = m_sPostAdjustY = m_sCenterAdjustment = 0; m_wMaxScale = m_wMinScale = m_wScaleDevice = 0; m_bfScaleOrientation = 0; m_cwaSpecial.InsertAt(0, 0, 1 + InternalLeading); // Initialize this array. // 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); } /****************************************************************************** CFontInfo::CFontInfo(const CFontInfo& cfiRef, WORD widCTT) This class constructor duplicates an existing font, but changes the CTT ID, and generates a new name and file name accordingly ******************************************************************************/ CFontInfo::CFontInfo(const CFontInfo& cfiRef, WORD widCTT) { m_pcmdt = cfiRef.m_pcmdt; m_pcfdBold = m_pcfdItalic = m_pcfdBoth = NULL; m_pcgmTranslation = NULL; m_cfn.SetExtension(_T(".UFM")); CString csWork; // Copy the file name and generate new names csWork.Format(_T("(CTT %d)"), (long) (short) widCTT); m_cfn.Rename(cfiRef.m_cfn.Path() + cfiRef.Name() + csWork); m_csSource = cfiRef.m_csSource; Rename(cfiRef.Name() + csWork); m_bCharacterSet = m_bPitchAndFamily = 0; m_wMaximumIncrement = m_wfStyle = m_wWeight = m_wAverageWidth = m_wHeight = 0; m_bLocation = m_bTechnology = m_bfGeneral = 0; m_bScalable = FALSE; m_wXResolution = m_wYResolution = m_wPrivateData = 0; m_sPreAdjustY = m_sPostAdjustY = m_sCenterAdjustment = 0; m_wMaxScale = m_wMinScale = m_wScaleDevice = 0; m_bfScaleOrientation = 0; m_cwaSpecial.InsertAt(0, 0, 1 + InternalLeading); // Initialize this array. m_widTranslation = widCTT; // Build the context menu control m_cwaMenuID.Copy(cfiRef.m_cwaMenuID); } CFontInfo::~CFontInfo() { if (m_pcfdBold) delete m_pcfdBold; if (m_pcfdItalic) delete m_pcfdItalic; if (m_pcfdBoth) delete m_pcfdBoth; } /****************************************************************************** CFontInfo::GTTDescription This returns a CString naming the GTT associated with this font. It will come from the workspace if the font is a resource, or the string table, if it is predefined. ******************************************************************************/ CString CFontInfo::GTTDescription() const { if (m_pcgmTranslation) return m_pcgmTranslation -> Name(); CString csName; if ((short) m_widTranslation <= 0) csName.LoadString(IDS_DefaultPage + (short) m_widTranslation); if (!csName.GetLength()) csName.Format(IDS_ResourceID, (short) m_widTranslation); return csName; } /****************************************************************************** CFontInfo::InterceptItalic This calculates where a line drawn at the italic slant angle would intercept a rectangle the height of the ascender, and twice the maximum width of the font. It is used to help draw the image of this line in the font editor. ******************************************************************************/ void CFontInfo::InterceptItalic(CPoint& cpt) const { if (!m_cwaSpecial[ItalicAngle]) { // Nothing cpt.x = 5; cpt.y = 0; return; } // First, assume we will hit the top- it's almost always true. cpt.x = 5 + (long) (0.5 + tan(((double) m_cwaSpecial[ItalicAngle]) / dConvert) * ((double) m_cwaSpecial[Baseline])); if (cpt.x <= -5 + 2 * m_wMaximumIncrement) { cpt.y = 0; return; } // OK, assume the opposite cpt.y = (long) (0.5 + tan(((double) (900 - m_cwaSpecial[ItalicAngle])) / dConvert) * ((double) (-10 + 2 * m_wMaximumIncrement))); cpt.x = -5 + 2 * m_wMaximumIncrement; } /****************************************************************************** CFontInfo::CompareWidths This compares the character widths for two indices, and returns, Less, More, or Equal, as need be. It is not const, because Glyph() is not, and I've already got a bazillion member functions. ******************************************************************************/ unsigned CFontInfo::CompareWidths(unsigned u1, unsigned u2) { _ASSERT(IsVariableWidth() && u1 < (unsigned) m_cpaGlyphs.GetSize() && u2 < (unsigned) m_cpaGlyphs.GetSize()); return (m_cwaWidth[u1] < m_cwaWidth[u2]) ? Less : (m_cwaWidth[u1] > m_cwaWidth[u2]) ? More : Equal; } /****************************************************************************** CFontInfo::MapKerning This maps out the available code points, and the kern pairs in both directions, into a CWordArray and a pair of CSafeMapWordToObs (where the underlying CObjects are CMapWordToDWords), respectively. This allows the Add Kerning Pair dialog to screen out already defined pairs, and invalid code points. ******************************************************************************/ void CFontInfo::MapKerning(CSafeMapWordToOb& csmw2o1, CSafeMapWordToOb& csmw2o2, CWordArray& cwaPoints) { // If this isn't variable width, then we'll need to pull up some glyph // data, temporarily. BOOL bDispose = !IsVariableWidth(); if (bDispose) m_pcgmTranslation -> Collect(m_cpaGlyphs); for (unsigned u = 0; u < m_pcgmTranslation -> Glyphs(); u++) if (!DBCSFont() || Glyph(u).CodePoint() < 0x80) cwaPoints.Add(Glyph(u).CodePoint()); else break; if (bDispose) m_cpaGlyphs.RemoveAll(); for (u = 0; u < m_csoaKern.GetSize(); u++) { CKern& ck = *(CKern *) m_csoaKern[u]; union { CObject *pco; CMapWordToDWord *pcmw2d; }; // Map first word to second if (csmw2o1.Lookup(ck.First(), pco)) { _ASSERT(!pcmw2d -> operator[](ck.Second())); pcmw2d -> operator[](ck.Second()) = (DWORD) ck.Amount(); } else { CMapWordToDWord *pcmw2d = new CMapWordToDWord; pcmw2d -> operator[](ck.Second()) = (DWORD) ck.Amount(); csmw2o1[ck.First()] = pcmw2d; } // Now the other direction if (csmw2o2.Lookup(ck.Second(), pco)) { _ASSERT(!pcmw2d -> operator[](ck.First())); pcmw2d -> operator[](ck.First()) = (DWORD) ck.Amount(); } else { CMapWordToDWord *pcmw2d = new CMapWordToDWord; pcmw2d -> operator[](ck.First()) = (DWORD) ck.Amount(); csmw2o2[ck.Second()] = pcmw2d; } } } /****************************************************************************** CFontInfo::CompareKernAmount This is an editor sort helper- it tells how two kern amounts compare by index. ******************************************************************************/ unsigned CFontInfo::CompareKernAmount(unsigned u1, unsigned u2) const { CKern &ck1 = *(CKern *) m_csoaKern[u1], &ck2 = *(CKern *) m_csoaKern[u2]; return (ck1.Amount() < ck2.Amount()) ? Less : (ck1.Amount() > ck2.Amount()) ? More : Equal; } /****************************************************************************** CFontInfo::CompareKernFirst This is an editor sort helper- it tells how two kern first characters compare by index. ******************************************************************************/ unsigned CFontInfo::CompareKernFirst(unsigned u1, unsigned u2) const { CKern &ck1 = *(CKern *) m_csoaKern[u1], &ck2 = *(CKern *) m_csoaKern[u2]; return (ck1.First() < ck2.First()) ? Less : (ck1.First() > ck2.First()) ? More : Equal; } /****************************************************************************** CFontInfo::CompareKernSecond This is an editor sort helper- it tells how two kern second characters compare by index. ******************************************************************************/ unsigned CFontInfo::CompareKernSecond(unsigned u1, unsigned u2) const { CKern &ck1 = *(CKern *) m_csoaKern[u1], &ck2 = *(CKern *) m_csoaKern[u2]; return (ck1.Second() < ck2.Second()) ? Less : (ck1.Second() > ck2.Second()) ? More : Equal; } /****************************************************************************** CFontInfo::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) the font data using the base file name. ******************************************************************************/ void CFontInfo::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(".PFM"))) { 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); } /****************************************************************************** CFontInfo::Generate This member generates the font information in one of the supported forms. I determine the desired form from the file's extension. ******************************************************************************/ BOOL ConvertPFMToIFI(LPCTSTR lpstrPFM, LPCTSTR lpstrIFI, LPCTSTR lpstrUniq); extern "C" { BOOL BConvertPFM(LPBYTE lpbPFM, DWORD dwCodePage, LPBYTE lpbGTT, PWSTR pwstrUnique, LPCTSTR lpstrUFM, int iGTTID); DWORD DwGetCodePage(LONG lPredefinedCTTId); } BOOL CFontInfo::Generate(CString csPath) { CString csExtension = csPath.Right(4); csExtension.MakeUpper(); if (csExtension == _T(".IFI")) return ConvertPFMToIFI(m_csSource, csPath, m_csUnique); if (csExtension == _T(".UFM")) { if (!m_pcgmTranslation) { CString csWork; csWork.Format(IDS_BadCTTID, (LPCTSTR) m_csSource, (long) (short) m_widTranslation); AfxMessageBox(csWork); return FALSE; } // Determine whether a GTT file or code page is to be used DWORD dwCodePage = DwGetCodePage((LONG) - (short) m_widTranslation); // Load the GTT file, if we need to. This handles predefined, as well CByteArray cbaMap; m_pcgmTranslation -> Load(cbaMap); if (!cbaMap.GetSize()) return FALSE; // Load the PFM file into memory (should already be there) if (!MapPFM()) return FALSE; // Couldn't load PFM- impossible at this point! // Convert the unique name string to Unicode CByteArray cbaIn; CWordArray cwaOut; cbaIn.SetSize(1 + m_csUnique.GetLength()); lstrcpy((LPSTR) cbaIn.GetData(), (LPCTSTR) m_csUnique); ccpi.Convert(cbaIn, cwaOut, GetACP()); // DO IT! return BConvertPFM(m_cbaPFM.GetData(), dwCodePage, cbaMap.GetData(), cwaOut.GetData(), FileName(), (short) m_widTranslation); } return TRUE; } /****************************************************************************** CFontInfo::AddFamily This searches for the given name in the list of families, and adds it if it is not there. It returns TRUE if it succeeded. ******************************************************************************/ BOOL CFontInfo::AddFamily(LPCTSTR lpstrNew) { for (unsigned u = 0; u < Families(); u++) if (!Family(u).CompareNoCase(lpstrNew)) break; if (u < Families()) return FALSE; // Already have it! try { m_csaFamily.Add(lpstrNew); } catch (CException * pce) { pce -> ReportError(); pce -> Delete(); return FALSE; } Changed(); return TRUE; } /****************************************************************************** CFontInfo::RemoveFamily This function removes the given family name from the list of aliases. This code is more robust than it needs to be- it'll remove duplicates, even though the add code won't allow them to be added. No telling what the input data looks like, though, is there? ******************************************************************************/ void CFontInfo::RemoveFamily(LPCTSTR lpstrDead) { for (unsigned u = 0; u < Families(); u ++) if (!Family(u).CompareNoCase(lpstrDead)) { m_csaFamily.RemoveAt(u--); // Decrement so we don't miss one Changed(); } } /***************************************************************************** CFontInfo::ChangePitch We exploit the fact that the widths are maintained in the CGlyphMap (actually the CGlyphHandle) class. All this method need do for a variable font flipping to fixed is to toss out the m_cpaGlyphs member's content. To flip to variable, collect the handles, then check the first one's width- if it's non-zero, then a previous transition from variable to fixed is being undone, and we can recycle the old values, thus keeping any edits that may have been lost. Otherwise, the trick code comes- the initial values get filled- what's tricky is that for a DBCS character set, only the SBCS values less than 0x80 can be variable. ******************************************************************************/ void CFontInfo::ChangePitch(BOOL bFixed) { if (bFixed == !IsVariableWidth()) return; // Nothing to change! if (bFixed) { m_cpaGlyphs.RemoveAll(); // CPtrArray doesn't delete anything m_wAverageWidth = DBCSFont() ? (1 + m_wMaximumIncrement) >> 1 : m_wMaximumIncrement; Changed(); return; } if (!m_pcgmTranslation) return; // Can't do this with no GTT available m_pcgmTranslation -> Collect(m_cpaGlyphs); if (!m_cwaWidth.GetSize()) m_cwaWidth.InsertAt(0, 0, m_cpaGlyphs.GetSize()); Changed(); // It sure has... if (!m_cpaGlyphs.GetSize() || m_cwaWidth[0]) { // Update the maximum and average width if this is not DBCS if (!DBCSFont()) CalculateWidths(); return; // We did all that needed to be done } if (!DBCSFont()) { for (int i = 0; i < m_cpaGlyphs.GetSize(); i++) m_cwaWidth[i] = m_wMaximumIncrement; return; } for (int i = 0; i < m_cpaGlyphs.GetSize() && Glyph(i).CodePoint() < 0x80;) m_cwaWidth[i++] = m_wAverageWidth; // In DBCS, this is always it } /***************************************************************************** CFontInfo::SetScalability This is called to turn scalability on or off. All that really needs to be done is to establish values for the maximum and minimum scale, the font -> device units mapping members, and the lowercase ascender /descender, if this is the first time this information has changed. ******************************************************************************/ void CFontInfo::SetScalability(BOOL bOn) { if (IsScalable() == !!bOn) return; // Nothing to change if (!bOn) { m_bScalable = FALSE; Changed(); return; } m_bScalable = TRUE; Changed(); if (m_wMaxScale && m_wMinScale && m_wMaxScale != m_wMinScale) return; // We've already got data. m_wMaxScale = m_wMinScale = m_wScaleDevice = m_wHeight - m_cwaSpecial[InternalLeading]; // Flaky, but set the initial max and min to +- 1 point from nominal m_wMaxScale += m_wYResolution / 72; m_wMinScale -= m_wYResolution / 72; // Finally, set the lowercase ascender and descender to simple defaults m_cwaSpecial[Lowerd] = m_cwaSpecial[Baseline] - m_cwaSpecial[InternalLeading]; m_cwaSpecial[Lowerp] = m_wHeight - m_cwaSpecial[Baseline]; } /***************************************************************************** CFontInfo::SetSpecial This adjusts anything that may need adjusting if a special metric is altered. ******************************************************************************/ void CFontInfo::SetSpecial(unsigned ufMetric, short sSpecial) { if (m_cwaSpecial[ufMetric] == (WORD) sSpecial) return; // Nothing changed m_cwaSpecial[ufMetric] = (WORD) sSpecial; switch (ufMetric) { case InternalLeading: // Adjust the scaling factors if need be if (m_wScaleDevice > m_wHeight - sSpecial) m_wScaleDevice = m_wHeight - sSpecial; if (m_wMinScale > m_wHeight - sSpecial) m_wMinScale = m_wHeight - sSpecial; } Changed(); } /***************************************************************************** CFontInfo::SetMaxWidth This is not as simple as it might seem. If the font is variable, don't do it. If it is not, then if it is DBCS, set the average width to 1/2 the new maximum. Otherwise, set it also to the maximum. ******************************************************************************/ void CFontInfo::SetMaxWidth(WORD wWidth) { if (IsVariableWidth()) return; if (wWidth == m_wMaximumIncrement) return; // Nothing to do! m_wMaximumIncrement = wWidth; m_wAverageWidth = DBCSFont() ? (wWidth + 1) >> 1 : wWidth; Changed(); } /***************************************************************************** CFontInfo::SetHeight This member checks to see if the new height is non-zero and new. If so, it uses it for the new height, then adjusts all of the possibly affected special metrics so they continue to meet the constraints. ******************************************************************************/ BOOL CFontInfo::SetHeight(WORD wHeight) { if (!wHeight || wHeight == m_wHeight) return FALSE; m_wHeight = wHeight; short sBaseline = (short) (min(wHeight, m_cwaSpecial[Baseline])); for (unsigned u = 0; u <= InternalLeading; u++) { switch (u) { case InterlineGap: if (m_cwaSpecial[u] > 2 * wHeight) m_cwaSpecial[u] = 2 * wHeight; continue; case UnderOffset: case SubMoveY: case Lowerd: if ((short) m_cwaSpecial[u] < sBaseline - wHeight) m_cwaSpecial[u] = sBaseline - wHeight; continue; case UnderSize: if (m_cwaSpecial[u] > wHeight - (unsigned) sBaseline) m_cwaSpecial[u] = wHeight = (unsigned) sBaseline; if (!m_cwaSpecial[u]) m_cwaSpecial[u] = 1; continue; case SuperSizeX: case SubSizeX: case SuperMoveX: case SubMoveX: case ItalicAngle: continue; // These aren't affected default: if (m_cwaSpecial[u] > (unsigned) sBaseline) m_cwaSpecial[u] = sBaseline; } } // Adjust the scaling factors if need be if (m_wScaleDevice > m_wHeight - m_cwaSpecial[InternalLeading]) m_wScaleDevice = m_wHeight - m_cwaSpecial[InternalLeading]; if (m_wMinScale > m_wHeight - m_cwaSpecial[InternalLeading]) m_wMinScale = m_wHeight - m_cwaSpecial[InternalLeading]; Changed(); return TRUE; } /***************************************************************************** CFontInfo::SetCharacterSet This one is a bit tricky- the new character set must be compatible with the GTT file associated with this font. So we need to check it before we pass on it. ASSUMPTIONS: (1) Things are bulletproof enough that the existing character set will already pass this test. ******************************************************************************/ BOOL CFontInfo::SetCharacterSet(BYTE bNew) { unsigned u; switch (bNew) { case SHIFTJIS_CHARSET: for (u = 0; u < m_pcgmTranslation -> CodePages(); u++) if (m_pcgmTranslation -> PageID(u) == 932) break; // We're OK if (u == m_pcgmTranslation -> CodePages()) return FALSE; break; case HANGEUL_CHARSET: for (u = 0; u < m_pcgmTranslation -> CodePages(); u++) if (m_pcgmTranslation -> PageID(u) == 949) break; // We're OK if (u == m_pcgmTranslation -> CodePages()) return FALSE; break; case CHINESEBIG5_CHARSET: for (u = 0; u < m_pcgmTranslation -> CodePages(); u++) if (m_pcgmTranslation -> PageID(u) == 950) break; // We're OK if (u == m_pcgmTranslation -> CodePages()) return FALSE; break; case GB2312_CHARSET: for (u = 0; u < m_pcgmTranslation -> CodePages(); u++) if (m_pcgmTranslation -> PageID(u) == 936) break; // We're OK if (u == m_pcgmTranslation -> CodePages()) return FALSE; break; default: // Don't accept any DBCS codepages for (u = 0; u < m_pcgmTranslation -> CodePages(); u++) switch (m_pcgmTranslation -> PageID(u)) { case 932: case 936: case 949: case 950: case 1361: // Johab- but it isn't in the converter! return FALSE; } } if (m_bCharacterSet != bNew) { m_bCharacterSet = bNew; Changed(); } return TRUE; } /****************************************************************************** CFontInfo::SetSignificant This member is called to change the value of one of the significant code points (break character or default) encoded in the font. Doing this correctly means getting the ANSI and UNICODE versions of the code point, and discarding any out-of-range values. This function returns an encoded value indicating success or cause of failure. ******************************************************************************/ WORD CFontInfo::SetSignificant(WORD wItem, WORD wChar, BOOL bUnicode) { _ASSERT(wItem > Last && wItem <= Break); if (!bUnicode && wChar > 255) return DoubleByte; CWaitCursor cwc; // Unfortunately, if not Unicode, htis is slow CPtrArray cpaGlyphs; CWordArray cwa; CByteArray cba; CDWordArray cdaPage; m_pcgmTranslation -> Collect(cpaGlyphs); m_pcgmTranslation -> CodePages(cdaPage); for (int i = 0; i < cpaGlyphs.GetSize(); i++) { CGlyphHandle& cgh = *(CGlyphHandle *) cpaGlyphs[i]; if (bUnicode) { if (cgh.CodePoint() == wChar) { cwa.Add(wChar); ccpi.Convert(cba, cwa, cdaPage[cgh.CodePage()]); break; } } else { if (i) cwa.SetAt(0, cgh.CodePoint()); else cwa.Add(cgh.CodePoint()); ccpi.Convert(cba, cwa, cdaPage[cgh.CodePage()]); if (cba.GetSize() == 1 && cba[0] == (BYTE) wChar) break; cba.RemoveAll(); // So we can try again } } if (i == cpaGlyphs.GetSize()) return InvalidChar; if (cba.GetSize() != 1) return DoubleByte; // OK, we passed all of the hurdles if (m_cwaSignificant[wItem] == cwa[0]) return OK; // Nothing changed!!!! m_cwaSignificant[wItem] = cwa[0]; m_cbaSignificant[wItem] = cba[0]; Changed(); return OK; } /****************************************************************************** CFontInfo::SetScaleLimit This member receives a proposed new maximum or minimum font size in device units. First, it is compared to the existing size, for a quick exit. Then we check to see that the ordering of the limits and the nominal size is preserved. If it is not, we describe the problem and leave. Otherwise, we update the value, and note that the font information has changed. ******************************************************************************/ WORD CFontInfo::SetScaleLimit(BOOL bMax, WORD wNew) { if (wNew == (bMax ? m_wMaxScale : m_wMinScale)) return ScaleOK; if (bMax ? wNew <= m_wMinScale : wNew >= m_wMaxScale) return Reversed; if (bMax ? wNew < m_wScaleDevice : wNew > m_wScaleDevice) return NotWindowed; if (bMax) m_wMaxScale = wNew; else m_wMinScale = wNew; Changed(); return ScaleOK; } /****************************************************************************** CFontInfo::SetDeviceEmHeight This member sets the units used for determing the conversion from font units (in which all metrics are given) to device units. The checking is similar to that above, except here, we need to make sure that the font units are always of equal or greater resolution than the device units. ******************************************************************************/ WORD CFontInfo::SetDeviceEmHeight(WORD wNew) { if (wNew == m_wScaleDevice) return ScaleOK; if (wNew > m_wHeight - m_cwaSpecial[InternalLeading]) return Reversed; if (wNew < m_wMinScale || wNew > m_wMaxScale) return NotWindowed; m_wScaleDevice = wNew; Changed(); return ScaleOK; } /****************************************************************************** CFontInfo::Load This member function loads the UFM file, finally initializing all of the tons of individual values we're trying to pretend we know how to manage here. ******************************************************************************/ // These flags have a negative sense, but the UI makes more sense if they // are positive, so their sense is inverted using the following constant #define FLIP_FLAGS (DF_NOITALIC | DF_NOUNDER | DF_NO_BOLD |\ DF_NO_DOUBLE_UNDERLINE | DF_NO_STRIKETHRU) BOOL CFontInfo::Load() { CFile cfUFM; if (!cfUFM.Open(m_cfn.FullName(), CFile::modeRead | CFile::shareDenyWrite)) { CString csMessage; csMessage.Format(IDS_LoadFailure, (LPCTSTR) m_cfn.FullName()); AfxMessageBox(csMessage); return FALSE; } // Try to load the file- proclaim defeat on any exception. CByteArray cbaUFM; try { cbaUFM.SetSize(cfUFM.GetLength()); cfUFM.Read(cbaUFM.GetData(), cbaUFM.GetSize()); } catch (CException *pce) { pce -> ReportError(); pce -> Delete(); CString csMessage; csMessage.Format(IDS_LoadFailure, (LPCTSTR) m_cfn.FullName()); AfxMessageBox(csMessage); return FALSE; } // First thing in the file is the header. PUNIFM_HDR pufmh = (PUNIFM_HDR) cbaUFM.GetData(); // pull up the GTT ID m_widTranslation = (WORD) pufmh -> lGlyphSetDataRCID; // Find the UNIDRVINFO PUNIDRVINFO pudi = (PUNIDRVINFO) (cbaUFM.GetData() + pufmh -> loUnidrvInfo); // Fill in the two invocation strings- why it is the offset is NULL // and the count is garbage when there is none is beyond me, but so be it. if (pudi -> SelectFont.dwofCommand) m_ciSelect.Init((PBYTE) pudi + pudi -> SelectFont.dwofCommand, pudi -> SelectFont.dwcbCommand); if (pudi -> UnSelectFont.dwofCommand) m_ciDeselect.Init((PBYTE) pudi + pudi -> UnSelectFont.dwofCommand, pudi -> UnSelectFont.dwcbCommand); // Initialize the files of interest from the UNIDRVINFO m_bScalable = (pudi -> flGenFlags & UFM_SCALABLE); m_bTechnology = (BYTE) pudi -> wType; if (pudi -> flGenFlags & UFM_SOFT) m_bLocation = Download; else if (pudi -> flGenFlags & UFM_CART_MAIN) m_bLocation = MainCartridge; else if (pudi -> flGenFlags & UFM_CART) m_bLocation = Cartridge; m_wXResolution = pudi -> wXRes; m_wYResolution = pudi -> wYRes; m_wPrivateData = pudi -> wPrivateData; m_sPreAdjustY = pudi -> sYAdjust; m_sPostAdjustY = pudi -> sYMoved; m_sCenterAdjustment = pudi -> sShift; m_bfGeneral = (BYTE) pudi -> fCaps ^ FLIP_FLAGS; // Time to bite the IFIMETRICS bullet union { PBYTE pbIFI; PIFIMETRICS pifi; }; pbIFI = cbaUFM.GetData() + pufmh -> loIFIMetrics; // Get the special metrics loaded first m_cwaSpecial[CapH] = pifi -> fwdCapHeight; m_cwaSpecial[LowerX] = pifi -> fwdXHeight; m_cwaSpecial[SuperMoveX] = pifi -> fwdSuperscriptXOffset; m_cwaSpecial[SuperMoveY] = pifi -> fwdSuperscriptYOffset; m_cwaSpecial[SubMoveX] = pifi -> fwdSubscriptXOffset; m_cwaSpecial[SubMoveY] = pifi -> fwdSubscriptYOffset; m_cwaSpecial[SuperSizeX] = pifi -> fwdSuperscriptXSize; m_cwaSpecial[SuperSizeY] = pifi -> fwdSuperscriptYSize; m_cwaSpecial[SubSizeX] = pifi -> fwdSubscriptXSize; m_cwaSpecial[SubSizeY] = pifi -> fwdSubscriptYSize; m_cwaSpecial[UnderOffset] = pifi -> fwdUnderscorePosition; m_cwaSpecial[UnderSize] = pifi -> fwdUnderscoreSize; m_cwaSpecial[StrikeOffset] = pifi -> fwdStrikeoutPosition; m_cwaSpecial[StrikeSize] = pifi -> fwdStrikeoutSize; m_cwaSpecial[ItalicAngle] = (WORD) (dConvert * atan2((double) pifi -> ptlCaret.x, (double) pifi -> ptlCaret.y)); m_cwaSpecial[Baseline] = pifi -> fwdWinAscender; m_cwaSpecial[InterlineGap] = pifi -> fwdMacLineGap; // Now, see if there is an EXTTEXTMETRIC- if so, fill in some fields from // it. PEXTTEXTMETRIC petm = (PEXTTEXTMETRIC) (pufmh -> loExtTextMetric ? (cbaUFM.GetData() + pufmh -> loExtTextMetric) : NULL); if (petm) { m_wMinScale = petm -> emMinScale; m_wMaxScale = petm -> emMaxScale; // Scads of special metrics- double underlining will not be preserved. m_cwaSpecial[Lowerd] = petm -> emLowerCaseAscent; m_cwaSpecial[Lowerp] = petm -> emLowerCaseDescent; m_cwaSpecial[ItalicAngle] = petm -> emSlant; // Orientation, Master Units (Font units are calculated) m_bfScaleOrientation = (BYTE) petm -> emOrientation; m_wScaleDevice = petm -> emMasterHeight; } // Let's get the rest of the IFIMETRICS stuff, now. m_cbaSignificant.SetSize(1 + Break); m_cwaSignificant.SetSize(1 + Break); m_cbaSignificant[Break] = pifi -> chBreakChar; m_bCharacterSet = pifi -> jWinCharSet; m_cbaSignificant[Default] = pifi -> chDefaultChar; m_cbaSignificant[First] = pifi -> chFirstChar; m_cbaSignificant[Last] = pifi -> chLastChar; m_bPitchAndFamily = pifi -> jWinPitchAndFamily; m_wAverageWidth = pifi -> fwdAveCharWidth; m_cwaSignificant[Break] = pifi -> wcBreakChar; m_cwaSignificant[Default] = pifi -> wcDefaultChar; m_cwaSignificant[First] = pifi -> wcFirstChar; m_cwaSignificant[Last] = pifi -> wcLastChar; m_wMaximumIncrement = pifi -> fwdMaxCharInc; m_wWeight = pifi -> usWinWeight; m_wHeight = pifi -> fwdWinAscender + pifi -> fwdWinDescender; m_cwaSpecial[InternalLeading] = m_wHeight - pifi -> fwdUnitsPerEm; m_wfStyle = pifi -> fsSelection; // Get the face and other name strings. Let CString handle the // Unicode conversions for us, m_csUnique = (PWSTR) (pbIFI + pifi -> dpwszUniqueName); m_csStyle = (PWSTR) (pbIFI + pifi -> dpwszStyleName); m_csFace = (PWSTR) (pbIFI + pifi -> dpwszFaceName); m_csaFamily.RemoveAll(); // Just in case it isn't clean PWSTR pwstrFamily = (PWSTR) (pbIFI + pifi -> dpwszFamilyName); CString csWork(pwstrFamily); m_csaFamily.Add(csWork); pwstrFamily += 1 + wcslen(pwstrFamily); if (pifi -> flInfo & FM_INFO_FAMILY_EQUIV) while (*pwstrFamily) { csWork = pwstrFamily; m_csaFamily.Add(csWork); pwstrFamily += 1 + wcslen(pwstrFamily); } // Font Difference structures, if any. if (pifi -> dpFontSim) { union { PBYTE pbfs; FONTSIM *pfs; }; pbfs = pbIFI + pifi -> dpFontSim; // If we're reloading, clean these up! if (m_pcfdBold) delete m_pcfdBold; if (m_pcfdItalic) delete m_pcfdItalic; if (m_pcfdBoth) delete m_pcfdBoth; // Bold simulation if (pfs -> dpBold) m_pcfdBold = new CFontDifference(pbfs + pfs -> dpBold, this); // Italic Simulation if (pfs -> dpItalic) m_pcfdItalic = new CFontDifference(pbfs + pfs -> dpItalic, this); // Bold Italic Simulation if (pfs -> dpBoldItalic) m_pcfdBoth = new CFontDifference(pbfs + pfs -> dpBoldItalic, this); } // Width table, but only if there is an associated GTT. if (m_pcgmTranslation && pufmh -> loWidthTable) { union { PBYTE pbwt; PWIDTHTABLE pwt; }; pbwt = cbaUFM.GetData() + pufmh -> loWidthTable; m_pcgmTranslation -> Collect(m_cpaGlyphs); // Collect all the handles m_cwaWidth.RemoveAll(); m_cwaWidth.InsertAt(0, 0, m_cpaGlyphs.GetSize()); for (unsigned u = 0; u < pwt -> dwRunNum; u++) { PWORD pwWidth = (PWORD) (pbwt + pwt -> WidthRun[u].loCharWidthOffset); for (unsigned uGlyph = 0; uGlyph < pwt -> WidthRun[u].wGlyphCount; uGlyph++) // Glyph handles start at 1, not 0! m_cwaWidth[uGlyph + -1 + pwt -> WidthRun[u].wStartGlyph] = *pwWidth++; } } // Kerning Table, if any m_csoaKern.RemoveAll(); if (pufmh -> loKernPair) { PKERNDATA pkd = (PKERNDATA) (cbaUFM.GetData() + pufmh -> loKernPair); for (unsigned u = 0; u < pkd -> dwKernPairNum; u++) m_csoaKern.Add(new CKern(pkd -> KernPair[u])); } // Return triumphant to whoever deigned to need this service. return TRUE; } /***************************************************************************** CUniString class This is a little helper class that will convert a CString to a UNICODE string, and take care of cleanup, etc., so the font storage code doesn't get any messier than it already will be. ******************************************************************************/ class CUniString : public CWordArray { public: CUniString(LPCSTR lpstrInit); operator PCWSTR() const { return GetData(); } unsigned GetSize() const { return sizeof (WCHAR) * (unsigned) CWordArray::GetSize(); } void Write(CFile& cf) { cf.Write(GetData(), GetSize()); } }; CUniString::CUniString(LPCSTR lpstrInit) { SetSize(MultiByteToWideChar(CP_ACP, MB_PRECOMPOSED, lpstrInit, -1, NULL, 0)); MultiByteToWideChar(CP_ACP, MB_PRECOMPOSED, lpstrInit, -1, GetData(), GetSize()); } /***************************************************************************** CFontInfo::Store This member function stores the UFM format information in the specified file by assembling it from the information we have cached in this class. ******************************************************************************/ BOOL CFontInfo::Store(LPCTSTR lpstrFile) { try { // Any exxceptions, we'll just fail gracelessly CFile cfUFM(lpstrFile, CFile::modeCreate | CFile::modeWrite | CFile::shareExclusive); // First thing in the file is the header. UNIFM_HDR ufmh = {sizeof ufmh, UNIFM_VERSION_1_0, 0, (short) m_widTranslation, sizeof ufmh}; // Use Glyph Map default code page if at all possible. ufmh.ulDefaultCodepage = m_pcgmTranslation -> DefaultCodePage(); // Zero fill reserved bytes. memset((PBYTE) ufmh.dwReserved, 0, sizeof ufmh.dwReserved); // Next is the UNIDRVINFO UNIDRVINFO udi = {sizeof udi}; // Invocation Strings affect the size, so get their specifics and // save them, updating the affected size fields as we go. udi.SelectFont.dwofCommand = m_ciSelect.Length() ? udi.dwSize : 0; udi.dwSize += udi.SelectFont.dwcbCommand = m_ciSelect.Length(); udi.UnSelectFont.dwofCommand = m_ciDeselect.Length() ? udi.dwSize : 0; udi.dwSize += udi.UnSelectFont.dwcbCommand = m_ciDeselect.Length(); // Pad this to keep everything DWORD aligned in the file image! unsigned uAdjustUDI = (sizeof udi.dwSize - (udi.dwSize % sizeof udi.dwSize)) % sizeof udi.dwSize; ufmh.loIFIMetrics = ufmh.dwSize += udi.dwSize += uAdjustUDI; // Initialize the UNIDRVINFO switch (m_bLocation) { case Download: udi.flGenFlags = UFM_SOFT; break; case MainCartridge: udi.flGenFlags = UFM_CART | UFM_CART_MAIN; break; case Cartridge: udi.flGenFlags = UFM_CART; break; default: // Resident; udi.flGenFlags = 0; } if (m_bScalable) udi.flGenFlags |= UFM_SCALABLE; udi.wType = m_bTechnology; udi.wXRes = m_wXResolution; udi.wYRes = m_wYResolution; udi.wPrivateData = m_wPrivateData; udi.sYAdjust = m_sPreAdjustY; udi.sYMoved = m_sPostAdjustY; udi.sShift = m_sCenterAdjustment; udi.fCaps = m_bfGeneral ^ FLIP_FLAGS; memset((PSTR) udi.wReserved, 0, sizeof udi.wReserved); // Time to bite the IFIMETRICS bullet IFIEXTRA ifie = {0, 0, m_pcgmTranslation -> Glyphs(), 0, 0, 0}; IFIMETRICS ifi = {sizeof ifi + sizeof ifie, sizeof ifie}; ifi.lEmbedId = ifi.lItalicAngle = ifi.lCharBias = 0; ifi.dpCharSets = 0; ifi.jWinCharSet = m_bCharacterSet; m_bPitchAndFamily = (m_bPitchAndFamily & 0xF0) | (IsVariableWidth() ? VARIABLE_PITCH : FIXED_PITCH); ifi.jWinPitchAndFamily = m_bPitchAndFamily; ifi.usWinWeight = m_wWeight; ifi.flInfo = FM_INFO_TECH_BITMAP | FM_INFO_1BPP | FM_INFO_INTEGER_WIDTH | FM_INFO_NOT_CONTIGUOUS | FM_INFO_RIGHT_HANDED; if (DBCSFont()) ifi.flInfo |= FM_INFO_DBCS_FIXED_PITCH; if (!IsVariableWidth()) { ifi.flInfo |= FM_INFO_OPTICALLY_FIXED_PITCH; if (!DBCSFont()) // DBCS is never constant width ifi.flInfo |= FM_INFO_CONSTANT_WIDTH; } if (m_bScalable) ifi.flInfo |= FM_INFO_ISOTROPIC_SCALING_ONLY; ifi.fsType = FM_NO_EMBEDDING; ifi.fwdLowestPPEm = 1; ifi.fwdUnitsPerEm = m_wHeight - m_cwaSpecial[InternalLeading]; ifi.fwdCapHeight = m_cwaSpecial[CapH]; ifi.fwdSuperscriptXOffset = m_cwaSpecial[SuperMoveX]; ifi.fwdSubscriptXOffset = m_cwaSpecial[SubMoveX]; ifi.fwdSuperscriptXSize = m_cwaSpecial[SuperSizeX]; ifi.fwdSubscriptXSize = m_cwaSpecial[SubSizeX]; ifi.ptlCaret.x = m_cwaSpecial[ItalicAngle] ? (long) ((double) 10000.0 * tan(((double) m_cwaSpecial[ItalicAngle]) / dConvert)) : 0; ifi.ptlCaret.y = m_cwaSpecial[ItalicAngle] ? 10000 : 1; ifi.fwdTypoAscender = ifi.fwdMacAscender = ifi.fwdWinAscender = m_cwaSpecial[Baseline]; ifi.fwdTypoDescender = ifi.fwdMacDescender = -(ifi.fwdWinDescender = m_wHeight - ifi.fwdWinAscender); ifi.fwdTypoLineGap = ifi.fwdMacLineGap = m_cwaSpecial[InterlineGap]; ifi.fwdXHeight = m_cwaSpecial[LowerX]; ifi.fwdSuperscriptYOffset = m_cwaSpecial[SuperMoveY]; ifi.fwdSubscriptYOffset = m_cwaSpecial[SubMoveY]; ifi.fwdSuperscriptYSize = m_cwaSpecial[SuperSizeY]; ifi.fwdSubscriptYSize = m_cwaSpecial[SubSizeY]; ifi.fwdUnderscorePosition = m_cwaSpecial[UnderOffset]; ifi.fwdUnderscoreSize = m_cwaSpecial[UnderSize]; ifi.fwdStrikeoutPosition = m_cwaSpecial[StrikeOffset]; ifi.fwdStrikeoutSize = m_cwaSpecial[StrikeSize]; ifi.fwdAveCharWidth = m_wAverageWidth; ifi.fwdMaxCharInc = m_wMaximumIncrement; ifi.fsSelection = m_wfStyle; ifi.wcBreakChar = m_cwaSignificant[Break]; ifi.wcDefaultChar = m_cwaSignificant[Default]; ifi.chBreakChar = m_cbaSignificant[Break]; ifi.chDefaultChar = m_cbaSignificant[Default]; ifi.chFirstChar = m_cbaSignificant[First]; ifi.chLastChar = m_cbaSignificant[Last]; ifi.wcFirstChar = m_cwaSignificant[First]; ifi.wcLastChar = m_cwaSignificant[Last]; ifi.ptlBaseline.x = 1; ifi.ptlBaseline.y = 0; ifi.ptlAspect.x = udi.wXRes; ifi.ptlAspect.y = udi.wYRes; memcpy(ifi.achVendId, "Unkn", 4); ifi.cKerningPairs = m_csoaKern.GetSize(); ifi.rclFontBox.left = 0; ifi.rclFontBox.top = ifi.fwdWinAscender; ifi.rclFontBox.right = ifi.fwdMaxCharInc; ifi.rclFontBox.bottom = ifi.fwdMacDescender; ifi.ulPanoseCulture = FM_PANOSE_CULTURE_LATIN; ifi.panose.bWeight = (m_wWeight >= FW_BOLD) ? PAN_WEIGHT_BOLD : (m_wWeight > FW_EXTRALIGHT) ? PAN_WEIGHT_MEDIUM : PAN_WEIGHT_LIGHT; ifi.panose.bFamilyType = ifi.panose.bSerifStyle = ifi.panose.bProportion = ifi.panose.bContrast = ifi.panose.bStrokeVariation = ifi.panose.bArmStyle = ifi.panose.bLetterform = ifi.panose.bMidline = ifi.panose.bXHeight = PAN_ANY; // Convert and "place" the various name strings CUniString cusUnique(m_csUnique), cusStyle(m_csStyle), cusFace(m_csFace), cusFamily(m_csaFamily[0]); ifi.dpwszFamilyName = ifi.cjThis; for (int i = 1; i < m_csaFamily.GetSize(); i++) { CUniString cusWork(m_csaFamily[i]); cusFamily.Append(cusWork); } if (m_csaFamily.GetSize() > 1) { cusFamily.Add(0); ifi.flInfo |= FM_INFO_FAMILY_EQUIV; } ifi.cjThis += cusFamily.GetSize(); ifi.dpwszFaceName = ifi.cjThis; ifi.cjThis += cusFace.GetSize(); ifi.dpwszUniqueName = ifi.cjThis; ifi.cjThis += cusUnique.GetSize(); ifi.dpwszStyleName = ifi.cjThis; ifi.cjThis += cusStyle.GetSize(); // The next field must be DWORD aligned, so see what padding // is needed. unsigned uAdjustIFI = (sizeof ifi.cjThis - (ifi.cjThis % sizeof ifi.cjThis)) % sizeof ifi.cjThis; ifi.cjThis += uAdjustIFI; // Finally, Allow for the size of any Font Difference structures. unsigned uSim = !!m_pcfdBold + !!m_pcfdItalic + !!m_pcfdBoth; ifi.dpFontSim = uSim ? ifi.cjThis : 0; ufmh.dwSize += ifi.cjThis += uSim * sizeof(FONTDIFF) + !!uSim * sizeof(FONTSIM); // Now, see if there is an EXTTEXTMETRIC- if so, fill it in. ufmh.loExtTextMetric = m_bScalable ? ufmh.dwSize : 0; EXTTEXTMETRIC etm; if (m_bScalable) { ufmh.dwSize += etm.emSize = sizeof etm; etm.emPointSize = (short)((DWORD)((DWORD) m_wScaleDevice * 1440) / (DWORD) m_wXResolution); // Use DWORD for precision etm.emOrientation = m_bfScaleOrientation; etm.emMasterHeight = m_wScaleDevice; etm.emMinScale = m_wMinScale; etm.emMaxScale = m_wMaxScale; etm.emMasterUnits = m_wHeight - m_cwaSpecial[InternalLeading]; etm.emKernPairs = m_csoaKern.GetSize(); etm.emKernTracks = 0; // Scads of special metrics etm.emCapHeight = m_cwaSpecial[CapH]; etm.emXHeight = m_cwaSpecial[LowerX]; etm.emSuperScript = m_cwaSpecial[SuperMoveY]; etm.emSubScript = m_cwaSpecial[SubMoveY]; etm.emSuperScriptSize = m_cwaSpecial[SuperSizeY]; etm.emSubScriptSize = m_cwaSpecial[SubSizeY]; etm.emUnderlineOffset = m_cwaSpecial[UnderOffset]; etm.emUnderlineWidth = m_cwaSpecial[UnderSize]; etm.emStrikeOutOffset = m_cwaSpecial[StrikeOffset]; etm.emStrikeOutWidth = m_cwaSpecial[StrikeSize]; etm.emDoubleUpperUnderlineOffset = // Not supported- zero them etm.emDoubleLowerUnderlineOffset = etm.emDoubleUpperUnderlineWidth = etm.emDoubleLowerUnderlineWidth = 0; etm.emLowerCaseAscent = m_cwaSpecial[Lowerd]; etm.emLowerCaseDescent = m_cwaSpecial[Lowerp]; etm.emSlant = m_cwaSpecial[ItalicAngle]; } // Width table, but only if there is an associated GTT. ufmh.loWidthTable = IsVariableWidth() * ufmh.dwSize; if (IsVariableWidth()) { // For now, we just need to calculate the size of the table unsigned uRuns = 0, uGlyphs = 0; if (DBCSFont()) { //DBCS // Determine the number of runs needed unsigned u = (unsigned) m_cpaGlyphs.GetSize(); do { while (u-- && !m_cwaWidth[u]); // DBCS has 0 width if (u == (unsigned) -1) break; // We're done! uRuns++, uGlyphs++; while (u-- && m_cwaWidth[u]) uGlyphs++; } while (u != (unsigned) -1); } else { uRuns++; uGlyphs = m_cwaWidth.GetSize(); } ufmh.dwSize += sizeof (WIDTHTABLE) + --uRuns * sizeof (WIDTHRUN) + uGlyphs * sizeof (WORD); } // Kerning Table, if any ufmh.loKernPair = CanKern() ? ufmh.dwSize : 0; //`A "secret" kern pair of all 0's must end this, so this size // is in fact correct. Also note that padding screws up the size of // the KERNDATA structure, so the if (CanKern()) ufmh.dwSize += ((sizeof (KERNDATA) - sizeof (FD_KERNINGPAIR)) & 0xFFFC) + ((1 + m_csoaKern.GetSize()) * sizeof (FD_KERNINGPAIR)); // All sizes have been calculated, and the important structures have // been initialized. Time to start writing all this great stuff! // Header cfUFM.Write(&ufmh, sizeof ufmh); // UNIDRVINFO cfUFM.Write(&udi, sizeof udi); m_ciSelect.WriteEncoding(cfUFM); m_ciDeselect.WriteEncoding(cfUFM); cfUFM.Write(ufmh.dwReserved, uAdjustUDI); // Padding // IFIMETRICS, IFIEXTRA, and names, follwed by any padding cfUFM.Write(&ifi, sizeof ifi); cfUFM.Write(&ifie, sizeof ifie); cusFamily.Write(cfUFM); cusFace.Write(cfUFM); cusUnique.Write(cfUFM); cusStyle.Write(cfUFM); cfUFM.Write(ufmh.dwReserved, uAdjustIFI); // PAdding // Any Font difference structures if (m_pcfdBold || m_pcfdItalic || m_pcfdBoth) { FONTSIM fs; unsigned uWhere = sizeof fs; fs.dpBold = m_pcfdBold ? uWhere : 0; uWhere += !!m_pcfdBold * sizeof (FONTDIFF); fs.dpItalic = m_pcfdItalic ? uWhere : 0; uWhere += !!m_pcfdItalic * sizeof (FONTDIFF); fs.dpBoldItalic = m_pcfdBoth ? uWhere : 0; cfUFM.Write(&fs, sizeof fs); if (m_pcfdBold) m_pcfdBold -> Store(cfUFM, m_wfStyle | FM_SEL_BOLD); if (m_pcfdItalic) m_pcfdItalic -> Store(cfUFM, m_wfStyle | FM_SEL_ITALIC); if (m_pcfdBoth) m_pcfdBoth -> Store(cfUFM, m_wfStyle | FM_SEL_BOLD| FM_SEL_ITALIC); } // EXTTEXTMETRIC if (m_bScalable) cfUFM.Write(&etm, sizeof etm); // Width table if (IsVariableWidth()) if (!DBCSFont()) { // Not DBCS- easy! (Handles always start at 1 WIDTHTABLE wdt = { sizeof wdt, 1, {1, m_cpaGlyphs.GetSize(), sizeof wdt}}; cfUFM.Write(&wdt, sizeof wdt); cfUFM.Write(m_cwaWidth.GetData(), m_cwaWidth.GetSize() * sizeof (WORD)); } else { // This case is a bit nastier CByteArray cbaTable; CWordArray cwaSize; cbaTable.SetSize(sizeof(WIDTHTABLE) - sizeof(WIDTHRUN)); PWIDTHTABLE pwdt = (PWIDTHTABLE) cbaTable.GetData(); pwdt -> dwRunNum = 0; // Calculate and fill in the WIDTHRUN structures and the // Size array unsigned u = 0, uMax = (unsigned) m_cpaGlyphs.GetSize(); do { while (u < uMax && !m_cwaWidth[u++]); if (u == uMax) break; // We're done! // We've found a run- lots of work to do cbaTable.InsertAt(cbaTable.GetSize(), 0, sizeof (WIDTHRUN)); // Add a run to the table pwdt = (PWIDTHTABLE) cbaTable.GetData(); // Remember the glyph handle is 1-based. pwdt -> WidthRun[pwdt -> dwRunNum].wStartGlyph = --u + 1; pwdt -> WidthRun[pwdt -> dwRunNum].wGlyphCount = 0; pwdt -> WidthRun[pwdt -> dwRunNum].loCharWidthOffset = cwaSize.GetSize() * sizeof (WORD); do { cwaSize.Add(m_cwaWidth[u]); pwdt -> WidthRun[pwdt -> dwRunNum].wGlyphCount++; } while (++u < uMax && m_cwaWidth[u]); pwdt -> dwRunNum++; // End of the run! } while (u < uMax); // OK, now we have to add the total size of the WIDTHTABLE // to the various offsets, but we are otherwise ready to rock // and roll. pwdt -> dwSize = cbaTable.GetSize(); for (u = 0; u < pwdt -> dwRunNum; u++) pwdt -> WidthRun[u].loCharWidthOffset += pwdt -> dwSize; // At last- time to send 'em packing! cfUFM.Write(pwdt, pwdt -> dwSize); for (u = 0; u < pwdt -> dwRunNum; u++) cfUFM.Write(cwaSize.GetData() + pwdt -> WidthRun[u].wStartGlyph - 1, pwdt -> WidthRun[u].wGlyphCount * sizeof (WORD)); } // Kern Pairs if (CanKern()) { // KERNDATA is DWORD-packed, but FD_KERNINGPAIR is WORD-packed // the following trick code allows for any slop. KERNDATA kd = {0xFFFC & (sizeof kd - sizeof kd.KernPair), m_csoaKern.GetSize()}; kd.dwSize += (1 + kd.dwKernPairNum) * sizeof kd.KernPair; cfUFM.Write(&kd, 0xFFFC & (sizeof kd - sizeof kd.KernPair)); for (unsigned u = 0; u < m_csoaKern.GetSize(); u++) ((CKern *) m_csoaKern[u]) -> Store(cfUFM); // Now for the "secret" sentinel- CKern ck; // Just happens to 0-init! ck.Store(cfUFM); } } catch (CException *pce) { pce -> ReportError(); pce -> Delete(); return FALSE; } // Return triumphant to whoever deigned to need this service. Changed(FALSE); return TRUE; } /***************************************************************************** CFontInfo::CreateEditor This member function launches an editing view for the font. ******************************************************************************/ CMDIChildWnd* CFontInfo::CreateEditor() { CFontInfoContainer* pcficMe= new CFontInfoContainer(this, FileName()); // Make up a cool title pcficMe -> SetTitle(m_pcbnWorkspace -> Name() + _TEXT(": ") + Name()); CMDIChildWnd *pcmcwNew = (CMDIChildWnd *) m_pcmdt -> CreateNewFrame(pcficMe, NULL); if (pcmcwNew) { m_pcmdt -> InitialUpdateFrame(pcmcwNew, pcficMe, TRUE); m_pcmdt -> AddDocument(pcficMe); } return pcmcwNew; } /****************************************************************************** CFontInfo::Serialize This is responsible for storing and restoring the entire maze of data in persistent object storage. ******************************************************************************/ void CFontInfo::Serialize(CArchive& car) { // We only serialize what's needed to use the UFM file in the editor, // i.e., the glue needed to hold us in the driver workspace. CProjectNode::Serialize(car); } /****************************************************************************** CFontInfo::EnableSim This method is called to turn simulation on or off for the specified item. It receives a reference to the editor's pointer for the same item. ******************************************************************************/ void CFontInfo::EnableSim(unsigned uSim, BOOL bOn, CFontDifference*& pcfd) { CFontDifference*& pcfdTarget = uSim ? (uSim == BothDiff) ? m_pcfdBoth : m_pcfdBold : m_pcfdItalic; // Clear out any irrelevant calls if (bOn == !!pcfd && pcfdTarget == pcfd) return; // If this call is just to init pcfd, do it and leave if (bOn && pcfdTarget) { pcfd = pcfdTarget; return; } if (bOn) pcfd = pcfdTarget = pcfd ? pcfd : new CFontDifference(m_wWeight, m_wMaximumIncrement, m_wAverageWidth, uSim == BoldDiff ? m_cwaSpecial[ItalicAngle] : 175, this); else pcfdTarget = NULL; // pcfd will already have been set correctly Changed(); } /****************************************************************************** CFontInfo::FillKern This preps the passed CListCtrl, if necessary, and fills it with the kerning information. ******************************************************************************/ void CFontInfo::FillKern(CListCtrl& clcView) { for (unsigned u = 0; u < m_csoaKern.GetSize(); u++) { CString csWork; CKern& ckThis = *(CKern *) m_csoaKern[u]; csWork.Format("%d", ckThis.Amount()); int idItem = clcView.InsertItem(u, csWork); clcView.SetItemData(idItem, u); csWork.Format("0x%X", ckThis.First()); clcView.SetItem(idItem, 1, LVIF_TEXT, csWork, -1, 0, 0, u); csWork.Format("0x%X", ckThis.Second()); clcView.SetItem(idItem, 2, LVIF_TEXT, csWork, -1, 0, 0, u); } } /****************************************************************************** CFontInfo::AddKern This method adds an additional kerning pair into the array. and also inserts it into the list view. ******************************************************************************/ void CFontInfo::AddKern(WORD wFirst, WORD wSecond, short sAmount, CListCtrl& clcView) { for (unsigned u = 0; u < KernCount(); u ++) { CKern& ckThis = *(CKern *) m_csoaKern[u]; if (ckThis.Second() < wSecond) continue; if (ckThis.Second() > wSecond) break; _ASSERT(ckThis.First() != wFirst); if (ckThis.First() < wFirst) continue; break; } FD_KERNINGPAIR fdkp = { wFirst, wSecond, sAmount }; m_csoaKern.InsertAt(u, new CKern(fdkp)); CString csWork; csWork.Format("%d", sAmount); int idItem = clcView.InsertItem(u, csWork); clcView.SetItemData(idItem, u); csWork.Format("0x%X", wFirst); clcView.SetItem(idItem, 1, LVIF_TEXT, csWork, -1, 0, 0, u); csWork.Format("0x%X", wSecond); clcView.SetItem(idItem, 2, LVIF_TEXT, csWork, -1, 0, 0, u); Changed(); } /****************************************************************************** CFontInfo::SetKernAmount This will change the kern amount entry for the specified item. ******************************************************************************/ void CFontInfo::SetKernAmount(unsigned u, short sAmount) { if (u >= KernCount()) return; CKern &ckThis = *(CKern *) m_csoaKern[u]; if (sAmount == ckThis.Amount()) return; ckThis.SetAmount(sAmount); Changed(); } /****************************************************************************** CFontInfo::FillWidths This preps the passed CListCtrl, if necessary, and fills it with the character width information. ******************************************************************************/ void CFontInfo::FillWidths(CListCtrl& clcView) { CWaitCursor cwc; clcView.SetItemCount(m_cpaGlyphs.GetSize()); for (int u = 0; u < m_cpaGlyphs.GetSize(); u++) { if (DBCSFont() && !m_cwaWidth[u]) continue; // Don't display these code points. CString csWork; CGlyphHandle& cghThis = *(CGlyphHandle *) m_cpaGlyphs[u]; csWork.Format("%d", m_cwaWidth[u]); int idItem = clcView.InsertItem(u, csWork); clcView.SetItemData(idItem, u); csWork.Format("0x%X", cghThis.CodePoint()); clcView.SetItem(idItem, 1, LVIF_TEXT, csWork, -1, 0, 0, u); } } /****************************************************************************** CFontInfo::SetWidth This member sets the width of a glyph. It also updates the Maximum and Average width information if the font is not a DBCS font. ******************************************************************************/ void CFontInfo::SetWidth(unsigned uGlyph, WORD wWidth) { m_cwaWidth[uGlyph] = wWidth; if (!DBCSFont()) CalculateWidths(); } /****************************************************************************** CFontInfoContainer class This class encapsulates one CFontInfo structure, and is used as a document class so we can leverage the MFC document/view architecture for editing this information both within the contet of the driver, and as a stand-alone file. ******************************************************************************/ IMPLEMENT_DYNCREATE(CFontInfoContainer, CDocument) /****************************************************************************** CFontInfoContainer::CFontInfoContainer() This constructor is used when the document is dynamically created- this will be when the user opens an existing font file, or creates a new one. ******************************************************************************/ CFontInfoContainer::CFontInfoContainer() { m_bEmbedded = FALSE; m_pcfi = new CFontInfo; m_pcfi -> NoteOwner(*this); } /****************************************************************************** CFontInfoContainer::CFontInfoContainer(CFontInfo *pcfi, CString csPath) { This constructor is called when we invoke an editing view from the driver editor. It gives us the font information to view and the name of the file to generate if the user decies to save the data from this view. ******************************************************************************/ CFontInfoContainer::CFontInfoContainer(CFontInfo *pcfi, CString csPath) { m_pcfi = pcfi; m_bEmbedded = TRUE; SetPathName(csPath, FALSE); m_pcfi -> NoteOwner(*this); // Even when embedded, we're editing a file. } /****************************************************************************** CFontInfo::OnNewDocument This is an override- it is called when we are asked to create new font information from scratch. For now, this will just fail. ******************************************************************************/ BOOL CFontInfoContainer::OnNewDocument() { AfxMessageBox(IDS_Unimplemented); return FALSE; // return m_pcfi && CDocument::OnNewDocument(); } /****************************************************************************** CFontInfo::~CFontInfo Our erstwhile destructor must destroy the font info if this wasn't an embedded view. ******************************************************************************/ CFontInfoContainer::~CFontInfoContainer() { if (!m_bEmbedded && m_pcfi) delete m_pcfi; } BEGIN_MESSAGE_MAP(CFontInfoContainer, CDocument) //{{AFX_MSG_MAP(CFontInfoContainer) // NOTE - the ClassWizard will add and remove mapping macros here. //}}AFX_MSG_MAP END_MESSAGE_MAP() ///////////////////////////////////////////////////////////////////////////// // CFontInfoContainer diagnostics #ifdef _DEBUG void CFontInfoContainer::AssertValid() const { CDocument::AssertValid(); } void CFontInfoContainer::Dump(CDumpContext& dc) const { CDocument::Dump(dc); } #endif //_DEBUG ///////////////////////////////////////////////////////////////////////////// // CFontInfoContainer serialization void CFontInfoContainer::Serialize(CArchive& ar) { if (ar.IsStoring()) { // TODO: add storing code here } else { // TODO: add loading code here } } ///////////////////////////////////////////////////////////////////////////// // CFontInfoContainer commands /****************************************************************************** CFontInfoContainer::OnSaveDocument This is called in response to a Save or Save As. We pass it directly to the CFontInfo for processing, rather than using the base class implementation, which would serialize the document. ******************************************************************************/ BOOL CFontInfoContainer::OnSaveDocument(LPCTSTR lpszPathName) { return m_pcfi -> Store(lpszPathName); } /****************************************************************************** CFontInfoContainer::OnOpenDocument For now, I can not allow this to be done, as no GTT file is available. ******************************************************************************/ BOOL CFontInfoContainer::OnOpenDocument(LPCTSTR lpstrFile) { /*m_pcfi -> SetFileName(lpstrFile); return m_pcfi -> Load();*/ return FALSE; }