Type 3 font implementation

This commit is contained in:
Jakub Melka 2019-07-14 19:03:15 +02:00
parent 4239b6686a
commit 32dae5dc3c
4 changed files with 413 additions and 94 deletions

View File

@ -313,18 +313,49 @@ PDFFont::PDFFont(FontDescriptor fontDescriptor) :
}
/// Implementation of the PDFRealizedFont class using PIMPL pattern
class PDFRealizedFontImpl
class IRealizedFontImpl
{
public:
explicit PDFRealizedFontImpl();
~PDFRealizedFontImpl();
explicit IRealizedFontImpl() = default;
virtual ~IRealizedFontImpl() = default;
/// Fills the text sequence by interpreting byte array according font data and
/// 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, PDFRenderErrorReporter* reporter);
virtual void fillTextSequence(const QByteArray& byteArray, TextSequence& textSequence, PDFRenderErrorReporter* reporter) = 0;
/// Returns true, if font has horizontal writing system
virtual bool isHorizontalWritingSystem() const = 0;
};
/// Implementation of the PDFRealizedFont class using PIMPL pattern for Type 3 fonts
class PDFRealizedType3FontImpl : public IRealizedFontImpl
{
public:
explicit PDFRealizedType3FontImpl(PDFFontPointer parentFont, PDFReal pixelSize) : m_parentFont(parentFont), m_pixelSize(pixelSize) { }
virtual ~PDFRealizedType3FontImpl() override = default;
virtual void fillTextSequence(const QByteArray& byteArray, TextSequence& textSequence, PDFRenderErrorReporter* reporter) override;
virtual bool isHorizontalWritingSystem() const override;
private:
/// Pixel size of the font
PDFReal m_pixelSize = 0.0;
/// Parent font
PDFFontPointer m_parentFont;
};
/// Implementation of the PDFRealizedFont class using PIMPL pattern
class PDFRealizedFontImpl : public IRealizedFontImpl
{
public:
explicit PDFRealizedFontImpl();
virtual ~PDFRealizedFontImpl();
virtual void fillTextSequence(const QByteArray& byteArray, TextSequence& textSequence, PDFRenderErrorReporter* reporter) override;
virtual bool isHorizontalWritingSystem() const override { return !m_isVertical; }
static constexpr const PDFReal PIXEL_SIZE_MULTIPLIER = 100.0;
@ -590,61 +621,69 @@ void PDFRealizedFont::fillTextSequence(const QByteArray& byteArray, TextSequence
bool PDFRealizedFont::isHorizontalWritingSystem() const
{
return !m_impl->m_isVertical;
return m_impl->isHorizontalWritingSystem();
}
PDFRealizedFontPointer PDFRealizedFont::createRealizedFont(PDFFontPointer font, PDFReal pixelSize)
{
PDFRealizedFontPointer result;
std::unique_ptr<PDFRealizedFontImpl> implPtr(new PDFRealizedFontImpl());
PDFRealizedFontImpl* impl = implPtr.get();
impl->m_parentFont = font;
impl->m_pixelSize = pixelSize;
const FontDescriptor* descriptor = font->getFontDescriptor();
if (descriptor->isEmbedded())
if (font->getFontType() == FontType::Type3)
{
PDFRealizedFontImpl::checkFreeTypeError(FT_Init_FreeType(&impl->m_library));
const QByteArray* embeddedFontData = descriptor->getEmbeddedFontData();
Q_ASSERT(embeddedFontData);
impl->m_embeddedFontData = *embeddedFontData;
// At this time, embedded font data should not be empty!
Q_ASSERT(!impl->m_embeddedFontData.isEmpty());
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::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()));
result.reset(new PDFRealizedFont(new PDFRealizedType3FontImpl(font, pixelSize)));
}
else
{
StandardFontType standardFontType = StandardFontType::Invalid;
if (font->getFontType() == FontType::Type1)
std::unique_ptr<PDFRealizedFontImpl> implPtr(new PDFRealizedFontImpl());
PDFRealizedFontImpl* impl = implPtr.get();
impl->m_parentFont = font;
impl->m_pixelSize = pixelSize;
const FontDescriptor* descriptor = font->getFontDescriptor();
if (descriptor->isEmbedded())
{
Q_ASSERT(dynamic_cast<const PDFType1Font*>(font.get()));
const PDFType1Font* type1Font = static_cast<const PDFType1Font*>(font.get());
standardFontType = type1Font->getStandardFontType();
PDFRealizedFontImpl::checkFreeTypeError(FT_Init_FreeType(&impl->m_library));
const QByteArray* embeddedFontData = descriptor->getEmbeddedFontData();
Q_ASSERT(embeddedFontData);
impl->m_embeddedFontData = *embeddedFontData;
// At this time, embedded font data should not be empty!
Q_ASSERT(!impl->m_embeddedFontData.isEmpty());
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::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()));
}
const PDFSystemFontInfoStorage* fontStorage = PDFSystemFontInfoStorage::getInstance();
impl->m_systemFontData = fontStorage->loadFont(descriptor, standardFontType);
if (impl->m_systemFontData.isEmpty())
else
{
throw PDFParserException(PDFTranslationContext::tr("Can't load system font '%1'.").arg(QString::fromLatin1(descriptor->fontName)));
}
StandardFontType standardFontType = StandardFontType::Invalid;
if (font->getFontType() == FontType::Type1)
{
Q_ASSERT(dynamic_cast<const PDFType1Font*>(font.get()));
const PDFType1Font* type1Font = static_cast<const PDFType1Font*>(font.get());
standardFontType = type1Font->getStandardFontType();
}
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::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()));
const PDFSystemFontInfoStorage* fontStorage = PDFSystemFontInfoStorage::getInstance();
impl->m_systemFontData = fontStorage->loadFont(descriptor, standardFontType);
if (impl->m_systemFontData.isEmpty())
{
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));
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::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()));
}
}
return result;
@ -718,12 +757,12 @@ PDFFontPointer PDFFont::createFont(const PDFObject& object, const PDFDocument* d
const PDFDictionary* fontDictionary = dereferencedFontDictionary.getDictionary();
PDFDocumentDataLoaderDecorator fontLoader(document);
// TODO: Fonts - Implement Type 3 font
// First, determine the font subtype
constexpr const std::array<std::pair<const char*, FontType>, 3> fontTypes = {
constexpr const std::array<std::pair<const char*, FontType>, 4> fontTypes = {
std::pair<const char*, FontType>{ "Type0", FontType::Type0 },
std::pair<const char*, FontType>{ "Type1", FontType::Type1 },
std::pair<const char*, FontType>{ "TrueType", FontType::TrueType }
std::pair<const char*, FontType>{ "TrueType", FontType::TrueType },
std::pair<const char*, FontType>{ "Type3", FontType::Type3}
};
const FontType fontType = fontLoader.readEnumByName(fontDictionary->get("Subtype"), fontTypes.cbegin(), fontTypes.cend(), FontType::Invalid);
@ -1128,6 +1167,83 @@ PDFFontPointer PDFFont::createFont(const PDFObject& object, const PDFDocument* d
return PDFFontPointer(new PDFType0Font(qMove(fontDescriptor), qMove(cmap), qMove(toUnicodeCMap), qMove(cidToGidMapper), defaultWidth, qMove(advances)));
}
case FontType::Type3:
{
// Read the font matrix
std::vector<PDFReal> fontMatrixValues = fontLoader.readNumberArrayFromDictionary(fontDictionary, "FontMatrix");
if (fontMatrixValues.size() != 6)
{
throw PDFParserException(PDFTranslationContext::tr("Invalid Type 3 font matrix."));
}
QMatrix fontMatrix(fontMatrixValues[0], fontMatrixValues[1], fontMatrixValues[2], fontMatrixValues[3], fontMatrixValues[4], fontMatrixValues[5]);
PDFObject charProcs = document->getObject(fontDictionary->get("CharProcs"));
if (!charProcs.isDictionary())
{
throw PDFParserException(PDFTranslationContext::tr("Invalid Type 3 font character content streams."));
}
const PDFDictionary* charProcsDictionary = charProcs.getDictionary();
PDFInteger firstChar = fontLoader.readIntegerFromDictionary(fontDictionary, "FirstChar", -1);
PDFInteger lastChar = fontLoader.readIntegerFromDictionary(fontDictionary, "LastChar", -1);
if (firstChar < 0 || lastChar > 255 || firstChar > lastChar)
{
throw PDFParserException(PDFTranslationContext::tr("Invalid Type 3 font character range (from %1 to %2).").arg(firstChar).arg(lastChar));
}
const PDFObject& encoding = document->getObject(fontDictionary->get("Encoding"));
if (!encoding.isDictionary())
{
throw PDFParserException(PDFTranslationContext::tr("Invalid Type 3 font encoding."));
}
const PDFDictionary* encodingDictionary = encoding.getDictionary();
const PDFObject& differences = document->getObject(encodingDictionary->get("Differences"));
if (!differences.isArray())
{
throw PDFParserException(PDFTranslationContext::tr("Invalid Type 3 font encoding."));
}
std::map<int, QByteArray> characterContentStreams;
const PDFArray* differencesArray = differences.getArray();
size_t currentOffset = 0;
for (size_t i = 0, count = differencesArray->getCount(); i < count; ++i)
{
const PDFObject& item = document->getObject(differencesArray->getItem(i));
if (item.isInt())
{
currentOffset = static_cast<size_t>(item.getInteger());
}
else if (item.isName())
{
if (currentOffset > 255)
{
throw PDFParserException(PDFTranslationContext::tr("Invalid differences in encoding entry of type 3 font."));
}
QByteArray characterName = item.getString();
const PDFObject& characterContentStreamObject = document->getObject(charProcsDictionary->get(characterName));
if (characterContentStreamObject.isStream())
{
QByteArray contentStream = document->getDecodedStream(characterContentStreamObject.getStream());
characterContentStreams[currentOffset] = qMove(contentStream);
}
++currentOffset;
}
else
{
throw PDFParserException(PDFTranslationContext::tr("Invalid differences in encoding entry of type 3 font."));
}
}
std::vector<PDFReal> widths = fontLoader.readNumberArrayFromDictionary(fontDictionary, "Widths");
return PDFFontPointer(new PDFType3Font(qMove(fontDescriptor), firstChar, lastChar, fontMatrix, qMove(characterContentStreams), qMove(widths), document->getObject(fontDictionary->get("Resources"))));
}
default:
{
Q_ASSERT(false);
@ -1707,4 +1823,86 @@ PDFReal PDFType0Font::getGlyphAdvance(CID cid) const
return m_defaultAdvance;
}
PDFType3Font::PDFType3Font(FontDescriptor fontDescriptor,
int firstCharacterIndex,
int lastCharacterIndex,
QMatrix fontMatrix,
std::map<int, QByteArray>&& characterContentStreams,
std::vector<double>&& widths,
const PDFObject& resources) :
PDFFont(qMove(fontDescriptor)),
m_firstCharacterIndex(firstCharacterIndex),
m_lastCharacterIndex(lastCharacterIndex),
m_fontMatrix(fontMatrix),
m_characterContentStreams(qMove(characterContentStreams)),
m_widths(qMove(widths)),
m_resources(resources)
{
}
FontType PDFType3Font::getFontType() const
{
return FontType::Type3;
}
double PDFType3Font::getWidth(int characterIndex) const
{
if (characterIndex >= m_firstCharacterIndex && characterIndex <= m_lastCharacterIndex)
{
size_t index = characterIndex - m_firstCharacterIndex;
if (index < m_widths.size())
{
return m_widths[index];
}
}
return 0.0;
}
const QByteArray* PDFType3Font::getContentStream(int characterIndex) const
{
auto it = m_characterContentStreams.find(characterIndex);
if (it != m_characterContentStreams.cend())
{
return &it->second;
}
return nullptr;
}
void PDFRealizedType3FontImpl::fillTextSequence(const QByteArray& byteArray, TextSequence& textSequence, PDFRenderErrorReporter* reporter)
{
Q_ASSERT(dynamic_cast<const PDFType3Font*>(m_parentFont.get()));
const PDFType3Font* parentFont = static_cast<const PDFType3Font*>(m_parentFont.get());
textSequence.items.reserve(byteArray.size());
for (int i = 0, characterCount = byteArray.size(); i < characterCount; ++i)
{
int index = static_cast<uint8_t>(byteArray[i]);
const QByteArray* contentStream = parentFont->getContentStream(index);
const double width = parentFont->getWidth(index);
if (contentStream)
{
textSequence.items.emplace_back(contentStream, width);
}
else
{
// Report error, and add advance, if we have it
reporter->reportRenderError(RenderErrorType::Warning, PDFTranslationContext::tr("Content stream for type 3 font character code '%1' not found.").arg(index));
if (width > 0.0)
{
textSequence.items.emplace_back(width);
}
}
}
}
bool PDFRealizedType3FontImpl::isHorizontalWritingSystem() const
{
return true;
}
} // namespace pdf

View File

@ -23,6 +23,7 @@
#include "pdfobject.h"
#include <QFont>
#include <QMatrix>
#include <QSharedPointer>
#include <unordered_map>
@ -57,12 +58,15 @@ struct TextSequenceItem
inline explicit TextSequenceItem() = default;
inline explicit TextSequenceItem(const QPainterPath* glyph, QChar character, PDFReal advance) : glyph(glyph), character(character), advance(advance) { }
inline explicit TextSequenceItem(PDFReal advance) : character(), advance(advance) { }
inline explicit TextSequenceItem(const QByteArray* characterContentStream, double advance) : characterContentStream(characterContentStream), advance(advance) { }
inline bool isContentStream() const { return characterContentStream; }
inline bool isCharacter() const { return glyph; }
inline bool isAdvance() const { return advance != 0.0; }
inline bool isNull() const { return !isCharacter() && !isAdvance(); }
const QPainterPath* glyph = nullptr;
const QByteArray* characterContentStream = nullptr;
QChar character;
PDFReal advance = 0;
};
@ -122,7 +126,8 @@ enum class FontType
Invalid,
Type0,
Type1,
TrueType
TrueType,
Type3
};
/// Standard Type1 fonts
@ -205,7 +210,7 @@ class PDFFont;
using PDFFontPointer = QSharedPointer<PDFFont>;
class PDFRealizedFont;
class PDFRealizedFontImpl;
class IRealizedFontImpl;
using PDFRealizedFontPointer = QSharedPointer<PDFRealizedFont>;
@ -232,9 +237,9 @@ public:
private:
/// Constructs new realized font
explicit PDFRealizedFont(PDFRealizedFontImpl* impl) : m_impl(impl) { }
explicit PDFRealizedFont(IRealizedFontImpl* impl) : m_impl(impl) { }
PDFRealizedFontImpl* m_impl;
IRealizedFontImpl* m_impl;
};
/// Base class representing font in the PDF file
@ -330,6 +335,38 @@ public:
virtual FontType getFontType() const override;
};
class PDFType3Font : public PDFFont
{
public:
explicit PDFType3Font(FontDescriptor fontDescriptor,
int firstCharacterIndex,
int lastCharacterIndex,
QMatrix fontMatrix,
std::map<int, QByteArray>&& characterContentStreams,
std::vector<double>&& widths,
const PDFObject& resources);
virtual FontType getFontType() const override;
/// Returns width of the character. If character doesn't exist, then zero is returned.
double getWidth(int characterIndex) const;
/// Return content stream for the character. If character doesn't exist, then nullptr
/// is returned.
const QByteArray* getContentStream(int characterIndex) const;
const QMatrix& getFontMatrix() const { return m_fontMatrix; }
const PDFObject& getResources() const { return m_resources; }
private:
int m_firstCharacterIndex;
int m_lastCharacterIndex;
QMatrix m_fontMatrix;
std::map<int, QByteArray> m_characterContentStreams;
std::vector<double> m_widths;
PDFObject m_resources;
};
/// Font cache which caches both fonts, and realized fonts. Cache has individual limit
/// for fonts, and realized fonts.
class PDFFontCache

