Better font handling

This commit is contained in:
Jakub Melka 2019-04-30 14:39:48 +02:00
parent 5318e0e465
commit 4d770fdfcf
8 changed files with 110 additions and 28 deletions

View File

@ -42,6 +42,7 @@ private:
enum RenderErrorType
{
Error,
Warning,
NotImplemented
};
@ -74,6 +75,19 @@ private:
PDFRenderError m_error;
};
/// Abstract class for reporting render errors
class PDFRenderErrorReporter
{
public:
explicit PDFRenderErrorReporter() = default;
virtual ~PDFRenderErrorReporter() = default;
/// Reports render errors
/// \param type Error type
/// \param message Error message
virtual void reportRenderError(RenderErrorType type, QString message) = 0;
};
} // namespace pdf
#endif // PDFEXCEPTION_H

View File

@ -324,12 +324,16 @@ public:
/// produces glyphs for the font.
/// \param byteArray Array of bytes to be interpreted
/// \param textSequence Text sequence to be filled
void fillTextSequence(const QByteArray& byteArray, TextSequence& textSequence);
void fillTextSequence(const QByteArray& byteArray, TextSequence& textSequence, PDFRenderErrorReporter* reporter);
static constexpr const PDFReal PIXEL_SIZE_MULTIPLIER = 100.0;
private:
friend class PDFRealizedFont;
static constexpr const PDFReal FONT_WIDTH_MULTIPLIER = 1.0 / 1000.0;
static constexpr const PDFReal FORMAT_26_6_MULTIPLIER = 1 / 64.0;
static constexpr const PDFReal FONT_MULTIPLIER = FORMAT_26_6_MULTIPLIER / PIXEL_SIZE_MULTIPLIER;
struct Glyph
{
@ -405,7 +409,7 @@ PDFRealizedFontImpl::~PDFRealizedFontImpl()
}
}
void PDFRealizedFontImpl::fillTextSequence(const QByteArray& byteArray, TextSequence& textSequence)
void PDFRealizedFontImpl::fillTextSequence(const QByteArray& byteArray, TextSequence& textSequence, PDFRenderErrorReporter* reporter)
{
switch (m_parentFont->getFontType())
{
@ -432,13 +436,21 @@ void PDFRealizedFontImpl::fillTextSequence(const QByteArray& byteArray, TextSequ
}
}
if (!glyphIndex)
{
throw PDFParserException(PDFTranslationContext::tr("Glyph for simple font character code '%1' not found.").arg(static_cast<uint8_t>(byteArray[i])));
}
const PDFReal glyphWidth = font->getGlyphWidth(static_cast<uint8_t>(byteArray[i]));
const Glyph& glyph = getGlyph(glyphIndex);
textSequence.items.emplace_back(&glyph.glyph, (*encoding)[static_cast<uint8_t>(byteArray[i])], glyph.advance);
if (glyphIndex)
{
const Glyph& glyph = getGlyph(glyphIndex);
textSequence.items.emplace_back(&glyph.glyph, (*encoding)[static_cast<uint8_t>(byteArray[i])], glyph.advance);
}
else
{
reporter->reportRenderError(RenderErrorType::Warning, PDFTranslationContext::tr("Glyph for simple font character code '%1' not found.").arg(static_cast<uint8_t>(byteArray[i])));
if (glyphWidth > 0)
{
textSequence.items.emplace_back(nullptr, QChar(), glyphWidth * m_pixelSize * FONT_WIDTH_MULTIPLIER);
}
}
}
break;
}
@ -482,28 +494,28 @@ void PDFRealizedFontImpl::fillTextSequence(const QByteArray& byteArray, TextSequ
int PDFRealizedFontImpl::outlineMoveTo(const FT_Vector* to, void* user)
{
Glyph* glyph = reinterpret_cast<Glyph*>(user);
glyph->glyph.moveTo(to->x * FORMAT_26_6_MULTIPLIER, to->y * FORMAT_26_6_MULTIPLIER);
glyph->glyph.moveTo(to->x * FONT_MULTIPLIER, to->y * FONT_MULTIPLIER);
return 0;
}
int PDFRealizedFontImpl::outlineLineTo(const FT_Vector* to, void* user)
{
Glyph* glyph = reinterpret_cast<Glyph*>(user);
glyph->glyph.lineTo(to->x * FORMAT_26_6_MULTIPLIER, to->y * FORMAT_26_6_MULTIPLIER);
glyph->glyph.lineTo(to->x * FONT_MULTIPLIER, to->y * FONT_MULTIPLIER);
return 0;
}
int PDFRealizedFontImpl::outlineConicTo(const FT_Vector* control, const FT_Vector* to, void* user)
{
Glyph* glyph = reinterpret_cast<Glyph*>(user);
glyph->glyph.quadTo(control->x * FORMAT_26_6_MULTIPLIER, control->y * FORMAT_26_6_MULTIPLIER, to->x * FORMAT_26_6_MULTIPLIER, to->y * FORMAT_26_6_MULTIPLIER);
glyph->glyph.quadTo(control->x * FONT_MULTIPLIER, control->y * FONT_MULTIPLIER, to->x * FONT_MULTIPLIER, to->y * FONT_MULTIPLIER);
return 0;
}
int PDFRealizedFontImpl::outlineCubicTo(const FT_Vector* control1, const FT_Vector* control2, const FT_Vector* to, void* user)
{
Glyph* glyph = reinterpret_cast<Glyph*>(user);
glyph->glyph.cubicTo(control1->x * FORMAT_26_6_MULTIPLIER, control1->y * FORMAT_26_6_MULTIPLIER, control2->x * FORMAT_26_6_MULTIPLIER, control2->y * FORMAT_26_6_MULTIPLIER, to->x * FORMAT_26_6_MULTIPLIER, to->y * FORMAT_26_6_MULTIPLIER);
glyph->glyph.cubicTo(control1->x * FONT_MULTIPLIER, control1->y * FONT_MULTIPLIER, control2->x * FONT_MULTIPLIER, control2->y * FONT_MULTIPLIER, to->x * FONT_MULTIPLIER, to->y * FONT_MULTIPLIER);
return 0;
}
@ -534,7 +546,7 @@ const PDFRealizedFontImpl::Glyph& PDFRealizedFontImpl::getGlyph(unsigned int gly
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;
glyph.advance *= FONT_MULTIPLIER;
m_glyphCache[glyphIndex] = qMove(glyph);
return m_glyphCache[glyphIndex];
@ -563,9 +575,9 @@ PDFRealizedFont::~PDFRealizedFont()
delete m_impl;
}
void PDFRealizedFont::fillTextSequence(const QByteArray& byteArray, TextSequence& textSequence)
void PDFRealizedFont::fillTextSequence(const QByteArray& byteArray, TextSequence& textSequence, PDFRenderErrorReporter* reporter)
{
m_impl->fillTextSequence(byteArray, textSequence);
m_impl->fillTextSequence(byteArray, textSequence, reporter);
}
bool PDFRealizedFont::isHorizontalWritingSystem() const
@ -580,6 +592,7 @@ PDFRealizedFontPointer PDFRealizedFont::createRealizedFont(PDFFontPointer font,
PDFRealizedFontImpl* impl = implPtr.get();
impl->m_parentFont = font;
impl->m_pixelSize = pixelSize;
const FontDescriptor* descriptor = font->getFontDescriptor();
if (descriptor->isEmbedded())
@ -594,7 +607,7 @@ PDFRealizedFontPointer PDFRealizedFont::createRealizedFont(PDFFontPointer font,
PDFRealizedFontImpl::checkFreeTypeError(FT_New_Memory_Face(impl->m_library, reinterpret_cast<const FT_Byte*>(impl->m_embeddedFontData.constData()), impl->m_embeddedFontData.size(), 0, &impl->m_face));
FT_Select_Charmap(impl->m_face, FT_ENCODING_UNICODE); // We try to select unicode encoding, but if it fails, we don't do anything (use glyph indices instead)
PDFRealizedFontImpl::checkFreeTypeError(FT_Set_Pixel_Sizes(impl->m_face, 0, qRound(pixelSize)));
PDFRealizedFontImpl::checkFreeTypeError(FT_Set_Pixel_Sizes(impl->m_face, 0, qRound(pixelSize * PDFRealizedFontImpl::PIXEL_SIZE_MULTIPLIER)));
impl->m_isVertical = impl->m_face->face_flags & FT_FACE_FLAG_VERTICAL;
impl->m_isEmbedded = true;
result.reset(new PDFRealizedFont(implPtr.release()));
@ -620,7 +633,7 @@ PDFRealizedFontPointer PDFRealizedFont::createRealizedFont(PDFFontPointer font,
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));
FT_Select_Charmap(impl->m_face, FT_ENCODING_UNICODE); // We try to select unicode encoding, but if it fails, we don't do anything (use glyph indices instead)
PDFRealizedFontImpl::checkFreeTypeError(FT_Set_Pixel_Sizes(impl->m_face, 0, qRound(pixelSize)));
PDFRealizedFontImpl::checkFreeTypeError(FT_Set_Pixel_Sizes(impl->m_face, 0, qRound(pixelSize * PDFRealizedFontImpl::PIXEL_SIZE_MULTIPLIER)));
impl->m_isVertical = impl->m_face->face_flags & FT_FACE_FLAG_VERTICAL;
impl->m_isEmbedded = false;
result.reset(new PDFRealizedFont(implPtr.release()));
@ -815,13 +828,7 @@ PDFFontPointer PDFFont::createFont(const PDFObject& object, const PDFDocument* d
throw PDFParserException(PDFTranslationContext::tr("Invalid differences in encoding entry of the font."));
}
QChar character = PDFNameToUnicode::getUnicodeForName(item.getString());
// Try ZapfDingbats, if this fails
if (character.isNull())
{
character = PDFNameToUnicode::getUnicodeForNameZapfDingbats(item.getString());
}
QChar character = PDFNameToUnicode::getUnicodeUsingResolvedName(item.getString());
differences[currentOffset] = character;
++currentOffset;
@ -1106,6 +1113,23 @@ PDFSimpleFont::PDFSimpleFont(FontDescriptor fontDescriptor,
}
PDFInteger PDFSimpleFont::getGlyphWidth(size_t index) const
{
const size_t min = m_firstChar;
const size_t max = m_lastChar;
if (index >= min && index <= max)
{
const size_t adjustedIndex = index - min;
if (adjustedIndex < m_widths.size())
{
return m_widths[adjustedIndex];
}
}
return 0;
}
PDFType1Font::PDFType1Font(FontDescriptor fontDescriptor,
QByteArray name,
QByteArray baseFont,

View File

@ -30,6 +30,7 @@ class QPainterPath;
namespace pdf
{
class PDFDocument;
class PDFRenderErrorReporter;
using CID = unsigned int;
using GID = unsigned int;
@ -217,7 +218,8 @@ public:
/// produces glyphs for the font.
/// \param byteArray Array of bytes to be interpreted
/// \param textSequence Text sequence to be filled
void fillTextSequence(const QByteArray& byteArray, TextSequence& textSequence);
/// \param reporter Error reporter
void fillTextSequence(const QByteArray& byteArray, TextSequence& textSequence, PDFRenderErrorReporter* reporter);
/// Return true, if we have horizontal writing system
bool isHorizontalWritingSystem() const;
@ -280,6 +282,9 @@ public:
const encoding::EncodingTable* getEncoding() const { return &m_encoding; }
const GlyphIndices* getGlyphIndices() const { return &m_glyphIndices; }
/// Returns the glyph width (or zero, if glyph width is invalid)
PDFInteger getGlyphWidth(size_t index) const;
protected:
QByteArray m_name;
QByteArray m_baseFont;

View File

@ -4540,4 +4540,27 @@ QChar PDFNameToUnicode::getUnicodeForNameZapfDingbats(const QByteArray& name)
}
}
QChar PDFNameToUnicode::getUnicodeUsingResolvedName(const QByteArray& name)
{
QChar character = getUnicodeForName(name);
// Try ZapfDingbats, if this fails
if (character.isNull())
{
character = getUnicodeForNameZapfDingbats(name);
}
if (character.isNull() && name.startsWith("uni"))
{
QByteArray hexValue = QByteArray::fromHex(name.mid(3, -1));
if (hexValue.size() == 2)
{
unsigned short value = (static_cast<unsigned char>(hexValue[0]) << 8) + static_cast<unsigned char>(hexValue[1]);
character = QChar(value);
}
}
return character;
}
} // namespace pdf

View File

@ -38,6 +38,9 @@ public:
/// Returns unicode character for name (for ZapfDingbats). If name is not found, then null character is returned.
static QChar getUnicodeForNameZapfDingbats(const QByteArray& name);
/// Tries to resolve unicode name
static QChar getUnicodeUsingResolvedName(const QByteArray& name);
private:
struct Comparator
{

View File

@ -257,6 +257,11 @@ QList<PDFRenderError> PDFPageContentProcessor::processContents()
return m_errorList;
}
void PDFPageContentProcessor::reportRenderError(RenderErrorType type, QString message)
{
m_errorList.append(PDFRenderError(type, qMove(message)));
}
void PDFPageContentProcessor::performPathPainting(const QPainterPath& path, bool stroke, bool fill, Qt::FillRule fillRule)
{
Q_UNUSED(path);
@ -1654,7 +1659,7 @@ void PDFPageContentProcessor::operatorTextShowTextString(PDFOperandString text)
// We use simple heuristic to ensure reallocation doesn't occur too often
textSequence.items.reserve(m_operands.size());
realizedFont->fillTextSequence(text.string, textSequence);
realizedFont->fillTextSequence(text.string, textSequence, this);
drawText(textSequence);
}
else
@ -1711,7 +1716,7 @@ void PDFPageContentProcessor::operatorTextShowTextIndividualSpacing()
case PDFLexicalAnalyzer::TokenType::String:
{
realizedFont->fillTextSequence(m_operands[i].data.toByteArray(), textSequence);
realizedFont->fillTextSequence(m_operands[i].data.toByteArray(), textSequence, this);
break;
}

View File

@ -37,7 +37,7 @@ namespace pdf
static constexpr const char* PDF_RESOURCE_EXTGSTATE = "ExtGState";
/// Process the contents of the page.
class PDFPageContentProcessor
class PDFPageContentProcessor : public PDFRenderErrorReporter
{
public:
explicit PDFPageContentProcessor(const PDFPage* page, const PDFDocument* document, const PDFFontCache* fontCache);
@ -155,6 +155,8 @@ public:
/// Process the contents of the page
QList<PDFRenderError> processContents();
virtual void reportRenderError(RenderErrorType type, QString message) override;
protected:
class PDFLineDashPattern

View File

@ -56,6 +56,12 @@ PDFRenderingErrorsWidget::PDFRenderingErrorsWidget(QWidget* parent, PDFWidget* p
break;
}
case RenderErrorType::Warning:
{
typeString = tr("Warning");
break;
}
case RenderErrorType::NotImplemented:
{
typeString = tr("Not implemented");