mirror of
https://github.com/JakubMelka/PDF4QT.git
synced 2025-06-05 21:59:17 +02:00
XFA: draw rectangle edges and corners
This commit is contained in:
@ -254,10 +254,10 @@ public:
|
|||||||
// Jakub Melka: set node value to null
|
// Jakub Melka: set node value to null
|
||||||
node = XFA_Node<Type>();
|
node = XFA_Node<Type>();
|
||||||
|
|
||||||
QDomNodeList elements = element.elementsByTagName(value);
|
QDomElement child = element.firstChildElement(value);
|
||||||
if (!elements.isEmpty())
|
if (!child.isNull())
|
||||||
{
|
{
|
||||||
node = XFA_Node<Type>(Type::parse(elements.item(0).toElement()));
|
node = XFA_Node<Type>(Type::parse(child));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -267,14 +267,11 @@ public:
|
|||||||
// Jakub Melka: clear node list
|
// Jakub Melka: clear node list
|
||||||
nodes.clear();
|
nodes.clear();
|
||||||
|
|
||||||
QDomNodeList elements = element.elementsByTagName(value);
|
QDomElement child = element.firstChildElement(value);
|
||||||
if (!elements.isEmpty())
|
while (!child.isNull())
|
||||||
{
|
{
|
||||||
nodes.resize(elements.size());
|
nodes.emplace_back(XFA_Node<Type>(Type::parse(child)));
|
||||||
for (int i = 0; i < elements.size(); ++i)
|
child = child.nextSiblingElement(value);
|
||||||
{
|
|
||||||
nodes[i] = XFA_Node<Type>(Type::parse(elements.item(i).toElement()));
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -9683,6 +9680,9 @@ private:
|
|||||||
|
|
||||||
QColor createColor(const xfa::XFA_color* color) const;
|
QColor createColor(const xfa::XFA_color* color) const;
|
||||||
QPen createPenFromEdge(const xfa::XFA_edge* edge, QList<PDFRenderError>& errors) const;
|
QPen createPenFromEdge(const xfa::XFA_edge* edge, QList<PDFRenderError>& errors) const;
|
||||||
|
QPen createPenFromCorner(const xfa::XFA_corner* corner, QList<PDFRenderError>& errors) const;
|
||||||
|
QPen createPenFromEdge(const std::vector<xfa::XFA_Node<xfa::XFA_edge>>& edges, size_t index, QList<PDFRenderError>& errors) const;
|
||||||
|
QPen createPenFromCorner(const std::vector<xfa::XFA_Node<xfa::XFA_corner>>& corners, size_t index, QList<PDFRenderError>& errors) const;
|
||||||
|
|
||||||
void drawItemBorder(const xfa::XFA_border* item,
|
void drawItemBorder(const xfa::XFA_border* item,
|
||||||
QList<PDFRenderError>& errors,
|
QList<PDFRenderError>& errors,
|
||||||
@ -11264,6 +11264,7 @@ void PDFXFAEngineImpl::draw(const QMatrix& pagePointToDevicePointMatrix,
|
|||||||
}
|
}
|
||||||
|
|
||||||
PDFPainterStateGuard guard(painter);
|
PDFPainterStateGuard guard(painter);
|
||||||
|
painter->setRenderHint(QPainter::Antialiasing);
|
||||||
painter->setWorldMatrix(pagePointToDevicePointMatrix, true);
|
painter->setWorldMatrix(pagePointToDevicePointMatrix, true);
|
||||||
painter->translate(0, page->getMediaBox().height());
|
painter->translate(0, page->getMediaBox().height());
|
||||||
painter->scale(1.0, -1.0);
|
painter->scale(1.0, -1.0);
|
||||||
@ -11436,97 +11437,173 @@ void PDFXFAEngineImpl::drawItemRectEdges(const std::vector<xfa::XFA_Node<xfa::XF
|
|||||||
const xfa::XFA_BaseNode::HAND hand,
|
const xfa::XFA_BaseNode::HAND hand,
|
||||||
QPainter* painter)
|
QPainter* painter)
|
||||||
{
|
{
|
||||||
if (edges.empty())
|
if (edges.empty() && corners.empty())
|
||||||
{
|
{
|
||||||
|
// We draw nothing
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
constexpr size_t LINE_COUNT = 4;
|
// Step 1) prepare edge array. We must note, that edges
|
||||||
std::array<xfa::XFA_Node<xfa::XFA_edge>, LINE_COUNT> fourEdges = { edges.back(), edges.back(), edges.back(), edges.back() };
|
// can be nullptr, in that case, do not draw anything.
|
||||||
for (size_t i = 0; i < edges.size(); ++i)
|
// All edges are null, then empty pen is returned.
|
||||||
{
|
std::array edgePens = {
|
||||||
if (i >= fourEdges.size())
|
createPenFromEdge(edges, 0, errors),
|
||||||
{
|
createPenFromEdge(edges, 1, errors),
|
||||||
break;
|
createPenFromEdge(edges, 2, errors),
|
||||||
}
|
createPenFromEdge(edges, 3, errors)
|
||||||
|
|
||||||
fourEdges[i] = edges[i];
|
|
||||||
}
|
|
||||||
|
|
||||||
std::array<QPen, LINE_COUNT> edgePen = {
|
|
||||||
createPenFromEdge(fourEdges[0].getValue(), errors),
|
|
||||||
createPenFromEdge(fourEdges[1].getValue(), errors),
|
|
||||||
createPenFromEdge(fourEdges[2].getValue(), errors),
|
|
||||||
createPenFromEdge(fourEdges[3].getValue(), errors)
|
|
||||||
};
|
};
|
||||||
|
|
||||||
// All lines are equal? If yes, just draw the rectangle using first pen. If lines
|
// Step 2) prepare draw rectangle, which can be different
|
||||||
// are not equal, then we must draw each line separately.
|
// from nominal content area, because we must handle handedness.
|
||||||
if (std::adjacent_find(edgePen.begin(), edgePen.end(), std::not_equal_to()) == edgePen.end())
|
|
||||||
|
auto getOffset = [&edgePens, hand](const size_t index)
|
||||||
{
|
{
|
||||||
QPen pen = edgePen.front();
|
PDFReal offset = 0.0;
|
||||||
QRectF lineRectangle = nominalContentArea;
|
|
||||||
|
|
||||||
qreal penWidthHalf = pen.widthF() * 0.5;
|
|
||||||
QMarginsF margins(penWidthHalf, penWidthHalf, penWidthHalf, penWidthHalf);
|
|
||||||
|
|
||||||
switch (hand)
|
switch (hand)
|
||||||
{
|
{
|
||||||
case pdf::xfa::XFA_BaseNode::HAND::Even:
|
case pdf::xfa::XFA_BaseNode::HAND::Even:
|
||||||
break;
|
break;
|
||||||
case pdf::xfa::XFA_BaseNode::HAND::Left:
|
case pdf::xfa::XFA_BaseNode::HAND::Left:
|
||||||
lineRectangle = lineRectangle.marginsAdded(margins);
|
offset = -edgePens[index].widthF() * 0.5;
|
||||||
break;
|
break;
|
||||||
case pdf::xfa::XFA_BaseNode::HAND::Right:
|
case pdf::xfa::XFA_BaseNode::HAND::Right:
|
||||||
lineRectangle = lineRectangle.marginsRemoved(margins);
|
offset = +edgePens[index].widthF() * 0.5;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
painter->setPen(pen);
|
return offset;
|
||||||
painter->setBrush(Qt::NoBrush);
|
};
|
||||||
painter->drawRect(lineRectangle);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
std::array<QLineF, LINE_COUNT> lines = {
|
|
||||||
QLineF(nominalContentArea.topLeft(), nominalContentArea.topRight()),
|
|
||||||
QLineF(nominalContentArea.topRight(), nominalContentArea.bottomRight()),
|
|
||||||
QLineF(nominalContentArea.bottomRight(), nominalContentArea.bottomLeft()),
|
|
||||||
QLineF(nominalContentArea.bottomLeft(), nominalContentArea.topLeft())
|
|
||||||
};
|
|
||||||
|
|
||||||
for (size_t i = 0; i < LINE_COUNT; ++i)
|
QMarginsF handMargins(getOffset(3), getOffset(0), -getOffset(1), -getOffset(2));
|
||||||
|
QRectF drawRect = nominalContentArea.marginsRemoved(handMargins);
|
||||||
|
|
||||||
|
// Step 3) Draw lines without corners. We must consider corner radius.
|
||||||
|
// Corners will be then drawm afterwards.
|
||||||
|
|
||||||
|
constexpr size_t CORNER_TOP_LEFT_INDEX = 0;
|
||||||
|
constexpr size_t CORNER_TOP_RIGHT_INDEX = 1;
|
||||||
|
constexpr size_t CORNER_BOTTOM_RIGHT_INDEX = 2;
|
||||||
|
constexpr size_t CORNER_BOTTOM_LEFT_INDEX = 3;
|
||||||
|
|
||||||
|
auto getRadiusFromCorner = [&corners](const size_t index) -> PDFReal
|
||||||
|
{
|
||||||
|
if (index < corners.size())
|
||||||
{
|
{
|
||||||
PDFReal offset = 0.0;
|
return corners[index].getValue()->getRadius().getValuePt(nullptr);
|
||||||
switch (hand)
|
}
|
||||||
{
|
else if (!corners.empty())
|
||||||
case pdf::xfa::XFA_BaseNode::HAND::Even:
|
{
|
||||||
break;
|
return corners.back().getValue()->getRadius().getValuePt(nullptr);
|
||||||
case pdf::xfa::XFA_BaseNode::HAND::Left:
|
|
||||||
offset = -edgePen[i].widthF() * 0.5;
|
|
||||||
break;
|
|
||||||
case pdf::xfa::XFA_BaseNode::HAND::Right:
|
|
||||||
offset = +edgePen[i].widthF() * 0.5;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!qFuzzyIsNull(offset))
|
|
||||||
{
|
|
||||||
QLineF& line = lines[i];
|
|
||||||
QLineF normal = line.normalVector().unitVector();
|
|
||||||
line.translate(normal.dx() * offset, normal.dy() * offset);
|
|
||||||
}
|
|
||||||
|
|
||||||
painter->setPen(edgePen[i]);
|
|
||||||
painter->setBrush(Qt::NoBrush);
|
|
||||||
painter->drawLine(lines[i]);
|
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
if (std::any_of(corners.cbegin(), corners.cend(), [](const auto& corner) { return corner.getValue()->getPresence() == xfa::XFA_BaseNode::PRESENCE::Visible; }))
|
return 0.0;
|
||||||
|
};
|
||||||
|
|
||||||
|
std::array cornerOffsets = {
|
||||||
|
getRadiusFromCorner(CORNER_TOP_LEFT_INDEX),
|
||||||
|
getRadiusFromCorner(CORNER_TOP_RIGHT_INDEX),
|
||||||
|
getRadiusFromCorner(CORNER_BOTTOM_RIGHT_INDEX),
|
||||||
|
getRadiusFromCorner(CORNER_BOTTOM_LEFT_INDEX)
|
||||||
|
};
|
||||||
|
|
||||||
|
auto drawLine = [painter](QPointF start, QPointF end, QPen pen, qreal offsetStart, qreal offsetEnd)
|
||||||
{
|
{
|
||||||
errors << PDFRenderError(RenderErrorType::NotSupported, PDFTranslationContext::tr("XFA: visual display of the corners of rectangle are not supported."));
|
QPointF adjustedStart = start;
|
||||||
}
|
QPointF adjustedEnd = end;
|
||||||
|
|
||||||
|
if (!qFuzzyIsNull(offsetStart))
|
||||||
|
{
|
||||||
|
QLineF line(start, end);
|
||||||
|
QLineF unitVector = line.unitVector();
|
||||||
|
|
||||||
|
adjustedStart += (unitVector.p2() - unitVector.p1()) * offsetStart;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!qFuzzyIsNull(offsetEnd))
|
||||||
|
{
|
||||||
|
QLineF line(end, start);
|
||||||
|
QLineF unitVector = line.unitVector();
|
||||||
|
|
||||||
|
adjustedEnd += (unitVector.p2() - unitVector.p1()) * offsetEnd;
|
||||||
|
}
|
||||||
|
|
||||||
|
painter->setPen(std::move(pen));
|
||||||
|
painter->drawLine(adjustedStart, adjustedEnd);
|
||||||
|
};
|
||||||
|
|
||||||
|
drawLine(drawRect.topLeft(), drawRect.topRight(), edgePens[0], cornerOffsets[CORNER_TOP_LEFT_INDEX], cornerOffsets[CORNER_TOP_RIGHT_INDEX]);
|
||||||
|
drawLine(drawRect.topRight(), drawRect.bottomRight(), edgePens[1], cornerOffsets[CORNER_TOP_RIGHT_INDEX], cornerOffsets[CORNER_BOTTOM_RIGHT_INDEX]);
|
||||||
|
drawLine(drawRect.bottomRight(), drawRect.bottomLeft(), edgePens[2], cornerOffsets[CORNER_BOTTOM_RIGHT_INDEX], cornerOffsets[CORNER_BOTTOM_LEFT_INDEX]);
|
||||||
|
drawLine(drawRect.bottomLeft(), drawRect.topLeft(), edgePens[3], cornerOffsets[CORNER_BOTTOM_LEFT_INDEX], cornerOffsets[CORNER_TOP_LEFT_INDEX]);
|
||||||
|
|
||||||
|
// Step 4) Draw corners
|
||||||
|
auto getCorner = [&corners](size_t index) -> const xfa::XFA_corner*
|
||||||
|
{
|
||||||
|
if (index < corners.size())
|
||||||
|
{
|
||||||
|
return corners[index].getValue();
|
||||||
|
}
|
||||||
|
else if (!corners.empty())
|
||||||
|
{
|
||||||
|
return corners.back().getValue();
|
||||||
|
}
|
||||||
|
|
||||||
|
return nullptr;
|
||||||
|
};
|
||||||
|
|
||||||
|
// Draws corner with down line and right line orientation (when rotation is zero).
|
||||||
|
|
||||||
|
std::array cornerPens = {
|
||||||
|
createPenFromCorner(corners, CORNER_TOP_LEFT_INDEX, errors),
|
||||||
|
createPenFromCorner(corners, CORNER_TOP_RIGHT_INDEX, errors),
|
||||||
|
createPenFromCorner(corners, CORNER_BOTTOM_RIGHT_INDEX, errors),
|
||||||
|
createPenFromCorner(corners, CORNER_BOTTOM_LEFT_INDEX, errors)
|
||||||
|
};
|
||||||
|
|
||||||
|
auto drawCorner = [painter](QPointF point, QPen pen, qreal rotation, const xfa::XFA_corner* corner)
|
||||||
|
{
|
||||||
|
if (!corner)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const xfa::XFA_BaseNode::JOIN join = corner->getJoin();
|
||||||
|
bool isInverted = corner->getInverted();
|
||||||
|
const PDFReal radius = corner->getRadius().getValuePt(nullptr);
|
||||||
|
|
||||||
|
PDFPainterStateGuard guard(painter);
|
||||||
|
painter->translate(point);
|
||||||
|
painter->rotate(rotation);
|
||||||
|
painter->setPen(std::move(pen));
|
||||||
|
|
||||||
|
switch (join)
|
||||||
|
{
|
||||||
|
case xfa::XFA_BaseNode::JOIN::Square:
|
||||||
|
{
|
||||||
|
painter->drawLine(0, 0, 0, radius);
|
||||||
|
painter->drawLine(0, 0, radius, 0);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
case xfa::XFA_BaseNode::JOIN::Round:
|
||||||
|
{
|
||||||
|
if (!isInverted)
|
||||||
|
{
|
||||||
|
painter->drawArc(0, 0, 2.0 * radius, 2.0 * radius, -180 * 16, -90 * 16);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
painter->drawArc(-radius, -radius, 2.0 * radius, 2.0 * radius, 0, -90 * 16);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
drawCorner(drawRect.topLeft(), cornerPens[CORNER_TOP_LEFT_INDEX], 0, getCorner(CORNER_TOP_LEFT_INDEX));
|
||||||
|
drawCorner(drawRect.topRight(), cornerPens[CORNER_TOP_RIGHT_INDEX], 90, getCorner(CORNER_TOP_RIGHT_INDEX));
|
||||||
|
drawCorner(drawRect.bottomRight(), cornerPens[CORNER_BOTTOM_RIGHT_INDEX], 180, getCorner(CORNER_BOTTOM_RIGHT_INDEX));
|
||||||
|
drawCorner(drawRect.bottomLeft(), cornerPens[CORNER_BOTTOM_LEFT_INDEX], 270, getCorner(CORNER_BOTTOM_LEFT_INDEX));
|
||||||
}
|
}
|
||||||
|
|
||||||
void PDFXFAEngineImpl::clear()
|
void PDFXFAEngineImpl::clear()
|
||||||
@ -11568,7 +11645,7 @@ QColor PDFXFAEngineImpl::createColor(const xfa::XFA_color* color) const
|
|||||||
|
|
||||||
QPen PDFXFAEngineImpl::createPenFromEdge(const xfa::XFA_edge* edge, QList<PDFRenderError>& errors) const
|
QPen PDFXFAEngineImpl::createPenFromEdge(const xfa::XFA_edge* edge, QList<PDFRenderError>& errors) const
|
||||||
{
|
{
|
||||||
QPen pen;
|
QPen pen(Qt::NoPen);
|
||||||
|
|
||||||
if (!edge)
|
if (!edge)
|
||||||
{
|
{
|
||||||
@ -11638,6 +11715,97 @@ QPen PDFXFAEngineImpl::createPenFromEdge(const xfa::XFA_edge* edge, QList<PDFRen
|
|||||||
return pen;
|
return pen;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
QPen PDFXFAEngineImpl::createPenFromCorner(const xfa::XFA_corner* corner, QList<PDFRenderError>& errors) const
|
||||||
|
{
|
||||||
|
QPen pen(Qt::NoPen);
|
||||||
|
|
||||||
|
if (!corner)
|
||||||
|
{
|
||||||
|
return pen;
|
||||||
|
}
|
||||||
|
|
||||||
|
switch (corner->getStroke())
|
||||||
|
{
|
||||||
|
case pdf::xfa::XFA_BaseNode::STROKE::Solid:
|
||||||
|
pen.setStyle(Qt::SolidLine);
|
||||||
|
break;
|
||||||
|
case pdf::xfa::XFA_BaseNode::STROKE::DashDot:
|
||||||
|
pen.setStyle(Qt::DashDotLine);
|
||||||
|
break;
|
||||||
|
case pdf::xfa::XFA_BaseNode::STROKE::DashDotDot:
|
||||||
|
pen.setStyle(Qt::DashDotDotLine);
|
||||||
|
break;
|
||||||
|
case pdf::xfa::XFA_BaseNode::STROKE::Dashed:
|
||||||
|
pen.setStyle(Qt::DashLine);
|
||||||
|
break;
|
||||||
|
case pdf::xfa::XFA_BaseNode::STROKE::Dotted:
|
||||||
|
pen.setStyle(Qt::DotLine);
|
||||||
|
break;
|
||||||
|
case pdf::xfa::XFA_BaseNode::STROKE::Embossed:
|
||||||
|
case pdf::xfa::XFA_BaseNode::STROKE::Etched:
|
||||||
|
case pdf::xfa::XFA_BaseNode::STROKE::Lowered:
|
||||||
|
case pdf::xfa::XFA_BaseNode::STROKE::Raised:
|
||||||
|
pen.setStyle(Qt::SolidLine); // Ignore these line types
|
||||||
|
errors << PDFRenderError(RenderErrorType::NotSupported, PDFTranslationContext::tr("XFA: special stroke is not supported."));
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
pen.setWidthF(corner->getThickness().getValuePt(nullptr));
|
||||||
|
|
||||||
|
QColor color = createColor(corner->getColor());
|
||||||
|
|
||||||
|
if (color.isValid())
|
||||||
|
{
|
||||||
|
const xfa::XFA_BaseNode::PRESENCE presence = corner->getPresence();
|
||||||
|
switch (presence)
|
||||||
|
{
|
||||||
|
case xfa::XFA_BaseNode::PRESENCE::Visible:
|
||||||
|
color.setAlphaF(1.0);
|
||||||
|
break;
|
||||||
|
case xfa::XFA_BaseNode::PRESENCE::Hidden:
|
||||||
|
case xfa::XFA_BaseNode::PRESENCE::Inactive:
|
||||||
|
case xfa::XFA_BaseNode::PRESENCE::Invisible:
|
||||||
|
color.setAlphaF(0.0);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pen.setColor(color);
|
||||||
|
return pen;
|
||||||
|
}
|
||||||
|
|
||||||
|
QPen PDFXFAEngineImpl::createPenFromEdge(const std::vector<xfa::XFA_Node<xfa::XFA_edge>>& edges,
|
||||||
|
size_t index,
|
||||||
|
QList<PDFRenderError>& errors) const
|
||||||
|
{
|
||||||
|
if (index < edges.size())
|
||||||
|
{
|
||||||
|
return createPenFromEdge(edges[index].getValue(), errors);
|
||||||
|
}
|
||||||
|
else if (!edges.empty())
|
||||||
|
{
|
||||||
|
return createPenFromEdge(edges.back().getValue(), errors);
|
||||||
|
}
|
||||||
|
|
||||||
|
return QPen(Qt::NoPen);
|
||||||
|
}
|
||||||
|
|
||||||
|
QPen PDFXFAEngineImpl::createPenFromCorner(const std::vector<xfa::XFA_Node<xfa::XFA_corner>>& corners,
|
||||||
|
size_t index,
|
||||||
|
QList<PDFRenderError>& errors) const
|
||||||
|
{
|
||||||
|
if (index < corners.size())
|
||||||
|
{
|
||||||
|
return createPenFromCorner(corners[index].getValue(), errors);
|
||||||
|
}
|
||||||
|
else if (!corners.empty())
|
||||||
|
{
|
||||||
|
return createPenFromCorner(corners.back().getValue(), errors);
|
||||||
|
}
|
||||||
|
|
||||||
|
return QPen(Qt::NoPen);
|
||||||
|
}
|
||||||
|
|
||||||
template<typename Node>
|
template<typename Node>
|
||||||
PDFXFALayoutEngine::SizeInfo PDFXFALayoutEngine::getSizeInfo(const Node* node) const
|
PDFXFALayoutEngine::SizeInfo PDFXFALayoutEngine::getSizeInfo(const Node* node) const
|
||||||
{
|
{
|
||||||
|
Reference in New Issue
Block a user