View File

@ -609,6 +609,20 @@ void PDFPageContentProcessor::processCommand(const QByteArray& command)
break;
}
case Operator::Type3FontSetOffset:
{
// d0, set width information, see PDF 1.7 Reference, Table 5.10
invokeOperator(&PDFPageContentProcessor::operatorType3FontSetOffset);
break;
}
case Operator::Type3FontSetOffsetAndBB:
{
// d1, set offset and glyph bounding box
invokeOperator(&PDFPageContentProcessor::operatorType3FontSetOffsetAndBB);
break;
}
case Operator::ColorSetStrokingColorSpace:
{
// CS, set current color space for stroking operations
@ -1459,6 +1473,16 @@ void PDFPageContentProcessor::operatorClipEvenOdd()
}
}
void PDFPageContentProcessor::operatorType3FontSetOffset(PDFReal wx, PDFReal wy)
{
performSetCharWidth(wx, wy);
}
void PDFPageContentProcessor::operatorType3FontSetOffsetAndBB(PDFReal wx, PDFReal wy, PDFReal llx, PDFReal lly, PDFReal urx, PDFReal ury)
{
performSetCacheDevice(wx, wy, llx, lly, urx, ury);
}
void PDFPageContentProcessor::operatorColorSetStrokingColorSpace(PDFPageContentProcessor::PDFOperandName name)
{
PDFColorSpacePointer colorSpace = PDFAbstractColorSpace::createColorSpace(m_colorSpaceDictionary, m_document, PDFObject::createName(std::make_shared<PDFString>(QByteArray(name.name))));
@ -2044,6 +2068,7 @@ void PDFPageContentProcessor::drawText(const TextSequence& textSequence)
const PDFRealizedFontPointer& font = getRealizedFont();
if (font)
{
const bool isType3Font = m_graphicState.getTextFont()->getFontType() == FontType::Type3;
const PDFReal fontSize = m_graphicState.getTextFontSize();
const PDFReal horizontalScaling = m_graphicState.getTextHorizontalScaling() * 0.01; // Horizontal scaling is in percents
const PDFReal characterSpacing = m_graphicState.getTextCharacterSpacing();
@ -2061,63 +2086,112 @@ void PDFPageContentProcessor::drawText(const TextSequence& textSequence)
QMatrix adjustMatrix(horizontalScaling, 0.0, 0.0, 1.0, 0.0, textRise);
QMatrix textMatrix = m_graphicState.getTextMatrix();
for (const TextSequenceItem& item : textSequence.items)
if (!isType3Font)
{
PDFReal displacementX = 0.0;
PDFReal displacementY = 0.0;
if (item.isCharacter())
for (const TextSequenceItem& item : textSequence.items)
{
QChar character = item.character;
QPointF advance = isHorizontalWritingSystem ? QPointF(item.advance, 0) : QPointF(0, item.advance);
PDFReal displacementX = 0.0;
PDFReal displacementY = 0.0;
// First, compute the advance
const PDFReal additionalAdvance = (character == QChar(QChar::Space)) ? wordSpacing + characterSpacing : characterSpacing;
if (isHorizontalWritingSystem)
if (item.isCharacter())
{
advance.rx() += additionalAdvance;
}
else
{
advance.ry() += additionalAdvance;
}
advance.rx() *= horizontalScaling;
QChar character = item.character;
QPointF advance = isHorizontalWritingSystem ? QPointF(item.advance, 0) : QPointF(0, item.advance);
// Then get the glyph path and paint it
if (item.glyph)
{
const QPainterPath& glyphPath = *item.glyph;
if (!glyphPath.isEmpty())
// First, compute the advance
const PDFReal additionalAdvance = (character == QChar(QChar::Space)) ? wordSpacing + characterSpacing : characterSpacing;
if (isHorizontalWritingSystem)
{
QMatrix textRenderingMatrix = adjustMatrix * textMatrix;
QPainterPath transformedGlyph = textRenderingMatrix.map(glyphPath);
performPathPainting(transformedGlyph, stroke, fill, true, transformedGlyph.fillRule());
advance.rx() += additionalAdvance;
}
else
{
advance.ry() += additionalAdvance;
}
advance.rx() *= horizontalScaling;
if (clipped)
// Then get the glyph path and paint it
if (item.glyph)
{
const QPainterPath& glyphPath = *item.glyph;
if (!glyphPath.isEmpty())
{
// Clipping is enabled, we must transform to the device coordinates
QMatrix toDeviceSpaceTransform = textRenderingMatrix * m_graphicState.getCurrentTransformationMatrix();
m_textClippingPath = m_textClippingPath.united(toDeviceSpaceTransform.map(glyphPath));
QMatrix textRenderingMatrix = adjustMatrix * textMatrix;
QPainterPath transformedGlyph = textRenderingMatrix.map(glyphPath);
performPathPainting(transformedGlyph, stroke, fill, true, transformedGlyph.fillRule());
if (clipped)
{
// Clipping is enabled, we must transform to the device coordinates
QMatrix toDeviceSpaceTransform = textRenderingMatrix * m_graphicState.getCurrentTransformationMatrix();
m_textClippingPath = m_textClippingPath.united(toDeviceSpaceTransform.map(glyphPath));
}
}
}
displacementX = advance.x();
displacementY = advance.y();
}
else if (item.isAdvance())
{
if (horizontalScaling)
{
displacementX = -item.advance * 0.001 * fontSize * horizontalScaling;
}
else
{
displacementY = -item.advance * 0.001 * fontSize;
}
}
displacementX = advance.x();
displacementY = advance.y();
textMatrix.translate(displacementX, displacementY);
}
else if (item.isAdvance())
}
else
{
// Type 3 Font
PDFPageContentProcessorStateGuard guard(this);
Q_ASSERT(dynamic_cast<const PDFType3Font*>(m_graphicState.getTextFont().get()));
const PDFType3Font* parentFont = static_cast<const PDFType3Font*>(m_graphicState.getTextFont().get());
QMatrix fontMatrix = parentFont->getFontMatrix();
PDFObject resources = parentFont->getResources();
if (!resources.isNull())
{
if (horizontalScaling)
{
displacementX = -item.advance * 0.001 * fontSize * horizontalScaling;
}
else
{
displacementY = -item.advance * 0.001 * fontSize;
}
initDictionaries(resources);
}
textMatrix.translate(displacementX, displacementY);
QMatrix fontAdjustedMatrix = fontMatrix * adjustMatrix;
for (const TextSequenceItem& item : textSequence.items)
{
// First, compute horizontal advance
qreal displacementX = 0.0;
if (item.advance != 0.0)
{
qreal ry = 0.0;
fontAdjustedMatrix.map(item.advance, 0.0, &displacementX, &ry);
}
if (item.characterContentStream)
{
PDFPageContentProcessorStateGuard guard(this);
// We must clear operands, because we are processing a new content stream
m_operands.clear();
QMatrix worldMatrix = fontAdjustedMatrix * textMatrix * m_graphicState.getCurrentTransformationMatrix();
m_graphicState.setCurrentTransformationMatrix(worldMatrix);
updateGraphicState();
processContent(*item.characterContentStream);
}
textMatrix.translate(displacementX, 0.0);
}
}
m_graphicState.setTextMatrix(textMatrix);

View File

@ -396,6 +396,12 @@ protected:
/// Implement to react on marked content end
virtual void performMarkedContentEnd();
/// Implement to react on set char width request
virtual void performSetCharWidth(PDFReal wx, PDFReal wy) { }
/// Implement to react on set cache device request
virtual void performSetCacheDevice(PDFReal wx, PDFReal wy, PDFReal llx, PDFReal lly, PDFReal urx, PDFReal ury) { }
/// Returns current graphic state
const PDFPageContentProcessorState* getGraphicState() const { return &m_graphicState; }
@ -598,6 +604,10 @@ private:
void operatorClipWinding(); ///< W, modify current clipping path by intersecting it with current path using "Non zero winding number rule"
void operatorClipEvenOdd(); ///< W*, modify current clipping path by intersecting it with current path using "Even-odd rule"
// Type 3 font: d0, d1
void operatorType3FontSetOffset(PDFReal wx, PDFReal wy); ///< d0, set width information, see PDF 1.7 Reference, Table 5.10
void operatorType3FontSetOffsetAndBB(PDFReal wx, PDFReal wy, PDFReal llx, PDFReal lly, PDFReal urx, PDFReal ury); ///< d1, set offset and glyph bounding box
// Color: CS, cs, SC, SCN, sc, scn, G, g, RG, rg, K, k
void operatorColorSetStrokingColorSpace(PDFOperandName name); ///< CS, set current color space for stroking operations
void operatorColorSetFillingColorSpace(PDFOperandName name); ///< cs, set current color space for filling operations