/* * @doc INTERNAL * * @module CFPF.C -- -- RichEdit CCharFormat and CParaFormat Classes | * * Created: * 9/1995 -- Murray Sargent * * @devnote * The this ptr for all methods points to an internal format class, i.e., * either a CCharFormat or a CParaFormat, which uses the cbSize field as * a reference count. The pCF or pPF argument points at an external * CCharFormat or CParaFormat class, that is, pCF->cbSize and pPF->cbSize * give the size of their structure. The code still assumes that both * internal and external forms are derived from the CHARFORMAT(2) and * PARAFORMAT(2) API structures, so some redesign would be necessary to * obtain a more space-efficient internal form. * * Copyright (c) 1995-2000, Microsoft Corporation. All rights reserved. */ #include "_common.h" #include "_array.h" // for fumemmov() #include "_rtfconv.h" // for IsCharSetValid() #include "_font.h" // for GetFontNameIndex(), GetFontName() ASSERTDATA // Table of formatting info for Normal and Heading styles const STYLEFORMAT g_Style[] = // {dwEffects; yHeight} { // Measurements in points {CFE_BOLD, 14}, // Heading 1 {CFE_BOLD + CFE_ITALIC, 12}, // Heading 2 {0, 12}, // Heading 3 {CFE_BOLD, 12}, // Heading 4 {0, 11}, // Heading 5 {CFE_ITALIC, 11}, // Heading 6 {0, 0}, // Heading 7 {CFE_ITALIC, 0}, // Heading 8 {CFE_BOLD + CFE_ITALIC, 9} // Heading 9 }; BOOL IsValidTwip(LONG dl) { static const LONG dlMax = 0x00FFFFFF; static const LONG dlMin = -0x00FFFFFF; if (dl > dlMax || dl < dlMin) return FALSE; return TRUE; } //------------------------- CCharFormat Class ----------------------------------- /* * CCharFormat::Apply(pCF, dwMask, dwMask2) * * @mfunc * Apply *

to this CCharFormat as specified by nonzero bits in * dwMask and dwMask2 * * @rdesc * HRESULT = NOERROR * * @devnote * Autocolor is dealt with through a neat little hack made possible * by the choice CFE_AUTOCOLOR = CFM_COLOR (see richedit.h). Hence * if

->dwMask specifies color, it automatically resets autocolor * provided (

->dwEffects & CFE_AUTOCOLOR) is zero. * * *

