mirror of
https://github.com/JakubMelka/PDF4QT.git
synced 2025-05-31 19:19:34 +02:00
Better font handling
This commit is contained in:
parent
5318e0e465
commit
4d770fdfcf
@ -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
|
||||
|
@ -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,
|
||||
|
@ -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;
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
{
|
||||
|
@ -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;
|
||||
}
|
||||
|
||||
|
@ -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
|
||||
|
@ -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");
|
||||
|
Loading…
x
Reference in New Issue
Block a user