Fix of font drawing

This commit is contained in:
Jakub Melka 2019-03-31 18:08:36 +02:00
parent a462e0b768
commit cd981183b0
5 changed files with 182 additions and 19 deletions

View File

@ -273,6 +273,17 @@ QByteArray PDFDocumentDataLoaderDecorator::readName(const PDFObject& object)
return QByteArray();
}
QByteArray PDFDocumentDataLoaderDecorator::readString(const PDFObject& object)
{
const PDFObject& dereferencedObject = m_document->getObject(object);
if (dereferencedObject.isString())
{
return dereferencedObject.getString();
}
return QByteArray();
}
PDFInteger PDFDocumentDataLoaderDecorator::readInteger(const PDFObject& object, PDFInteger defaultValue) const
{
const PDFObject& dereferencedObject = m_document->getObject(object);
@ -479,4 +490,14 @@ QByteArray PDFDocumentDataLoaderDecorator::readNameFromDictionary(const PDFDicti
return QByteArray();
}
QByteArray PDFDocumentDataLoaderDecorator::readStringFromDictionary(const PDFDictionary* dictionary, const char* key)
{
if (dictionary->hasKey(key))
{
return readString(dictionary->get(key));
}
return QByteArray();
}
} // namespace pdf

View File

@ -92,6 +92,11 @@ public:
/// \param object Object, can be an indirect reference to object (it is dereferenced)
QByteArray readName(const PDFObject& object);
/// Reads a string from the object, if it is possible. If object is not a string,
/// then empty byte array is returned.
/// \param object Object, can be an indirect reference to object (it is dereferenced)
QByteArray readString(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
@ -227,6 +232,11 @@ public:
/// \param key Entry key
QByteArray readNameFromDictionary(const PDFDictionary* dictionary, const char* key);
/// Reads a string 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 readStringFromDictionary(const PDFDictionary* dictionary, const char* key);
private:
const PDFDocument* m_document;
};

View File