is an external CCharFormat, i.e., it's either a CHARFORMAT * or a CHARFORMAT2 with the appropriate size given by cbSize. But * this CCharFormat is internal and cbSize is used as a reference count. */ HRESULT CCharFormat::Apply ( const CCharFormat *pCF, //@parm CCharFormat to apply to this CF DWORD dwMask, //@parm Mask corresponding to CHARFORMAT2 DWORD dwMask2) //@parm Mask for additional internal parms { TRACEBEGIN(TRCSUBSYSBACK, TRCSCOPEINTERN, "CCharFormat::Apply"); DWORD dwEffectMask = dwMask & CFM_EFFECTS2; bool fNewCharset = false; // Reset effect bits to be modified and OR in supplied values _dwEffects &= ~dwEffectMask; _dwEffects |= pCF->_dwEffects & dwEffectMask; // Ditto for additional effects given by dwMask2 dwEffectMask = dwMask2 & 0xBBFC0000; // Don't allow autocolors, sub/sups _dwEffects &= ~dwEffectMask; _dwEffects |= pCF->_dwEffects & dwEffectMask; // Setup Temp. display attributes idx if (dwMask2 & CFM2_USETMPDISPATTR) _sTmpDisplayAttrIdx = pCF->_sTmpDisplayAttrIdx; // If CFM_BOLD is specified, it overrides the font weight if(dwMask & CFM_BOLD) _wWeight = (pCF->_dwEffects & CFE_BOLD) ? FW_BOLD : FW_NORMAL; // Handle CFM_COLOR since it's overloaded with CFE_AUTOCOLOR if(dwMask & CFM_COLOR) _crTextColor = pCF->_crTextColor; if(dwMask & ~CFM_EFFECTS) // Early out if only dwEffects { // is modified. Note that if(dwMask & CFM_SIZE) // CFM_EFFECTS includes CFM_COLOR { // If dwMask2 CFM2_USABLEFONT bit is set, pCF->_yHeight (from // EM_SETFONTSIZE wparam) is signed increment in points. _yHeight = dwMask2 & CFM2_USABLEFONT ? GetUsableFontHeight(_yHeight, pCF->_yHeight) : pCF->_yHeight; } if(dwMask & CFM_OFFSET) _yOffset = pCF->_yOffset; if((dwMask & CFM_CHARSET) && // Caller guarantees no check needed (dwMask2 & (CFM2_NOCHARSETCHECK | CFM2_MATCHFONT) || // Caller is itemizer. Change to ANSI_CHARSET only if current is BiDi, // dont change if current is DBCS/FE charset or symbol. (dwMask2 & CFM2_SCRIPT && (!pCF->_iCharRep && IsBiDiCharRep(_iCharRep) || pCF->_iCharRep && _iCharRep < NCHARSETS && !IsFECharRep(_iCharRep) && !IsSymbolOrOEMCharRep(_iCharRep) && !(_dwEffects & CFE_RUNISDBCS))) || // Caller is not itemizer. Allow consistent direction (!(dwMask2 & CFM2_SCRIPT) && (!(IsRTLCharRep(_iCharRep) ^ IsRTLCharRep(pCF->_iCharRep)) || IsSymbolOrOEMCharRep(pCF->_iCharRep))))) { fNewCharset = TRUE; _iCharRep = pCF->_iCharRep; } if ((dwMask2 & (CFM2_MATCHFONT | CFM2_ADJUSTFONTSIZE)) == (CFM2_MATCHFONT | CFM2_ADJUSTFONTSIZE) && _iCharRep != pCF->_iCharRep && (dwMask & CFM_SIZE)) { // Check if we need to adjust the font size _yHeight = W32->GetPreferredFontHeight( (dwMask2 & CFM2_UIFONT) != 0, pCF->_iCharRep, _iCharRep, _yHeight); } if(dwMask & CFM_FACE) { _bPitchAndFamily = pCF->_bPitchAndFamily; _iFont = pCF->_iFont; const WCHAR *pch = GetFontName((LONG)_iFont); WCHAR ch = pch[0]; if (!fNewCharset && ch == L'\0') { // API to choose default font BYTE bDefPitchAndFamily; SHORT iDefFont; int iCharRep = GetLocaleCharRep(); BYTE yDefHeight; // Get default font name and charset bool fr = W32->GetPreferredFontInfo( iCharRep, FALSE, iDefFont, (BYTE&)yDefHeight, bDefPitchAndFamily ); if (fr) { _iCharRep = iCharRep; _iFont = iDefFont; if(!(dwMask & CFM_SIZE) || _yHeight < yDefHeight * 20) // Setup default height if needed. _yHeight = yDefHeight * 20; _bPitchAndFamily = bDefPitchAndFamily; } } else if(GetCharFlags(pch, 2) & FFE && !IsFECharRep(_iCharRep)) { // make sure that we dont end up having DBCS facename with Non-FE charset QWORD qwFontSig; if (GetFontSignatureFromFace(_iFont, &qwFontSig)) { qwFontSig &= FFE ; // Only interest in FE charset if (qwFontSig) _iCharRep = GetFirstAvailCharRep(qwFontSig); } } #ifdef DEBUG _pchFaceName = GetFontName((LONG)_iFont); #endif } if(!(dwMask2 & CFM2_CHARFORMAT) && (dwMask & ~CFM_ALL)) // CHARFORMAT2 extensions { if((dwMask & (CFM_WEIGHT | CFM_BOLD)) == CFM_WEIGHT) { _wWeight = pCF->_wWeight; _dwEffects |= CFE_BOLD; // Set above-average if(_wWeight < 551) // weights to bold _dwEffects &= ~CFE_BOLD; } if(dwMask & CFM_BACKCOLOR) _crBackColor = pCF->_crBackColor; if(dwMask & CFM_LCID) { _lcid = pCF->_lcid; if(!(dwMask & CFM_CHARSET) && !IsFECharRep(_iCharRep) && PRIMARYLANGID(_lcid)) _iCharRep = CharRepFromLID(_lcid); } if(dwMask & CFM_SPACING) _sSpacing = pCF->_sSpacing; if(dwMask & CFM_KERNING) _wKerning = pCF->_wKerning; if(dwMask & CFM_STYLE) _sStyle = pCF->_sStyle; if(dwMask & CFM_UNDERLINETYPE) { _bUnderlineType = pCF->_bUnderlineType; if(!(dwMask & CFM_UNDERLINE)) // If CFE_UNDERLINE { // isn't defined, _dwEffects &= ~CFE_UNDERLINE; // set it according to if(_bUnderlineType) // bUnderlineType _dwEffects |= CFE_UNDERLINE; } else _bUnderlineColor = pCF->_bUnderlineColor; } if((dwMask & CFM_ANIMATION) && pCF->_bAnimation <= 18) _bAnimation = pCF->_bAnimation; if(dwMask & CFM_REVAUTHOR) _bRevAuthor = pCF->_bRevAuthor; } } // RichEdit currently doesn't do a good job of assigning LangIDs to text, // primarily assigning them only on initialization, when the keyboard // changes, and when LangIDs are read in from RTF. The following weak // LangID recognizer uses the facts that most languages are associated // with a single CharRep and that the keyboard provides a good hint as // to which language. The else if could be expanded to include more // CharReps than Arabic, Hebrew, and Ansi, e.g., CJK. More generally, // LangID recognition requires natural language processing beyond the // choice of the writing system, which is given in RichEdit by _iCharRep. if(CharRepFromLID(_lcid) != _iCharRep) { LONG lcid = (WORD)W32->GetPreferredKbd(_iCharRep); if(lcid) _lcid = lcid; // Use keyboard lang for _iCharRep else if(_iCharRep == ANSI_INDEX) // No ANSI_CHARSET keyboard _lcid = MAKELANGID(LANG_ENGLISH, SUBLANG_DEFAULT); else if(IsBiDiCharRep(_iCharRep)) // Use default langs for BiDi _lcid = MAKELANGID(_iCharRep == HEBREW_INDEX ? LANG_HEBREW : LANG_ARABIC, SUBLANG_DEFAULT); } if(dwMask2 & CFM2_SCRIPT) _wScript = pCF->_wScript; return NOERROR; } /* * CCharFormat::ApplyDefaultStyle(Style) * * @mfunc * Set default style properties in this CCharFormat */ void CCharFormat::ApplyDefaultStyle ( LONG Style) //@parm Style to use { Assert(IsKnownStyle(Style)); if(IsHeadingStyle(Style)) { LONG i = -Style + STYLE_HEADING_1; _dwEffects = (_dwEffects & 0xFFFFFF00) | g_Style[i].bEffects; _wWeight = (_dwEffects & CFE_BOLD) ? FW_BOLD : FW_NORMAL; if(g_Style[i].bHeight) _yHeight = g_Style[i].bHeight * 20; QWORD qwFontSig; LONG iFont = _iFont; // Save _iFont in case Arial doesn't _iFont = IFONT_ARIAL; // support _bCharSet GetFontSignatureFromFace(_iFont, &qwFontSig); if(FontSigFromCharRep(_iCharRep) & qwFontSig) _bPitchAndFamily = FF_SWISS;// Arial supports _iCharRep else _iFont = iFont; // Restore iFont #ifdef DEBUG _pchFaceName = GetFontName((LONG)_iFont); #endif } } BOOL CCharFormat::CanKernWith(const CCharFormat *pCF) const { const DWORD dwMask = CFE_ITALIC; return this == pCF || (!((_dwEffects ^ pCF->_dwEffects) & dwMask) && _iFont == pCF->_iFont && _yHeight == pCF->_yHeight && _wWeight == pCF->_wWeight); } /* * CCharFormat::Compare(pCF) * * @mfunc * Compare this CCharFormat to *

* * @rdesc * TRUE if they are the same not including _cRefs */ BOOL CCharFormat::Compare ( const CCharFormat *pCF) const //@parm CCharFormat to compare this { // CCharFormat to TRACEBEGIN(TRCSUBSYSBACK, TRCSCOPEINTERN, "CCharFormat::Compare"); return !CompareMemory(this, pCF, sizeof(CCharFormat)); } /* * CCharFormat::Delta(pCF, fCHARFORMAT) * * @mfunc * Calculate dwMask for differences between this CCharformat and * *

