mirror of https://github.com/JakubMelka/PDF4QT.git
Type 3 font implementation
This commit is contained in:
parent
4239b6686a
commit
32dae5dc3c
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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
|
||||
|
|
Loading…
Reference in New Issue