Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions LoopLibCore/sources/pdffont.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1217,10 +1217,12 @@ void PDFRealizedFontImpl::fillTextSequence(const QByteArray& byteArray, TextSequ
{
const Glyph& glyph = getGlyph(glyphIndex);
textSequence.items.emplace_back(&glyph.glyph, font->getUnicode(cid), glyph.advance, cid);
textSequence.items.back().glyphIndex = glyphIndex;
}
else
{
reporter->reportRenderError(RenderErrorType::Warning, PDFTranslationContext::tr("Glyph for simple font character code '%1' not found.").arg(cid));
textSequence.unresolvedCodes.push_back(cid);
if (glyphWidth > 0)
{
const QPainterPath* nullpath = nullptr;
Expand Down Expand Up @@ -1275,13 +1277,15 @@ void PDFRealizedFontImpl::fillTextSequence(const QByteArray& byteArray, TextSequ
{
const Glyph& glyph = getGlyph(*glyphIndex);
textSequence.items.emplace_back(&glyph.glyph, character, glyph.advance, cid);
textSequence.items.back().glyphIndex = *glyphIndex;
}
else
{
if (cid > 0)
{
// Character with CID == 0 is treated as default whitespace, it hasn't glyph
reporter->reportRenderError(RenderErrorType::Warning, PDFTranslationContext::tr("Glyph for composite font character with cid '%1' not found.").arg(cid));
textSequence.unresolvedCodes.push_back(cid);
}

if (glyphWidth > 0)
Expand Down
54 changes: 43 additions & 11 deletions LoopLibCore/sources/pdffont.h
Original file line number Diff line number Diff line change
Expand Up @@ -79,9 +79,25 @@ class ITreeFactory
struct TextSequenceItem
{
inline explicit TextSequenceItem() = default;
inline explicit TextSequenceItem(const QPainterPath* glyph, QChar character, PDFReal advance, CID cid) : glyph(glyph), character(character), advance(advance), cid(cid) { }
inline explicit TextSequenceItem(PDFReal advance) : character(), advance(advance) { }
inline explicit TextSequenceItem(const QByteArray* characterContentStream, QChar character, PDFReal advance, uint cid) : characterContentStream(characterContentStream), character(character), advance(advance), cid(cid) { }
inline explicit TextSequenceItem(const QPainterPath* glyph, QChar character, PDFReal advance, CID cid) :
glyph(glyph),
character(character),
advance(advance),
cid(cid)
{
}
inline explicit TextSequenceItem(PDFReal advance) :
character(),
advance(advance)
{
}
inline explicit TextSequenceItem(const QByteArray* characterContentStream, QChar character, PDFReal advance, uint cid) :
characterContentStream(characterContentStream),
character(character),
advance(advance),
cid(cid)
{
}

inline bool isContentStream() const { return characterContentStream; }
inline bool isCharacter() const { return glyph; }
Expand All @@ -93,11 +109,18 @@ struct TextSequenceItem
QChar character;
PDFReal advance = 0;
CID cid = 0;
/// Glyph index in the font program; 0 is .notdef. Zero also for advances and Type 3 glyphs.
GID glyphIndex = 0;
};

struct TextSequence
{
std::vector<TextSequenceItem> items;

/// Shown codes (character codes of simple fonts, CIDs of composite fonts) that
/// resolved to no glyph, whatever their advance. Composite CID 0 is the default
/// whitespace and is never listed.
std::vector<CID> unresolvedCodes;
};

constexpr bool isTextRenderingModeFilled(TextRenderingMode mode)
Expand Down Expand Up @@ -295,7 +318,10 @@ class LOOPLIBCORESHARED_EXPORT PDFRealizedFont

private:
/// Constructs new realized font
explicit PDFRealizedFont(IRealizedFontImpl* impl) : m_impl(impl) { }
explicit PDFRealizedFont(IRealizedFontImpl* impl) :
m_impl(impl)
{
}

IRealizedFontImpl* m_impl;
};
Expand Down Expand Up @@ -417,7 +443,7 @@ class LOOPLIBCORESHARED_EXPORT PDFSimpleFont : public PDFFont
bool m_hasToUnicode;
GlyphIndices m_glyphIndices;
GlyphNames m_glyphNames;
StandardFontType m_standardFontType; ///< Type of the standard font (or invalid, if it is not a standard font)
StandardFontType m_standardFontType; ///< Type of the standard font (or invalid, if it is not a standard font)
};

class PDFType1Font : public PDFSimpleFont
Expand Down Expand Up @@ -468,7 +494,6 @@ class LOOPLIBCORESHARED_EXPORT PDFFontCache
m_realizedFontCacheLimit(realizedFontCacheLimit),
m_document(nullptr)
{

}

~PDFFontCache();
Expand Down Expand Up @@ -543,7 +568,10 @@ class LOOPLIBCORESHARED_EXPORT PDFFontCache
class PDFCIDtoGIDMapper
{
public:
explicit inline PDFCIDtoGIDMapper(QByteArray&& mapping) : m_mapping(qMove(mapping)) { }
explicit inline PDFCIDtoGIDMapper(QByteArray&& mapping) :
m_mapping(qMove(mapping))
{
}

/// Maps CID to GID (glyph identifier). Nullopt means no valid mapping exists.
std::optional<GID> tryMap(CID cid) const
Expand Down Expand Up @@ -669,11 +697,16 @@ class LOOPLIBCORESHARED_EXPORT PDFFontCMap
bool containsCode(unsigned int code, unsigned int byteCount) const;

private:

struct Entry
{
constexpr explicit inline Entry() = default;
constexpr explicit inline Entry(unsigned int from, unsigned int to, unsigned int byteCount, CID cid) : from(from), to(to), byteCount(byteCount), cid(cid) { }
constexpr explicit inline Entry(unsigned int from, unsigned int to, unsigned int byteCount, CID cid) :
from(from),
to(to),
byteCount(byteCount),
cid(cid)
{
}

unsigned int from = 0;
unsigned int to = 0;
Expand Down Expand Up @@ -777,7 +810,6 @@ class LOOPLIBCORESHARED_EXPORT PDFType0Font : public PDFFont
m_defaultAdvance(defaultAdvance),
m_advances(qMove(advances))
{

}

virtual ~PDFType0Font() = default;
Expand Down Expand Up @@ -845,4 +877,4 @@ class LOOPLIBCORESHARED_EXPORT PDFSystemFont

} // namespace pdf

#endif // PDFFONT_H
#endif // PDFFONT_H
65 changes: 51 additions & 14 deletions LoopLibCore/sources/pdffontintegrity.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@

#include "pdffont.h"

#include <QPainterPath>
#include <QSet>

#include <algorithm>
Expand All @@ -41,10 +42,7 @@ quint16 readU16(const QByteArray& data, int offset)

quint32 readU32(const QByteArray& data, int offset)
{
return (quint32(uchar(data.at(offset))) << 24)
| (quint32(uchar(data.at(offset + 1))) << 16)
| (quint32(uchar(data.at(offset + 2))) << 8)
| quint32(uchar(data.at(offset + 3)));
return (quint32(uchar(data.at(offset))) << 24) | (quint32(uchar(data.at(offset + 1))) << 16) | (quint32(uchar(data.at(offset + 2))) << 8) | quint32(uchar(data.at(offset + 3)));
}

void inspectTrueType(const QByteArray& program, FontType fontType, QStringList& defects)
Expand All @@ -56,8 +54,7 @@ void inspectTrueType(const QByteArray& program, FontType fontType, QStringList&
}

const QByteArray magic = program.left(4);
if (magic != QByteArrayLiteral("OTTO")
&& magic != QByteArray::fromHex("00010000"))
if (magic != QByteArrayLiteral("OTTO") && magic != QByteArray::fromHex("00010000"))
{
defects.append(QStringLiteral("UnreadableTableDirectory"));
return;
Expand All @@ -79,8 +76,7 @@ void inspectTrueType(const QByteArray& program, FontType fontType, QStringList&
tables.insert(tag);
const quint32 length = readU32(program, offset + 12);
const quint32 tableOffset = readU32(program, offset + 8);
if (tableOffset > quint32(program.size())
|| length > quint32(program.size()) - tableOffset)
if (tableOffset > quint32(program.size()) || length > quint32(program.size()) - tableOffset)
{
defects.append(QStringLiteral("TruncatedProgram"));
continue;
Expand All @@ -96,14 +92,13 @@ void inspectTrueType(const QByteArray& program, FontType fontType, QStringList&
}
}

if (fontType == FontType::TrueType && tables.contains(QByteArrayLiteral("glyf"))
&& !tables.contains(QByteArrayLiteral("loca")))
if (fontType == FontType::TrueType && tables.contains(QByteArrayLiteral("glyf")) && !tables.contains(QByteArrayLiteral("loca")))
{
defects.append(QStringLiteral("GlyfLocaInconsistent"));
}
}

} // namespace
} // namespace

PDFFontIntegrityResult inspectPDFFontIntegrity(const PDFFont& font)
{
Expand All @@ -130,8 +125,7 @@ PDFFontIntegrityResult inspectPDFFontIntegrity(const PDFFont& font)
break;
case FontType::Type1:
case FontType::MMType1:
if (!program->startsWith("%!")
&& !(program->size() >= 2 && uchar(program->at(0)) == 0x80 && uchar(program->at(1)) == 0x01))
if (!program->startsWith("%!") && !(program->size() >= 2 && uchar(program->at(0)) == 0x80 && uchar(program->at(1)) == 0x01))
{
result.defects.append(QStringLiteral("UnreadableType1Program"));
}
Expand All @@ -151,4 +145,47 @@ PDFFontIntegrityResult inspectPDFFontIntegrity(const PDFFont& font)
return result;
}

} // namespace pdf
PDFShownGlyphDefect classifyShownGlyph(const TextSequenceItem& item)
{
if (item.isContentStream())
{
return PDFShownGlyphDefect::None;
}

if (!item.glyph)
{
// Codes that resolve to nothing are listed in TextSequence::unresolvedCodes.
return PDFShownGlyphDefect::None;
}

if (item.glyphIndex == 0)
{
return PDFShownGlyphDefect::Notdef;
}

const bool visibleCharacter = !item.character.isNull() && !item.character.isSpace() && item.character.category() != QChar::Other_Format && item.character.category() != QChar::Other_Control;
if (visibleCharacter && item.glyph->isEmpty())
{
return PDFShownGlyphDefect::EmptyOutline;
}

return PDFShownGlyphDefect::None;
}

QString shownGlyphDefectName(PDFShownGlyphDefect defect)
{
switch (defect)
{
case PDFShownGlyphDefect::Unresolved:
return QStringLiteral("MissingGlyph");
case PDFShownGlyphDefect::Notdef:
return QStringLiteral("NotdefGlyph");
case PDFShownGlyphDefect::EmptyOutline:
return QStringLiteral("EmptyGlyph");
case PDFShownGlyphDefect::None:
break;
}
return QString();
}

} // namespace pdf
20 changes: 18 additions & 2 deletions LoopLibCore/sources/pdffontintegrity.h
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@ namespace pdf
{

class PDFFont;
struct TextSequenceItem;

struct LOOPLIBCORESHARED_EXPORT PDFFontIntegrityResult
{
Expand All @@ -46,6 +47,21 @@ struct LOOPLIBCORESHARED_EXPORT PDFFontIntegrityResult
/// does not alter the existing embedded-fonts check contract.
LOOPLIBCORESHARED_EXPORT PDFFontIntegrityResult inspectPDFFontIntegrity(const PDFFont& font);

} // namespace pdf
enum class PDFShownGlyphDefect
{
None,
Unresolved, ///< The shown code resolved to no glyph (TextSequence::unresolvedCodes).
Notdef, ///< The shown code resolved to glyph 0 (.notdef).
EmptyOutline ///< The glyph exists but draws nothing for a visible character.
};

/// Classifies one resolved glyph item of a text sequence. Advances (TJ
/// adjustments), glyphless items and Type 3 glyph procedures are never defects
/// here; codes that resolved to nothing are reported by the text sequence.
LOOPLIBCORESHARED_EXPORT PDFShownGlyphDefect classifyShownGlyph(const TextSequenceItem& item);

LOOPLIBCORESHARED_EXPORT QString shownGlyphDefectName(PDFShownGlyphDefect defect);

} // namespace pdf

#endif // PDFFONTINTEGRITY_H
#endif // PDFFONTINTEGRITY_H
10 changes: 10 additions & 0 deletions LoopLibCore/sources/pdfpagecontentprocessor.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -540,6 +540,11 @@ void PDFPageContentProcessor::performProcessTextSequence(const TextSequence& tex
Q_UNUSED(order);
}

void PDFPageContentProcessor::performTextGlyphsUnresolved(const TextSequence& textSequence)
{
Q_UNUSED(textSequence);
}

bool PDFPageContentProcessor::isContentKindSuppressed(ContentKind kind) const
{
Q_UNUSED(kind);
Expand Down Expand Up @@ -3406,6 +3411,11 @@ void PDFPageContentProcessor::operatorCompatibilityEnd()

void PDFPageContentProcessor::drawText(const TextSequence& textSequence)
{
if (!textSequence.unresolvedCodes.empty())
{
performTextGlyphsUnresolved(textSequence);
}

if (textSequence.items.empty())
{
// Do not display empty text
Expand Down
4 changes: 4 additions & 0 deletions LoopLibCore/sources/pdfpagecontentprocessor.h
Original file line number Diff line number Diff line change
Expand Up @@ -753,6 +753,10 @@ class LOOPLIBCORESHARED_EXPORT PDFPageContentProcessor : public PDFRenderErrorRe
/// Implement to respond to text sequence processing
virtual void performProcessTextSequence(const TextSequence& textSequence, ProcessOrder order);

/// Called once per shown string for which at least one code resolved to no
/// glyph, including strings that produce no drawable items at all.
virtual void performTextGlyphsUnresolved(const TextSequence& textSequence);

enum class ContentKind
{
Shapes, ///< General shapes (they can be also shaded / tiled)
Expand Down
Loading
Loading