1 // Copyright 2014 PDFium Authors. All rights reserved.
\r
2 // Use of this source code is governed by a BSD-style license that can be
\r
3 // found in the LICENSE file.
\r
5 // Original code copyright 2014 Foxit Software Inc. http://www.foxitsoftware.com
\r
7 #include "../fgas_base.h"
\r
8 #include "fx_gdifont.h"
\r
9 #include "fx_stdfontmgr.h"
\r
11 #if _FX_OS_ == _FX_WIN32_DESKTOP_ || _FX_OS_ == _FX_WIN32_MOBILE_ || _FX_OS_ == _FX_WIN64_
\r
12 CFX_GdiFontCache::CFX_GdiFontCache()
\r
16 CFX_GdiFontCache::~CFX_GdiFontCache()
\r
18 FX_POSITION pos = m_GlyphMap.GetStartPosition();
\r
20 FX_LPGDIGOCACHE pGlyph;
\r
21 while (pos != NULL) {
\r
23 m_GlyphMap.GetNextAssoc(pos, (void*&)iGlyph, (void*&)pGlyph);
\r
24 if (pGlyph != NULL) {
\r
25 FDE_Free(pGlyph->pOutline);
\r
29 m_GlyphMap.RemoveAll();
\r
31 void CFX_GdiFontCache::SetCachedGlyphOutline(FX_DWORD dwGlyph, const GLYPHMETRICS &gm, FX_LPBYTE pOutline)
\r
33 FXSYS_assert(pOutline != NULL);
\r
34 FX_LPGDIGOCACHE pGlyph = (FX_LPGDIGOCACHE)FDE_Alloc(sizeof(FX_GDIGOCACHE));
\r
36 pGlyph->pOutline = pOutline;
\r
37 m_GlyphMap.SetAt((void*)dwGlyph, (void*)pGlyph);
\r
39 FX_LPCGDIGOCACHE CFX_GdiFontCache::GetCachedGlyphOutline(FX_DWORD dwGlyph) const
\r
41 FX_LPCGDIGOCACHE pGlyph = NULL;
\r
42 if (!m_GlyphMap.Lookup((void*)dwGlyph, (void*&)pGlyph)) {
\r
47 IFX_Font* IFX_Font::LoadFont(FX_LPCWSTR pszFontFamily, FX_DWORD dwFontStyles, FX_WORD wCodePage, IFX_FontMgr *pFontMgr)
\r
49 CFX_GdiFont *pFont = FX_NEW CFX_GdiFont(pFontMgr);
\r
50 if (!pFont->LoadFont(pszFontFamily, dwFontStyles, wCodePage)) {
\r
56 IFX_Font* IFX_Font::LoadFont(FX_LPCBYTE pBuffer, FX_INT32 iLength, IFX_FontMgr *pFontMgr)
\r
58 CFX_GdiFont *pFont = FX_NEW CFX_GdiFont(pFontMgr);
\r
59 if (!pFont->LoadFont(pBuffer, iLength)) {
\r
65 IFX_Font* IFX_Font::LoadFont(FX_LPCWSTR pszFileName, IFX_FontMgr *pFontMgr)
\r
67 CFX_GdiFont *pFont = FX_NEW CFX_GdiFont(pFontMgr);
\r
68 if (!pFont->LoadFont(pszFileName)) {
\r
74 IFX_Font* IFX_Font::LoadFont(IFX_Stream *pFontStream, IFX_FontMgr *pFontMgr, FX_BOOL bSaveStream )
\r
76 CFX_GdiFont *pFont = FX_NEW CFX_GdiFont(pFontMgr);
\r
77 if (!pFont->LoadFont(pFontStream)) {
\r
83 IFX_Font* IFX_Font::LoadFont(CFX_Font *pExtFont, IFX_FontMgr *pFontMgr)
\r
85 FXSYS_assert(FALSE);
\r
88 #define FX_GDIFONT_FONTCACHESIZE 8
\r
89 CFX_GdiFont::CFX_GdiFont(IFX_FontMgr *pFontMgr)
\r
90 : m_pFontMgr(pFontMgr)
\r
92 , m_WidthCache(1024)
\r
96 , m_wsFontFileName()
\r
102 , m_FontCache(FX_GDIFONT_FONTCACHESIZE)
\r
104 m_hDC = ::CreateCompatibleDC(NULL);
\r
105 FX_memset(&m_LogFont, 0, sizeof(m_LogFont));
\r
106 FXSYS_assert(m_hDC != NULL);
\r
108 CFX_GdiFont::~CFX_GdiFont()
\r
110 FX_INT32 iCount = m_SubstFonts.GetSize();
\r
111 for (FX_INT32 i = 0; i < iCount; i ++) {
\r
112 IFX_Font *pFont = (IFX_Font*)m_SubstFonts[i];
\r
115 m_SubstFonts.RemoveAll();
\r
116 m_FontMapper.RemoveAll();
\r
117 if (m_hFont != NULL) {
\r
118 ::SelectObject(m_hDC, m_hOldFont);
\r
119 ::DeleteObject(m_hFont);
\r
122 if (m_hRes != NULL) {
\r
123 if (m_wsFontFileName.GetLength() > 0) {
\r
124 ::RemoveFontResourceW((FX_LPCWSTR)m_wsFontFileName);
\r
126 ::RemoveFontMemResourceEx(m_hRes);
\r
129 m_WidthCache.RemoveAll();
\r
132 void CFX_GdiFont::ClearCache()
\r
134 FX_INT32 iCount = m_SubstFonts.GetSize();
\r
135 for (FX_INT32 i = 0; i < iCount; i ++) {
\r
136 IFX_Font *pFont = (IFX_Font*)m_SubstFonts[i];
\r
137 ((CFX_GdiFont*)pFont)->ClearCache();
\r
139 FX_POSITION pos = m_FontCache.GetStartPosition();
\r
141 CFX_GdiFontCache *pCache;
\r
142 while (pos != NULL) {
\r
144 m_FontCache.GetNextAssoc(pos, (void*&)dwMAT2, (void*&)pCache);
\r
145 if (pCache != NULL) {
\r
149 m_FontCache.RemoveAll();
\r
151 void CFX_GdiFont::Release()
\r
153 if (-- m_iRefCount < 1) {
\r
154 if (m_pFontMgr != NULL) {
\r
155 m_pFontMgr->RemoveFont(this);
\r
160 IFX_Font* CFX_GdiFont::Retain()
\r
165 FX_BOOL CFX_GdiFont::LoadFont(FX_LPCWSTR pszFontFamily, FX_DWORD dwFontStyles, FX_WORD wCodePage)
\r
167 FXSYS_assert(m_hFont == NULL);
\r
169 FX_memset(&lf, 0, sizeof(lf));
\r
170 lf.lfHeight = -1000;
\r
171 lf.lfWeight = (dwFontStyles & FX_FONTSTYLE_Bold) ? FW_BOLD : FW_NORMAL;
\r
172 lf.lfItalic = (dwFontStyles & FX_FONTSTYLE_Italic) != 0;
\r
173 lf.lfPitchAndFamily = (dwFontStyles & FX_FONTSTYLE_FixedPitch) ? FIXED_PITCH : VARIABLE_PITCH;
\r
174 if (dwFontStyles & FX_FONTSTYLE_Serif) {
\r
175 lf.lfPitchAndFamily |= FF_ROMAN;
\r
177 if (dwFontStyles & FX_FONTSTYLE_Script) {
\r
178 lf.lfPitchAndFamily |= FF_SCRIPT;
\r
180 if (dwFontStyles & FX_FONTSTYLE_Symbolic) {
\r
181 lf.lfCharSet = SYMBOL_CHARSET;
\r
183 FX_WORD wCharSet = FX_GetCharsetFromCodePage(wCodePage);
\r
184 lf.lfCharSet = wCharSet != 0xFFFF ? (FX_BYTE)wCharSet : DEFAULT_CHARSET;
\r
186 if (pszFontFamily == NULL) {
\r
187 lf.lfFaceName[0] = L'\0';
\r
189 FXSYS_wcsncpy(lf.lfFaceName, pszFontFamily, 31);
\r
191 return LoadFont(lf);
\r
193 FX_BOOL CFX_GdiFont::LoadFont(FX_LPCBYTE pBuffer, FX_INT32 iLength)
\r
195 FXSYS_assert(m_hFont == NULL && pBuffer != NULL && iLength > 0);
\r
196 Gdiplus::PrivateFontCollection pfc;
\r
197 if (pfc.AddMemoryFont(pBuffer, iLength) != Gdiplus::Ok) {
\r
200 if (GetFontFamilies(pfc) < 1) {
\r
203 FX_DWORD dwCount = 0;
\r
204 m_hRes = ::AddFontMemResourceEx((void*)pBuffer, iLength, 0, &dwCount);
\r
205 if (m_hRes == NULL) {
\r
208 CFX_WideString wsFamily = m_FontFamilies[0];
\r
209 m_hFont = ::CreateFontW(-1000, 0, 0, 0, FW_NORMAL, FALSE, 0, 0, DEFAULT_CHARSET, 0, 0, 0, 0, (FX_LPCWSTR)wsFamily);
\r
210 if (m_hFont == NULL) {
\r
211 ::RemoveFontMemResourceEx(m_hRes);
\r
215 RetrieveFontStyles();
\r
216 m_hOldFont = ::SelectObject(m_hDC, m_hFont);
\r
217 ::GetOutlineTextMetricsW(m_hDC, sizeof(m_OutlineTM), &m_OutlineTM);
\r
220 FX_BOOL CFX_GdiFont::LoadFont(FX_LPCWSTR pszFileName)
\r
222 FXSYS_assert(m_hFont == NULL && pszFileName != NULL);
\r
223 Gdiplus::PrivateFontCollection pfc;
\r
224 if (pfc.AddFontFile(pszFileName) != Gdiplus::Ok) {
\r
227 if (GetFontFamilies(pfc) < 1) {
\r
230 m_wsFontFileName = pszFileName;
\r
231 m_hRes = (HANDLE)::AddFontResourceW(pszFileName);
\r
232 if (m_hRes == NULL) {
\r
235 CFX_WideString wsFamily = m_FontFamilies[0];
\r
236 m_hFont = ::CreateFontW(-1000, 0, 0, 0, FW_NORMAL, FALSE, 0, 0, DEFAULT_CHARSET, 0, 0, 0, 0, (FX_LPCWSTR)wsFamily);
\r
237 if (m_hFont == NULL) {
\r
238 ::RemoveFontResourceW(pszFileName);
\r
242 RetrieveFontStyles();
\r
243 ::SelectObject(m_hDC, m_hFont);
\r
244 ::GetOutlineTextMetricsW(m_hDC, sizeof(m_OutlineTM), &m_OutlineTM);
\r
247 FX_BOOL CFX_GdiFont::LoadFont(IFX_Stream *pFontStream)
\r
249 FXSYS_assert(m_hFont == NULL && pFontStream != NULL);
\r
250 FX_INT32 iLength = pFontStream->GetLength();
\r
254 FX_LPBYTE pBuf = (FX_LPBYTE)FDE_Alloc(iLength);
\r
255 if (pBuf == NULL) {
\r
258 iLength = pFontStream->ReadData(pBuf, iLength);
\r
259 FX_BOOL bRet = LoadFont(pBuf, iLength);
\r
263 FX_BOOL CFX_GdiFont::LoadFont(const LOGFONTW &lf)
\r
265 FXSYS_assert(m_hFont == NULL);
\r
266 m_hFont = ::CreateFontIndirectW((LPLOGFONTW)&lf);
\r
267 if (m_hFont == NULL) {
\r
270 RetrieveFontStyles();
\r
271 ::SelectObject(m_hDC, m_hFont);
\r
272 ::GetOutlineTextMetricsW(m_hDC, sizeof(m_OutlineTM), &m_OutlineTM);
\r
275 FX_INT32 CFX_GdiFont::GetFontFamilies(Gdiplus::FontCollection &fc)
\r
277 FX_INT32 iCount = fc.GetFamilyCount();
\r
281 Gdiplus::FontFamily *pFontFamilies = (Gdiplus::FontFamily*)FDE_Alloc(iCount * sizeof(Gdiplus::FontFamily));
\r
282 if (pFontFamilies == NULL) {
\r
285 FX_INT32 iFind = 0;
\r
286 fc.GetFamilies(iCount, pFontFamilies, &iFind);
\r
287 for (FX_INT32 i = 0; i < iCount; i ++) {
\r
288 CFX_WideString wsFamilyName;
\r
289 FX_LPWSTR pName = wsFamilyName.GetBuffer(LF_FACESIZE);
\r
290 pFontFamilies[i].GetFamilyName(pName);
\r
291 wsFamilyName.ReleaseBuffer();
\r
292 m_FontFamilies.Add(wsFamilyName);
\r
294 FDE_Free(pFontFamilies);
\r
297 void CFX_GdiFont::RetrieveFontStyles()
\r
299 FXSYS_assert(m_hFont != NULL);
\r
300 FX_memset(&m_LogFont, 0, sizeof(m_LogFont));
\r
301 ::GetObjectW(m_hFont, sizeof(m_LogFont), &m_LogFont);
\r
302 m_dwStyles = FX_GetGdiFontStyles(m_LogFont);
\r
304 void CFX_GdiFont::GetFamilyName(CFX_WideString &wsFamily) const
\r
306 FXSYS_assert(m_hFont != NULL);
\r
307 wsFamily = m_LogFont.lfFaceName;
\r
309 FX_BOOL CFX_GdiFont::GetCharWidth(FX_WCHAR wUnicode, FX_INT32 &iWidth, FX_BOOL bRecursive, FX_BOOL bCharCode)
\r
311 iWidth = (FX_INT32)(FX_SHORT)m_WidthCache.GetAt(wUnicode, 0);
\r
312 if (iWidth == 0 || iWidth == -1) {
\r
313 IFX_Font *pFont = NULL;
\r
314 FX_INT32 iGlyph = GetGlyphIndex(wUnicode, TRUE, &pFont, bCharCode);
\r
315 if (iGlyph != 0xFFFF && pFont != NULL) {
\r
316 if (pFont == (IFX_Font*)this) {
\r
317 if (!::GetCharWidthI(m_hDC, iGlyph, 1, NULL, &iWidth)) {
\r
320 } else if (((CFX_GdiFont*)pFont)->GetCharWidth(wUnicode, iWidth, FALSE, bCharCode)) {
\r
327 m_WidthCache.SetAtGrow(wUnicode, (FX_SHORT)iWidth);
\r
332 FX_BOOL CFX_GdiFont::GetCharWidth(FX_WCHAR wUnicode, FX_INT32 &iWidth, FX_BOOL bCharCode)
\r
334 return GetCharWidth(wUnicode, iWidth, TRUE, bCharCode);
\r
336 FX_INT32 CFX_GdiFont::GetGlyphIndex(FX_WCHAR wUnicode, FX_BOOL bRecursive, IFX_Font **ppFont, FX_BOOL bCharCode)
\r
338 FX_INT32 iGlyph = 0XFFFF;
\r
339 if (::GetGlyphIndicesW(m_hDC, &wUnicode, 1, (LPWORD)&iGlyph, GGI_MARK_NONEXISTING_GLYPHS) != GDI_ERROR && iGlyph != 0xFFFF) {
\r
340 if (ppFont != NULL) {
\r
341 *ppFont = (IFX_Font*)this;
\r
345 FX_LPCFONTUSB pFontUSB = FX_GetUnicodeBitField(wUnicode);
\r
346 if (pFontUSB == NULL) {
\r
349 FX_WORD wBitField = pFontUSB->wBitField;
\r
350 if (wBitField >= 128) {
\r
353 IFX_Font *pFont = NULL;
\r
354 m_FontMapper.Lookup((void*)wBitField, (void*&)pFont);
\r
355 if (pFont != NULL && pFont != (IFX_Font*)this) {
\r
356 iGlyph = ((CFX_GdiFont*)pFont)->GetGlyphIndex(wUnicode, FALSE, NULL, bCharCode);
\r
357 if (iGlyph != 0xFFFF) {
\r
358 FX_INT32 i = m_SubstFonts.Find(pFont);
\r
360 iGlyph |= ((i + 1) << 24);
\r
361 if (ppFont != NULL) {
\r
368 if (m_pFontMgr != NULL && bRecursive) {
\r
369 IFX_Font *pFont = m_pFontMgr->GetDefFontByUnicode(wUnicode, m_dwStyles, m_LogFont.lfFaceName);
\r
370 if (pFont != NULL) {
\r
371 if (pFont == (IFX_Font*)this) {
\r
375 m_FontMapper.SetAt((void*)wBitField, (void*)pFont);
\r
376 FX_INT32 i = m_SubstFonts.GetSize();
\r
377 m_SubstFonts.Add(pFont);
\r
378 iGlyph = ((CFX_GdiFont*)pFont)->GetGlyphIndex(wUnicode, FALSE, NULL, bCharCode);
\r
379 if (iGlyph != 0xFFFF) {
\r
380 iGlyph |= ((i + 1) << 24);
\r
381 if (ppFont != NULL) {
\r
390 FX_INT32 CFX_GdiFont::GetGlyphIndex(FX_WCHAR wUnicode, FX_BOOL bCharCode)
\r
392 return GetGlyphIndex(wUnicode, TRUE, NULL, bCharCode);
\r
394 FX_INT32 CFX_GdiFont::GetAscent() const
\r
396 return m_OutlineTM.otmAscent;
\r
398 FX_INT32 CFX_GdiFont::GetDescent() const
\r
400 return m_OutlineTM.otmDescent;
\r
402 FX_BOOL CFX_GdiFont::GetCharBBox(FX_WCHAR wUnicode, CFX_Rect &bbox, FX_BOOL bCharCode)
\r
404 FX_INT32 iGlyphIndex = GetGlyphIndex(wUnicode, bCharCode);
\r
405 if (iGlyphIndex == 0xFFFF) {
\r
408 IFX_Font *pFont = GetSubstFont(iGlyphIndex);
\r
409 if (pFont == NULL) {
\r
413 iGlyphIndex &= 0x00FFFFFF;
\r
414 static const MAT2 mat2 = {{0, 1}, {0, 0}, {0, 0}, {0, 1}};
\r
415 if (::GetGlyphOutlineW(((CFX_GdiFont*)pFont)->m_hDC, iGlyphIndex, GGO_GLYPH_INDEX | GGO_METRICS, &gm, 0, NULL, &mat2) != GDI_ERROR) {
\r
416 bbox.left = gm.gmptGlyphOrigin.x;
\r
417 bbox.top = gm.gmptGlyphOrigin.y;
\r
418 bbox.width = gm.gmBlackBoxX;
\r
419 bbox.height = gm.gmBlackBoxY;
\r
424 FX_BOOL CFX_GdiFont::GetBBox(CFX_Rect &bbox)
\r
426 bbox.left = m_OutlineTM.otmrcFontBox.left;
\r
427 bbox.top = m_OutlineTM.otmrcFontBox.top;
\r
428 bbox.width = m_OutlineTM.otmrcFontBox.right - m_OutlineTM.otmrcFontBox.left;
\r
429 bbox.height = m_OutlineTM.otmrcFontBox.bottom - m_OutlineTM.otmrcFontBox.top;
\r
432 FX_INT32 CFX_GdiFont::GetItalicAngle() const
\r
434 return m_OutlineTM.otmItalicAngle / 10;
\r
436 void CFX_GdiFont::Reset()
\r
439 m_WidthCache.RemoveAll();
\r
443 IFX_Font* CFX_GdiFont::GetSubstFont(FX_INT32 iGlyphIndex) const
\r
445 FX_INT32 iHigher = (iGlyphIndex & 0x7F000000) >> 24;
\r
446 if (iHigher == 0) {
\r
447 return (IFX_Font*)this;
\r
449 if (iHigher > m_SubstFonts.GetSize()) {
\r
450 return (IFX_Font*)this;
\r
452 return (IFX_Font*)m_SubstFonts[iHigher - 1];
\r
454 FX_DWORD CFX_GdiFont::GetGlyphDIBits(FX_INT32 iGlyphIndex, FX_ARGB argb, const MAT2 *pMatrix, GLYPHMETRICS &gm, FX_LPVOID pBuffer, FX_DWORD bufSize)
\r
456 static const UINT uFormat = GGO_GLYPH_INDEX | GGO_GRAY8_BITMAP;
\r
457 IFX_Font *pFont = GetSubstFont(iGlyphIndex);
\r
458 if (pFont == NULL) {
\r
461 if (pFont != (IFX_Font*)this) {
\r
462 return ((CFX_GdiFont*)pFont)->GetGlyphDIBits(iGlyphIndex & 0x00FFFFFF, argb, pMatrix, gm, pBuffer, bufSize);
\r
464 FX_LPBYTE pGlyphOutline = NULL;
\r
465 FXSYS_assert(pMatrix != NULL);
\r
466 FX_DWORD dwMAT2 = GetMAT2HashCode((const FIXED*)pMatrix);
\r
467 CFX_GdiFontCache *pCache = NULL;
\r
468 if (m_FontCache.Lookup((void*)dwMAT2, (void*&)pCache) && pCache != NULL) {
\r
469 FX_LPCGDIGOCACHE pGO = pCache->GetCachedGlyphOutline(iGlyphIndex);
\r
472 pGlyphOutline = pGO->pOutline;
\r
475 if (pGlyphOutline == NULL) {
\r
476 FX_DWORD dwGlyphSize = ::GetGlyphOutlineW(m_hDC, iGlyphIndex, uFormat, &gm, 0, NULL, pMatrix);
\r
477 if (dwGlyphSize == 0 || dwGlyphSize == GDI_ERROR) {
\r
480 pGlyphOutline = (FX_LPBYTE)FX_Alloc(dwGlyphSize);
\r
481 ::GetGlyphOutlineW(m_hDC, iGlyphIndex, uFormat, &gm, dwGlyphSize, pGlyphOutline, pMatrix);
\r
482 if (pCache == NULL) {
\r
483 pCache = FX_NEW CFX_GdiFontCache;
\r
484 if (m_FontCache.GetCount() >= FX_GDIFONT_FONTCACHESIZE) {
\r
487 m_FontCache.SetAt((void*)dwMAT2, (void*)pCache);
\r
489 pCache->SetCachedGlyphOutline(iGlyphIndex, gm, pGlyphOutline);
\r
491 FX_DWORD dwDibSize = gm.gmBlackBoxX * 4 * gm.gmBlackBoxY;
\r
492 if (pBuffer == NULL || bufSize < dwDibSize) {
\r
495 CreateGlyphBitmap(gm.gmBlackBoxX, gm.gmBlackBoxY, pGlyphOutline, (FX_LPDWORD)pBuffer, argb);
\r
498 FX_DWORD CFX_GdiFont::GetHashCode() const
\r
500 return FX_GetFontHashCode(FX_GetCodePageFromCharset(m_LogFont.lfCharSet), FX_GetGdiFontStyles(m_LogFont));
\r
502 FX_DWORD CFX_GdiFont::GetMAT2HashCode(const FIXED *pFixed)
\r
504 FXSYS_assert(pFixed != NULL);
\r
505 FX_DWORD dwHash1 = 0, dwHash2 = 5381, dwRet;
\r
506 for (int i = 0; i < 4; i ++) {
\r
507 dwRet = *((const FX_DWORD*)pFixed);
\r
508 dwHash1 = 1313 * dwHash1 + dwRet;
\r
509 dwHash2 += (dwHash2 << 5) + dwRet;
\r
512 return ((dwHash1 & 0x0000FFFF) << 16) | (dwHash2 & 0x0000FFFF);
\r
514 void CFX_GdiFont::CreateGlyphBitmap(FX_INT32 iWidth, FX_INT32 iHeight, FX_LPBYTE pOutline, FX_LPDWORD pDIB, FX_ARGB argb)
\r
516 FX_INT32 padding = ((iWidth + 3) / 4) * 4 - iWidth;
\r
519 for (j = iHeight - 1; j >= 0; --j) {
\r
520 for (i = iWidth - 1; i >= 0; --i) {
\r
521 if ((alpha = *pOutline++) == 0) {
\r
524 alpha = (argb >> 24) * (alpha * 4 - 1) / 256;
\r
525 *pDIB++ = (alpha << 24) | (argb & 0x00FFFFFF);
\r
528 pOutline += padding;
\r