mirror of
https://github.com/JakubMelka/PDF4QT.git
synced 2025-02-11 09:10:36 +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
|
PDFInteger PDFDocumentDataLoaderDecorator::readInteger(const PDFObject& object, PDFInteger defaultValue) const
|
||||||
{
|
{
|
||||||
const PDFObject& dereferencedObject = m_document->getObject(object);
|
const PDFObject& dereferencedObject = m_document->getObject(object);
|
||||||
@ -458,4 +469,14 @@ bool PDFDocumentDataLoaderDecorator::readBooleanFromDictionary(const PDFDictiona
|
|||||||
return defaultValue;
|
return defaultValue;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
QByteArray PDFDocumentDataLoaderDecorator::readNameFromDictionary(const PDFDictionary* dictionary, const char* key)
|
||||||
|
{
|
||||||
|
if (dictionary->hasKey(key))
|
||||||
|
{
|
||||||
|
return readName(dictionary->get(key));
|
||||||
|
}
|
||||||
|
|
||||||
|
return QByteArray();
|
||||||
|
}
|
||||||
|
|
||||||
} // namespace pdf
|
} // namespace pdf
|
||||||
|
@ -87,6 +87,11 @@ public:
|
|||||||
inline explicit PDFDocumentDataLoaderDecorator(const PDFDocument* document) : m_document(document) { }
|
inline explicit PDFDocumentDataLoaderDecorator(const PDFDocument* document) : m_document(document) { }
|
||||||
inline ~PDFDocumentDataLoaderDecorator() = default;
|
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.
|
/// Reads an integer from the object, if it is possible.
|
||||||
/// \param object Object, can be an indirect reference to object (it is dereferenced)
|
/// \param object Object, can be an indirect reference to object (it is dereferenced)
|
||||||
/// \param defaultValue Default value
|
/// \param defaultValue Default value
|
||||||
@ -217,6 +222,11 @@ public:
|
|||||||
/// \param defaultValue Default value
|
/// \param defaultValue Default value
|
||||||
bool readBooleanFromDictionary(const PDFDictionary* dictionary, const char* key, bool defaultValue) const;
|
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:
|
private:
|
||||||
const PDFDocument* m_document;
|
const PDFDocument* m_document;
|
||||||
};
|
};
|
||||||
|
@ -46,7 +46,11 @@ public:
|
|||||||
PDFDoc, ///< Appendix D, Section D.1/D.2, PDFDocEncoding
|
PDFDoc, ///< Appendix D, Section D.1/D.2, PDFDocEncoding
|
||||||
MacExpert, ///< Appendix D, Section D.3, MacExpertEncoding
|
MacExpert, ///< Appendix D, Section D.3, MacExpertEncoding
|
||||||
Symbol, ///< Appendix D, Section D.4, Symbol Set and Encoding
|
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
|
/// Converts byte array to the unicode string using specified encoding
|
||||||
@ -73,11 +77,11 @@ public:
|
|||||||
/// \param stream Stream, from which date/time is read
|
/// \param stream Stream, from which date/time is read
|
||||||
static QDateTime convertToDateTime(const QByteArray& stream);
|
static QDateTime convertToDateTime(const QByteArray& stream);
|
||||||
|
|
||||||
private:
|
|
||||||
/// Returns conversion table for particular encoding
|
/// Returns conversion table for particular encoding
|
||||||
/// \param encoding Encoding
|
/// \param encoding Encoding
|
||||||
static const encoding::EncodingTable* getTableForEncoding(Encoding encoding);
|
static const encoding::EncodingTable* getTableForEncoding(Encoding encoding);
|
||||||
|
|
||||||
|
private:
|
||||||
/// Returns true, if byte array has UTF-16BE unicode marking bytes at the
|
/// 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
|
/// stream start. If they are present, then byte stream is probably encoded
|
||||||
/// as unicode.
|
/// as unicode.
|
||||||
|
@ -16,6 +16,9 @@
|
|||||||
// along with PDFForQt. If not, see <https://www.gnu.org/licenses/>.
|
// along with PDFForQt. If not, see <https://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
#include "pdffont.h"
|
#include "pdffont.h"
|
||||||
|
#include "pdfdocument.h"
|
||||||
|
#include "pdfparser.h"
|
||||||
|
#include "pdfnametounicode.h"
|
||||||
|
|
||||||
namespace pdf
|
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
|
} // namespace pdf
|
||||||
|
@ -19,11 +19,15 @@
|
|||||||
#define PDFFONT_H
|
#define PDFFONT_H
|
||||||
|
|
||||||
#include "pdfglobal.h"
|
#include "pdfglobal.h"
|
||||||
|
#include "pdfencoding.h"
|
||||||
|
#include "pdfobject.h"
|
||||||
|
|
||||||
|
#include <QRawFont>
|
||||||
#include <QSharedPointer>
|
#include <QSharedPointer>
|
||||||
|
|
||||||
namespace pdf
|
namespace pdf
|
||||||
{
|
{
|
||||||
|
class PDFDocument;
|
||||||
|
|
||||||
enum class TextRenderingMode
|
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
|
class PDFFont
|
||||||
{
|
{
|
||||||
public:
|
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
|
} // namespace pdf
|
||||||
|
|
||||||
|
@ -153,20 +153,29 @@ PDFPageContentProcessor::PDFPageContentProcessor(const PDFPage* page, const PDFD
|
|||||||
m_page(page),
|
m_page(page),
|
||||||
m_document(document),
|
m_document(document),
|
||||||
m_colorSpaceDictionary(nullptr),
|
m_colorSpaceDictionary(nullptr),
|
||||||
|
m_fontDictionary(nullptr),
|
||||||
m_textBeginEndState(0)
|
m_textBeginEndState(0)
|
||||||
{
|
{
|
||||||
Q_ASSERT(page);
|
Q_ASSERT(page);
|
||||||
Q_ASSERT(document);
|
Q_ASSERT(document);
|
||||||
|
|
||||||
|
auto getDictionary = [this](const char* resourceName) -> const pdf::PDFDictionary*
|
||||||
|
{
|
||||||
const PDFObject& resources = m_document->getObject(m_page->getResources());
|
const PDFObject& resources = m_document->getObject(m_page->getResources());
|
||||||
if (resources.isDictionary() && resources.getDictionary()->hasKey(COLOR_SPACE_DICTIONARY))
|
if (resources.isDictionary() && resources.getDictionary()->hasKey(resourceName))
|
||||||
{
|
{
|
||||||
const PDFObject& colorSpace = m_document->getObject(resources.getDictionary()->get(COLOR_SPACE_DICTIONARY));
|
const PDFObject& resourceDictionary = m_document->getObject(resources.getDictionary()->get(resourceName));
|
||||||
if (colorSpace.isDictionary())
|
if (resourceDictionary.isDictionary())
|
||||||
{
|
{
|
||||||
m_colorSpaceDictionary = colorSpace.getDictionary();
|
return resourceDictionary.getDictionary();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return nullptr;
|
||||||
|
};
|
||||||
|
|
||||||
|
m_colorSpaceDictionary = getDictionary(COLOR_SPACE_DICTIONARY);
|
||||||
|
m_fontDictionary = getDictionary("Font");
|
||||||
}
|
}
|
||||||
|
|
||||||
PDFPageContentProcessor::~PDFPageContentProcessor()
|
PDFPageContentProcessor::~PDFPageContentProcessor()
|
||||||
@ -260,17 +269,21 @@ void PDFPageContentProcessor::performClipping(const QPainterPath& path, Qt::Fill
|
|||||||
Q_UNUSED(fillRule);
|
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);
|
Q_UNUSED(order);
|
||||||
}
|
}
|
||||||
|
|
||||||
void PDFPageContentProcessor::performRestoreGraphicState(PDFPageContentProcessor::ProcessOrder order)
|
void PDFPageContentProcessor::performRestoreGraphicState(ProcessOrder order)
|
||||||
{
|
{
|
||||||
Q_UNUSED(order);
|
Q_UNUSED(order);
|
||||||
}
|
}
|
||||||
@ -689,6 +702,34 @@ void PDFPageContentProcessor::processCommand(const QByteArray& command)
|
|||||||
break;
|
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:
|
case Operator::Invalid:
|
||||||
{
|
{
|
||||||
m_errorList.append(PDFRenderError(RenderErrorType::Error, PDFTranslationContext::tr("Unknown operator '%1'.").arg(QString::fromLatin1(command))));
|
m_errorList.append(PDFRenderError(RenderErrorType::Error, PDFTranslationContext::tr("Unknown operator '%1'.").arg(QString::fromLatin1(command))));
|
||||||
@ -902,7 +943,7 @@ void PDFPageContentProcessor::operatorSetLineDashPattern()
|
|||||||
updateGraphicState();
|
updateGraphicState();
|
||||||
}
|
}
|
||||||
|
|
||||||
void PDFPageContentProcessor::operatorSetRenderingIntent(PDFName intent)
|
void PDFPageContentProcessor::operatorSetRenderingIntent(PDFOperandName intent)
|
||||||
{
|
{
|
||||||
m_graphicState.setRenderingIntent(intent.name);
|
m_graphicState.setRenderingIntent(intent.name);
|
||||||
updateGraphicState();
|
updateGraphicState();
|
||||||
@ -915,7 +956,7 @@ void PDFPageContentProcessor::operatorSetFlatness(PDFReal flatness)
|
|||||||
updateGraphicState();
|
updateGraphicState();
|
||||||
}
|
}
|
||||||
|
|
||||||
void PDFPageContentProcessor::operatorSetGraphicState(PDFName dictionaryName)
|
void PDFPageContentProcessor::operatorSetGraphicState(PDFOperandName dictionaryName)
|
||||||
{
|
{
|
||||||
const PDFObject& resources = m_page->getResources();
|
const PDFObject& resources = m_page->getResources();
|
||||||
if (resources.isDictionary())
|
if (resources.isDictionary())
|
||||||
@ -1101,7 +1142,7 @@ PDFInteger PDFPageContentProcessor::readOperand<PDFInteger>(size_t index) const
|
|||||||
}
|
}
|
||||||
|
|
||||||
template<>
|
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())
|
if (index < m_operands.size())
|
||||||
{
|
{
|
||||||
@ -1110,7 +1151,7 @@ PDFPageContentProcessor::PDFName PDFPageContentProcessor::readOperand<PDFPageCon
|
|||||||
switch (token.type)
|
switch (token.type)
|
||||||
{
|
{
|
||||||
case PDFLexicalAnalyzer::TokenType::Name:
|
case PDFLexicalAnalyzer::TokenType::Name:
|
||||||
return PDFName{ token.data.toByteArray() };
|
return PDFOperandName{ token.data.toByteArray() };
|
||||||
|
|
||||||
default:
|
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)));
|
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()));
|
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)
|
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))));
|
PDFColorSpacePointer colorSpace = PDFAbstractColorSpace::createColorSpace(m_colorSpaceDictionary, m_document, PDFObject::createName(std::make_shared<PDFString>(QByteArray(name.name))));
|
||||||
if (colorSpace)
|
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))));
|
PDFColorSpacePointer colorSpace = PDFAbstractColorSpace::createColorSpace(m_colorSpaceDictionary, m_document, PDFObject::createName(std::make_shared<PDFString>(QByteArray(name.name))));
|
||||||
if (colorSpace)
|
if (colorSpace)
|
||||||
@ -1458,13 +1524,27 @@ void PDFPageContentProcessor::operatorTextSetLeading(PDFReal leading)
|
|||||||
updateGraphicState();
|
updateGraphicState();
|
||||||
}
|
}
|
||||||
|
|
||||||
void PDFPageContentProcessor::operatorTextSetFontAndFontSize(PDFPageContentProcessor::PDFName fontName, PDFReal fontSize)
|
void PDFPageContentProcessor::operatorTextSetFontAndFontSize(PDFOperandName fontName, PDFReal fontSize)
|
||||||
{
|
{
|
||||||
Q_UNUSED(fontName);
|
if (m_fontDictionary)
|
||||||
Q_UNUSED(fontSize);
|
{
|
||||||
|
if (m_fontDictionary->hasKey(fontName.name))
|
||||||
|
{
|
||||||
|
PDFFontPointer font = PDFFont::createFont(m_fontDictionary->get(fontName.name), m_document);
|
||||||
|
|
||||||
// TODO: Implement this operator
|
m_graphicState.setTextFont(qMove(font));
|
||||||
throw PDFRendererException(RenderErrorType::NotImplemented, PDFTranslationContext::tr("Set font not implemented."));
|
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)
|
void PDFPageContentProcessor::operatorTextSetRenderMode(PDFInteger mode)
|
||||||
@ -1533,6 +1613,105 @@ void PDFPageContentProcessor::operatorTextMoveByLeading()
|
|||||||
operatorTextMoveByOffset(0.0, m_graphicState.getTextLeading());
|
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() :
|
PDFPageContentProcessor::PDFPageContentProcessorState::PDFPageContentProcessorState() :
|
||||||
m_currentTransformationMatrix(),
|
m_currentTransformationMatrix(),
|
||||||
m_fillColorSpace(),
|
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
|
} // namespace pdf
|
||||||
|
@ -22,8 +22,10 @@
|
|||||||
#include "pdfparser.h"
|
#include "pdfparser.h"
|
||||||
#include "pdfcolorspaces.h"
|
#include "pdfcolorspaces.h"
|
||||||
#include "pdffont.h"
|
#include "pdffont.h"
|
||||||
|
#include "pdfutils.h"
|
||||||
|
|
||||||
#include <QMatrix>
|
#include <QMatrix>
|
||||||
|
#include <QRawFont>
|
||||||
#include <QPainterPath>
|
#include <QPainterPath>
|
||||||
#include <QSharedPointer>
|
#include <QSharedPointer>
|
||||||
|
|
||||||
@ -348,6 +350,28 @@ protected:
|
|||||||
StateFlags m_stateFlags;
|
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
|
enum class ProcessOrder
|
||||||
{
|
{
|
||||||
BeforeOperation,
|
BeforeOperation,
|
||||||
@ -398,11 +422,17 @@ private:
|
|||||||
void processCommand(const QByteArray& command);
|
void processCommand(const QByteArray& command);
|
||||||
|
|
||||||
/// Wrapper for PDF Name
|
/// Wrapper for PDF Name
|
||||||
struct PDFName
|
struct PDFOperandName
|
||||||
{
|
{
|
||||||
QByteArray name;
|
QByteArray name;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/// Wrapper for PDF String
|
||||||
|
struct PDFOperandString
|
||||||
|
{
|
||||||
|
QByteArray string;
|
||||||
|
};
|
||||||
|
|
||||||
template<typename T>
|
template<typename T>
|
||||||
T readOperand(size_t index) const;
|
T readOperand(size_t index) const;
|
||||||
|
|
||||||
@ -413,7 +443,10 @@ private:
|
|||||||
PDFInteger readOperand<PDFInteger>(size_t index) const;
|
PDFInteger readOperand<PDFInteger>(size_t index) const;
|
||||||
|
|
||||||
template<>
|
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>
|
template<size_t index, typename T>
|
||||||
inline T readOperand() const { return readOperand<T>(index); }
|
inline T readOperand() const { return readOperand<T>(index); }
|
||||||
@ -484,9 +517,9 @@ private:
|
|||||||
void operatorSetLineJoin(PDFInteger lineJoin); ///< j, sets the line join
|
void operatorSetLineJoin(PDFInteger lineJoin); ///< j, sets the line join
|
||||||
void operatorSetMitterLimit(PDFReal mitterLimit); ///< M, sets the mitter limit
|
void operatorSetMitterLimit(PDFReal mitterLimit); ///< M, sets the mitter limit
|
||||||
void operatorSetLineDashPattern(); ///< d, sets the line dash pattern
|
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 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
|
// Special graphic state: q, Q, cm
|
||||||
void operatorSaveGraphicState(); ///< q, saves the graphic state
|
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"
|
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
|
// 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 operatorColorSetStrokingColorSpace(PDFOperandName name); ///< CS, set current color space for stroking operations
|
||||||
void operatorColorSetFillingColorSpace(PDFName name); ///< cs, set current color space for filling operations
|
void operatorColorSetFillingColorSpace(PDFOperandName name); ///< cs, set current color space for filling operations
|
||||||
void operatorColorSetStrokingColor(); ///< SC, set current stroking color
|
void operatorColorSetStrokingColor(); ///< SC, set current stroking color
|
||||||
void operatorColorSetStrokingColorN(); ///< SCN, same as SC, but also supports Pattern, Separation, DeviceN and ICCBased color spaces
|
void operatorColorSetStrokingColorN(); ///< SCN, same as SC, but also supports Pattern, Separation, DeviceN and ICCBased color spaces
|
||||||
void operatorColorSetFillingColor(); ///< sc, set current filling color
|
void operatorColorSetFillingColor(); ///< sc, set current filling color
|
||||||
@ -540,7 +573,7 @@ private:
|
|||||||
void operatorTextSetWordSpacing(PDFReal wordSpacing); ///< Tw, set text word spacing
|
void operatorTextSetWordSpacing(PDFReal wordSpacing); ///< Tw, set text word spacing
|
||||||
void operatorTextSetHorizontalScale(PDFReal horizontalScaling); ///< Tz, set text horizontal scaling (in percents, 100% = normal scaling)
|
void operatorTextSetHorizontalScale(PDFReal horizontalScaling); ///< Tz, set text horizontal scaling (in percents, 100% = normal scaling)
|
||||||
void operatorTextSetLeading(PDFReal leading); ///< TL, set text leading
|
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 operatorTextSetRenderMode(PDFInteger mode); ///< Tr, set text render mode
|
||||||
void operatorTextSetRise(PDFReal rise); ///< Ts, set text rise
|
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 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
|
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 PDFPage* m_page;
|
||||||
const PDFDocument* m_document;
|
const PDFDocument* m_document;
|
||||||
const PDFDictionary* m_colorSpaceDictionary;
|
const PDFDictionary* m_colorSpaceDictionary;
|
||||||
|
const PDFDictionary* m_fontDictionary;
|
||||||
|
|
||||||
// Default color spaces
|
// Default color spaces
|
||||||
PDFColorSpacePointer m_deviceGrayColorSpace;
|
PDFColorSpacePointer m_deviceGrayColorSpace;
|
||||||
@ -576,6 +625,9 @@ private:
|
|||||||
|
|
||||||
/// Nesting level of the begin/end of text object
|
/// Nesting level of the begin/end of text object
|
||||||
int m_textBeginEndState;
|
int m_textBeginEndState;
|
||||||
|
|
||||||
|
/// Actually realized physical font
|
||||||
|
PDFCachedItem<QRawFont> m_realizedFont;
|
||||||
};
|
};
|
||||||
|
|
||||||
} // namespace pdf
|
} // namespace pdf
|
||||||
|
@ -102,6 +102,8 @@ void PDFPainter::performUpdateGraphicsState(const PDFPageContentProcessorState&
|
|||||||
{
|
{
|
||||||
m_currentBrush.dirty();
|
m_currentBrush.dirty();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
PDFPageContentProcessor::performUpdateGraphicsState(state);
|
||||||
}
|
}
|
||||||
|
|
||||||
void PDFPainter::performSaveGraphicState(ProcessOrder order)
|
void PDFPainter::performSaveGraphicState(ProcessOrder order)
|
||||||
|
Loading…
x
Reference in New Issue
Block a user