mirror of
https://github.com/JakubMelka/PDF4QT.git
synced 2025-02-05 20:03:40 +01:00
Implementation of font drawing - first part
This commit is contained in:
parent
21e125bd40
commit
81be3cbd21
@ -262,6 +262,17 @@ const PDFObject& PDFObjectStorage::getObject(PDFObjectReference reference) const
|
||||
}
|
||||
}
|
||||
|
||||
QByteArray PDFDocumentDataLoaderDecorator::readName(const PDFObject& object)
|
||||
{
|
||||
const PDFObject& dereferencedObject = m_document->getObject(object);
|
||||
if (dereferencedObject.isName())
|
||||
{
|
||||
return dereferencedObject.getString();
|
||||
}
|
||||
|
||||
return QByteArray();
|
||||
}
|
||||
|
||||
PDFInteger PDFDocumentDataLoaderDecorator::readInteger(const PDFObject& object, PDFInteger defaultValue) const
|
||||
{
|
||||
const PDFObject& dereferencedObject = m_document->getObject(object);
|
||||
@ -458,4 +469,14 @@ bool PDFDocumentDataLoaderDecorator::readBooleanFromDictionary(const PDFDictiona
|
||||
return defaultValue;
|
||||
}
|
||||
|
||||
QByteArray PDFDocumentDataLoaderDecorator::readNameFromDictionary(const PDFDictionary* dictionary, const char* key)
|
||||
{
|
||||
if (dictionary->hasKey(key))
|
||||
{
|
||||
return readName(dictionary->get(key));
|
||||
}
|
||||
|
||||
return QByteArray();
|
||||
}
|
||||
|
||||
} // namespace pdf
|
||||
|
@ -87,6 +87,11 @@ public:
|
||||
inline explicit PDFDocumentDataLoaderDecorator(const PDFDocument* document) : m_document(document) { }
|
||||
inline ~PDFDocumentDataLoaderDecorator() = default;
|
||||
|
||||
/// Reads a name from the object, if it is possible. If object is not a name,
|
||||
/// then empty byte array is returned.
|
||||
/// \param object Object, can be an indirect reference to object (it is dereferenced)
|
||||
QByteArray readName(const PDFObject& object);
|
||||
|
||||
/// Reads an integer from the object, if it is possible.
|
||||
/// \param object Object, can be an indirect reference to object (it is dereferenced)
|
||||
/// \param defaultValue Default value
|
||||
@ -217,6 +222,11 @@ public:
|
||||
/// \param defaultValue Default value
|
||||
bool readBooleanFromDictionary(const PDFDictionary* dictionary, const char* key, bool defaultValue) const;
|
||||
|
||||
/// Reads a name from dictionary. If dictionary entry doesn't exist, or error occurs, empty byte array is returned.
|
||||
/// \param dictionary Dictionary containing desired data
|
||||
/// \param key Entry key
|
||||
QByteArray readNameFromDictionary(const PDFDictionary* dictionary, const char* key);
|
||||
|
||||
private:
|
||||
const PDFDocument* m_document;
|
||||
};
|
||||
|
@ -46,7 +46,11 @@ public:
|
||||
PDFDoc, ///< Appendix D, Section D.1/D.2, PDFDocEncoding
|
||||
MacExpert, ///< Appendix D, Section D.3, MacExpertEncoding
|
||||
Symbol, ///< Appendix D, Section D.4, Symbol Set and Encoding
|
||||
ZapfDingbats ///< Appendix D, Section D.5, Zapf Dingbats Encoding
|
||||
ZapfDingbats, ///< Appendix D, Section D.5, Zapf Dingbats Encoding
|
||||
|
||||
// Following encodings are used for internal use only and are not a part of PDF reference
|
||||
Custom,
|
||||
Invalid
|
||||
};
|
||||
|
||||
/// Converts byte array to the unicode string using specified encoding
|
||||
@ -73,11 +77,11 @@ public:
|
||||
/// \param stream Stream, from which date/time is read
|
||||
static QDateTime convertToDateTime(const QByteArray& stream);
|
||||
|
||||
private:
|
||||
/// Returns conversion table for particular encoding
|
||||
/// \param encoding Encoding
|
||||
static const encoding::EncodingTable* getTableForEncoding(Encoding encoding);
|
||||
|
||||
private:
|
||||
/// Returns true, if byte array has UTF-16BE unicode marking bytes at the
|
||||
/// stream start. If they are present, then byte stream is probably encoded
|
||||
/// as unicode.
|
||||
|
@ -16,6 +16,9 @@
|
||||
// along with PDFForQt. If not, see <https://www.gnu.org/licenses/>.
|
||||
|
||||
#include "pdffont.h"
|
||||
#include "pdfdocument.h"
|
||||
#include "pdfparser.h"
|
||||
#include "pdfnametounicode.h"
|
||||
|
||||
namespace pdf
|
||||
{
|
||||
@ -25,4 +28,295 @@ PDFFont::PDFFont()
|
||||
|
||||
}
|
||||
|
||||
PDFFontPointer PDFFont::createFont(const PDFObject& object, const PDFDocument* document)
|
||||
{
|
||||
const PDFObject& dereferencedFontDictionary = document->getObject(object);
|
||||
if (!dereferencedFontDictionary.isDictionary())
|
||||
{
|
||||
throw PDFParserException(PDFTranslationContext::tr("Font object must be a dictionary."));
|
||||
}
|
||||
|
||||
const PDFDictionary* fontDictionary = dereferencedFontDictionary.getDictionary();
|
||||
PDFDocumentDataLoaderDecorator fontLoader(document);
|
||||
|
||||
// TODO: Fonts - implement all types of the font
|
||||
// First, determine the font subtype
|
||||
constexpr const std::array<std::pair<const char*, FontType>, 2> fontTypes = {
|
||||
std::pair<const char*, FontType>{ "Type1", FontType::Type1 },
|
||||
std::pair<const char*, FontType>{ "TrueType", FontType::TrueType }
|
||||
};
|
||||
|
||||
const FontType fontType = fontLoader.readEnumByName(fontDictionary->get("Subtype"), fontTypes.cbegin(), fontTypes.cend(), FontType::Invalid);
|
||||
if (fontType == FontType::Invalid)
|
||||
{
|
||||
throw PDFParserException(PDFTranslationContext::tr("Invalid font type."));
|
||||
}
|
||||
|
||||
QByteArray name = fontLoader.readNameFromDictionary(fontDictionary, "Name");
|
||||
QByteArray baseFont = fontLoader.readNameFromDictionary(fontDictionary, "BaseFont");
|
||||
const PDFInteger firstChar = fontLoader.readIntegerFromDictionary(fontDictionary, "FirstChar", 0);
|
||||
const PDFInteger lastChar = fontLoader.readIntegerFromDictionary(fontDictionary, "LastChar", 255);
|
||||
std::vector<PDFInteger> widths = fontLoader.readIntegerArrayFromDictionary(fontDictionary, "Widths");
|
||||
|
||||
// Read standard font
|
||||
constexpr const std::array<std::pair<const char*, StandardFontType>, 14> standardFonts = {
|
||||
std::pair<const char*, StandardFontType>{ "Times-Roman", StandardFontType::TimesRoman },
|
||||
std::pair<const char*, StandardFontType>{ "Times-Bold", StandardFontType::TimesRomanBold },
|
||||
std::pair<const char*, StandardFontType>{ "Times-Italic", StandardFontType::TimesRomanItalics },
|
||||
std::pair<const char*, StandardFontType>{ "Times-BoldItalic", StandardFontType::TimesRomanBoldItalics },
|
||||
std::pair<const char*, StandardFontType>{ "Helvetica", StandardFontType::Helvetica },
|
||||
std::pair<const char*, StandardFontType>{ "Helvetica-Bold", StandardFontType::HelveticaBold },
|
||||
std::pair<const char*, StandardFontType>{ "Helvetica-Oblique", StandardFontType::HelveticaOblique },
|
||||
std::pair<const char*, StandardFontType>{ "Helvetica-BoldOblique", StandardFontType::HelveticaBoldOblique },
|
||||
std::pair<const char*, StandardFontType>{ "Courier", StandardFontType::Courier },
|
||||
std::pair<const char*, StandardFontType>{ "Courier-Bold", StandardFontType::CourierBold },
|
||||
std::pair<const char*, StandardFontType>{ "Courier-Oblique", StandardFontType::CourierOblique },
|
||||
std::pair<const char*, StandardFontType>{ "Courier-BoldOblique", StandardFontType::CourierBoldOblique },
|
||||
std::pair<const char*, StandardFontType>{ "Symbol", StandardFontType::Symbol },
|
||||
std::pair<const char*, StandardFontType>{ "ZapfDingbats", StandardFontType::ZapfDingbats }
|
||||
};
|
||||
const StandardFontType standardFont = fontLoader.readEnumByName(fontDictionary->get("BaseFont"), standardFonts.cbegin(), standardFonts.cend(), StandardFontType::Invalid);
|
||||
|
||||
// Read Font Descriptor
|
||||
// TODO: Read font descriptor
|
||||
|
||||
// Read Font Encoding
|
||||
// The font encoding for the simple font is determined by this algorithm:
|
||||
// 1) Try to use Encoding dictionary to determine base encoding
|
||||
// (it can be MacRomanEncoding, MacExpertEncoding, WinAnsiEncoding or StandardEncoding)
|
||||
// 2) If it is not present, then try to obtain built-in encoding from the font file (usually, this is not possible)
|
||||
// 3) Use default encoding for the font depending on the font type
|
||||
// - one of the 14 base fonts - use builtin encoding for the font type
|
||||
// - TrueType - use WinAnsiEncoding
|
||||
// - all others - use StandardEncoding
|
||||
// 4) Merge with Differences, if present
|
||||
// 5) Fill missing characters from StandardEncoding
|
||||
|
||||
// TODO: Read font encoding from the font file
|
||||
PDFEncoding::Encoding encoding = PDFEncoding::Encoding::Invalid;
|
||||
encoding::EncodingTable simpleFontEncodingTable = { };
|
||||
switch (fontType)
|
||||
{
|
||||
case FontType::Type1:
|
||||
case FontType::TrueType:
|
||||
{
|
||||
bool hasDifferences = false;
|
||||
encoding::EncodingTable differences = { };
|
||||
|
||||
if (fontDictionary->hasKey("Encoding"))
|
||||
{
|
||||
constexpr const std::array<std::pair<const char*, PDFEncoding::Encoding>, 3> encodings = {
|
||||
std::pair<const char*, PDFEncoding::Encoding>{ "MacRomanEncoding", PDFEncoding::Encoding::MacRoman },
|
||||
std::pair<const char*, PDFEncoding::Encoding>{ "MacExpertEncoding", PDFEncoding::Encoding::MacExpert },
|
||||
std::pair<const char*, PDFEncoding::Encoding>{ "WinAnsiEncoding", PDFEncoding::Encoding::WinAnsi }
|
||||
};
|
||||
|
||||
const PDFObject& encodingObject = document->getObject(fontDictionary->get("Encoding"));
|
||||
if (encodingObject.isName())
|
||||
{
|
||||
// Decode name of the encoding
|
||||
encoding = fontLoader.readEnumByName(encodingObject, encodings.cbegin(), encodings.cend(), PDFEncoding::Encoding::Invalid);
|
||||
}
|
||||
else if (encodingObject.isDictionary())
|
||||
{
|
||||
// Dictionary with base encoding and differences (all optional)
|
||||
const PDFDictionary* encodingDictionary = encodingObject.getDictionary();
|
||||
if (encodingDictionary->hasKey("BaseEncoding"))
|
||||
{
|
||||
encoding = fontLoader.readEnumByName(encodingDictionary->get("BaseEncoding"), encodings.cbegin(), encodings.cend(), PDFEncoding::Encoding::Invalid);
|
||||
}
|
||||
else
|
||||
{
|
||||
// We get encoding for the standard font. If we have invalid standard font,
|
||||
// then we get standard encoding. So we shouldn't test it.
|
||||
encoding = getEncodingForStandardFont(standardFont);
|
||||
}
|
||||
|
||||
if (encodingDictionary->hasKey("Differences"))
|
||||
{
|
||||
const PDFObject& differencesArray = document->getObject(encodingDictionary->get("Differences"));
|
||||
if (differencesArray.isArray())
|
||||
{
|
||||
hasDifferences = true;
|
||||
const PDFArray* array = differencesArray.getArray();
|
||||
size_t currentOffset = 0;
|
||||
for (size_t i = 0, count = array->getCount(); i < count; ++i)
|
||||
{
|
||||
const PDFObject& item = document->getObject(array->getItem(i));
|
||||
if (item.isInt())
|
||||
{
|
||||
currentOffset = static_cast<size_t>(item.getInteger());
|
||||
}
|
||||
else if (item.isName())
|
||||
{
|
||||
if (currentOffset >= differences.size())
|
||||
{
|
||||
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());
|
||||
}
|
||||
differences[currentOffset] = character;
|
||||
|
||||
++currentOffset;
|
||||
}
|
||||
else
|
||||
{
|
||||
throw PDFParserException(PDFTranslationContext::tr("Invalid differences in encoding entry of the font."));
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
throw PDFParserException(PDFTranslationContext::tr("Invalid differences in encoding entry of the font."));
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
throw PDFParserException(PDFTranslationContext::tr("Invalid encoding entry of the font."));
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// We get encoding for the standard font. If we have invalid standard font,
|
||||
// then we get standard encoding. So we shouldn't test it.
|
||||
encoding = getEncodingForStandardFont(standardFont);
|
||||
}
|
||||
|
||||
if (encoding == PDFEncoding::Encoding::Invalid)
|
||||
{
|
||||
throw PDFParserException(PDFTranslationContext::tr("Invalid encoding entry of the font."));
|
||||
}
|
||||
|
||||
simpleFontEncodingTable = *PDFEncoding::getTableForEncoding(encoding);
|
||||
|
||||
// Fill in differences
|
||||
if (hasDifferences)
|
||||
{
|
||||
for (size_t i = 0; i < differences.size(); ++i)
|
||||
{
|
||||
if (!differences[i].isNull())
|
||||
{
|
||||
simpleFontEncodingTable[i] = differences[i];
|
||||
}
|
||||
}
|
||||
|
||||
// Set the encoding to custom
|
||||
encoding = PDFEncoding::Encoding::Custom;
|
||||
}
|
||||
|
||||
// Fill in missing characters from standard encoding
|
||||
const encoding::EncodingTable& standardEncoding = *PDFEncoding::getTableForEncoding(PDFEncoding::Encoding::Standard);
|
||||
for (size_t i = 0; i < standardEncoding.size(); ++i)
|
||||
{
|
||||
if ((simpleFontEncodingTable[i].isNull() || simpleFontEncodingTable[i] == QChar(QChar::SpecialCharacter::ReplacementCharacter)) &&
|
||||
(!standardEncoding[i].isNull() && standardEncoding[i] != QChar(QChar::SpecialCharacter::ReplacementCharacter)))
|
||||
{
|
||||
simpleFontEncodingTable[i] = standardEncoding[i];
|
||||
}
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
default:
|
||||
{
|
||||
Q_ASSERT(false);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
switch (fontType)
|
||||
{
|
||||
case FontType::Type1:
|
||||
return PDFFontPointer(new PDFType1Font(qMove(name), qMove(baseFont), firstChar, lastChar, qMove(widths), encoding, simpleFontEncodingTable, standardFont));
|
||||
|
||||
case FontType::TrueType:
|
||||
return PDFFontPointer(new PDFTrueTypeFont(qMove(name), qMove(baseFont), firstChar, lastChar, qMove(widths), encoding, simpleFontEncodingTable));
|
||||
|
||||
default:
|
||||
{
|
||||
Q_ASSERT(false);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// Read To Unicode
|
||||
// TODO: Read To Unicode
|
||||
|
||||
// Read Embedded fonts
|
||||
// TODO: Read embedded fonts
|
||||
return PDFFontPointer();
|
||||
}
|
||||
|
||||
PDFSimpleFont::PDFSimpleFont(QByteArray name,
|
||||
QByteArray baseFont,
|
||||
PDFInteger firstChar,
|
||||
PDFInteger lastChar,
|
||||
std::vector<PDFInteger> widths,
|
||||
PDFEncoding::Encoding encodingType,
|
||||
encoding::EncodingTable encoding) :
|
||||
m_name(qMove(name)),
|
||||
m_baseFont(qMove(baseFont)),
|
||||
m_firstChar(firstChar),
|
||||
m_lastChar(lastChar),
|
||||
m_widths(qMove(widths)),
|
||||
m_encodingType(encodingType),
|
||||
m_encoding(encoding)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
QRawFont PDFSimpleFont::getRealizedFont(PDFReal fontSize) const
|
||||
{
|
||||
// TODO: Fix font creation to use also embedded fonts, font descriptor, etc.
|
||||
QFont font(m_baseFont);
|
||||
font.setHintingPreference(QFont::PreferNoHinting);
|
||||
font.setPixelSize(fontSize);
|
||||
return QRawFont::fromFont(font, QFontDatabase::Any);
|
||||
}
|
||||
|
||||
QString PDFSimpleFont::getTextUsingEncoding(const QByteArray& byteArray) const
|
||||
{
|
||||
QString string;
|
||||
string.resize(byteArray.size(), QChar());
|
||||
|
||||
for (int i = 0, count = byteArray.size(); i < count; ++i)
|
||||
{
|
||||
string[i] = m_encoding[static_cast<uint8_t>(byteArray[i])];
|
||||
}
|
||||
|
||||
return string;
|
||||
}
|
||||
|
||||
PDFType1Font::PDFType1Font(QByteArray name,
|
||||
QByteArray baseFont,
|
||||
PDFInteger firstChar,
|
||||
PDFInteger lastChar,
|
||||
std::vector<PDFInteger> widths,
|
||||
PDFEncoding::Encoding encodingType,
|
||||
encoding::EncodingTable encoding,
|
||||
StandardFontType standardFontType) :
|
||||
PDFSimpleFont(qMove(name), qMove(baseFont), firstChar, lastChar, qMove(widths), encodingType, encoding),
|
||||
m_standardFontType(standardFontType)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
FontType PDFType1Font::getFontType() const
|
||||
{
|
||||
return FontType::Type1;
|
||||
}
|
||||
|
||||
FontType PDFTrueTypeFont::getFontType() const
|
||||
{
|
||||
return FontType::TrueType;
|
||||
}
|
||||
|
||||
} // namespace pdf
|
||||
|
@ -19,11 +19,15 @@
|
||||
#define PDFFONT_H
|
||||
|
||||
#include "pdfglobal.h"
|
||||
#include "pdfencoding.h"
|
||||
#include "pdfobject.h"
|
||||
|
||||
#include <QRawFont>
|
||||
#include <QSharedPointer>
|
||||
|
||||
namespace pdf
|
||||
{
|
||||
class PDFDocument;
|
||||
|
||||
enum class TextRenderingMode
|
||||
{
|
||||
@ -82,13 +86,131 @@ constexpr bool isTextRenderingModeClipped(TextRenderingMode mode)
|
||||
}
|
||||
}
|
||||
|
||||
enum class FontType
|
||||
{
|
||||
Invalid,
|
||||
Type1,
|
||||
TrueType
|
||||
};
|
||||
|
||||
/// Standard Type1 fonts
|
||||
enum class StandardFontType
|
||||
{
|
||||
Invalid,
|
||||
TimesRoman,
|
||||
TimesRomanBold,
|
||||
TimesRomanItalics,
|
||||
TimesRomanBoldItalics,
|
||||
Helvetica,
|
||||
HelveticaBold,
|
||||
HelveticaOblique,
|
||||
HelveticaBoldOblique,
|
||||
Courier,
|
||||
CourierBold,
|
||||
CourierOblique,
|
||||
CourierBoldOblique,
|
||||
Symbol,
|
||||
ZapfDingbats
|
||||
};
|
||||
|
||||
/// Returns builtin encoding for the standard font
|
||||
static constexpr PDFEncoding::Encoding getEncodingForStandardFont(StandardFontType standardFont)
|
||||
{
|
||||
switch (standardFont)
|
||||
{
|
||||
case StandardFontType::Symbol:
|
||||
return PDFEncoding::Encoding::Symbol;
|
||||
|
||||
case StandardFontType::ZapfDingbats:
|
||||
return PDFEncoding::Encoding::ZapfDingbats;
|
||||
|
||||
default:
|
||||
return PDFEncoding::Encoding::Standard;
|
||||
}
|
||||
}
|
||||
|
||||
class PDFFont;
|
||||
|
||||
using PDFFontPointer = QSharedPointer<PDFFont>;
|
||||
|
||||
/// Base class representing font in the PDF file
|
||||
class PDFFont
|
||||
{
|
||||
public:
|
||||
PDFFont();
|
||||
explicit PDFFont();
|
||||
virtual ~PDFFont() = default;
|
||||
|
||||
/// Returns the font type
|
||||
virtual FontType getFontType() const = 0;
|
||||
|
||||
/// Realizes the font (physical materialization of the font using pixel size,
|
||||
/// if font can't be realized, then empty QRawFont is returned).
|
||||
/// \param fontSize Size of the font
|
||||
virtual QRawFont getRealizedFont(PDFReal fontSize) const = 0;
|
||||
|
||||
/// Returns text using the font encoding
|
||||
/// \param byteArray Byte array with encoded string
|
||||
virtual QString getTextUsingEncoding(const QByteArray& byteArray) const = 0;
|
||||
|
||||
static PDFFontPointer createFont(const PDFObject& object, const PDFDocument* document);
|
||||
};
|
||||
|
||||
using PDFFontPointer = QSharedPointer<PDFFont>;
|
||||
/// Simple font, see PDF reference 1.7, chapter 5.5. Simple fonts have encoding table,
|
||||
/// which maps single-byte character to the glyph in the font.
|
||||
class PDFSimpleFont : public PDFFont
|
||||
{
|
||||
public:
|
||||
explicit PDFSimpleFont(QByteArray name,
|
||||
QByteArray baseFont,
|
||||
PDFInteger firstChar,
|
||||
PDFInteger lastChar,
|
||||
std::vector<PDFInteger> widths,
|
||||
PDFEncoding::Encoding encodingType,
|
||||
encoding::EncodingTable encoding);
|
||||
virtual ~PDFSimpleFont() override = default;
|
||||
|
||||
virtual QRawFont getRealizedFont(PDFReal fontSize) const override;
|
||||
virtual QString getTextUsingEncoding(const QByteArray& byteArray) const override;
|
||||
|
||||
protected:
|
||||
QByteArray m_name;
|
||||
QByteArray m_baseFont;
|
||||
PDFInteger m_firstChar;
|
||||
PDFInteger m_lastChar;
|
||||
std::vector<PDFInteger> m_widths;
|
||||
PDFEncoding::Encoding m_encodingType;
|
||||
encoding::EncodingTable m_encoding;
|
||||
};
|
||||
|
||||
class PDFType1Font : public PDFSimpleFont
|
||||
{
|
||||
public:
|
||||
explicit PDFType1Font(QByteArray name,
|
||||
QByteArray baseFont,
|
||||
PDFInteger firstChar,
|
||||
PDFInteger lastChar,
|
||||
std::vector<PDFInteger> widths,
|
||||
PDFEncoding::Encoding encodingType,
|
||||
encoding::EncodingTable encoding,
|
||||
StandardFontType standardFontType);
|
||||
virtual ~PDFType1Font() override = default;
|
||||
|
||||
virtual FontType getFontType() const override;
|
||||
|
||||
/// Returns the assigned standard font (or invalid, if font is not standard)
|
||||
StandardFontType getStandardFontType() const { return m_standardFontType; }
|
||||
|
||||
private:
|
||||
StandardFontType m_standardFontType; ///< Type of the standard font (or invalid, if it is not a standard font)
|
||||
};
|
||||
|
||||
class PDFTrueTypeFont : public PDFSimpleFont
|
||||
{
|
||||
public:
|
||||
using PDFSimpleFont::PDFSimpleFont;
|
||||
|
||||
virtual FontType getFontType() const override;
|
||||
};
|
||||
|
||||
} // namespace pdf
|
||||
|
||||
|
@ -153,20 +153,29 @@ PDFPageContentProcessor::PDFPageContentProcessor(const PDFPage* page, const PDFD
|
||||
m_page(page),
|
||||
m_document(document),
|
||||
m_colorSpaceDictionary(nullptr),
|
||||
m_fontDictionary(nullptr),
|
||||
m_textBeginEndState(0)
|
||||
{
|
||||
Q_ASSERT(page);
|
||||
Q_ASSERT(document);
|
||||
|
||||
const PDFObject& resources = m_document->getObject(m_page->getResources());
|
||||
if (resources.isDictionary() && resources.getDictionary()->hasKey(COLOR_SPACE_DICTIONARY))
|
||||
auto getDictionary = [this](const char* resourceName) -> const pdf::PDFDictionary*
|
||||
{
|
||||
const PDFObject& colorSpace = m_document->getObject(resources.getDictionary()->get(COLOR_SPACE_DICTIONARY));
|
||||
if (colorSpace.isDictionary())
|
||||
const PDFObject& resources = m_document->getObject(m_page->getResources());
|
||||
if (resources.isDictionary() && resources.getDictionary()->hasKey(resourceName))
|
||||
{
|
||||
m_colorSpaceDictionary = colorSpace.getDictionary();
|
||||
const PDFObject& resourceDictionary = m_document->getObject(resources.getDictionary()->get(resourceName));
|
||||
if (resourceDictionary.isDictionary())
|
||||
{
|
||||
return resourceDictionary.getDictionary();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return nullptr;
|
||||
};
|
||||
|
||||
m_colorSpaceDictionary = getDictionary(COLOR_SPACE_DICTIONARY);
|
||||
m_fontDictionary = getDictionary("Font");
|
||||
}
|
||||
|
||||
PDFPageContentProcessor::~PDFPageContentProcessor()
|
||||
@ -260,17 +269,21 @@ void PDFPageContentProcessor::performClipping(const QPainterPath& path, Qt::Fill
|
||||
Q_UNUSED(fillRule);
|
||||
}
|
||||
|
||||
void PDFPageContentProcessor::performUpdateGraphicsState(const PDFPageContentProcessor::PDFPageContentProcessorState& state)
|
||||
void PDFPageContentProcessor::performUpdateGraphicsState(const PDFPageContentProcessorState& state)
|
||||
{
|
||||
Q_UNUSED(state);
|
||||
if (state.getStateFlags().testFlag(PDFPageContentProcessorState::StateTextFont) ||
|
||||
state.getStateFlags().testFlag(PDFPageContentProcessorState::StateTextFontSize))
|
||||
{
|
||||
m_realizedFont.dirty();
|
||||
}
|
||||
}
|
||||
|
||||
void PDFPageContentProcessor::performSaveGraphicState(PDFPageContentProcessor::ProcessOrder order)
|
||||
void PDFPageContentProcessor::performSaveGraphicState(ProcessOrder order)
|
||||
{
|
||||
Q_UNUSED(order);
|
||||
}
|
||||
|
||||
void PDFPageContentProcessor::performRestoreGraphicState(PDFPageContentProcessor::ProcessOrder order)
|
||||
void PDFPageContentProcessor::performRestoreGraphicState(ProcessOrder order)
|
||||
{
|
||||
Q_UNUSED(order);
|
||||
}
|
||||
@ -689,6 +702,34 @@ void PDFPageContentProcessor::processCommand(const QByteArray& command)
|
||||
break;
|
||||
}
|
||||
|
||||
case Operator::TextShowTextString:
|
||||
{
|
||||
// Tj, show text string
|
||||
invokeOperator(&PDFPageContentProcessor::operatorTextShowTextString);
|
||||
break;
|
||||
}
|
||||
|
||||
case Operator::TextShowTextIndividualSpacing:
|
||||
{
|
||||
// TJ, show text, allow individual text spacing
|
||||
invokeOperator(&PDFPageContentProcessor::operatorTextShowTextIndividualSpacing);
|
||||
break;
|
||||
}
|
||||
|
||||
case Operator::TextNextLineShowText:
|
||||
{
|
||||
// ', move to the next line and show text ("string '" is equivalent to "T* string Tj")
|
||||
invokeOperator(&PDFPageContentProcessor::operatorTextNextLineShowText);
|
||||
break;
|
||||
}
|
||||
|
||||
case Operator::TextSetSpacingAndShowText:
|
||||
{
|
||||
// ", move to the next line, set spacing and show text (equivalent to sequence "w1 Tw w2 Tc string '")
|
||||
invokeOperator(&PDFPageContentProcessor::operatorTextSetSpacingAndShowText);
|
||||
break;
|
||||
}
|
||||
|
||||
case Operator::Invalid:
|
||||
{
|
||||
m_errorList.append(PDFRenderError(RenderErrorType::Error, PDFTranslationContext::tr("Unknown operator '%1'.").arg(QString::fromLatin1(command))));
|
||||
@ -902,7 +943,7 @@ void PDFPageContentProcessor::operatorSetLineDashPattern()
|
||||
updateGraphicState();
|
||||
}
|
||||
|
||||
void PDFPageContentProcessor::operatorSetRenderingIntent(PDFName intent)
|
||||
void PDFPageContentProcessor::operatorSetRenderingIntent(PDFOperandName intent)
|
||||
{
|
||||
m_graphicState.setRenderingIntent(intent.name);
|
||||
updateGraphicState();
|
||||
@ -915,7 +956,7 @@ void PDFPageContentProcessor::operatorSetFlatness(PDFReal flatness)
|
||||
updateGraphicState();
|
||||
}
|
||||
|
||||
void PDFPageContentProcessor::operatorSetGraphicState(PDFName dictionaryName)
|
||||
void PDFPageContentProcessor::operatorSetGraphicState(PDFOperandName dictionaryName)
|
||||
{
|
||||
const PDFObject& resources = m_page->getResources();
|
||||
if (resources.isDictionary())
|
||||
@ -1101,7 +1142,7 @@ PDFInteger PDFPageContentProcessor::readOperand<PDFInteger>(size_t index) const
|
||||
}
|
||||
|
||||
template<>
|
||||
PDFPageContentProcessor::PDFName PDFPageContentProcessor::readOperand<PDFPageContentProcessor::PDFName>(size_t index) const
|
||||
PDFPageContentProcessor::PDFOperandName PDFPageContentProcessor::readOperand<PDFPageContentProcessor::PDFOperandName>(size_t index) const
|
||||
{
|
||||
if (index < m_operands.size())
|
||||
{
|
||||
@ -1110,7 +1151,7 @@ PDFPageContentProcessor::PDFName PDFPageContentProcessor::readOperand<PDFPageCon
|
||||
switch (token.type)
|
||||
{
|
||||
case PDFLexicalAnalyzer::TokenType::Name:
|
||||
return PDFName{ token.data.toByteArray() };
|
||||
return PDFOperandName{ token.data.toByteArray() };
|
||||
|
||||
default:
|
||||
throw PDFRendererException(RenderErrorType::Error, PDFTranslationContext::tr("Can't read operand (name) on index %1. Operand is of type '%2'.").arg(index + 1).arg(PDFLexicalAnalyzer::getStringFromOperandType(token.type)));
|
||||
@ -1121,7 +1162,32 @@ PDFPageContentProcessor::PDFName PDFPageContentProcessor::readOperand<PDFPageCon
|
||||
throw PDFRendererException(RenderErrorType::Error, PDFTranslationContext::tr("Can't read operand (name) on index %1. Only %2 operands provided.").arg(index + 1).arg(m_operands.size()));
|
||||
}
|
||||
|
||||
return PDFName();
|
||||
return PDFOperandName();
|
||||
}
|
||||
|
||||
|
||||
template<>
|
||||
PDFPageContentProcessor::PDFOperandString PDFPageContentProcessor::readOperand<PDFPageContentProcessor::PDFOperandString>(size_t index) const
|
||||
{
|
||||
if (index < m_operands.size())
|
||||
{
|
||||
const PDFLexicalAnalyzer::Token& token = m_operands[index];
|
||||
|
||||
switch (token.type)
|
||||
{
|
||||
case PDFLexicalAnalyzer::TokenType::String:
|
||||
return PDFOperandString{ token.data.toByteArray() };
|
||||
|
||||
default:
|
||||
throw PDFRendererException(RenderErrorType::Error, PDFTranslationContext::tr("Can't read operand (string) on index %1. Operand is of type '%2'.").arg(index + 1).arg(PDFLexicalAnalyzer::getStringFromOperandType(token.type)));
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
throw PDFRendererException(RenderErrorType::Error, PDFTranslationContext::tr("Can't read operand (string) on index %1. Only %2 operands provided.").arg(index + 1).arg(m_operands.size()));
|
||||
}
|
||||
|
||||
return PDFOperandString();
|
||||
}
|
||||
|
||||
void PDFPageContentProcessor::operatorMoveCurrentPoint(PDFReal x, PDFReal y)
|
||||
@ -1275,7 +1341,7 @@ void PDFPageContentProcessor::operatorClipEvenOdd()
|
||||
}
|
||||
}
|
||||
|
||||
void PDFPageContentProcessor::operatorColorSetStrokingColorSpace(PDFPageContentProcessor::PDFName name)
|
||||
void PDFPageContentProcessor::operatorColorSetStrokingColorSpace(PDFPageContentProcessor::PDFOperandName name)
|
||||
{
|
||||
PDFColorSpacePointer colorSpace = PDFAbstractColorSpace::createColorSpace(m_colorSpaceDictionary, m_document, PDFObject::createName(std::make_shared<PDFString>(QByteArray(name.name))));
|
||||
if (colorSpace)
|
||||
@ -1291,7 +1357,7 @@ void PDFPageContentProcessor::operatorColorSetStrokingColorSpace(PDFPageContentP
|
||||
}
|
||||
}
|
||||
|
||||
void PDFPageContentProcessor::operatorColorSetFillingColorSpace(PDFName name)
|
||||
void PDFPageContentProcessor::operatorColorSetFillingColorSpace(PDFOperandName name)
|
||||
{
|
||||
PDFColorSpacePointer colorSpace = PDFAbstractColorSpace::createColorSpace(m_colorSpaceDictionary, m_document, PDFObject::createName(std::make_shared<PDFString>(QByteArray(name.name))));
|
||||
if (colorSpace)
|
||||
@ -1458,13 +1524,27 @@ void PDFPageContentProcessor::operatorTextSetLeading(PDFReal leading)
|
||||
updateGraphicState();
|
||||
}
|
||||
|
||||
void PDFPageContentProcessor::operatorTextSetFontAndFontSize(PDFPageContentProcessor::PDFName fontName, PDFReal fontSize)
|
||||
void PDFPageContentProcessor::operatorTextSetFontAndFontSize(PDFOperandName fontName, PDFReal fontSize)
|
||||
{
|
||||
Q_UNUSED(fontName);
|
||||
Q_UNUSED(fontSize);
|
||||
if (m_fontDictionary)
|
||||
{
|
||||
if (m_fontDictionary->hasKey(fontName.name))
|
||||
{
|
||||
PDFFontPointer font = PDFFont::createFont(m_fontDictionary->get(fontName.name), m_document);
|
||||
|
||||
// TODO: Implement this operator
|
||||
throw PDFRendererException(RenderErrorType::NotImplemented, PDFTranslationContext::tr("Set font not implemented."));
|
||||
m_graphicState.setTextFont(qMove(font));
|
||||
m_graphicState.setTextFontSize(fontSize);
|
||||
updateGraphicState();
|
||||
}
|
||||
else
|
||||
{
|
||||
throw PDFRendererException(RenderErrorType::Error, PDFTranslationContext::tr("Font '%1' not found in font dictionary.").arg(QString::fromLatin1(fontName.name)));
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
throw PDFRendererException(RenderErrorType::Error, PDFTranslationContext::tr("Invalid font dictionary."));
|
||||
}
|
||||
}
|
||||
|
||||
void PDFPageContentProcessor::operatorTextSetRenderMode(PDFInteger mode)
|
||||
@ -1533,6 +1613,105 @@ void PDFPageContentProcessor::operatorTextMoveByLeading()
|
||||
operatorTextMoveByOffset(0.0, m_graphicState.getTextLeading());
|
||||
}
|
||||
|
||||
void PDFPageContentProcessor::operatorTextShowTextString(PDFOperandString text)
|
||||
{
|
||||
if (m_graphicState.getTextFont())
|
||||
{
|
||||
QString textDecoded = m_graphicState.getTextFont()->getTextUsingEncoding(text.string);
|
||||
drawText(TextSequence::fromString(textDecoded));
|
||||
}
|
||||
else
|
||||
{
|
||||
throw PDFRendererException(RenderErrorType::Error, PDFTranslationContext::tr("Invalid font, text can't be printed."));
|
||||
}
|
||||
}
|
||||
|
||||
void PDFPageContentProcessor::operatorTextShowTextIndividualSpacing()
|
||||
{
|
||||
// Operand stack must be of this form [ ... text, number, text, number, number, text ... ]. We check it.
|
||||
|
||||
if (m_operands.size() < 2)
|
||||
{
|
||||
throw PDFRendererException(RenderErrorType::Error, PDFTranslationContext::tr("Invalid parameters of text operator with individual character spacing."));
|
||||
}
|
||||
|
||||
// Now, we have at least 2 arguments. Check we have an array
|
||||
if (m_operands[0].type != PDFLexicalAnalyzer::TokenType::ArrayStart ||
|
||||
m_operands[m_operands.size() - 1].type != PDFLexicalAnalyzer::TokenType::ArrayEnd)
|
||||
{
|
||||
throw PDFRendererException(RenderErrorType::Error, PDFTranslationContext::tr("Invalid line dash pattern."));
|
||||
}
|
||||
|
||||
if (m_graphicState.getTextFont())
|
||||
{
|
||||
TextSequence textSequence;
|
||||
|
||||
// We use simple heuristic to ensure reallocation doesn't occur too often
|
||||
textSequence.items.reserve(m_operands.size() * 4);
|
||||
|
||||
for (size_t i = 1, lastIndex = m_operands.size() - 1; i < lastIndex; ++i)
|
||||
{
|
||||
switch (m_operands[i].type)
|
||||
{
|
||||
case PDFLexicalAnalyzer::TokenType::Integer:
|
||||
{
|
||||
textSequence.items.push_back(TextSequenceItem(m_operands[i].data.value<PDFInteger>()));
|
||||
break;
|
||||
}
|
||||
|
||||
case PDFLexicalAnalyzer::TokenType::String:
|
||||
{
|
||||
QString string = m_graphicState.getTextFont()->getTextUsingEncoding(m_operands[i].data.toByteArray());
|
||||
std::transform(string.cbegin(), string.cend(), std::back_inserter(textSequence.items), [](const QChar character) { return TextSequenceItem(character); });
|
||||
break;
|
||||
}
|
||||
|
||||
default:
|
||||
{
|
||||
// Error - we have operand of different type
|
||||
throw PDFRendererException(RenderErrorType::Error, PDFTranslationContext::tr("Invalid operand of text show operator."));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
drawText(textSequence);
|
||||
}
|
||||
else
|
||||
{
|
||||
throw PDFRendererException(RenderErrorType::Error, PDFTranslationContext::tr("Invalid font, text can't be printed."));
|
||||
}
|
||||
}
|
||||
|
||||
void PDFPageContentProcessor::operatorTextNextLineShowText(PDFOperandString text)
|
||||
{
|
||||
operatorTextMoveByLeading();
|
||||
operatorTextShowTextString(qMove(text));
|
||||
}
|
||||
|
||||
void PDFPageContentProcessor::operatorTextSetSpacingAndShowText(PDFReal t_w, PDFReal t_c, PDFOperandString text)
|
||||
{
|
||||
m_graphicState.setTextWordSpacing(t_w);
|
||||
m_graphicState.setTextCharacterSpacing(t_c);
|
||||
updateGraphicState();
|
||||
|
||||
operatorTextNextLineShowText(qMove(text));
|
||||
}
|
||||
|
||||
void PDFPageContentProcessor::drawText(const TextSequence& textSequence)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
QRawFont PDFPageContentProcessor::getRealizedFontImpl() const
|
||||
{
|
||||
if (m_graphicState.getTextFont())
|
||||
{
|
||||
return m_graphicState.getTextFont()->getRealizedFont(m_graphicState.getTextFontSize());
|
||||
}
|
||||
|
||||
return QRawFont();
|
||||
}
|
||||
|
||||
PDFPageContentProcessor::PDFPageContentProcessorState::PDFPageContentProcessorState() :
|
||||
m_currentTransformationMatrix(),
|
||||
m_fillColorSpace(),
|
||||
@ -1810,4 +1989,12 @@ void PDFPageContentProcessor::PDFPageContentProcessorState::setTextCharacterSpac
|
||||
}
|
||||
}
|
||||
|
||||
PDFPageContentProcessor::TextSequence PDFPageContentProcessor::TextSequence::fromString(const QString& string)
|
||||
{
|
||||
TextSequence result;
|
||||
result.items.reserve(string.size());
|
||||
std::transform(string.cbegin(), string.cend(), std::back_inserter(result.items), [](const QChar character) { return TextSequenceItem(character); });
|
||||
return result;
|
||||
}
|
||||
|
||||
} // namespace pdf
|
||||
|
@ -22,8 +22,10 @@
|
||||
#include "pdfparser.h"
|
||||
#include "pdfcolorspaces.h"
|
||||
#include "pdffont.h"
|
||||
#include "pdfutils.h"
|
||||
|
||||
#include <QMatrix>
|
||||
#include <QRawFont>
|
||||
#include <QPainterPath>
|
||||
#include <QSharedPointer>
|
||||
|
||||
@ -348,6 +350,28 @@ protected:
|
||||
StateFlags m_stateFlags;
|
||||
};
|
||||
|
||||
/// Item of the text sequence (either single character, or advance)
|
||||
struct TextSequenceItem
|
||||
{
|
||||
inline explicit TextSequenceItem() = default;
|
||||
inline explicit TextSequenceItem(QChar character) : character(character), advance(0) { }
|
||||
inline explicit TextSequenceItem(PDFInteger advance) : character(), advance(advance) { }
|
||||
|
||||
inline bool isCharacter() const { return !character.isNull(); }
|
||||
inline bool isAdvance() const { return advance != 0; }
|
||||
inline bool isNull() const { return !isCharacter() && !isAdvance(); }
|
||||
|
||||
QChar character;
|
||||
PDFInteger advance = 0;
|
||||
};
|
||||
|
||||
struct TextSequence
|
||||
{
|
||||
static TextSequence fromString(const QString& string);
|
||||
|
||||
std::vector<TextSequenceItem> items;
|
||||
};
|
||||
|
||||
enum class ProcessOrder
|
||||
{
|
||||
BeforeOperation,
|
||||
@ -398,11 +422,17 @@ private:
|
||||
void processCommand(const QByteArray& command);
|
||||
|
||||
/// Wrapper for PDF Name
|
||||
struct PDFName
|
||||
struct PDFOperandName
|
||||
{
|
||||
QByteArray name;
|
||||
};
|
||||
|
||||
/// Wrapper for PDF String
|
||||
struct PDFOperandString
|
||||
{
|
||||
QByteArray string;
|
||||
};
|
||||
|
||||
template<typename T>
|
||||
T readOperand(size_t index) const;
|
||||
|
||||
@ -413,7 +443,10 @@ private:
|
||||
PDFInteger readOperand<PDFInteger>(size_t index) const;
|
||||
|
||||
template<>
|
||||
PDFName readOperand<PDFName>(size_t index) const;
|
||||
PDFOperandName readOperand<PDFOperandName>(size_t index) const;
|
||||
|
||||
template<>
|
||||
PDFOperandString readOperand<PDFOperandString>(size_t index) const;
|
||||
|
||||
template<size_t index, typename T>
|
||||
inline T readOperand() const { return readOperand<T>(index); }
|
||||
@ -484,9 +517,9 @@ private:
|
||||
void operatorSetLineJoin(PDFInteger lineJoin); ///< j, sets the line join
|
||||
void operatorSetMitterLimit(PDFReal mitterLimit); ///< M, sets the mitter limit
|
||||
void operatorSetLineDashPattern(); ///< d, sets the line dash pattern
|
||||
void operatorSetRenderingIntent(PDFName intent); ///< ri, sets the rendering intent
|
||||
void operatorSetRenderingIntent(PDFOperandName intent); ///< ri, sets the rendering intent
|
||||
void operatorSetFlatness(PDFReal flatness); ///< i, sets the flattness (number in range from 0 to 100)
|
||||
void operatorSetGraphicState(PDFName dictionaryName); ///< gs, sets the whole graphic state (stored in resource dictionary)
|
||||
void operatorSetGraphicState(PDFOperandName dictionaryName); ///< gs, sets the whole graphic state (stored in resource dictionary)
|
||||
|
||||
// Special graphic state: q, Q, cm
|
||||
void operatorSaveGraphicState(); ///< q, saves the graphic state
|
||||
@ -518,8 +551,8 @@ private:
|
||||
void operatorClipEvenOdd(); ///< W*, modify current clipping path by intersecting it with current path using "Even-odd rule"
|
||||
|
||||
// Color: CS, cs, SC, SCN, sc, scn, G, g, RG, rg, K, k
|
||||
void operatorColorSetStrokingColorSpace(PDFName name); ///< CS, set current color space for stroking operations
|
||||
void operatorColorSetFillingColorSpace(PDFName name); ///< cs, set current color space for filling operations
|
||||
void operatorColorSetStrokingColorSpace(PDFOperandName name); ///< CS, set current color space for stroking operations
|
||||
void operatorColorSetFillingColorSpace(PDFOperandName name); ///< cs, set current color space for filling operations
|
||||
void operatorColorSetStrokingColor(); ///< SC, set current stroking color
|
||||
void operatorColorSetStrokingColorN(); ///< SCN, same as SC, but also supports Pattern, Separation, DeviceN and ICCBased color spaces
|
||||
void operatorColorSetFillingColor(); ///< sc, set current filling color
|
||||
@ -540,7 +573,7 @@ private:
|
||||
void operatorTextSetWordSpacing(PDFReal wordSpacing); ///< Tw, set text word spacing
|
||||
void operatorTextSetHorizontalScale(PDFReal horizontalScaling); ///< Tz, set text horizontal scaling (in percents, 100% = normal scaling)
|
||||
void operatorTextSetLeading(PDFReal leading); ///< TL, set text leading
|
||||
void operatorTextSetFontAndFontSize(PDFName fontName, PDFReal fontSize); ///< Tf, set text font (name from dictionary) and its size
|
||||
void operatorTextSetFontAndFontSize(PDFOperandName fontName, PDFReal fontSize); ///< Tf, set text font (name from dictionary) and its size
|
||||
void operatorTextSetRenderMode(PDFInteger mode); ///< Tr, set text render mode
|
||||
void operatorTextSetRise(PDFReal rise); ///< Ts, set text rise
|
||||
|
||||
@ -550,9 +583,25 @@ private:
|
||||
void operatorTextSetMatrix(PDFReal a, PDFReal b, PDFReal c, PDFReal d, PDFReal e, PDFReal f); ///< Tm, set text matrix
|
||||
void operatorTextMoveByLeading(); ///< T*, moves text by leading, equivalent to 0 leading Td
|
||||
|
||||
// Text showing: Tj, TJ, ', "
|
||||
void operatorTextShowTextString(PDFOperandString text); ///< Tj, show text string
|
||||
void operatorTextShowTextIndividualSpacing(); ///< TJ, show text, allow individual text spacing
|
||||
void operatorTextNextLineShowText(PDFOperandString text); ///< ', move to the next line and show text ("string '" is equivalent to "T* string Tj")
|
||||
void operatorTextSetSpacingAndShowText(PDFReal t_w, PDFReal t_c, PDFOperandString text); ///< ", move to the next line, set spacing and show text (equivalent to sequence "w1 Tw w2 Tc string '")
|
||||
|
||||
// Draws the text using the text sequence
|
||||
void drawText(const TextSequence& textSequence);
|
||||
|
||||
/// Returns realized font
|
||||
const QRawFont& getRealizedFont() { return m_realizedFont.get(this, &PDFPageContentProcessor::getRealizedFontImpl); }
|
||||
|
||||
/// Returns realized font (or empty font, if font can't be realized)
|
||||
QRawFont getRealizedFontImpl() const;
|
||||
|
||||
const PDFPage* m_page;
|
||||
const PDFDocument* m_document;
|
||||
const PDFDictionary* m_colorSpaceDictionary;
|
||||
const PDFDictionary* m_fontDictionary;
|
||||
|
||||
// Default color spaces
|
||||
PDFColorSpacePointer m_deviceGrayColorSpace;
|
||||
@ -576,6 +625,9 @@ private:
|
||||
|
||||
/// Nesting level of the begin/end of text object
|
||||
int m_textBeginEndState;
|
||||
|
||||
/// Actually realized physical font
|
||||
PDFCachedItem<QRawFont> m_realizedFont;
|
||||
};
|
||||
|
||||
} // namespace pdf
|
||||
|
@ -102,6 +102,8 @@ void PDFPainter::performUpdateGraphicsState(const PDFPageContentProcessorState&
|
||||
{
|
||||
m_currentBrush.dirty();
|
||||
}
|
||||
|
||||
PDFPageContentProcessor::performUpdateGraphicsState(state);
|
||||
}
|
||||
|
||||
void PDFPainter::performSaveGraphicState(ProcessOrder order)
|
||||
|
Loading…
x
Reference in New Issue
Block a user