* * @rdesc * return dwMask of differences (1 bit means a difference) */ DWORD CCharFormat::Delta ( CCharFormat *pCF, //@parm CCharFormat to compare this one to BOOL fCHARFORMAT) const //@parm Only compare CHARFORMAT members { TRACEBEGIN(TRCSUBSYSBACK, TRCSCOPEINTERN, "CCharFormat::Delta"); // Collect bits for properties LONG dw = _dwEffects ^ pCF->_dwEffects; // that change. Note: auto // color is handled since if(_yHeight != pCF->_yHeight) // CFM_COLOR = CFE_AUTOCOLOR dw |= CFM_SIZE; if(_yOffset != pCF->_yOffset) dw |= CFM_OFFSET; if(_crTextColor != pCF->_crTextColor) dw |= CFM_COLOR; if(_iCharRep != pCF->_iCharRep) dw |= CFM_CHARSET; if(_iFont != pCF->_iFont) dw |= CFM_FACE; if(fCHARFORMAT) return dw; // Done with CHARFORMAT stuff if(_crBackColor != pCF->_crBackColor) // CHARFORMAT2 stuff dw |= CFM_BACKCOLOR; if(_wKerning != pCF->_wKerning) dw |= CFM_KERNING; if(_lcid != pCF->_lcid) dw |= CFM_LCID; if(_wWeight != pCF->_wWeight) dw |= CFM_WEIGHT; if(_sSpacing != pCF->_sSpacing) dw |= CFM_SPACING; if(_sStyle != pCF->_sStyle) dw |= CFM_STYLE; if(_bUnderlineType != pCF->_bUnderlineType) dw |= CFM_UNDERLINETYPE; if(_bAnimation != pCF->_bAnimation) dw |= CFM_ANIMATION; if(_bRevAuthor != pCF->_bRevAuthor) dw |= CFM_REVAUTHOR; return dw; } /* * CCharFormat::fSetStyle(dwMask) * * @mfunc * return TRUE iff pCF specifies that the style should be set. See * code for list of conditions for this to be true * * @rdesc * TRUE iff pCF specifies that the style _sStyle should be set */ BOOL CCharFormat::fSetStyle(DWORD dwMask, DWORD dwMask2) const { return dwMask != CFM_ALL2 && dwMask & CFM_STYLE && !(dwMask2 & CFM2_CHARFORMAT) && IsKnownStyle(_sStyle); } /* * CCharFormat::Get(pCF, CodePage) * * @mfunc * Copy this CCharFormat to the CHARFORMAT or CHARFORMAT2 *

*/ void CCharFormat::Get ( CHARFORMAT2 *pCF2, //@parm CHARFORMAT to copy this CCharFormat to UINT CodePage) const { TRACEBEGIN(TRCSUBSYSBACK, TRCSCOPEINTERN, "CCharFormat::Get"); pCF2->dwMask = CFM_ALL; // Use CHARFORMAT pCF2->dwEffects = _dwEffects; pCF2->yHeight = _yHeight; pCF2->yOffset = _yOffset; pCF2->crTextColor = _crTextColor; pCF2->bCharSet = CharSetFromCharRep(_iCharRep); pCF2->bPitchAndFamily = _bPitchAndFamily; UINT cb = pCF2->cbSize; const WCHAR *pch = GetFontName((LONG)_iFont); AssertSz((CodePage != 1200) ^ IsValidCharFormatW(pCF2), "CCharFormat::Get: wrong codepage for CHARFORMAT"); if(CodePage != 1200) { if(_dwEffects & CFE_FACENAMEISDBCS) { // The face name is actually DBCS stuffed into the unicode // buffer, so simply un-stuff this DBCS into the ANSI string char *pachDst = (char *)pCF2->szFaceName; while(*pch) *pachDst++ = *pch++; *pachDst = 0; } else { MbcsFromUnicode((char *)pCF2->szFaceName, LF_FACESIZE, pch, -1, CodePage, UN_NOOBJECTS); } } else wcscpy(pCF2->szFaceName, pch); if (cb == sizeof(CHARFORMATW) || cb == sizeof(CHARFORMATA)) // We're done return; char *pvoid = (char *)&pCF2->wWeight; if(pCF2->cbSize == sizeof(CHARFORMAT2A)) pvoid -= sizeof(CHARFORMAT2W) - sizeof(CHARFORMAT2A); else Assert(pCF2->cbSize == sizeof(CHARFORMAT2));// Better be a CHARFORMAT2 pCF2->dwMask = CFM_ALL2; CopyMemory(pvoid, &_wWeight, 3*sizeof(DWORD)); CopyMemory(pvoid + 4*sizeof(DWORD), &_sStyle, 2*sizeof(DWORD)); *(DWORD *)(pvoid + 3*sizeof(DWORD)) = 0; } /* * CCharFormat::InitDefault(hfont) * * @mfunc * Initialize this CCharFormat with information coming from the font *

* * @rdesc * HRESULT = (if success) ? NOERROR : E_FAIL */ HRESULT CCharFormat::InitDefault ( HFONT hfont) //@parm Handle to font info to use { TRACEBEGIN(TRCSUBSYSBACK, TRCSCOPEINTERN, "CCharFormat::InitDefault"); LOGFONT lf; BOOL fUseStockFont = hfont == NULL; ZeroMemory(this, sizeof(CCharFormat)); // If hfont isn't defined, get LOGFONT for default font if(!hfont) hfont = W32->GetSystemFont(); // Get LOGFONT for passed hfont if(!W32->GetObject(hfont, sizeof(LOGFONT), &lf)) return E_FAIL; _yHeight = (lf.lfHeight * LY_PER_INCH) / W32->GetYPerInchScreenDC(); if(_yHeight <= 0) _yHeight = -_yHeight; else if (fUseStockFont) // This is Cell Height for System Font case _yHeight -= (W32->GetSysFontLeading() * LY_PER_INCH) / W32->GetYPerInchScreenDC(); else { // This is Cell Height, need to get the character height by subtracting // the tm.tmInternalLeading. CLock lock; HDC hdc = W32->GetScreenDC(); HFONT hOldFont = SelectFont(hdc, hfont); TEXTMETRIC tm; if(hOldFont) { if(GetTextMetrics(hdc, &tm)) _yHeight -= (tm.tmInternalLeading * LY_PER_INCH) / W32->GetYPerInchScreenDC(); SelectFont(hdc, hOldFont); } } _dwEffects = (CFM_EFFECTS | CFE_AUTOBACKCOLOR) & ~(CFE_PROTECTED | CFE_LINK); if(lf.lfWeight < FW_BOLD) _dwEffects &= ~CFE_BOLD; if(!lf.lfItalic) _dwEffects &= ~CFE_ITALIC; if(!lf.lfUnderline) _dwEffects &= ~CFE_UNDERLINE; if(!lf.lfStrikeOut) _dwEffects &= ~CFE_STRIKEOUT; _wWeight = (WORD)lf.lfWeight; _lcid = GetSystemDefaultLCID(); _iCharRep = CharRepFromCharSet(lf.lfCharSet); _bPitchAndFamily= lf.lfPitchAndFamily; _iFont = GetFontNameIndex(lf.lfFaceName); _bUnderlineType = CFU_UNDERLINE; // Default solid underlines // Are gated by CFE_UNDERLINE // Qualify the charformat produced by incoming hfont before exit. // We did this to make sure that the charformat we derived from hfont is usable // since caller can send us bad font like given facename can't handle given charset. if (!fUseStockFont) { QWORD qwFontSig; if (GetFontSignatureFromFace(_iFont, &qwFontSig) && !(FontSigFromCharRep(_iCharRep) & qwFontSig)) _iCharRep = GetFirstAvailCharRep(qwFontSig); _bQuality = lf.lfQuality; } // Initialize to no temp. display attr. _sTmpDisplayAttrIdx = -1; #ifdef DEBUG _pchFaceName = GetFontName((LONG)_iFont); #endif return NOERROR; } /* * CCharFormat::Set(pCF, CodePage) * * @mfunc * Copy the CHARFORMAT or CHARFORMAT2 *

to this CCharFormat */ void CCharFormat::Set ( const CHARFORMAT2 *pCF2, //@parm CHARFORMAT to copy to this CCharFormat UINT CodePage) { TRACEBEGIN(TRCSUBSYSBACK, TRCSCOPEINTERN, "CCharFormat::Set"); _dwEffects = pCF2->dwEffects; _iCharRep = CharRepFromCharSet(pCF2->bCharSet); _bPitchAndFamily = pCF2->bPitchAndFamily; if(pCF2->dwMask & CFM_FACE) { AssertSz((CodePage != 1200) ^ IsValidCharFormatW(pCF2), "CCharFormat::Set: wrong codepage for CHARFORMAT"); if(CodePage != 1200) { WCHAR sz[LF_FACESIZE + 1]; UnicodeFromMbcs(sz, LF_FACESIZE, (char *)pCF2->szFaceName, LF_FACESIZE, CodePage); _iFont = GetFontNameIndex(sz); } else _iFont = GetFontNameIndex(pCF2->szFaceName); } _yHeight = Get16BitTwips(pCF2->yHeight); _yOffset = Get16BitTwips(pCF2->yOffset); _crTextColor = pCF2->crTextColor; UINT cb = pCF2->cbSize; if(cb == sizeof(CHARFORMATW) || cb == sizeof(CHARFORMATA)) { _dwEffects |= CFE_AUTOBACKCOLOR; _bUnderlineType = CFU_UNDERLINE; ZeroMemory((LPBYTE)&_wWeight, sizeof(CCharFormat) - offsetof(CCharFormat, _wWeight)); #ifdef DEBUG _pchFaceName = GetFontName((LONG)_iFont); #endif return; } char *pvoid = (char *)&pCF2->wWeight; if(pCF2->cbSize == sizeof(CHARFORMAT2A)) pvoid -= sizeof(CHARFORMAT2W) - sizeof(CHARFORMAT2A); else Assert(pCF2->cbSize == sizeof(CHARFORMAT2));// Better be a CHARFORMAT2 CopyMemory(&_wWeight, pvoid, 3*sizeof(DWORD)); CopyMemory(&_sStyle, pvoid + 4*sizeof(DWORD), 2*sizeof(DWORD)); #ifdef DEBUG _pchFaceName = GetFontName((LONG)_iFont); #endif } //------------------------- CParaFormat Class ----------------------------------- /* * CParaFormat::AddTab(tbPos, tbAln, tbLdr, prgxTabs) * * @mfunc * Add tabstop at position

, alignment type

, and * leader style

* * @rdesc * (success) ? NOERROR : S_FALSE * * @devnote * Tab struct that overlays LONG in internal _rgxTabs is * * DWORD tabPos : 24; * DWORD tabType : 4; * DWORD tabLeader : 4; */ HRESULT CParaFormat::AddTab ( LONG tbPos, //@parm New tab position LONG tbAln, //@parm New tab alignment type LONG tbLdr, //@parm New tab leader style LONG * prgxTabs) //@parm Where the tabs are { TRACEBEGIN(TRCSUBSYSBACK, TRCSCOPEINTERN, "CParaFormat::AddTab"); if (IsTableRowDelimiter() || (DWORD)tbAln > tomAlignBar || // Validate arguments (DWORD)tbLdr > tomEquals || // Comparing DWORDs causes (DWORD)tbPos > 0xffffff || !tbPos) // negative values to be { // treated as invalid return E_INVALIDARG; } LONG iTab; LONG tbValue = tbPos + (tbAln << 24) + (tbLdr << 28); for(iTab = 0; iTab < _bTabCount && // Determine where to tbPos > GetTabPos(prgxTabs[iTab]); // insert new tabstop iTab++) ; if(iTab >= MAX_TAB_STOPS) return S_FALSE; LONG tbPosCurrent = GetTabPos(prgxTabs[iTab]); if(iTab == _bTabCount || tbPosCurrent != tbPos) { if(_bTabCount >= MAX_TAB_STOPS) return S_FALSE; MoveMemory(&prgxTabs[iTab + 1], // Shift array down &prgxTabs[iTab], // (unless iTab = Count) (_bTabCount - iTab)*sizeof(LONG)); _bTabCount++; // Increment tab count } prgxTabs[iTab] = tbValue; return NOERROR; } /* * CParaFormat::Apply(pPF, dwMask, dwMask2) * * @mfunc * Apply *

to this CParaFormat as specified by nonzero bits in *

->dwMask * * @rdesc * HRESULT = E_INVALIDARG or NOERROR */ HRESULT CParaFormat::Apply ( const CParaFormat *pPF, //@parm CParaFormat to apply to this PF DWORD dwMask, //@parm mask to use DWORD dwMask2) //@parm Mask for additional internal parms { TRACEBEGIN(TRCSUBSYSBACK, TRCSCOPEINTERN, "CParaFormat::Apply"); const DWORD dwMaskApply = dwMask; BOOL fPF = dwMask2 & PFM2_PARAFORMAT; WORD wEffectMask; if(dwMaskApply & PFM_NUMBERING) _wNumbering = pPF->_wNumbering; if(dwMaskApply & PFM_OFFSET) { if (!IsValidTwip(pPF->_dxOffset)) return E_INVALIDARG; _dxOffset = pPF->_dxOffset; } if(dwMaskApply & PFM_STARTINDENT) { if (!IsValidTwip(pPF->_dxStartIndent)) return E_INVALIDARG; _dxStartIndent = pPF->_dxStartIndent; } else if(dwMaskApply & PFM_OFFSETINDENT) { if (!IsValidTwip(pPF->_dxStartIndent)) return E_INVALIDARG; // bug fix #5761 LONG dx = max(0, _dxStartIndent + pPF->_dxStartIndent); // Disallow shifts that move start of first or subsequent lines left of left margin. // Normally we just make indent zero in paraformat check below, // but in the case of bullet we want some space left. if(!_wNumbering || dx + _dxOffset >= 0) _dxStartIndent = dx; } if(dwMaskApply & PFM_RIGHTINDENT) { if (!IsValidTwip(pPF->_dxRightIndent)) return E_INVALIDARG; _dxRightIndent = pPF->_dxRightIndent; } if(dwMaskApply & PFM_ALIGNMENT) { if(!fPF && !IN_RANGE(PFA_LEFT, pPF->_bAlignment, PFA_SNAP_GRID)) { TRACEERRORSZ("CParaFormat::Apply: invalid Alignment ignored"); return E_INVALIDARG; } if(pPF->_bAlignment <= PFA_SNAP_GRID) _bAlignment = pPF->_bAlignment; } // Reset effect bits to be modified and OR in supplied values wEffectMask = (WORD)((dwMaskApply >> 16) | (dwMask2 & PFM2_TABLEROWSHIFTED)); _wEffects &= ~wEffectMask; _wEffects |= pPF->_wEffects & wEffectMask; if(dwMaskApply & PFM_TABSTOPS) { LONG cmax = IsTableRowDelimiter() ? MAX_TABLE_CELLS : MAX_TAB_STOPS; _bTabCount = (BYTE)min(pPF->_bTabCount, cmax); _bTabCount = (BYTE)max(_bTabCount, 0); _iTabs = pPF->_iTabs; AssertSz(!_bTabCount || _iTabs >= 0, "CParaFormat::Apply: illegal _iTabs value"); } if ((dwMaskApply & PFM_RTLPARA) && !(dwMaskApply & PFM_ALIGNMENT) && _bAlignment != PFA_CENTER) { _bAlignment = IsRtlPara() ? PFA_RIGHT : PFA_LEFT; } // PARAFORMAT check if(fPF) { if(dwMaskApply & (PFM_STARTINDENT | PFM_OFFSET)) { if(_dxStartIndent < 0) // Don't let indent go _dxStartIndent = 0; // negative if(_dxStartIndent + _dxOffset < 0) // Don't let indent + _dxOffset = -_dxStartIndent; // offset go negative } return NOERROR; // Nothing more for } // PARAFORMAT // PARAFORMAT2 extensions if(dwMaskApply & PFM_SPACEBEFORE) { _dySpaceBefore = 0; if (pPF->_dySpaceBefore > 0) _dySpaceBefore = pPF->_dySpaceBefore; } if(dwMaskApply & PFM_SPACEAFTER) { _dySpaceAfter = 0; if (pPF->_dySpaceAfter > 0) _dySpaceAfter = pPF->_dySpaceAfter; } if(dwMaskApply & PFM_LINESPACING) { _dyLineSpacing = pPF->_dyLineSpacing; _bLineSpacingRule = pPF->_bLineSpacingRule; } if(dwMaskApply & PFM_OUTLINELEVEL) _bOutlineLevel = pPF->_bOutlineLevel; if(dwMaskApply & PFM_STYLE) HandleStyle(pPF->_sStyle); Assert((_bOutlineLevel & 1) ^ IsHeadingStyle(_sStyle)); if(dwMaskApply & PFM_SHADING) { _wShadingWeight = pPF->_wShadingWeight; _wShadingStyle = pPF->_wShadingStyle; } if(dwMaskApply & PFM_NUMBERINGSTART) _wNumberingStart = pPF->_wNumberingStart; if(dwMaskApply & PFM_NUMBERINGSTYLE) _wNumberingStyle = pPF->_wNumberingStyle; if(dwMaskApply & PFM_NUMBERINGTAB) _wNumberingTab = pPF->_wNumberingTab; if(dwMaskApply & PFM_BORDER) { _dwBorderColor = pPF->_dwBorderColor; _wBorders = pPF->_wBorders; _wBorderSpace = pPF->_wBorderSpace; _wBorderWidth = pPF->_wBorderWidth; } if(dwMaskApply & PFM_TABLE) _bTableLevel = pPF->_bTableLevel; #ifdef DEBUG ValidateTabs(); AssertSz((!(_wEffects & PFE_TABLE) || _bTableLevel) && _bTableLevel >= 0, "CParaFormat::Apply: invalid table level"); #endif // DEBUG return NOERROR; } /* * CParaFormat::ApplyDefaultStyle(Style) * * @mfunc * Copy default properties for Style */ void CParaFormat::ApplyDefaultStyle ( LONG Style) //@parm Style to apply { Assert(IsKnownStyle(Style)); if(IsHeadingStyle(Style)) // Set Style's dySpaceBefore, { // dySpaceAfter (in twips) _dySpaceBefore = 12*20; // (same for all headings) _dySpaceAfter = 3*20; _wNumbering = 0; // No numbering } } /* * CParaFormat::DeleteTab(tbPos) * * @mfunc * Delete tabstop at position

* * @rdesc * (success) ? NOERROR : S_FALSE */ HRESULT CParaFormat::DeleteTab ( LONG tbPos, //@parm Tab position to delete LONG * prgxTabs) //@parm Tab array to use { TRACEBEGIN(TRCSUBSYSBACK, TRCSCOPEINTERN, "CParaFormat::DeleteTab"); if(tbPos <= 0 || IsTableRowDelimiter()) return E_INVALIDARG; LONG Count = _bTabCount; for(LONG iTab = 0; iTab < Count; iTab++) // Find tabstop for position { if(GetTabPos(prgxTabs[iTab]) == tbPos) { MoveMemory(&prgxTabs[iTab], // Shift array down &prgxTabs[iTab + 1], // (unless iTab is last tab) (Count - iTab - 1)*sizeof(LONG)); _bTabCount--; // Decrement tab count and return NOERROR; // signal no error } } return S_FALSE; } /* * CParaFormat::Delta(pPF, fPARAFORMAT) * * @mfunc * return mask of differences between this CParaFormat and *

. * 1-bits indicate corresponding parameters differ; 0 indicates they * are the same * * @rdesc * mask of differences between this CParaFormat and *

*/ DWORD CParaFormat::Delta ( CParaFormat *pPF, //@parm CParaFormat to compare this BOOL fPARAFORMAT) const // CParaFormat to { TRACEBEGIN(TRCSUBSYSBACK, TRCSCOPEINTERN, "CParaFormat::Delta"); LONG dwT = 0; // No differences yet if(_wNumbering != pPF->_wNumbering) dwT |= PFM_NUMBERING; // _wNumbering values differ if(_dxStartIndent != pPF->_dxStartIndent) dwT |= PFM_STARTINDENT; // ... if(_dxRightIndent != pPF->_dxRightIndent) dwT |= PFM_RIGHTINDENT; if(_dxOffset != pPF->_dxOffset) dwT |= PFM_OFFSET; if(_bAlignment != pPF->_bAlignment) dwT |= PFM_ALIGNMENT; AssertSz(pPF->_bTabCount >= 0 && pPF->_bTabCount <= (pPF->IsTableRowDelimiter() ? MAX_TABLE_CELLS : MAX_TAB_STOPS), "RTR::GetParaFormat(): illegal tab count"); if (_bTabCount != pPF->_bTabCount) dwT |= PFM_TABSTOPS; else if (_bTabCount > 0) { const LONG *pTabs1 = GetTabs(); const LONG *pTabs2 = pPF->GetTabs(); LONG cb = _bTabCount * sizeof(LONG); if(IsTableRowDelimiter()) cb *= CELL_EXTRA + 1; if (pTabs1 != pTabs2 && (pTabs1 == 0 || pTabs2 == 0 || CompareMemory(pTabs1, pTabs2, cb))) dwT |= PFM_TABSTOPS; } dwT |= (_wEffects ^ pPF->_wEffects) << 16; if(!fPARAFORMAT) { if(_dySpaceBefore != pPF->_dySpaceBefore) dwT |= PFM_SPACEBEFORE; if(_dySpaceAfter != pPF->_dySpaceAfter) dwT |= PFM_SPACEAFTER; if (_dyLineSpacing != pPF->_dyLineSpacing || _bLineSpacingRule!= pPF->_bLineSpacingRule) { dwT |= PFM_LINESPACING; } if(_sStyle != pPF->_sStyle) dwT |= PFM_STYLE; if (_wShadingWeight != pPF->_wShadingWeight || _wShadingStyle != pPF->_wShadingStyle) { dwT |= PFM_SHADING; } if(_wNumberingStart != pPF->_wNumberingStart) dwT |= PFM_NUMBERINGSTART; if(_wNumberingStyle != pPF->_wNumberingStyle) dwT |= PFM_NUMBERINGSTYLE; if(_wNumberingTab != pPF->_wNumberingTab) dwT |= PFM_NUMBERINGTAB; if (_wBorders != pPF->_wBorders || _wBorderWidth != pPF->_wBorderWidth || _wBorderSpace != pPF->_wBorderSpace || _dwBorderColor != pPF->_dwBorderColor) { dwT |= PFM_BORDER; } if(_bTableLevel != pPF->_bTableLevel) dwT |= PFM_TABLEROWDELIMITER; } return dwT; } #define PFM_IGNORE (PFM_OUTLINELEVEL | PFM_COLLAPSED | PFM_BOX | PFM_TABLE | PFM_TABLEROWDELIMITER) /* * CParaFormat::fSetStyle(dwMask, dwMask2) * * @mfunc * Return TRUE iff this PF specifies that the style should be set. * See code for list of conditions for this to be true * * @rdesc * TRUE iff pCF specifies that the style _sStyle should be set */ BOOL CParaFormat::fSetStyle( DWORD dwMask, //@parm mask to use DWORD dwMask2) const //@parm Mask for additional internal parms { return (dwMask & ~PFM_IGNORE) != (PFM_ALL2 & ~(PFM_TABLE | PFM_TABLEROWDELIMITER)) && !(dwMask2 & (PFM2_PARAFORMAT | PFM2_ALLOWTRDCHANGE)) && dwMask & PFM_STYLE && IsKnownStyle(_sStyle); } /* * CParaFormat::Get(pPF2) * * @mfunc * Copy this CParaFormat to *

*/ void CParaFormat::Get ( PARAFORMAT2 *pPF2) const //@parm PARAFORMAT2 to copy this CParaFormat to { TRACEBEGIN(TRCSUBSYSBACK, TRCSCOPEINTERN, "CParaFormat::Get"); LONG cb = pPF2->cbSize; pPF2->dwMask = PFM_ALL2; // Default PARAFORMAT2 if(cb != sizeof(PARAFORMAT2)) // It isn't { pPF2->dwMask = PFM_ALL; // Make it PARAFORMAT Assert(cb == sizeof(PARAFORMAT)); // It better be a PARAFORMAT } CopyMemory(&pPF2->wNumbering, &_wNumbering, (char *)&_bAlignment - (char *)&_wNumbering); pPF2->wAlignment = _bAlignment; pPF2->cTabCount = _bTabCount; LONG cb1 = _bTabCount*sizeof(LONG); if(_bTabCount) { // Need API for handling table cells. For now, just don't overwrite // pPF2->rgxTabs[]. cb1 = min(cb1, (LONG)MAX_TAB_STOPS*sizeof(LONG)); AssertSz(_iTabs >= 0, "CParaFormat::Get: illegal _iTabs value"); CopyMemory(pPF2->rgxTabs, GetTabsCache()->Deref(_iTabs), cb1); } ZeroMemory(pPF2->rgxTabs + _bTabCount, MAX_TAB_STOPS*sizeof(LONG) - cb1); CopyMemory(&pPF2->dySpaceBefore, &_dySpaceBefore, cb - offsetof(PARAFORMAT2, dySpaceBefore)); } /* * CParaFormat::GetRTLRowLength() * * @mfunc * Get the length of the current row if this CParaFormat is a table * row delimiter with a PFE_RTLPara effect. * * @rdesc * Length of current row if PFE_TABLEROWDELIMITER and PFE_RTLPARA are * on; else 0 */ LONG CParaFormat::GetRTLRowLength() const { if ((_wEffects & (PFE_TABLEROWDELIMITER | PFE_RTLPARA)) != (PFE_TABLEROWDELIMITER | PFE_RTLPARA)) { return 0; // Not an RTL row } const CELLPARMS * prgCellParms = GetCellParms(); for(LONG i = 0, dulRTLRow = 0; i < _bTabCount; i++) dulRTLRow += GetCellWidth(prgCellParms[i].uCell); return dulRTLRow; } /* * CParaFormat::GetTab (iTab, ptbPos, ptbAln, ptbLdr, prgxTabs) * * @mfunc * Get tab parameters for the

th tab, that is, set *

, * *

, and *

equal to the

th tab's * displacement, alignment type, and leader style, respectively. The * displacement is given in twips. * * @rdesc * HRESULT = (no

tab) ? E_INVALIDARG : NOERROR */ HRESULT CParaFormat::GetTab ( long iTab, //@parm Index of tab to retrieve info for long * ptbPos, //@parm Out parm to receive tab displacement long * ptbAln, //@parm Out parm to receive tab alignment type long * ptbLdr, //@parm Out parm to receive tab leader style const LONG *prgxTabs) const //@parm Tab array { TRACEBEGIN(TRCSUBSYSBACK, TRCSCOPEEXTERN, "CParaFormat::GetTab"); AssertSz(ptbPos && ptbAln && ptbLdr, "CParaFormat::GetTab: illegal arguments"); if(IsTableRowDelimiter()) return E_INVALIDARG; if(iTab < 0) // Get tab previous to, at, { // or subsequent to the if(iTab < tomTabBack) // position *ptbPos return E_INVALIDARG; LONG i; LONG tbPos = *ptbPos; LONG tbPosi; *ptbPos = 0; // Default tab not found for(i = 0; i < _bTabCount && // Find *ptbPos tbPos > GetTabPos(prgxTabs[i]); i++) ; tbPosi = GetTabPos(prgxTabs[i]); // tbPos <= tbPosi if(iTab == tomTabBack) // Get tab info for tab i--; // previous to tbPos else if(iTab == tomTabNext) // Get tab info for tab { // following tbPos if(tbPos == tbPosi) i++; } else if(tbPos != tbPosi) // tomTabHere return S_FALSE; iTab = i; } if((DWORD)iTab >= (DWORD)_bTabCount) // DWORD cast also return E_INVALIDARG; // catches values < 0 iTab = prgxTabs[iTab]; *ptbPos = GetTabPos(iTab); *ptbAln = GetTabAlign(iTab); *ptbLdr = GetTabLdr(iTab); return NOERROR; } /* * CParaFormat::GetTabs () * * @mfunc * Get ptr to tab array. Use GetTabPos(), GetTabAlign(), and * GetTabLdr() to access the tab position, alignment, and leader * type, respectively. * * @rdesc * Ptr to tab array. */ const LONG * CParaFormat::GetTabs () const { return GetTabsCache()->Deref(_iTabs); } /* * CParaFormat::HandleStyle(Style) * * @func * If Style is a promote/demote command, i.e., if abs((char)Style) * <= # heading styles - 1, add (char)Style to sStyle (if heading) * and to bOutlineLevel (subject to defined max and min values); * else sStyle = Style. * * @rdesc * return TRUE iff sStyle or bOutlineLevel changed * * @devnote * Heading styles are -2 (heading 1) through -10 (heading 9), which * with TOM and WOM. Heading outline levels are 0, 2,..., 16, * corresponding to headings 1 through 9 (NHSTYLES), respectively, * while text that follows has outline levels 1, 3,..., 17. This value * is used for indentation. Collapsed text has the PFE_COLLAPSED bit set. */ BOOL CParaFormat::HandleStyle( LONG Style) //@parm Style, promote/demote code, or collapse-level code { if(IsStyleCommand(Style)) // Set collapse level { WORD wEffectsSave = _wEffects; Style = (char)Style; // Sign extend low byte if(IN_RANGE(1, Style, NHSTYLES)) { _wEffects &= ~PFE_COLLAPSED; if((_bOutlineLevel & 1) || _bOutlineLevel > 2*(Style - 1)) _wEffects |= PFE_COLLAPSED; // Collapse nonheadings and } // higher numbered headings else if(Style == -1) _wEffects &= ~PFE_COLLAPSED; // Expand all return _wEffects != wEffectsSave; // Return whether something } // changed // Ordinary Style specification BYTE bLevel = _bOutlineLevel; _bOutlineLevel |= 1; // Default not a heading if(IsHeadingStyle(Style)) // Headings have levels { // 0, 2,..., 16, while the _bOutlineLevel = -2*(Style // text that follows has - STYLE_HEADING_1); // 1, 3,..., 17. } if(_sStyle == Style && bLevel == _bOutlineLevel) return FALSE; // No change _sStyle = (SHORT)Style; return TRUE; } /* * CParaFormat::InitDefault(wDefEffects) * * @mfunc * Initialize this CParaFormat with default paragraph formatting * * @rdesc * HRESULT = (if success) ? NOERROR : E_FAIL */ HRESULT CParaFormat::InitDefault( WORD wDefEffects) { TRACEBEGIN(TRCSUBSYSBACK, TRCSCOPEINTERN, "CParaFormat::InitDefault"); ZeroMemory(this, sizeof(CParaFormat)); _bAlignment = PFA_LEFT; _sStyle = STYLE_NORMAL; // Default style _wEffects = wDefEffects; _bOutlineLevel = 1; // Default highest text outline _iTabs = -1; // level #if lDefaultTab <= 0 #error "default tab (lDefaultTab) must be > 0" #endif return NOERROR; } /* * CParaFormat::NumToStr(pch, n) * * @mfunc * Convert the list number n to a string taking into consideration * CParaFormat::wNumbering, wNumberingStart, and wNumberingStyle * * @rdesc * cch of string converted */ LONG CParaFormat::NumToStr( TCHAR * pch, //@parm Target string LONG n, //@parm Number + 1 to convert DWORD grf) const //@parm Collection of flags { if(IsNumberSuppressed()) { *pch = 0; return 0; // Number/bullet suppressed } if(!n) // Bullet of some kind { // CParaFormat::wNumbering *pch++ = (_wNumbering > ' ') // values > ' ' are Unicode ? _wNumbering : 0x00B7; // bullets. Else use bullet return 1; // in symbol font } // Numbering of some kind // i ii iii iv v vi vii viii ix const BYTE RomanCode[] = {1, 5, 0x15, 9, 2, 6, 0x16, 0x56, 0xd}; const char RomanLetter[] = "ivxlcdmno"; BOOL fRtlPara = IsRtlPara() && !(grf & fRtfWrite); LONG RomanOffset = 0; LONG cch = 0; // No chars yet WCHAR ch = fRtlPara && (grf & fIndicDigits) ? 0x0660 : '0'; // Default char code offset LONG d = 1; // Divisor LONG r = 10; // Default radix LONG quot, rem; // ldiv result LONG Style = (_wNumberingStyle << 8) & 0xF0000; n--; // Convert to number offset if(Style == tomListParentheses || // Numbering like: (x) fRtlPara && Style == 0) // or 1) in bidi text. { cch = 1; // Store leading lparen *pch++ = '('; } else if (Style == tomListPeriod && fRtlPara) { cch = 1; *pch++ = '.'; Style = tomListPlain; } if(_wNumbering == tomListNumberAsSequence) ch = _wNumberingStart; // Needs generalizations, e.g., // appropriate radix else { n += _wNumberingStart; if(IN_RANGE(tomListNumberAsLCLetter, _wNumbering, tomListNumberAsUCLetter)) { ch = (_wNumbering == tomListNumberAsLCLetter) ? 'a' : 'A'; if(_wNumberingStart >= 1) n--; r = 26; // LC or UC alphabetic number } // Radix 26 } while(d < n) { d *= r; // d = smallest power of r > n RomanOffset += 2; } if(n && d > n) { d /= r; RomanOffset -= 2; } while(d) { quot = n / d; rem = n % d; if(IN_RANGE(tomListNumberAsLCRoman, _wNumbering, tomListNumberAsUCRoman)) { if(quot) { n = RomanCode[quot - 1]; while(n) { ch = RomanLetter[(n & 3) + RomanOffset - 1]; if(_wNumbering == tomListNumberAsUCRoman) ch &= 0x5F; *pch++ = ch; n >>= 2; cch++; } } RomanOffset -= 2; } else { n = quot + ch; if(r == 26 && d > 1) // If alphabetic higher-order n--; // digit, base it on 'a' or 'A' *pch++ = (WORD)n; // Store digit cch++; } n = rem; // Setup remainder d /= r; } if (Style != tomListPlain && // Trailing text (!fRtlPara || Style)) { // We only do rparen or period *pch++ = (Style == tomListPeriod) ? '.' : ')'; cch++; } *pch = 0; // Null terminate for RTF writer return cch; } /* * CParaFormat::Set(pPF2) * * @mfunc * Copy PARAFORMAT or PARAFORMAT2 *

to this CParaFormat */ void CParaFormat::Set ( const PARAFORMAT2 *pPF2) //@parm PARAFORMAT to copy to this CParaFormat { TRACEBEGIN(TRCSUBSYSBACK, TRCSCOPEINTERN, "CParaFormat::Set"); CopyMemory(&_wNumbering, &pPF2->wNumbering, (char *)&_bAlignment - (char *)&_wNumbering); _bAlignment = (BYTE)pPF2->wAlignment; _iTabs = -1; _bTabCount = 0; _wEffects &= ~PFE_TABLEROWDELIMITER; if((pPF2->dwMask & PFM_TABSTOPS) && pPF2->cTabCount) { LONG cTab = min(pPF2->cTabCount, MAX_TAB_STOPS); if (pPF2->dwMask & PFM_TABLEROWDELIMITER && pPF2->wEffects & PFE_TABLEROWDELIMITER) { // TODO: create an API for setting cell info } else { _bTabCount = cTab; _iTabs = GetTabsCache()->Cache(pPF2->rgxTabs, cTab); } } if(pPF2->dwMask & ~PFM_ALL) { Assert(pPF2->cbSize == sizeof(PARAFORMAT2)); CopyMemory(&_dySpaceBefore, &pPF2->dySpaceBefore, sizeof(PARAFORMAT2) - offsetof(PARAFORMAT2, dySpaceBefore)); } #ifdef DEBUG ValidateTabs(); #endif // DEBUG } /* * CParaFormat::UpdateNumber(n, pPF) * * @mfunc * Return new value of number for paragraph described by this PF * following a paragraph described by pPF * * @rdesc * New number for paragraph described by this PF */ LONG CParaFormat::UpdateNumber ( LONG n, //@parm Current value of number const CParaFormat *pPF) const //@parm Previous CParaFormat { TRACEBEGIN(TRCSUBSYSBACK, TRCSCOPEINTERN, "CParaFormat::UpdateNumber"); if(!IsListNumbered()) return 0; // No numbering if(IsNumberSuppressed()) return n; // Number is suppressed, so no change if (!pPF || (pPF->_wEffects & PFE_TABLEROWDELIMITER) || _wNumbering != pPF->_wNumbering || (_wNumberingStyle & PFNS_NEWNUMBER) || (_wNumberingStyle != pPF->_wNumberingStyle && !pPF->IsNumberSuppressed()) || _wNumberingStart != pPF->_wNumberingStart) { // Numbering type or style return 1; // changed, so start over } return n + 1; // Same kind of numbering, } #ifdef DEBUG /* * CParaFormat::ValidateTabs() * * @mfunc * Makes sure that a set of tabs would make sense for a non-table * paragraph. Is called in places where we have set the tabs. * * @rdesc * None. */ void CParaFormat::ValidateTabs() { if (_wEffects & PFE_TABLE) { // It would be nice to assert something reasonable here. However, the // rtf reader insists on setting things inconsistenly and I don't // have time at the moment to figure out why. (a-rsail) // AssertSz((_bTabCount != 0), // "CParaFormat::ValidateTabs: table with invalid tab count "); return; } // Non-table case. // It would be nice to assert on the consistency between these _bTabCount and _iTabs // but rtf reader has troubles with this. // AssertSz(((_bTabCount != 0) && (-1 != _iTabs)) || ((-1 == _iTabs) && (0 == _bTabCount)), // "CParaFormat::ValidateTabs: tab count and default tab index inconsistent"); if (-1 == _iTabs) { // No tabs to validate so we are done. return; } const LONG *prgtabs = GetTabs(); AssertSz(prgtabs != NULL, "CParaFormat::ValidateTabs: missing tab table"); for (int i = 0; i < _bTabCount; i++) { AssertSz(GetTabAlign(prgtabs[i]) <= tomAlignBar, "CParaFormat::ValidateTabs: Invalid tab being set"); } } #endif // DEBUG /* * CELLPARMS::ICellFromUCell(prgCell, dul, cCell) * * @mfunc * Return iCell such that SUM(CellWidths[iCell]) == dul. Return -1 if * no dul is found in prgCell * * @rdesc * iCell such that SUM(CellWidths[iCell]) == dul */ LONG CELLPARMS::ICellFromUCell( LONG dul, //@parm Logical x offset of cell right border LONG cCell) const //@parm Count of cells in array { const CELLPARMS *prgCell = this; LONG x = 0; for(LONG i = 0; i < cCell; i++) { x += GetCellWidth(prgCell[i].uCell); if(x == dul) return i; } return -1; } //------------------------- Helper Functions ----------------------------------- // Defines and fixed font size details for increasing/decreasing font size #define PWD_FONTSIZEPOINTMIN 1 // The following corresponds to the max signed 2-byte TWIP value, (32760) #define PWD_FONTSIZEPOINTMAX 1638 typedef struct tagfsFixup { BYTE EndValue; BYTE Delta; } FSFIXUP; const FSFIXUP fsFixups[] = { 12, 1, 28, 2, 36, 0, 48, 0, 72, 0, 80, 0, 0, 10 // EndValue = 0 case is treated as "infinite" }; #define PWD_FONTSIZEMAXFIXUPS (sizeof(fsFixups)/sizeof(fsFixups[0])) /* * GetUsableFontHeight(ySrcHeight, lPointChange) * * @func * Return a font size for setting text or insertion point attributes * * @rdesc * New TWIPS height * * @devnote * Copied from WinCE RichEdit code (written by V-GUYB) */ LONG GetUsableFontHeight( LONG ySrcHeight, //@parm Current font size in twips LONG lPointChange) //@parm Increase in pt size, (-ve if shrinking) { LONG EndValue; LONG Delta; int i; LONG yRetHeight; // Input height in twips here, (TWentIeths of a Point). // Note, a Point is a 1/72th of an inch. To make these // calculations clearer, use point sizes here. Input height // in twips is always divisible by 20 (NOTE (MS3): maybe with // a truncation, since RTF uses half-point units). yRetHeight = (ySrcHeight / 20) + lPointChange; // Fix new font size to match sizes used by Word95 for(i = 0; i < PWD_FONTSIZEMAXFIXUPS; ++i) { EndValue = fsFixups[i].EndValue; Delta = fsFixups[i].Delta; // Does new height lie in this range of point sizes? if(yRetHeight <= EndValue || !EndValue) { // If new height = EndValue, then it doesn't need adjusting if(yRetHeight != EndValue) { // Adjust new height to fit this range of point sizes. If // Delta = 1, all point sizes in this range stay as they are. if(!Delta) { // Everything in this range is rounded to the EndValue yRetHeight = fsFixups[(lPointChange > 0 ? i : max(i - 1, 0))].EndValue; } else if(Delta != 1) { // Round new height to next delta in this range yRetHeight = ((yRetHeight + (lPointChange > 0 ? Delta - 1 : 0)) / Delta) * Delta; } } break; } } // Limit the new text size. Note, if we fix the text size // now, then we won't take any special action if we change // the text size later in the other direction. For example, // we shrink chars with size 1 and 2. They both change to // size 1. Then we grow them both to 2. So they are the // same size now, even though they weren't before. This // matches Word95 behavior. yRetHeight = max(yRetHeight, PWD_FONTSIZEPOINTMIN); yRetHeight = min(yRetHeight, PWD_FONTSIZEPOINTMAX); return yRetHeight*20; // Return value in twips } /* * IsValidCharFormatW(pCF) * * @func * Return TRUE iff the structure *

has the correct size to be * a CHARFORMAT or a CHARFORMAT2 * * @rdesc * Return TRUE if *

is a valid CHARFORMAT(2) */ BOOL IsValidCharFormatW ( const CHARFORMAT * pCF) //@parm CHARFORMAT to validate { TRACEBEGIN(TRCSUBSYSBACK, TRCSCOPEINTERN, "IsValidCharFormat"); return pCF && (pCF->cbSize == sizeof(CHARFORMATW) || pCF->cbSize == sizeof(CHARFORMAT2W)); } /* * IsValidCharFormatA(pCFA) * * @func * Return TRUE iff the structure *

has the correct size to be * a CHARFORMATA or a CHARFORMAT2A * * @rdesc * Return TRUE if *

is a valid CHARFORMAT(2)A */ BOOL IsValidCharFormatA ( const CHARFORMATA * pCFA) //@parm CHARFORMATA to validate { TRACEBEGIN(TRCSUBSYSBACK, TRCSCOPEINTERN, "IsValidCharFormatA"); return pCFA && (pCFA->cbSize == sizeof(CHARFORMATA) || pCFA->cbSize == sizeof(CHARFORMAT2A)); } /* * IsValidParaFormat(pPF) * * @func * Return TRUE iff the structure *

has the correct size to be * a PARAFORMAT or a PARAFORMAT2 * * @rdesc * Return TRUE if *

is a valid PARAFORMAT(2) */ BOOL IsValidParaFormat ( const PARAFORMAT * pPF) //@parm PARAFORMAT to validate { TRACEBEGIN(TRCSUBSYSBACK, TRCSCOPEINTERN, "IsValidParaFormat"); if (pPF && (pPF->cbSize == sizeof(PARAFORMAT) || pPF->cbSize == sizeof(PARAFORMAT2))) { return TRUE; } TRACEERRORSZ("!!!!!!!!!!! bogus PARAFORMAT from client !!!!!!!!!!!!!"); return FALSE; } /* * Get16BitTwips(dy) * * @func * Return dy if |dy| < 32768; else return 32767, i.e., max value * that fits into a SHORT * * @rdesc * dy if abs(cy) < 32768; else 32767 */ SHORT Get16BitTwips(LONG dy) { return abs(dy) < 32768 ? (SHORT)dy : 32767; }