@ -23,7 +23,8 @@
namespace pdf
{
PDFFont::PDFFont()
PDFFont::PDFFont(FontDescriptor fontDescriptor) :
m_fontDescriptor(qMove(fontDescriptor))
{
}
@ -78,7 +79,57 @@ PDFFontPointer PDFFont::createFont(const PDFObject& object, const PDFDocument* d
const StandardFontType standardFont = fontLoader.readEnumByName(fontDictionary->get("BaseFont"), standardFonts.cbegin(), standardFonts.cend(), StandardFontType::Invalid);
// Read Font Descriptor
// TODO: Read font descriptor
FontDescriptor fontDescriptor;
const PDFObject& fontDescriptorObject = document->getObject(fontDictionary->get("FontDescriptor"));
if (fontDescriptorObject.isDictionary())
{
const PDFDictionary* fontDescriptorDictionary = fontDescriptorObject.getDictionary();
fontDescriptor.fontName = fontLoader.readNameFromDictionary(fontDescriptorDictionary, "FontName");
fontDescriptor.fontFamily = fontLoader.readStringFromDictionary(fontDescriptorDictionary, "FontFamily");
constexpr const std::array<std::pair<const char*, QFont::Stretch>, 9> stretches = {
std::pair<const char*, QFont::Stretch>{ "UltraCondensed", QFont::UltraCondensed },
std::pair<const char*, QFont::Stretch>{ "ExtraCondensed", QFont::ExtraCondensed },
std::pair<const char*, QFont::Stretch>{ "Condensed", QFont::Condensed },
std::pair<const char*, QFont::Stretch>{ "SemiCondensed", QFont::SemiCondensed },
std::pair<const char*, QFont::Stretch>{ "Normal", QFont::Unstretched },
std::pair<const char*, QFont::Stretch>{ "SemiExpanded", QFont::SemiExpanded },
std::pair<const char*, QFont::Stretch>{ "Expanded", QFont::Expanded },
std::pair<const char*, QFont::Stretch>{ "ExtraExpanded", QFont::ExtraExpanded },
std::pair<const char*, QFont::Stretch>{ "UltraExpanded", QFont::UltraExpanded }
};
fontDescriptor.fontStretch = fontLoader.readEnumByName(fontDescriptorDictionary->get("FontStretch"), stretches.cbegin(), stretches.cend(), QFont::Unstretched);
fontDescriptor.fontWeight = fontLoader.readNumberFromDictionary(fontDescriptorDictionary, "FontWeight", 500);
fontDescriptor.italicAngle = fontLoader.readNumberFromDictionary(fontDescriptorDictionary, "ItalicAngle", 0.0);
fontDescriptor.ascent = fontLoader.readNumberFromDictionary(fontDescriptorDictionary, "Ascent", 0.0);
fontDescriptor.descent = fontLoader.readNumberFromDictionary(fontDescriptorDictionary, "Descent", 0.0);
fontDescriptor.leading = fontLoader.readNumberFromDictionary(fontDescriptorDictionary, "Leading", 0.0);
fontDescriptor.capHeight = fontLoader.readNumberFromDictionary(fontDescriptorDictionary, "CapHeight", 0.0);
fontDescriptor.xHeight = fontLoader.readNumberFromDictionary(fontDescriptorDictionary, "XHeight", 0.0);
fontDescriptor.stemV = fontLoader.readNumberFromDictionary(fontDescriptorDictionary, "StemV", 0.0);
fontDescriptor.stemH = fontLoader.readNumberFromDictionary(fontDescriptorDictionary, "StemH", 0.0);
fontDescriptor.avgWidth = fontLoader.readNumberFromDictionary(fontDescriptorDictionary, "AvgWidth", 0.0);
fontDescriptor.maxWidth = fontLoader.readNumberFromDictionary(fontDescriptorDictionary, "MaxWidth", 0.0);
fontDescriptor.missingWidth = fontLoader.readNumberFromDictionary(fontDescriptorDictionary, "MissingWidth", 0.0);
fontDescriptor.flags = fontLoader.readIntegerFromDictionary(fontDescriptorDictionary, "Flags", 0);
fontDescriptor.boundingBox = fontLoader.readRectangle(fontDescriptorDictionary->get("FontBBox"), QRectF());
fontDescriptor.charset = fontLoader.readStringFromDictionary(fontDescriptorDictionary, "Charset");
auto loadStream = [fontDescriptorDictionary, document](QByteArray& byteArray, const char* name)
{
if (fontDescriptorDictionary->hasKey(name))
{
const PDFObject& streamObject = document->getObject(fontDescriptorDictionary->get(name));
if (streamObject.isStream())
{
byteArray = document->getDecodedStream(streamObject.getStream());
}
}
};
loadStream(fontDescriptor.fontFile, "FontFile");
loadStream(fontDescriptor.fontFile2, "FontFile2");
loadStream(fontDescriptor.fontFile3, "FontFile3");
}
// Read Font Encoding
// The font encoding for the simple font is determined by this algorithm:
@ -235,10 +286,10 @@ PDFFontPointer PDFFont::createFont(const PDFObject& object, const PDFDocument* d
switch (fontType)
{
case FontType::Type1:
return PDFFontPointer(new PDFType1Font(qMove(name), qMove(baseFont), firstChar, lastChar, qMove(widths), encoding, simpleFontEncodingTable, standardFont));
return PDFFontPointer(new PDFType1Font(qMove(fontDescriptor), 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));
return PDFFontPointer(new PDFTrueTypeFont(qMove(fontDescriptor), qMove(name), qMove(baseFont), firstChar, lastChar, qMove(widths), encoding, simpleFontEncodingTable));
default:
{
@ -255,13 +306,15 @@ PDFFontPointer PDFFont::createFont(const PDFObject& object, const PDFDocument* d
return PDFFontPointer();
}
PDFSimpleFont::PDFSimpleFont(QByteArray name,
PDFSimpleFont::PDFSimpleFont(FontDescriptor fontDescriptor,
QByteArray name,
QByteArray baseFont,
PDFInteger firstChar,
PDFInteger lastChar,
std::vector<PDFInteger> widths,
PDFEncoding::Encoding encodingType,
encoding::EncodingTable encoding) :
PDFFont(qMove(fontDescriptor)),
m_name(qMove(name)),
m_baseFont(qMove(baseFont)),
m_firstChar(firstChar),
@ -276,10 +329,40 @@ PDFSimpleFont::PDFSimpleFont(QByteArray name,
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);
QRawFont rawFont;
if (m_fontDescriptor.isEmbedded())
{
// Type 1 font
if (!m_fontDescriptor.fontFile.isEmpty())
{
rawFont.loadFromData(m_fontDescriptor.fontFile, fontSize, QFont::PreferNoHinting);
}
else if (!m_fontDescriptor.fontFile2.isEmpty())
{
rawFont.loadFromData(m_fontDescriptor.fontFile2, fontSize, QFont::PreferNoHinting);
}
if (!rawFont.isValid())
{
throw PDFParserException(PDFTranslationContext::tr("Can't load embedded font."));
}
}
else
{
// TODO: Zkontrolovat, zda se zde opravdu prebiraji spravne fonty
const int weight = qBound<int>(0, m_fontDescriptor.fontWeight / 10.0, 99);
const int stretch = qBound<int>(1, m_fontDescriptor.fontStretch, 4000);
QFont font(m_baseFont);
font.setHintingPreference(QFont::PreferNoHinting);
font.setStretch(stretch);
font.setWeight(weight);
font.setPixelSize(fontSize);
rawFont = QRawFont::fromFont(font, QFontDatabase::Any);
}
return rawFont;
}
QString PDFSimpleFont::getTextUsingEncoding(const QByteArray& byteArray) const
@ -295,7 +378,8 @@ QString PDFSimpleFont::getTextUsingEncoding(const QByteArray& byteArray) const
return string;
}
PDFType1Font::PDFType1Font(QByteArray name,
PDFType1Font::PDFType1Font(FontDescriptor fontDescriptor,
QByteArray name,
QByteArray baseFont,
PDFInteger firstChar,
PDFInteger lastChar,
@ -303,7 +387,7 @@ PDFType1Font::PDFType1Font(QByteArray name,
PDFEncoding::Encoding encodingType,
encoding::EncodingTable encoding,
StandardFontType standardFontType) :
PDFSimpleFont(qMove(name), qMove(baseFont), firstChar, lastChar, qMove(widths), encodingType, encoding),
PDFSimpleFont(qMove(fontDescriptor), qMove(name), qMove(baseFont), firstChar, lastChar, qMove(widths), encodingType, encoding),
m_standardFontType(standardFontType)
{

View File

@ -129,6 +129,42 @@ static constexpr PDFEncoding::Encoding getEncodingForStandardFont(StandardFontTy
}
}
struct FontDescriptor
{
bool isEmbedded() const { return !fontFile.isEmpty() || !fontFile2.isEmpty() || !fontFile3.isEmpty(); }
QByteArray fontName;
QByteArray fontFamily;
QFont::Stretch fontStretch = QFont::AnyStretch;
PDFReal fontWeight = 400.0;
PDFInteger flags;
QRectF boundingBox;
PDFReal italicAngle = 0.0;
PDFReal ascent = 0.0;
PDFReal descent = 0.0;
PDFReal leading = 0.0;
PDFReal capHeight = 0.0;
PDFReal xHeight = 0.0;
PDFReal stemV = 0.0;
PDFReal stemH = 0.0;
PDFReal avgWidth = 0.0;
PDFReal maxWidth = 0.0;
PDFReal missingWidth = 0.0;
/// Byte array with Type 1 font program (embedded font)
QByteArray fontFile;
/// Byte array with TrueType font program (embedded font)
QByteArray fontFile2;
/// Byte array with font program, whose format is defined by the Subtype array
/// in the font dictionary.
QByteArray fontFile3;
/// Character set
QByteArray charset;
};
class PDFFont;
using PDFFontPointer = QSharedPointer<PDFFont>;
@ -137,7 +173,7 @@ using PDFFontPointer = QSharedPointer<PDFFont>;
class PDFFont
{
public:
explicit PDFFont();
explicit PDFFont(FontDescriptor fontDescriptor);
virtual ~PDFFont() = default;
/// Returns the font type
@ -152,7 +188,16 @@ public:
/// \param byteArray Byte array with encoded string
virtual QString getTextUsingEncoding(const QByteArray& byteArray) const = 0;
/// Returns font descriptor
const FontDescriptor* getFontDescriptor() const { return &m_fontDescriptor; }
/// Creates font from the object. If font can't be created, exception is thrown.
/// \param object Font dictionary
/// \param document Document
static PDFFontPointer createFont(const PDFObject& object, const PDFDocument* document);
protected:
FontDescriptor m_fontDescriptor;
};
/// Simple font, see PDF reference 1.7, chapter 5.5. Simple fonts have encoding table,
@ -160,7 +205,8 @@ public:
class PDFSimpleFont : public PDFFont
{
public:
explicit PDFSimpleFont(QByteArray name,
explicit PDFSimpleFont(FontDescriptor fontDescriptor,
QByteArray name,
QByteArray baseFont,
PDFInteger firstChar,
PDFInteger lastChar,
@ -185,7 +231,8 @@ protected:
class PDFType1Font : public PDFSimpleFont
{
public:
explicit PDFType1Font(QByteArray name,
explicit PDFType1Font(FontDescriptor fontDescriptor,
QByteArray name,
QByteArray baseFont,
PDFInteger firstChar,
PDFInteger lastChar,

View File

@ -1564,12 +1564,11 @@ void PDFPageContentProcessor::operatorTextMoveByOffset(PDFReal t_x, PDFReal t_y)
{
const QMatrix& textLineMatrix = m_graphicState.getTextLineMatrix();
QMatrix translationMatrix;
translationMatrix.translate(t_x, t_y);
QMatrix transformedMatrix = textLineMatrix;
transformedMatrix.translate(t_x, t_y);
QMatrix resultMatrix = textLineMatrix * translationMatrix;
m_graphicState.setTextMatrix(resultMatrix);
m_graphicState.setTextLineMatrix(resultMatrix);
m_graphicState.setTextMatrix(transformedMatrix);
m_graphicState.setTextLineMatrix(transformedMatrix);
updateGraphicState();
}
@ -1705,6 +1704,7 @@ void PDFPageContentProcessor::operatorTextSetSpacingAndShowText(PDFReal t_w, PDF
void PDFPageContentProcessor::drawText(const TextSequence& textSequence)
{
// TODO: Kdyz nejsme v text rezimu, tak nekreslime text
if (textSequence.items.empty())
{
// Do not display empty text
@ -1748,6 +1748,7 @@ void PDFPageContentProcessor::drawText(const TextSequence& textSequence)
const bool stroke = isTextRenderingModeStroked(textRenderingMode);
const bool clipped = isTextRenderingModeClipped(textRenderingMode);
// TODO: Add Text Clipping
// TODO: Pouzit pravdepodobne sirky z widths array?
// Detect horizontal writing system
const bool isHorizontalWritingSystem = std::any_of(advances.cbegin(), advances.cend(), [](const QPointF& point) { return !qFuzzyIsNull(point.x()); });