This is an automated email from the ASF dual-hosted git repository. leginee pushed a commit to branch bugfix-dxf-filter in repository https://gitbox.apache.org/repos/asf/openoffice.git
commit d8752c8838d3faf6aa52f4718ee75f420c6dd657 Author: Peter Kovacs <[email protected]> AuthorDate: Sun Jul 5 10:38:40 2026 +0200 for the boundary we need to measure as the renderer does. fix correct rendering for arcs. --- main/filter/source/graphicfilter/idxf/dxfreprd.cxx | 110 ++++++++++++--------- main/filter/source/graphicfilter/idxf/dxfvec.cxx | 13 ++- 2 files changed, 75 insertions(+), 48 deletions(-) diff --git a/main/filter/source/graphicfilter/idxf/dxfreprd.cxx b/main/filter/source/graphicfilter/idxf/dxfreprd.cxx index 4fd7c62039..3a1b1c44cf 100644 --- a/main/filter/source/graphicfilter/idxf/dxfreprd.cxx +++ b/main/filter/source/graphicfilter/idxf/dxfreprd.cxx @@ -247,6 +247,21 @@ void DXFRepresentation::ReadHeader(DXFGroupReader & rDGR) } +// Map a single OCS point of an entity to WCS via its extrusion, then add it to +// the bounding box. The renderer (DXF2GDIMetaFile::DrawEntities) applies the +// same ECS->WCS transform per entity, so the extent has to be measured in WCS +// as well — otherwise drawings that use a non-default (e.g. negative-Z) +// extrusion come out mis-scaled and offset (issue 16564: PL1CAFE.dxf reaches +// OCS X=-10.88 on mirrored polylines that actually render near WCS X=+10.88). +static inline void UnionOCS(DXFBoundingBox & rBox, const DXFTransform & rE2W, + const DXFVector & rP) +{ + DXFVector aW; + rE2W.Transform(rP, aW); + rBox.Union(aW); +} + + // Approximate the world-space extent of a single line of text and add it to // the bounding box, so labels sitting near the drawing edge are not cropped. // AOO renders TEXT/ATTRIB left-aligned on the baseline at the insertion point, @@ -254,12 +269,15 @@ void DXFRepresentation::ReadHeader(DXFGroupReader & rDGR) // renderer, see DXF2GDIMetaFile::DrawTextEntity), so bound that rectangle, // rotated by the text angle. The per-character advance and descent are // deliberately generous fractions of the text height so the label never clips. -static void UnionTextExtent(DXFBoundingBox & rBox, const DXFVector & rInsert, +// rE2W maps the (OCS) text rectangle to WCS so text on an extruded entity is +// measured where it is drawn. +static void UnionTextExtent(DXFBoundingBox & rBox, const DXFTransform & rE2W, + const DXFVector & rInsert, double fHeight, double fXScale, double fRotAngle, const char * pText) { if (fHeight <= 0.0) { - rBox.Union(rInsert); + UnionOCS(rBox, rE2W, rInsert); return; } if (fXScale <= 0.0) @@ -278,7 +296,7 @@ static void UnionTextExtent(DXFBoundingBox & rBox, const DXFVector & rInsert, const double fAlong = a ? fWidth : 0.0; for (int b = 0; b < 2; b++) { const double fPerp = b ? fAscent : -fDescent; - rBox.Union(rInsert + aDir * fAlong + aUp * fPerp); + UnionOCS(rBox, rE2W, rInsert + aDir * fAlong + aUp * fPerp); } } } @@ -289,63 +307,61 @@ void DXFRepresentation::CalcBoundingBox(const DXFEntities & rEntities, { DXFBasicEntity * pBE=rEntities.pFirst; while (pBE!=NULL) { + // The renderer builds an ECS->WCS transform from the entity's extrusion + // whenever it is not the default (DrawEntities checks fz != 1.0); measure + // through the same transform so the box is in WCS, not raw OCS. + DXFTransform aE2W; // identity by default + if (pBE->aExtrusion.fz != 1.0) + aE2W = DXFTransform(pBE->aExtrusion); switch (pBE->eType) { case DXF_LINE: { const DXFLineEntity * pE = (DXFLineEntity*)pBE; - rBox.Union(pE->aP0); - rBox.Union(pE->aP1); + UnionOCS(rBox, aE2W, pE->aP0); + UnionOCS(rBox, aE2W, pE->aP1); break; } case DXF_POINT: { const DXFPointEntity * pE = (DXFPointEntity*)pBE; - rBox.Union(pE->aP0); + UnionOCS(rBox, aE2W, pE->aP0); break; } case DXF_CIRCLE: { const DXFCircleEntity * pE = (DXFCircleEntity*)pBE; - DXFVector aP; - aP=pE->aP0; - aP.fx-=pE->fRadius; - aP.fy-=pE->fRadius; - rBox.Union(aP); - aP=pE->aP0; - aP.fx+=pE->fRadius; - aP.fy+=pE->fRadius; - rBox.Union(aP); + // radius is invariant under the extrusion (a rotation/reflection), + // so map the centre to WCS and take +/-radius there. + DXFVector aC; + aE2W.Transform(pE->aP0, aC); + DXFVector aP=aC; aP.fx-=pE->fRadius; aP.fy-=pE->fRadius; rBox.Union(aP); + aP=aC; aP.fx+=pE->fRadius; aP.fy+=pE->fRadius; rBox.Union(aP); break; } case DXF_ARC: { const DXFArcEntity * pE = (DXFArcEntity*)pBE; - DXFVector aP; - aP=pE->aP0; - aP.fx-=pE->fRadius; - aP.fy-=pE->fRadius; - rBox.Union(aP); - aP=pE->aP0; - aP.fx+=pE->fRadius; - aP.fy+=pE->fRadius; - rBox.Union(aP); + DXFVector aC; + aE2W.Transform(pE->aP0, aC); + DXFVector aP=aC; aP.fx-=pE->fRadius; aP.fy-=pE->fRadius; rBox.Union(aP); + aP=aC; aP.fx+=pE->fRadius; aP.fy+=pE->fRadius; rBox.Union(aP); break; } case DXF_TRACE: { const DXFTraceEntity * pE = (DXFTraceEntity*)pBE; - rBox.Union(pE->aP0); - rBox.Union(pE->aP1); - rBox.Union(pE->aP2); - rBox.Union(pE->aP3); + UnionOCS(rBox, aE2W, pE->aP0); + UnionOCS(rBox, aE2W, pE->aP1); + UnionOCS(rBox, aE2W, pE->aP2); + UnionOCS(rBox, aE2W, pE->aP3); break; } case DXF_SOLID: { const DXFSolidEntity * pE = (DXFSolidEntity*)pBE; - rBox.Union(pE->aP0); - rBox.Union(pE->aP1); - rBox.Union(pE->aP2); - rBox.Union(pE->aP3); + UnionOCS(rBox, aE2W, pE->aP0); + UnionOCS(rBox, aE2W, pE->aP1); + UnionOCS(rBox, aE2W, pE->aP2); + UnionOCS(rBox, aE2W, pE->aP3); break; } case DXF_TEXT: { const DXFTextEntity * pE = (DXFTextEntity*)pBE; - UnionTextExtent(rBox, pE->aP0, pE->fHeight, pE->fXScale, + UnionTextExtent(rBox, aE2W, pE->aP0, pE->fHeight, pE->fXScale, pE->fRotAngle, pE->sText); break; } @@ -369,11 +385,11 @@ void DXFRepresentation::CalcBoundingBox(const DXFEntities & rEntities, aP.fx=(aBox.fMinX-pB->aBasePoint.fx)*pE->fXScale+pE->aP0.fx; aP.fy=(aBox.fMinY-pB->aBasePoint.fy)*pE->fYScale+pE->aP0.fy; aP.fz=(aBox.fMinZ-pB->aBasePoint.fz)*pE->fZScale+pE->aP0.fz; - rBox.Union(aP); + UnionOCS(rBox, aE2W, aP); aP.fx=(aBox.fMaxX-pB->aBasePoint.fx)*pE->fXScale+pE->aP0.fx; aP.fy=(aBox.fMaxY-pB->aBasePoint.fy)*pE->fYScale+pE->aP0.fy; aP.fz=(aBox.fMaxZ-pB->aBasePoint.fz)*pE->fZScale+pE->aP0.fz; - rBox.Union(aP); + UnionOCS(rBox, aE2W, aP); break; } case DXF_ATTDEF: { @@ -385,21 +401,21 @@ void DXFRepresentation::CalcBoundingBox(const DXFEntities & rEntities, } case DXF_ATTRIB: { const DXFAttribEntity * pE = (DXFAttribEntity*)pBE; - UnionTextExtent(rBox, pE->aP0, pE->fHeight, pE->fXScale, + UnionTextExtent(rBox, aE2W, pE->aP0, pE->fHeight, pE->fXScale, pE->fRotAngle, pE->sText); break; } case DXF_VERTEX: { const DXFVertexEntity * pE = (DXFVertexEntity*)pBE; - rBox.Union(pE->aP0); + UnionOCS(rBox, aE2W, pE->aP0); break; } case DXF_3DFACE: { const DXF3DFaceEntity * pE = (DXF3DFaceEntity*)pBE; - rBox.Union(pE->aP0); - rBox.Union(pE->aP1); - rBox.Union(pE->aP2); - rBox.Union(pE->aP3); + UnionOCS(rBox, aE2W, pE->aP0); + UnionOCS(rBox, aE2W, pE->aP1); + UnionOCS(rBox, aE2W, pE->aP2); + UnionOCS(rBox, aE2W, pE->aP3); break; } case DXF_DIMENSION: { @@ -414,11 +430,11 @@ void DXFRepresentation::CalcBoundingBox(const DXFEntities & rEntities, aP.fx=aBox.fMinX-pB->aBasePoint.fx; aP.fy=aBox.fMinY-pB->aBasePoint.fy; aP.fz=aBox.fMinZ-pB->aBasePoint.fz; - rBox.Union(aP); + UnionOCS(rBox, aE2W, aP); aP.fx=aBox.fMaxX-pB->aBasePoint.fx; aP.fy=aBox.fMaxY-pB->aBasePoint.fy; aP.fz=aBox.fMaxZ-pB->aBasePoint.fz; - rBox.Union(aP); + UnionOCS(rBox, aE2W, aP); break; } case DXF_POLYLINE: { @@ -451,7 +467,7 @@ void DXFRepresentation::CalcBoundingBox(const DXFEntities & rEntities, if (rPath.bIsPolyLine == sal_True) { if (rPath.pP != NULL) { for (sal_Int32 i = 0; i < rPath.nPointCount; i++) - rBox.Union(rPath.pP[i]); + UnionOCS(rBox, aE2W, rPath.pP[i]); } } else { @@ -459,8 +475,8 @@ void DXFRepresentation::CalcBoundingBox(const DXFEntities & rEntities, const DXFEdgeType * pEdge = rPath.aEdges[i]; if (pEdge != NULL && pEdge->nEdgeType == 1) { const DXFEdgeTypeLine * pLine = (DXFEdgeTypeLine*)pEdge; - rBox.Union(pLine->aStartPoint); - rBox.Union(pLine->aEndPoint); + UnionOCS(rBox, aE2W, pLine->aStartPoint); + UnionOCS(rBox, aE2W, pLine->aEndPoint); } } } @@ -476,7 +492,7 @@ void DXFRepresentation::CalcBoundingBox(const DXFEntities & rEntities, const DXFLWPolyLineEntity * pE = (DXFLWPolyLineEntity*)pBE; if (pE->pP != NULL) { for (sal_Int32 i = 0; i < pE->nCount; i++) - rBox.Union(pE->pP[i]); + UnionOCS(rBox, aE2W, pE->pP[i]); } break; } diff --git a/main/filter/source/graphicfilter/idxf/dxfvec.cxx b/main/filter/source/graphicfilter/idxf/dxfvec.cxx index 61b4ad7c73..201f06095e 100644 --- a/main/filter/source/graphicfilter/idxf/dxfvec.cxx +++ b/main/filter/source/graphicfilter/idxf/dxfvec.cxx @@ -245,5 +245,16 @@ double DXFTransform::CalcRotAngle() const sal_Bool DXFTransform::Mirror() const { - if (aMZ.SProd(aMX*aMY)<0) return sal_True; else return sal_False; + // True when this transform reverses orientation *in the projected XY plane* — + // the plane the 2D metafile is actually drawn in. Arc rendering uses this to + // choose the sweep direction handed to DrawArc. + // + // The 3D handedness aMZ.(aMX x aMY) is the wrong test here: a negative-Z + // extrusion feeds the Arbitrary Axis Algorithm a 180-degrees rotation about Y + // (a proper rotation), which flips the in-plane orientation yet leaves the 3D + // determinant sign unchanged (the flipped aMZ.z masks it). That drew arcs with + // negative-Z extrusion as their complement (e.g. a quarter arc as three + // quarters — issue 16564). Use the sign of the 2D projected determinant, which + // is what actually governs clockwise vs counter-clockwise on the page. + if (aMX.fx*aMY.fy - aMX.fy*aMY.fx < 0) return sal_True; else return sal_False; }
