System font loading

This commit is contained in:
Jakub Melka 2019-04-14 18:14:41 +02:00
parent 90767ddfa5
commit 27fa4eb8aa
1 changed files with 194 additions and 10 deletions

View File

@ -28,9 +28,176 @@
#include <QMutex>
#ifdef Q_OS_WIN
#include "Windows.h"
#pragma comment(lib, "Gdi32")
#pragma comment(lib, "User32")
#endif
namespace pdf
{
/// Storage class for system fonts
class PDFSystemFontInfoStorage
{
public:
/// Returns instance of storage
static const PDFSystemFontInfoStorage* getInstance();
/// Loads font from descriptor
/// \param descriptor Descriptor describing the font
QByteArray loadFont(const FontDescriptor* descriptor) const;
private:
explicit PDFSystemFontInfoStorage();
#ifdef Q_OS_WIN
/// Callback for enumerating fonts
static int CALLBACK enumerateFontProc(const LOGFONT* font, const TEXTMETRIC* textMetrics, DWORD fontType, LPARAM lParam);
/// Retrieves font data for desired font
static QByteArray getFontData(const LOGFONT* font, HDC hdc);
struct FontInfo
{
QString faceName;
LOGFONT logFont;
TEXTMETRIC textMetric;
};
struct CallbackInfo
{
PDFSystemFontInfoStorage* storage = nullptr;
HDC hdc = nullptr;
};
std::vector<FontInfo> m_fontInfos;
#endif
};
const PDFSystemFontInfoStorage* PDFSystemFontInfoStorage::getInstance()
{
static PDFSystemFontInfoStorage instance;
return &instance;
}
QByteArray PDFSystemFontInfoStorage::loadFont(const FontDescriptor* descriptor) const
{
QByteArray result;
#ifdef Q_OS_WIN
HDC hdc = GetDC(NULL);
// Exact match for font, if font can't be exact matched, then match font family
// and try to set weight
QString fontFamily = QString::fromLatin1(descriptor->fontFamily);
for (const FontInfo& fontInfo : m_fontInfos)
{
if (fontInfo.faceName.contains(fontFamily) &&
fontInfo.logFont.lfWeight == descriptor->fontWeight &&
fontInfo.logFont.lfItalic == (descriptor->italicAngle != 0.0 ? TRUE : FALSE))
{
result = getFontData(&fontInfo.logFont, hdc);
if (!result.isEmpty())
{
break;
}
}
}
// Match for font family
if (result.isEmpty())
{
for (const FontInfo& fontInfo : m_fontInfos)
{
if (fontInfo.faceName.contains(fontFamily))
{
LOGFONT logFont = fontInfo.logFont;
logFont.lfWeight = descriptor->fontWeight;
logFont.lfItalic = (descriptor->italicAngle != 0.0 ? TRUE : FALSE);
result = getFontData(&logFont, hdc);
if (!result.isEmpty())
{
break;
}
}
}
}
ReleaseDC(NULL, hdc);
#endif
return result;
}
PDFSystemFontInfoStorage::PDFSystemFontInfoStorage()
{
#ifdef Q_OS_WIN
LOGFONT logfont;
std::memset(&logfont, 0, sizeof(logfont));
logfont.lfCharSet = DEFAULT_CHARSET;
logfont.lfFaceName[0] = 0;
logfont.lfPitchAndFamily = 0;
HDC hdc = GetDC(NULL);
CallbackInfo callbackInfo{ this, hdc};
EnumFontFamiliesEx(hdc, &logfont, &PDFSystemFontInfoStorage::enumerateFontProc, reinterpret_cast<LPARAM>(&callbackInfo), 0);
ReleaseDC(NULL, hdc);
#endif
}
#ifdef Q_OS_WIN
int PDFSystemFontInfoStorage::enumerateFontProc(const LOGFONT* font, const TEXTMETRIC* textMetrics, DWORD fontType, LPARAM lParam)
{
if ((fontType & TRUETYPE_FONTTYPE) && (font->lfCharSet == ANSI_CHARSET))
{
CallbackInfo* callbackInfo = reinterpret_cast<CallbackInfo*>(lParam);
FontInfo fontInfo;
fontInfo.logFont = *font;
fontInfo.textMetric = *textMetrics;
fontInfo.faceName = QString::fromWCharArray(font->lfFaceName);
callbackInfo->storage->m_fontInfos.push_back(qMove(fontInfo));
// For debug purposes only!
#if 0
QByteArray byteArray = getFontData(font, callbackInfo->hdc);
qDebug() << "Font: " << QString::fromWCharArray(font->lfFaceName) << ", italic = " << font->lfItalic << ", weight = " << font->lfWeight << ", data size = " << byteArray.size();
#endif
}
return TRUE;
}
QByteArray PDFSystemFontInfoStorage::getFontData(const LOGFONT* font, HDC hdc)
{
QByteArray byteArray;
if (HFONT fontHandle = ::CreateFontIndirect(font))
{
HGDIOBJ oldFont = ::SelectObject(hdc, fontHandle);
DWORD size = ::GetFontData(hdc, 0, 0, nullptr, 0);
if (size != GDI_ERROR)
{
byteArray.resize(static_cast<int>(size));
::GetFontData(hdc, 0, 0, byteArray.data(), byteArray.size());
}
::SelectObject(hdc, oldFont);
::DeleteObject(fontHandle);
}
return byteArray;
}
#endif
PDFFont::PDFFont(FontDescriptor fontDescriptor) :
m_fontDescriptor(qMove(fontDescriptor))
{
@ -81,6 +248,9 @@ private:
/// For embedded fonts, this byte array contains embedded font data
QByteArray m_embeddedFontData;
/// For system fonts, this byte array contains system font data
QByteArray m_systemFontData;
/// Instance of FreeType library assigned to this font
FT_Library m_library;
@ -198,16 +368,16 @@ const PDFRealizedFontImpl::Glyph& PDFRealizedFontImpl::getGlyphForUnicode(QChar
{
Glyph glyph;
FT_Outline_Funcs interface;
interface.delta = 0;
interface.shift = 0;
interface.move_to = PDFRealizedFontImpl::outlineMoveTo;
interface.line_to = PDFRealizedFontImpl::outlineLineTo;
interface.conic_to = PDFRealizedFontImpl::outlineConicTo;
interface.cubic_to = PDFRealizedFontImpl::outlineCubicTo;
FT_Outline_Funcs glyphOutlineInterface;
glyphOutlineInterface.delta = 0;
glyphOutlineInterface.shift = 0;
glyphOutlineInterface.move_to = PDFRealizedFontImpl::outlineMoveTo;
glyphOutlineInterface.line_to = PDFRealizedFontImpl::outlineLineTo;
glyphOutlineInterface.conic_to = PDFRealizedFontImpl::outlineConicTo;
glyphOutlineInterface.cubic_to = PDFRealizedFontImpl::outlineCubicTo;
checkFreeTypeError(FT_Load_Glyph(m_face, glyphIndex, FT_LOAD_NO_BITMAP | FT_LOAD_NO_HINTING));
checkFreeTypeError(FT_Outline_Decompose(&m_face->glyph->outline, &interface, &glyph));
checkFreeTypeError(FT_Outline_Decompose(&m_face->glyph->outline, &glyphOutlineInterface, &glyph));
glyph.glyph.closeSubpath();
glyph.advance = !m_isVertical ? m_face->glyph->advance.x : m_face->glyph->advance.y;
glyph.advance *= FORMAT_26_6_MULTIPLIER;
@ -292,8 +462,22 @@ PDFRealizedFontPointer PDFRealizedFont::createRealizedFont(PDFFontPointer font,
}
else
{
// TODO: Support non-embedded fonts
throw PDFParserException(PDFTranslationContext::tr("Only embedded fonts are supported."));
const PDFSystemFontInfoStorage* fontStorage = PDFSystemFontInfoStorage::getInstance();
impl->m_systemFontData = fontStorage->loadFont(descriptor);
if (impl->m_systemFontData.isEmpty())
{
// TODO: Upravit vyjimky do separatniho souboru
throw PDFParserException(PDFTranslationContext::tr("Can't load system font '%1'.").arg(QString::fromLatin1(descriptor->fontName)));
}
PDFRealizedFontImpl::checkFreeTypeError(FT_Init_FreeType(&impl->m_library));
PDFRealizedFontImpl::checkFreeTypeError(FT_New_Memory_Face(impl->m_library, reinterpret_cast<const FT_Byte*>(impl->m_systemFontData.constData()), impl->m_systemFontData.size(), 0, &impl->m_face));
PDFRealizedFontImpl::checkFreeTypeError(FT_Select_Charmap(impl->m_face, FT_ENCODING_UNICODE));
PDFRealizedFontImpl::checkFreeTypeError(FT_Set_Pixel_Sizes(impl->m_face, 0, qRound(pixelSize)));
impl->m_isVertical = impl->m_face->face_flags & FT_FACE_FLAG_VERTICAL;
impl->m_isEmbedded = false;
result.reset(new PDFRealizedFont(implPtr.release()));
}
return result;