mirror of https://github.com/JakubMelka/PDF4QT.git
Glyph painting
This commit is contained in:
parent
81be3cbd21
commit
a462e0b768
|
@ -1659,6 +1659,12 @@ void PDFPageContentProcessor::operatorTextShowTextIndividualSpacing()
|
|||
break;
|
||||
}
|
||||
|
||||
case PDFLexicalAnalyzer::TokenType::Real:
|
||||
{
|
||||
textSequence.items.push_back(TextSequenceItem(m_operands[i].data.value<PDFReal>()));
|
||||
break;
|
||||
}
|
||||
|
||||
case PDFLexicalAnalyzer::TokenType::String:
|
||||
{
|
||||
QString string = m_graphicState.getTextFont()->getTextUsingEncoding(m_operands[i].data.toByteArray());
|
||||
|
@ -1699,7 +1705,127 @@ void PDFPageContentProcessor::operatorTextSetSpacingAndShowText(PDFReal t_w, PDF
|
|||
|
||||
void PDFPageContentProcessor::drawText(const TextSequence& textSequence)
|
||||
{
|
||||
if (textSequence.items.empty())
|
||||
{
|
||||
// Do not display empty text
|
||||
return;
|
||||
}
|
||||
|
||||
const QRawFont& font = getRealizedFont();
|
||||
if (font.isValid())
|
||||
{
|
||||
std::vector<QChar> chars;
|
||||
chars.reserve(textSequence.items.size());
|
||||
for (const TextSequenceItem& item : textSequence.items)
|
||||
{
|
||||
if (item.isCharacter())
|
||||
{
|
||||
chars.push_back(item.character);
|
||||
}
|
||||
}
|
||||
|
||||
int numGlyphs = static_cast<int>(chars.size());
|
||||
std::vector<uint32_t> glyphIndices;
|
||||
glyphIndices.resize(chars.size(), 0);
|
||||
if (font.glyphIndexesForChars(chars.data(), static_cast<int>(chars.size()), glyphIndices.data(), &numGlyphs))
|
||||
{
|
||||
if (chars.size() != static_cast<size_t>(numGlyphs))
|
||||
{
|
||||
throw PDFRendererException(RenderErrorType::Error, PDFTranslationContext::tr("Cant convert unicode to glyph indices, text can't be printed."));
|
||||
}
|
||||
|
||||
std::vector<QPointF> advances;
|
||||
advances.resize(numGlyphs, QPointF());
|
||||
if (font.advancesForGlyphIndexes(glyphIndices.data(), advances.data(), numGlyphs, QRawFont::SeparateAdvances | QRawFont::UseDesignMetrics))
|
||||
{
|
||||
const PDFReal fontSize = m_graphicState.getTextFontSize();
|
||||
const PDFReal horizontalScaling = m_graphicState.getTextHorizontalScaling() * 0.01; // Horizontal scaling is in percents
|
||||
const PDFReal characterSpacing = m_graphicState.getTextCharacterSpacing();
|
||||
const PDFReal wordSpacing = m_graphicState.getTextWordSpacing();
|
||||
const PDFReal textRise = m_graphicState.getTextRise();
|
||||
const TextRenderingMode textRenderingMode = m_graphicState.getTextRenderingMode();
|
||||
const bool fill = isTextRenderingModeFilled(textRenderingMode);
|
||||
const bool stroke = isTextRenderingModeStroked(textRenderingMode);
|
||||
const bool clipped = isTextRenderingModeClipped(textRenderingMode);
|
||||
// TODO: Add Text Clipping
|
||||
|
||||
// Detect horizontal writing system
|
||||
const bool isHorizontalWritingSystem = std::any_of(advances.cbegin(), advances.cend(), [](const QPointF& point) { return !qFuzzyIsNull(point.x()); });
|
||||
|
||||
// Calculate text rendering matrix
|
||||
QMatrix adjustMatrix(horizontalScaling, 0.0, 0.0, 1.0, 0.0, textRise);
|
||||
QMatrix textMatrix = m_graphicState.getTextMatrix();
|
||||
QMatrix fontMatrix(1.0, 0.0, 0.0, -1.0, 0.0, 0.0);
|
||||
|
||||
size_t characterIndex = 0;
|
||||
for (const TextSequenceItem& item : textSequence.items)
|
||||
{
|
||||
PDFReal displacementX = 0.0;
|
||||
PDFReal displacementY = 0.0;
|
||||
|
||||
if (item.isCharacter())
|
||||
{
|
||||
QChar character = item.character;
|
||||
QPointF advance = advances[characterIndex];
|
||||
|
||||
// First, compute the advance
|
||||
const PDFReal additionalAdvance = (character == QChar(QChar::Space)) ? wordSpacing : characterSpacing;
|
||||
if (isHorizontalWritingSystem)
|
||||
{
|
||||
advance.rx() += additionalAdvance;
|
||||
}
|
||||
else
|
||||
{
|
||||
advance.ry() += additionalAdvance;
|
||||
}
|
||||
advance.rx() *= horizontalScaling;
|
||||
|
||||
// Then get the glyph path and paint it
|
||||
QPainterPath glyphPath = fontMatrix.map(font.pathForGlyph(glyphIndices[characterIndex]));
|
||||
if (!glyphPath.isEmpty())
|
||||
{
|
||||
QMatrix textRenderingMatrix = textMatrix * adjustMatrix;
|
||||
QPainterPath transformedGlyph = textRenderingMatrix.map(glyphPath);
|
||||
performPathPainting(transformedGlyph, stroke, fill, transformedGlyph.fillRule());
|
||||
}
|
||||
|
||||
displacementX = advance.x();
|
||||
displacementY = advance.y();
|
||||
|
||||
++characterIndex;
|
||||
}
|
||||
else if (item.isAdvance())
|
||||
{
|
||||
if (horizontalScaling)
|
||||
{
|
||||
displacementX = -item.advance * 0.001 * fontSize * horizontalScaling;
|
||||
}
|
||||
else
|
||||
{
|
||||
displacementY = -item.advance * 0.001 * fontSize;
|
||||
}
|
||||
}
|
||||
|
||||
textMatrix.translate(displacementX, displacementY);
|
||||
}
|
||||
|
||||
m_graphicState.setTextMatrix(textMatrix);
|
||||
updateGraphicState();
|
||||
}
|
||||
else
|
||||
{
|
||||
throw PDFRendererException(RenderErrorType::Error, PDFTranslationContext::tr("Cant convert unicode to glyph indices, text can't be printed."));
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
throw PDFRendererException(RenderErrorType::Error, PDFTranslationContext::tr("Cant convert unicode to glyph indices, text can't be printed."));
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
throw PDFRendererException(RenderErrorType::Error, PDFTranslationContext::tr("Invalid font, text can't be printed."));
|
||||
}
|
||||
}
|
||||
|
||||
QRawFont PDFPageContentProcessor::getRealizedFontImpl() const
|
||||
|
|
|
@ -355,14 +355,14 @@ protected:
|
|||
{
|
||||
inline explicit TextSequenceItem() = default;
|
||||
inline explicit TextSequenceItem(QChar character) : character(character), advance(0) { }
|
||||
inline explicit TextSequenceItem(PDFInteger advance) : character(), advance(advance) { }
|
||||
inline explicit TextSequenceItem(PDFReal advance) : character(), advance(advance) { }
|
||||
|
||||
inline bool isCharacter() const { return !character.isNull(); }
|
||||
inline bool isAdvance() const { return advance != 0; }
|
||||
inline bool isAdvance() const { return advance != 0.0; }
|
||||
inline bool isNull() const { return !isCharacter() && !isAdvance(); }
|
||||
|
||||
QChar character;
|
||||
PDFInteger advance = 0;
|
||||
PDFReal advance = 0;
|
||||
};
|
||||
|
||||
struct TextSequence
|
||||
|
|
Loading…
Reference in New Issue